2026-07-21 16:23:33 +08:00
|
|
|
|
# PPE 与电话使用检测流水线
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
`configs/edge_ai.yaml` 中的 `detection` Pipeline 是当前可运行的园区视觉告警链路。
|
|
|
|
|
|
它与 `talk` Pipeline 共用一个部署 YAML,并从同一个 decoder 并行运行 Construction
|
|
|
|
|
|
PPE 与 People-Talking 两个模型。两个 detector 使用独立 repeat gate,再把带有不同
|
|
|
|
|
|
`model_id`/`rule_id` 的告警交给同一个平台 Sink:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
cmvr-es CameraService
|
|
|
|
|
|
-> H264/H265 ImageFrame/v1
|
|
|
|
|
|
-> media.video_decoder.pyav@1
|
|
|
|
|
|
-> BGR8 ImageFrame/v1
|
2026-07-21 16:23:33 +08:00
|
|
|
|
|-> construction-ppe-yolov8@1 -> PPE repeat gate --|
|
|
|
|
|
|
`-> people-talking-yolov8x@1 -> phone repeat gate --+-> DetectionAlert/v1
|
|
|
|
|
|
`-> POST /v1/detection-alerts
|
2026-07-20 16:59:37 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 安装与启动
|
|
|
|
|
|
|
|
|
|
|
|
从仓库根目录执行一键安装。默认 profile 安装锁定的 CPU 检测环境、生成 cmvr-es
|
2026-07-21 12:07:12 +08:00
|
|
|
|
bindings,并验证最小测试 fixture 与统一部署配置中的检测链路:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
|
|
|
|
|
```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
|
2026-07-21 16:23:33 +08:00
|
|
|
|
0.22.0,通过 `image` extra 安装 Pillow 以生成告警图片,并显式安装 People-Talking
|
|
|
|
|
|
checkpoint 反序列化所需的 `dill`;所有具体包版本记录在仓库的 `uv.lock` 中。
|
|
|
|
|
|
GPU/Jetson 需要单独适配与驱动或 JetPack 匹配的 PyTorch
|
2026-07-20 16:59:37 +08:00
|
|
|
|
wheel,不能直接复用 `detection-cpu` profile。手动组合依赖时必须显式增加
|
|
|
|
|
|
`--extra image`,不能只依赖 YOLO 间接安装 Pillow。
|
|
|
|
|
|
|
2026-07-21 12:07:12 +08:00
|
|
|
|
直接编辑 `configs/edge_ai.yaml` 中 `detection` Pipeline 的部署参数:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
endpoints:
|
|
|
|
|
|
cmvr_es:
|
|
|
|
|
|
target: 127.0.0.1:50052
|
2026-07-21 12:07:12 +08:00
|
|
|
|
ppe_alert_platform:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
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:
|
2026-07-21 12:07:12 +08:00
|
|
|
|
weights: models/detection/construction-ppe-yolov8/v1/best.pt
|
2026-07-20 16:59:37 +08:00
|
|
|
|
device: cpu
|
|
|
|
|
|
repeat_gate:
|
|
|
|
|
|
with:
|
|
|
|
|
|
alert_image:
|
|
|
|
|
|
enabled: true
|
|
|
|
|
|
jpeg_quality: 85
|
2026-07-21 16:23:33 +08:00
|
|
|
|
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
|
2026-07-21 12:07:12 +08:00
|
|
|
|
alert_platform:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
with:
|
2026-07-21 12:07:12 +08:00
|
|
|
|
endpoint: ppe_alert_platform
|
2026-07-20 16:59:37 +08:00
|
|
|
|
failure_mode: log_and_drop
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-21 12:07:12 +08:00
|
|
|
|
上述相对权重路径按进程启动时的当前工作目录(`cwd`)解析,不是按
|
|
|
|
|
|
`configs/edge_ai.yaml` 所在目录解析。下面的 validate、run 和 Viewer 命令都应先
|
|
|
|
|
|
`cd /home/xtkuang/Projects/cmvr/cmvr_edge_ai`;如果必须在其他 `cwd` 启动,请在
|
|
|
|
|
|
YAML 中使用正确的绝对权重路径。
|
|
|
|
|
|
|
2026-07-20 16:59:37 +08:00
|
|
|
|
完成配置后启动,不需要再通过 shell `export` 传入这些值:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
uv run --no-sync cmvr-edge-ai models
|
|
|
|
|
|
uv run --no-sync cmvr-edge-ai validate \
|
2026-07-21 12:07:12 +08:00
|
|
|
|
-c configs/edge_ai.yaml \
|
2026-07-20 16:59:37 +08:00
|
|
|
|
--pipeline detection
|
2026-07-21 12:07:12 +08:00
|
|
|
|
uv run --no-sync cmvr-edge-ai run -c configs/edge_ai.yaml \
|
2026-07-20 16:59:37 +08:00
|
|
|
|
--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` 秒,
|
|
|
|
|
|
省略该字段会关闭周期推理日志。
|
|
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
运行前确认 cmvr-es 已启用 `right_hand_cam`、两份生产权重存在,并且 8081
|
2026-07-20 16:59:37 +08:00
|
|
|
|
平台接受 `POST /v1/detection-alerts`。默认 profile 下 `model_options.device` 应设为
|
|
|
|
|
|
`cpu`;只有完成设备专用的 CUDA/Jetson PyTorch 环境适配后,才能改为 `cuda:0` 等值。
|
|
|
|
|
|
|
2026-07-21 12:07:12 +08:00
|
|
|
|
## 实时画框 Demo
|
|
|
|
|
|
|
|
|
|
|
|
`configs/debug/detection_viewer.yaml` 和 `show_detections.py` 提供一个不访问 HTTP 平台的
|
|
|
|
|
|
独立调试链路:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-21 16:23:33 +08:00
|
|
|
|
cmvr-es CameraService -> PyAV decoder -> latest decoded frame
|
|
|
|
|
|
|-> Construction PPE --|
|
|
|
|
|
|
`-> YOLOv8n Mobile ----+-> same-frame join
|
|
|
|
|
|
-> OpenCV window
|
2026-07-21 12:07:12 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
先编辑 `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:
|
2026-07-21 16:23:33 +08:00
|
|
|
|
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
|
2026-07-21 12:07:12 +08:00
|
|
|
|
device: cpu
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
Demo 只在共享 fan-out 之前通过 `realtime_latest + drop_oldest` 选择最新解码帧,之后用
|
|
|
|
|
|
两条 `block` 边把同一个 `ImageFrame` 交给两个 detector。两个 detector 都不设置独立
|
|
|
|
|
|
`max_fps`,避免各自跳过不同帧。Viewer 按 frame identity 等待两个结果配齐,再在同一
|
|
|
|
|
|
张图上绘制;因此预览吞吐由较慢的模型分支决定。
|
2026-07-21 12:07:12 +08:00
|
|
|
|
|
|
|
|
|
|
只检查配置和插件连线,不连接相机、不加载模型、也不创建窗口:
|
|
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
相机 Source 会先调用 `StartCamera`,成功后再建立 gRPC 视频流。窗口只在同一帧的两个
|
|
|
|
|
|
推理结果都到达后刷新;PPE 框显示为 `PPE:<label>`,手机框显示为
|
|
|
|
|
|
`PHONE:mobile_phone`,顶部同时显示两模型的框数和推理耗时。即使两模型都没有框,
|
|
|
|
|
|
配对完成后也会刷新。按 `q`、`Q`、`Esc` 或关闭窗口可安全退出。这个 Demo 绕过
|
|
|
|
|
|
`repeat_gate` 和 HTTP Sink,因此只用于观察模型效果,不代表某条告警规则已满足。
|
2026-07-21 12:07:12 +08:00
|
|
|
|
|
|
|
|
|
|
OpenCV 窗口出现在运行命令的机器上。无桌面的边缘设备不能直接显示;通过 SSH 运行时
|
|
|
|
|
|
需要启用 X11 转发并确保 `DISPLAY` 可用,否则程序会给出明确错误并退出。依赖缺失时先
|
|
|
|
|
|
执行 `bash scripts/bootstrap.sh`。
|
|
|
|
|
|
|
2026-07-20 16:59:37 +08:00
|
|
|
|
## 模型与标签
|
|
|
|
|
|
|
|
|
|
|
|
`detection.model@1` 不绑定某一个框架;它通过 `DetectionModelRegistry` 查找配置中的
|
|
|
|
|
|
`model`。每个 `DetectionModelSpec` 注册版本化模型 ID、模型名称、backend 和有序
|
2026-07-21 16:23:33 +08:00
|
|
|
|
`supported_labels`。注册表当前包含四个内置模型:生产 Pipeline 引用 Construction PPE
|
|
|
|
|
|
与 People-Talking,Debug Viewer 则并行运行 Construction PPE 与 YOLOv8n Mobile Phone:
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
2026-07-21 12:07:12 +08:00
|
|
|
|
- [`construction-ppe-yolov8@1`](../models/detection/construction-ppe-yolov8/v1/README.md):
|
|
|
|
|
|
对应制品目录 `v1`,19 类,包含用于违规告警的 `No-*` 标签;
|
|
|
|
|
|
- [`ppe-6classes-yolov8n@1`](../models/detection/ppe-6classes-yolov8n/v1/README.md):
|
|
|
|
|
|
对应制品目录 `v1`,包含 `Gloves`、`Vest`、`goggles`、`helmet`、`mask`、
|
2026-07-20 16:59:37 +08:00
|
|
|
|
`safety_shoe` 六个正向装备标签。
|
2026-07-21 16:23:33 +08:00
|
|
|
|
- [`people-talking-yolov8x@1`](../models/detection/people-talking-yolov8x/v1/README.md):
|
|
|
|
|
|
对应制品目录 `v1`,checkpoint 标签为 `label`、`talking on phone`;生产配置只选择
|
|
|
|
|
|
语义明确的 `talking on phone`。
|
|
|
|
|
|
- [`yolov8n-mobile-phone@1`](../models/detection/yolov8n-mobile-phone/README.md):
|
|
|
|
|
|
checkpoint 单类标签为 `mobile_phone`;Debug Viewer 用它显示画面中的手机物体。
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
模型 ID 尾部的 `@1` 表示注册版本 1;多数制品使用 `v1` 子目录,当前
|
|
|
|
|
|
YOLOv8n Mobile Phone 保留导入时的扁平目录和上游文件名。运行时不会根据 ID 自动
|
|
|
|
|
|
拼接路径,仍由 YAML 中的 `model_options.weights` 显式指定。训练信息、完整标签顺序、
|
|
|
|
|
|
性能、限制和许可信息请查看各自的 model card。
|
2026-07-21 12:07:12 +08:00
|
|
|
|
|
2026-07-20 16:59:37 +08:00
|
|
|
|
可用 `cmvr-edge-ai models` 核对 ID、名称、backend 和标签顺序。六类模型只表达
|
|
|
|
|
|
“检测到某件装备”,不包含 `Person` 或 `No-*` 类,也不执行人员/PPE 关联;所以它
|
|
|
|
|
|
不能直接判断某个人缺少装备。需要这种语义时,仍应增加人员检测、空间关联和缺失
|
|
|
|
|
|
判定节点,不能把“没有检测到 helmet”直接当成“人员未戴安全帽”。
|
|
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
People-Talking 是纯视觉检测:它只能判断画面是否像“正在用手机通话”,不能读取真实
|
|
|
|
|
|
通话状态,不能区分接听/拨出,也不能证明电话已经接通。上游 checkpoint 中含义不清的
|
|
|
|
|
|
`label` 类为了保持 class ID 对齐仍在注册表中,但不会进入当前告警规则。
|
|
|
|
|
|
|
|
|
|
|
|
YOLOv8n Mobile Phone 只检测手机物体,不能据此判断人员正在接打电话。合成全黑图的
|
|
|
|
|
|
本地 smoke test 也出现过高置信度误报,因此 Viewer 默认阈值只是起点,应使用真实相机
|
|
|
|
|
|
画面校准 `confidence`。
|
|
|
|
|
|
|
2026-07-20 16:59:37 +08:00
|
|
|
|
检测节点的关键参数:
|
|
|
|
|
|
|
|
|
|
|
|
| 参数 | 语义 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `model` | 必须是已注册的精确模型 ID |
|
|
|
|
|
|
| `detect_labels` | 本次部署需要的标签;省略表示模型的全部注册标签 |
|
|
|
|
|
|
| `confidence` | 所有选中标签的默认置信度阈值 |
|
|
|
|
|
|
| `label_confidence` | 可选的逐标签阈值覆盖 |
|
|
|
|
|
|
| `max_fps` | 推理启动频率上限;过密的解码帧会被跳过 |
|
|
|
|
|
|
| `inference_log_interval_s` | 可选推理摘要周期(秒);首帧立即输出,省略表示关闭周期日志 |
|
|
|
|
|
|
| `attach_frame` | 是否让 `DetectionResult` 临时附带对应解码帧;生成告警图片时必须为 `true` |
|
|
|
|
|
|
| `model_options` | backend 私有选项;当前 YOLO 支持 weights、device、imgsz、iou、half、max_det、agnostic_nms |
|
|
|
|
|
|
|
|
|
|
|
|
配置阶段会拒绝未知模型、模型不支持的 `detect_labels`、重复标签和非法阈值。YOLO 加载 checkpoint 时还会严格比较 checkpoint 的实际标签及顺序与注册信息,避免类别编号静默错位。
|
|
|
|
|
|
|
|
|
|
|
|
## 六类逐推理结果分支
|
|
|
|
|
|
|
|
|
|
|
|
这一分支当前默认关闭。恢复后,六类 detector 不连接 repeat gate,而是把每次完成推理产生的
|
|
|
|
|
|
`DetectionResult/v1` 直接交给第二个 HTTP Sink。其 POST Envelope 的
|
|
|
|
|
|
`schema` 为 `DetectionResult/v1`,`payload` 示例为:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"detections": [
|
|
|
|
|
|
{
|
|
|
|
|
|
"label": "helmet",
|
|
|
|
|
|
"confidence": 0.91,
|
|
|
|
|
|
"box": {"x_min": 120.0, "y_min": 60.0, "x_max": 250.0, "y_max": 220.0},
|
|
|
|
|
|
"track_id": null
|
|
|
|
|
|
}
|
|
|
|
|
|
],
|
|
|
|
|
|
"model_id": "ppe-6classes-yolov8n@1",
|
|
|
|
|
|
"inference_ms": 34.2,
|
|
|
|
|
|
"model_name": "PPE Detection YOLOv8n (6 Classes)"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
该结构刻意不同于原分支的 `DetectionAlert/v1`:它没有 `rule_id`、`hit_count`、
|
|
|
|
|
|
`event_id` 或 `image`,空检测帧也会以空 `detections` 数组上报。`cooldown_ms` 是
|
|
|
|
|
|
repeat gate 的内部规则配置,不属于 `DetectionAlert` payload。由于
|
|
|
|
|
|
payload 没有 event ID,HTTP Sink 的 `Idempotency-Key` 回退使用 Envelope
|
|
|
|
|
|
`trace_id`。这只是模拟的逐结果接口;若平台要求另一套字段命名或嵌套,需要增加
|
|
|
|
|
|
平台专用转换节点/Sink,而不是仅修改 endpoint。
|
|
|
|
|
|
|
|
|
|
|
|
## 重复触发规则
|
|
|
|
|
|
|
|
|
|
|
|
`detection.repeat_gate@1` 按规则维护滑动时间窗口。规则字段如下:
|
|
|
|
|
|
|
|
|
|
|
|
| 参数 | 语义 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `id` | 唯一规则 ID,写入告警 |
|
|
|
|
|
|
| `labels` | 任一标签匹配即视为该规则在当前帧命中 |
|
|
|
|
|
|
| `min_confidence` | 规则侧最低置信度,可高于 detector 阈值 |
|
|
|
|
|
|
| `min_hits` | 触发所需的不同帧数 |
|
|
|
|
|
|
| `window_ms` | 上述命中必须落入的时间窗口 |
|
|
|
|
|
|
| `cooldown_ms` | 告警后的静默期;期间不累计,结束后重新计数 |
|
|
|
|
|
|
| `scope` | `source` 按相机统计;`track` 按相机内的 `track_id` 分别统计 |
|
|
|
|
|
|
|
|
|
|
|
|
一次命中按 `(source_id, sequence)` 的不同视频帧计算:同一帧出现多个相同标签框只算一次,重复投递同一帧也不会增加计数。一个规则即使配置多个标签,同一 scope 在一帧内仍只增加一次。`time_source` 可选 `captured`、`received` 或优先采集时间的 `auto`;当前 cmvr-es 成功帧通常没有 `header.timestamp`,示例显式使用 `received`。
|
|
|
|
|
|
|
|
|
|
|
|
`alert_image.enabled` 默认为 `false`;启用后要求 detector 同时配置
|
|
|
|
|
|
`attach_frame: true`。`jpeg_quality` 是 `1..95` 的整数,默认 85。repeat gate
|
|
|
|
|
|
只在规则达到 `min_hits`、准备输出告警时,才用 Pillow 在当前阈值帧上绘制匹配的
|
|
|
|
|
|
bounding boxes、标签和置信度并编码 JPEG,不会为每个推理结果都渲染。告警里的
|
|
|
|
|
|
`detections` 和图片框均来自阈值帧;窗口内更早的命中只参与次数、时间范围和
|
|
|
|
|
|
`max_confidence` 统计。
|
|
|
|
|
|
|
|
|
|
|
|
同一阈值帧同时触发多条规则或多个 track 时,repeat gate 对这些告警相关框取并集,
|
|
|
|
|
|
只画框并编码一次,然后让本帧产生的告警共享该 JPEG,避免在边缘端重复编码。
|
|
|
|
|
|
非有限坐标、反向/退化框、非法置信度等 detection 会在规则计数前被忽略,并记录在
|
|
|
|
|
|
`invalid_detections` 健康指标中,避免 `NaN/Inf` 令整个 HTTP JSON 告警无法发送。
|
|
|
|
|
|
|
|
|
|
|
|
当前 YOLO adapter 是逐帧检测,不执行人员跟踪,输出的 `track_id` 为 `None`。因此示例使用 `scope: source`;如果业务要求“同一个人连续多次违规”,必须在 detector 和 repeat gate 之间加入 tracker 及人员/PPE 关联节点,之后才能使用 `scope: track`。
|
|
|
|
|
|
|
|
|
|
|
|
## 相机启动与视频流日志
|
|
|
|
|
|
|
|
|
|
|
|
cmvr-es 的 `StartCamera` 和 `GetRGBImageStream` 是两个不同阶段:前者打开物理相机,
|
|
|
|
|
|
后者只启动编码和流式传输。连接器在每个首次连接或重连 session 中严格执行:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
StartCamera -> 检查 feedback.header.success -> GetRGBImageStream -> 等待首帧
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`StartCamera` 使用 `cmvr_es.timeout_s`;RPC 异常或 `header.success=false` 都进入同一套
|
|
|
|
|
|
指数退避重连。正常关闭只取消当前 stream,不自动调用设备级 `StopCamera`,避免影响
|
|
|
|
|
|
同一相机的其他客户端。
|
|
|
|
|
|
|
|
|
|
|
|
使用 `--log-level INFO --log-format json` 时,按顺序关注:
|
|
|
|
|
|
|
|
|
|
|
|
- `camera source configured`:endpoint、device 和超时配置完成;
|
|
|
|
|
|
- `camera start requested/succeeded`:StartCamera 已被调用并成功;
|
|
|
|
|
|
- `camera stream opening`:流请求已创建,正在等待第一帧;
|
|
|
|
|
|
- `camera stream first frame`:已经收到真实编码帧,包含 codec、尺寸、字节数和远程序号;
|
|
|
|
|
|
- `camera stream progress`:每 `stream_log_interval_s` 秒输出帧数、FPS、bitrate、关键帧、
|
|
|
|
|
|
最后序号和 `last_frame_age_s`;即使完全没帧也会输出 `first_frame_received=false`;
|
|
|
|
|
|
- `camera stream disconnected`:包含错误类型、重连次数和退避时间;
|
|
|
|
|
|
- `camera stream closed`、`camera source stopped`:当前 session 和本地 Source 已清理。
|
|
|
|
|
|
|
|
|
|
|
|
INFO 日志不会打印帧二进制。示例设置 `stream_log_interval_s: 5` 便于联调,正式部署
|
|
|
|
|
|
可改为 30~60 秒。
|
|
|
|
|
|
|
|
|
|
|
|
## 视频连续性和背压
|
|
|
|
|
|
|
|
|
|
|
|
H264/H265 是有参考关系的编码流,解码前不能任意丢包:
|
|
|
|
|
|
|
|
|
|
|
|
- `camera -> decoder` 使用 `video_contiguous + block`,保持 edge-ai 内部已经接受的数据连续;
|
|
|
|
|
|
- decoder 在新 session、编码参数变化、sequence gap 或解码错误后释放上下文,等待下一个关键帧;
|
|
|
|
|
|
- `decoder -> detector` 已经是完整 BGR 图像,使用 `realtime_latest + drop_oldest` 和容量 1,以有限内存换取较低实时延迟;
|
|
|
|
|
|
- `detector -> repeat_gate` 在 `attach_frame: true` 时携带未压缩图像,示例把队列容量限制为 2,避免排队的 `DetectionResult` 长时间占用大量内存。
|
|
|
|
|
|
|
|
|
|
|
|
该边使用 `drop_oldest` 偏向低延迟;持续过载时,被丢弃的推理结果不会计入
|
|
|
|
|
|
`min_hits`。如果业务更重视每次推理结果都参与计数,可改为 `overflow: block`,但要
|
|
|
|
|
|
接受延迟向上游传播,并继续保持很小的队列容量。
|
|
|
|
|
|
|
|
|
|
|
|
恢复第二模型时,应从 decoder 输出端口 fan-out,以避免第二次相机订阅和第二次
|
|
|
|
|
|
H264/H265 解码;这不会复用模型计算,两个 YOLO 实例仍会分别加载权重并共享应用的
|
|
|
|
|
|
有界线程池。
|
|
|
|
|
|
|
|
|
|
|
|
这个保证只覆盖 edge-ai 内部。当前 cmvr-es 服务端通过 `getLatestEncodedFrame` 读取最新编码数据;如果它在负载或时序竞争下已经跳过参考包,`video_contiguous` 无法恢复丢失内容,而且当前 edge-ai 的本地 sequence 不能可靠暴露这种上游跳包。此时 PyAV 可能报错,decoder 会重置并等待关键帧。上线前必须用真实摄像头长时间验证连续性;更稳妥的方案是让 cmvr-es 提供连续 access unit 流,或额外提供原始/JPEG/带显式 discontinuity 的 AI 接口。
|
|
|
|
|
|
|
|
|
|
|
|
相机 Source 默认启用指数退避重连;每次重新订阅生成新的 `session_id`,使 decoder 主动重置。`reconnect_initial_s`、`reconnect_max_s` 和可选 `max_reconnect_attempts` 可配置;省略最大次数表示持续重连直到进程关闭。
|
|
|
|
|
|
|
|
|
|
|
|
## HTTP 输出可靠性
|
|
|
|
|
|
|
|
|
|
|
|
每次规则触发会产生带唯一 `event_id` 的 `DetectionAlert/v1`。HTTP Sink 优先把该
|
|
|
|
|
|
`event_id` 放入 `Idempotency-Key`,没有事件 ID 时才回退到 `trace_id`。它对连接/超时
|
|
|
|
|
|
错误和配置的临时 HTTP 状态执行有限指数退避,示例最多尝试 3 次。
|
|
|
|
|
|
|
|
|
|
|
|
`failure_mode` 支持两种明确语义:默认 `raise` 在最终投递失败时让 Pipeline 失败;当前
|
|
|
|
|
|
检测配置使用 `log_and_drop`,最终连接失败、超时或非 2xx 时输出
|
|
|
|
|
|
`HTTP report dropped ... action=drop` WARNING,丢弃当前告警并继续处理后续视频帧。
|
|
|
|
|
|
`CancelledError`、序列化错误和未知本地程序异常不会被吞掉。
|
|
|
|
|
|
|
2026-07-21 16:23:33 +08:00
|
|
|
|
当前唯一 HTTP Sink 将 PPE 和电话使用告警发送到
|
|
|
|
|
|
`http://127.0.0.1:8081/v1/detection-alerts`;payload 中的 `model_id` 和 `rule_id`
|
|
|
|
|
|
用于区分来源。8082 六类逐推理结果 endpoint 当前未配置。
|
2026-07-20 16:59:37 +08:00
|
|
|
|
|
|
|
|
|
|
启用告警图片后,POST JSON 的 `payload.image` 结构如下:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"media_type": "image/jpeg",
|
|
|
|
|
|
"width": 1280,
|
|
|
|
|
|
"height": 720,
|
|
|
|
|
|
"encoding": "base64",
|
|
|
|
|
|
"data": "/9j/4AAQSk..."
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`width/height` 是 JPEG 的像素尺寸;HTTP JSON Sink 将内部 JPEG `bytes` 特判为
|
|
|
|
|
|
上述扁平对象,`encoding` 固定为 `base64`。关闭 `alert_image`,或运行时因第三方
|
|
|
|
|
|
结果未附带帧、坏帧等原因渲染失败时,告警仍会发送且该字段为 `null`。Base64 会
|
|
|
|
|
|
额外增加约三分之一的体积,平台和反向代理需要配置足够的请求体上限。
|
|
|
|
|
|
|
|
|
|
|
|
这不是持久化 outbox:`log_and_drop` WARNING 表示该告警已经永久丢失,不会自动补发;
|
|
|
|
|
|
每次告警在放弃前仍会完成有限重试,因此平台离线时 Sink 队列会短暂阻塞。进程崩溃、
|
|
|
|
|
|
断电或告警仍在内存队列中时也可能丢失。平台必须按幂等键去重;对“不可丢告警”的
|
|
|
|
|
|
部署,还需要后续增加有界磁盘 outbox、投递确认和恢复发送。
|
|
|
|
|
|
|
|
|
|
|
|
## 部署风险
|
|
|
|
|
|
|
|
|
|
|
|
- 模型 README 报告某些小样本类别存在漏检,实际园区需要按相机角度、光照、遮挡和距离重新标定置信度与 `min_hits`;规则降噪不能补偿模型系统性漏检。
|
|
|
|
|
|
- 权重目录对权重许可的描述与 Ultralytics runtime/checkpoint 中的 AGPL 信息需要在商业部署前核对;同时确认训练数据来源和权重再分发权利。
|
|
|
|
|
|
- `.pt` 使用 PyTorch checkpoint loader,只加载可信构建和受控分发的权重,并固定、验证实际 Ultralytics 8.x 版本。
|
|
|
|
|
|
|
|
|
|
|
|
如果检测结果还要驱动机器人,控制路径必须保持:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
DetectionResult -> policy -> RobotCommand/v1
|
|
|
|
|
|
-> safety.robot_command_gate@1
|
|
|
|
|
|
-> ApprovedRobotCommand/v1
|
|
|
|
|
|
-> cmvr.grpc.agv_command_sink@1
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
编译器要求 actuator 的每个直接前驱都是安全门,安全门和执行器之间不能插入普通变换节点,也不能存在绕过路径。
|