Ruview/docs/adr/ADR-063-mmwave-sensor-fusion.md
rUv 66e2fa0835
feat: ADR-063/064 mmWave sensor fusion + multimodal ambient intelligence (#269)
* docs: ADR-063 mmWave sensor fusion with WiFi CSI

60 GHz mmWave radar (Seeed MR60BHA2, HLK-LD2410/LD2450) fusion
with WiFi CSI for dual-confirm fall detection, clinical-grade
vitals, and self-calibrating CSI pipeline.

Covers auto-detection, 6 supported sensors, Kalman fusion,
extended 48-byte vitals packet, RuVector/RuvSense integration
points, and 6-phase implementation plan.

Based on live hardware capture from ESP32-C6 + MR60BHA2 on COM4.

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat(firmware): ADR-063 mmWave sensor fusion — full implementation

Phase 1-2 of ADR-063:

mmwave_sensor.c/h:
- MR60BHA2 UART parser (60 GHz: HR, BR, presence, distance)
- LD2410 UART parser (24 GHz: presence, distance)
- Auto-detection: probes UART for known frame headers at boot
- Mock generator for QEMU testing (synthetic HR 72±2, BR 16±1)
- Capability flag registration per sensor type

edge_processing.c/h:
- 48-byte fused vitals packet (magic 0xC5110004)
- Kalman-style fusion: mmWave 80% + CSI 20% when both available
- Automatic fallback to CSI-only 32-byte packet when no mmWave
- Dual presence flag (Bit3 = mmwave_present)

main.c:
- mmwave_sensor_init() called at boot with auto-detect
- Status logged in startup banner

Fuzz stubs updated for mmwave_sensor API.
Build verified: QEMU mock build passes.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(firmware): correct MR60BHA2 + LD2410 UART protocols (ADR-063)

MR60BHA2: SOF=0x01 (not 0x5359), XOR+NOT checksums on header and
data, frame types 0x0A14 (BR), 0x0A15 (HR), 0x0A16 (distance),
0x0F09 (presence). Based on Seeed Arduino library research.

LD2410: 256000 baud (not 115200), 0xAA report head marker,
target state byte at offset 2 (after data_type + head_marker).

Auto-detect: probes MR60 at 115200 first, then LD2410 at 256000.
Sets final baud rate after detection.

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat: ADR-063 Phase 6 server-side mmWave + CSI fusion bridge

Python script reads both serial ports simultaneously:
- COM4 (ESP32-C6 + MR60BHA2): parses ESPHome debug output for HR, BR, presence, distance
- COM7 (ESP32-S3): reads CSI edge processing frames

Kalman-style fusion: mmWave 80% + CSI 20% for vitals, OR gate for presence.

Verified on real hardware: mmWave HR=75bpm, BR=25/min at 52cm range,
CSI frames flowing concurrently. Both sensors live for 30 seconds.

Co-Authored-By: claude-flow <ruv@ruv.net>

* docs: ADR-064 multimodal ambient intelligence roadmap

25+ applications across 4 tiers from practical to exotic:
- Tier 1 (build now): zero-FP fall detection, sleep monitoring,
  occupancy HVAC, baby breathing, bathroom safety
- Tier 2 (research): gait analysis, stress detection, gesture
  control, respiratory screening, multi-room activity
- Tier 3 (frontier): cardiac arrhythmia, RF tomography, sign
  language, cognitive load, swarm sensing
- Tier 4 (exotic): emotion contagion, lucid dreaming, plant
  monitoring, pet behavior

Priority matrix with effort estimates. All P0-P1 items work with
existing hardware (ESP32-S3 + MR60BHA2 + BH1750).

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): add ESP_ERR_NOT_FOUND to fuzz stubs

mmwave_sensor stub returns ESP_ERR_NOT_FOUND which wasn't
defined in the minimal esp_stubs.h for host-based fuzz testing.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-15 16:10:10 -04:00

14 KiB

ADR-063: 60 GHz mmWave Sensor Fusion with WiFi CSI

Status: Proposed Date: 2026-03-15 Deciders: @ruvnet Related: ADR-014 (SOTA signal processing), ADR-021 (vital sign extraction), ADR-029 (RuvSense multistatic), ADR-039 (edge intelligence), ADR-042 (CHCI coherent sensing)

Context

