← 返回报告索引
2026-09-04 · 执行全记录(按跑的顺序)

远程四件事:我按什么顺序跑的、每一步看到了什么、哪几步走错了

对应 HANDOFF-REMOTE-2026-09-04.md 的 A/B/C/D,全程没碰硬件。 这一份按执行顺序写,包括走错的和自己改回来的 —— 那几步恰恰是这次信息量最大的地方。 只想看结论的看另一份按结论组织的。 末尾是需要你考虑的 7 个问题,每个都附了我的建议。

⚠ 2026-09-04 二次更正 —— 操作者看图时指出三处,全部证实,结论改了一半。
操作者看第 4 步那张候选图时指出:① 左上角那个是 VR 手柄;② 桌上还有白洞洞也被框了; ③ 我叫「背景白玻璃杯」的其实是蓝色塑料杯;随后又指出 ④ 我说的「天花板通风口」就是桌面那个白洞洞。 把 43 个框逐个裁出来放大核对,四条全对。头相机是俯视,画面上方是桌子远端,我把桌面穿线孔当成了天花板。

后果是结论说轻了:我原来写「识别没坏,只是选错杯子」, 但 OWL-ViT 16 个框里有 6 个根本不是杯子,GDINO 27 个里26 个不是。 本页第 5、6、11 步的数字和第 4 步的结论已就地改正,并新增第 24 步记录这次复核。 逐框记录 05-training/detect/manual_review.json。
怎么读左边那条竖线的颜色: 绿 = 转折点(结论因此改变)· 红 = 死路(试了,不通,记下来免得重试)· 黄 = 我自己的错,后来更正的。

目录

0 · 环境核对 1 · 拉 OWL-ViT,第一版检测器 → 0/6 2 · 看图:全局最高分被整幅「桌子」框吃掉 3 · 改成按类挑框 → 10/12,看图还是错 4 · ★ 打印全部候选 → 转折点 5 · 正式 36 帧 OWL-ViT → 16/36 6 · Grounding DINO → 75%,是通风口 7 · 摘掉毒 query → 2/36,反证 8 · 仓库里早就有一个 COCO 检测器 9 · 试「挑最大的框」当消歧 → 没救回来 10 · 腕相机 37.5% —— 是我的过滤器的错 11 · A 判定 12 · B:先决定「怎么砍相机才算数」 13 · 第一次跑崩了 14 · ★ 1337 ms 和文档的 146 ms 对不上 15 · 加分块口径 + ACT 对照 16 · B 判定 17 · C:读五个文件,发现第三个模型 18 · ★ OpenCV 5 删掉了 CascadeClassifier 19 · C3:独立复核 19453 帧 20 · C4:递交文献 21 · D:先写自检,再写功能 22 · D:接进去,--dry 验 23 · 自测与发布 24 · ★ 复核:43 个框逐个裁开看 ★ 需要你考虑的 7 个问题

0 · 环境核对

0先验交接文档说的环境事实,不然全白跑

交接 §0 说「量任何模型耗时都必须用 train310,用 lerobot 会得到一个假的跑不动」。先验:

项实测
磁盘34 G / 116 G 可用(跑完剩 32 G)
train310torch 2.8.0,cuda.is_available() = True,transformers 5.15.1
数据集本地 14 批,全在 xlerobot-team/ 下

✅ 交接说的都对。往下走。

A · 离线跑开放词表检测

1拉 OWL-ViT(655 MB),写第一版检测器 → 0/6

第一版的选框规则是取全局最高分的那个框,并且用「干扰项 query」(robotic arm / table / human hand …) 让机械臂有自己的标签可归 —— 想法是:只要最高分那个框的标签不是 cup 类,这一帧就记未检出。 这道闸正是前三次缺的。

结果 0/6。

2看图:全局最高分被整幅画面的「a table」吃掉了

第一版:每帧都是整幅红框
六帧全是覆盖整幅画面的红框(红 = 判定非杯子)。而米色目标杯明明就在桌面中央。

打印原始分数才看明白:

