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
18 KiB
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:
- Bandwidth: Channel-hopping across 2.4 GHz channels 1/6/11 triples effective bandwidth from 20 MHz to 60 MHz, 3x multipath separation
- Carrier frequency: Dual-band sensing (2.4 + 5 GHz) doubles phase sensitivity to small motion
- 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:
- Build cross-link temporal correlation graph (nodes = TX-RX links, edges = correlation coefficient)
DynamicMinCutpartitions into K clusters (one per detected person)- 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:
- Freeze output at last known good pose
- Collect 200 frames (10s) of unlabeled CSI
- Run AETHER contrastive TTT (ADR-024) to adapt encoder
- Update SONA LoRA weights (ADR-005), <1ms per update
- 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.hfirmware/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(exportruvsensemodule)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 |
10. Related ADRs
| 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
- IEEE 802.11bf-2024. "WLAN Sensing." IEEE Standards Association.
- Geng, J., Huang, D., De la Torre, F. (2023). "DensePose From WiFi." arXiv:2301.00250.
- Yan, K. et al. (2024). "Person-in-WiFi 3D." CVPR 2024, pp. 969-978.
- Chen, L. et al. (2026). "PerceptAlign: Geometry-Aware WiFi Sensing." arXiv:2601.12252.
- Kotaru, M. et al. (2015). "SpotFi: Decimeter Level Localization Using WiFi." SIGCOMM.
- Zheng, Y. et al. (2019). "Zero-Effort Cross-Domain Gesture Recognition with Wi-Fi." MobiSys.
- Zeng, Y. et al. (2019). "FarSense: Pushing the Range Limit of WiFi-based Respiration Sensing." MobiCom.
- AM-FM (2026). "A Foundation Model for Ambient Intelligence Through WiFi." arXiv:2602.11200.
- Espressif ESP-CSI. https://github.com/espressif/esp-csi