← reports index
03-software/scripts/cup_mask.py · 方案评估 + 离线验证 + 空跑 · 2026-09-11

颜色选杯:把「选哪只」搬出策略 —— 视觉掩膜路线

桌上放黄、蓝两只杯,左臂模型不管起手位、不管腕相机看不看得见、不管指令里加不加 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

结论

能做,而且是重录数据之外最便宜的一条路。 策略不学颜色;颜色由外面决定,把非目标杯从头相机画面里抹掉,策略看到的就是它训练过的单杯场景。抹一帧 3.3 ms(424×240,CPU),控制回路一个周期约 100 ms,占 3%。离线在今天的关键帧上验证通过;空跑结果见第 5 节;实机 5+5 集预注册写好、今天没跑(操作者暂停)。
改指令这条路今天关掉了。 训练集 54 集只有一句话,语言在微调里是常量。加 yellow 让轨迹变乱(多偏左三倍、闭爪 3 次),目标不变。它是扰动,不是选择。
Astra 帮不上抹杯子。 它是一个纯文本 LLM 代理循环(OpenAI 接口),没有一行图像代码,技能表是空的,没上过实机。它能做的只是以后把一句话变成「目标颜色」这个参数,再交给这条链。

1 · 今天的证据(证据等级:全部跑测过)

条件起手位指令去向肩 pan 最左(轨迹单位)闭爪次数判定
中线起手 ×3(早上)示范起点原句蓝(中)—1成功 ×3
左前起手左移 10 cm,腕相机里黄杯更近原句蓝(中)−3.61成功
左前起手 + yellow同上,杯位不动Pick up the yellow cup…先朝黄杯偏,第 1 次闭爪碰倒黄杯,最后仍去蓝杯−12.63失败

去向由操作者看录像判定;closure 关键帧只记第一次闭爪,开合大于 1 次的集单看关键帧会判错(今天判错过一次,已存记忆)。

被否掉的解释:偏好画面中间的位置 —— 训练集闭爪时肩 pan 中位数 −13.5°、左侧档 20 集多于中间档 18 集,杯位分散且偏左;且左前起手、腕相机里黄杯更近时它照样去蓝杯。选最近的 / 腕相机看得见的 —— 同一次实验否掉。剩下站得住的只有一句:它认的是蓝杯这只东西。

2 · 为什么策略学不会颜色

训练集 xlerobot-left-pick-cup-sep3-clean-20260905:每帧一只奶黄杯,meta/tasks.parquet 只有一句「Pick up the cup with the left arm.」。语言编码器输入恒定,梯度里没有任何「按词选物」的信号。桌上两只杯本身就是分布外,「选哪只」从没被教过。要让语言真的选杯,唯一的办法是数据里有对照:同一桌面两只杯,一半集说 yellow 一半说 blue,杯位每集换,至少 50 集,4090 上重训 20k 步 —— 一到两天。

2·5 · SmolVLA 本来有没有「按词选物」这个能力(2026-09-11 追加)

有 —— 架构上语言是一等输入,坏掉的是下游那一半。SmolVLA 的前缀是 图像 → 语言 token → state,动作专家(action expert)对这个前缀做跨注意力 (lerobot/policies/smolvla/modeling_smolvla.py 的结构图在 487 行, 语言嵌入在 608 行,注意力掩码在 629 行)。所以「让文字改变动作」这条通路是存在的,不需要改架构。

关键的一条,今天才查出来:这个 checkpoint 是 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 是冻结的,我们只训了动作专家。
好消息:单指令微调没有破坏底座的语言/视觉理解 —— 它压根没被更新过, SmolVLM2 对「yellow / blue」的理解还在那儿。
坏消息:真正学我们这个任务的是动作专家,而它面对的语言 token 54 集完全相同, 梯度里没有任何理由让它去看那几个 token。它学会的是忽略。
推论:重训要教的不是「语言是什么」,而是「去看语言那一段」—— 这比从零学语义低一个量级,所以 ③号路线(多杯多指令)的把握比看上去大。 但这一条没跑过,是架构层面的推断,不是实验结论。

一般多物件任务是怎么训的

公开做法(RT-1 / BridgeData V2 / Open X-Embodiment 这一脉)都绕着同一个词: 让「指令 → 目标」这个映射在数据里可辨识。四件事缺一不可:

做法为什么必须
场景里放干扰物(distractors)只有一个候选时,「选」这个动作在数据里根本不存在 —— 正是我们现在的处境
指令随目标变,一句话点名目标指令是常量 = 没有梯度信号;换成另一个常量还是常量
★ 同一幅画面配不同指令、走向不同目标 ★这才是真正的对照组。缺了它,模型可以靠「场景长相」记住答案,完全不看指令
位置反转对冲:黄左蓝右 与 蓝左黄右 各一半否则学到的是「去左边」,而且你事后测不出来 —— 在那个布局上它表现完美

常见的第五件事是指令改写增广(同一集配多种说法:pick up the yellow cup / grab the cream one / take the yellow mug),目的是让模型抓语义而不是记住一个固定字符串。 对冻结 VLM 的设定尤其划算 —— 底座本来就认得这些同义说法。

规模怎么估。本页第 2 节给的「至少 50 集」是下限量级; 如果按项目自己的录制指南「每档 ≥ 12 集」去铺满 2 种颜色 × 3 个横向档 × 左右互换 = 12 个格子, 是 约 140 集 —— 比现在整个右臂合集(73 集)还多一倍。 先做 20 集 pilot,只看一个指标:同一幅画面,指令从 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 集 —— 「中间那个点位示范多」也不成立
3 · 方案:选择搬到策略外面
1
指令 → 目标颜色。现在是命令行参数 --mask-cup blue(=抹掉蓝杯、去抓黄杯)。以后可以由 brain 或 Astra 从一句话里解析出来。
2
每一帧头相机画面:HSV 颜色分割找到非目标杯 → 整块饱和度判定它是不是杯 → 掩膜外扩 9 px → 用外圈桌面色填掉。3.3 ms
3
抹过的图只喂给策略(run_policy_trials.py 的 pin_obs(),和「state 恒定维钉回训练均值」同一层)。关键帧、录像、安全层看的仍是真图。
4
腕相机不动。横向选目标由头相机主导、腕相机主导闭爪(记忆 camera-division-of-labour);而且腕相机有强烈青色偏色 —— 白桌面在它眼里 H≈83、S 中位 10~51,浅蓝杯(H 90、S 43~94)在 HSV 里和桌面分不开(今天实测)。
5
杯子不会自己动:第一帧锁定质心,之后只认 40 px 内的块;检测丢失时沿用上一帧掩膜最多 5 帧。这两条是防「闪烁」的 —— 掩膜一闪,策略看到杯子忽隐忽现,比不抹更糟。

4 · 实时抹杯子,选什么技术

技术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 在这个预算里可以忽略。

5 · 第 1 步:离线,在今天的关键帧上

抹掉蓝杯:左原图,右抹后
抹掉蓝杯(去抓黄杯的场景)。左原图,右喂策略的图。左臂自己的白色件、橙色壳、黄杯都没动。
抹掉黄杯
抹掉黄杯(去抓蓝杯的对照)。紧挨着的橙色腕相机壳保住了。
蓝杯已在爪中
蓝杯已被握住时抹蓝:粉色手指保留。这时抹不抹已无所谓,但说明掩膜不吞手臂。

两个假阳性,都用「整块饱和度中位数」切开:

候选块HS 中位是什么判据
蓝杯9065目标blue 整块 S 中位 ≥ 55
左臂白色件在阴影里9947手臂
奶黄杯16223目标yellow 像素与整块 S ≥ 150
腕相机橙色壳16103手臂

只按像素抬高阈值不行:浅蓝杯自己的 S p10 只有 43,切碎了。所以像素阈值放宽让块长完整,再按整块判。

6 · 第 2 步:空跑,策略输出往哪边走

没跑完(证据等级:被挡住)。不抹的对照组 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 组更偏向黄杯(左)。

7 · 第 3 步:实机预注册(今天没跑)

项值
配置左臂 · 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 会话只留一个;杯子颜色阈值是白天量的,晚上有绿灯要复测。

8 · Astra 模块能帮什么

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 步实机过了再接它,现在接是把两个没验证的东西叠在一起。

9 · 边界和风险

10 · 今天改了什么

文件改动
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 下午。