RuView currently senses the environment using WiFi CSI — a passive technique that analyzes how WiFi signals are disturbed by human presence and movement. While this works through walls and requires no line of sight, CSI-derived vital signs (breathing rate, heart rate) are inherently noisy because they rely on phase extraction from multipath-rich WiFi channels.

A complementary sensing modality exists: 60 GHz mmWave radar modules (e.g., Seeed MR60BHA2) that use active FMCW radar at 60 GHz to measure breathing and heart rate with clinical-grade accuracy. These modules are inexpensive (~$15), run on ESP32-C6/C3, and output structured vital signs over UART.

Live hardware capture (COM4, 2026-03-15) from a Seeed MR60BHA2 on an ESP32-C6 running ESPHome:

[D][sensor:093]: 'Real-time respiratory rate': Sending state 22.00000
[D][sensor:093]: 'Real-time heart rate': Sending state 92.00000 bpm
[D][sensor:093]: 'Distance to detection object': Sending state 0.00000 cm
[D][sensor:093]: 'Target Number': Sending state 0.00000
[D][binary_sensor:036]: 'Person Information': Sending state OFF
[D][sensor:093]: 'Seeed MR60BHA2 Illuminance': Sending state 0.67913 lx

The Opportunity

Fusing WiFi CSI with mmWave radar creates a sensor system that is greater than the sum of its parts:

Capability WiFi CSI Alone mmWave Alone Fused
Through-wall sensing Yes (5m+) No (LoS only, ~3m) Yes — CSI for room-scale, mmWave for precision
Heart rate accuracy ±5-10 BPM ±1-2 BPM ±1-2 BPM (mmWave primary, CSI cross-validates)
Breathing accuracy ±2-3 BPM ±0.5 BPM ±0.5 BPM
Presence detection Good (adaptive threshold) Excellent (range-gated) Excellent + through-wall
Multi-person Via subcarrier clustering Via range-Doppler bins Combined spatial + RF resolution
Fall detection Phase acceleration Range/velocity + micro-Doppler Dual-confirm reduces false positives to near-zero
Pose estimation Via trained model Not available CSI provides pose; mmWave provides ground-truth vitals for training
Coverage Whole room (passive) ~120° cone, 3m range Full room + precision zone
Cost per node ~$9 (ESP32-S3) ~$15 (ESP32-C6 + MR60BHA2) ~$24 combined

RuVector Integration Points

The RuVector v2.0.4 stack (already integrated per ADR-016) provides the signal processing backbone:

RuVector Component Role in mmWave Fusion
ruvector-attention (bvp.rs) Blood Volume Pulse estimation — mmWave heart rate can calibrate the WiFi CSI BVP phase extraction
ruvector-temporal-tensor (breathing.rs) Breathing rate estimation — mmWave provides ground-truth for adaptive filter tuning
ruvector-solver (triangulation.rs) Multilateration — mmWave range-gated distance + CSI amplitude = 3D position
ruvector-attn-mincut (spectrogram.rs) Time-frequency decomposition — mmWave Doppler complements CSI phase spectrogram
ruvector-mincut (metrics.rs, DynamicPersonMatcher) Multi-person association — mmWave target IDs help disambiguate CSI subcarrier clusters

RuvSense Integration Points

The RuvSense multistatic sensing pipeline (ADR-029) gains new capabilities:

RuvSense Module mmWave Integration
pose_tracker.rs (AETHER re-ID) mmWave distance + velocity as additional re-ID features for Kalman tracker
longitudinal.rs (Welford stats) mmWave vitals as reference signal for CSI drift detection
intention.rs (pre-movement) mmWave micro-Doppler detects pre-movement 100-200ms earlier than CSI
adversarial.rs (consistency check) mmWave provides independent signal to detect CSI spoofing/anomalies
coherence_gate.rs mmWave presence as additional gate input — if mmWave says "no person", CSI coherence gate rejects

Cross-Viewpoint Fusion Integration

The viewpoint fusion pipeline (ruvector/src/viewpoint/) extends naturally:

Viewpoint Module mmWave Extension
attention.rs (CrossViewpointAttention) mmWave range becomes a new "viewpoint" in the attention mechanism
geometry.rs (GeometricDiversityIndex) mmWave cone geometry contributes to Fisher Information / Cramer-Rao bounds
coherence.rs (phase phasor) mmWave phase coherence as validation for WiFi phasor coherence
fusion.rs (MultistaticArray) mmWave node becomes a member of the multistatic array with its own domain events

