docs/adr/ADR-035-live-sensing-ui-accuracy.md
Accepted
2026-03-02
Issue #86 reported that the live demo shows a static/barely-animated stick figure and the sensing page displays inaccurate data, despite a working ESP32 sending real CSI frames. Investigation revealed three root causes:
--source simulated — even with a real ESP32 connected, the server generates synthetic sine-wave data instead of reading UDP frames.derive_pose_from_sensing() generates keypoints using sin(tick) math unrelated to actual signal content. No trained .rvf model is loaded by default.CSI_SOURCE changed from simulated to auto.auto probes UDP port 5005 for an ESP32; falls back to simulation if none found.CSI_SOURCE=esp32 docker-compose up.derive_pose_from_sensing() now reads actual sensing features:
motion_band_power drives limb splay and walking gait detection (> 0.55).breathing_band_power drives torso expansion/contraction phased to breathing rate.variance seeds per-joint noise so the skeleton moves independently.dominant_freq_hz drives lateral torso lean.change_points add burst jitter to extremity keypoints.pose_source field (signal_derived | model_inference) added to every WebSocket frame.VecDeque) added to AppStateInner..rvf model + 4+ ESP32 nodes.All four render modes in the pose visualization dropdown now produce distinct visual output:
| Mode | Rendering |
|---|---|
| Skeleton | Green lines connecting joints + red keypoint dots |
| Keypoints | Large colored dots with glow and labels, no connecting lines |
| Heatmap | Gaussian radial blobs per keypoint (hue per person), faint skeleton overlay at 25% opacity |
| Dense | Body region segmentation with colored filled polygons — head (red), torso (blue), left arm (green), right arm (orange), left leg (purple), right leg (yellow) |
Previously heatmap and dense were stubs that fell back to skeleton mode.
The pose_source field from the WebSocket message was being dropped in convertZoneDataToRestFormat() in pose.service.js. Now passed through so the Estimation Mode badge displays correctly.
docker/Dockerfile.rust — CSI_SOURCE=auto env, shell entrypoint for variable expansiondocker/docker-compose.yml — CSI_SOURCE=${CSI_SOURCE:-auto}, shell command stringwifi-densepose-sensing-server/src/main.rs — frame history buffer, Goertzel breathing estimation, temporal motion score, signal-driven pose derivation, pose_source field, 100ms tick defaultui/services/sensing.service.js — dataSource state, delayed simulation fallback, _simulated markerui/services/pose.service.js — pose_source passthrough in data conversionui/components/SensingTab.js — data source banner, "About This Data" cardui/components/LiveDemoTab.js — estimation mode badge, setup guide panel, dark mode themeui/utils/pose-renderer.js — heatmap (Gaussian blobs) and dense (body region segmentation) render modesui/style.css — banner, badge, guide panel, and about-text stylesREADME.md — live pose detection screenshotassets/screen.png — screenshot asset