docs(adr): ADR-103 — learned multi-person counter (SOTA path)#693
Merged
Conversation
Motivated by #499 (multi-node double-skeletons) which PR #491 stopped the bleeding on but didn't take to the WiFi-CSI literature's state of the art. Designs a learned counter that replaces today's slot heuristic + dedup_factor knob, reusing the primitives we've already shipped this week: * Candle / RTX 5080 training pipeline (proven yesterday, 2.1 s for 400 epochs on pose_v1.safetensors) * HF presence encoder as initialization (architectures compatible, unlike the pose head case) * ruvector-mincut (Stoer-Wagner) for multi-node fusion upper-bound * Cog packaging spec (ADR-100) + edge module registry (ADR-102) * Paired-data pipeline (PR #641 streaming-safe align-ground-truth.js) — `n_persons` labels come for free; no new data collection campaign required to bootstrap. Architecture: per-node CSI [56×20] -> frozen HF encoder -> 128-dim embedding \ > count head (softmax {0..7}) > confidence head (sigmoid) N nodes' distributions -> confidence-weighted log-sum -> Stoer-Wagner min-cut upper-bound clip -> { count, confidence, count_p95_low, count_p95_high, per_node_breakdown } Compares the proposal explicitly against WiCount / DeepCount / CrossCount / HeadCount published numbers and is honest about the hardware gap (their 3x3 MIMO research NICs vs our 1x1 SISO ESP32-S3). v0.1.0 acceptance gates target >=80% within-+/-1 same-room and >=60% cross-room — modest on purpose; bounded by the same paired- data scarcity #645 documents for pose. The framework is the deliverable; the accuracy follows the data. Includes: * Architecture diagram in ascii * Comparison table vs published WiFi-CSI counting SOTA * Per-failure-mode mapping from #499 symptoms to how the learned counter addresses each * v0.1.0 + v0.2.0 acceptance gates with measurable thresholds * Repo layout for the new `v2/crates/cog-person-count/` crate * Five-step migration plan from this ADR -> first GCS release Status: Proposed. Implementation follows in the same incremental pattern ADR-101 used: scaffold-cog PR -> train+publish PR -> server-wiring PR.
ruvnet
added a commit
that referenced
this pull request
May 21, 2026
…al (#697) Phase 4 of ADR-103. Adds the long-running polling loop so the cog's fourth verb (`run`) does real work, completing the ADR-100 runtime contract end-to-end: cog-person-count version → "person-count 0.3.0" cog-person-count manifest → JSON skeleton cog-person-count health → loads weights + 1-shot infer + emit cog-person-count run --config → long-running per-frame emit ← THIS What ships: * src/runtime.rs (new) — `run_loop` polls sensing_url every poll_ms, slides a [56, 20] CSI window, runs InferenceEngine::infer, emits publisher::person_count events. Same shape as cog-pose-estimation::runtime — fetch_frame extracts amplitudes from `snapshot.nodes[0].amplitude[]`, fails open on connect errors with a WARN log rather than crashing. * src/lib.rs — registers the runtime module. * src/main.rs — cmd_run now loads RunConfig from a JSON file, builds the InferenceEngine (with weights if cfg.model_path is set, otherwise auto-discover), emits a run.started event, and hands off to the Tokio multi-thread runtime's block_on(run_loop). Single-node fusion is a no-op for N=1 today; v0.2.0 will append predictions from sibling nodes and call fusion::fuse_confidence_weighted before emit. Verified locally: cargo check -p cog-person-count --no-default-features → clean cargo test -p cog-person-count → 15/15 pass (no regressions) cargo build -p cog-person-count --release → 2.36 MB unchanged ./cog-person-count run --config bad-config.json: line 1: {"event":"run.started","fields":{"cog":"person-count", "sensing_url":"http://127.0.0.1:9999/...",poll_ms:100, "model_path":"(auto-discover)"}} line 2: WARN sensing-server fetch failed error=Connection Failed: Connect error: actively refused (loop alive — exits cleanly on SIGTERM, no crash, no NaN) Also adds a "Relationship to the in-process score_to_person_count heuristic" section to cog/README.md explaining the dual-emitter design (sensing-server keeps emitting the PR #491 slot heuristic; the cog runs out-of-process and emits person.count events from the learned model). Operators choose by installing the cog or not — no sensing-server rebuild required. ADR-103 §"Migration" status: 1. Land ADR + scaffold ........... done (#693, #694) 2. Train count_v1 ................ done (#695) 3. Cross-compile + sign + GCS .... done (#696) 4. Server-side wiring ............ done — out-of-process design means no rewire needed; this cog is the wiring. 5. v0.2.0 multi-room + LoRA ...... data-bound (#645)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposes a learned multi-person counter cog (
cog-person-count) that closes the conceptual loop opened by #499 — turns multi-person counting from a heuristic + runtime knob into a learned task, reusing the Candle / HF-encoder / Cog-packaging primitives we shipped this week.Headline architecture: per-node CSI → HF encoder → count softmax {0..7} + confidence sigmoid → multi-node confidence-weighted log-sum + Stoer-Wagner min-cut upper-bound clip →
{count, confidence, count_p95_low, count_p95_high}.Compares directly against published WiFi-CSI counting SOTA (WiCount 89%/MIMO, DeepCount 92%/MIMO, CrossCount 84% cross-room, HeadCount <1 MAE/MIMO) and is explicit about the hardware gap. v0.1.0 acceptance: ≥80% within-±1 same-room / ≥60% cross-room.
Notable: count labels come for free from the existing
collect-ground-truth.pypipeline (recordsn_personsper frame), so this is not data-bound to bootstrap — can train tomorrow on the 1,077 samples that producedpose_v1yesterday.Status: Proposed. This PR lands only the ADR. Implementation follows in incremental PRs per ADR-101's pattern.
🤖 Generated with claude-flow