frame 0     a table  0.235  (422x227 整幅)   ← 全局最高分
            a robot gripper 0.219
            a mug    0.138  (53x52)          ← 真的杯子,排第 3
frame 5495  a table  0.348  (421x230 整幅)   ← 又是它
            a cup    0.133  (50x50)          ← 真的杯子,排第 8

是我的选框规则错了,不是模型不行。桌子确实是桌子,它有权拿到最高分; 错在我拿「全局第一名」去回答「杯子在哪」。改成两条规则: ①只在标签属于 cup 类的框里挑;②丢掉面积超过全帧 25% 的框。

3改完 → 10/12 = 83%,但看图还是错的

改完之后:框全在左上角背景玻璃杯上
出框率一下到 83%,看着像成功了。但绿框一次也没框到桌面中央的目标杯。 (第一版我写「全钉在背景白玻璃杯上」—— 两处都错:那是一只蓝绿色塑料杯, 而且 12 格里只有 5 格是它,另外 5 格框的是白色 VR 手柄。见第 24 步。) 这是第四次「数字好看、锁错目标」。

如果这一步不看图,我会写「OWL-ViT 83%,接近可用」交给你。GOAL 把人工看图写成不许跳过的一步,是对的。

4★ 转折点:把 cup 类全部候选打印出来

要分清是「模型看不见目标杯」还是「我挑错了」,只能把候选全列出来:

--- frame 0 ---                             --- frame 8242 ---
 #0 0.138 a mug  (7,52,60,103)   背景玻璃杯   #0 0.090 a mug (303,47,381,110) ← 目标杯
 #3 0.044 a cup  (195,65,235,122) ← 目标杯    #1 0.032 a cup (308,48,366,86)  ← 目标杯
--- frame 12363 ---                          #2 0.032 a cup (298,49,360,109) ← 目标杯
 #2 0.040 a cup  (216,79,258,135) ← 目标杯
把 cup 类前 8 个候选框全部画出来
四帧,每帧画出 cup 类的前 8 个候选。目标杯每一帧都在候选里(左上第一格那个洋红框、 右下两格框在杯子上的),只是分数排在背景玻璃杯后面。
操作者在这一步给出了判断,而它是对的:
「一开始我以为他无法识别什么是玻璃杯,但现在看来它可以识别, 那么其实是如何选择杯子的问题……但底层的杯子识别没有错。」
这把「杯子定位」从一个问题拆成了两个:
① 识别(这团是不是杯子)
② 消歧(几只里哪只是目标)—— 从来没做过
前四次全在修①。
⚠ 但我当时把这条推过头了。我写的是「识别一直是好的,只是消歧没做」。 第 24 步逐框核对之后:识别也不行 —— OWL-ViT 那 16 个 cup 框里 6 个是 VR 手柄和夹爪, GDINO 那 27 个里 26 个是桌面圆孔之类。
仍然成立的那半:把候选全部列出来时,目标杯在检查过的帧里都在候选中。 但「候选里有」推不出「识别没坏」—— 同一份候选里也混着非杯子。 正确的说法是:两个问题同时存在。

5正式跑:36 帧、跨 3 个数据集 → 16/36 = 44.4%

3 个数据集各抽 12 帧。不同批次 = 不同杯子,天然覆盖 GOAL 那条「至少换一种杯子」的验收。

数据集出框看图
right-pick-cup-20260826(当前主数据集)10/125 个是另一只蓝绿塑料杯,5 个是 VR 手柄,目标杯 0 个
cup-grasp-20260820(现役模型用的)1/12那 1 个框得很准
left-pick-cup-sep3-202609035/124 个准,1 个框到黑色夹爪
OWL-ViT 在 sep3 上的联系表
它不是不会。同一个模型在 sep3 这批上,第 1、3、7、10 格都紧紧框住了目标杯。 问题是不稳定 + 场上有第二只杯子时选错。

6按交接顺序上 Grounding DINO → 27/36 = 75%,看图是桌面的穿线圆孔

