Files
wifi-densepose/docs/adr/ADR-029-ruvsense-multistatic-sensing-mode.md
Claude 25b005a0d6 docs: add RuvSense sensing-first RF mode architecture
Research, ADR, and DDD specification for multistatic WiFi DensePose
with coherence-gated tracking and complete ruvector integration.

- docs/research/ruvsense-multistatic-fidelity-architecture.md:
  SOTA research covering bandwidth/frequency/viewpoint fidelity levers,
  ESP32 multistatic mesh design, coherence gating, AETHER embedding
  integration, and full ruvector crate mapping

- docs/adr/ADR-029-ruvsense-multistatic-sensing-mode.md:
  Architecture decision for sensing-first RF mode on existing ESP32
  silicon. GOAP integration plan (9 actions, 4 phases, 36 cost units).
  TDMA schedule for 20 Hz update rate from 4-node mesh.
  IEEE 802.11bf forward-compatible design.

- docs/ddd/ruvsense-domain-model.md:
  Domain-Driven Design with 3 bounded contexts (Multistatic Sensing,
  Coherence, Pose Tracking), aggregate roots, domain events, context
  map, anti-corruption layers, and repository interfaces.

Acceptance test: 2 people, 20 Hz, 10 min stable tracks, zero ID swaps,
<30mm torso keypoint jitter.

https://claude.ai/code/session_01QTX772SDsGVSPnaphoNgNY
2026-03-02 00:17:30 +00:00

18 KiB
Raw Blame History

ADR-029: Project RuvSense -- Sensing-First RF Mode for Multistatic WiFi DensePose

Field Value
Status Proposed
Date 2026-03-02
Deciders ruv
Codename RuvSense -- RuVector-Enhanced Sensing for Multistatic Fidelity
Relates to ADR-012 (ESP32 Mesh), ADR-014 (SOTA Signal Processing), ADR-016 (RuVector Training), ADR-017 (RuVector Signal+MAT), ADR-018 (ESP32 Implementation), ADR-024 (AETHER Embeddings), ADR-026 (Survivor Track Lifecycle), ADR-027 (MERIDIAN Generalization)

1. Context

1.1 The Fidelity Gap

Current WiFi-DensePose achieves functional pose estimation from a single ESP32 AP, but three fidelity metrics prevent production deployment:

Metric Current (Single ESP32) Required (Production) Root Cause
Torso keypoint jitter ~15cm RMS <3cm RMS Single viewpoint, 20 MHz bandwidth, no temporal smoothing
Multi-person separation Fails >2 people, frequent ID swaps 4+ people, zero swaps over 10 min Underdetermined with 1 TX-RX link; no person-specific features
Small motion sensitivity Gross movement only Breathing at 3m, heartbeat at 1.5m Insufficient phase sensitivity at 2.4 GHz; noise floor too high
Update rate ~10 Hz effective 20 Hz Single-channel serial CSI collection
Temporal stability Drifts within hours Stable over days No coherence gating; model absorbs environmental drift

1.2 The Insight: Sensing-First RF Mode on Existing Silicon

You do not need to invent a new WiFi standard. The winning move is a sensing-first RF mode that rides on existing silicon (ESP32-S3), existing bands (2.4/5 GHz), and existing regulations (802.11n NDP frames). The fidelity improvement comes from three physical levers:

  1. Bandwidth: Channel-hopping across 2.4 GHz channels 1/6/11 triples effective bandwidth from 20 MHz to 60 MHz, 3x multipath separation
  2. Carrier frequency: Dual-band sensing (2.4 + 5 GHz) doubles phase sensitivity to small motion
  3. Viewpoints: Multistatic ESP32 mesh (4 nodes = 12 TX-RX links) provides 360-degree geometric diversity

1.3 Acceptance Test

Two people in a room, 20 Hz update rate, stable tracks for 10 minutes with no identity swaps and low jitter in the torso keypoints.

Quantified:

  • Torso keypoint jitter < 30mm RMS (hips, shoulders, spine)
  • Zero identity swaps over 600 seconds (12,000 frames)
  • 20 Hz output rate (50 ms cycle time)
  • Breathing SNR > 10dB at 3m (validates small-motion sensitivity)

