9.8 KiB
9.8 KiB
T-101〜T-105 代码审核记录
- 日期:2026-07-21
- 范围:T-101(安全配置)、T-102(视频源)、T-103(Pose 适配器)、T-104(跟踪与证据)、T-105(时序状态机)
- 视角:全栈开发工程师
- 基线验证:
python -m pytest v1/tests -q→ 20 passed - 结论:单模块工艺整体偏高,问题集中在系统装配——五个 task 的单元测试各自通过,但当前拼不成一条能判摔倒的完整链路。进入 T-201 之前应先补第 1–3 项。
总体评价
- 模块职责与 04-architecture.md 的模块表严格对应;边界克制得当:Pose 不做业务结论、evidence 不报警、状态机不做副作用。
- 防御性校验到位:SHA-256 锁模型、
pose/person/17×3 契约校验、config 拒绝内嵌 RTSP 凭证。 - 依赖注入(
capture_factory/model_factory)使单元测试无需真实权重或摄像头,TDD 流程真实。 - 跨重连的时间戳单调性已正确处理(
VideoSource._last_timestamp不在重连时重置)。
问题清单(按优先级)
P1 · 两个 Evidence 类型没有桥接,摔倒判定核心逻辑缺失
v1/evidence.py产出PoseEvidence(horizontal_pose、rapid_vertical_change、horizontal_angle_degrees…)。v1/fall_state.py消费另一个Evidence(is_fall_candidate、is_recovery_candidate)。- 全仓库无任何代码把前者映射为后者。即"什么几何证据算
is_fall_candidate/is_recovery_candidate"这一真正的领域决策目前为空。 - 影响:看似完成度高,实际把最有业务风险的一环留在无人负责的缝隙。
- 建议:在 T-201 之前明确它落在
app.py还是evidence.py,并为该映射单独写正反例测试,不要混入 GUI task。
P2 · config 有两个字段被解析校验但从不消费(配置-现实漂移)
event.suspect_window_seconds与event.cooldown_seconds在v1/config.py完整解析并出现在config.example.json,但全仓库无消费点。cooldown_seconds尤其冲突:04-architecture.md 状态模型写"经配置冷却与恢复稳定站立 → RECOVERING",而fall_state.py中 CONFIRMED→RECOVERING 一收到 recovery 证据即转,无任何 cooldown。- 违反项目纪律"需求或代码现实变化时必须同步"。
- 建议:要么接线实现,要么从 config 与示例中删除,避免"看似可配、实则无效"的旋钮。
P3 · FallEvent 缺 config_version
- 04-architecture.md 要求 FallEvent 记录
config_version,使截图/JSONL 可追溯实际阈值。 - 当前
v1/fall_state.py的FallEvent无该字段。 - 建议:现在就在 dataclass 补上,避免 T-202 写 JSONL 时无法追溯。
P4 · 轨迹过期(2s) 与确认窗口(最长3s)互相打架
PersonTracker默认max_age_seconds=2.0,确认窗口可配到 3.0s。- 摔倒过程恰是遮挡、姿态最不稳、Pose 最易丢的时刻。Pose 丢失 >2s 时此人拿到新
track_id,状态机视为新人从 NORMAL 起算,之前累积的 SUSPECT 时间丢失 → 漏报。 - 建议:
max_age至少对齐/大于确认窗口,或让状态在短暂 track 断裂中存活。
P5 · 贪心最近邻跟踪,顺序依赖
_nearest_available_track按 pose 出现顺序逐个抢最近 track,非全局最优(非匈牙利匹配)。两人靠近/交叉时可能换 ID。- 固定机位、稀疏大厅场景大概率够用,属已知局限。
- 建议:在文档写明限制,避免后期误判为 bug;多人密集场景再考虑升级匹配算法。
P6 · 回放默认 reconnect=True 是使用陷阱
VideoSource默认reconnect=True面向 RTSP。对有限本地录像,EOF 时会无限重试重开(从第 0 帧重放)而非报 EOF。- 回归测试/smoke 必须显式传
reconnect=False。 - 建议:对文件路径做启发式默认,或在文档强调。
P7 · RTSP 时间戳可靠性未验证
- 状态机 1–3s 判定依赖
CAP_PROP_POS_MSEC,实况 RTSP 上该值常为 0 或乱跳。虽有 frame_index/fps 与 wall-clock 回退,但真实流上窗口计时准确性未验证。 - 建议:列为 T-203 关键风险,接入真实流后专项验证窗口计时。
建议处理顺序
- 先补 P1 / P2 / P3(属事件引擎本身,非 GUI 活),再开 T-201。
- P4 / P6 改动成本低,可顺手处理。
- P5 / P7 记入已知风险,分别在多人场景与 T-203 真实流阶段处置。
独立复核补充与处理建议
复核结论:上述 P1、P2、P5、P6、P7 的事实判断成立;P3、P4 也成立,但实现方式需要避免引入新的事件一致性或身份错配问题。当前 20 项测试均通过,只能证明各模块的局部契约,不能证明完整数据流能对摔倒作出正确判定。
P0 · 缺失 Pose 尚未中断“连续证据”
FallStateMachine.update()只会在某个track_id有输入时更新时间;跟踪器和状态机都没有“本帧该人员缺失”的显式合约。- 某人已进入
SUSPECT后,若短暂丢失 Pose、但仍未超过 tracker 的 2 秒过期时间,下一次以同一 ID 回来时,状态机可能把缺失的时间算入确认窗口。这与“倒地证据持续”的需求不符,可能造成误确认。 - 处理:事件管线必须为已知但本帧缺失或低质量的人员输入拒绝证据,或定义并测试独立的
max_evidence_gap_seconds;不得把 tracker 的身份保留时间当作证据连续时间。
对原建议的工程化校正
- P1:规则应落在领域层,不落在 GUI。 建议保留
evidence.py输出的几何事实,新增明确的领域规则/管线层,将每人连续的PoseEvidence映射为状态机Evidence。该层必须覆盖突然倒地、持续倒地、坐下、弯腰、捡物、短暂低姿态和短暂丢 Pose 的正反例。 - P2:先定义字段语义,再接线。
suspect_window_seconds应明确描述“突发下移到持续倒地”可接受的时间关系;cooldown_seconds应明确描述确认后何时允许进入恢复或产生下一事件。若不能定义可验收语义,应从公开配置中移除,不能保留无效旋钮。 - P3:先统一事件契约。
04-architecture.md要求FallEvent记录config_version,而api.md说 T-202 的报警记录补充配置版本,二者目前不一致。应将不可变的config_version纳入FallEvent或统一定义的事件记录,并同步 V1/V2 合约;不能只在 JSONL 写入时临时拼接。 - P4:不能只把
max_age_seconds调大。 这样可能在遮挡后把另一人错误延续为原 ID。身份保留、短时遮挡和证据连续性必须分别建模;P0 的缺失证据处理是其前提。 - P6:使用显式来源模式,而非隐式默认。 应由“录像回放”明确产生 EOF,由“实时流”明确允许重连;仅靠调用者记住
reconnect=False或按字符串猜测路径都容易造成回归测试与生产行为漂移。 - P7:T-203 应验证并区分计时源。 录像使用容器 PTS/帧序号时间轴;实时 RTSP 使用成功收帧时的
time.monotonic()作为事件计时依据。不能只假设CAP_PROP_POS_MSEC在实况流上可靠。
推荐的任务调整
不建议把上述工作混入 T-201 的 GUI 实现。应在 T-105 后插入一个独立的事件集成任务,例如:
| ID | 任务 | 依赖 | 验收要点 |
|---|---|---|---|
| T-106 | 装配事件管线与固化事件契约 | T-105 | Pose → 跟踪 → 质量/几何证据 → 领域规则 → 按 ID 状态机可回放运行;P0 的缺失证据行为受测试保护;所有公开事件参数均生效;事件含实际配置版本;录像 EOF 不会重放。 |
完成 T-106 后,T-201 才接收真实管线输出的画面、骨架、ID、连接状态和 NORMAL/SUSPECT/CONFIRMED 状态。GUI 只负责显示与配置草稿,不能承载摔倒判定规则。P5 作为当前稀疏人员固定机位的已知限制记录;P7 则作为 T-203 接入真实海康流时的阻断验收项。
第二轮复核补充(全栈视角)
对上一节独立复核的结论予以确认:P0 事实成立且优先级判断正确,P3/P4/P6 是对首轮建议的有效纠偏。以下两点为在此基础上的工程细化,建议一并纳入 T-106。
P0 的修法首选状态机内建守卫,而非依赖调用者注入拒绝证据
- 上一节给出两个方向:① 管线为“本帧缺失/低质量”的已知人员喂拒绝证据;② 定义并测试
max_evidence_gap_seconds。 - 方向 ① 要求编排层每帧枚举所有存活 track(不仅是本帧检测到的),漏掉任何一个就退化为误确认;正确性依赖调用者每次都记得注入,防呆性差。
- 方向 ② 是调用者无关的内建守卫:状态机记录
last_fall_candidate_at,当now - last_fall_candidate_at超过max_evidence_gap_seconds时,即使本次是 fall candidate 也必须重置 SUSPECT 计时、不得把间隔折算进确认窗口。 - 建议:以 ② 为主、① 为辅,并在 T-106 验收中固化“SUSPECT 中间出现超过 gap 阈值的证据空档 → 不确认”的正反例测试。
乱序时间戳应降级处理,不应直接抛异常
- 当前
FallStateMachine.update()对now < last_updated_at直接raise ValueError("timestamps must be monotonic per track")。 - 一旦 P7 落实后计时源切换,或某 track 以重置时钟复现,这个硬
raise会让整条实时管线崩溃,而不是容错降级——对一个需要长时间无人值守运行的告警 demo 是不可接受的失败模式。 - 建议:把乱序帧改为丢弃该帧并记录状态(不推进时间、不产生事件、可选计一次告警计数),而非中断。该改动与 P7 的显式计时源在 T-106 一并处理。