GDINO 把桌面穿线圆孔判成 drinking glass
绿框几乎每帧都在画面上方那个圆形物上,标签 a drinking glass(0.17–0.22)。 第一版我把它写成「天花板通风口」,错了 —— 头相机是俯视,画面上方是桌子的远端, 那是桌面上的穿线圆孔(白色桌面上一个带小孔环的圆洞)。操作者指出后裁图放大证实,见第 24 步。
不管它叫什么,结论方向不变:目标杯清清楚楚在画面里,被完全忽略。 75% 这个数字如果不看图,会被当成好消息报上去。

而且它 2657 ms/帧 —— 比 OWL-ViT 慢 12 倍。

7摘掉毒 query a drinking glass → 2/36,一条反证

既然通风口是被 a drinking glass 钓来的,摘掉它应该变好。结果 GDINO 掉到 2/36 —— 说明它那 75% 几乎全是通风口,真杯子在 0.15 阈值下本来就不出框。

更意外的是:同一条 query 对 OWL-ViT 是主力。摘掉后 OWL-ViT 从 16/36 掉到 12/36。

由此得到一条边界条件:query 集合本身是个变量,而且各模型口味相反。 「换个模型沿用同一套 prompt」不成立 —— 每换一个模型,query 集合要重新过一遍人工看图。

8★ 差点白干:仓库里 2026-09-03 就有一个 COCO 检测器

盘 C 的时候顺手翻到 03-software/conversational_ai/vision/objects.py —— MediaPipe EfficientDet-Lite,COCO 80 类里本来就有 cup 和 wine glass, 文件头写着 65 ms/帧 CPU。交接文档没提它,我差一点又去下一遍别的模型。

它跑在 conversational_ai/.venv(只有那个环境装了 mediapipe), 所以先写了 eval_frames.py 把 36 帧固化成 PNG,保证几个环境比的是同一批画面, 不然分不清差异来自模型还是来自抽帧。

配置出框耗时
Lite0-int8,阈值 0.30/3666 ms CPU
Lite2,阈值 0.17/36 = 19.4%411 ms CPU

不行 —— head 相机只有 424×240,模型内部还会缩到 320,杯子只剩约 38 px, 正好卡在 objects.py 自己写的「40 px 以下看不见」那条线下面。

但这条仍然值钱:下模型之前先查仓库(memory 里「自研前先查上游」那条)。

9试消歧:按框面积最大挑(=「最近的那个杯子」)→ 没救回来

既然问题是消歧,最省事的消歧信号是「哪个杯子离机器人近」。俯视视角下,近的框应该大。 加了 --pick largest 重跑 —— 还是那只背景玻璃杯。

原因:那只玻璃杯离相机更近(在画面左上角、靠镜头),框反而更大。 「离相机近」≠「离机械臂近」。2D 框面积不能当「最近」用。 真正的「最近」得用深度旁路(640×480 16-bit PNG、毫米,要按 100–2000 mm 裁离群值)。

10换 640×480 腕相机 → 37.5%。但这个数是我自己的 bug

腕相机:杯子占满画面却被判 no cup
看红字:no cup (a cup) —— 最高分那个框的标签本来就是 "a cup", 却被判成未检出。因为我在第 2 步加的「框面积不得超过全帧 25%」规则, 在腕相机里把占了半个画面的杯子给滤掉了。第 2 排第 1 格那个 a cup 0.47 才是它本来的水平。

放宽到 85% 重跑:38.9%(sep3 是左臂批次,右腕相机看不到杯子,本来就该低)。

记在这里免得下次又当成模型证据:37.5% 从来不是模型的问题,是我的过滤器的问题。

11A 判定:没过验收,不进入阶段 B

(下表的「是杯子 / 是目标杯」两列是第 24 步逐框放大核对后填的, 第一版只有「出框率」那一列,把这一节读得比实际乐观。)

模型出框率是杯子是目标杯耗时/帧
OWL-ViT base-patch3216/36 = 44.4%10/36 = 27.8%5/36 = 13.9%225 ms GPU
Grounding DINO tiny27/36 = 75%1/36 = 2.8%1/36 = 2.8%2657 ms GPU

