VLM 描述选目标:一句「追踪红色小轿车」如何变成一个 ID
用户不会总记得 ID。当门控传回「有描述、无 id」时,VLM 网格登场:把当前帧切成带编号的缩略图网格、让 Qwen3.5-0.8B_vision 只答一个数字、再用 cell_ids[k-1] 把格子映射回追踪 ID。cell_cap=16 的硬上限、0/99 拒答、三路径 fail-closed 共同保证"选不准就不锁"。
01问题引入:说颜色怎么锁 ID
你说「追踪红色小轿车」,可系统内部流通的只有 DeepSORT 的数字 ID(id18、id24…)。颜色词与 ID 之间隔着一道翻译鸿沟——LLM 只负责听懂并吐出 track_vlm(EXP-05 第③口),真正"看图认车"的活落在 VLM 头上:
02原理通俗讲解:先看缩略图再放大
VLM 网格就像先看缩略图再放大细节——监控室值班员不是把每辆车放大数车牌,而是先在九宫格缩略图上圈"红色那格",再看该格里的具体目标。
build_grid(vlm_real.py L175-213)把帧切成 rows×cols 网格,每格标 1..n 的大号数字、空位标 ?,统一压到 448×448。格子编号是给模型看的"座位号"。
L170-185 按排序键 (|r-c|, 格子数, min(r,c), r) 在候选布局中挑一个——让网格尽量方正、优先 2×2 这类小布局,候选多了才升 4×4。
「图片是 R 行 C 列的网格…只答数字 1..N,找不到答 0」(grid_select_prompt L215-240);故意不提供 99 这个"逃生口"(L218 注释)——模型答 0/99/越界一律按拒答处理。
_parse_tile(L241-263)把「3」还原成 k=3,select_crops(L433-473)在 L471 取 cell_ids[k-1]。座位号 ≠ 追踪 ID,这一步才是换算。
_vlm_none(track_app.py L402-405,打 WARN + 引导话术、绝不 submit)——拿不准宁不放行。03关联知识点
「描述有、id 无」才走 track_vlm;有 id 走 ID 对齐、纯清除走 clear_track——VLM 只接门控筛剩下的那一类话。
选中的 ID 经门控放行后进 maxsize=8 队列 → 主循环 apply → DeepSORT 按 min_confidence=0.3 等规则继续跟(EXP-04)。
llm_mgr 拉起日志 Qwen3.5-0.8B_w4a16_g128_rk3576.rkllm … --port 8080,首载 30-60s;VLM 懒加载,首次选车前才实例化。
test_vlm_grid / test_vlm_real / test_vlm_fake;track_config.yaml L33 fake_result_id=3 → FakeVLMSelect(vlm_fake.py L14-18)无真模型也能全链路回归。
04实验目标
- 用 dump_track_grids 生成带标签网格图,读懂 JSON 里的 cell_cap / order / cells / 448×448
- 跑 test_vlm_select 传入
--color 红,核对 frame200(2×2,4 id)与 frame400(4×4,11 id)的选格结果 - 手推公式
cell_px=⌊448/max(rows,cols)⌋、k=r×cols+c+1、id=cell_ids[k-1] - 复现一段成功链路日志:[cmd] 描述指令 → track_vlm → [track_vlm] 锁定目标 ID
- 构造一个失败场景(候选 > 16 或模型答 0),确认走 _vlm_none 引导话术、ID 不被锁定
05实验环境与准备
用视频 + 文本模式即可复现全链路(摄像头模式同理,只是帧源不同)。测试可先走 fake_result_id 假件,再换真 VLM:
# ① 测试(假件链,不依赖真模型) python3 -m pytest tests/ -q -k "vlm" # ② 启动文本模式(llm_server 由 ensure_running 自动拉起) python3 apps/track_app.py \ --video /home/iflytek/ros2_ws/src/xflz_eai_car1/vision/07_multi_object_tracking/test.mp4 \ --text --mjpeg # ③ 配置确认(fake_result_id 位置) grep -n "fake_result_id" config/track_config.yaml # L33 默认 3
fake_result_id(FakeVLMSelect 固定返回该 ID)——把"映射逻辑对不对"与"模型认得准不准"分成两件事验证。06动画讲解:切格 → 选格 → 锁 ID
一笔动画看完三步:帧先被切成带编号的 2×2 网格,VLM 只答一个座位号,最后换算成追踪 ID。红色虚线出口代表 fail-closed——任何一步拿不准都滑向那里:
07互动讲解:网格选格演练台
扮演 VLM 做一遍选择题:给定网格布局与候选 ID,选一格看映射结果,再故意触发一次 fail-closed:
cell_px=⌊448/max(rows,cols)⌋(2×2 → 224)→ k=r×cols+c+1(第 2 行第 1 列 → 3)→ id=cell_ids[k-1]。三步任一错位,锁到的就是旁边那辆车。08实操步骤:dump 网格与 VLM 选车
# ① 抽帧切网格(frame-step 200 → 第 200/400 帧) python3 apps/dump_track_grids.py \ --video /home/iflytek/ros2_ws/src/xflz_eai_car1/vision/07_multi_object_tracking/test.mp4 \ --frame-step 200 → runs/vlm_frames/grid_f200_rx2c2.jpg / .json(含 cell_cap、order、cells、448×448) # ② 指定颜色选格 python3 apps/test_vlm_select.py \ --image runs/vlm_frames/grid_f200_rx2c2.jpg \ --json runs/vlm_frames/grid_f200_rx2c2.json --color 红 frame200 n=4 ids=[29,30,32,33] grid 2×2 → PROMPT → 答 '3'(5s)→ cell_ids[2]=32 frame400 n=11 → 4×4 网格(仍 ≤ cell_cap=16) # ③ 完整链路成功日志(--text 模式逐句验证) [cmd] 追踪红色小轿车 → native tool=track_vlm [cmd] track_ids 无 id 但含描述,改 VLM 选车 [track_vlm] dump 带标签网格 → [track_vlm] 锁定目标 ID 24 # ④ 失败链路(如「追踪火车」画面中不存在) [cmd] track_ids 无可用 id,回引导 —— 不锁定任何 ID # ⑤ 行号锚点 grep -n "def build_grid\|def _pick_grid\|def _parse_tile" vision/.../vlm_real.py grep -n "_vlm_none" apps/track_app.py # L402-405 grep -n "cell_cap" apps/track_app.py config/track_config.yaml
09重点与易错点
- 座位号 ≠ 追踪 ID:格子 k 是给模型看的,真正 ID 必须经
cell_ids[k-1];直接把 k 当 ID 会锁错目标 - cell_cap=16 是硬上限:候选框超过 16 个直接 None——宁可让用户换个描述,也不让模型在 16+ 格里盲选
- 0/99/越界一律拒答:_parse_tile 的宽容解析不是"尽力猜",而是把所有歧义都送进同一条 fail-closed 路
- VLM 是懒加载:第一次说颜色指令才实例化模型,首载 30-60s 内"没反应"是正常加载,不是卡死
- 实验完确认锁定状态再收尾:失败链路必须停在引导话术,锁定了 ID 说明门控或映射有漏
| ⚠️ 常见坑 | 现象 → 解法 |
|---|---|
| 选中格却锁了隔壁车 | k 与 cell_ids 下标错位 → 核对 cell_ids[k-1] 与 _pick_grid 的 order 顺序是否一致 |
| n=11 却仍是 2×2 | 先看 _pick_grid 排序键 (|r-c|, 格子数, …) 的输出 → 必要时读 dump JSON 的 order 字段 |
| 模型回了长句子不给数字 | _parse_tile 解析不到 1..N → 按拒答处理进 _vlm_none,检查 PROMPT 是否完整下发 |
| 测试与真机结果不一致 | fake_result_id=3 固定返回 → 跑真链路先清掉/留档假件再验 |
| 首载 60s 以为挂了 | llm_mgr 30-60s 属正常 → 看「Qwen3.5-0.8B…--port 8080」拉起日志 |
10总结与思考
grid_f200/*.json 在手,color=红 的选格结果与 cell_ids 换算过程可完整复述。
把"从图里找 ID"降维成"四选一选格",模型小也能做对——这是端侧 VLM 的工程智慧。
FakeVLMSelect 验映射逻辑,真模型验识别能力,两层分开跑,归因不慌。
dump 带标签网格 → 锁定目标 ID / 无法识别——三段日志把"切、选、映"拆开定位。