桌上放黄、蓝两只杯,左臂模型不管起手位、不管腕相机看不看得见、不管指令里加不加 yellow,都去抓蓝杯。这页回答三件事:为什么改指令没用;把非目标杯从画面里抹掉能不能让它去抓黄杯、用什么技术抹才跟得上实时画面;库里的 Astra 模块帮不帮得上。
来源:05-training/trials/cup-colour-experiment.json · trials-20260911-105804.json · traces/trace-20260911-102454-t1.npz / -105804-t2.npz · 03-software/scripts/cup_mask.py · cup_locate.py:47-80 · run_policy_trials.py(--mask-cup) · 03-software/astra-robot/ · 记忆 camera-division-of-labour / left-dataset-is-single-cream-cup
| 条件 | 起手位 | 指令 | 去向 | 肩 pan 最左(轨迹单位) | 闭爪次数 | 判定 |
|---|---|---|---|---|---|---|
| 中线起手 ×3(早上) | 示范起点 | 原句 | 蓝(中) | — | 1 | 成功 ×3 |
| 左前起手 | 左移 10 cm,腕相机里黄杯更近 | 原句 | 蓝(中) | −3.6 | 1 | 成功 |
| 左前起手 + yellow | 同上,杯位不动 | Pick up the yellow cup… | 先朝黄杯偏,第 1 次闭爪碰倒黄杯,最后仍去蓝杯 | −12.6 | 3 | 失败 |
去向由操作者看录像判定;closure 关键帧只记第一次闭爪,开合大于 1 次的集单看关键帧会判错(今天判错过一次,已存记忆)。
被否掉的解释:偏好画面中间的位置 —— 训练集闭爪时肩 pan 中位数 −13.5°、左侧档 20 集多于中间档 18 集,杯位分散且偏左;且左前起手、腕相机里黄杯更近时它照样去蓝杯。选最近的 / 腕相机看得见的 —— 同一次实验否掉。剩下站得住的只有一句:它认的是蓝杯这只东西。
训练集 xlerobot-left-pick-cup-sep3-clean-20260905:每帧一只奶黄杯,meta/tasks.parquet 只有一句「Pick up the cup with the left arm.」。语言编码器输入恒定,梯度里没有任何「按词选物」的信号。桌上两只杯本身就是分布外,「选哪只」从没被教过。要让语言真的选杯,唯一的办法是数据里有对照:同一桌面两只杯,一半集说 yellow 一半说 blue,杯位每集换,至少 50 集,4090 上重训 20k 步 —— 一到两天。
有 —— 架构上语言是一等输入,坏掉的是下游那一半。SmolVLA 的前缀是
图像 → 语言 token → state,动作专家(action expert)对这个前缀做跨注意力
(lerobot/policies/smolvla/modeling_smolvla.py 的结构图在 487 行,
语言嵌入在 608 行,注意力掩码在 629 行)。所以「让文字改变动作」这条通路是存在的,不需要改架构。
train_expert_only = True。
freeze_vision_encoder True
train_expert_only True
load_vlm_weights True
vlm_model_name HuggingFaceTB/SmolVLM2-500M-Video-Instruct
tokenizer_max_length 48
也就是说 整个 VLM 是冻结的,我们只训了动作专家。公开做法(RT-1 / BridgeData V2 / Open X-Embodiment 这一脉)都绕着同一个词: 让「指令 → 目标」这个映射在数据里可辨识。四件事缺一不可:
| 做法 | 为什么必须 |
|---|---|
| 场景里放干扰物(distractors) | 只有一个候选时,「选」这个动作在数据里根本不存在 —— 正是我们现在的处境 |
| 指令随目标变,一句话点名目标 | 指令是常量 = 没有梯度信号;换成另一个常量还是常量 |
| ★ 同一幅画面配不同指令、走向不同目标 ★ | 这才是真正的对照组。缺了它,模型可以靠「场景长相」记住答案,完全不看指令 |
| 位置反转对冲:黄左蓝右 与 蓝左黄右 各一半 | 否则学到的是「去左边」,而且你事后测不出来 —— 在那个布局上它表现完美 |
常见的第五件事是指令改写增广(同一集配多种说法:pick up the yellow cup / grab the cream one / take the yellow mug),目的是让模型抓语义而不是记住一个固定字符串。 对冻结 VLM 的设定尤其划算 —— 底座本来就认得这些同义说法。
yellow 换成 blue 时
闭爪 shoulder_pan 切不切换。切不动再投大批量才不冤枉。
(两个数字都是按指南推算,不是实测。)| 测什么 | 结果 |
|---|---|
训练视频里的蓝色像素(HSV H∈90~130, S>60, V>60,等间隔抽帧) | 占比 ≈ 0(最高 0.94%,是背景不是杯子)—— 训练里没有蓝杯子,所以「偏好蓝色」不可能是学来的 |
54 集闭爪 shoulder_pan 直方图(9° 一档,负 = 画面左) | [-45,-36)3 [-36,-27)11 [-27,-18)9 [-18,-9)11 [-9,0)8 [0,+9)10 [+9,+18)1中位数 −13.5°;左侧档 20 集 > 中间档 18 集 —— 「中间那个点位示范多」也不成立 |
--mask-cup blue(=抹掉蓝杯、去抓黄杯)。以后可以由 brain 或 Astra 从一句话里解析出来。pin_obs(),和「state 恒定维钉回训练均值」同一层)。关键帧、录像、安全层看的仍是真图。| 技术 | 424×240 一帧 | 填出来像不像 | 要不要额外准备 | 判断 |
|---|---|---|---|---|
| HSV 分割 + 外圈桌面中位色填 | 3.3 ms(实测) | 平色块,比桌面略灰 | 无 | 默认 |
| HSV 分割 + 空桌底片填 | ≈3 ms | 就是真桌面(逐像素对齐) | 跑前拍一张没杯子的桌面;头必须固定(本来就固定在 cup-grasp-v0) | 效果最好,下次实机用它 |
| HSV 分割 + cv2.inpaint(Telea) | 26 ms(实测) | 纹理连续 | 无 | 离线对照可以,实时占 26% 周期 |
| 神经分割 / 开放词汇检测 | OWLv2 1623 ms、EfficientDet 找到 0 框(cup_locate.py 实测) | — | 模型、显存 | 问题比「找杯子」窄:要抹的就是按颜色定义的,用不着 |
| GPU 上做阈值 | — | — | cupy/torch 搬运开销 | 3 ms 的活,搬去 GPU 更慢 |
控制回路实测 251 帧 / 26.1 s ≈ 9.6 Hz,一个周期约 104 ms;推理本身每次约 1.4 s 阻塞在 predict_action 里(chunk 大时)。3 ms 在这个预算里可以忽略。