真正的命中率是 13.9%,不是 44.4%。

模型出框率耗时/帧备注
GDINO 摘毒 query2/362660 ms基本不出框
EfficientDet-Lite00/3666 ms CPU—
EfficientDet-Lite27/36 = 19.4%411 ms CPU召回太低(这一行未逐框核对)

没有一个接近 90%,最好的一个真实命中率只有 13.9%。

B · 判 B0 生死

12先决定「怎么砍相机才算数」

要量「每多一路相机值多少毫秒」,最容易犯的错是把画面涂黑 —— 涂黑的图照样过 vision encoder、照样产生 token,量出来会是 0,那是个假答案。

去读了上游代码:modeling_smolvla.py:341 present_img_keys = [k for k in config.image_features if k in batch] —— 从 batch 里删键是合法的;再确认本 checkpoint empty_cameras = 0, 所以删掉的键不会被补成黑图。这才是真的少了 token。

13第一次跑崩了

ValueError: (b,c,h,w) expected, but got torch.Size([240, 424, 3])

我自己拼的 observation 少了一步。去看上游线上推理怎么走的 (control_utils.predict_action:82),发现要先过 prepare_observation_for_inference()(HWC→CHW、uint8→float[0,1]、加 batch 维、塞 task)。 照抄这条链,量到的才是线上真实推理的耗时,不是我另拼的一个东西。

14★ 跑出 1337 ms —— 和文档说的 146 ms 差 9 倍

差 9 倍不能糊过去。全仓 grep 「146 ms」,找到源头 run_policy_trials.py:265,原文写的是:

2026-08-21 实测(ACT 51.6M,三路相机,本机 Orin,12 次取热身后均值)
cpu 2397 ms/帧 → 0.4 Hz  cuda 146 ms/帧 → 6.9 Hz
146 ms 是 ACT 的数,不是 SmolVLA 的。 传到交接文档和 GOAL-DEPTH-INTEGRATION.md(两处)时,模型名被换成了 SmolVLA。

15加「分块口径」+ ACT 对照,把这条钉死

同机、同批图、同样的计时方法量 ACT:

模型一次推理(3 路相机)chunk
ACT129.5 ms100 步
SmolVLA1329.2 ms50 步

129.5 和文档的 146 同一量级 —— 归属确认无误。SmolVLA 是它的 10.3 倍。

还发现第二个框架性错误:「6.9 Hz vs 训练 30 Hz」对分块策略根本不成立。 SmolVLA 一次推理规划 n_action_steps = 50 步,在 30 Hz 回路上等于 1667 ms 的动作。 该问的不是「推理频率够不够 30 Hz」,而是一次推理能不能在 1667 ms 内算完:
实测 1329 ms,余量 +337 ms,预算用掉 80%。紧,但没超。 这和「6.9 Hz 远远追不上 30 Hz」是完全不同的处境。

16B 判定:B0 的速度理由毙掉

相机数中位耗时
1 路1210.7 ms
2 路1278.4 ms
3 路1329.2 ms

每多一路 +59.2 ms,三路视觉部分合计 177.7 ms = 总耗时的 13%。 而这是 Y 的上界 —— B0 是把每路 token 从 256 压到 16,不是压到 0,真实 Y 更小。

X(分割模型每帧开销)必须显著小于 177.7 ms,B0 才可能净正。 FT-Dinosaur 是个 ViT,在这块板子上不可能比 177.7 ms 小多少。不用去下它了。 要留 B0,只能靠「结构先验帮泛化」这一条,那就得和 B1/B2 重新比。

C · 盘点 HRI + 修安全层数字错误

17读五个文件(1201 行),顺手发现第三个模型

conversational_ai/models/ 里躺着一个 blaze_face_short_range.task (MediaPipe BlazeFace,229 KB)。全仓 grep 过,没有任何代码引用它 —— 是个没接线的资产,不是第三套实现。记一笔免得有人以为「已经有三套了」,也别顺手接上去。

