193 lines
9.3 KiB
Markdown
193 lines
9.3 KiB
Markdown
# PRD_05:Device 侧轻量级 Tracker 节点(可插拔)
|
||
|
||
## 1. 背景与问题
|
||
当前 Device 侧媒体服务采用“配置驱动 DAG(Graph)+ 插件节点(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 loop),Tracker 节点不应引入额外线程(除非明确需求)。
|
||
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。
|
||
- 更新:匹配成功 → 更新 bbox、last_seen、hit_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 成本。
|