两个假阳性,都用「整块饱和度中位数」切开:
| 候选块 | H | S 中位 | 是什么 | 判据 |
|---|---|---|---|---|
| 蓝杯 | 90 | 65 | 目标 | blue 整块 S 中位 ≥ 55 |
| 左臂白色件在阴影里 | 99 | 47 | 手臂 | |
| 奶黄杯 | 16 | 223 | 目标 | yellow 像素与整块 S ≥ 150 |
| 腕相机橙色壳 | 16 | 103 | 手臂 |
只按像素抬高阈值不行:浅蓝杯自己的 S p10 只有 43,切碎了。所以像素阈值放宽让块长完整,再按整块判。
没跑完(证据等级:被挡住)。不抹的对照组 A 跑完一次(trials-20260911-120050.json,51 帧 / 10.3 s,trace-20260911-120050-t1.npz);抹蓝杯的 B 组两次启动都夭折(一次被面板停止按钮杀掉 rc −15,一次 rc 2),没有留下 trace。操作者 2026-09-11 12:50 暂停:Ville 在做同一件事,这条线交给他。判据仍是:抹蓝后策略输出的肩 pan 应比 A 组更偏向黄杯(左)。
| 项 | 值 |
|---|---|
| 配置 | 左臂 · sd-p0.2-b16-u20000@020000 · chunk 1/1 · 25 s · 左前起手(--home-left 10)· --keep-torque · --mask-fill plate(跑前拍空桌底片) |
| A 组 5 集 | --mask-cup blue,黄杯在画面左、蓝杯在中。成功 = 黄杯被提起离桌 >3 cm 保持 >1 s。去抓蓝杯 = 失败(且单独计数「去向」)。 |
| B 组 5 集 | --mask-cup yellow,同杯位。成功 = 蓝杯被提起。这组是对照:抹了黄杯它还该去蓝杯。 |
| void | 摆放错误 / 人为干预 / 硬件故障 / 过热停机 / 掩膜统计里 none 帧超过 20% |
| 分开记 | k/n 按组,不合并。另记「去向」k/n(去了目标杯几次),这是这条路真正要证明的量,比抓没抓起来更关键。 |
| 跑前 | 左肩 shoulder_lift 今天全天 50 °C 以上、空载归位欠 6~7°,先降温或物理检查;两个 Claude 会话只留一个;杯子颜色阈值是白天量的,晚上有绿灯要复测。 |
03-software/astra-robot/(7 个文件,2026-09-10 提交后没动过):observe-act-correct 循环,每周期调一次 OpenAI Responses 接口(默认 model gpt-6-astra),工具是 observe_robot / execute_skill / stop_robot;图像只能从 --image 指的文件读进来 base64 原样送给模型;命令桥只跑配置表里写死的 argv,安全闸要 ENABLE_ROBOT_ACTUATION 加急停口令、温度 ≥ 55 °C 拦、遥测超 1.5 s 拦。
| 它有 | 它没有 |
|---|---|
| 把自然语言变成技能调用的框架;本地安全闸和 7 个单元测试 | 任何相机 / 图像处理 / 分割 / 检测代码;skills.json(技能表为空,桥起不来);openai 包没装;没上过实机 |
判断:抹杯子这件事它零贡献。它的位置在这条链的最前面 —— 「把黄色的杯子拿起来」→ execute_skill("pick", {"colour": "yellow"}) → 桥 → run_policy_trials.py --mask-cup blue。等第 3 步实机过了再接它,现在接是把两个没验证的东西叠在一起。
| 文件 | 改动 |
|---|---|
03-software/scripts/cup_mask.py | 新。CupMasker:分割、整块判定、锁定/沿用、三种填法;命令行可离线测和测速。 |
03-software/scripts/run_policy_trials.py | 加 --mask-cup / --mask-fill / --mask-plate;pin_obs 里换头相机图;每集 reset;run JSON 记 mask_cup / mask_stats。备份 .bak-before-maskcup-20260911。同日另一处:连相机重试 2 次→4 次(.bak-before-camretry-20260911)。 |
05-training/trials/cup-colour-experiment.json | 加 yellow 那轮的记录、操作者复看录像的判定、夭折原因。 |
| 本页三张图 | cupmask-*-2026-09-11.jpg |
数字全部实测于 2026-09-11 本机 Jetson(Orin,8 GB);空跑与实机的差别见第 6 节;页面写于 2026-09-11 下午。