另外发现一处真实重复:face_follow.py:262 把 Haar 的代码又内联抄了一遍, 而 face_perception.py:159 已经有一个 HaarFaceDetector 类。同一段代码两份拷贝。

18★ C2 不用权衡:OpenCV 5.0.0 删掉了 cv2.CascadeClassifier

环境cv2CascadeClassifier(Haar)FaceDetectorYN(YuNet)
train310(跑策略/脚本的)5.0.0没有有
conversational_ai/.venv5.0.0没有有
lerobot4.13.0有有

实测在 train310 里构造 face_perception.HaarFaceDetector(),直接抛 AttributeError: module 'cv2' has no attribute 'CascadeClassifier'。 face_follow.py:262 调同一个 API,同环境会死在同一个地方 (未单独跑 —— 跑它要开相机、动舵机,属于现场)。

Haar 那套不是「不如 YuNet」,是「现在根本跑不起来」, 而唯一还能跑它的 lerobot 环境恰恰是 torch 没 CUDA、不能跑策略的那个。
决定:留 YuNet。实测 640×480 18.9 ms/帧、320×240 10.0 ms/帧,模型 227 KB 已在仓库里。
只量了耗时,没量准确率 —— 手头数据集画面里没有人脸。 「YuNet 比 Haar 检得准」这条我们没有自己的证据,只靠上游说法。要验得用带人脸的录像。

19C3:独立复核,不照抄交接文档

交接说「实测 19453 帧全部是 49.846」。我自己读了一遍 observation.state[13](DIMS 下标 13 = head_motor_2.pos):

frames: 19453
head_motor_2: min=49.8462  max=49.8462  唯一值数=1

按注释自己那条公式重算(deg*4095/360 + (range_min+range_max)/2,range 1479..2617):

head_2counts对 range_max = 2617
99.649(注释写的)3181.5超出 +564.5
49.8462(实测)2615.0在范围内,差 −2.0

旁证:configs/head_presets.json 的 cup-grasp-v0 tilt = 2619,比 range_max 高 2 counts。 两个独立来源都说标定边界和头的实际位置只差个位数 counts。

用仓库自己的 joint_degree_limits() 换算成度,这就是我们手上可用的头部边界:

head_motor_1.pos   ±149.98 度      (录制时实测 -4.31 度)
head_motor_2.pos   ± 50.02 度      (录制时实测 49.85 度,差 0.18 度到头)

改了什么:policy_safety.py 的 HEAD_LOCK_NOTE 改成「撤回 + 复算 + 说明这撤回了什么」。 钳位行为一个字没动 —— 那是行为变更,按规矩要你拍板。详见下面问题 1。

20C4:人机递交文献(我们从没查过)

文献对我们的直接含义
Ortenzi 等,Object Handovers: A Review for Robotics IEEE T-RO 37(6):1855–1873, 2021 递交分 pre-handover 和 physical exchange 两段。 我们连第一段都没有 —— 没有任何机制和观众就「在哪交、什么时候交」达成约定。
Intent-Handover 2025,n=30 被试内实验 摘要举的例子就是 "the cup"。三条能直接抄: ①抓的位置要给人留出握的地方;②夹爪不能贴着人的手过去; ③用上半身关键点估计接收姿态(YuNet 的 5 个关键点是起点)。
Khanna 等,Impact of Object Weight in Handovers 2025 松手时机是独立的控制问题,不是「到位了就张爪」。而我们的杯子会从空到满变重量。
证据等级:三篇的摘要都拉了 arXiv 原文核对(作者、年份、原话)。 搜索结果里流传的具体数字 ——「递交平均时长约 500 ms」「拉力阈值 3 N 松手」—— 我没核到原文,所以不写进结论。

D · 关键帧采集

21先写自检,再写功能

GOAL 边界条件 2 要求「改完用 --dry 跑一次验证」。但 --dry 在进入试验循环之前就 return 了 —— 光跑它碰不到关键帧那段代码。 所以先把验证做成 trial_keyframes.selftest():合成一段「爪子先开后闭再开」的观测, 把帧号编进像素值,核对挑中的确实是最闭合那一帧 —— 不是只看有没有生成文件。

