# WiFlow Browser Trainer · calibrate → capture → train → infer, in your camera's frame

0 CALIBRATE
→
1 CAPTURE
→
2 TRAIN
→
3 INFER
empty-room baseline (ADR-151) — step OUT of the space
not detected
get-ready countdown s
camera: off
baseline
CSI source—
statusNOT CALIBRATED
frames in baseline0
age—
The room's static WiFi channel is mostly constant. We capture ~10 s of the quiescent field (you OUT of the space) and compute a per-feature running mean + std (Welford) over the 410-d CSI vector. Afterwards every CSI vector is expressed as deviation from baseline: `x_norm = (x − base_mean) / (base_std + ε)` — applied consistently in capture, train, and infer. This makes a body's perturbation stand out from the static channel. You must calibrate before capturing.
laptop camera MediaPipe skeleton = GROUND TRUTH (the label)
stand still
—
camera: off
guided capture
CSI source—
CSI nodes—
pose visibility—
pass / activity— / —
this activity samples0
total samples0
est. time / samples—
last skip reason—
passes sec / activity get-ready s
per-pose coverage (balance the dataset)
A pair is recorded only when BOTH (a) a confident MediaPipe pose (mean visibility > 0.5) AND (b) a fresh live CSI frame (`source==esp32`) exist. We store the baseline-normalized CSI + the 17 keypoints, mirrored to IndexedDB so a refresh keeps them. The routine runs multiple interleaved passes through every activity, so a chronological held-out split contains the same activity mix as training (fixes the OOD split). Follow the prompt so every pose bucket fills up.
train (TensorFlow.js)
total samples0
train / val split— / — (chronological 80/20)
epoch0
train MSE—
val MSE—
held-out PCK@0.10—
held-out PCK@0.05—
held-out MPJPE—
mean-pose baseline PCK@0.10—
epochs patience
no model yet — calibrate, capture, then train.
The bar to beat is the mean-pose baseline (predict the train-mean pose for everything). A model that doesn't clear it has learned no usable CSI→pose signal — this page says so plainly. Inputs are standardized on the train split only (after baseline-normalization); the val split is the chronological last 20%, never trained on.
loss curve — train (amber) vs val (blue)
Idle.
WiFi-inferred pose CSI ONLY — no camera in the loop
hide camera (skeleton on black)
camera: off
no model loaded
live inference
CSI source—
CSI nodes—
presence—
infer fps—
measured held-out PCK@0.10—
This skeleton is inferred from WiFi CSI only (baseline-normalized, then through the model). It is coarse — the held-out PCK above is the real number. It is drawn over the same laptop-camera frame it trained in, so it aligns with the image. Same person / room / session — not validated cross-day or through-wall.