feat(examples): real live WiFi-CSI through-wall sensing demo

Self-contained Three.js r128 demo at examples/through-wall/ that renders
ONLY genuine data streamed from the running sensing-server over
ws://localhost:8765/ws/sensing. No simulation, no fabricated frames, no
fake skeleton.

Renders, driven by real /ws/sensing frames:
- 20x20 signal_field floor heatmap (real values)
- coarse RF-localization puck from persons[0].position (labeled coarse,
  NOT pose; peak signal_field cell as fallback)
- live motion/breathing/variance/rssi bars + motion sparkline
- presence/confidence/estimated_persons/active-node/tick/Hz meters
- 3D room with wall + doorway dividing office (node 9) / hallway (node 13)
- honest mutually-exclusive banner: LIVE (source=esp32) / SIMULATED /
  NO SERVER, showing the real source verbatim
- optional webcam tile (ground-truth-when-visible, separate from CSI)

Reuses scene/lights/bloom/CSS + webcam path from
examples/three.js/demos/05-skinned-realtime.html, the floor-heatmap idea
from ui/observatory/js/, and the threaded no-cache server from
examples/three.js/server/serve-demo.py (serve.py on :8080).

Verified against the live server: real frame source=esp32, nodes [9,13],
400 signal_field values, persons[0].position present. Python proof PASS.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-06-15 16:03:22 -04:00
parent cafbeb1e81
commit ebe217569b
3 changed files with 827 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# Through-Wall WiFi Sensing Demo (LIVE CSI — no simulation, no fake skeleton)
A self-contained 3D demo that renders **only real data** streamed from the
running `wifi-densepose-sensing-server`, which ingests genuine WiFi Channel
State Information (CSI) from a live ESP32-S3 node over UDP.
It honestly shows what WiFi CSI sensing actually delivers:
- **motion** and **presence** — does the RF field say someone is here and moving?
- a **coarse RF localization** marker — roughly *where* the energy is, in metres.
- a **20×20 signal-field heatmap** on the floor — the live "where is the motion" map.
…and it shows all of this **through drywall**. That is the real wow of WiFi
sensing — not skeletal pose.
## What this is NOT
- **Not a skeleton / pose.** The sensing-server's `persons[].keypoints` carry
`confidence: 0.0` (they are image-pixel placeholders, not real 3D joints), so
this demo never draws them. WiFi CSI here gives motion / presence / coarse
position — that is the honest output, and we render exactly that.
- **Not a simulation.** If the server is sending `source: "simulated"`, the
banner says **SIMULATED — not real** in orange. If the server is unreachable,
the page shows **NO SERVER** with start instructions. It never invents frames.
## What it renders (all driven by real `/ws/sensing` frames)
| Element | Real field used |
|---|---|
| Floor heatmap (20×20 tiles) | `signal_field.values` (400 floats ~0..1) |
| Coarse localization puck | `persons[0].position` `[x,0,z]` (peak cell as fallback) |
| Motion / breathing / variance / RSSI bars | `features.*` |
| Presence / motion level / confidence | `classification.*` |
| Estimated persons | `estimated_persons` |
| Active node markers | `nodes[].node_id` (node 9 = office, node 13 = hallway) |
| Update rate (Hz) | measured from frame arrival times |
| Status banner | `source` verbatim ("esp32" = LIVE) |
The 3D room is split by a **wall + doorway** into **OFFICE** (node 9) and
**HALLWAY** (node 13). Node markers light up only when that node actually
appears in the live `nodes` list.
## The through-wall story
WiFi (2.4/5 GHz) penetrates interior drywall. When you walk from the office
into the hallway — *behind the wall* — node 9's `signal_field` and
`motion_band_power` **still register the motion** even though there is a wall
between you and the antenna. That is real through-wall motion sensing on a
single node.
Once a **second ESP32-S3 is flashed and placed in the hallway** (node 13, the
`esp32-csi-node` firmware), the server fuses both nodes (multistatic) and the
hallway node localizes you on its side of the wall — true two-room through-wall
localization. With one node today you already get through-wall *motion*; the
second node adds *where*.
## How to run
### 1. Start the REAL sensing-server
```bash
cd v2
cargo build -p wifi-densepose-sensing-server
./target/debug/sensing-server.exe --ws-port 8765 --udp-port 5005
```
This is the process that ingests real CSI from the ESP32-S3 (UDP on 5005) and
serves the live WebSocket on `ws://localhost:8765/ws/sensing`. A real ESP32-S3
must be provisioned and streaming for `source` to read `esp32` (see the repo's
ESP32 firmware build/provision steps in `CLAUDE.local.md`).
### 2. Start the static server for this page
```bash
python examples/through-wall/serve.py
```
(Serves on **port 8080** — 8765 is the WebSocket, a different process.)
### 3. Open the page
```
http://localhost:8080/examples/through-wall/index.html
```
The page connects automatically. If you want to point at a server on another
host (e.g. an ESP32 streaming to a Pi), override the endpoint:
```
http://localhost:8080/examples/through-wall/index.html?ws=ws://192.168.1.20:8765/ws/sensing
```
## Optional: webcam ground-truth tile
The bottom-right tile can enable your webcam ("camera — ground truth when
visible"). This is **separate** from the CSI sensing — it is only there to let a
viewer confirm with their eyes what the WiFi is detecting. The WiFi works in the
dark and through walls; the camera does not. The sensing itself is the CSI.
## Honest scope
- Real: motion, presence, coarse position (incl. through drywall on the office
node), the live signal-field heatmap, RSSI, and a measured update rate.
- The coarse-localization puck is labeled **"RF localization (coarse)"** — it is
metre-scale, not centimetre pose. It uses `persons[0].position` when a person
is tracked, otherwise the peak cell of the live `signal_field`.
- Two-room (office + hallway) through-wall *localization* needs the hallway node
(node 13) flashed and placed; until then node 13 stays dimmed and the demo
shows single-node through-wall *motion*.
## Reused from existing examples
- 3D scene setup, lights, fog, post-processing bloom, dark amber CSS, and the
optional webcam path — from `examples/three.js/demos/05-skinned-realtime.html`.
- Floor-heatmap-on-the-grid idea and presence/field rendering — from
`ui/observatory/js/` (`presence-cartography.js`, `subcarrier-manifold.js`).
- Threaded no-cache static server — from
`examples/three.js/server/serve-demo.py`.