四个设计选择,都是绕开已知的坑:

坑做法
30 Hz 回路里同步写盘会掉帧回路里只做数组 copy(约 0.1 ms),三张 JPEG 全在循环结束后的 finish() 里写
面板模式不能插 input()本模块完全不与人交互
「闭爪瞬间」拍脑袋定阈值,换夹爪就废定义成整场里爪子开度最小的那一帧 —— 无阈值、无标定
写死 "right" 会在左臂数据上错两只爪子都跟,取行程更大的那只(sep2/sep3 是左臂批次)

22接进 run_policy_trials.py,--dry 验

改之前先 pgrep 确认没人在跑(它会驱动真手臂)。改完 --dry:

  推理成功,动作 (17,)  范围 [-13.29, 49.84]
  安全层判定: OK
== 关键帧采集自检
   ok  作业臂判成 right      ok  闭爪在第 5 个 tick
   ok  init 是第 1 帧        ok  closure 是第 5 帧       ok  end 是最后一帧
  关键帧自检 PASS   (10 项全过)

再端到端过读取端 trials_io.normalize_trial,字段原样读出。写入端和读取端都通了。

23自测与发布

项结果
selfcheck.py(HTML 标签配平 + 7 个 py 的 py_compile)全过
trial_keyframes.py 自检 10 项全过
run_policy_trials.py --dry通过
trials_io 端到端读新字段通过
36 帧联系表人工看图全部看过
安全层行为改动零 —— 只改注释
硬件 / git 写命令全程没碰 / 一条没跑

三份 GOAL 已按「累积原则」回填,包括把 GOAL-DEPTH-INTEGRATION.md 里那两处 146 ms 更正掉。

复核 · 操作者指出三处错误之后重新逐框核对

24★ 把 43 个框全部裁出来放大,逐个看它到底是什么

操作者看第 4 步那张候选图时说:

「实际上它把左上角的 VR 手柄、桌子上的白洞洞都进行了识别, 虽然说右下角的橙色杯子是相对稳定的,左边那个也并非玻璃杯子而是蓝色塑料杯。」
随后又补一句:「其实你说的天花板通风口就是桌上的白洞洞。」

这四条如果成立,我第 4 步那个「识别没坏、只是消歧」的结论就说轻了。 所以写了 crop_review_boxes.py:按 records.json 的框把原帧裁出来、放大到 300 px、拼成表, 逐个看。四条全对。

OWL-ViT 的 16 个 cup 框逐个放大
OWL-ViT 判成 cup 的 16 个框,全在这里。 前 5 个是淡蓝绿色不透明塑料杯(真杯子,但不是目标;不是玻璃,我原来写错了); 第 6–10 个是白色 VR 手柄(带一圈追踪环,根本不是杯子); 第 11–15 个是米色目标杯;最后 1 个是白色夹爪。
Grounding DINO 的 27 个 cup 框逐个放大
Grounding DINO 那 27 个框,26 个不是杯子。 满屏那个白色桌面上带一圈小孔的圆洞,就是桌面穿线孔 —— 第一版我称它「天花板通风口」是错的,头相机俯视,画面上方是桌子远端不是天花板。 唯一一个真杯子是第 11 格(cup-grasp f4863 plastic cup 0.16),而且正好是目标杯。 其余是圆孔 ×10、夹爪/机械臂 ×8、线缆或运动模糊 ×7、鼠标 ×1。

逐框点数的结果

