OrangePi3588Media/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

9.3 KiB
Raw Blame History

PRD_05Device 侧轻量级 Tracker 节点(可插拔)

1. 背景与问题

当前 Device 侧媒体服务采用“配置驱动 DAGGraph+ 插件节点Node+ SPSC 队列”的流水线架构输入RTSP/文件)→ 预处理 → AI 推理(检测/识别等)→ OSD/推流/告警。

在实际部署中常见三类痛点:

  1. 重复报警:同一目标在画面连续出现时,规则可能在采样频率下重复触发。
  2. 重复上传MinIO/HTTP 等):报警动作触发后在短时间内重复上传同类素材。
  3. 推理开销过高:虽然已有 infer_fps 等限频,但在低推理频率下希望仍保持事件稳定性与可解释性。

现有代码中:

  • Frame::detDetectionResult)包含 Detection{cls_id, score, bbox, track_id},其中 track_id 当前默认 -1(未做追踪)。
  • alarm 侧已有 rules.cooldown_msmin_duration_msactions.*.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 typetracker
  • rolefilter
  • 输入:FramePtr(读取 frame->det
  • 输出:同一个 FramePtr(仅可能修改 frame->det->items[*].track_id
  • frame->det == nullptritems 为空:直接透传。

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_metaclip 仍可用)。
  2. 需要 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_mstrack 允许丢失的时间窗口(基于 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_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_mstrack 不应频繁抖动创建新 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 成本。