从一个好奇的问题开始——用深度相机的人一般怎么训练?大部分人用 VLA 还是点云?——
一路追到 SmolVLA 注意力掩码里的一个 bit。
结论是:不是二选一,而是有一个所有人都收敛到的结构做法;而那个做法能不能不破坏预训练,取决于一行 att_masks。
实测方法:遍历 lerobot/policies/*/ 全文搜 depth / point_cloud,
命中全部是 transformer 的层数(depth: int = 24),没有一个是深度图。
depth-pipeline-2026-09-03.html 原话:「深度现在被谁消费:没有任何人」)。
所以这不是我们做得差,是整个生态的默认状态。最硬的证据是基座模型的输入规格:
| 模型 | 输入 | 深度 |
|---|---|---|
| OpenVLA | RGB patch + 语言 | 无 |
| π0 / π0.5 | RGB + 语言 + 本体 | 无 |
| GR00T N1 | 头部单目 RGB 224×224 + 语言 + 本体 | 无 |
| SmolVLA(我们在用) | 三路 RGB + 语言 + 17 维关节 | 无 |
| 做法 | 深度以什么形式进模型 | 代表 | 要仿真吗 | |
|---|---|---|---|---|
| ① | 点云直接当观测 | 深度图 → 点云 → 轻量编码器 | DP3 / iDP3 | 不要 |
| ② | 只在训练时用,推理可只用 RGB | 辅助监督 / 特征注入 | Depth Helps | 不要 |
| ③ | 不进策略,只做几何模块 | 掩码+深度→物体点云→抓取位姿 | Contact-GraspNet / 质心+PCA | 不要 |
| ④ | RGB-D 早融合当第四路相机 | 伪彩成一路图像 | — | 不要 |
| ⑤ | sim2real 桥梁 | 仿真里渲染深度 | — | 要 |
depth-grasp.html(08-28)给的推荐路线是 ③ 里的「质心 + PCA」启发式——
非学习、几乎零算力,对杯子这种规则形状够用。整条路连训练都不需要。④ 已经被我们排除过:Point Cloud Matters 系统比较 RGB / RGB-D / 点云三种观测空间,点云常优于另两者,尤其在泛化和低数据量场景。 我们原来的路线 B 正是「深度伪彩成第四路相机」,就是因为这篇被改掉的。
DP3 放弃语言泛化,换几何精度和低数据量:
iDP3 把它从世界系改成相机系(egocentric), 因此不需要相机外参标定、不需要点云分割,作者报机上算力实时 15 Hz。
我们自己合并集的 tasks.parquet,实测:
Pick up the cup with the right arm. task_index 0
Pick up the cup with the left arm. task_index 1
一个模型、两种行为,靠一句话切换。DP3 结构上做不到——它没有语言输入,一个策略对一个任务。
left 而不是 right)导致 0/3、
夹爪反复开合——这恰恰证明语言条件真的在起作用。点云策略不会有这个 bug,因为它压根没有这个能力。
DP3 用的是无色点云——这也正是它免疫光照和纹理干扰的原因。代价是: 形状相同、外观不同的两个物体,它物理上分辨不了。 「拿红色的杯子,别拿蓝的」——同款塑料杯,点云完全一样。 这不是调参问题,是表征里根本没有那个信息。
| SmolVLA | DP3 | |
|---|---|---|
| 相机内参 | 不需要 | 需要(深度→点云要内参) |
| 相机外参 | 不需要 | DP3 需要;iDP3 免了外参,内参仍要 |
| 手眼 | 不需要 | 要送位姿就需要 |
我们现在手眼没做完、深度内参没标——SmolVLA 是在这两件事都缺的情况下今天就能跑的唯一选择。 这不是理论优势,是此刻的实际状态。
透明+反光会让深度直接空洞。目标清单里有彩色透明高脚杯——那种杯子上点云是空的,SmolVLA 只是看图。
互联网规模视觉-语言预训练打底,lerobot 内置、数据格式/训练脚本/安全层全现成。 DP3 是另一套代码库——这就是把 B3 排除的理由:换策略家族 = 整条链路重来。
| DP3 | SmolVLA(本机实测) | |
|---|---|---|
| 单次推理 | ~51 ms(论文,非 Jetson) | 1329 ms |
| 感知编码器 | 三层 MLP,64 维 | SigLIP,192 token |
| 几何精度 | 直接编码三维结构 | 只有 2D,深度靠推断 |
| 光照/纹理干扰 | 免疫(无色) | 敏感 |
| 每任务示范量 | 10–40 条 | 更多 |
这是整个调研里最有信息量的一条。2025–2026 结合类的工作在变多,而且结构决策高度一致:
| 工作 | 怎么结合 |
|---|---|
| PointVLA | 冻结 action expert,3D 特征走一个轻量旁路模块注入;做 skip-block 分析, 只往最没用的那几个 block 注入,把对预训练表征的干扰降到最小 |
| PointACT | 层次化点云表征进动作解码,RLBench-10 上 +10% |
| Any3D-VLA / QDepth-VLA / Evo-Depth | 深度当辅助监督 / 轻量增强 |
| Depth Helps | 冻结 RGB 主干,只加两个小模块,推理时可只用 RGB(只掉 0.8%) |
| 我们的 B0 | 冻结视觉编码器,物体 token 走 object_proj append 进 prefix |
怎么选,判据是你缺哪种能力:
| 你缺的是 | 该走哪条 |
|---|---|
| 语义泛化(听懂指令、认没见过的物体) | VLA 本身就在做,别动它 |
| 几何精度(桌面高度变了、真假物体、精确对位) | 旁路注入 3D——PointVLA 报的三个收益正是这类: 20 条示范做 4 任务、分得清真实物体和它的照片、适应训练时没见过的桌面高度 |
| 极低数据 + 固定场景 + 不需要语言 | 纯点云策略(DP3 那档),最省算力 |
「旁路注入不破坏预训练」听起来像个愿望。在 SmolVLA 上它可以做到严格成立,而且能证明——
但成不成立完全取决于 att_masks 里的一个数。
policies/common/vla_utils.py:87-90:
cumsum = torch.cumsum(att_masks, dim=1)
att_2d_masks = cumsum[:, None, :] <= cumsum[:, :, None]
pad_2d_masks = pad_masks[:, None, :] * pad_masks[:, :, None]
return att_2d_masks & pad_2d_masks
att_masks 不是「能不能看」的开关,是块边界标记:cumsum 给出块号,
token i 能看到 token j ⟺ 块号(j) ≤ 块号(i)。
modeling_smolvla.py)| 段 | 数量 | att_mask | 块号 | 出处 |
|---|---|---|---|---|
| 三路图像 | 192 | 0 | 0 | :593 |
| 语言 | 48 | 0 | 0 | :617 |
| state | 1 | 1 | 1 | :630 |
| (动作,在 suffix) | 50 | 1 | 更大 | embed_suffix |
总长 241 token。源码注释原话: "Set attention masks so that image and language inputs do not attend to state or actions"—— 图像和语言看不到 state。
| ↓查询 / 键→ | 图像+语言 块 0 | state 块 1 | 物体 token 块 2 | 动作 块 3 |
|---|---|---|---|---|
| 图像 + 语言 | ✅ | ❌ | ❌ | ❌ |
| state | ✅ | ✅ | ❌ | ❌ |
| 物体 token | ✅ | ✅ | ✅ | ❌ |
| 动作(action expert) | ✅ | ✅ | ✅ | ✅ |
| 写法 | 块号 | 图像/语言看得到它吗 | 后果 |
|---|---|---|---|
append 在最后,att_mask=1 | 2 | 不能 | ✅ 预训练通路零影响 ← 正确写法 |
append 在最后,att_mask=0 | 1(并进 state 块) | 不能 | 也不干扰,但和 state 挤同一块 |
插在 state 之前,att_mask=0 | 0 | 能 | ❌ 预训练视觉/语言特征被改写 |
所以「加法不干扰」这句话,只在「放最后 + 开新块」时成立。写错一个数,性质就完全变了。
object_proj 随机初始化,
训练一开始往 prefix 里塞的是随机向量。缓解办法是把最后一层零初始化——
但要注意:即使 value 全零,多出来的 key 仍然参与 softmax 归一化,会稀释原有注意力权重。
所以「零初始化 = 完全恒等」是错的,仍然需要 warmup 或小学习率起步。prefix_length 的 padding 分支要处理」——这条顾虑不存在。
部署 config 里 prefix_length = 0,而代码是 if seq_len < self.prefix_length,
这个分支永远不会触发。加 token 不涉及 padding 或截断。
N_obj 必须固定。
att_masks 是个 python list,att_masks[None,:].expand(bsize,-1) 对整个 batch 共享同一长度。
检出不足时要用 pad_masks=0 补。
好消息是补得很干净:pad_2d_masks 把 padding token 在 key 和 query 两侧都屏蔽,空物体不污染任何东西。按我们自己的实测记录排,真正挡着深度路线的:
| 排名 | 约束 | 证据 |
|---|---|---|
| 1 | 深度没有任何消费者——只写到磁盘 | depth-pipeline-2026-09-03.html |
| 2 | 数据断续、格式是自研旁路——09-02 那晚 102 集里 88 集丢了深度 | 同上,实测 |
| 3 | 手眼标定没做(几何模块路线的前置) | 09-04 棋盘没夹在爪上,作废 |
| 4 | 换策略家族的代价 | B3 已排除 |
| 5 | Gemini 本身 | — |
Gemini 335 是主动+被动立体(850 nm),不是 ToF——比 DP3 用的 L515 差一档, 但那个差距只在跑 DP3 那条路时才存在。 相机是这条链路上唯一已经验证没问题的一环;挡住我们的三条全是我们自己这边的事。
| 结论 | 等级 | 怎么来的 |
|---|---|---|
| lerobot 22 个内置策略,0 个吃深度 | 本机实测 | 遍历 policies/*/ 全文搜 |
| SmolVLA prefix = 192 图像 + 48 语言 + 1 state = 241 | 读源码 + 读部署 config | modeling_smolvla.py / config.json |
| 图像和语言看不到后面的块 | 读源码,可证明 | vla_utils.py:87-90 的 cumsum 比较 |
prefix_length = 0,padding 分支不触发 | 读部署 config | 部署 checkpoint 的 config.json |
| 合并集有 left / right 两条指令 | 本机实测 | meta/tasks.parquet |
| SmolVLA 推理 1329 ms、视觉占 13% | 本机实测 | 09-04 计时 |
| 深度有效像素 0.88 | 本机实测 | 09-03 深度管线评估 |
| 基座模型输入规格(OpenVLA/π0/GR00T) | 二手 | 检索摘要,未逐篇读原文 |
| DP3 / iDP3 / PointVLA 的数字 | 二手 | 论文与检索摘要;延迟几乎肯定非 Orin Nano 实测 |
| 点云是不是更省数据 | 有矛盾证据 | Point Cloud Matters 说低数据量更好; 也有研究观察到点云 sample efficiency 低于 RGB/RGB-D。不能当定论 |
不是「VLA 还是点云」二选一,是「保住预训练,旁路加几何」——
独立几组人收敛到了同一个做法。而在 SmolVLA 上,这个做法能不能不破坏预训练,
取决于 att_masks 里的一个 bit:放最后、开新块,图像和语言的表征就逐比特不变;
写错位置,预训练特征就被改写。