模型出框是杯子是目标杯cup 类精度非杯子的是什么
OWL-ViT16/3610/36 = 27.8%5/36 = 13.9%62.5%VR 手柄 ×5、夹爪 ×1
GDINO tiny27/361/36 = 2.8%1/36 = 2.8%3.7%桌面圆孔 ×10、夹爪/臂 ×8、线缆/模糊 ×7、鼠标 ×1
结论要改一半:两个问题是【同时】存在的,不是只有消歧。
问题证据
①精度不行:cup 类会框到非杯子OWL-ViT 6/16 不是杯子;GDINO 26/27 不是
②消歧没做:场上第二只真杯子会赢OWL-ViT 那 10 个真杯子里,5 个是另一只塑料杯
仍然成立的那半:把候选全部列出来时,目标杯在检查过的帧里都在候选中(第 4 步)。 但「候选里有」推不出「识别没坏」。
★ 这次复核带出一个几乎零成本的动作 ★
实测的假阳性高度集中在两样东西上:VR 手柄(OWL-ViT 5/16)和桌面穿线圆孔(GDINO 10/27)。 手柄是录制时随手放在桌边的,收走就没了;圆孔是桌子的固定特征,盖一下就没了。
在换模型、上深度之前,先把这两样从画面里去掉重录一小批,是整件事里性价比最高的一步。 这不是「写外观规则」—— 是改场景,不是改代码。 (我远程做不了这一步,属于现场。)

脚本 03-software/scripts/crop_review_boxes.py, 逐框记录 05-training/detect/manual_review.json, 裁图 05-training/detect/runlog/crops_*.jpg。

★ 需要你考虑的 7 个问题

每个都附了我的建议。只有问题 3 和 4 会挡住下一步,其余可以延后。

1 · 头部钳位:现在改,还是跟「头部跟随」一起做?

安全层第 3 步把头的两个值整个覆盖成启动位置,第 4 步的绝对钳位因此continue跳过了头 (代码注释:already overwritten above)。今天头不动,所以无害。

但观众互动方案要让头跟随人脸。那时第 3 步不能再锁死,头的目标值会真发出去, 而第 4 步仍然跳过 —— 头的指令一路上没有任何绝对上限检查。 原来那条被撤回的注释(「标定不可信」)会让人以为没法加钳位;现在知道能加,数字也有了: head_motor_1 ±149.98°、head_motor_2 ±50.02°。

我的建议:现在不动。这不是今天要拍的板 —— 头没在动,改了也只是把一个不会触发的分支换个写法, 反而在没有硬件可验的情况下动安全层。正确做法是谁实现「头部跟随」谁顺手把 HEAD_DIMS 从第 4 步的 continue 里拿出来,用上面两个数。 这件事和数字都已经写进注释和 GOAL,不会丢。你需要做的只是认可这个安排。
2 · 演示里到底要不要真的把杯子交到人手上?

这个决定在写任何递交代码之前。递交要碰人,而安全层现在对「人在哪」一无所知 —— z_floor(pitch) / y_floor(pitch) 管的是桌面,没有任何关于人的约束。 Intent-Handover 那篇还指出:夹爪离接收方的手太近,被感知到的安全性会下降。

我的建议:先做降级方案 ——「把杯子放到人面前的桌上」。 它不需要接收姿态估计、不需要新的安全约束、不碰人,而且演示效果差别没有想象中大。 真递交留到有明确需求、且安全层补上「人的位置」之后再做。但这是产品决定,你说了算。
3 · A 失败之后走哪条?(会挡住下一步)

识别是好的,缺的是消歧。四条路:

路线代价风险
a深度消歧:检测出所有 cup 框 → 查深度 → 取离机械臂最近的中(深度已有,要对齐 RGB)深度-RGB 时间对齐没做过
b提高 head 相机分辨率(现在 424×240)低,但要重录数据策略见过的就是 424×240,换了要重训
c语言 prompt 指定("the beige cup")最低换个杯子要改 prompt,不是根本解
d二维码兜底(memory 里已列为备选)低演示时贴码,观感差
我的建议改了(因为第 24 步的复核):先做 e,再做 a。
e · 清场重录一小批 —— 收走 VR 手柄、盖住桌面圆孔。 这两样贡献了 OWL-ViT 5/16 和 GDINO 10/27 的假阳性,而它们不是算法问题,是场景问题。 几分钟的事,却能直接把「精度不行」这一半问题削掉一大块。不做这一步就去调模型,等于在给噪声调参。
然后再 a · 深度消歧:消歧信号里只有深度对外观不敏感(磨砂杯、彩色高脚杯都成立), 而且深度已经采了 863 MB、质量验过(有效像素 76–86%)。 b 要重录重训,代价和 A 不成比例;c 只能当权宜;d 留作演示当天的保险。
e 和 a 的前置都在现场(清场 / 相机内参 + 深度-RGB 对齐),我远程做不了。
4 · B0 还留不留?(会挡住下一步)

