第89章:Visual Whole-Body Control 精读——两级 RL 架构¶
| 元信息 | 值 |
|---|---|
| 难度 | ⭐⭐⭐⭐(低层全身策略、视觉高层、特权教师、深度图蒸馏、Sim2Real) |
| 预计时间 | 1.5 周,30-40 小时 |
| 前置依赖 | 复合/180_Deep_WBC精读、足式/190_腿足RL训练栈、复合/160_四足臂动力学概览 |
| 项目定位 | 从"目标已知的全身控制"走向"视觉闭环移动操作" |
| 核心问题 | 视觉系统如何给四足臂低层全身策略提供可执行目标 |
89.0 前置自测¶
开始本章前,请先回答下面问题。
| # | 问题 | 前置 |
|---|---|---|
| 1 | Deep-WBC 为什么可以在无视觉时工作?它的目标从哪里来? | 复合/180_Deep_WBC精读 |
| 2 | Teacher-Student 中,哪些信息可以给教师,哪些不能给部署策略? | 足式/190_腿足RL训练栈 |
| 3 | 四足臂低层策略为什么不能只控制机械臂,而要控制全身? | 复合/160_四足臂动力学概览 |
| 4 | 深度图相机常见误差有哪些?延迟和遮挡会怎样影响操作? | 感知与腿足前置 |
| 5 | DAgger 或在线模仿解决的核心分布偏移是什么? | 足式/190_腿足RL训练栈 |
自测建议:
| 正确题数 | 建议 |
|---|---|
| 5 | 可以直接阅读 |
| 3-4 | 重点补 Teacher-Student 和视觉观测 |
| 0-2 | 先读 Deep-WBC 和腿足 RL 训练栈 |
89.1 本章目标¶
本章精读 Visual Whole-Body Control,简称 VBC。
它解决 Deep-WBC 留下的关键缺口:目标不再假设已知,而是由视觉高层从场景中产生。
读完后你应该能做到:
- 画出 VBC 的三阶段训练流程:低层策略、特权教师、深度图学生。
- 解释低层 goal-reaching 策略为什么要先单独训练。
- 写出低层观测、动作、奖励和目标接口。
- 说明特权教师如何利用物体真值规划可执行目标。
- 解释深度图学生的在线蒸馏损失和分布偏移问题。
- 分析 IK 不可达、深度噪声、照明、坐标标定和目标高度变化造成的失败。
- 读懂
visual_wholebody仓库的低层/高层划分,定位球坐标命令、ROA、DAgger 蒸馏的源码位置。 - 区分论文叙述与源码实现的偏差,建立 VBC 的歧义审计清单,独立完成最小复现。
本节目标与新增内容的对应:第 7、8 两条目标对应本次精读补充的源码走读(§89.3b、§89.4b、§89.5b)、研究视角(§89.9d)、歧义审计与复现指南(§89.11、§89.12)。如果你只关心算法直觉,可先读 §89.2-§89.9 的讲授线;如果你要复现,务必读完所有
b后缀的源码走读节。
89.1b 论文信息与知识导航¶
论文信息¶
| 字段 | 值 |
|---|---|
| 标题 | Visual Whole-Body Control for Legged Loco-Manipulation |
| 作者 | Minghuan Liu, Zixuan Chen, Xuxin Cheng, Yandong Ji, Ri-Zhao Qiu, Ruihan Yang, Xiaolong Wang(UC San Diego) |
| 会议 | CoRL 2024(Oral) |
| arXiv | 2403.16967 |
| 代码 | github.com/Ericonaldo/visual_wholebody(下文统一记为 visual_wholebody) |
| 项目页 | wholebody-b1.github.io |
| 硬件 | Unitree B1(12 DoF 腿)+ Unitree Z1 臂(6 DoF)+ 夹爪(1 DoF),合计 19 DoF |
| 仿真器 | IsaacGym(并行训练) |
VBC 是本方向的**核心论文 ⭐⭐⭐**:它第一次把"视觉移动操作"完整拆成"低层全身控制 + 特权教师 + 深度图学生"三阶段,并给出可复现的开源实现。后续的 RoboDuet、UMI-on-Legs、力敏感 LocoMani 都可看作在它确立的 goal 接口上做替换或扩展。因此本章按论文解读规范的核心论文标准展开——除算法直觉外,还做完整的源码走读、论文-代码差异分析和歧义审计。
本质洞察:VBC 论文的真正贡献不是"给四足臂加了相机",而是**确立了一个可被复用的分层契约**——低层只承诺"给定一个身体系下的末端球坐标目标与底盘速度,我负责全身到达并保持平衡",高层只承诺"从深度图产生这样一个目标"。一旦这个契约稳定,高层就可以被任何目标生成器(深度学生、VLA、传统几何、扩散策略)替换。本章所有源码走读都围绕"这个契约在代码里到底长什么样"展开。
知识导航(§号与正文一一对应)¶
| § | 标题 | 性质 | 难度 |
|---|---|---|---|
| 89.0 | 前置自测 | 自测 | — |
| 89.1 | 本章目标 | 导引 | — |
| 89.1b | 论文信息与知识导航 | 导引 | — |
| 89.1c | 论文-代码映射表 | 索引 | ⭐⭐ |
| 89.1d | 技术树全景 | 导引 | ⭐⭐ |
| 89.2 | 从 Deep-WBC 到 Visual-WBC:为什么要分层 | 讲授 | ⭐⭐ |
| 89.3 | Stage 1:低层通用 goal-reaching 策略 | 讲授 | ⭐⭐⭐ |
| 89.3b | 源码走读:低层真实架构(球坐标命令 + ROA + IK) | 源码 | ⭐⭐⭐⭐ |
| 89.4 | Stage 2:特权教师规划器 | 讲授 | ⭐⭐⭐ |
| 89.4b | 源码走读:高层 9D 动作与特权教师 | 源码 | ⭐⭐⭐ |
| 89.5 | Stage 3:深度图学生与在线蒸馏 | 讲授 | ⭐⭐⭐⭐ |
| 89.5b | 源码走读:DAgger 蒸馏与深度编码器 | 源码 | ⭐⭐⭐⭐ |
| 89.6 | 高低层接口:单位、坐标系和频率 | 讲授 | ⭐⭐⭐ |
| 89.7 | 评估:14 物体 × 3 高度与 retry 行为 | 讲授 | ⭐⭐ |
| 89.8 | IK 不可达、深度敏感与 Sim2Real 难点 | 讲授 | ⭐⭐⭐ |
| 89.9 | 与 RoboDuet、UMI-on-Legs 的关系 | 讲授 | ⭐⭐ |
| 89.9d | 🔬 研究视角:VBC 的贡献结构与局限 | 研究 | ⭐⭐⭐⭐ |
| 89.9b | 跨章综合练习 | 练习 | ⭐⭐⭐ |
| 89.10 | 本章小结 | 总结 | — |
| 89.11 | 歧义审计汇总表 | 索引 | ⭐⭐⭐ |
| 89.12 | 复现指南 | 工程 | ⭐⭐⭐ |
| 累积项目 / 延伸阅读 / 故障排查 / 综合项目 | 章末组件 | — | — |
89.1c 论文-代码映射表 ⭐⭐¶
论文解读的第一步是建立"论文节 → 代码模块"的映射,并标注每一项的清晰度级别(SPECIFIED 论文明确且与代码一致 / PARTIAL 论文提及但细节不足 / UNSPECIFIED 论文未提但代码存在 / CONFLICT 论文与代码不一致)。下表覆盖 VBC 的全部方法论组件,是后续源码走读与歧义审计的总索引。表中行号以 visual_wholebody 主分支为准,不同 commit 可能有 ±数行偏移,读时以函数名定位更稳妥。
| 论文内容 | 对应代码 | 清晰度 | 备注(下文展开节) |
|---|---|---|---|
| 低层观测构成 | low-level/.../manip_loco/manip_loco.py(obs 拼装)、b1z1_config.py |
PARTIAL | 论文给分组维度,逐项来源需读代码(§89.3b) |
| 低层末端目标 = 身体系球坐标 \((l,p,y)\) | b1z1_config.py:43 command_mode='sphere',:54-56 ranges |
SPECIFIED | 论文 §III 与代码一致(§89.3b) |
| 球坐标 → 笛卡尔转换与目标插值 | manip_loco.py:1262-1265 lerp + sphere2cart |
PARTIAL | 论文给插值公式,arm_induced_pitch 未提(§89.3b) |
| 低层末端跟踪奖励 | envs/rewards/maniploco_rewards.py:12-15 |
CONFLICT | 论文写指数核,代码用 **L1 误差**而非 L2(§89.3b) |
| 低层 PD/IK:臂用伪逆 Jacobian | manip_loco.py(IK 控制路径) |
CONFLICT | 论文与高层 wrapper 中低层"直接输出关节"两种说法并存(§89.3b) |
| ROA 特权编码 + 自适应模块 | b1z1_config.py:396 priv_reg_coef_schedual |
PARTIAL | 论文说 ROA,schedule 数值从 config 提取(§89.3b) |
| 高层 9D 动作 | high-level/envs/b1z1_base.py:39-45,195-199 |
SPECIFIED | EE 位姿增量 6D + 底盘速度 2D + 夹爪 1D(§89.4b) |
| 高层特权教师(物体形状 PointNet++、真值位姿) | high-level/envs/b1z1_pickmulti.py、modules/feature_extractor.py |
PARTIAL | 论文说用点云特征,网络细节读代码(§89.4b) |
| 高层奖励 | high-level/envs/reward_vec_task.py、b1z1_pickmulti.py |
PARTIAL | 论文 Appendix C.2,权重从代码提取(§89.4b) |
| 深度学生编码器 | modules/feature_extractor.py:66 DepthOnlyFCBackbone58x87 |
CONFLICT | 真实分辨率 58×87,非常见的 64×64(§89.5b) |
| DAgger 在线蒸馏 | high-level/learning/dagger_trainer.py:94-95、dagger.py |
PARTIAL | 论文说 online imitation,混合策略是**硬切换**(§89.5b) |
| 低层 checkpoint 装载进高层 | b1z1_base.py(torch.load(low_policy_path),hist_encoding=True) |
SPECIFIED | 高层 step 内每步调用低层(§89.4b、§89.6) |
| 评估协议 | 论文 §IV + 项目页 | SPECIFIED | sim 33 YCB / 7 类;real 14 物体 × 3 高度 × 5 次(§89.7) |
如何用这张表:遇到 CONFLICT 行,先读对应的源码走读节,理解"论文为什么这么写、代码为什么那么做、该信谁";遇到 PARTIAL 行,表示论文只给了骨架,复现时必须回到代码补全数值。所有 CONFLICT/PARTIAL 项在 §89.11 歧义审计汇总表中再统一收口。
89.1d 技术树全景 ⭐⭐¶
在逐节展开前,先用一张技术树看清 VBC 在学习型四足臂谱系中的位置。读者应当先看到森林,再看每棵树。
感知行走(Miki 2022,深度→鲁棒 locomotion)
│ 把"深度图 + 本体感知 + 教师-学生"范式带入腿足
▼
Deep-WBC(Fu 2022,统一低层全身策略,Advantage Mixing)
│ 解决"目标已知时如何全身到达",但目标仍需外部给定
▼
Visual-WBC(Liu 2024,本章)★ 里程碑
├─ Stage 1 低层 goal-reaching:身体系球坐标目标 + 底盘速度 + ROA
├─ Stage 2 特权教师:物体真值 + PointNet++ → 9D 高层动作(RL)
└─ Stage 3 深度图学生:分割深度 58×87 → DAgger 蒸馏教师
│ 确立"低层 goal 契约"——高层可被任意目标生成器替换
├──────────────┬───────────────────┐
▼ ▼ ▼
RoboDuet UMI-on-Legs VLA / 零样本基础模型
(双策略协作) (任务帧 EE 轨迹) (语义目标生成)
这条线的内在逻辑是**接口逐层上移**:感知行走把"视觉"接进腿足;Deep-WBC 把"全身控制"做成统一低层;VBC 在两者之上插入"视觉目标生成",并把低层接口固化为球坐标 goal;再往上,UMI-on-Legs 把目标从"一个点"升级为"一段任务帧轨迹",VLA 则把目标来源升级为"语义指令"。理解这条线,就理解了为什么 VBC 的 goal 接口设计是它最有复用价值的贡献。
对比性思维(不是 X 而是 Y):VBC 在这条树上的角色,不是"又一个端到端视觉策略",而是"第一个把视觉移动操作显式分解为可独立验证的三段、并开源了完整三段实现的工作"。它的价值在分解本身,而非某个单点网络的精巧。
89.2 从 Deep-WBC 到 Visual-WBC:为什么要分层 ⭐⭐¶
这一节回答架构选择问题。
Deep-WBC 训练一个统一策略。
它假设低层知道目标末端位置。
但真实任务中,机器人看到的是深度图、RGB 图、物体轮廓和遮挡。
因此必须增加视觉层。
89.2.1 端到端视觉到关节为什么困难¶
朴素做法是把深度图、本体感知和目标命令全部输入一个网络,直接输出 18 或 19 个关节动作。
这个做法看起来最简单,但训练非常困难。
| 困难 | 具体表现 |
|---|---|
| 样本效率低 | 视觉输入高维,策略要同时学感知和控制 |
| 信用分配难 | 失败时不知道是识别错、规划错还是控制错 |
| 安全探索难 | 随机视觉策略会撞物体或摔倒 |
| 迁移困难 | 视觉域差距比动力学域差距更大 |
| 调试困难 | 输出动作错时无法定位中间目标 |
分层架构把问题拆开。
低层只学"给定目标,如何全身到达"。
高层只学"从视觉中选择下一个目标"。
如果不分层,让一个网络直接从深度图输出全身关节动作,训练信号会同时包含视觉识别误差、目标选择误差、低层跟踪误差和接触稳定误差。一次失败无法判断该改相机随机化、教师标注、低层奖励还是关节动作尺度。分层不是为了形式整齐,而是把调试问题切成可验证的接口。
89.2.2 VBC 三层结构¶
┌──────────────────────────────────────────────┐
│ Stage 3: Depth Student │
│ 输入: 深度图 + 本体感知 │
│ 输出: 高层目标或动作分布 │
└────────────────────┬─────────────────────────┘
│ imitate
┌────────────────────▼─────────────────────────┐
│ Stage 2: Privileged Teacher │
│ 输入: 物体真值位姿 + 机器人状态 │
│ 输出: 低层 goal │
└────────────────────┬─────────────────────────┘
│ goal
┌────────────────────▼─────────────────────────┐
│ Stage 1: Low-Level Whole-Body Policy │
│ 输入: 本体感知 + goal │
│ 输出: 全身关节动作 │
└──────────────────────────────────────────────┘
89.2.3 分层的本质¶
本质洞察:VBC 的分层不是为了让网络数量变多。 它是把"视觉理解"和"全身动力学执行"拆成两个可验证接口。 低层接口是 goal,视觉层只要输出可执行 goal,就能复用同一个全身控制器。
这种接口化带来三个好处。
| 好处 | 说明 |
|---|---|
| 可复用 | 低层策略可换高层视觉、语言或规划器 |
| 可调试 | 目标错和动作错可分开定位 |
| 可迁移 | 换机器人时先保留高层接口,再重训低层 |
89.2.4 与 Deep-WBC 的对比¶
| 维度 | Deep-WBC | Visual-WBC |
|---|---|---|
| 目标来源 | 外部给定 | 视觉高层产生 |
| 训练层数 | 主要单层 | 低层、教师、学生三阶段 |
| 输入 | 本体感知 + 目标 | 本体感知 + 深度/视觉 |
| 优势 | 简洁,直接 | 可处理目标感知和泛化 |
| 难点 | 无视觉,接口简单 | 视觉域差距和蒸馏 |
⚠️ 常见陷阱¶
⚠️ 概念误区:VBC 只是 Deep-WBC 加相机
不是。
VBC 的关键是把视觉高层和低层全身控制通过 goal 接口解耦。
直接把深度图塞进 Deep-WBC 通常会显著增加训练难度。
⚠️ 工程陷阱:低层没训好就训练视觉高层
如果低层 goal-reaching 本身不稳定,高层学习到的目标再好也无法执行。
正确顺序是先保证低层在目标分布内成功率足够高。
🧠 思维陷阱:分层一定损失最优性
理论上端到端可能更优。
工程上分层提升了样本效率、可调试性和迁移性。
对高维机器人,这些因素通常比理论最优更重要。
练习 89.2¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 画出 Deep-WBC 和 VBC 的输入输出接口,标出新增视觉层。 | ⭐ |
| 2 | 设计一个端到端视觉策略的训练方案,并说明它会遇到哪些困难。 | ⭐⭐ |
| 3 | 说明 VBC 的 goal 接口如何被 VLA、扩散策略或传统规划器复用。 | ⭐⭐⭐ |
89.3 Stage 1:低层通用 goal-reaching 策略 ⭐⭐⭐¶
这一节讲 VBC 的基础。
低层策略接收本体感知和目标,输出全身关节动作。
它不直接看深度图。
它只关心一个问题:给定目标,身体如何配合手去够到它。
89.3.1 低层观测¶
调研中提到的低层观测可概括为:
| 观测组 | 示例维度 | 含义 |
|---|---|---|
| body | 5D | 机身状态、目标相关低维量 |
| arm | 12D | 臂关节位置速度 |
| leg | 28D | 腿部关节、本体感知和接触相关 |
| last action | 12D 或更多 | 动作平滑和延迟补偿 |
| goal | 3D/6D | 末端目标或相对物体目标 |
不同实现的精确维度可能不同。
读源码时不要只记数字。
要看每一组观测的物理来源和部署可得性。
89.3.2 goal 表达¶
goal 可以是:
| 表达 | 优势 | 局限 |
|---|---|---|
| body frame 下的 3D 位置 | 对基座移动更鲁棒 | 不含姿态 |
| world frame 下的位置 | 任务直观 | 基座误差会影响跟踪 |
| 6D pose | 可做抓取和插入 | 学习更难 |
| task frame 相对位姿 | 适合操作迁移 | 需要定义任务坐标 |
VBC 低层常先从 3D goal-reaching 开始。
姿态和接触力可作为后续扩展。
89.3.2b 多模态目标表达:位置 + 姿态 + 夹爪 ⭐⭐⭐¶
3D 位置目标足以完成简单的接近和触碰任务。但真实操作往往需要更丰富的目标表达。
为什么 3D 位置不够? 考虑以下任务:
| 任务 | 需要位置 | 需要姿态 | 需要夹爪 | 原因 |
|---|---|---|---|---|
| 触碰物体 | 是 | 否 | 否 | 只要够到即可 |
| 抓杯把手 | 是 | 是 | 是 | 把手方向和夹持宽度都很重要 |
| 插入操作 | 是 | 是 | 否 | 插入方向必须对齐 |
| 推动物体 | 是 | 是 | 否 | 推力方向需要控制 |
| 翻转开关 | 是 | 是 | 是 | 手指方向和接触角 |
多模态目标向量的设计:
其中 \(p_{goal}\) 是末端目标位置,\(q_{goal}\) 是目标姿态的四元数表示,\(w_{gripper}\) 是夹爪开合目标。
也可以使用 6D rotation representation(周等人提出的 6D 连续旋转表示)替代四元数:
6D 表示的优势是连续(无四元数的反号歧义),缺点是维度更高、需要在网络输出后做正交化。
渐进式目标扩展策略:
不建议一开始就使用完整的 10D 目标。推荐的训练流程是:
| 阶段 | 目标维度 | 训练轮数 | 验证标准 |
|---|---|---|---|
| 1 | 3D 位置 | 充分训练至稳定 | 0.5 m 范围内成功率 > 90% |
| 2 | 3D + 3D 姿态 | 接续训练或微调 | 姿态误差 < 15° |
| 3 | 6D + 1D 夹爪 | 最终微调 | 抓取成功率 > 70% |
反事实推理:如果一开始就训练 10D 目标会怎样?低层策略需要同时学习位置到达、姿态对齐和夹爪控制,奖励函数中的多个目标会互相竞争。经验表明,直接训练 10D 目标的样本效率比渐进式差约 2-3 倍,且容易出现"位置到了但姿态不对"或"姿态对了但位置偏"的局部最优。
89.3.3 动作空间¶
动作可以是关节位置目标:
底层 PD 转换为力矩:
为什么低层不直接输出末端速度?
因为全身策略要同时控制腿和臂。
末端速度还需要 IK 或 WBC 才能变成关节动作。
VBC 低层直接输出关节目标,避免了在线 IK 的一部分复杂性。
89.3.4 低层奖励¶
低层奖励通常分三类。
| 类别 | 奖励 |
|---|---|
| tracking | EE goal 跟踪、base velocity 跟踪 |
| regularization | action rate、torque、joint limit、姿态正则 |
| survival | base 高度、姿态、episode 存活 |
末端跟踪奖励:
基座稳定惩罚:
动作平滑:
89.3.5 低层环境代码骨架¶
class LowLevelGoalEnv:
def compute_observation(self):
obs = []
obs.append(self.projected_gravity)
obs.append(self.base_ang_vel * 0.25)
obs.append(self.dof_pos - self.default_dof_pos)
obs.append(self.dof_vel * 0.05)
obs.append(self.ee_goal_body)
obs.append(self.last_action)
return torch.cat(obs, dim=-1)
def compute_reward(self):
ee_err = torch.norm(self.ee_pos_body - self.ee_goal_body, dim=-1)
r_goal = torch.exp(-(ee_err ** 2) / (self.ee_sigma ** 2))
c_tilt = torch.sum(self.projected_gravity[:, :2] ** 2, dim=-1)
c_rate = torch.sum((self.action - self.last_action) ** 2, dim=-1)
return self.w_goal * r_goal - self.w_tilt * c_tilt - self.w_rate * c_rate
89.3.6 低层目标采样¶
目标分布必须与高层输出匹配。
如果低层只见过机身前方 20 cm 的目标,高层输出侧后方目标时必然失败。
| 采样维度 | 简单 | 困难 |
|---|---|---|
| 目标距离 | 近 | 远 |
| 目标高度 | 中间 | 高/低 |
| 基座速度 | 静止 | 行走 |
| 地形 | 平地 | 粗糙 |
| 负载 | 无 | 有 |
⚠️ 常见陷阱¶
⚠️ 工程陷阱:goal 采样超出工作空间
低层会在不可达目标上反复失败,学习到奇怪的身体倾斜。
应先用几何或采样统计估计可达空间,再逐步扩大。
💡 概念误区:低层只需要追末端
低层必须同时保持基座稳定和动作平滑。
否则高层给出合理目标时,机器人仍可能为了够到目标而摔倒。
🧠 思维陷阱:低层越通用越好
太宽的目标分布会降低学习效率。
通用性应通过课程逐步扩展,而不是一开始覆盖所有任务。
练习 89.3¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 为 Go2+ARX5 设计低层观测向量,列出每项维度和传感来源。 | ⭐⭐ |
| 2 | 采样 1000 个目标,用 IK 或随机动作估计可达率,画出工作空间热力图。 | ⭐⭐⭐ |
| 3 | 训练低层时分别使用窄目标分布和宽目标分布,比较收敛速度和泛化。 | ⭐⭐⭐ |
89.3b 源码走读:低层真实架构(球坐标命令 + ROA + IK)⭐⭐⭐⭐¶
§89.3 用直觉和公式讲清了"低层是什么",但论文 §III 对低层的描述高度压缩——它只用半页篇幅交代"低层是一个 goal-reaching 的全身策略",却把三个对复现至关重要的设计藏进了代码:目标用什么坐标表达、特权信息如何蒸馏进可部署网络、臂的关节命令到底是 RL 直接输出还是经过 IK。本节按论文解读规范的三栏式(论文说了什么 → 用人话重讲 → 代码怎么做的)逐项展开,把这三个设计从源码里挖出来。所有代码定位以 visual_wholebody 仓库的函数名为准——主分支重构后行号会漂移,但函数名和配置键名稳定。
89.3b.1 目标表达:身体系球坐标 \((l, p, y)\) 而非笛卡尔 \((x,y,z)\)¶
论文原文¶
"The low-level policy takes as input the proprioception and a target end-effector position command, and outputs joint actions for the whole body."(论文 §III-A,对目标表达只说"position command")
论文这句话给读者留下一个自然但错误的印象:目标是一个笛卡尔三元组 \((x, y, z)\)。如果你按这个印象去采样目标、设计课程,会发现训练分布和论文项目页展示的"机器人绕着物体转、从各个朝向接近"对不上。
用人话重讲¶
代码里低层目标命令的真实表达是**身体系下的球坐标** \((l, p, y)\),即半径(length)、俯仰(pitch)、偏航(yaw)。配置键 command_mode='sphere' 切换到这一模式,三个分量各自有独立的采样区间 l_range / p_range / y_range。为什么用球坐标而不是笛卡尔?
- 可达空间的形状是球壳,不是立方体:机械臂末端的可达集近似一个以肩关节为中心的球壳。用 \((l, p, y)\) 采样,沿半径方向均匀就天然落在可达壳层内;用 \((x,y,z)\) 均匀采样,立方体的角落大量落在不可达区域,浪费样本并诱导策略学习"够不到时怎么挣扎"的坏行为。这正是 §89.3.6 "goal 采样超出工作空间"陷阱的根因——而球坐标从参数化层面就缓解了它。
- 方位角 \(y\) 直接对应"从哪个朝向接近":高层要让机器人绕到物体侧面、后面接近时,输出的是方位角的变化。球坐标让"接近朝向"成为一个独立、可解释、可课程化的维度。
这就是代码里 sphere2cart 做的事:把网络/课程使用的球坐标命令转回笛卡尔,喂给跟踪奖励和 IK。
代码怎么做的¶
# 来源:low-level .../b1z1 config + manip_loco(源码原文,按函数名定位)
command_mode = 'sphere' # 切换到球坐标命令
l_range = [0.20, 0.75] # 半径:从贴身到接近臂展上限
p_range = [-1.0, 1.0] # 俯仰(弧度):覆盖低处与高处目标
y_range = [-1.5, 1.5] # 偏航(弧度):覆盖侧前到侧后
# 命令插值 + 球转笛卡尔(manip_loco 中目标更新路径)
curr_ee_goal_sphere = lerp(prev_goal_sphere, target_goal_sphere, blend) # 帧间线性插值,防跳变
curr_ee_goal_cart = sphere2cart(curr_ee_goal_sphere) # 转回笛卡尔供奖励/IK 使用
本质洞察:低层目标用球坐标,不是一个无关紧要的实现选择,而是 VBC "可达性友好"的第一道防线。它把"目标几乎总是可达"这个性质编码进了参数化本身——网络几乎不会被喂一个落在球壳之外的命令。这解释了为什么 VBC 低层能在很宽的目标范围上保持高成功率:宽,但宽在球壳的"表面积"上,而不是在立方体的"体积"里。
⚠️ 论文-代码差异(CONFLICT/PARTIAL)¶
| 项目 | 论文 | 代码 | 判断 |
|---|---|---|---|
| 目标坐标 | "position command"(暗示笛卡尔) | command_mode='sphere',球坐标 \((l,p,y)\) |
PARTIAL——论文未明说,复现须用球坐标采样 |
| 帧间插值 | 未提 | lerp 在球坐标上插值 |
UNSPECIFIED——防跳变 trick,论文遗漏 |
arm_induced_pitch |
未提 | 目标俯仰受臂姿态影响的补偿项 | UNSPECIFIED——代码存在,论文完全没提 |
89.3b.2 末端跟踪奖励:代码用 L1 误差,不是论文写的指数 L2¶
论文原文¶
论文把末端跟踪奖励写成指数核形式 \(r_{\text{track}} = \exp(-\|p_{ee}-p_{goal}\|^2 / \sigma^2)\),这是腿足 RL 里最标准的跟踪奖励写法(§89.3.4 也沿用了它)。
用人话重讲¶
读到这里你会默认照搬这个指数 L2 核就行。但低层奖励源码里,末端到目标的距离项用的是 L1 误差(逐分量绝对值求和),而非论文公式里的 L2 平方。这是 §89.1c 映射表中标为 CONFLICT 的那一行。差别看似微小,对训练动力学却有实际影响:
- L2 平方在误差小时梯度趋零:当 \(\|p_{ee}-p_{goal}\|\) 已经很小,\(\nabla \|e\|^2 = 2e \to 0\),最后几厘米的"精修"信号很弱,策略容易停在"差不多到了"。
- L1 在误差小时梯度恒定:\(\nabla |e| = \text{sign}(e)\),幅度不随误差缩小而衰减,对末端精度要求高的抓取任务,能持续提供"再靠近一点"的压力。
- 代价:L1 在零点不可导、对噪声更敏感,但 RL 里目标是统计期望,单点不可导不构成问题。
对比性思维(不是 X 而是 Y):论文写指数 L2,不是因为它一定最优,而是因为它是社区的"默认模板",写进论文最不容易招审稿人质疑。代码改用 L1,才是作者在真实训练里调出来的选择。这正是论文解读的独特价值——论文给你范式,代码给你真相。如果你按论文的 L2 复现却调不出论文的末端精度,第一个该怀疑的就是这里。
代码怎么做的¶
# 来源:low-level 末端跟踪奖励(源码原文,按函数名定位 _reward_tracking_ee_*)
# 逐分量绝对值求和(L1),而非论文的平方 L2:
ee_pos_error = torch.sum(torch.abs(ee_pos_local - ee_goal_cart), dim=-1)
reward = torch.exp(-ee_pos_error / sigma) # 指数核外壳保留,但核内是 L1
注意指数核的"外壳"还在,变的是核内的距离度量:论文是 \(\exp(-\|e\|_2^2/\sigma^2)\),代码是 \(\exp(-\|e\|_1/\sigma)\)。复现时若直接用 L2 平方,\(\sigma\) 的量纲和数值都对不上,会得到完全不同的奖励地形。
💡 论文没告诉你的¶
代码里末端奖励往往还叠了一个**到达后的稳定 bonus**(误差进入阈值后给固定正奖励),鼓励策略"到了就稳住"而不是"路过目标继续动"。论文正文只字未提,但它对抓取前的静止至关重要——抓取要求末端在接触瞬间几乎不动。来源:低层奖励函数中的阈值分支(代码逻辑推断)。
89.3b.3 ROA 特权蒸馏:低层自己也是一个 Teacher-Student¶
论文原文¶
论文提到低层用 Regularized Online Adaptation(ROA) 处理特权信息——把仿真才有的特权量(如精确摩擦、外力、连杆质量)编码成一个潜变量,再训练一个只看历史本体感知的自适应模块去回归它。
用人话重讲¶
这是初学者最容易忽略的一层嵌套:VBC 的低层内部,本身就是一个小型的 teacher-student。不要把"教师-学生"只理解为高层那一级(特权教师 → 深度学生)。低层这一级也有:
- 特权编码器(teacher 侧):输入仿真特权量 \(s^{\text{priv}}\),输出潜变量 \(z = E_{\text{priv}}(s^{\text{priv}})\)。
- 自适应模块(student 侧):输入一段历史本体感知 \(o_{t-H:t}\),输出估计 \(\hat z = E_{\text{adapt}}(o_{t-H:t})\)。
- 正则项:训练中用一个随训练进度调度的系数
priv_reg_coef_schedual把 \(\hat z\) 拉向 \(z\)(或反向蒸馏),使部署时只用 \(\hat z\) 也能近似特权策略的行为。
为什么不能把特权量直接喂给部署策略?因为真机上没有"精确摩擦系数"这种量。ROA 的本质,是让策略学会**从可观测的历史里隐式推断不可观测的环境参数**——历史里藏着环境的指纹(同样的动作在不同摩擦下产生不同的本体响应),自适应模块学的就是读这个指纹。
其中 \(\text{sg}[\cdot]\) 是 stop-gradient,\(\lambda(t)\) 是 priv_reg_coef_schedual 给出的调度系数——训练早期小(让两支先各自学好),后期增大(强制对齐)。
代码怎么做的¶
# 来源:low-level config(源码原文,按键名定位)
priv_reg_coef_schedual = [0, 0.1, 2000, 3000]
# 读法:在第 2000~3000 次迭代间,把正则系数从 0 线性升到 0.1
# 早期 (<2000 iter):系数 0,特权编码器和自适应模块各自自由学习
# 升温期 (2000~3000):系数线性增大,逐步强制自适应模块对齐特权潜变量
# 后期 (>3000):系数保持 0.1,对齐约束稳定生效
hist_encoding = True # 高层装载低层时打开历史编码,部署用自适应模块而非特权编码器
理论-工程桥接:
priv_reg_coef_schedual不是随手设的魔法数字,它编码了一个课程式假设——"先让两个编码器各自学好,再强迫学生对齐教师"。如果一开始就用大系数硬对齐,自适应模块会在特权编码器还没学好时就去模仿一个乱动的目标,导致两支一起崩。这与 §89.5 高层 DAgger 的"先 teacher pretrain 再切 student"是同一种课程思想在不同层级的复现。
⚠️ 论文-代码差异(PARTIAL)¶
| 项目 | 论文 | 代码 | 判断 |
|---|---|---|---|
| ROA 机制 | 文字描述,给方法名 | priv_reg_coef_schedual=[0,0.1,2000,3000] |
PARTIAL——调度数值只在代码里 |
| 部署用哪支编码器 | 未明说 | hist_encoding=True,用自适应模块 |
PARTIAL——复现须确认这个开关 |
89.3b.4 臂的关节命令:IK 伪逆 还是 RL 直接输出?¶
论文原文¶
论文 §III 读起来像是低层直接对全部 19 个自由度输出关节位置增量(end-to-end joint action)。但代码里高层调用低层的 wrapper 中,又出现了"低层直接输出关节"的旁路说法,两处并存。
用人话重讲¶
这是映射表里另一处 CONFLICT,也是最容易让人复现失败的歧义。代码里实际存在两条目标→关节的路径,复现时必须确认你跑的是哪一条:
| 路径 | 臂关节如何得到 | 适用 |
|---|---|---|
| RL 直出 | 策略网络直接输出 19D 关节位置增量 \(\Delta q\),含臂 | 低层训练主路径 |
| IK 伪逆旁路 | 由球坐标目标解伪逆 Jacobian \(\dot q_{\text{arm}} = J^{+}\dot x\) 得到臂关节,腿仍由 RL 控制 | 某些对照/wrapper 路径 |
为什么会并存?一种合理推断是:作者在迭代中试过"臂用 IK、腿用 RL"的混合方案(IK 让臂的末端跟踪更精确),最后主线选择了全 RL 直出(避免在线求伪逆、避免奇异点),但旁路代码留在了仓库里——这正是论文解读规范"废弃代码"类隐藏信息的典型样本(被 flag 控制的旧方案,往往是作者试过但没写进论文的)。
💡 论文没告诉你的¶
如果你复现时发现臂末端跟踪精度远好于或远差于论文,先确认你激活的是哪条路径。IK 旁路在可达区内精度更高但遇奇异点会爆,RL 直出更鲁棒但末端精度依赖奖励调参。来源:高层 wrapper 调用低层的控制路径(代码逻辑推断,论文未提)。
⚠️ 常见陷阱(源码层)¶
📄 论文误导陷阱:按论文笛卡尔目标采样训练低层
错误做法:读论文"position command"后,用 \((x,y,z)\) 立方体均匀采样目标训练低层。
现象:训练 loss 正常下降,但成功率卡在 60% 上不去,且策略学出明显的"身体歪斜去够边角目标"的怪姿态。
根本原因:代码用球坐标
command_mode='sphere'采样,目标天然落在可达球壳上;笛卡尔立方体采样把大量样本投到不可达角落,策略被迫在不可达目标上"挣扎",污染了奖励地形。正确做法:以源码 config 的
l_range/p_range/y_range为准做球坐标采样。自检:打印目标分布,确认它是一个球壳层而非实心立方体。📄 论文误导陷阱:照搬指数 L2 跟踪奖励
错误做法:用论文公式 \(\exp(-\|e\|_2^2/\sigma^2)\) 作为末端跟踪奖励。
现象:末端能"大致到达"但最后 2-3 cm 总是精修不到位,抓取成功率偏低。
根本原因:L2 平方在误差小时梯度趋零,精修信号弱;代码实际用 L1 距离,小误差区梯度恒定。
正确做法:核内改用 L1,重新标定 \(\sigma\)。自检:画奖励-误差曲线,确认在 0~3 cm 区间仍有明显斜率。
🧠 思维陷阱:低层只有一级网络
新手以为低层就是一个 actor MLP。实际上低层内部嵌套了 ROA 的特权编码器 + 自适应模块两支,部署时用自适应模块。忽略这一点,会在装载 checkpoint 时漏掉
hist_encoding=True,导致部署策略行为与训练时不一致。
练习 89.3b¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 实现 sphere2cart 与其逆 cart2sphere,用 1000 个随机球坐标命令验证往返误差 < 1e-5。 |
⭐⭐ |
| 2 | 在自己的低层环境中分别用 L1 和指数 L2 跟踪奖励训练,画出末端误差随训练的收敛曲线,对比最后 3 cm 的精修速度。 | ⭐⭐⭐ |
| 3 | 复现 ROA:搭一个特权编码器 + 历史自适应模块,用 priv_reg_coef_schedual 式调度对齐二者,绘制 \(\|\hat z - z\|\) 随迭代的下降曲线。 |
⭐⭐⭐⭐ |
89.4 Stage 2:特权教师规划器 ⭐⭐⭐¶
这一节讲视觉学生要模仿的对象。
特权教师不受真实传感限制。
它可以读取物体真值位置、目标类别和环境状态。
这不是作弊。
这是训练时利用仿真真值生成监督信号。
部署时,这些真值必须由视觉学生近似。
89.4.1 教师输入¶
教师可使用:
| 信息 | 部署是否可直接使用 | 用途 |
|---|---|---|
| 物体 6D 位姿真值 | 否 | 生成目标 |
| 物体尺寸 | 可由识别估计 | 判断可达区域 |
| 机器人全状态 | 部分可用 | 评估当前姿态 |
| 目标类别 | 可由视觉识别 | 选择策略 |
| 深度图真值 mask | 否 | 训练学生 |
教师输出通常是低层 goal。
低层执行:
89.4.2 教师为什么不直接输出关节动作¶
如果教师直接输出关节动作,学生模仿会更困难。
因为学生必须同时学习视觉和全身控制。
让教师输出 goal,低层负责控制,可以显著降低模仿难度。
| 教师输出 | 学生学习难度 | 可复用性 |
|---|---|---|
| 关节动作 | 高 | 低 |
| 末端 goal | 中 | 高 |
| 子任务阶段 | 中 | 高 |
| 视觉 embedding | 高 | 中 |
89.4.3 教师目标生成¶
一个简单教师可以写成:
其中 \(\Delta p_{approach}\) 根据物体类别和任务定义。
例如抓杯子:
| 阶段 | goal |
|---|---|
| approach | 杯子侧上方 |
| align | 杯子把手附近 |
| contact | 夹爪中心到杯子 |
| lift | 杯子上方 |
VBC 中教师可通过 RL 或规划学习更复杂目标。
但无论教师复杂与否,输出必须在低层可达空间内。
89.4.4 教师训练目标¶
教师奖励可包含:
| 项 | 作用 |
|---|---|
| 任务成功 | 接近、触碰、抓取或到达 |
| goal 可执行性 | 低层能跟踪 |
| 距离缩短 | 逐步靠近目标 |
| 碰撞避免 | 不撞身体和环境 |
| 平滑 | goal 不剧烈跳变 |
目标平滑非常重要。
如果教师输出的 goal 在相邻帧跳动,低层会产生动作尖峰。
89.4.5 教师伪代码¶
class PrivilegedTeacher:
def compute_goal(self, state):
obj_pos = state.object_pos_world
base_T_world = state.base_pose.inverse()
# 根据任务阶段选择目标偏移。
if state.phase == "approach":
offset = torch.tensor([0.0, 0.0, 0.10], device=obj_pos.device)
elif state.phase == "contact":
offset = torch.tensor([0.0, 0.0, 0.02], device=obj_pos.device)
else:
offset = torch.tensor([0.0, 0.0, 0.20], device=obj_pos.device)
goal_world = obj_pos + offset
goal_body = transform_point(base_T_world, goal_world)
return clamp_to_low_level_workspace(goal_body)
⚠️ 常见陷阱¶
⚠️ 工程陷阱:教师输出低层不可达目标
现象:教师看起来合理,但低层始终失败。
正确做法:用低层成功率作为教师奖励或过滤条件。
💡 概念误区:教师越强越好
教师使用的信息若无法从深度图推断,学生永远学不出来。
教师应强,但其输出规律要能被学生观测解释。
🧠 思维陷阱:教师阶段可以和低层训练完全独立
教师的目标分布必须覆盖低层训练分布。
低层和教师之间需要接口对齐。
练习 89.4¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 为抓取桌面物体设计四阶段教师 goal 生成规则。 | ⭐⭐ |
| 2 | 在教师奖励中加入低层跟踪误差,观察教师 goal 是否更平滑。 | ⭐⭐⭐ |
| 3 | 分析哪些教师特权信息可以由深度图推断,哪些很难推断。 | ⭐⭐⭐ |
89.4b 源码走读:高层 9D 动作与特权教师 ⭐⭐⭐¶
§89.4 把教师讲成了"输出一个低层 goal 的规划器",这是为了让你先抓住直觉。但代码里的高层比这丰富得多——它不是只吐一个 3D 点,而是输出一个 9D 动作向量,并且它本身也是用 RL 训练出来的(不是手写规则),输入端还接了一个吃物体点云的 PointNet++ 特征提取器。本节把高层从"概念上的规划器"还原成"代码里真实的 9D RL 策略"。
89.4b.1 高层动作不是 3D goal,而是 9D 复合动作¶
论文原文¶
论文 §III-B 说高层"proposes the end-effector position and the base velocity"。读起来像是高层输出"末端位置 + 底盘速度",维度模糊。
用人话重讲¶
代码里高层动作向量是明确的 9 维,由三部分拼成(对应 §89.1c 映射表标 SPECIFIED 的那一行,b1z1_base.py 中的动作定义):
| 分段 | 维度 | 含义 | 喂给谁 |
|---|---|---|---|
| EE 位姿增量 | 6D | 末端位置增量 \(\Delta(x,y,z)\) + 姿态增量(轴角/欧拉 3D) | 转成低层球坐标 goal |
| 底盘速度 | 2D | \(v_x, \omega_z\)(前进 + 转向) | 低层的速度命令通道 |
| 夹爪 | 1D | 夹爪开合 | 直接驱动夹爪 |
关键点有三个:
- 是增量(delta)不是绝对位姿:高层每步输出的是"在当前末端基础上往哪挪一点",不是"末端应该在哪"。增量动作天然平滑、范围有界,避免了高层一步跳到远处目标导致低层动作尖峰——这是 §89.4.4 "目标平滑"在代码里的实现方式。
- 底盘速度和末端目标同时输出:高层不只挑末端去哪,还同时决定身体怎么走。这是"loco-manipulation"里 loco 和 mani 协同的体现——要够到远处物体,得一边挪底盘一边伸臂,两者必须联合决策。
- 夹爪是独立的第 9 维:抓取时机由高层学,不是几何规则触发。
代码怎么做的¶
# 来源:high-level/envs/b1z1_base.py(源码原文,按动作切片定位)
# 9D 高层动作的语义切片:
action_ee = actions[:, 0:6] # 末端位姿增量(位置 3D + 姿态 3D)
action_base = actions[:, 6:8] # 底盘速度 (v_x, omega_z)
action_gripper = actions[:, 8:9] # 夹爪开合
# 末端增量累加到当前 goal,再转球坐标喂低层
ee_goal = current_ee + scale_pos * action_ee[:, 0:3] # 增量更新,scale 限幅
# ...姿态增量同理累加,最终打包成低层接口需要的命令
本质洞察:高层输出"增量 + 底盘速度 + 夹爪"这个 9D 组合,等价于给低层下达一份**完整的全身意图**——"末端朝这个方向挪一点、身体这样走、夹爪这样动"。它比单纯的 3D goal 表达力强得多,但又比直接输出 19 个关节动作抽象得多。VBC 选这个抽象层级,是它分层契约的精髓:高层管"意图",低层管"如何用全身实现意图"。
89.4b.2 教师是 RL 策略 + PointNet++,不是手写规则¶
论文原文¶
论文说教师用物体的真值位姿和形状特征(point cloud feature),通过 RL 训练。§89.4.5 给的手写四阶段规则只是教学简化,用来建立直觉。
用人话重讲¶
真实教师有两个关键部件:
- PointNet++ 点云特征提取器:输入物体的点云(仿真里从物体网格采样得到),输出一个形状特征向量。为什么要形状特征?因为"该从哪抓"依赖物体几何——抓杯子要找把手,抓盒子要找边缘。形状特征让同一个教师网络能泛化到不同形状的物体(论文 sim 评估覆盖 33 个 YCB 物体 / 7 类,正是靠这个特征做类别泛化)。
- 特权状态输入:物体真值 6D 位姿、机器人全状态——这些部署时没有,只在仿真训练教师时可用。
教师的奖励(论文 Appendix C.2,权重从代码 reward_vec_task.py / b1z1_pickmulti.py 提取)大致包含:接近奖励(末端到物体距离缩短)、抓取/抬起成功奖励、动作正则、不掉落惩罚。注意论文只给了奖励的"结构",具体权重藏在代码里——这是 §89.1c 标 PARTIAL 的原因。
代码怎么做的¶
# 来源:high-level/modules/feature_extractor.py(源码原文,按类名定位)
# 物体点云 → PointNet++ → 形状特征,拼进高层观测
obj_feature = self.pointnet(object_point_cloud) # 形状特征向量
high_level_obs = torch.cat([
proprio, # 本体感知
obj_pose_priv, # 物体真值位姿(特权,仅训练教师可用)
obj_feature, # PointNet++ 形状特征
low_level_state, # 低层可达性相关状态
], dim=-1)
理论-工程桥接:教师用 PointNet++ 而不是把点云拍平成向量,是因为点云是无序集合——拍平会让"同一物体、点的顺序不同"变成两个不同输入,网络学不动。PointNet++ 的对称聚合(max-pooling over points)保证了置换不变性。这不是炫技,而是"输入是集合"这一数据结构性质强制的选择。深度学生 §89.5 之所以学起来难,部分原因就是它要从深度图里隐式重建这个本来由 PointNet++ 显式提供的形状信息。
89.4b.3 低层 checkpoint 如何装进高层¶
用人话重讲¶
高层训练时,低层是**冻结的、预训练好的**。高层每走一步,内部会调用一次低层把 9D 意图转成关节动作执行。这就是 §89.6 "频率"那张表的代码体现——高层一步对应低层若干步(高层 5-20 Hz,低层 50-200 Hz)。
# 来源:high-level/envs/b1z1_base.py(源码原文,按加载逻辑定位)
self.low_policy = torch.load(low_policy_path) # 装载冻结的低层 checkpoint
# 关键:必须打开 hist_encoding,部署时低层用自适应模块(见 §89.3b.3 ROA)
self.low_policy.hist_encoding = True
# 高层 step 内,每个低层子步:
for _ in range(self.decimation): # 高层 1 步 = 低层 decimation 步
low_action = self.low_policy.act(low_obs, hist_encoding=True)
self.env.step(low_action) # 低层实际驱动关节
⚠️ 论文-代码差异(PARTIAL/SPECIFIED)¶
| 项目 | 论文 | 代码 | 判断 |
|---|---|---|---|
| 高层动作维度 | "EE position + base velocity"(模糊) | 明确 9D:6D 增量 + 2D 速度 + 1D 夹爪 | SPECIFIED(代码明确) |
| 教师形状特征网络 | "point cloud feature" | PointNet++,细节在代码 | PARTIAL |
| 教师奖励权重 | Appendix C.2 给结构 | 数值在 reward_vec_task.py |
PARTIAL |
| 低层装载开关 | 未提 | hist_encoding=True 必开 |
UNSPECIFIED |
⚠️ 常见陷阱(源码层)¶
📄 论文误导陷阱:把高层实现成只输出 3D goal 的规则规划器
错误做法:照 §89.4.5 的手写四阶段规则做高层,输出 3D 点。
现象:固定物体能抓,但换形状/换高度就失败,且机器人不会主动挪底盘去够远处物体。
根本原因:真实高层是 9D RL 动作(含底盘速度和夹爪),且靠 PointNet++ 形状特征做物体泛化。3D 规则版丢了 loco-mani 协同和形状泛化两大能力。
正确做法:复现核心结果须用 9D 动作 + RL 教师;手写规则仅用于打通低层接口的最小验证。
⚠️ 工程陷阱:装载低层 checkpoint 时漏开
hist_encoding现象:高层训练 reward 异常低,低层行为抖动。
根本原因:低层用 ROA 训练(§89.3b.3),部署须用历史自适应模块。不开
hist_encoding会走特权编码器分支,而部署期没有特权输入,潜变量全错。正确做法:
torch.load后显式置hist_encoding=True,并打印低层潜变量确认非全零/非 NaN。💡 概念误区:高层输出绝对末端位置
高层输出的是增量 \(\Delta\) 不是绝对位姿。误当绝对位姿累加会导致目标"漂移加倍"或完全失控。复现时先确认这一点。
练习 89.4b¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 把高层动作从 9D 拆解打印,验证 6D 增量 + 2D 速度 + 1D 夹爪的语义切片正确。 | ⭐⭐ |
| 2 | 用一个小 PointNet++ 提取 5 类物体的形状特征,做 t-SNE 可视化,确认同类聚集。 | ⭐⭐⭐ |
| 3 | 把高层动作从"增量"改成"绝对位姿",对比两种参数化下教师的训练稳定性和 goal 平滑度。 | ⭐⭐⭐ |
89.5 Stage 3:深度图学生与在线蒸馏 ⭐⭐⭐⭐¶
这一节讲 VBC 的视觉学习核心。
学生不能访问物体真值。
它只能看到深度图和本体感知,并模仿教师输出。
89.5.1 学生输入¶
学生输入通常包括:
| 输入 | 作用 |
|---|---|
| 深度图 | 物体位置、形状、遮挡 |
| 前景 mask | 聚焦目标区域 |
| 本体感知 | 机器人姿态和可达性 |
| 低层状态 | 判断目标是否可执行 |
| 历史 | 缓解深度噪声和延迟 |
深度图不是完美几何。
真实深度相机会有空洞、噪声、反光和边缘错误。
89.5.2 深度处理 pipeline¶
raw depth
├─ clip range
├─ remove invalid pixels
├─ foreground mask
├─ resize / crop
├─ normalize
└─ CNN encoder
↓
visual latent
↓
student head
↓
low-level goal
89.5.3 蒸馏目标¶
教师输出:
学生输出:
监督损失:
如果教师输出动作分布,也可用 KL:
89.5.4 在线蒸馏为什么重要¶
离线蒸馏只收集教师访问的状态。
学生部署时会犯小错误。
小错误会把机器人带到教师数据中较少出现的状态。
这就是分布偏移。
在线蒸馏的做法是让学生参与采样,再由教师标注当前状态下应输出的 goal。
这与 DAgger 的思想一致。
本质洞察:在线蒸馏的目的不是让学生永远追随教师,而是把教师的特权决策投影到学生真实可见的信息空间。教师负责在训练期提供"这一步该去哪里"的标签,学生负责学会在缺少真值物体位姿时做出足够接近的判断。两者的边界如果混乱,学生就会在部署时等待一个永远不会出现的特权输入。
89.5.5 学生网络代码骨架¶
import torch
import torch.nn as nn
class DepthStudent(nn.Module):
def __init__(self, proprio_dim, goal_dim):
super().__init__()
self.cnn = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=5, stride=2),
nn.ELU(),
nn.Conv2d(16, 32, kernel_size=3, stride=2),
nn.ELU(),
nn.Conv2d(32, 64, kernel_size=3, stride=2),
nn.ELU(),
nn.AdaptiveAvgPool2d((1, 1)),
nn.Flatten(),
)
self.head = nn.Sequential(
nn.Linear(64 + proprio_dim, 256),
nn.ELU(),
nn.Linear(256, 128),
nn.ELU(),
nn.Linear(128, goal_dim),
)
def forward(self, depth, proprio):
z = self.cnn(depth)
return self.head(torch.cat([z, proprio], dim=-1))
def distillation_loss(goal_student, goal_teacher, mask_valid=None):
error = (goal_student - goal_teacher) ** 2
if mask_valid is not None:
per_sample = torch.mean(error, dim=-1)
valid = mask_valid.to(dtype=per_sample.dtype)
denom = torch.clamp(valid.sum(), min=1.0)
return torch.sum(per_sample * valid) / denom
return torch.mean(error)
AdaptiveAvgPool2d((1, 1)) 让 CNN 输出固定为 64 维,因此深度图分辨率可以在训练配置中调整,而不需要同步改全连接层输入维度。
带 mask_valid 的蒸馏损失先对每个样本的目标维度求均值,再只按有效样本数归一化。
这样无效深度帧不会把 batch 平均值稀释,也不会在遮挡比例变化时改变有效样本的梯度尺度。
89.5.5b 深度编码器架构对比:ResNet-18 vs 轻量 CNN ⭐⭐⭐¶
深度编码器的选择直接影响视觉策略的精度、延迟和部署可行性。工程中常见两种路线:重型骨干(如 ResNet-18)和轻量定制 CNN。
ResNet-18 编码器:
| 特性 | 值 |
|---|---|
| 参数量 | 11.2M(全量),5.6M(截断至 layer3) |
| 输入分辨率 | 通常 \(224 \times 224\) |
| 输出特征维度 | 256(layer3)或 512(layer4) |
| 单帧推理延迟 | ~3-5 ms(GPU),~15-30 ms(边缘设备) |
| 优势 | ImageNet 预训练权重可迁移,特征表达能力强 |
| 劣势 | 参数量大,边缘设备延迟高,深度图与 RGB 分布不同导致预训练收益有限 |
轻量定制 CNN(如上文 89.5.5 的 3 层 CNN):
| 特性 | 值 |
|---|---|
| 参数量 | ~0.05-0.2M |
| 输入分辨率 | \(64 \times 64\) 或 \(48 \times 48\) |
| 输出特征维度 | 32-128 |
| 单帧推理延迟 | ~0.3-1 ms(GPU),~2-5 ms(边缘设备) |
| 优势 | 快速,适合策略频率 > 20 Hz 的闭环控制 |
| 劣势 | 对小物体和远距离物体识别能力弱 |
计算预算分析:
VBC 的视觉策略通常以 10-30 Hz 运行。每次推理包含深度预处理、CNN 编码、MLP 头部、坐标转换和 goal 滤波。总预算约 30-100 ms。
| 组件 | 轻量 CNN | ResNet-18 |
|---|---|---|
| 深度预处理 | 1 ms | 2 ms |
| CNN 编码 | 1 ms | 5 ms |
| MLP 头部 | 0.2 ms | 0.5 ms |
| 坐标转换 + 滤波 | 0.3 ms | 0.3 ms |
| 总计 | ~2.5 ms | ~7.8 ms |
| 可支撑频率 | ~400 Hz | ~128 Hz |
对于 20 Hz 的视觉策略,两者都能满足。但在边缘设备(如 Jetson Orin NX)上,ResNet-18 的延迟可能达到 30 ms,成为瓶颈。
本质洞察:深度编码器的选择不是"越大越好"。VBC 的视觉目标是从深度图中估计一个 3D 点——这个任务的信息熵远低于 ImageNet 分类。轻量 CNN 往往足够,而 ResNet-18 的多余容量可能导致过拟合仿真深度特征、在真实深度图上泛化变差。
选型建议:
| 场景 | 推荐编码器 | 原因 |
|---|---|---|
| 仿真快速迭代 | 轻量 CNN | 训练快、调试快 |
| 真机部署、目标简单 | 轻量 CNN | 延迟优先 |
| 目标多样性高(多物体类别) | ResNet-18 截断至 layer2 | 需要更强的特征提取 |
| RGB 辅助输入 | ResNet-18 + 预训练 | RGB 域迁移有收益 |
89.5.6 深度噪声随机化¶
训练学生时必须模拟视觉误差。
| 随机化 | 作用 |
|---|---|
| Gaussian noise | 模拟测量噪声 |
| dropout holes | 模拟无效深度 |
| blur | 模拟运动模糊 |
| crop jitter | 模拟检测框误差 |
| latency | 模拟处理延迟 |
| lighting domain shift | 对 RGB 有用,对深度也会影响部分相机 |
⚠️ 常见陷阱¶
⚠️ 工程陷阱:学生训练只用干净深度图
仿真中效果很好,真实深度图一有空洞就失败。
必须加入噪声、遮挡、延迟和无效像素。
💡 概念误区:监督学生只需要 MSE
如果同一深度图下存在多个合理目标,MSE 会学平均目标。
对多峰任务可用阶段分类、混合分布或高层离散动作。
🧠 思维陷阱:学生失败说明视觉网络不够大
失败可能来自相机标定、低层不可达、教师目标跳变或深度延迟。
先检查接口和数据,再加网络规模。
练习 89.5¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 实现一个深度图学生网络,输入 \(64\times64\) 深度图和本体感知,输出 3D goal。 | ⭐⭐ |
| 2 | 加入 depth dropout 和 latency,比较学生目标误差。 | ⭐⭐⭐ |
| 3 | 用在线蒸馏和离线蒸馏分别训练学生,比较滚动执行成功率。 | ⭐⭐⭐⭐ |
89.5b 源码走读:DAgger 蒸馏与深度编码器 ⭐⭐⭐⭐¶
§89.5 把蒸馏讲成"学生模仿教师、用 DAgger 缓解分布偏移",方向正确,但代码里有两个细节决定复现成败:深度图的真实分辨率不是教学常用的 64×64,而是 58×87;DAgger 的师生混合不是按概率软混合,而是按时间步的硬切换。这两点都在 §89.1c 映射表里标了 CONFLICT,本节把它们从源码里坐实。
89.5b.1 深度编码器:真实输入是 58×87,不是 64×64¶
论文原文¶
论文说学生用深度图作为视觉输入,经 CNN 编码。论文正文没有给出确切分辨率——这正是论文"隐性知识省略"缺陷的典型(§十一)。
用人话重讲¶
教学代码(包括本章 §89.5.5 的骨架)习惯用 64×64 或 48×48 这种"整齐"的方形分辨率。但 VBC 真实代码里,深度编码器类名直接写死了分辨率:DepthOnlyFCBackbone58x87——输入是 1×58×87 的单通道深度图(高 58、宽 87,长宽比约 2:3,对应裁剪后的相机视场,不是正方形)。这个非整齐的分辨率不是随意的:
- 长宽比贴合相机视场:深度相机的水平视场通常比垂直视场宽,58×87(高<宽)保留了这个比例,避免把宽视场硬压成方形导致水平方向分辨率损失。
- 尺寸偏小:58×87 ≈ 5000 像素,远小于 ImageNet 的 224×224 ≈ 50000 像素。这印证了 §89.5.5b 的洞察——"从深度图估计一个 3D 点"的信息熵远低于分类,小分辨率 + 轻量 CNN 足够。
编码器结构(已核实):两层卷积 Conv2d(num_frames→32, k=5) → Conv2d(32→64, k=3),配 max-pooling,再展平接两层全连接,输出 latent_dim 维潜向量。注意第一层输入通道是 num_frames——多帧深度堆叠,用来缓解 §89.5.1 提到的深度噪声和延迟(多帧给网络时间上下文去平滑单帧的空洞和抖动)。
代码怎么做的¶
# 来源:high-level/modules/feature_extractor.py(源码原文,按类名定位)
class DepthOnlyFCBackbone58x87(nn.Module):
def __init__(self, ..., num_frames, latent_dim):
# 输入:num_frames × 58 × 87 (多帧单通道深度堆叠)
self.image_compression = nn.Sequential(
nn.Conv2d(in_channels=num_frames, out_channels=32, kernel_size=5),
# ...activation + maxpool...
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3),
# ...activation + maxpool, flatten...
)
# 展平后接两层 FC,输出 latent_dim 维形状/位置潜向量
本质洞察:分辨率 58×87 这个"不整齐"的数字,是论文解读才能挖出的硬约束。如果你按论文模糊描述用 64×64 训练,再把真机深度图(按相机原生比例裁剪到 58×87)喂进去,输入尺寸就对不上——要么 resize 引入畸变,要么直接维度报错。论文给你"用深度图",代码给你"58×87"——后者才是能跑通的那个。
89.5b.2 DAgger 混合:硬切换(threshold timestep)而非概率软混合¶
论文原文¶
论文说用 "online imitation"(在线模仿,DAgger 思想)训练学生。经典 DAgger 的混合策略是 \(\pi_{\text{mix}} = \beta_i \pi_{\text{teacher}} + (1-\beta_i)\pi_{\text{student}}\),按概率 \(\beta_i\) 在每步随机选谁来 rollout,\(\beta_i\) 随迭代衰减。
用人话重讲¶
VBC 代码的混合**不是**经典 DAgger 的概率软混合,而是**按时间步的硬切换**(§89.1c 标 CONFLICT 的那一行,已核实于 dagger_trainer.py):
- 设一个阈值步数
pretrain_timesteps(代码默认 4000)。 - 第 0 ~ 4000 步:环境完全执行 teacher 动作(
env.step(teacher_actions))。学生只在一旁观察、记录 (student_obs, teacher_action) 数据对,不参与 rollout。 - 第 4000 步之后:环境完全执行 student 动作(
env.step(actions))。学生开始用自己的策略采样,进入真正的 on-policy 分布。 - 全程:教师每步都对**当前访问到的状态**输出标签
teacher_actions,作为学生回归目标——这是 DAgger "在学生访问的状态上要教师标注"的核心,硬切换只是改了"何时让学生主导 rollout"的调度方式。
这其实是**两阶段课程**:先 teacher pretrain(让学生在教师轨迹覆盖的"好状态"上学会基本映射),再切到 student rollout(让学生在自己会犯错带到的状态上继续被教师纠正,正面对抗分布偏移)。与经典 DAgger 概率混合的对比:
| 维度 | 经典 DAgger(概率混合) | VBC 代码(硬切换) |
|---|---|---|
| 谁来 rollout | 每步按 \(\beta_i\) 随机选师/生 | 前 4000 步全教师,之后全学生 |
| \(\beta\) 调度 | 连续衰减 | 阶跃(0/1 两段) |
| 教师标签 | 始终在访问状态上标注 | 同左 |
| 实现复杂度 | 需每步采样混合 | 一个 if timestep < threshold |
| 风险 | 早期学生介入可能带偏 | 切换点附近有一次分布跳变 |
代码怎么做的¶
# 来源:high-level/learning/dagger_trainer.py(源码原文)
threshold_timestep = self.cfg.get("pretrain_timesteps", 4000) # 硬切换阈值
for timestep in range(...):
teacher_actions = self.teacher_agents.act(teacher_obs, ...)[0] # 教师标签:始终算
actions = self.agents.act(student_obs, ...)[0] # 学生动作
if timestep < threshold_timestep:
next_states, ... = self.env.step(teacher_actions) # 前期:执行教师
else:
next_states, ... = self.env.step(actions) # 后期:执行学生
# 不论谁 rollout,都用 teacher_actions 作为监督标签记录
self.agents.record_transition(student_obs=student_obs,
teacher_actions=teacher_actions, # 回归目标
...)
对比性思维(反事实):如果去掉前 4000 步的 teacher pretrain,一开始就让学生 rollout 会怎样?学生网络随机初始化,第一步就把机器人带到教师数据从未覆盖的"灾难状态"(摔倒、末端乱挥),教师在这些状态上的标签本身也不可靠(教师没在这练过),学生学到的是"垃圾状态 → 垃圾标签"的映射,训练直接发散。teacher pretrain 阶段的作用,就是先在"好状态"上把学生拉到一个合理的初始策略,再放它去探索——这与 §89.3b.3 低层 ROA 的"先各自学好再对齐"是同一种课程智慧。
💡 论文没告诉你的¶
record_transition 里还有一个 replaced_action 分支:某些情况下环境会用一个"被替换的动作"覆盖教师标签(如安全过滤把不可行动作改掉后,用改后的动作做标签)。论文完全没提,但它意味着学生学的不一定是教师的原始输出,而可能是经过可行性修正的版本。来源:dagger_trainer.py 的 infos.get("replaced_action") 分支(代码逻辑,论文未提)。
89.5b.3 多峰目标问题在代码里怎么躲过¶
§89.5 的陷阱提过"MSE 会学平均目标"。VBC 怎么避免?三个工程选择共同作用:
- 教师本身是确定性策略:同一特权状态,教师输出唯一动作,蒸馏标签无歧义——多峰性在"教师决策"这一步就被消解了,学生回归的是单峰目标。
- 回归动作而非回归 goal:学生模仿的是教师的 9D 动作(§89.4b),不是显式 3D goal。动作空间里"往哪挪一点"的增量比"去哪个绝对点"更少多峰。
- 底盘速度纳入动作:当有多个同类物体时,教师靠底盘朝向先"选定"一个,再输出末端动作——选择被编码进了底盘速度而非末端目标的多峰分布里。
本质洞察:VBC 没有用混合密度网络或离散化这些"对付多峰"的重武器,而是靠"确定性教师 + 增量动作空间"从源头让目标分布接近单峰。这是一个常被忽略的设计智慧——最好的多峰处理,是让问题一开始就不多峰。
⚠️ 常见陷阱(源码层)¶
📄 论文误导陷阱:用 64×64 方形深度图训练学生
错误做法:照教学常例和论文模糊描述,用 64×64 输入训练深度编码器。
现象:仿真里训练正常,部署真机时输入尺寸或长宽比对不上,resize 引入畸变,目标系统性偏移。
根本原因:真实编码器是
DepthOnlyFCBackbone58x87,输入 58×87(高<宽,贴合相机视场)。正确做法:按 58×87 裁剪/缩放深度图,且保持训练和部署的裁剪流程一致。自检:打印 encoder 第一层期望的 H×W,与数据张量 shape 对齐。
📄 论文误导陷阱:按经典概率 DAgger 实现混合采样
错误做法:实现 \(\beta_i\) 概率混合,每步随机选师/生 rollout。
现象:能训出来,但与论文复现曲线对不上,且早期不稳定。
根本原因:代码用硬切换(前
pretrain_timesteps=4000步全教师,之后全学生),不是概率混合。正确做法:先 teacher pretrain 固定步数,再整体切到 student rollout,全程用教师标签。自检:打印每步执行的是 teacher 还是 student 动作,确认在阈值步发生一次性切换。
🧠 思维陷阱:学生学不好就加大网络
学生失败更可能源于分辨率不匹配、缺多帧上下文、或没做 teacher pretrain,而非容量不足(§89.5.5b 已论证小网络足够)。先核对 58×87、多帧堆叠、硬切换三件事,再考虑加网络。
练习 89.5b¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 实现 DepthOnlyFCBackbone58x87:输入 \(N_{\text{frames}}\times 58\times 87\),验证两层卷积 + maxpool 后展平维度,确认能接 latent_dim 维 FC。 |
⭐⭐ |
| 2 | 实现硬切换 DAgger 训练循环,记录切换点前后的学生 rollout 成功率,观察分布跳变。 | ⭐⭐⭐ |
| 3 | 把硬切换换成经典概率 DAgger(\(\beta\) 衰减),在同一任务上对比两者的收敛稳定性和最终成功率。 | ⭐⭐⭐⭐ |
89.6 高低层接口:单位、坐标系和频率 ⭐⭐⭐¶
这一节讲最容易导致复现失败的工程细节。
VBC 的高低层接口看似只是一个 goal 向量。
实际包含坐标系、频率、单位、延迟和限幅。
89.6.1 坐标系¶
goal 可以在多种坐标系中表达。
| 坐标系 | 优势 | 风险 |
|---|---|---|
| world | 与物体位姿一致 | 依赖全局定位 |
| base/body | 对基座运动相对稳定 | 基座姿态估计误差会传入 |
| camera | 与视觉直接一致 | 低层控制不方便 |
| task | 适合任务迁移 | 需要定义任务 frame |
常见流程:
变换链:
如果使用世界坐标:
89.6.2 频率¶
| 模块 | 频率 |
|---|---|
| 深度相机 | 10-30 Hz |
| 高层视觉策略 | 5-20 Hz |
| 低层全身策略 | 50-200 Hz |
| 物理仿真 | 200-1000 Hz |
| 电机控制 | 1 kHz 级 |
高层输出不能每个低层周期都变化很大。
应使用保持、插值或低通滤波。
89.6.3 goal 限幅¶
def filter_goal(goal_raw, goal_prev, workspace):
goal = torch.clamp(goal_raw, workspace.low, workspace.high)
max_step = 0.03
delta = torch.clamp(goal - goal_prev, -max_step, max_step)
goal_smooth = goal_prev + delta
return goal_smooth
这个小函数很重要。
它防止视觉误差直接变成低层目标跳变。
89.6.4 接口日志¶
每次实验都应记录:
| 字段 | 作用 |
|---|---|
| raw depth timestamp | 检查延迟 |
| teacher goal | 监督目标 |
| student goal | 学生输出 |
| filtered goal | 低层实际输入 |
| EE position | 执行结果 |
| base pose | 身体状态 |
| success flag | 任务结果 |
⚠️ 常见陷阱¶
⚠️ 编程陷阱:相机外参方向写反
\(T_{base}^{camera}\) 和 \(T_{camera}^{base}\) 互为逆。
写反后目标会镜像或偏移,低层看起来像完全不会操作。
💡 概念误区:高层输出 goal 后低层一定能执行
goal 需要在低层训练分布内。
还要经过限幅、平滑和工作空间检查。
🧠 思维陷阱:频率越高越好
视觉高层频率过高且噪声未滤波,会让低层不断追逐抖动目标。
更稳定的低频目标常更适合全身控制。
练习 89.6¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 写出 camera、base、world 三个 frame 的目标转换链,并用单位测试验证。 | ⭐⭐ |
| 2 | 给学生 goal 加限幅和平滑,对比低层动作变化率。 | ⭐⭐ |
| 3 | 设计接口日志格式,要求能够定位相机延迟和目标跳变。 | ⭐⭐⭐ |
89.7 评估:14 物体 × 3 高度与 retry 行为 ⭐⭐¶
这一节讲 VBC 如何证明视觉泛化。
视觉移动操作不能只在一个目标位置上测试。
必须改变物体形状、高度、位置和遮挡。
89.7.1 泛化维度¶
| 维度 | 示例 |
|---|---|
| 物体类别 | 杯子、盒子、把手、瓶子、工具 |
| 高度 | 地面、中层、高处 |
| 距离 | 近、中、远 |
| 方向 | 正前、侧前、侧方 |
| 遮挡 | 无、部分、强 |
| 背景 | 简单、杂乱 |
| 光照 | 均匀、阴影、反光 |
89.7.2 retry 行为¶
VBC 中一个重要现象是 retry。
如果第一次目标不准,机器人会重新调整身体和末端,再次接近。
这说明高层不是只输出一次开环目标。
它在视觉反馈下闭环修正。
| 没有 retry | 有 retry |
|---|---|
| 一次失败直接结束 | 失败后重新估计目标 |
| 对视觉误差敏感 | 可修正部分误差 |
| 更像开环规划 | 更像视觉伺服 |
89.7.3 评估指标¶
| 指标 | 含义 |
|---|---|
| success rate | 任务最终成功率 |
| first-attempt success | 第一次尝试成功率 |
| retry count | 平均重试次数 |
| time to success | 成功耗时 |
| goal error | 学生目标与教师目标误差 |
| EE tracking error | 低层执行误差 |
| fall rate | 失稳率 |
| collision rate | 碰撞率 |
分层评估方法论:
VBC 系统的评估不能只看最终成功率。必须按层拆分,才能定位瓶颈:
| 评估层 | 方法 | 通过标准 |
|---|---|---|
| 低层单独 | 给定真值 goal,测试跟踪 | EE error < 3 cm,fall rate < 2% |
| 教师单独 | 教师 + 低层,无视觉 | 任务成功率 > 90% |
| 学生单独 | 学生 + 低层,仿真深度 | 成功率 > 80% |
| 端到端 | 学生 + 低层,真实深度 | 成功率 > 60%(首次目标) |
如果教师单独成功率 > 90% 但端到端 < 60%,瓶颈在视觉蒸馏或 Sim2Real。如果教师单独也只有 70%,瓶颈在教师或低层。这种分层评估避免了"全系统调试"的低效做法。
反事实推理:如果不做分层评估,只看端到端成功率会怎样?当成功率从 80% 下降到 60% 时,你无法判断应该改相机随机化、教师奖励、低层 goal 分布还是深度编码器。分层评估把 4 个可能的原因分别量化,让调试时间从数天缩短到数小时。
89.7.4 失败归因¶
| 失败类型 | 证据 |
|---|---|
| 视觉识别错 | student goal 偏离 teacher goal |
| 高层目标不可达 | teacher goal 在低层工作空间外 |
| 低层跟踪失败 | goal 正确但 EE tracking error 大 |
| 基座失稳 | roll/pitch 超阈值 |
| 接口延迟 | depth timestamp 与动作滞后明显 |
| 坐标系错误 | goal 在固定方向系统偏移 |
⚠️ 常见陷阱¶
⚠️ 评价陷阱:只报告总成功率
总成功率不能说明失败来自视觉、高层还是低层。
必须拆分 student goal error、EE tracking error 和 fall rate。
💡 概念误区:retry 行为一定是好事
retry 可以修正误差,但过多 retry 表示高层目标不稳定或低层执行差。
应同时报告平均重试次数和成功耗时。
🧠 思维陷阱:泛化只看物体类别
高度、遮挡、距离和光照同样会造成失败。
泛化评估应覆盖完整维度。
练习 89.7¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 设计 10 物体 × 3 高度的评估表,列出每项指标。 | ⭐ |
| 2 | 给一次失败日志,判断失败来自视觉、高层、低层还是坐标系。 | ⭐⭐⭐ |
| 3 | 设计 retry 限制策略:最多几次、何时放弃、何时移动基座重新观察。 | ⭐⭐⭐ |
89.8 IK 不可达、深度敏感与 Sim2Real 难点 ⭐⭐⭐¶
这一节讲 VBC 的局限。
VBC 虽然把视觉和控制解耦,但仍有很多工程难点。
89.8.1 IK 不可达¶
低层 goal 可能超出工作空间。
即使目标在几何上可达,也可能因为支撑、关节限位或自碰撞不可执行。
| 不可达原因 | 处理 |
|---|---|
| 距离太远 | 移动基座 |
| 高度太高 | 改变机身姿态或放弃 |
| 侧向太大 | 转身或换步态 |
| 关节限位 | 目标投影到可达集 |
| 自碰撞 | 调整 approach 方向 |
89.8.2 深度图敏感¶
真实深度图常出现:
| 问题 | 影响 |
|---|---|
| 反光物体 | 深度空洞 |
| 黑色物体 | 深度缺失 |
| 边缘噪声 | 目标边界抖动 |
| 遮挡 | 物体中心估计偏 |
| 强光 | 传感器失效 |
| 运动模糊 | 延迟和目标漂移 |
训练中需要模拟这些情况。
部署中需要监控深度质量。
89.8.3 相机标定¶
相机外参误差会直接变成 goal 偏差。
如果相机到 base 的平移误差为 3 cm,那么低层目标可能始终偏 3 cm。
这对于抓取任务已经很大。
标定应包含:
- 相机内参。
- 深度尺度。
- 相机到 base 外参。
- 时间同步。
- 运动时外参稳定性。
实操标定流程:
标定不是一次性完成的。四足臂平台振动大,相机安装点可能松动。推荐以下工程流程:
| 步骤 | 工具 | 关键参数 | 验证方法 |
|---|---|---|---|
| 内参标定 | OpenCV 棋盘格标定或 Kalibr | 焦距 \(f_x, f_y\)、主点 \(c_x, c_y\)、畸变系数 | 重投影误差 < 0.5 pixel |
| 深度尺度 | 已知距离平面(如墙面) | 深度比例因子 \(s\) | 实测距离 vs 深度值偏差 < 1% |
| 外参标定 | AprilTag + hand-eye calibration | \(T_{base}^{camera}\) 的 6 个参数 | 多点投影误差 < 5 mm |
| 时间同步 | 硬件触发或 NTP + 手动延迟估计 | 相机到控制的延迟 \(\Delta t\) | 运动中目标位置一致性 |
| 运动稳定性 | 重复标定对比 | 外参变化量 | 多次标定差异 < 2 mm / 0.5° |
常见标定故障与诊断:
| 故障现象 | 可能原因 | 诊断方法 |
|---|---|---|
| 目标系统偏移 3-5 cm | 外参平移错误 | 在静止状态放一个已知位置物体,对比视觉和手动测量 |
| 目标镜像或方向反转 | 坐标系手性不一致或变换逆写反 | 让相机看一个偏右的物体,检查 goal 是否也偏右 |
| 行走时目标漂移但静止时正确 | 时间同步误差或基座震动导致外参变化 | 比较静止和行走时的 EE tracking error |
| 远处误差比近处大 | 内参焦距或深度尺度不准 | 画深度值-真实距离的散点图,检查线性关系 |
工程经验:四足臂平台的标定比桌面机械臂更难。行走时的冲击振动可达 \(\pm 5g\),长期运行后相机支架螺丝可能松动,外参漂移 2-5 mm。建议每次部署前用一个标定板快速验证外参——这 5 分钟的检查可以避免数小时的调试。
89.8.4 Sim2Real 策略¶
| 差距 | 对策 |
|---|---|
| 深度噪声 | 噪声、空洞、模糊随机化 |
| 视觉延迟 | 历史观测和延迟随机化 |
| 物体材质 | 域随机化和真实数据微调 |
| 相机外参 | 标定扰动随机化 |
| 动力学差距 | 低层 DR 和安全过滤 |
| 目标分布 | 真实场景采样或示教数据 |
VBC 的 Sim2Real 比纯足式更难。纯足式的 Sim2Real 主要面对动力学域差距(摩擦、质量、延迟)。VBC 额外面对视觉域差距——这是一个更高维度的问题。
回顾复合/270_SimToReal精读中的域随机化分类:动力学随机化作用于低层策略,视觉随机化作用于深度图学生。两者必须同时进行但分开管理。如果只做动力学随机化,真机深度图的噪声会让学生目标偏差过大。如果只做视觉随机化,真机的电机延迟和摩擦差异会让低层跟踪失败。
Sim2Real 优先级排序:
| 优先级 | 随机化类型 | 影响强度 | 实现难度 |
|---|---|---|---|
| 1(必做) | 深度噪声 + 空洞 | 高 | 低 |
| 2(必做) | 相机延迟(50-150 ms) | 高 | 低 |
| 3(必做) | 动力学参数(摩擦、质量) | 中 | 低 |
| 4(推荐) | 相机外参扰动(\(\pm 1\) cm) | 中 | 中 |
| 5(推荐) | 光照和背景变化 | 中 | 中 |
| 6(可选) | 物体材质和形状微调 | 低 | 高 |
经验表明,前三项覆盖了大约 80% 的 Sim2Real 失败。第 4-6 项是进一步提升真机成功率的精细调整。
89.8.5 安全降级¶
视觉不可靠时不应继续执行强操作。
可设计状态机:
视觉置信度正常
→ 执行 VBC
视觉置信度下降
→ 降低 goal 更新频率,减小动作 scale
目标丢失
→ 保持末端安全姿态,基座停止
姿态异常
→ 切换站立恢复
多次失败
→ 重新观察或请求高层重新规划
⚠️ 常见陷阱¶
⚠️ 工程陷阱:忽略相机和控制的时间同步
深度图对应的是过去的世界。
机器人移动时,100 ms 延迟足以让目标相对位置明显变化。
必须记录时间戳并在训练中加入延迟。
💡 概念误区:IK 不可达只属于传统方法
RL 低层也有工作空间边界。
只是边界不是显式 IK 失败,而是表现为低层跟踪误差和姿态失稳。
🧠 思维陷阱:视觉失败靠更多训练就能解决
标定、照明、材质和遮挡是系统问题。
需要感知质量监控、数据随机化和任务降级共同处理。
练习 89.8¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 为深度图学生设计 8 种视觉随机化,并说明对应真实误差。 | ⭐⭐ |
| 2 | 人为加入 5 cm 相机外参误差,观察 student goal 和 EE tracking 的变化。 | ⭐⭐⭐ |
| 3 | 设计视觉置信度到动作 scale 的降级函数。 | ⭐⭐⭐ |
89.9 与 RoboDuet、UMI-on-Legs 的关系 ⭐⭐¶
这一节把 VBC 放到更大的学习型四足臂谱系中。
89.9.1 与 Deep-WBC¶
Deep-WBC 解决低层全身控制。
VBC 在此基础上增加视觉高层。
| 维度 | Deep-WBC | VBC |
|---|---|---|
| 目标 | 已知 | 视觉估计 |
| 架构 | 更统一 | 更分层 |
| 训练 | 低层为主 | 低层 + 教师 + 学生 |
| 适合 | 目标给定任务 | 视觉目标任务 |
89.9.2 与 RoboDuet¶
RoboDuet 强调双策略协作。
一个策略偏 locomotion,一个策略偏 arm。
二者通过指挥信号协作。
| 维度 | VBC | RoboDuet |
|---|---|---|
| 分解方式 | 视觉高层 + 全身低层 | locomotion 策略 + arm 策略 |
| 优势 | 感知接口清晰 | 跨本体迁移更自然 |
| 风险 | 低层要足够通用 | 双策略协调复杂 |
89.9.3 与 UMI-on-Legs¶
UMI-on-Legs 更强调操作为中心。
高层操作策略产生任务帧末端轨迹,低层全身策略跟踪。
VBC 的 goal 接口与它相通。
区别在于:
| 维度 | VBC | UMI-on-Legs |
|---|---|---|
| 高层来源 | 深度视觉策略 | UMI 数据和扩散策略 |
| 低层目标 | goal-reaching | 未来 EE 轨迹窗口 |
| 重点 | 视觉闭环目标选择 | 操作策略跨平台复用 |
89.9.3b 与零样本基础模型方法的比较 ⭐⭐⭐⭐¶
2024-2025 年,基于 VLM/VLA 的零样本(zero-shot)方法成为移动操作的热门方向。它们是否会取代 VBC 这类训练式方法?
两种范式的核心区别:
| 维度 | VBC(训练式) | 零样本基础模型(如 RT-2、Octo、pi0) |
|---|---|---|
| 目标获取 | 从仿真中训练深度图学生 | 从预训练视觉-语言模型推理 |
| 泛化来源 | 域随机化 + 大量仿真数据 | 互联网规模预训练 |
| 物体泛化 | 需要训练覆盖物体分布 | 理论上可泛化到未见物体 |
| 低层控制 | 独立训练的全身策略 | 通常假设有稳定的低层 |
| 安全保证 | 仿真验证 + 域随机化 | 缺乏形式化安全保证 |
| 推理延迟 | 2-10 ms(小网络) | 100-500 ms(大模型) |
| 部署硬件 | 边缘 GPU 即可 | 需要强力 GPU 或云端 |
| 失败模式 | 视觉域差距导致目标偏差 | 幻觉(hallucination)导致错误动作 |
| 可调试性 | goal 接口可直接检查 | 端到端黑箱,调试困难 |
关键权衡:
零样本方法的优势在于不需要针对每个任务从头训练,能处理开放世界的语义指令。但它们面临三个致命挑战:
- 延迟:大模型推理 100-500 ms,无法满足全身控制 > 20 Hz 的频率要求。必须额外增加低层缓存和插值。
- 幻觉:VLM 可能输出语义合理但物理不可行的目标(如"把杯子放到天花板上")。需要外部可行性检查。
- 安全:缺乏对动力学约束的理解,可能让机器人做出危险动作。必须在下游增加安全滤波。
VBC 的优势在于系统经过端到端仿真验证,低层策略可靠,推理延迟小。但它不能处理训练分布外的物体或任务。
本质洞察:VBC 和零样本方法不是互相替代的关系——它们解决的是不同层次的问题。VBC 解决"如何可靠地执行全身到达动作",零样本方法解决"如何从语义理解中产生目标"。最有前景的方向是将两者结合:用 VLM/VLA 做高层语义目标生成,用 VBC 风格的训练式低层做稳定执行。这正是 VBC 的 goal 接口设计的优势——高层可以被替换为任何目标生成器。
89.9.3c 详细失败模式分析 ⭐⭐⭐¶
VBC 系统的失败不是随机的——它们遵循可预测的模式。系统性地理解失败模式是改进系统的前提。
失败分类体系:
按四个维度穷举 VBC 的失败模式:
1. 感知层失败
| 失败 | 触发条件 | 典型表现 | 频率 |
|---|---|---|---|
| 深度空洞 | 反光/黑色/透明物体 | 学生目标随机跳变 | 高 |
| 遮挡 | 手臂遮挡相机 | 目标消失或偏移 | 高 |
| 运动模糊 | 行走中高速转头 | 目标瞬时偏移 | 中 |
| 光照变化 | 强光/阴影/室外 | 深度图质量下降 | 中 |
| 标定漂移 | 长时间运行后 | 目标系统性偏移 | 低 |
2. 高层策略失败
| 失败 | 触发条件 | 典型表现 | 频率 |
|---|---|---|---|
| 目标多峰 | 同类物体多个 | 目标在物体间跳动 | 中 |
| 分布偏移 | 真实场景与仿真差异大 | 目标持续偏离真实位置 | 高 |
| 阶段错判 | 接触条件判断不准 | 过早抓取或过晚释放 | 中 |
| 时序错误 | 运动过程中物体移动 | 目标追踪滞后 | 低 |
3. 低层执行失败
| 失败 | 触发条件 | 典型表现 | 频率 |
|---|---|---|---|
| IK 不可达 | 目标超出工作空间 | 身体过度倾斜 | 高 |
| 稳定性丧失 | 大幅度伸展 | roll/pitch 超限 | 中 |
| 关节限位 | 极端目标方向 | 动作卡死或抖动 | 中 |
| 自碰撞 | 手臂穿过躯干 | 力矩尖峰 | 低 |
4. 系统层失败
| 失败 | 触发条件 | 典型表现 | 频率 |
|---|---|---|---|
| 延迟积累 | GPU 负载过高 | 控制周期抖动 | 中 |
| 坐标系错误 | 变换链有误 | 目标镜像或偏移 | 首次部署 |
| 频率不匹配 | 高层太慢、低层等待 | 动作不连续 | 中 |
| 通信丢包 | 网络不稳定 | 目标冻结 | 低 |
失败诊断流程:
任务失败
├─ 检查 student goal vs teacher goal
│ ├─ 偏差 > 5 cm → 感知层或高层策略失败
│ │ ├─ 检查深度图质量(invalid pixel ratio)
│ │ ├─ 检查遮挡状态
│ │ └─ 检查标定一致性
│ └─ 偏差 < 5 cm → 低层或系统层失败
│ ├─ 检查 EE tracking error
│ ├─ 检查 base stability(roll/pitch)
│ └─ 检查关节限位和力矩
└─ 检查时间同步
├─ depth timestamp 延迟 > 100 ms → 延迟问题
└─ 延迟 < 100 ms → 非时间问题
89.9.4 技术谱系¶
Deep-WBC
├─ 统一低层全身策略
├─ ROA / Advantage Mixing
↓
Visual-WBC
├─ 低层 goal-reaching
├─ 特权教师
└─ 深度图学生
↓
UMI-on-Legs
├─ 任务帧 EE 轨迹
└─ 示教操作策略迁移
⚠️ 常见陷阱¶
⚠️ 概念误区:这些方法互相替代
它们更像接口逐步上移。
Deep-WBC 管低层,VBC 管视觉目标,UMI-on-Legs 管操作轨迹迁移。
🧠 思维陷阱:只按论文年份排序理解
更重要的是接口差异。
目标点、目标轨迹、任务帧和策略残差代表不同抽象层。
练习 89.9¶
| # | 练习 | 难度 |
|---|---|---|
| 1 | 对比 VBC 和 UMI-on-Legs 的低层接口,说明 goal 和 trajectory window 的差异。 | ⭐⭐ |
| 2 | 设计一个把 VBC 高层替换成 VLA 高层的接口方案。 | ⭐⭐⭐ |
| 3 | 为同一抓取任务选择 Deep-WBC、VBC、RoboDuet 或 UMI-on-Legs,并说明理由。 | ⭐⭐⭐ |
89.9d 🔬 研究视角:VBC 的贡献结构与局限 ⭐⭐⭐⭐¶
读完前面所有节,你已经知道 VBC "做了什么、代码怎么实现"。本节换一个视角——从研究方法论的角度问:这篇论文为什么够发 CoRL Oral?它的贡献结构是什么?哪些局限是它没解决、留给后人的? 这超越了"讲清楚论文说了什么",目标是教你**如何评估一篇系统类论文**。
89.9d.1 贡献结构三维分析¶
顶会论文的贡献通常落在三个维度上,看一篇论文强不强,就看它在这三维上各占了多少。
| 贡献维度 | VBC 的具体贡献 | 强度评估 |
|---|---|---|
| 问题定义 | 把"视觉移动操作"显式定义为可分解的三段(低层 goal-reaching / 特权教师 / 深度学生),并明确了低层 goal 契约的接口语义 | 中——分层非首创,但"球坐标 goal 契约"的清晰化是它的定义贡献 |
| 框架/方法 | 给出完整端到端的三阶段训练框架,且每段都能独立验证;ROA 嵌套在低层、PointNet++ 教师、DAgger 硬切换学生 | 强——首个把三段全部打通并**开源完整实现**的工作 |
| 实验方法论 | sim 33 YCB / 7 类做泛化验证,real 14 物体 × 3 高度 × 5 次做系统评估,且报告了 retry 这种涌现行为 | 强——评估覆盖物体/高度/朝向多维,retry 分析是亮点 |
本质洞察:VBC 的贡献重心在"框架"和"实验"两维,而非"问题定义"。它的真正杀伤力是 "分解 + 开源完整实现"——在它之前,视觉移动操作要么是端到端黑箱、要么各段散落在不同工作里无法复用。VBC 第一次把三段拼成一条可复现的流水线并开源。这解释了为什么它的引用和后续工作(RoboDuet、UMI-on-Legs)大多是"在它的 goal 契约上做替换",而不是"推翻它重来"——一个好的接口定义,比一个高的成功率数字更有生命力。
89.9d.2 为什么这个分解是"对"的分解¶
不是所有分层都值得发顶会。VBC 的分解之所以成立,是因为它切在了**信息可得性的天然断层**上:
- 断层之上(高层):处理"物体在哪、该怎么抓"——这是**视觉/语义**问题,信息来自外部世界,部署时只能由传感器近似。
- 断层之下(低层):处理"全身如何协调到达目标"——这是**动力学**问题,信息来自机器人自身,部署时本体感知完全可得。
把切口放在这个断层上,两段各自的输入可得性是**齐次的**(高层全是"需要从感知推断的外部量",低层全是"自身可测的本体量"),于是各段都能用最适合自己的范式:高层用"特权训练 + 视觉蒸馏"对付感知不可得,低层用"ROA + 域随机化"对付动力学不确定。
对比性思维(反事实):如果切口放错位置会怎样?比如让低层也吃一部分原始深度图、让高层也输出一部分关节扭矩——两段的输入就不再齐次,每段都得同时处理感知和动力学,分层的"可独立验证"优势荡然无存,退化成一个被人为切开但耦合依旧的网络。分层的价值不在于"切",而在于"切在断层上"。
89.9d.3 三个明确局限(串到后续工作)¶
论文解读的研究视角必须诚实地列出局限——它们正是后续工作的动机源头。
| 局限 | 表现 | 谁来解决 |
|---|---|---|
| 目标是单点而非轨迹 | 低层 goal 是一个末端目标点(+底盘速度),无法表达"沿某条轨迹运动"这类需要时序的操作(如擦桌子、画圆) | UMI-on-Legs 把目标升级为任务帧 EE 轨迹窗口 |
| 物体泛化受限于训练分布 | 靠 PointNet++ + 域随机化泛化,但本质仍是"训练覆盖过的物体类别",开放世界未见物体会失败 | VLA/基础模型方向用互联网规模预训练补这块(§89.9.3b) |
| 单网络承载行走与操作的耦合 | 一个学生网络既要管 loco 又要管 mani,两类任务的视觉关注区不同,互相干扰 | RoboDuet 拆成运动学生 + 操作学生两个协作策略 |
这三条局限不是"论文做得不好",而是"一篇论文不可能解决所有问题"。识别它们,等于看清了整个子方向接下来三条演进路线的起点——这正是 §89.1d 技术树上那三个分叉箭头的研究动机来源。
89.9d.4 科研启发¶
| 启发类型 | 内容 |
|---|---|
| 局限→改进 | 低层 goal 是单点 → 能否让低层接口直接吃"短 horizon 目标序列",在不重训低层骨干的前提下扩展到轨迹任务? |
| 假设挑战 | VBC 假设特权教师的决策能被深度图"投影回来" → 对透明/反光物体,深度本身就失效,特权信息根本无法投影,此时是否该换触觉或多模态教师? |
| 跨论文组合 | VBC 的可复用 goal 契约 + VLA 的语义目标生成 → 用 VLA 产生球坐标 goal、用 VBC 低层执行,既得开放世界泛化又得可靠全身控制(§89.9.3b 已论证这是最有前景的融合方向) |
| 代码中的发现 | 仓库里并存的 IK 旁路(§89.3b.4)→ "臂 IK + 腿 RL"的混合控制是否在某些高精度任务上优于全 RL?这是作者试过但没写进论文的方向 |
89.9b 跨章综合练习¶
综合题 1(需要复合/180 Deep-WBC + 本章 + 复合/200 UMI-on-Legs 知识):设计一个"低层 goal-reaching 策略的通用性测试"。要求:用同一个低层策略,分别接入 Deep-WBC 的外部 goal、VBC 的深度图学生 goal、和 UMI-on-Legs 的轨迹窗口 goal,比较三种高层在相同物体抓取任务上的成功率、EE tracking error 和基座稳定性。说明这个测试如何验证"低层策略的接口设计是否足够通用"。
综合题 2(需要足式/190 腿足 RL 训练栈 + 本章知识):VBC 的在线蒸馏使用 DAgger 思想。对比 DAgger 和 DAPG(Demo Augmented Policy Gradient)两种方法在视觉策略蒸馏中的适用性。哪种方法更适合从特权教师蒸馏到深度图学生?为什么?
综合题 3(需要复合/270 SimToReal + 本章知识):列出 VBC 系统从仿真到真机部署时需要验证的 10 个关键检查项,按"标定 -> 低层 -> 教师 -> 学生 -> 端到端"的顺序排列。每个检查项给出通过标准和失败时的修复方向。
89.10 本章小结¶
89.10.1 核心概念表¶
| 概念 | 一句话理解 |
|---|---|
| VBC | 用视觉高层为低层全身策略生成可执行目标 |
| 低层 goal-reaching | 给定目标,全身协调腿和臂到达 |
| 特权教师 | 用仿真真值生成高质量目标 |
| 深度图学生 | 从部署可得视觉中模仿教师 |
| 在线蒸馏 | 让学生在自己访问的状态上继续获得教师标注 |
| retry 行为 | 视觉闭环修正第一次失败 |
| 接口过滤 | 对 goal 做坐标转换、限幅、平滑 |
| 视觉降级 | 深度不可靠时降低动作或停止任务 |
89.10.2 核心公式速查¶
| 公式 | 含义 |
|---|---|
| \(a_t=\pi_L(o_t,g_t)\) | 低层策略 |
| \(g_t^T=\pi_T(s_t^{priv})\) | 特权教师目标 |
| \(g_t^S=\pi_S(D_t,o_t)\) | 深度图学生目标 |
| \(L_{imit}=\|g_t^S-g_t^T\|^2\) | 蒸馏损失 |
| \(r_{goal}=\exp(-\|p_{ee}-p_{goal}\|^2/\sigma^2)\) | 低层目标跟踪 |
| \(p^{base}=T_{base}^{camera}p^{camera}\) | 相机到基座目标转换 |
89.10.3 与后续章节连接¶
| 后续章节 | 本章提供的基础 |
|---|---|
| 复合/200_UMI_on_Legs精读 | 高层操作策略与低层目标接口 |
| 复合/210_RAMBO混合MPC_RL | 安全过滤和 model-based 残差 |
| 复合/140_VLA移动操作 | 视觉/语言高层替换 VBC 高层 |
| 感知 MPC 相关章节 | 深度图、目标估计和任务可行性 |
89.11 歧义审计汇总表 ⭐⭐⭐¶
本表把全文(含 §89.1c 映射表与三个源码走读节)出现的所有 PARTIAL / UNSPECIFIED / CONFLICT 项统一收口。论文解读规范要求:每一项都必须在正文有对应的解读段落——下表"解读位置"列即指向那段分析。读这张表的正确姿势是"反向索引":复现时遇到某个细节拿不准,先在这查它属于哪一类、该信论文还是信代码、去哪一节看完整分析。
| # | 项目 | 级别 | 论文说法 | 代码真相 | 该信谁 | 解读位置 |
|---|---|---|---|---|---|---|
| 1 | 末端目标坐标 | PARTIAL | "position command"(暗示笛卡尔) | 身体系球坐标 \((l,p,y)\),command_mode='sphere' |
代码 | §89.3b.1 |
| 2 | 球坐标帧间插值 | UNSPECIFIED | 未提 | lerp 在球坐标上插值防跳变 |
代码 | §89.3b.1 |
| 3 | arm_induced_pitch |
UNSPECIFIED | 未提 | 目标俯仰受臂姿态影响的补偿项 | 代码 | §89.3b.1 |
| 4 | 末端跟踪奖励度量 | CONFLICT | 指数 L2 核 \(\exp(-\|e\|_2^2/\sigma^2)\) | 指数核内用 L1 距离 | 代码 | §89.3b.2 |
| 5 | 到达稳定 bonus | UNSPECIFIED | 未提 | 误差进阈值后给固定正奖励 | 代码 | §89.3b.2 |
| 6 | ROA 正则调度 | PARTIAL | 给方法名 ROA | priv_reg_coef_schedual=[0,0.1,2000,3000] |
代码补数值 | §89.3b.3 |
| 7 | 部署用哪支编码器 | PARTIAL | 未明说 | hist_encoding=True,用历史自适应模块 |
代码 | §89.3b.3 |
| 8 | 臂关节命令路径 | CONFLICT | 像是 RL 全直出 | RL 直出 与 IK 伪逆旁路并存 | 代码(主线 RL 直出) | §89.3b.4 |
| 9 | 高层动作维度 | SPECIFIED→明确 | "EE position + base velocity"(模糊) | 9D:6D 增量 + 2D 速度 + 1D 夹爪 | 代码明确 | §89.4b.1 |
| 10 | 高层末端动作是增量 | UNSPECIFIED | 未明说 | \(\Delta\) 增量而非绝对位姿 | 代码 | §89.4b.1 |
| 11 | 教师形状特征网络 | PARTIAL | "point cloud feature" | PointNet++,细节在代码 | 代码补细节 | §89.4b.2 |
| 12 | 教师奖励权重 | PARTIAL | Appendix C.2 给结构 | 数值在 reward_vec_task.py |
代码补数值 | §89.4b.2 |
| 13 | 低层装载开关 | UNSPECIFIED | 未提 | hist_encoding=True 必开 |
代码 | §89.4b.3 |
| 14 | 深度图分辨率 | CONFLICT | 未给确切值 | DepthOnlyFCBackbone58x87,输入 58×87 |
代码 | §89.5b.1 |
| 15 | 深度多帧堆叠 | UNSPECIFIED | 未提 | 第一层输入通道 = num_frames |
代码 | §89.5b.1 |
| 16 | DAgger 混合方式 | CONFLICT | "online imitation"(暗示经典 DAgger) | 硬切换:前 pretrain_timesteps=4000 步全教师 |
代码 | §89.5b.2 |
| 17 | replaced_action 标签 |
UNSPECIFIED | 未提 | 可行性修正后的动作可覆盖教师标签 | 代码 | §89.5b.2 |
统计:CONFLICT 4 项(#4、#8、#14、#16)、PARTIAL 6 项、UNSPECIFIED 7 项。其中 4 个 CONFLICT 是复现最易翻车的地方——它们的共同特征是"论文给了一个看似标准的写法,代码却换了实现",这正是 §四论文误导陷阱的高发区。
本质洞察:这张表的密度本身就是一个信号——一篇 CoRL Oral 级别的工作,方法论细节里仍藏着 17 个论文-代码不一致点,其中 4 个会直接导致复现失败。这不是 VBC 写得差,而是**学术论文的篇幅约束与工程实现的复杂度之间存在结构性鸿沟**。论文解读文档的独特价值,就是把这道鸿沟逐项填平——读者拿着这张表,能省下数周自己踩坑的时间。
89.12 复现指南 ⭐⭐⭐¶
本节给出从零跑通 VBC 最小系统的可操作路径。它不替代仓库 README,而是把"按什么顺序做、每步通过标准是什么、踩到哪个坑回查哪一节"串起来——这正是论文解读规范"可复现性"门禁要求的内容。
89.12.1 环境配置¶
# 来源:visual_wholebody 仓库 README(按其说明,版本以仓库为准)
# 1. 创建环境(仓库使用 conda 环境名 b1z1,Python 3.8)
conda create -n b1z1 python=3.8
conda activate b1z1
# 2. 安装 IsaacGym Preview 4(需 NVIDIA 账号下载,VBC 训练依赖它做并行仿真)
# 解压后 cd isaacgym/python && pip install -e .
# 3. 安装仓库的 low-level 与 high-level 两套依赖
# low-level 基于 legged_gym/rsl_rl 风格栈;high-level 含 PointNet++ 与深度编码器
⚠️ IsaacGym Preview 4 对 CUDA / 驱动版本敏感。先单独跑通 IsaacGym 自带 example,再装 VBC——否则后面所有报错都会被归咎到 VBC 代码,浪费排查时间。
89.12.2 运行顺序(必须分阶段)¶
复现 VBC **绝不能**指望一条命令端到端跑出来。按下表分四阶段,每阶段验证通过再进下一阶段——这与 §89.7.3 的分层评估方法论是同一逻辑,只是用在训练侧。
| 阶段 | 做什么 | 通过标准 | 失败回查 |
|---|---|---|---|
| 1 低层 | 训练低层 goal-reaching(球坐标命令 + ROA) | 目标分布内 EE error < 3 cm,fall rate < 2% | §89.3b(球坐标采样、L1 奖励、hist_encoding) |
| 2 教师 | 装载低层 checkpoint,训练高层 9D RL 教师 | 用真值位姿,sim 抓取成功率 > 90% | §89.4b(9D 动作、PointNet++、hist_encoding=True) |
| 3 学生 | DAgger 蒸馏深度学生(58×87,硬切换) | sim 深度成功率 > 80% | §89.5b(分辨率、硬切换、多帧) |
| 4 端到端 | 学生 + 低层,真实/带噪深度 | 首次成功率 > 60% | §89.6(坐标/频率/限幅)、§89.8(Sim2Real) |
89.12.3 预期结果与对照¶
| 指标 | 量级(量级直觉,非精确复刻论文数字) | 对照节 |
|---|---|---|
| sim 教师抓取成功率 | 90%+(7 类 YCB) | §89.7、§89.9d |
| real 端到端首次成功率 | 60%+(14 物体 × 3 高度) | §89.7 |
| 视觉策略推理延迟 | 单帧 2-10 ms(轻量编码器) | §89.5.5b |
| retry 涌现 | 首次失败后能自发重新接近 | §89.7.2 |
89.12.4 常见复现问题(速查)¶
| 问题 | 最可能原因 | 一句话修复 |
|---|---|---|
| 低层训练成功率卡在 60% | 用了笛卡尔目标采样 | 改球坐标 command_mode='sphere'(#1) |
| 末端精修不到位 | 照搬指数 L2 奖励 | 核内改 L1,重标 \(\sigma\)(#4) |
| 高层 reward 异常低 + 低层抖动 | 漏开 hist_encoding |
torch.load 后置 True(#7、#13) |
| 学生输入维度报错 / 部署偏移 | 用了 64×64 而非 58×87 | 按 58×87 裁剪并保持训练-部署一致(#14) |
| 学生训练发散 | 缺 teacher pretrain,过早 student rollout | 加硬切换前置阶段(#16) |
| 真机目标系统性偏移 3-5 cm | 相机外参错 | 单点标定测试(§89.8.3) |
复现哲学:VBC 的复现难度不在任何单个算法,而在"分层系统的接口对齐"。上表每个问题都对应歧义审计表里的一个编号(#N)——这不是巧合,而是说明**复现失败几乎总是踩在论文-代码不一致点上**。把 §89.11 那 17 项逐一核对,比盲目调超参高效得多。
累积项目:Go2+ARX5 / B1+Z1 的 VBC 最小复现¶
目标:用分层方式实现一个视觉目标到全身动作的最小系统。
阶段 1:低层策略¶
- 准备四足臂资产。
- 训练低层 goal-reaching。
- goal 使用 body frame 3D 位置。
- 验证目标采样分布内成功率。
- 记录 EE error 和 base stability。
阶段 2:特权教师¶
- 在仿真中读取物体真值位姿。
- 设计 approach、contact、lift 三阶段 goal。
- 用低层执行教师 goal。
- 过滤不可达目标。
- 记录 teacher success。
阶段 3:深度图学生¶
- 渲染或采集深度图。
- 加入 mask、crop、resize 和 normalize。
- 学生输出 3D goal。
- 用教师 goal 监督。
- 加入在线蒸馏。
阶段 4:评估和降级¶
| 测试 | 指标 |
|---|---|
| 多物体 | success rate |
| 多高度 | height-wise success |
| 遮挡 | student goal error |
| 深度噪声 | robustness |
| 延迟 | time to success |
| 目标丢失 | safe stop |
延伸阅读¶
| 材料 | 类型 | 难度 | 阅读目标 |
|---|---|---|---|
| Liu et al. 2024 Visual Whole-Body Control | 论文 | ⭐⭐⭐⭐ | 本章主轴 |
| visual_wholebody 仓库 | 代码 | ⭐⭐⭐ | high-level / low-level 结构 |
| Fu et al. 2022 Deep-WBC | 论文 | ⭐⭐⭐ | 低层统一策略来源 |
| RoboDuet | 论文/代码 | ⭐⭐⭐ | 双策略协作替代路线 |
| Miki et al. 2022 感知行走 | 论文 | ⭐⭐⭐ | 深度感知与鲁棒腿足控制 |
| UMI-on-Legs | 论文/代码 | ⭐⭐⭐⭐ | 操作轨迹接口 |
| IsaacLab sensor 文档 | 文档 | ⭐⭐ | 深度相机仿真 |
🔧 故障排查手册¶
| 症状 | 可能原因 | 排查步骤 | 相关章节 |
|---|---|---|---|
| 低层无法跟踪 goal | goal 超出工作空间,奖励权重不平衡 | 1. 可达性热力图 2. 缩小目标范围 3. 提高 base 稳定奖励 | 89.3 |
| 教师成功但学生失败 | 深度图无法推断教师使用的信息 | 1. 比较 student goal 和 teacher goal 2. 检查输入可见性 3. 降低教师特权依赖 | 89.4, 89.5 |
| 学生目标抖动 | 深度噪声、无平滑、目标多峰 | 1. 加 goal filter 2. 加深度噪声训练 3. 增加阶段分类 | 89.5, 89.6 |
| 目标方向系统偏差 | 相机外参或坐标转换错误 | 1. 单点标定测试 2. 检查变换逆 3. 打印 camera/base/world goal | 89.6 |
| retry 次数过多 | 高层目标不稳定或低层执行差 | 1. 记录 retry count 2. 分解 goal error 和 EE error 3. 限制重试次数 | 89.7 |
| 高处物体失败率高 | 目标超出可达空间或姿态不稳定 | 1. 按高度统计成功率 2. 调整 goal 投影 3. 允许基座移动 | 89.7, 89.8 |
| 真机深度图失效 | 反光、黑色物体、光照或遮挡 | 1. 检查 invalid pixel ratio 2. 加置信度降级 3. 增加真实数据微调 | 89.8 |
| 行走中视觉目标滞后 | 相机和控制延迟未建模 | 1. 记录时间戳 2. 加延迟随机化 3. 用预测目标 | 89.6, 89.8 |
| 学生离线训练好,在线执行差 | 分布偏移 | 1. 使用在线蒸馏 2. 让学生参与采样 3. 增加失败状态标注 | 89.5 |
综合项目:把 VBC 高层替换成传统感知模块¶
目标:不使用学习高层,先用传统视觉几何产生 goal,验证低层接口是否健康。
项目要求¶
- 深度图生成点云。
- 对目标区域做简单分割。
- 估计目标中心点。
- 转换到 base frame。
- 经过 workspace clamp 和 goal smoothing。
- 输入低层 goal-reaching 策略。
- 记录传统 goal、学生 goal、教师 goal 三者差异。
交付物¶
| 交付 | 要求 |
|---|---|
| 坐标系图 | camera、base、world、goal |
| 代码 | depth 到 goal 的最小 pipeline |
| 对比 | 传统几何 goal 与学生 goal |
| 日志 | 时间戳、置信度、EE error |
| 失败分析 | 至少 5 种视觉失败模式 |
推荐扩展¶
- 将点云目标替换成语义分割输出。
- 将单点 goal 替换成短 horizon 目标序列。
- 将高层输出接入 UMI-on-Legs 式任务帧轨迹。
- 将安全过滤接入 RAMBO 式 model-based 前馈。
Visual WBC 后续工作与 Perceptive MPC 对比¶
后续工作脉络¶
Visual WBC(VBC)的教师-学生蒸馏范式在 2024-2025 年间催生了若干重要的后续工作。RoboDuet(2024)将 VBC 的单学生网络拆分为"运动学生"和"操作学生"两个协作策略,各自负责底盘行走和末端操作,通过共享的 goal 接口协调。这种双策略设计降低了单个网络的学习难度,但引入了策略间同步的新问题。Manipulation Locomotion Transformer(2025)则尝试用 Transformer 替代 MLP 作为学生网络的骨干,利用注意力机制自动学习深度图中不同区域对行走和操作的不同重要性——本质上是让网络自己发现"看哪里",而非人工设计感受野。
与 Perceptive MPC 的对比¶
Visual WBC 和 Perceptive MPC(如 Grandia et al. 2023,见足式/230 章)解决的是同一类问题——"让腿足机器人利用视觉感知来适应复杂地形/执行操作任务"——但方法论路线截然不同。
| 维度 | Visual WBC | Perceptive MPC |
|---|---|---|
| 核心方法 | RL + 教师-学生蒸馏 | 非线性 MPC + 地形约束 |
| 感知集成方式 | 端到端学习(深度图直接输入网络) | 模块化(先建高程图,再作为约束输入优化器) |
| 物理保证 | 无显式保证,依赖训练分布 | 有约束满足保证(在模型准确的前提下) |
| 泛化能力 | 对训练分布内的地形泛化较好 | 对任意地形几何泛化,但依赖高程图质量 |
| 计算需求 | 推理快(网络前向传播,< 1ms) | 推理慢(每步求解 NLP,10-50 ms) |
| 可解释性 | 黑盒 | 可检查约束违反、代价分项 |
核心权衡:Visual WBC 用训练时的大量仿真计算换取了部署时的低延迟和端到端简洁性,但牺牲了可解释性和形式化安全保证。Perceptive MPC 保留了优化问题的结构化信息,可以精确地告诉你"为什么选择了这个落脚点",但需要更高的在线计算资源和更可靠的感知前端。
趋势:2025-2026 年的研究开始探索两者的融合——用 RL 学习一个暖启动策略为 MPC 提供初始猜测,或者用 MPC 的约束结构作为 RL 的奖励信号。RAMBO(复合/210 章)正是这种融合趋势的一个具体实例。
学习路径建议:掌握本章 VBC 的教师-学生蒸馏范式后,建议对照阅读足式/230 章(Perceptive MPC)的 Grandia 论文精读,两者形成"学习派 vs 优化派"的完整对比视角。
—— 本章终 ——