Decision

Add 60 GHz mmWave radar sensor support to the RuView firmware and sensing pipeline with auto-detection and device-specific capabilities.

Architecture

┌─────────────────────────────────────────────────────────┐
│                    Sensing Node                          │
│                                                          │
│  ┌──────────────┐    ┌──────────────┐    ┌────────────┐ │
│  │ ESP32-S3     │    │ ESP32-C6     │    │ Combined   │ │
│  │ WiFi CSI     │    │ + MR60BHA2   │    │ S3 + UART  │ │
│  │ (COM7)       │    │ 60GHz mmWave │    │ mmWave     │ │
│  │              │    │ (COM4)       │    │            │ │
│  │ Passive      │    │ Active radar │    │ Both modes │ │
│  │ Through-wall │    │ LoS, precise │    │            │ │
│  └──────┬───────┘    └──────┬───────┘    └─────┬──────┘ │
│         │                    │                   │       │
│         └────────┬───────────┘                   │       │
│                  ▼                               │       │
│         ┌────────────────┐                       │       │
│         │ Fusion Engine  │◄──────────────────────┘       │
│         │                │                               │
│         │ • Kalman fuse  │  Vitals packet (extended):    │
│         │ • Cross-validate│  magic 0xC5110004             │
│         │ • Ground-truth │  + mmwave_hr, mmwave_br       │
│         │   calibration  │  + mmwave_distance             │
│         │ • Fall confirm │  + mmwave_target_count         │
│         └────────────────┘  + confidence scores           │
└─────────────────────────────────────────────────────────┘

Three Deployment Modes

Mode 1: Standalone CSI (existing) — ESP32-S3 only, WiFi CSI sensing.

Mode 2: Standalone mmWave — ESP32-C6 + MR60BHA2, precise vitals in a single room.

Mode 3: Fused (recommended) — ESP32-S3 + mmWave module on UART, or two separate nodes with server-side fusion.

Auto-Detection Protocol

The firmware will auto-detect connected mmWave modules at boot:

  1. UART probe — On configured UART pins, send the MR60BHA2 identification command (0x01 0x01 0x00 0x01 ...) and check for valid response header
  2. Protocol detection — Identify the sensor family:
    • Seeed MR60BHA2 (breathing + heart rate)
    • Seeed MR60FDA1 (fall detection)
    • Seeed MR24HPC1 (presence + light sleep/deep sleep)
    • HLK-LD2410 (presence + distance)
    • HLK-LD2450 (multi-target tracking)
  3. Capability registration — Register detected sensor capabilities in the edge config:
typedef struct {
    uint8_t  mmwave_detected;      /** 1 if mmWave module found on UART */
    uint8_t  mmwave_type;          /** Sensor family (MR60BHA2, MR60FDA1, etc.) */
    uint8_t  mmwave_has_hr;        /** Heart rate capability */
    uint8_t  mmwave_has_br;        /** Breathing rate capability */
    uint8_t  mmwave_has_fall;      /** Fall detection capability */
    uint8_t  mmwave_has_presence;  /** Presence detection capability */
    uint8_t  mmwave_has_distance;  /** Range measurement capability */
    uint8_t  mmwave_has_tracking;  /** Multi-target tracking capability */
    float    mmwave_hr_bpm;        /** Latest heart rate from mmWave */
    float    mmwave_br_bpm;        /** Latest breathing rate from mmWave */
    float    mmwave_distance_cm;   /** Distance to nearest target */
    uint8_t  mmwave_target_count;  /** Number of detected targets */
    bool     mmwave_person_present;/** mmWave presence state */
} mmwave_state_t;

Supported Sensors

Sensor Frequency Capabilities UART Protocol Cost
Seeed MR60BHA2 60 GHz HR, BR, presence, illuminance Seeed proprietary frames ~$15
Seeed MR60FDA1 60 GHz Fall detection, presence Seeed proprietary frames ~$15
Seeed MR24HPC1 24 GHz Presence, sleep stage, distance Seeed proprietary frames ~$10
HLK-LD2410 24 GHz Presence, distance (motion + static) HLK binary protocol ~$3
HLK-LD2450 24 GHz Multi-target tracking (x,y,speed) HLK binary protocol ~$5

