2026-04-20 13:51:58 +08:00
|
|
|
|
# yolo + stable track 说明
|
|
|
|
|
|
|
|
|
|
|
|
这套逻辑的目标不是直接相信 ByteTrack 的 `raw id`,而是维护一个更稳定的 `stable id`。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 基本概念
|
|
|
|
|
|
|
|
|
|
|
|
- `raw id`
|
|
|
|
|
|
ByteTrack 当前帧给出的轨迹号,只表示短时 2D 跟踪结果,可能抖动、复用、换人。
|
|
|
|
|
|
- `stable id`
|
|
|
|
|
|
本节点自己维护的长期身份号,用于:
|
|
|
|
|
|
- `/target_observation`
|
|
|
|
|
|
- `/track_observations`
|
|
|
|
|
|
- RViz marker
|
|
|
|
|
|
- ReID gallery
|
|
|
|
|
|
- `primary target`
|
|
|
|
|
|
当前跟随目标。它只是某一个 `stable id`,不是单独一套编号。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 当前关联逻辑
|
|
|
|
|
|
|
|
|
|
|
|
每一帧大致按下面顺序运行:
|
|
|
|
|
|
|
|
|
|
|
|
1. `YOLO pose`
|
|
|
|
|
|
2. `ByteTrack`
|
|
|
|
|
|
3. `raw-id fast path`
|
|
|
|
|
|
- 如果某个 row 的 `raw id` 能续上上一帧同一个 `stable id`,先临时绑定
|
|
|
|
|
|
- 注意:这一步现在只是“初始猜测”,不是最终判决
|
|
|
|
|
|
4. `ReID`
|
|
|
|
|
|
- 对还没绑定的 row,和所有已有 `stable id` 的 gallery 做相似度比较
|
|
|
|
|
|
- 若满足 `match_threshold` 且领先第二名至少 `gap_threshold`,直接回绑旧 `stable id`
|
|
|
|
|
|
5. `single-person 3D fallback`
|
|
|
|
|
|
- 只在单人场景下启用
|
|
|
|
|
|
- 只服务当前 `primary target`
|
|
|
|
|
|
- 用于 ReID crop 不可用时的兜底恢复
|
|
|
|
|
|
6. `new stable id allocate`
|
|
|
|
|
|
- 如果和所有旧 `stable id` 都无法匹配
|
|
|
|
|
|
- 且同一个 ByteTrack `raw id` 连续出现满 `target_reid_confirm_hits_before_allocate`
|
|
|
|
|
|
- 才会申请一个新的 `stable id`
|
|
|
|
|
|
|
|
|
|
|
|
## 3. stable id 的原则
|
|
|
|
|
|
|
|
|
|
|
|
- `stable id` 是长期身份,不是当前画面里的顺位号
|
|
|
|
|
|
- 某个目标暂时丢失时,它原来的 `stable id` 会继续保留,直到 timeout age out
|
|
|
|
|
|
- 不能因为画面里少了一个人,就把别人的 `stable id` 顶上去
|
|
|
|
|
|
|
|
|
|
|
|
这也是为什么:
|
|
|
|
|
|
- `raw id` 可以变
|
|
|
|
|
|
- 但 `stable id` 应该尽量不变
|
|
|
|
|
|
|
|
|
|
|
|
## 4. gallery 更新原则
|
|
|
|
|
|
|
|
|
|
|
|
`gallery` 现在不会随意更新。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 什么时候能进 gallery
|
|
|
|
|
|
|
|
|
|
|
|
只有在下列情况之一时,当前 feature 才允许写进对应 `stable id`:
|
|
|
|
|
|
|
|
|
|
|
|
- 该 row 通过 ReID 成功回绑到旧 `stable id`
|
|
|
|
|
|
- 该 row 已经拿到正式 `stable id`,并且到了定期 refresh 时机
|
|
|
|
|
|
- 该 row 是经过连续确认后刚申请到的新 `stable id`
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 什么时候不允许更新
|
|
|
|
|
|
|
|
|
|
|
|
如果一个 row 是通过 `raw-id fast path` 临时续上的,但 ReID 明显反对这个绑定,则:
|
|
|
|
|
|
|
|
|
|
|
|
- 该帧不会更新这个 `stable id` 的 gallery
|
|
|
|
|
|
- 必要时会直接撤销 fast-path 绑定,让后续 ReID 重新决定该 row 属于哪个旧 `stable id`
|
|
|
|
|
|
|
|
|
|
|
|
这样做是为了避免:
|
|
|
|
|
|
|
|
|
|
|
|
- ByteTrack 一次短时漂移
|
|
|
|
|
|
- 把错人的 feature 写进旧 `stable id`
|
|
|
|
|
|
- 进而污染整个 gallery
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 fallback dist 会不会更新 gallery
|
|
|
|
|
|
|
|
|
|
|
|
不会。
|
|
|
|
|
|
|
|
|
|
|
|
`fallback dist` 只负责在单人场景下帮 `primary target` 暂时保住 `stable id`,不负责更新外观库。
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 默认关键参数
|
|
|
|
|
|
|
|
|
|
|
|
这些参数都在:
|
|
|
|
|
|
|
|
|
|
|
|
- [control_command.yaml](./config/control_command.yaml)
|
|
|
|
|
|
|
|
|
|
|
|
当前常用参数:
|
|
|
|
|
|
|
|
|
|
|
|
- `target_reid_gallery_size: 8`
|
|
|
|
|
|
每个 `stable id` 最多缓存 8 个 ReID feature
|
|
|
|
|
|
- `target_reid_feature_update_interval: 5`
|
|
|
|
|
|
最多每 5 帧尝试刷新一次 gallery
|
|
|
|
|
|
- `target_reid_match_threshold: 0.65`
|
|
|
|
|
|
- `target_reid_gap_threshold: 0.10`
|
|
|
|
|
|
- `target_reid_lost_timeout_frames: 150`
|
|
|
|
|
|
- `target_reid_confirm_hits_before_allocate: 5`
|
|
|
|
|
|
新人申请新 `stable id` 前,要求同一个 ByteTrack `raw id` 连续确认 5 帧
|
|
|
|
|
|
- `target_reid_3d_fallback_enabled: 1`
|
|
|
|
|
|
单人场景启用 3D fallback
|
|
|
|
|
|
|
|
|
|
|
|
## 6. debug topic
|
|
|
|
|
|
|
|
|
|
|
|
假设 `sync_topic_prefix=/odin/sync`,则当前会有这些调试输出:
|
|
|
|
|
|
|
|
|
|
|
|
- `/odin/sync/detection_img_debug/compressed`
|
|
|
|
|
|
调试图。未分配 `stable id` 的 detection 画白色;已分配 `stable id` 的 detection 画对应彩色。
|
|
|
|
|
|
- `/odin/sync/gallery_debug`
|
|
|
|
|
|
原始 `sensor_msgs/Image`,不是 compressed。
|
|
|
|
|
|
用于看 ReID gallery 当前缓存了哪些人像 crop。
|
|
|
|
|
|
当前布局是:
|
|
|
|
|
|
- 最多 8 行,对应 8 个 `stable id`
|
|
|
|
|
|
- 10 列,对应 gallery 槽位
|
|
|
|
|
|
|
|
|
|
|
|
此外还有:
|
|
|
|
|
|
|
|
|
|
|
|
- `/odin/sync/target_observation`
|
|
|
|
|
|
当前 primary target
|
|
|
|
|
|
- `/odin/sync/track_observations`
|
|
|
|
|
|
当前帧所有 fresh 3D 的 stable tracks
|
|
|
|
|
|
- `/odin/sync/detection_pos_cam`
|
|
|
|
|
|
- `/odin/sync/detection_pos_world`
|
|
|
|
|
|
|
|
|
|
|
|
## 7. debug_reid
|
|
|
|
|
|
|
|
|
|
|
|
开启:
|
|
|
|
|
|
|
|
|
|
|
|
- `debug_reid: 1`
|
|
|
|
|
|
|
|
|
|
|
|
后,会打印每一帧的 ReID 相似度矩阵,格式类似:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
[ReIDMatrix] prev_sid=1 bytetrack_id=[2,1] stable_id=[1,0] sims=[1->[sid0:0.905,sid1:0.508],2->[sid1:0.938,sid0:0.489]]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
解释:
|
|
|
|
|
|
|
|
|
|
|
|
- `prev_sid`
|
|
|
|
|
|
上一时刻的 primary stable id
|
|
|
|
|
|
- `bytetrack_id=[...]`
|
|
|
|
|
|
当前帧 ByteTrack row 的 raw ids
|
|
|
|
|
|
- `stable_id=[...]`
|
|
|
|
|
|
当前系统最终给每个 row 绑定到的 stable ids
|
|
|
|
|
|
- `sims=[...]`
|
|
|
|
|
|
每个 ByteTrack row 和所有已有 `stable id` gallery 的相似度
|
|
|
|
|
|
|
|
|
|
|
|
这个日志主要用来排查:
|
|
|
|
|
|
|
|
|
|
|
|
- 为什么某个 row 没能回绑到旧 `stable id`
|
|
|
|
|
|
- 是 ByteTrack 漂了,还是 gallery 被污染了
|
|
|
|
|
|
- 当前绑定结果和 ReID 结果是否一致
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 当前已知设计取向
|
|
|
|
|
|
|
|
|
|
|
|
这版实现明确偏向下面的目标:
|
|
|
|
|
|
|
|
|
|
|
|
- `stable id` 尽量稳定
|
|
|
|
|
|
- 允许 ByteTrack 暂时失败
|
|
|
|
|
|
- 不允许一次错误 fast-path 直接污染 gallery
|
|
|
|
|
|
- 在多人场景下,优先相信 ReID,不用 3D fallback 去硬改多人 stable id
|
|
|
|
|
|
|
|
|
|
|
|
如果后面还要继续调,最值得重点观察的是:
|
|
|
|
|
|
|
|
|
|
|
|
- `debug_reid` 的矩阵输出
|
|
|
|
|
|
- `/odin/sync/gallery_debug`
|
|
|
|
|
|
- 某个 `stable id` 的 gallery 是否被混入了别人的 crop
|