safesight-edge/PRD_05_Device_Tracker_Node.md
sladro 9c59c5d310
Some checks are pending
CI / host-build (push) Waiting to run
CI / rk3588-cross-build (push) Waiting to run
开发跟踪
2026-01-11 20:15:27 +08:00

193 lines
9.3 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.

# PRD_05Device 侧轻量级 Tracker 节点(可插拔)
## 1. 背景与问题
当前 Device 侧媒体服务采用“配置驱动 DAGGraph+ 插件节点Node+ SPSC 队列”的流水线架构输入RTSP/文件)→ 预处理 → AI 推理(检测/识别等)→ OSD/推流/告警。
在实际部署中常见三类痛点:
1) **重复报警**:同一目标在画面连续出现时,规则可能在采样频率下重复触发。
2) **重复上传MinIO/HTTP 等)**:报警动作触发后在短时间内重复上传同类素材。
3) **推理开销过高**:虽然已有 `infer_fps` 等限频,但在低推理频率下希望仍保持事件稳定性与可解释性。
现有代码中:
- `Frame::det``DetectionResult`)包含 `Detection{cls_id, score, bbox, track_id}`,其中 `track_id` 当前默认 `-1`(未做追踪)。
- `alarm` 侧已有 `rules.cooldown_ms`、`min_duration_ms` 与 `actions.*.min_interval_ms` 的时间窗限流,但属于“按规则/动作的时间窗”粒度。
## 2. 目标Goals
提供一个 **可选、可插拔**`tracker` 插件节点,用于为检测结果补充稳定的 `track_id`,以支持:
1) **按目标实例去重**(基于 `track_id`):同一目标在持续存在期间不重复触发/上传。
2) **稳定性/精准性优先,资源占用尽可能低**:不引入 ReID/特征网络,主要使用几何与置信度关联。
3) **按类别选择性追踪**:并非所有类别都追踪,可由配置指定哪些 `cls_id` 参与追踪。
4) **架构适配**:完全符合现有 Graph/Node 插件机制,插入与否由 `configs/*.json` 决定;不插入则不改变任何行为。
## 3. 非目标Non-Goals
1) 不做跨摄像头/跨 Graph 的关联(不做全局 ID
2) 不做基于外观特征ReID的强一致性追踪如 DeepSORT+ReID
3) 不保证对极端遮挡/快速运动/密集重叠场景达到 SOTA 跟踪指标;本 PRD 目标是“工程可用+低资源”。
## 4. 现有架构约束(必须遵守)
1) Node 单输入、多输出Graph 当前限制每个节点 **只能有一个 input queue**
2) Node 的 `Process(FramePtr)` 由框架线程调用(`Graph::Start()` 中的 worker loopTracker 节点不应引入额外线程(除非明确需求)。
3) Tracker 节点不得破坏 `Frame` 上其他元信息(尤其是 `publish` 节点可能写入 `frame->user_meta` 用于 clip
## 5. 功能设计
### 5.1 节点定义
- Node type`tracker`
- role`filter`
- 输入:`FramePtr`(读取 `frame->det`
- 输出:同一个 `FramePtr`(仅可能修改 `frame->det->items[*].track_id`
-`frame->det == nullptr``items` 为空:直接透传。
### 5.2 追踪算法推荐ByteTrack-lite + IOU 关联)
在不引入外观特征的前提下,为提高稳定性,采用“两段式关联”策略:
**核心思想**
1) 将 det 按置信度分成高置信(`score >= high_th`)与低置信(`low_th <= score < high_th`)。
2) 优先用高置信 det 去匹配已有 tracks未匹配的 tracks 再用低置信 det 做二次匹配减少断轨)。
3) 匹配度量使用 IOU可加入 gating中心点距离/面积比等轻量约束)。
**匹配策略**
- 为降低资源占用默认使用 greedy matching IOU 从高到低选择不冲突匹配不强制使用匈牙利算法
- 允许后续通过配置切换到 Hungarian可选增强项但不作为本 PRD 必需
**Track 生命周期**
- 创建未匹配到现有 track 的高置信 det 创建新 track
- 更新匹配成功 更新 bboxlast_seenhit_streak
- 丢失超过 `max_age_ms` `max_age_frames`未匹配 删除
**分类策略**
- 默认按 `cls_id` 分组匹配避免不同类别互相抢 track)。
- 可配置 `per_class=false` 以允许跨类别匹配默认不建议)。
### 5.3 按类别选择性追踪
支持以下配置至少实现其一推荐两者都实现
- `track_classes`: `int[]`白名单非空时仅追踪这些 `cls_id`
- `ignore_classes`: `int[]`黑名单用于排除某些 `cls_id`
规则
- `track_classes` 非空则优先按白名单过滤
- 否则按 `ignore_classes` 排除
- 未参与追踪的 det 必须保持 `track_id = -1`
### 5.4 按模型类型/推理类型启用(可选,但推荐)
`DetectionResult.model_name` 已存在Tracker 支持
- `allowed_models`: `string[]`当非空时仅对 `model_name` 在集合内的帧启用
用途同一 pipeline 可能挂多种推理节点yolo/face_det/自定义模型避免对不需要的输出做追踪
### 5.5 插入位置建议(不强制)
1) 仅为告警去重/上传去重服务
- 推荐 `publish -> tracker -> alarm`
- 原因不影响推流不破坏 `publish` 写入的 `frame->user_meta`clip 仍可用)。
2) 需要 OSD 展示稳定 ID
- 推荐 `ai_* -> tracker -> osd -> ...`
## 6. 配置Config Schema
### 6.1 tracker 节点配置字段
节点 JSON 示例
```json
{
"id": "trk_cam1",
"type": "tracker",
"role": "filter",
"enable": true,
"mode": "bytetrack_lite",
"per_class": true,
"track_classes": [0, 2, 3],
"ignore_classes": [],
"allowed_models": ["yolov5", "yolov8"],
"high_th": 0.5,
"low_th": 0.1,
"iou_th": 0.3,
"max_age_ms": 1500,
"min_hits": 2,
"max_tracks": 128,
"debug": {
"stats": true,
"stats_interval": 200
}
}
```
字段说明
- `mode``"off" | "bytetrack_lite"`最少实现这两种
- `per_class`默认 `true`
- `track_classes`白名单默认空表示全类别)。
- `ignore_classes`黑名单默认空
- `allowed_models`默认空表示不过滤)。
- `high_th/low_th`置信度分段阈值
- `iou_th`匹配阈值
- `max_age_ms`track 允许丢失的时间窗口基于 `frame->pts` 计算 pts 无效则可退化为帧计数)。
- `min_hits`一个 track 连续命中次数达到后才稳定输出”(用于减少误检带来的短暂 track)。
- `max_tracks`上限保护避免极端场景内存增长
### 6.2 Graph 接入示例
在现有 graph 中插入示例`pub -> tracker -> alarm`
```json
"nodes": [
{"id":"pub_cam1","type":"publish","role":"filter","enable":true, ...},
{"id":"trk_cam1","type":"tracker","role":"filter","enable":true, ...},
{"id":"alarm_cam1","type":"alarm","role":"sink","enable":true, ...}
],
"edges": [
["post_cam1","pub_cam1"],
["pub_cam1","trk_cam1"],
["trk_cam1","alarm_cam1"]
]
```
## 7. 对外接口与数据契约
### 7.1 输入输出契约
- 输入:`frame->det` 必须由上游检测节点填充Tracker 不负责生成 det。
- 输出:
- `frame` 本体不变;
- `frame->det->items[*].track_id` 可能从 `-1` 变为 `>=0`
- 不修改 `frame->user_meta`、不修改 `frame->data/planes`
### 7.2 与 alarm/http/minio 的配合
- `plugins/alarm/actions/http_action.cpp` 已会输出 `track_id` 字段(现有实现),因此 Tracker 生效后 HTTP 报警可以自然携带 `track_id`
- MinIO 上传去重当前依赖 `actions.*.min_interval_ms`;后续可扩展为 per-track 去重(见第 10 节增强建议)。
## 8. 指标与可观测性
### 8.1 Custom Metrics
Tracker 节点建议实现 `GetCustomMetrics()` 输出:
- `tracks_active`
- `tracks_created_total`
- `tracks_removed_total`
- `matched_total`
- `unmatched_dets_total`
- `avg_process_time_ms`
### 8.2 Debug
- `debug.stats`:周期打印关键统计。
- 不输出过多 per-frame 日志,避免影响实时性。
## 9. 性能与资源约束
目标(以单路为单位,具体需实测校准):
- `N_det <= 64` 时,每帧 Tracker 处理耗时应为毫秒级以下(通常 < 1ms依平台不同)。
- 内存track 状态结构固定上限 `max_tracks`避免无界增长
## 10. 验收标准Acceptance Criteria
1) **可插拔**不配置 `tracker` 节点时系统行为与当前版本一致
2) **正确写入 track_id**配置启用时目标类别的 det `track_id` 在连续帧中稳定且可复用
3) **按类别控制有效**不在白名单/在黑名单的 `cls_id` 必须保持 `track_id=-1`
4) **稳定性**短暂漏检场景下`max_age_ms` track 不应频繁抖动创建新 ID
5) **资源可控**`max_tracks` 生效极端场景不会导致内存持续增长
## 11. 开发拆解(实现步骤)
1) 新增插件目录与编译接入`plugins/tracker/`更新 `plugins/CMakeLists.txt` 注册编译
2) 实现 `TrackerNode`
- `Init()` 解析配置
- `Process()`过滤模型/类别 运行 ByteTrack-lite `track_id` 推送下游
- `UpdateConfig()`支持热更新可退化为关键字段变更则拒绝原地更新”)。
- `GetCustomMetrics()`输出统计
3) 增加一个示例配置不强制提交到 README可在现有 `configs/*` 中添加一份测试 json若产品流程允许)。
## 12. 增强建议(不属于本 PRD 必做,但推荐路线)
1) **Alarm 去重升级为 per-track** `alarm` 节点内部维护 `(rule_name, track_id)` 的冷却/状态机enter/stay/leave)。
2) **自适应推理频率**结合最近 N 秒的命中情况动态调整 `infer_fps`需与现有热更新机制配合)。
3) **Hungarian 可选开关** det 数量大且遮挡多时提升一致性但要评估 CPU 成本