feat(v1): relax fall sensitivity for low overhead cameras

Adds configurable event.require_rapid_drop (default off), require_lower_body
(default off) and horizontal_angle_threshold_degrees (default 45). With the
lenient defaults a sustained horizontal pose alone enters SUSPECT and confirms
after the confirm window, which is now the main false-positive guard; missing
knees/ankles no longer reject the pose. Thresholds flow into config_version.
End-to-end smoke confirms a lying pose; 70 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-21 23:29:16 +08:00
co-authored by Claude Opus 4.8
parent 5f29a8e60f
commit b1f0e8aeb4
12 changed files with 104 additions and 12 deletions
+11 -3
View File
@@ -9,8 +9,11 @@ from v1.fall_state import Evidence, FallState
class FallEvidencePolicy:
"""Require a recent rapid drop before a horizontal pose becomes a candidate."""
def __init__(self, suspect_window_seconds: float) -> None:
def __init__(
self, suspect_window_seconds: float, require_rapid_drop: bool = True
) -> None:
self._suspect_window_seconds = float(suspect_window_seconds)
self._require_rapid_drop = bool(require_rapid_drop)
self._rapid_drop_at: Dict[str, float] = {}
def evaluate(
@@ -30,8 +33,13 @@ class FallEvidencePolicy:
candidate = False
if state is FallState.SUSPECT:
candidate = pose_evidence.horizontal_pose
elif pose_evidence.horizontal_pose and track_id in self._rapid_drop_at:
candidate = timestamp - self._rapid_drop_at[track_id] <= self._suspect_window_seconds
elif pose_evidence.horizontal_pose:
if not self._require_rapid_drop:
candidate = True
elif track_id in self._rapid_drop_at:
candidate = (
timestamp - self._rapid_drop_at[track_id] <= self._suspect_window_seconds
)
recovery = (
state in (FallState.CONFIRMED, FallState.RECOVERING)