2. Decision

2.1 Architecture Overview

Implement RuvSense as a new bounded context within wifi-densepose-signal, consisting of 6 modules:

wifi-densepose-signal/src/ruvsense/
├── mod.rs              // Module exports, RuvSense pipeline orchestrator
├── multiband.rs        // Multi-band CSI frame fusion (§2.2)
├── phase_align.rs      // Cross-channel phase alignment (§2.3)
├── multistatic.rs      // Multi-node viewpoint fusion (§2.4)
├── coherence.rs        // Coherence metric computation (§2.5)
├── coherence_gate.rs   // Gated update policy (§2.6)
└── pose_tracker.rs     // 17-keypoint Kalman tracker with re-ID (§2.7)

2.2 Channel-Hopping Firmware (ESP32-S3)

Modify the ESP32 firmware (firmware/esp32-csi-node/main/csi_collector.c) to cycle through non-overlapping channels at configurable dwell times:

// Channel hop table (populated from NVS at boot)
static uint8_t s_hop_channels[6] = {1, 6, 11, 36, 40, 44};
static uint8_t s_hop_count = 3;   // default: 2.4 GHz only
static uint32_t s_dwell_ms = 50;  // 50ms per channel

At 100 Hz raw CSI rate with 50 ms dwell across 3 channels, each channel yields ~33 frames/second. The existing ADR-018 binary frame format already carries channel_freq_mhz at offset 8, so no wire format change is needed.

NDP frame injection: esp_wifi_80211_tx() injects deterministic Null Data Packet frames (preamble-only, no payload, ~24 us airtime) at GPIO-triggered intervals. This is sensing-first: the primary RF emission purpose is CSI measurement, not data communication.

2.3 Multi-Band Frame Fusion

Aggregate per-channel CSI frames into a wideband virtual snapshot:

/// Fused multi-band CSI from one node at one time slot.
pub struct MultiBandCsiFrame {
    pub node_id: u8,
    pub timestamp_us: u64,
    /// One canonical-56 row per channel, ordered by center frequency.
    pub channel_frames: Vec<CanonicalCsiFrame>,
    /// Center frequencies (MHz) for each channel row.
    pub frequencies_mhz: Vec<u32>,
    /// Cross-channel coherence score (0.0-1.0).
    pub coherence: f32,
}

Cross-channel phase alignment uses ruvector-solver::NeumannSolver to solve for the channel-dependent phase rotation introduced by the ESP32 local oscillator during channel hops. The system:

[Φ₁, Φ₆, Φ₁₁] = [Φ_body + δ₁, Φ_body + δ₆, Φ_body + δ₁₁]

NeumannSolver fits the δ offsets from the static subcarrier components (which should have zero body-caused phase shift), then removes them.

2.4 Multistatic Viewpoint Fusion

With N ESP32 nodes, collect N MultiBandCsiFrame per time slot and fuse with geometric diversity:

TDMA Sensing Schedule (4 nodes):

Slot TX RX₁ RX₂ RX₃ Duration
0 Node A B C D 4 ms
1 Node B A C D 4 ms
2 Node C A B D 4 ms
3 Node D A B C 4 ms
4 -- Processing + fusion 30 ms
Total 50 ms = 20 Hz

Synchronization: GPIO pulse from aggregator node at cycle start. Clock drift at ±10ppm over 50 ms is ~0.5 us, well within the 1 ms guard interval.

Cross-node fusion uses ruvector-attn-mincut::attn_mincut where time-frequency cells from different nodes attend to each other. Cells showing correlated motion energy across nodes (body reflection) are amplified; cells with single-node energy (local multipath artifact) are suppressed.

Multi-person separation via ruvector-mincut::DynamicMinCut:

  1. Build cross-link temporal correlation graph (nodes = TX-RX links, edges = correlation coefficient)
  2. DynamicMinCut partitions into K clusters (one per detected person)
  3. Attention fusion (§5.3 of research doc) runs independently per cluster

