move_detect/edge/detectors
유창욱 3240428380 fix(edge): require sustained non-prone before FallDetector recover (FIX 2)
edge/detectors/fall.py update(): a single frame where prone was false while
state was in {FALLING, FALLEN, NO_MOTION} immediately emitted "recover" and
reset _State. One jittery frame (a momentary pose-estimation miss) could
wipe an in-progress CRITICAL no_motion incident.

Add FallConfig.recover_grace_s (default 1.0s) and _State.not_prone_since.
On the first non-prone frame while in an incident state, start the grace
timer instead of recovering immediately; only reset to STANDING once
recover_grace_s of sustained non-prone has elapsed. A re-prone frame while
in an incident state clears the pending grace timer (cancels the recover
attempt). The non-incident STANDING path is unchanged.

BEHAVIOR CHANGE: recover now requires recover_grace_s of sustained
non-prone instead of firing on a single frame.

Tests: test_recovery_clears_state (tests/test_fall.py) strengthened per
review brief to assert a single standing frame does NOT recover but
sustained standing across the grace window does. Added
test_single_noisy_nonprone_frame_does_not_cancel_no_motion covering the
life-safety scenario: one noisy non-prone frame during NO_MOTION must not
cancel the incident.
2026-07-11 22:15:47 +09:00
..
__init__.py feat(edge): FallDetector state machine (OR-logic, baseline freeze, post-fall no-motion) 2026-07-11 20:41:11 +09:00
fall.py fix(edge): require sustained non-prone before FallDetector recover (FIX 2) 2026-07-11 22:15:47 +09:00
ghost.py feat(edge): GhostIncidentDetector (collapse-then-occluded spatial incident) 2026-07-11 20:59:31 +09:00
immobility.py feat(edge): ImmobilityWatchdog (posture-independent prolonged immobility) 2026-07-11 20:51:33 +09:00
zone.py feat(edge): ZoneIntrusionDetector (foot-anchor point-in-polygon, debounce) 2026-07-11 21:05:42 +09:00