Fusion Algorithms

1. Vital Sign Fusion (Kalman filter)

mmWave HR (high confidence, 1 Hz) ─┐
                                    ├─► Kalman fuse → fused HR ± confidence
CSI-derived HR (lower confidence)  ─┘

2. Fall Detection (dual-confirm)

CSI phase accel > thresh ──────┐
                               ├─► AND gate → confirmed fall (near-zero false positives)
mmWave range-velocity pattern ─┘

3. Presence Validation

CSI adaptive threshold ────┐
                           ├─► Weighted vote → robust presence
mmWave target count > 0 ──┘

4. Training Calibration

mmWave ground-truth vitals → train CSI BVP extraction model
mmWave distance → calibrate CSI triangulation
mmWave micro-Doppler → label CSI activity patterns

Vitals Packet Extension

Extend the existing 32-byte vitals packet (magic 0xC5110002) with a new 48-byte fused packet:

typedef struct __attribute__((packed)) {
    /* Existing 32-byte vitals fields */
    uint32_t magic;            /* 0xC5110004 (fused vitals) */
    uint8_t  node_id;
    uint8_t  flags;            /* Bit0=presence, Bit1=fall, Bit2=motion, Bit3=mmwave_present */
    uint16_t breathing_rate;   /* Fused BPM * 100 */
    uint32_t heartrate;        /* Fused BPM * 10000 */
    int8_t   rssi;
    uint8_t  n_persons;
    uint8_t  mmwave_type;      /* Sensor type enum */
    uint8_t  fusion_confidence;/* 0-100 fusion quality score */
    float    motion_energy;
    float    presence_score;
    uint32_t timestamp_ms;
    /* New mmWave fields (16 bytes) */
    float    mmwave_hr_bpm;    /* Raw mmWave heart rate */
    float    mmwave_br_bpm;    /* Raw mmWave breathing rate */
    float    mmwave_distance;  /* Distance to nearest target (cm) */
    uint8_t  mmwave_targets;   /* Target count */
    uint8_t  mmwave_confidence;/* mmWave signal quality 0-100 */
    uint16_t reserved;
} edge_fused_vitals_pkt_t;

_Static_assert(sizeof(edge_fused_vitals_pkt_t) == 48, "fused vitals must be 48 bytes");

NVS Configuration

New provisioning parameters:

python provision.py --port COM7 \
  --mmwave-uart-tx 17 --mmwave-uart-rx 18 \  # UART pins for mmWave module
  --mmwave-type auto \                         # auto-detect, or: mr60bha2, ld2410, etc.
  --fusion-mode kalman \                       # kalman, vote, mmwave-primary, csi-primary
  --fall-dual-confirm true                     # require both CSI + mmWave for fall alert

Implementation Phases

Phase Scope Effort
Phase 1 UART driver + MR60BHA2 parser + auto-detection 2 weeks
Phase 2 Fused vitals packet + Kalman vital sign fusion 1 week
Phase 3 Dual-confirm fall detection + presence voting 1 week
Phase 4 HLK-LD2410/LD2450 support + multi-target fusion 2 weeks
Phase 5 RuVector calibration pipeline (mmWave as ground truth) 3 weeks
Phase 6 Server-side fusion for separate CSI + mmWave nodes 2 weeks

Consequences

Positive

  • Near-zero false positive fall detection (dual-confirm)
  • Clinical-grade vital signs when mmWave is present, with CSI as fallback
  • Self-calibrating CSI pipeline using mmWave ground truth
  • Backward compatible — existing CSI-only nodes work unchanged
  • Low incremental cost (~$3-15 per mmWave module)
  • Auto-detection means zero configuration for supported sensors
  • RuVector attention/solver/temporal-tensor modules gain a high-quality reference signal

Negative

  • Added firmware complexity (~2-3 KB RAM for mmWave state + UART buffer)
  • mmWave modules require line-of-sight (complementary to CSI, not replacement)
  • Multiple UART protocols to maintain (Seeed, HLK families)
  • 48-byte fused packet requires server parser update

Neutral

  • ESP32-C6 cannot run the full CSI pipeline (single-core RISC-V) but can serve as a dedicated mmWave bridge node
  • mmWave modules add ~15 mA power draw per node