「机器人抓不到杯子」· 假设清单
2026-09-07 夜 ~ 09-08 凌晨 · 15 次实机试跑 + 4 个模型的离线视觉测试 · 全部 0 成功
★ 结论:state 泄漏 ★
模型的输入里含着正在运动的那条手臂的关节角,而训练目标就是「预测下一步关节角」——
state ↔ action 相关系数 0.99,把当前角度原样抄一遍误差只有 0.9°。
捷径几乎白送,模型就不学看图了。
决定性证据:2×2 单变量对照
| 模型 | E(数据翻几遍) | state 含正在动的臂? | pan 斜率 | 相关 r | 会看图? |
smolvla_1502 b2 | 1.64 | ❌ 不含(左臂不动) | +0.210 | +0.565 | 会 |
a100-6000 | 19.74 | ❌ 不含(左臂不动) | +0.214 | +0.417 | 会 |
b16 right-pick | 19.4 | ✅ 含(全 17 维) | +0.004 | +0.013 | 不会 |
sep3-left | 93.5 | ✅ 含(左臂正在动) | +0.006 | +0.313 | 不会 |
中间两行:E 几乎相同(19.4 vs 19.74),只差 state → 斜率差 50 倍。
上面两行:state 同样不含任务臂,E 差 12 倍 → 斜率几乎相同(0.210 vs 0.214)。
→ E 不是原因,state 才是。「斜率」= 杯子挪 1°,模型输出跟着挪几度;示范里应接近 +1。
全部假设一览
| # | 假设 | 状态 | 依据 |
| 1 | state 泄漏:输入含正在动的臂,模型抄 state 不看图 |
成立(主因) |
上面的 2×2;state↔action r=0.99,照抄误差 0.9°;四个模型无一例外 |
| 2 | E 太高(数据翻太多遍导致过拟合) |
排除 |
E=19.74 与 E=19.4 斜率差 50 倍;E=1.64 与 E=19.74 斜率几乎相同。E 与看不看图无关 |
| 3 | state[6] 被截断(17 维砍成 6 维)是 bug |
排除 |
唯一成功过的模型正是 state[6]。它恰好截掉了正在动的臂,是这个「bug」救了它 |
| 4 | rename_map 把相机改名导致图像进不去 |
排除 |
键名不匹配时 lerobot 会 raise ValueError,从没报过;empty_cameras=0 也不会偷偷补零。成功那个模型同样 rename 了 |
| 5 | decay_steps=30000 ≠ steps=20000 导致学习率没降完 |
排除 |
optim/schedulers.py:149 会自动把曲线缩放到 steps,S=1.0。30000 是官方默认值 |
| 6 | 安全层把动作拦住了 |
排除 |
15 次里 14 次 safety layer cut 0%,策略想动多少就动多少(743°、886°、1722° 全部执行) |
| 7 | 内存不足导致推理抖动、闭环太慢 |
排除 |
释放 3G 内存后慢帧中位 278ms,落在原区间 148~414ms 内,没变 |
| 8 | 换 checkpoint(训练步数不同)能改善 |
排除 |
006000(28 ep)比 020000(93.5 ep)更差,cycles 2→5 恶化 |
| 9 | 夹爪换过,标定值不对导致空抓 |
排除 |
0820 / 0826 / sep3 三个数据集(跨越换爪)夹住杯子都是 28 左右,标定一致 |
| 10 | 爪上贴的黄标签干扰视觉 |
排除 |
拿掉后 cycles 仍是 2,照样失败 |
| 11 | 相机被占 / 预览抢设备影响试跑 |
排除 |
是我的预览服务造成的,改成读 /dev/shm 共享帧后 8770 恢复正常 |
| 12 | 示范阶段性不清晰(elbow 来回摆,不是单调下降) |
未排除 |
三个数据集干净下降 0/40、0/54、0/50,下降帧占比都只有 39~42%。真实存在,但解释不了模型之间的差异 |
| 13 | 杯位超出训练分布 |
未排除 |
今晚杯子始终在画面中央,对右臂偏远;训练集右臂抓取 pan 中位 −22.6°。没测过分布最密的位置 |
| 14 | 桌面那块透明塑料膜干扰 |
未排除 |
训练集桌面是干净的,膜是新增的反光物,一直没拿掉 |
| 15 | 数据量不够(40~54 集不足以学会视觉定位) |
未排除 |
会看图的两个模型斜率也只有 +0.21,离示范的 +1 差 5 倍——看得见但不够准 |
四种失败模式(今晚实机观察到的)
| 模型 | 动作幅度 | 夹爪开合 | 病症 |
sep3-left (b64) | 5.9~9.1 °/s | 2~5 | 走同一条固定轨迹,六次重合到 1.7~3.7° |
b16 right | 8.4~11.2 °/s | 6~10 | 输出噪声大,方向偶然对 |
smolvla b2 | 11~12 °/s | 5~9 | 够得到,抓不准(手伸到位,合爪抓空) |
a100-6000 | 28 °/s | 1 | 动作最猛,唯一顶到安全边界(cut 5%),一次连续抓取但仍空抓 |
ACT | 1.0 °/s | 0 | 几乎不动,60 秒总共只动 60° |
官方对 state 泄漏的配方(存在,但 SmolVLA 用不了)
class RelativeActionsProcessorStep:
"""Converts absolute actions to relative actions (action -= state)...
Mirrors OpenPI's DeltaActions transform."""
| 途径 | 可用? |
use_relative_actions(训练目标改成增量) | pi0 / pi05 / pi0_fast / groot 有 |
SmolVLA 的 use_delta_joint_actions_aloha | 一开就 raise NotImplementedError |
数据集层面转换 recompute_stats(relative_action=True) | 存在,要改数据管线 |
| 把任务臂的 state 拿掉(今晚验证有效的那条) | 右臂任务直接可用;左臂要重排维度 |
★ 截断永远取前 6 维 = 左臂。所以「不传 --policy.input_features=null 让它截断」这招
只对右臂任务有效;左臂任务要用,必须重排数据集里 state 的维度顺序,推理侧也得同步改。
下一步(按性价比排)
| 做什么 | 成本 | 为什么 |
| 杯子挪到训练集最密的位置 + 拿掉塑料膜 | 零 | 假设 13、14 还没排除,而且不花任何代价 |
| 训一批「右臂 + 不传 input_features=null」 | 租 GPU 1~2h | 主动利用 state 截断,而不是碰运气 |
| 左臂数据集重排 state 维度 | 写脚本 + 改推理侧 | 让左臂也能用这招;要先等右臂验证 |
| 补「接近—下降—合爪」阶段清晰的示范 | 录制时间 | 假设 12、15;会看图的模型斜率也只有 +0.21,说明还不够准 |
今晚我归因错了六次
内存 · checkpoint 步数 · rename_map · decay_steps · gripper over-range 误读 · E 假设。
每次都是先下结论再去查证。最后那个 2×2 之所以能定案,是因为它是单变量的——
这也是今晚唯一一次先设计对照再看结果。