feat: cross-node fusion + DynamicMinCut + RSSI tracking (v0.5.3)
Some checks failed
Continuous Deployment / Pre-deployment Checks (push) Has been cancelled
Continuous Integration / Code Quality & Security (push) Has been cancelled
Continuous Integration / Tests (push) Has been cancelled
Continuous Integration / Tests-1 (push) Has been cancelled
Continuous Integration / Tests-2 (push) Has been cancelled
Firmware CI / Build ESP32-S3 Firmware (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Has been cancelled
Security Scanning / Static Application Security Testing (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Container Security Scan (push) Has been cancelled
Security Scanning / Infrastructure Security Scan (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / License Compliance Scan (push) Has been cancelled
Security Scanning / Security Policy Compliance (push) Has been cancelled
Continuous Deployment / Deploy to Staging (push) Has been cancelled
Continuous Deployment / Deploy to Production (push) Has been cancelled
Continuous Deployment / Rollback Deployment (push) Has been cancelled
Continuous Deployment / Post-deployment Monitoring (push) Has been cancelled
Continuous Deployment / Notify Deployment Status (push) Has been cancelled
Continuous Integration / Performance Tests (push) Has been cancelled
Continuous Integration / Docker Build & Test (push) Has been cancelled
Continuous Integration / API Documentation (push) Has been cancelled
Continuous Integration / Notify (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been cancelled
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been cancelled
Security Scanning / Security Report (push) Has been cancelled

* feat(server): cross-node RSSI-weighted feature fusion + benchmarks

Adds fuse_multi_node_features() that combines CSI features across all
active ESP32 nodes using RSSI-based weighting (closer node = higher weight).

Benchmark results (2 ESP32 nodes, 30s, ~1500 frames):

  Metric               | Baseline | Fusion  | Improvement
  ---------------------|----------|---------|------------
  Variance mean        |    109.4 |    77.6 | -29% noise
  Variance std         |    154.1 |   105.4 | -32% stability
  Confidence           |    0.643 |   0.686 | +7%
  Keypoint spread std  |      4.5 |     1.3 | -72% jitter
  Presence ratio       |   93.4%  |  94.6%  | +1.3pp

Person count still fluctuates near threshold — tracked as known issue.

Verified on real hardware: COM6 (node 1) + COM9 (node 2) on ruv.net.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ui): add client-side lerp smoothing to pose renderer

Keypoints now interpolate between frames (alpha=0.25) instead of
jumping directly to new positions. This eliminates visual jitter
that persists even with server-side EMA smoothing, because the
renderer was drawing every WebSocket frame at full rate.

Applied to skeleton, keypoints, and dense body rendering paths.

Co-Authored-By: claude-flow <ruv@ruv.net>

* feat: DynamicMinCut person separation + UI lerp smoothing

- Added ruvector-mincut dependency to sensing server
- Replaced variance-based person scoring with actual graph min-cut on
  subcarrier temporal correlation matrix (Pearson correlation edges,
  DynamicMinCut exact max-flow)
- Recalibrated feature scaling for real ESP32 data ranges
- UI: client-side lerp interpolation (alpha=0.25) on keypoint positions
- Dampened procedural animation (noise, stride, extremity jitter)
- Person count thresholds retuned for mincut ratio

Co-Authored-By: claude-flow <ruv@ruv.net>

* docs: update CHANGELOG with v0.5.1-v0.5.3 releases

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
rUv 2026-03-30 21:55:44 -04:00 committed by GitHub
parent cd84c35f8f
commit 3733e54aef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 2548 additions and 32 deletions

View file

@ -56,10 +56,47 @@ export class PoseRenderer {
[11, 13], [12, 14], [13, 15], [14, 16] // Legs
];
// Client-side keypoint smoothing: lerp between frames to reduce jitter.
// Maps person index → array of {x, y} for each keypoint.
this._smoothedKeypoints = new Map();
this._lerpAlpha = 0.25; // 0 = frozen, 1 = instant (no smoothing)
// Initialize rendering context
this.initializeContext();
}
// Lerp a single value toward target
_lerp(current, target, alpha) {
return current + (target - current) * alpha;
}
// Get smoothed keypoint positions for a person
_getSmoothedKeypoints(personIdx, keypoints) {
if (!this.config.enableSmoothing || !keypoints || keypoints.length === 0) {
return keypoints;
}
let prev = this._smoothedKeypoints.get(personIdx);
if (!prev || prev.length !== keypoints.length) {
// First frame or keypoint count changed — initialize
prev = keypoints.map(kp => ({ x: kp.x, y: kp.y, z: kp.z || 0, confidence: kp.confidence, name: kp.name }));
this._smoothedKeypoints.set(personIdx, prev);
return keypoints;
}
const alpha = this._lerpAlpha;
const smoothed = keypoints.map((kp, i) => ({
...kp,
x: this._lerp(prev[i].x, kp.x, alpha),
y: this._lerp(prev[i].y, kp.y, alpha),
}));
// Update stored positions
this._smoothedKeypoints.set(personIdx, smoothed.map(kp => ({ x: kp.x, y: kp.y, z: kp.z || 0, confidence: kp.confidence, name: kp.name })));
return smoothed;
}
createLogger() {
return {
debug: (...args) => console.debug('[RENDERER-DEBUG]', new Date().toISOString(), ...args),
@ -150,18 +187,17 @@ export class PoseRenderer {
return; // Skip low confidence detections
}
console.log(`✅ [RENDERER] Rendering person ${index} with confidence: ${person.confidence}`);
// Apply client-side lerp smoothing to reduce visual jitter
const smoothedKps = this._getSmoothedKeypoints(index, person.keypoints);
// Render skeleton connections
if (this.config.showSkeleton && person.keypoints) {
console.log(`🦴 [RENDERER] Rendering skeleton for person ${index}`);
this.renderSkeleton(person.keypoints, person.confidence);
if (this.config.showSkeleton && smoothedKps) {
this.renderSkeleton(smoothedKps, person.confidence);
}
// Render keypoints
if (this.config.showKeypoints && person.keypoints) {
console.log(`🔴 [RENDERER] Rendering keypoints for person ${index}`);
this.renderKeypoints(person.keypoints, person.confidence);
if (this.config.showKeypoints && smoothedKps) {
this.renderKeypoints(smoothedKps, person.confidence);
}
// Render bounding box
@ -265,7 +301,7 @@ export class PoseRenderer {
persons.forEach((person, personIdx) => {
if (person.confidence < this.config.confidenceThreshold || !person.keypoints) return;
const kps = person.keypoints;
const kps = this._getSmoothedKeypoints(personIdx, person.keypoints);
bodyParts.forEach((part) => {
// Collect valid keypoints for this body part