# PPE 与电话使用检测流水线 `configs/edge_ai.yaml` 中的 `detection` Pipeline 是当前可运行的园区视觉告警链路。 它与 `talk` Pipeline 共用一个部署 YAML,并从同一个 decoder 并行运行 Construction PPE 与 People-Talking 两个模型。两个 detector 使用独立 repeat gate,再把带有不同 `model_id`/`rule_id` 的告警交给同一个平台 Sink: ```text cmvr-es CameraService -> H264/H265 ImageFrame/v1 -> media.video_decoder.pyav@1 -> BGR8 ImageFrame/v1 |-> construction-ppe-yolov8@1 -> PPE repeat gate --| `-> people-talking-yolov8x@1 -> phone repeat gate --+-> DetectionAlert/v1 `-> POST /v1/detection-alerts ``` ## 安装与启动 从仓库根目录执行一键安装。默认 profile 安装锁定的 CPU 检测环境、生成 cmvr-es bindings,并验证最小测试 fixture 与统一部署配置中的检测链路: ```bash cd /home/xtkuang/Projects/cmvr/cmvr_edge_ai bash scripts/bootstrap.sh ``` cmvr-es 不在相邻的 `../cmvr-es` 时指定实际路径: ```bash bash scripts/bootstrap.sh \ --cmvr-es-root /home/xtkuang/Projects/cmvr/cmvr-es ``` 环境固定 Python 3.10、Ultralytics 8.4.31、PyTorch 2.7.0 CPU 和 torchvision 0.22.0,通过 `image` extra 安装 Pillow 以生成告警图片,并显式安装 People-Talking checkpoint 反序列化所需的 `dill`;所有具体包版本记录在仓库的 `uv.lock` 中。 GPU/Jetson 需要单独适配与驱动或 JetPack 匹配的 PyTorch wheel,不能直接复用 `detection-cpu` profile。手动组合依赖时必须显式增加 `--extra image`,不能只依赖 YOLO 间接安装 Pillow。 直接编辑 `configs/edge_ai.yaml` 中 `detection` Pipeline 的部署参数: ```yaml endpoints: cmvr_es: target: 127.0.0.1:50052 ppe_alert_platform: base_url: http://127.0.0.1:8081 pipelines: detection: nodes: camera: with: device_id: right_hand_cam stream_log_interval_s: 5 detector: with: attach_frame: true inference_log_interval_s: 5 model_options: weights: models/detection/construction-ppe-yolov8/v1/best.pt device: cpu repeat_gate: with: alert_image: enabled: true jpeg_quality: 85 phone_detector: with: model: people-talking-yolov8x@1 detect_labels: [talking on phone] max_fps: 5 attach_frame: true model_options: weights: models/detection/people-talking-yolov8x/v1/best.pt device: cpu phone_repeat_gate: with: alert_image: enabled: true jpeg_quality: 85 alert_platform: with: endpoint: ppe_alert_platform failure_mode: log_and_drop ``` 上述相对权重路径按进程启动时的当前工作目录(`cwd`)解析,不是按 `configs/edge_ai.yaml` 所在目录解析。下面的 validate、run 和 Viewer 命令都应先 `cd /home/xtkuang/Projects/cmvr/cmvr_edge_ai`;如果必须在其他 `cwd` 启动,请在 YAML 中使用正确的绝对权重路径。 完成配置后启动,不需要再通过 shell `export` 传入这些值: ```bash uv run --no-sync cmvr-edge-ai models uv run --no-sync cmvr-edge-ai validate \ -c configs/edge_ai.yaml \ --pipeline detection uv run --no-sync cmvr-edge-ai run -c configs/edge_ai.yaml \ --pipeline detection \ --log-level INFO \ --log-format json ``` 运行时会看到类似下面两类 JSON 日志: ```json {"level":"INFO","logger":"cmvr_edge_ai.detection.operator","message":"detection model loaded node=detector model=construction-ppe-yolov8@1 ..."} {"level":"INFO","logger":"cmvr_edge_ai.detection.operator","message":"detection inference node=detector model=construction-ppe-yolov8@1 total_frames=1 window_frames=1 window_detections=2 hit_labels=No-Helmet:2 ..."} ``` - `detection model loaded`:checkpoint 已成功加载并完成标签顺序校验;当前 detector 应出现一条; - `detection inference`:模型确实收到解码帧并执行了 `predict`;第一帧立即输出, 后续按配置周期聚合; - `window_detections=0 hit_labels=none`:模型在工作,但本周期没有高于配置阈值的命中; - 只有 loaded、长期没有 inference:优先检查 cmvr-es 相机流、decoder 和关键帧; - 短时观察可把 `inference_log_interval_s` 改为 `1` 秒,长期运行建议 `30`~`60` 秒, 省略该字段会关闭周期推理日志。 运行前确认 cmvr-es 已启用 `right_hand_cam`、两份生产权重存在,并且 8081 平台接受 `POST /v1/detection-alerts`。默认 profile 下 `model_options.device` 应设为 `cpu`;只有完成设备专用的 CUDA/Jetson PyTorch 环境适配后,才能改为 `cuda:0` 等值。 ## 实时画框 Demo `configs/debug/detection_viewer.yaml` 和 `show_detections.py` 提供一个不访问 HTTP 平台的 独立调试链路: ```text cmvr-es CameraService -> PyAV decoder -> latest decoded frame |-> Construction PPE --| `-> YOLOv8n Mobile ----+-> same-frame join -> OpenCV window ``` 先编辑 `configs/debug/detection_viewer.yaml` 中的远端 cmvr-es 地址、相机 ID 和权重路径: ```yaml endpoints: cmvr_es: target: 192.168.0.119:50052 pipelines: detection_show: nodes: camera: with: device_id: wrist_cam detector: with: model_options: weights: models/detection/construction-ppe-yolov8/v1/best.pt device: cpu phone_detector: with: model: yolov8n-mobile-phone@1 detect_labels: [mobile_phone] model_options: weights: models/detection/yolov8n-mobile-phone/yolov8n-mobile-phone.pt device: cpu ``` Demo 只在共享 fan-out 之前通过 `realtime_latest + drop_oldest` 选择最新解码帧,之后用 两条 `block` 边把同一个 `ImageFrame` 交给两个 detector。两个 detector 都不设置独立 `max_fps`,避免各自跳过不同帧。Viewer 按 frame identity 等待两个结果配齐,再在同一 张图上绘制;因此预览吞吐由较慢的模型分支决定。 只检查配置和插件连线,不连接相机、不加载模型、也不创建窗口: ```bash uv run --no-sync python detect_server/show_detections.py \ --config configs/debug/detection_viewer.yaml \ --validate-only ``` 启动实时显示: ```bash uv run --no-sync python detect_server/show_detections.py \ --config configs/debug/detection_viewer.yaml \ --pipeline detection_show \ --log-level INFO \ --log-format json ``` 相机 Source 会先调用 `StartCamera`,成功后再建立 gRPC 视频流。窗口只在同一帧的两个 推理结果都到达后刷新;PPE 框显示为 `PPE: