9.3 KiB
9.3 KiB
PRD_05:Device 侧轻量级 Tracker 节点(可插拔)
1. 背景与问题
当前 Device 侧媒体服务采用“配置驱动 DAG(Graph)+ 插件节点(Node)+ SPSC 队列”的流水线架构:输入(RTSP/文件)→ 预处理 → AI 推理(检测/识别等)→ OSD/推流/告警。
在实际部署中常见三类痛点:
- 重复报警:同一目标在画面连续出现时,规则可能在采样频率下重复触发。
- 重复上传(MinIO/HTTP 等):报警动作触发后在短时间内重复上传同类素材。
- 推理开销过高:虽然已有
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,以支持:
- 按目标实例去重(基于
track_id):同一目标在持续存在期间不重复触发/上传。 - 稳定性/精准性优先,资源占用尽可能低:不引入 ReID/特征网络,主要使用几何与置信度关联。
- 按类别选择性追踪:并非所有类别都追踪,可由配置指定哪些
cls_id参与追踪。 - 架构适配:完全符合现有 Graph/Node 插件机制,插入与否由
configs/*.json决定;不插入则不改变任何行为。
3. 非目标(Non-Goals)
- 不做跨摄像头/跨 Graph 的关联(不做全局 ID)。
- 不做基于外观特征(ReID)的强一致性追踪(如 DeepSORT+ReID)。
- 不保证对极端遮挡/快速运动/密集重叠场景达到 SOTA 跟踪指标;本 PRD 目标是“工程可用+低资源”。
4. 现有架构约束(必须遵守)
- Node 单输入、多输出:Graph 当前限制每个节点 只能有一个 input queue。
- Node 的
Process(FramePtr)由框架线程调用(Graph::Start()中的 worker loop),Tracker 节点不应引入额外线程(除非明确需求)。 - 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 关联)
在不引入外观特征的前提下,为提高稳定性,采用“两段式关联”策略:
核心思想:
- 将 det 按置信度分成高置信(
score >= high_th)与低置信(low_th <= score < high_th)。 - 优先用高置信 det 去匹配已有 tracks;未匹配的 tracks 再用低置信 det 做二次匹配(减少断轨)。
- 匹配度量使用 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 插入位置建议(不强制)
- 仅为“告警去重/上传去重”服务:
- 推荐
publish -> tracker -> alarm - 原因:不影响推流;不破坏
publish写入的frame->user_meta(clip 仍可用)。
- 推荐
- 需要 OSD 展示稳定 ID:
- 推荐
ai_* -> tracker -> osd -> ...
- 推荐
6. 配置(Config Schema)
6.1 tracker 节点配置字段
节点 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):
"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_activetracks_created_totaltracks_removed_totalmatched_totalunmatched_dets_totalavg_process_time_ms
8.2 Debug
debug.stats:周期打印关键统计。- 不输出过多 per-frame 日志,避免影响实时性。
9. 性能与资源约束
目标(以单路为单位,具体需实测校准):
N_det <= 64时,每帧 Tracker 处理耗时应为毫秒级以下(通常 < 1ms,依平台不同)。- 内存:track 状态结构固定上限
max_tracks,避免无界增长。
10. 验收标准(Acceptance Criteria)
- 可插拔:不配置
tracker节点时,系统行为与当前版本一致。 - 正确写入 track_id:配置启用时,目标类别的 det
track_id在连续帧中稳定且可复用。 - 按类别控制有效:不在白名单/在黑名单的
cls_id必须保持track_id=-1。 - 稳定性:短暂漏检场景下(
max_age_ms内),track 不应频繁抖动创建新 ID。 - 资源可控:
max_tracks生效;极端场景不会导致内存持续增长。
11. 开发拆解(实现步骤)
- 新增插件目录与编译接入:
plugins/tracker/,更新plugins/CMakeLists.txt注册编译。 - 实现
TrackerNode:Init()解析配置。Process():过滤模型/类别 → 运行 ByteTrack-lite → 写track_id→ 推送下游。UpdateConfig():支持热更新(可退化为“关键字段变更则拒绝原地更新”)。GetCustomMetrics():输出统计。
- 增加一个示例配置(不强制提交到 README):可在现有
configs/*中添加一份测试 json(若产品流程允许)。
12. 增强建议(不属于本 PRD 必做,但推荐路线)
- Alarm 去重升级为 per-track:在
alarm节点内部维护(rule_name, track_id)的冷却/状态机(enter/stay/leave)。 - 自适应推理频率:结合最近 N 秒的命中情况动态调整
infer_fps(需与现有热更新机制配合)。 - Hungarian 可选开关:当 det 数量大且遮挡多时提升一致性,但要评估 CPU 成本。