# CMS v9.10 YOLO + RTMW 门区风险部署包 该目录是 v9.10 的精简部署版本,只保留运行 `YOLO + RTMW + risk` 所需的代码、模型、标定文件和依赖清单。历史推理结果、研发测试、标注网页、参考图片、缓存与离线分析入口均未放入部署包。 ## 1. 功能链路 ```text 视频帧 -> YOLO11s 人体检测与轨迹 ID -> RTMW-L COCO-WholeBody 133 点关键点 -> 手臂/手部时序融合 -> 骨骼位置和尺度为主、人体框为辅的前后门归属 -> CAN 活跃门过滤 -> 手部区域(无有效手部时使用人体框兜底) -> 与固定车门危险区 mask 计算二维风险 -> causal_results.jsonl ``` 本版本不包含深度估计和 EdgeTAM。输出是二维图像风险,不是物理距离,也不是安全认证结论。 ## 2. 目录结构 ```text run_risk.py 主入口 requirements-realtime-gpu.txt CUDA 推理依赖 requirements-tensorrt-windows.txt Windows TensorRT 可选依赖 models/ yolo11s.onnx 人体检测模型 rtmw-l-384.onnx WholeBody 关键点模型 annotations/ constant_door_mask.json 当前固定机位车门危险区 door_zone_multifeature.json 当前前门/后门多特征标定 pipeline/ 推理、融合、门区和风险计算代码 ``` `annotations` 中的两个 JSON 都与摄像头机位相关。当前参数来自研发临时机位;换成实车机位后必须重新标定和验证,不能直接将临时机位结论作为量产参数。 ## 3. 环境安装 推荐 Python 3.10~3.12 和 NVIDIA CUDA GPU。先安装与目标机 CUDA 或 JetPack 匹配的 PyTorch,再安装其余依赖: ```powershell python -m venv .venv .\.venv\Scripts\Activate.ps1 python -m pip install --upgrade pip pip install -r requirements-realtime-gpu.txt ``` 不要同时安装 CPU 版 `onnxruntime` 和 `onnxruntime-gpu`。Jetson Orin 应优先使用 NVIDIA 针对对应 JetPack 发布的 PyTorch/torchvision,而不是强制使用桌面 wheel。 如需在 Windows 上使用 RTMW TensorRT Execution Provider,再执行: ```powershell pip install -r requirements-tensorrt-windows.txt ``` ## 4. 运行 ### 4.1 固定车门危险区 以后门 CAN=0、前门未进入电吸范围为例: ```powershell python run_risk.py ` --video D:\data\segment_008.mp4 ` --constant-door-mask annotations\constant_door_mask.json ` --door-zone-config annotations\door_zone_multifeature.json ` --active-door rear ` --output outputs\seg_08_rear ` --det-device 0 ` --pose-device cuda ` --write-video ``` `--active-door` 对应当前 CAN=0 的车门: - `front`:只有前门进入电吸范围; - `rear`:只有后门进入电吸范围; - `both`:前后门都进入电吸范围; - `none`:两门都不进行风险判断。 当前命令行参数对整个视频保持不变,用于离线部署验证。实车 CAN 随帧变化时,集成层需要把每帧实际 CAN 状态传给门级风险选择逻辑。 不需要叠加视频时不要传 `--write-video`,可减少编码和磁盘 I/O。完整参数说明: ```powershell python run_risk.py --help ``` ### 4.2 TensorRT RTMW ```powershell python run_risk.py ` --video D:\data\test.mp4 ` --constant-door-mask annotations\constant_door_mask.json ` --active-door rear ` --output outputs\test_trt ` --pose-provider tensorrt ` --pose-device cuda ``` 首次运行会在 `models/rtmw_l_trt_cache` 生成 TensorRT 缓存,该缓存与目标 GPU、TensorRT 版本和运行环境相关,不随部署包预置。 ## 5. 输出文件 输出目录包含: - `causal_results.jsonl`:逐帧因果结果,一行一个 JSON 对象,是 Demo 对接的主要接口; - `summary.json`:模型、帧数、吞吐、延迟及风险统计; - `rtmw_risk_overlay.mp4`:仅在传入 `--write-video` 时生成。 `causal_results.jsonl` 每行顶层字段: | 字段 | 含义 | |---|---| | `frame_index` | 从 0 开始的视频帧号 | | `track_ids` | 当前帧人体轨迹 ID 数组 | | `boxes` | 与 `track_ids` 同序的人体框,格式 `[x1,y1,x2,y2]` | | `skeletons` | 与检测人体对齐的 133 点融合骨骼 | | `active_door` | 本次运行指定的 CAN=0 门:`front/rear/both/none` | | `door_assignments` | 每个轨迹的前门/后门归属及特征解释 | | `door_risks` | `front` 和 `rear` 两扇门各自的最终风险 | | `risk` | 经过 CAN 和门区过滤后供业务使用的当前风险 | | `ungated_risk` | 未经过前后门/CAN 过滤的原始二维风险,仅用于诊断 | 数组关联规则:`boxes[i]`、`track_ids[i]`、`skeletons[i].person_index == i`、`door_assignments[i].person_index == i` 表示同一个人。跨帧关联应使用 `track_id`,不要依赖数组下标。 ## 6. `risk` 的含义 ### 6.1 应读取哪个字段 单门 CAN=0 时,业务防夹建议读取: ```text risk.risk_type risk.risk_level risk.risk_score risk.decision ``` 需要同时展示前后门时分别读取: ```text door_risks.front door_risks.rear ``` `ungated_risk` 只用于排查“图像上是否靠近危险区”,不能直接代替门级最终结论,否则会绕过前后门归属和 CAN 过滤。 ### 6.2 风险标签 `risk_type` 是带门位置的最终风险标签: | `risk_type` | `risk_level` | 含义 | |---|---|---| | `FRONT_CLEAR` / `REAR_CLEAR` | `clear` | 对应活跃门没有风险 | | `FRONT_WATCH` / `REAR_WATCH` | `watch` | 进入关注距离,但未达到 warning | | `FRONT_WARNING` / `REAR_WARNING` | `warning` | 距离达到 warning 阈值 | | `FRONT_DANGER` / `REAR_DANGER` | `danger` | 人体风险区域与车门危险区相交 | | `FRONT_UNKNOWN` / `REAR_UNKNOWN` | `unknown` | 人物处于 `transition`,无法可靠确定前后门 | | `FRONT_INACTIVE` / `REAR_INACTIVE` | `inactive` | 该门 CAN 不活跃,不计算防夹风险 | | `NO_ACTIVE_DOOR` | `inactive` | 前后门均不活跃 | 对应的 `decision`: | `decision` | 含义 | |---|---| | `DANGER_INTERSECTION` | 风险区域与车门 mask 有像素相交 | | `WARNING_NEAR` | 相对距离小于等于 warning 阈值 | | `WATCH` | 相对距离处于 warning 与 release 之间 | | `CLEAR` | 相对距离大于 release 阈值 | | `CLEAR_NO_PERSON_ASSIGNED_TO_ACTIVE_DOOR` | 没有人被分配给该活跃门 | | `EVIDENCE_UNAVAILABLE_TRANSITION_ZONE` | 存在 `transition` 人员,门归属不确定 | | `DOOR_CAN_NOT_ACTIVE` | 该门 CAN 不活跃 | | `NO_DOOR_CAN_ACTIVE` | 没有活跃门 | 默认阈值: ```text warning_distance_relative = 0.30 release_distance_relative = 0.45 ``` 对每个风险区域: ```text distance_relative = min_distance_px / sqrt(region_area_px) risk_score = clip(1 - distance_relative / release_distance_relative, 0, 1) ``` 只要 `door_overlap_px > 0`,就强制 `risk_score=1.0` 且等级为 `danger`。因此 `risk_score` 是归一化二维接近程度,不是概率,也不是厘米/毫米距离。 ### 6.3 `regions` 与证据来源 每个门风险中的 `regions` 保存独立人体区域的解释信息,常用字段包括: - `track_id`、`side`:所属轨迹和左右手; - `source`:`RTMW_HAND_KEYPOINTS` 或 `YOLO_BODY_BOX_FALLBACK`; - `region_area_px`:风险区域面积; - `door_overlap_px`、`region_overlap_ratio`:与车门 mask 的相交量; - `min_distance_px`:区域到车门 mask 的最小像素距离; - `region_scale_px`:`sqrt(region_area_px)`; - `distance_relative`:尺度归一化距离; - `risk_score`、`risk_level`、`decision`:该区域自己的风险结果。 存在可用手部时,每只手用 21 点凸包计算风险;一个人没有任何可用手部时,才使用整个人体框兜底。人体框兜底更保守,也更容易产生误报。 ### 6.4 `transition` 的当前行为 `door_assignments[].door_zone` 的位置标签为: - `front`:归属前门; - `rear`:归属后门; - `transition`:前后门证据不足或位于过渡区; - `unassigned`:人体尺度低于有效阈值,不参与门级风险。 当前 v9.10 中,`transition` 人员即使在 `ungated_risk` 中距离很近,也不会直接变成 `FRONT_WARNING/REAR_WARNING` 或 `DANGER`,而是让活跃门输出 `*_UNKNOWN`,其 `risk_score` 为 `null`。Demo 必须显式展示或处理 `unknown`,不能把它当成 `clear`。这是当前版本行为,不代表最终量产安全策略。 ## 7. `skeletons` 的含义与可视化对接 ### 7.1 数据结构 每个人的骨骼对象如下: ```json { "track_id": 1, "person_index": 0, "layout": "coco_wholebody_133", "keypoints": [[x0, y0], [x1, y1]], "metadata": { "left": {}, "right": {} } } ``` - `keypoints` 实际固定为 `133 x 2`,示例中省略了其余点; - 坐标单位是原视频像素,原点在左上角,x 向右、y 向下; - 坐标保留 3 位小数; - 这里没有逐点置信度,`metadata` 提供的是手臂/手部融合后的状态; - `keypoints` 使用的正是当前叠加视频 `draw_fused` 使用的融合结果。 ### 7.2 COCO-WholeBody 133 点索引 | 索引 | 含义 | |---|---| | `0` | nose | | `1/2` | left/right eye | | `3/4` | left/right ear | | `5/6` | left/right shoulder | | `7/8` | left/right elbow | | `9/10` | left/right wrist | | `11/12` | left/right hip | | `13/14` | left/right knee | | `15/16` | left/right ankle | | `17/18/19` | left big toe / small toe / heel | | `20/21/22` | right big toe / small toe / heel | | `23–90` | 68 个面部关键点 | | `91–111` | 左手 21 点 | | `112–132` | 右手 21 点 | 每只手的局部 21 点顺序: ```text 0 wrist 1-4 thumb 5-8 index finger 9-12 middle finger 13-16 ring finger 17-20 little finger ``` 左手局部索引需要加 91,右手局部索引需要加 112,才能得到 `keypoints` 中的全局索引。 ### 7.3 与当前叠加视频一致的绘制规则 当前风险叠加视频只绘制手臂和双手,不绘制面部、躯干和腿部。手臂连线: ```text (5,7), (7,9) 左肩-左肘-左腕 (6,8), (8,10) 右肩-右肘-右腕 ``` 每只手使用以下局部连线,分别作用到 `91–111` 和 `112–132`: ```text (0,1),(1,2),(2,3),(3,4) (0,5),(5,6),(6,7),(7,8) (0,9),(9,10),(10,11),(11,12) (0,13),(13,14),(14,15),(15,16) (0,17),(17,18),(18,19),(19,20) ``` 为了与现有视频表现一致: - `metadata[side].limb_state == "consensus"`:手臂画实线,否则画虚线; - `metadata[side].hand_state == "rejected"`:不画该手; - 手未被拒绝且 `distance_eligible == true`:手部画实线; - 手未被拒绝但 `distance_eligible == false`:手部画虚线; - `alarm_eligible == true` 表示该手允许进入 risk 区域计算。 Demo 如果只需要复现当前可视化,读取上述 48 个点即可;如果后续需要完整人体展示,可以使用全部 133 点,但应自行补充完整 WholeBody 连线定义。 ## 8. `door_assignments` 对接说明 常用字段: - `track_id`、`person_index`:与检测框和骨骼关联; - `door_zone`:经过最多 5 帧因果中值后的 `front/rear/transition/unassigned`; - `raw_door_zone`:当前帧未经时序中值稳定的标签; - `front_probability`:当前帧前门概率; - `smoothed_front_probability`:因果时序中值; - `confidence`:相对 0.5 的归属置信度,不是人体检测置信度; - `pose_feature_count`:本帧可用骨骼特征数量; - `features`、`feature_frontness`:标定特征值和各特征前门贡献; - `history_frames`:当前轨迹已用于中值的历史帧数。 当前标定阈值为:`smoothed_front_probability >= 0.72` 判为前门,`<= 0.28` 判为后门,中间为 `transition`。这些参数应随实车机位重新验证。 ## 9. Demo 集成注意事项 1. 每帧先按 `track_id` 关联检测框、骨骼、门区和风险,避免多人时按数组顺序长期跟踪。 2. 主界面风险展示使用 `risk`;需要前后门双状态时使用 `door_risks.front/rear`。 3. `unknown`、`inactive` 和 `clear` 是三种不同状态,不能合并显示。 4. `risk_score == null` 表示该状态没有可用数值风险,不应转换成 0。 5. `ungated_risk` 只用于诊断叠加,不能作为最终防夹指令。 6. 更换分辨率或裁剪策略后必须核对车门 mask、骨骼坐标与视频坐标是否仍一致。 7. 当前系统可能把玻璃倒影或反光误检成人,部署 Demo 应保留原视频、检测框、`track_id` 和风险解释字段用于问题回溯。