Skip to content

Predictive coast: LOST tracks follow damped pre-loss velocity#142

Merged
TCVinNYC merged 2 commits into
mainfrom
fix/tracker-predictive-coast
Jul 3, 2026
Merged

Predictive coast: LOST tracks follow damped pre-loss velocity#142
TCVinNYC merged 2 commits into
mainfrom
fix/tracker-predictive-coast

Conversation

@TCVinNYC

@TCVinNYC TCVinNYC commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

  • Problem (verified root cause of the 2026-06 "camera bounce" verdict): LOST tracks froze their bbox and emitted velocity=(0,0); the first re-detection after an N-frame gap reported the whole gap as one frame of motion (measured 40 px/frame for a 3-frame gap at a true 10 px/frame) — no lead through occlusion, whip on re-acquire.
  • Fix (autoptz/engine/pipeline/track.py): _TrackRecord carries an EMA velocity (α=0.5, ~3-frame window); LOST tracks coast their bbox along per-frame-decayed velocity (decays to 10% after 1 s → bounded total drift ≈0.45 s of pre-loss motion), centre clamped to frame bounds, prev_c* follows the coast so re-acquire has no velocity spike, and the re-acquire frame emits the EMA instead of the gap-spanning diff. Matches upstream ByteTrack semantics (source-verified).
  • Also records the 2026-06-30 post-estimator 8×1080p30 model-server 30-min gate artifact that was left untracked.

Test plan

  • tests/test_track.py::TestPredictiveCoast — 7 new tests, written RED-first (the spike test failed with vx=40.0 before the fix).
  • Full suite: 1921 passed, 1 skipped locally (the 1 unrelated failure is the LAN-dependent discovery test, fixed separately in Isolate wsdiscovery-unavailable test from the real package #141).
  • ruff + mypy (runtime/config) clean.

Validation gate (do not merge on CI alone)

Per project policy this changes tracker behavior → needs a Mark GUI 8-NDI A/B (lost_event_count / reacquire / id_switch vs main) and explicit approval. This PR isolates the tracker-side effect; the worker-side coast-follow (R-1) comes next and gets the real-camera occlusion walk.

🤖 Generated with Claude Code

TCVinNYC and others added 2 commits July 3, 2026 12:25
LOST tracks were emitted with a frozen bbox and velocity=(0,0), so the
controller lost all lead during occlusions and the first re-detection
reported the whole gap as one frame of motion (40 px/frame for a
3-frame gap at 10 px/frame true motion) — the PTZ re-acquire whip.
Upstream ByteTrack/BoT-SORT Kalman-predict lost tracks with positional
velocity intact; the wrapper now does the same at its level:

- _TrackRecord keeps an EMA velocity (~3-frame window) so one noisy
  detection can't dominate the coast direction.
- LOST tracks coast their bbox along that velocity, damped to 10%/s so
  a wrong guess drifts at most ~half a second of pre-loss motion;
  centre clamped to frame bounds; box size preserved.
- prev-centre follows the coast, and the re-acquire frame emits the
  smoothed velocity, so re-detection after a gap no longer spikes the
  feed-forward.

Also gitignore /fable/ (local audit workspace).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TCVinNYC

TCVinNYC commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Headless Mark A/B (M4 Pro, full profile, 8-cam cap, quiet machine):

leg score sustained min fps @8 app-induced drops id_switches
A = main @ 33b8973 7.96 8 cams 29.9 0 0
B = coast + R-1 @ d103bfc 7.99 8 cams 29.9 0 0

No throughput/drop/id-switch regression. Note: occlusion-quality metrics (lost_event_count/reacquire) are 0 in BOTH legs — the current synthetic Mark scenario has no occlusion events (that's the designed-but-unbuilt M-A scenario from the Mark spec), so the coast improvement is evidenced by the RED-first unit tests (re-acquire spike 40→<25 px/frame) and needs the V1 real-camera walk-behind-occluder for camera-level confirmation.

@TCVinNYC TCVinNYC merged commit 7c8b3be into main Jul 3, 2026
3 checks passed
@TCVinNYC TCVinNYC deleted the fix/tracker-predictive-coast branch July 3, 2026 19:44
TCVinNYC added a commit that referenced this pull request Jul 3, 2026
…148)

Reopens #143 (auto-closed when its stacked base merged as #142);
identical change, rebased onto main — now just the R-1 commit.

## Summary
Worker-side half of predictive tracking (tracker-side coast merged in
#142):
- `_maybe_track` publishes the **locked target's** LOST track (coasted
bbox + decayed velocity); all other LOST tracks stay filtered.
- `_drive_ptz_auto` follows a lost target iff `hypot(vx, vy) >= 1.0
px/frame` (`_COAST_FOLLOW_MIN_V`, module constant). Velocity decay makes
it self-limiting: a mover is followed ≲1 s through occlusion then falls
back to hold→coast→search; a stationary occluded subject is never
followed.
- Exactly one TrackInfo carries the target id during coast (no
held-duplicate) — test-pinned.

## Evidence
- 7 RED-first tests (tests/test_worker_coast_follow.py); focused suites
re-run green after rebase.
- Task review: Approved, 0 Critical/Important findings.
- Headless Mark A/B (posted on #142/#143): no throughput/drop/id-switch
regression; coast improvement quantified by unit tests (re-acquire spike
40→<25 px/frame); GUI/desktop validation session next.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant