feat(server): accuracy sprint 001 — Kalman tracker, multi-node fusion, eigenvalue counting#20
Open
taylorjdawson wants to merge 12 commits intomainfrom
Open
feat(server): accuracy sprint 001 — Kalman tracker, multi-node fusion, eigenvalue counting#20taylorjdawson wants to merge 12 commits intomainfrom
taylorjdawson wants to merge 12 commits intomainfrom
Conversation
…uvnet#295) The person-count heuristic was causing widespread flickering (ruvnet#237, ruvnet#249, ruvnet#280, ruvnet#292) because: 1. Threshold 0.50 for 2-persons was too low — multipath reflections in small rooms easily exceeded it 2. No actual hysteresis despite the comment claiming asymmetric thresholds 3. EMA smoothing (α=0.15) was too responsive to transient spikes Changes: - Raise up-thresholds: 1→2 persons at 0.65 (was 0.50), 2→3 at 0.85 (was 0.80) - Add true hysteresis with asymmetric down-thresholds: 2→1 at 0.45, 3→2 at 0.70 - Track prev_person_count in SensingState for state-aware transitions - Increase EMA smoothing to α=0.10 (~2s time constant at 20 Hz) - Update all 4 call sites (ESP32, Windows WiFi, multi-BSSID, simulated) Fixes ruvnet#292, ruvnet#280, ruvnet#237 Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
4-phase plan to upgrade core ruvector dependencies and adopt new crates: - Phase 1: Bump 5 core crates 2.0.4→2.0.5 (10-30% mincut perf, security fixes) - Phase 2: Add ruvector-coherence for spectral multi-node CSI coherence - Phase 3: Add SONA adaptive learning to replace manual logistic regression - Phase 4: Evaluate ruvector-core ONNX embeddings for CSI pattern matching Co-Authored-By: claude-flow <ruv@ruv.net>
…net#300) The source field was set to "esp32" on the first UDP frame but never reverted when frames stopped arriving. This caused the UI to show "Real hardware connected" indefinitely after powering off all nodes. Changes: - Add last_esp32_frame timestamp to AppStateInner - Add effective_source() method with 5-second timeout - Source becomes "esp32:offline" when no frames received within 5s - Health endpoint shows "degraded" instead of "healthy" when offline - All 6 status/health/info API endpoints use effective_source() Fixes ruvnet#297 Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
Updated project description to include Cognitum.One.
The README Quick Start tells users to `pip install wifi-densepose` and then `from wifi_densepose import WiFiDensePose`, but no `wifi_densepose` Python package existed — only `v1/src`. This adds a top-level `wifi_densepose/` package with a WiFiDensePose facade class matching the documented API, and updates pyproject.toml to include it in the distribution. Closes ruvnet#314
…uvnet#321, ruvnet#323) * fix(firmware,server): watchdog crash on busy LANs + no detection from edge vitals (ruvnet#321, ruvnet#323) **Firmware (ruvnet#321):** edge_dsp task now batch-limits frame processing to 4 frames before a 10ms yield. On corporate LANs with high CSI frame rates, the previous 1-tick-per-frame yield wasn't enough to prevent IDLE1 starvation and task watchdog triggers. **Sensing server (ruvnet#323):** When ESP32 runs the edge DSP pipeline (Tier 2+), it sends vitals packets (magic 0xC5110002) instead of raw CSI frames. Previously, the server broadcast these as raw edge_vitals but never generated a sensing_update, so the UI showed "connected" but "0 persons". Now synthesizes a full sensing_update from vitals data including classification, person count, and pose generation. Closes ruvnet#321 Closes ruvnet#323 Co-Authored-By: claude-flow <ruv@ruv.net> * fix(firmware): address review findings — idle busy-spin and observability - Fix pdMS_TO_TICKS(5)==0 at 100Hz causing busy-spin in idle path (use vTaskDelay(1) instead) - Post-batch yield now 2 ticks (20ms) for genuinely longer pause - Add s_ring_drops counter to ring_push for diagnosing frame drops - Expose drop count in periodic vitals log line Co-Authored-By: claude-flow <ruv@ruv.net> * fix(server): set breathing_band_power for skeleton animation from vitals When presence is detected via edge vitals, set breathing_band_power to 0.5 so the UI's torso breathing animation works. Previously hardcoded to 0.0 which made the skeleton appear static even when breathing rate was being reported. Co-Authored-By: claude-flow <ruv@ruv.net>
List specific known issues (multi-node detection, training plateau, no pre-trained weights, hardware compatibility) to set expectations for new users. Co-Authored-By: claude-flow <ruv@ruv.net>
…vnet#249) Documents the architectural change from single shared state to per-node HashMap<u8, NodeState> in the sensing server. Includes scaling analysis (256 nodes < 13 MB), QEMU validation plan, and aggregation strategy. Also links README hero image to the explainer video. Co-Authored-By: claude-flow <ruv@ruv.net>
) * docs(adr): ADR-068 per-node state pipeline for multi-node sensing (ruvnet#249) Documents the architectural change from single shared state to per-node HashMap<u8, NodeState> in the sensing server. Includes scaling analysis (256 nodes < 13 MB), QEMU validation plan, and aggregation strategy. Also links README hero image to the explainer video. Co-Authored-By: claude-flow <ruv@ruv.net> * feat(server): per-node state pipeline for multi-node sensing (ADR-068, ruvnet#249) Replaces the single shared state pipeline with per-node HashMap<u8, NodeState>. Each ESP32 node now gets independent: - frame_history (temporal analysis) - smoothed_person_score / prev_person_count - smoothed_motion / baseline / debounce state - vital sign detector + smoothing buffers - RSSI history Multi-node aggregation: - Person count = sum of per-node counts for active nodes (seen <10s) - SensingUpdate.nodes includes all active nodes - estimated_persons reflects cross-node aggregate Single-node deployments behave identically (HashMap has one entry). Simulated data path unchanged for backward compatibility. Closes ruvnet#249 Refs ruvnet#237, ruvnet#276, ruvnet#282 Co-Authored-By: claude-flow <ruv@ruv.net>
Complements ruvnet#326 (per-node state pipeline) with additional features: - Dynamic adaptive classifier: discover activity classes from training data filenames instead of hardcoded array. Users add classes via filename convention (train_<class>_<desc>.jsonl), no code changes. - Per-node UI cards: SensingTab shows individual node status with color-coded markers, RSSI, variance, and classification per node. - Colored node markers in 3D gaussian splat view (8-color palette). - Per-node RSSI history tracking in sensing service. - XSS fix: UI uses createElement/textContent instead of innerHTML. - RSSI sign fix: ensure dBm values are always negative. - GET /api/v1/nodes endpoint for per-node health monitoring. - node_features field in WebSocket SensingUpdate messages. - Firmware watchdog fix: yield after every frame to prevent IDLE1 starvation. Addresses ruvnet#237, ruvnet#276, ruvnet#282 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, eigenvalue counting Wire three existing signal-crate components into the live sensing path: Step 1 — Kalman Tracker (tracker_bridge.rs): - PoseTracker from wifi-densepose-signal wired into all 5 mutable derive_pose_from_sensing call sites - Stable TrackId-based person IDs replace ephemeral 0-based indices - Greedy Mahalanobis assignment with proper lifecycle transitions (Tentative → Active → Lost → Terminated) - Kalman-smoothed keypoint positions reduce frame-to-frame jitter Step 2 — Multi-Node Fusion (multistatic_bridge.rs): - MultistaticFuser replaces naive .sum() aggregation at both ESP32 paths - Attention-weighted CSI fusion across nodes with cosine-similarity weights - Fallback uses max (not sum) to avoid double-counting overlapping coverage - Node positions configurable via --node-positions CLI arg - Single-node passthrough preserved (min_nodes=1) Step 3 — Eigenvalue Person Counting (field_model.rs upgrade): - Full covariance matrix accumulation (replaces diagonal variance approx) - True eigendecomposition via ndarray-linalg Eigh (Marcenko-Pastur threshold) - estimate_occupancy() for runtime eigenvalue-based counting - Calibration API: POST /calibration/start|stop, GET /calibration/status - Graceful fallback to score_to_person_count when uncalibrated New files: tracker_bridge.rs, multistatic_bridge.rs, field_bridge.rs Modified: sensing-server main.rs, Cargo.toml; signal field_model.rs, Cargo.toml Refs: .swarm/plans/accuracy-sprint-001.md Co-Authored-By: claude-flow <ruv@ruv.net>
Critical fixes: - C1: FieldModel created with n_links=1 (single_link_config) so feed_calibration/extract_perturbation no longer get DimensionMismatch - C2: variance_explained now uses centered covariance trace (E[x²]-E[x]²) matching mode_energies normalization - C3: MP ratio uses total_obs = frames * links for consistent threshold between calibration and runtime - C4: Noise estimator filters to positive eigenvalues only, preventing collapse to ~0 on rank-deficient matrices (p > n) - C5: ESP32 paths gate total_persons on presence — empty room reports 0 High fixes: - H1: Bounding box computed from observed keypoints only (confidence > 0), preventing collapse from centroid-filled unobserved slots - H2: fuse_or_fallback returns Option<usize> instead of sentinel 0, eliminating type ambiguity between "fusion succeeded" and "zero people" - H3: Monotonic epoch-relative timestamps replace wall-clock/Instant mixing, preventing spurious TimestampMismatch on NTP steps - H5: ndarray-linalg gated behind "eigenvalue" feature flag (default=on), diagonal fallback used with --no-default-features Moderate fixes: - M1: calibration_start guards against replacing Fresh calibration - M2: parse_node_positions logs warning for malformed entries Co-Authored-By: claude-flow <ruv@ruv.net>
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.
Summary
Wire three existing signal-crate components into the live sensing path to replace heuristic person counting with physics-grounded approaches:
tracker_bridge.rs): Stable person IDs viaPoseTrackerwith greedy Mahalanobis assignment, proper lifecycle transitions (Tentative→Active→Lost→Terminated), and Kalman-smoothed keypoints — eliminates frame-to-frame jitter and ephemeral 0-based IDsmultistatic_bridge.rs):MultistaticFuserreplaces naive.sum()of per-node person counts with attention-weighted CSI fusion — two nodes seeing one person now reports 1 (not 2). Fallback usesmaxto avoid double-counting overlapping coveragefield_model.rsupgrade): Full covariance matrix +ndarray-linalgeigendecomposition with Marcenko-Pastur noise threshold replaces diagonal variance approximation.estimate_occupancy()for runtime counting, calibration API (/calibration/start|stop|status)Upstream ADR Alignment
Also submitted upstream as ruvnet#341.
Changes
New files
sensing-server/src/tracker_bridge.rssensing-server/src/multistatic_bridge.rssensing-server/src/field_bridge.rsModified files
sensing-server/Cargo.tomlwifi-densepose-signaldependencysensing-server/src/main.rssignal/Cargo.tomlndarray-linalgdependencysignal/src/ruvsense/field_model.rsestimate_occupancy(),baseline_eigenvalue_countTest plan
cargo test -p wifi-densepose-signal --no-default-features— existing 49+ tests + 4 new field_model testscargo test -p wifi-densepose-sensing-server— existing server tests + new bridge testscargo test --workspace --no-default-features— full 1031+ test suite greenPOST /calibration/start, wait,POST /calibration/stop— verify baseline eigenvalue countRefs:
.swarm/plans/accuracy-sprint-001.md🤖 Generated with claude-flow