2.5 Coherence Metric

Per-link coherence quantifies consistency with recent history:

pub fn coherence_score(
    current: &[f32],
    reference: &[f32],
    variance: &[f32],
) -> f32 {
    current.iter().zip(reference.iter()).zip(variance.iter())
        .map(|((&c, &r), &v)| {
            let z = (c - r).abs() / v.sqrt().max(1e-6);
            let weight = 1.0 / (v + 1e-6);
            ((-0.5 * z * z).exp(), weight)
        })
        .fold((0.0, 0.0), |(sc, sw), (c, w)| (sc + c * w, sw + w))
        .pipe(|(sc, sw)| sc / sw)
}

The static/dynamic decomposition uses ruvector-solver to separate environmental drift (slow, global) from body motion (fast, subcarrier-specific).

2.6 Coherence-Gated Update Policy

pub enum GateDecision {
    /// Coherence > 0.85: Full Kalman measurement update
    Accept(Pose),
    /// 0.5 < coherence < 0.85: Kalman predict only (3x inflated noise)
    PredictOnly,
    /// Coherence < 0.5: Reject measurement entirely
    Reject,
    /// >10s continuous low coherence: Trigger SONA recalibration (ADR-005)
    Recalibrate,
}

When Recalibrate fires:

  1. Freeze output at last known good pose
  2. Collect 200 frames (10s) of unlabeled CSI
  3. Run AETHER contrastive TTT (ADR-024) to adapt encoder
  4. Update SONA LoRA weights (ADR-005), <1ms per update
  5. Resume sensing with adapted model

2.7 Pose Tracker (17-Keypoint Kalman with Re-ID)

Lift the Kalman + lifecycle + re-ID infrastructure from wifi-densepose-mat/src/tracking/ (ADR-026) into the RuvSense bounded context, extended for 17-keypoint skeletons:

Parameter Value Rationale
State dimension 6 per keypoint (x,y,z,vx,vy,vz) Constant-velocity model
Process noise σ_a 0.3 m/s² Normal walking acceleration
Measurement noise σ_obs 0.08 m Target <8cm RMS at torso
Mahalanobis gate χ²(3) = 9.0 3σ ellipsoid (same as ADR-026)
Birth hits 2 frames (100ms at 20Hz) Reject single-frame noise
Loss misses 5 frames (250ms) Brief occlusion tolerance
Re-ID feature AETHER 128-dim embedding Body-shape discriminative (ADR-024)
Re-ID window 5 seconds Sufficient for crossing recovery

Track assignment uses ruvector-mincut's DynamicPersonMatcher (already integrated in metrics.rs, ADR-016) with joint position + embedding cost:

cost(track_i, det_j) = 0.6 * mahalanobis(track_i, det_j.position)
                      + 0.4 * (1 - cosine_sim(track_i.embedding, det_j.embedding))

3. GOAP Integration Plan (Goal-Oriented Action Planning)

3.1 Action Dependency Graph

Phase 1: Foundation
  Action 1: Channel-Hopping Firmware ──────────────────────┐
      │                                                      │
      v                                                      │
  Action 2: Multi-Band Frame Fusion ──→ Action 6: Coherence │
      │                                  Metric              │
      v                                    │                 │
  Action 3: Multistatic Mesh              v                 │
      │                              Action 7: Coherence    │
      v                                  Gate               │
Phase 2: Tracking                         │                 │
  Action 4: Pose Tracker ←────────────────┘                 │
      │                                                      │
      v                                                      │
  Action 5: End-to-End Pipeline @ 20 Hz ←────────────────────┘
      │
      v
Phase 4: Hardening
  Action 8: AETHER Track Re-ID
      │
      v
  Action 9: ADR-029 Documentation (this document)

3.2 Cost and RuVector Mapping

