Files
silver_pose/docs/04-architecture.md
T

151 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构设计
## 系统结构
```text
海康 RTSP 流(凭证仅在本机)
↓
RTSP 采集器 / 帧时间戳
↓
Pose 推理适配器(person box + 17 keypoints)
↓
人员跟踪器(track_id)
↓
姿态质量门控 + 倒地证据提取
↓
倒地领域规则(突发下移 → 水平姿态)
↓
每人摔倒事件状态机
↓
确认事件
├── PyQt 实时画面:红框、骨架、状态
├── 本地声音与弹窗
├── 标注截图
└── JSONL 事件日志
```
V1 的同一数据流既可接 RTSP,也可回放本地录像。V2 复用同一配置、事件字段和录像集,但将 Pose 推理替换为 ONNX 适配器;不得改变事件语义。
T-302/T-303 的实现路线为:受控 `ffmpeg.exe` 负责解码为 BGR 原始帧,Go 预处理模块以 114 补边、双线性缩放与 RGB/CHW 生成固定 `1×3×640×640` tensor,`onnxruntime_go` 以显式 DLL 路径执行 Pose ONNX,随后由 NMS、17 点解析、坐标还原、轻量跟踪、质量/几何证据、倒地策略和四态状态机生成不变的 `FallEvent`。Windows Walk UI 只接收已完成渲染的数据。解码、推理和事件计算都不得在 UI 消息线程运行;UI 只显示最新完成帧和事件状态。
## 模块职责
| 模块 | V1 位置 | 职责 | 不负责 |
| --- | --- | --- | --- |
| 应用入口 | `v1/app.py` | 装配配置、窗口、线程和依赖 | 推理细节、事件判定 |
| 配置 | `v1/config.py` | 解析示例和本地配置,校验非敏感字段 | 保存真实凭证 |
| 摄像头接入 | `v1/camera.py` | 由 host/port/channel/账号/密码构建 RTSP URL(凭证百分号编码),并对流做一次有界连接测试抓帧 | 事件判定、持久化 |
| 视频源 | `v1/video_source.py` | 打开、读取、重连 RTSP 或录像;输出帧、单调回放时间戳和显式来源状态 | Pose、报警 |
| Pose 适配器 | `v1/pose.py` | 校验锁定模型的 SHA-256、pose/person/17×3 契约,统一返回 box、关键点、置信度 | 跟踪、摔倒业务结论 |
| 跟踪 | `v1/tracking.py` | 以归一化 box 中心距离为连续人员输出稳定 `track_id` | 根据姿态报警 |
| 质量与证据 | `v1/evidence.py` | 拒绝缺失肩/髋/膝/踝的姿态,计算水平姿态和躯干归一化下移证据 | GUI 状态、确认事件 |
| 倒地领域规则 | `v1/fall_policy.py` | 将每人连续的 `PoseEvidence` 映射为状态机 `Evidence`;要求快速下移后在 suspect 窗口内转为水平,确认后才接受恢复证据 | GUI、报警副作用、模型推理 |
| 事件管线 | `v1/pipeline.py` | 按一帧顺序装配 Pose、跟踪、质量/证据、领域规则和状态机;对缺帧、低质量和断流输入拒绝证据 | 读取视频、加载模型、GUI、报警副作用 |
| 状态机 | `v1/fall_state.py` | 管理每个 ID 的 NORMAL、SUSPECT、CONFIRMED、RECOVERING,并在首次确认时产生带确认延迟和 `config_version` 的事件 | 播放声音或存文件 |
| 报警工件 | `v1/alerts.py` | 对确认事件去重、播放声音、保存截图、写日志 | 推理或事件计算 |
| 视图模型 | `v1/view_model.py` | 由 `FrameAnalysis` 构建 Qt-free 监控视图状态(连接文案、状态语义色、骨架段、人员叠加)与设置草稿(运行/已保存/草稿三份隔离,下次启动生效) | Qt 渲染、事件判定、读 RTSP |
| PyQt UI | `v1/gui.py` | 渲染帧、骨架、状态、设置和弹窗,绑定视图模型 | 直接读 RTSP 或写判定规则 |
| 应用装配 | `v1/app.py` | 装配配置、Pose、管线线程与窗口;`FrameWorker` 只发出已判定的 `FrameAnalysis` | 事件判定、渲染细节 |
| 回归工具 | `v1/tests/` 与 `v1/scripts/` | 回放录像、断言事件和延迟 | 生产 UI |
V2 对应模块的当前落实与 T-304 目标如下:
| 模块 | V2 目标位置 | 职责 | 不负责 |
| --- | --- | --- | --- |
| 回放解码器 | `v2/cmd/regression` | 用受控 FFmpeg 顺序读取 BGR 帧,供本机录像回归 | RTSP 重连、UI 绘制 |
| 运行配置 | `v2/internal/config` | 校验公开 JSON、仅从环境变量解析 RTSP URL、生成非敏感配置版本 | 保存或显示 URL、账号、密码 |
| ONNX Pose | `v2/internal/pose` | 114 letterbox、RGB/CHW、NMS、关键点及坐标还原 | 人员 ID、摔倒结论 |
| 事件引擎 | `v2/internal/fall` | 复现 V1 的跟踪、证据、四态状态机和 `FallEvent` | 视频解码、声音、文件 |
| Windows UI | `v2/internal/ui` | Walk 顶部“监控/设置”Tab、渲染最新帧和已计算状态 | 直接读 RTSP、执行 ONNX 或事件规则 |
| 应用装配 | `v2/cmd/silver-pose`(T-304) | 管理 worker 生命周期、取消、最新帧投递和依赖注入 | 重写领域规则 |
## UI 导航与配置生效生命周期
PyQt 主窗口只包含实时监控和设置两个顶部 Tab。实时监控 Tab 保持视频画面优先;设置 Tab 不能直接调用视频源、Pose 或状态机。
设置页面产生的是已校验的草稿配置。用户点击开始监控时,应用创建不可变的运行配置快照与配置版本,并将该快照传给视频源、Pose、证据和状态机。运行期间编辑设置不会修改该快照;保存后的草稿在下一次开始监控时才会成为新的运行配置。`AppConfig.runtime_config_version` 从非敏感来源 ID、锁定模型与事件参数计算,不含 RTSP 地址或凭证;`FallEvent` 继续记录它,使截图和 JSONL 可以追溯到实际阈值。
真实 RTSP 凭证仍只由环境变量或未跟踪本地配置提供。UI 只显示环境变量是否就绪,不能回显或记录具体值。
## 状态模型
每个 `track_id` 独立维护状态:
```text
NORMAL
└─ 高质量证据显示快速下移,且在 suspect 窗口内转为水平姿态 → SUSPECT
SUSPECT
├─ 水平倒地证据在 confirm 时间窗内持续 → CONFIRMED(产生一次 FallEvent)
└─ 缺失、低质量或非倒地证据 → NORMAL
CONFIRMED
└─ 经配置冷却与恢复稳定站立 → RECOVERING
RECOVERING
├─ 恢复证据持续 → NORMAL
└─ 再次倒地证据 → SUSPECT
```
状态机只以秒和单调时间为准,不以固定帧数为准。这样 15 FPS、30 FPS、丢帧或录像回放速度变化不会改变 1–3 秒业务目标。
录像源和实时流使用不同的计时策略:回放优先容器 PTS、再回退到帧序号/FPS;实时流以成功读帧时的 `time.monotonic()` 计时。任意非连接帧、人员缺帧或低质量姿态都会向该人员输入拒绝证据,不能被计入连续倒地确认时间。
## 数据和文件
| 数据 | 位置 | 规则 |
| --- | --- | --- |
| 公共配置示例 | `v1/config.example.json` | 可提交;只含 `rtsp_url_env`,不含 URL 或密码。 |
| 本地配置 | `v1/config.local.json` | 忽略提交;可引用环境变量。 |
| V1 模型 | `v1/models/best.pt` 或受控相对路径 | 记录 SHA-256、来源与验证日期。 |
| V2 模型 | `v2/assets/best.onnx` | 从锁定 V1 权重导出,记录导出命令、输入尺寸和 SHA-256。 |
| 事件截图 | `artifacts/events/YYYYMMDD/` | 文件名含事件 ID,不含客户姓名或 RTSP 地址。 |
| 事件日志 | `artifacts/events/YYYYMMDD/events.jsonl` | 一行一个 `FallEvent`。 |
| 回归录像 | `testdata/videos/` | 只保存经同意的演示素材;外部客户视频不提交。 |
| 回归标签 | `testdata/expected_events.json` | 每段录像的期望事件、非事件和最大延迟。 |
## 关键风险与应对
| 风险 | 原因 | 应对 |
| --- | --- | --- |
| 单帧误报 | 当前 `demo/` 任一规则命中即报警 | V1 用质量门控、跟踪和时序状态机;反例录像必测。 |
| 俯视关键点不稳 | 1.6 米俯视、遮挡或远距离会影响膝踝 | 全身可见前提、ROI、质量拒绝和现场回归;必要时再采集数据。 |
| RTSP 断流 | 网络抖动或摄像头重连 | 视频源显式状态、指数退避重连、断流不报警。 |
| 多人交叉换 ID | 当前 V1 使用贪心中心点匹配,未做全局最优匹配 | 固定机位、稀疏人员演示中记录为已知限制;多人密集场景在有回归素材后再评估升级。 |
| 域外倒地漏检 | Pose 模型可能在室外斜视、字幕遮挡或与部署机位差异很大的倒地画面中失去人体/关键点 | `demo/1.mp4` 已出现该现象;不以降低事件阈值伪造修复。仅用符合固定俯视大厅/走廊、全身大部分可见边界的经同意录像做验收;若业务要覆盖域外画面,另立模型数据适配任务。 |
| 同一事件重复报警 | CONFIRMED 状态持续多帧 | 每个事件 ID 仅执行一次报警副作用,恢复后才允许新事件。 |
| Go 行为漂移 | ONNX 预后处理与 Python 不同 | 导出后跑同一录像,比较关键点、事件数量、确认时间和截图。 |
| 模型误解 | Pose 指标被误当摔倒指标 | 文案仅说明姿态模型;事件级指标单独记录。 |
## 开发顺序
1. 冻结 `demo/`、创建 V1 配置和可验证骨架。
2. 用本地录像打通 Pose、质量门控、跟踪和状态机。
3. 加入 PyQt 实时画面、RTSP 重连、报警和截图。
4. 用正反例录像和现场流完成 V1 事件级验收。
5. 锁定模型并导出 ONNX,完成 Python/ONNX 一致性。
6. 由 Go V2 重复相同行为后,再作为客户演示部署版。
## 目标目录
```text
v1/
├── app.py
├── config.py
├── config.example.json
├── gui.py
├── video_source.py
├── pose.py
├── tracking.py
├── evidence.py
├── fall_policy.py
├── fall_state.py
├── pipeline.py
├── alerts.py
├── models/
├── scripts/
└── tests/
v2/
├── cmd/silver-pose/
├── internal/
├── assets/
└── testdata/
```