速度理由已经没了。剩下的唯一理由是「物体 token 当结构先验,帮泛化」。 而 Oat-VLA 的真机成功率用我们自己的 Clopper-Pearson 算是不显著的(29/49 vs 20/49,p = 0.106)。

我的建议:降级为「暂不做」,但不要删。 它现在既没有速度优势,泛化优势也只有一个不显著的真机数字撑着。 把 B1/B2 重新拿出来比一遍,比继续投 B0 划算。 不删的理由:它是唯一不依赖相机内参和手眼标定的路线, 如果问题 3 选了 a 而标定卡住了,B0 会重新变得有吸引力。
5 · YuNet 的收编要不要现在做?

C 是「盘点不是开发」,所以我只出了决定,没动代码。 真正落地要两步:①在 face_perception.py 加 YuNetFaceDetector 后端 (那文件第 133 行本来就定义了 FaceDetectorBackend 协议,是设计好的扩展点); ②让 face_follow.py 改用 face_perception,删掉那份内联的 Haar 拷贝。

我的建议:值得做,而且我可以纯远程做完(不碰硬件)。 理由是 Haar 那套现在是坏的 —— 不是「有两套要选一套」,是「有一套死的和一套活的」。 放着不管,下次谁去跑 face_follow.py 会撞一个莫名其妙的 AttributeError。 但准确率仍然验不了(没有带人脸的录像),所以我只能保证「能跑起来、耗时已知」, 不能保证「检得比 Haar 准」。要我做的话说一声。
6 · 开放词表这条线,在这块板子上还试不试更大的模型?

试过 OWL-ViT base(225 ms)和 Grounding DINO tiny(2657 ms)。 更上一档是 OWLv2 / GDINO base / SAM,体积和耗时都要再上一个台阶。

我的建议仍是先别试,但理由要改一条。 我原来写「第 4 步已经证明识别不是瓶颈」—— 第 24 步推翻了这句, 精度确实是瓶颈之一,更强的模型对它会有帮助。 但顺序不变:先清场(问题 3 的 e),因为假阳性里 VR 手柄和桌面圆孔占了大头, 清掉之后再看还差多少,才知道要不要为精度花这笔算力。 而且只要场上有第二只杯子,消歧问题换多强的模型都原样存在。 先把问题 3 定了(消歧信号),再回来决定要不要更强的识别器。 另外 GDINO tiny 已经 2657 ms,比 SmolVLA 一次推理还慢一倍,在线用是不可能的。
7 · 提交范围

你已经说了 detect 那 11 MB 可以提交。清单:

类型文件
新增脚本detect_cup_openvocab.py · detect_cup_coco.py · eval_frames.py · bench_smolvla_cameras.py · trial_keyframes.py
改动run_policy_trials.py(接关键帧)· policy_safety.py(只改注释)
文档三份 GOAL 回填 · 本页 + 结论页 + index 卡片
数据 11 MB05-training/detect/(36 帧 PNG + 8 组联系表 + records.json + bench_cameras.json)
提醒一句:仓库在我这次工作期间被别人动过(多了 upload_server.py、GOAL-TRIALS-VIEW-2026-09-04.md 等文件, robot_server_dashboard.html 也有改动)。那些不是我的,提交时别一起裹进去。 git 写命令我一条没跑。
2026-09-04 · 对应 HANDOFF-REMOTE-2026-09-04.md。 按结论组织的版本在这里。 原始数据 05-training/detect/*/records.json, 联系表 05-training/detect/*/sheet_*.jpg,计时 05-training/detect/bench_cameras.json。