# Action Cost Preconditions RuVector Crates Effects
1 Channel-hopping firmware 4/10 ESP32 firmware exists None (pure C) bandwidth_extended = true
2 Multi-band frame fusion 5/10 Action 1 solver, attention fused_multi_band_frame = true
3 Multistatic mesh aggregation 5/10 Action 2 mincut, attn-mincut multistatic_mesh = true
4 Pose tracker 4/10 Action 3, 7 mincut pose_tracker = true
5 End-to-end pipeline 6/10 Actions 2-4 temporal-tensor, attention 20hz_update = true
6 Coherence metric 3/10 Action 2 solver coherence_metric = true
7 Coherence gate 3/10 Action 6 attn-mincut coherence_gating = true
8 AETHER re-ID 4/10 Actions 4, 7 attention identity_stable = true
9 ADR documentation 2/10 All above None Decision documented

Total cost: 36 units. Minimum viable path to acceptance test: Actions 1-5 + 6-7 = 30 units.

3.3 Latency Budget (50ms cycle)

Stage Budget Method
UDP receive + parse <1 ms ADR-018 binary, 148 bytes, zero-alloc
Multi-band fusion ~2 ms NeumannSolver on 2×2 phase alignment
Multistatic fusion ~3 ms attn_mincut on 3-6 nodes × 64 velocity bins
Model inference ~30-40 ms CsiToPoseTransformer (lightweight, no ResNet)
Kalman update <1 ms 17 independent 6D filters, stack-allocated
Total ~37-47 ms Fits in 50 ms

4. Hardware Bill of Materials

Component Qty Unit Cost Purpose
ESP32-S3-DevKitC-1 4 $10 TX/RX sensing nodes
ESP32-S3-DevKitC-1 1 $10 Aggregator (or x86/RPi host)
External 5dBi antenna 4-8 $3 Improved gain, directional coverage
USB-C hub (4 port) 1 $15 Power distribution
Wall mount brackets 4 $2 Ceiling/wall installation
Total $73-91 Complete 4-node mesh

5. RuVector v2.0.4 Integration Map

All five published crates are exercised:

Crate Actions Integration Point Algorithmic Advantage
ruvector-solver 2, 6 Phase alignment; coherence matrix decomposition O(√n) Neumann convergence
ruvector-attention 2, 5, 8 Cross-channel weighting; ring buffer; embedding similarity Sublinear attention for small d
ruvector-mincut 3, 4 Viewpoint diversity partitioning; track assignment O(n^1.5 log n) dynamic updates
ruvector-attn-mincut 3, 7 Cross-node spectrogram fusion; coherence gating Attention + mincut in one pass
ruvector-temporal-tensor 5 Compressed sensing window ring buffer 50-75% memory reduction

6. IEEE 802.11bf Alignment

RuvSense's TDMA sensing schedule is forward-compatible with IEEE 802.11bf (WLAN Sensing, published 2024):

RuvSense Concept 802.11bf Equivalent
TX slot Sensing Initiator
RX slot Sensing Responder
TDMA cycle Sensing Measurement Instance
NDP frame Sensing NDP
Aggregator Sensing Session Owner

When commercial APs support 802.11bf, the ESP32 mesh can interoperate by translating SSP slots into 802.11bf Sensing Trigger frames.


7. Dependency Changes

Firmware (C)

New files:

  • firmware/esp32-csi-node/main/sensing_schedule.h
  • firmware/esp32-csi-node/main/sensing_schedule.c

Modified files:

  • firmware/esp32-csi-node/main/csi_collector.c (add channel hopping, link tagging)
  • firmware/esp32-csi-node/main/main.c (add GPIO sync, TDMA timer)

Rust

New module: crates/wifi-densepose-signal/src/ruvsense/ (6 files, ~1500 lines estimated)

Modified files:

  • crates/wifi-densepose-signal/src/lib.rs (export ruvsense module)
  • crates/wifi-densepose-signal/Cargo.toml (no new deps; all ruvector crates already present per ADR-017)
  • crates/wifi-densepose-sensing-server/src/main.rs (wire RuvSense pipeline into WebSocket output)

No new workspace dependencies. All ruvector crates are already in the workspace Cargo.toml.


8. Implementation Priority

Priority Actions Weeks Milestone
P0 1 (firmware) 2 Channel-hopping ESP32 prototype
P0 2 (multi-band) 2 Wideband virtual frames
P1 3 (multistatic) 2 Multi-node fusion
P1 4 (tracker) 1 17-keypoint Kalman
P1 6, 7 (coherence) 1 Gated updates
P2 5 (end-to-end) 2 20 Hz pipeline
P2 8 (AETHER re-ID) 1 Identity hardening
P3 9 (docs) 0.5 This ADR finalized
Total ~10 weeks Acceptance test

9. Consequences

9.1 Positive

  • 3x bandwidth improvement without hardware changes (channel hopping on existing ESP32)
  • 12 independent viewpoints from 4 commodity $10 nodes (C(4,2) × 2 links)
  • 20 Hz update rate with Kalman-smoothed output for sub-30mm torso jitter
  • Days-long stability via coherence gating + SONA recalibration
  • All five ruvector crates exercised — consistent algorithmic foundation
  • $73-91 total BOM — accessible for research and production
  • 802.11bf forward-compatible — investment protected as commercial sensing arrives
  • Cognitum upgrade path — same software stack, swap ESP32 for higher-bandwidth front end

9.2 Negative

  • 4-node deployment requires physical installation and calibration of node positions
  • TDMA scheduling reduces per-node CSI rate (each node only transmits 1/4 of the time)
  • Channel hopping introduces ~1-5ms gaps during esp_wifi_set_channel() transitions
  • 5 GHz CSI on ESP32-S3 may not be available (ESP32-C6 supports it natively)
  • Coherence gate may reject valid measurements during fast body motion (mitigation: gate only on static-subcarrier coherence)

9.3 Risks

Risk Probability Impact Mitigation
ESP32 channel hop causes CSI gaps Medium Reduced effective rate Measure gap duration; increase dwell if >5ms
5 GHz CSI unavailable on S3 High Lose frequency diversity Fallback: 3-channel 2.4 GHz still provides 3x BW; ESP32-C6 for dual-band
Model inference >40ms Medium Miss 20 Hz target Run model at 10 Hz; Kalman predict at 20 Hz interpolates
Two-person separation fails at 3 nodes Low Identity swaps AETHER re-ID recovers; increase to 4-6 nodes
Coherence gate false-triggers Low Missed updates Gate on environmental coherence only, not body-motion subcarriers

ADR Relationship
ADR-012 Extended: RuvSense adds TDMA multistatic to single-AP mesh
ADR-014 Used: All 6 SOTA algorithms applied per-link
ADR-016 Extended: New ruvector integration points for multi-link fusion
ADR-017 Extended: Coherence gating adds temporal stability layer
ADR-018 Modified: Firmware gains channel hopping, TDMA schedule, HT40
ADR-022 Complementary: RuvSense is the ESP32 equivalent of Windows multi-BSSID
ADR-024 Used: AETHER embeddings for person re-identification
ADR-026 Reused: Kalman + lifecycle infrastructure lifted to RuvSense
ADR-027 Used: GeometryEncoder, HardwareNormalizer, FiLM conditioning

11. References

  1. IEEE 802.11bf-2024. "WLAN Sensing." IEEE Standards Association.
  2. Geng, J., Huang, D., De la Torre, F. (2023). "DensePose From WiFi." arXiv:2301.00250.
  3. Yan, K. et al. (2024). "Person-in-WiFi 3D." CVPR 2024, pp. 969-978.
  4. Chen, L. et al. (2026). "PerceptAlign: Geometry-Aware WiFi Sensing." arXiv:2601.12252.
  5. Kotaru, M. et al. (2015). "SpotFi: Decimeter Level Localization Using WiFi." SIGCOMM.
  6. Zheng, Y. et al. (2019). "Zero-Effort Cross-Domain Gesture Recognition with Wi-Fi." MobiSys.
  7. Zeng, Y. et al. (2019). "FarSense: Pushing the Range Limit of WiFi-based Respiration Sensing." MobiCom.
  8. AM-FM (2026). "A Foundation Model for Ambient Intelligence Through WiFi." arXiv:2602.11200.
  9. Espressif ESP-CSI. https://github.com/espressif/esp-csi