让机械臂听懂“把红色笔放进蓝色文具盒”,第一眼看起来像一道视觉问答题。但当夹爪真正靠近笔,问题马上变得具体:抓取点在哪里,夹爪朝向是否合适,笔抬起来后下一步怎么走,最后又凭什么判断已经放好了?
我在 Franka Panda 的 PyBullet 仿真里尝试搭建这样的链路。Qwen2.5-VL 负责看场景、拆任务和检查结果;π0 通过 openpi 接入动作侧,根据观测和子任务生成动作。两者之间保留 grounding、primitive 控制器和协议校验,让模型输出能够进入一个可调试的执行闭环。
大小脑的分工,落在模型输出上
这里的“大脑”和“小脑”描述的是系统职责。VLM 并不缺少空间理解能力,VLA 也往往继承了视觉语言底座;把它们分层,是为了让语义规划与动作生成各自有清楚的接口。
| 层次 | 输入 | 输出 | 项目中的角色 |
|---|---|---|---|
| VLM | 场景图、语言指令、任务上下文 | 结构化任务步骤、验证判断 | Qwen2.5-VL |
| Grounding | 目标名称、图像与深度 | 目标位置、形状与方向 | 检测器与 RGB-D 几何 |
| VLA | 当前图像、机器人状态、当前子任务 | 连续动作或动作片段 | π0 / openpi 接入 |
| 控制与验证 | 动作、传感器反馈、任务约束 | 关节执行结果、完成或重规划 | primitive 控制器与 verifier |
VLM 在任务阶段切换时处理语义,VLA 在执行过程中反复根据新观测给出动作。这种快慢关系并不等于本项目已经测得某个固定控制频率,电机伺服仍由底层控制器负责。
Qwen2.5-VL:把场景理解变成可执行计划
Qwen2.5-VL 能处理图像与文本,并提供定位和结构化输出能力。项目让它承担 planner 的职责:看见什么、指令涉及哪些对象、要以什么顺序改变场景。
“把所有笔收进盒子”需要展开到具体实例,例如 red_pen 和 blue_pen。如果计划始终只有一个类别名 pen,执行器无法确定该抓哪一个,也无法判断是否漏掉对象。
服务端的 /vlm/plan 因此返回 symbolic primitive。一个子任务的动作链可以写成:
locate_object -> move_to_pregrasp -> grasp -> lift
-> move_to_target -> place -> releaseJSON 是接口,还需要语义检查
下面只展示计划中的一个步骤,不是完整可直接执行的任务:
{
"action": "move_to_target",
"object": "red_pen",
"target": "blue_stationery_box",
"parameters": {
"relation": "inside",
"place_orientation": "align_with_container_long_axis"
}
}JSON repair 可以修复轻微语法问题,Pydantic 检查字段与类型,动作白名单限制可调用的 primitive。再往后,还要核对对象是否存在、对象名是否已经归一化、复合任务是否包含全部子任务,以及空间关系是否符合原指令。
inside 被误写为 left_of 时,JSON 仍然可能完全合法。因此格式通过只是第一道检查。语义不明确或缺少对象时,应该进入确认或重规划,而不是让执行端猜测。
π0 与 OpenVLA,如何把理解连接到动作
VLA 的输出已经进入动作空间。它要处理当前视角、末端状态、夹爪语义和时间上的连续性,不能只给出“向目标移动”这样的语言解释。
π0 在预训练 VLM 之上加入动作专家,通过 flow matching 生成连续动作;OpenVLA 的原始模型则预测离散化的动作 token,再解码为连续控制量。两者都属于 VLA,但动作表示和推理过程不同,接入时需要各自的适配层。
这次系统的主要接入路径是 π0 的 openpi remote policy。OpenVLA 是动作建模和后续适配的参照,不能把两个名称当成可以直接互换的服务实现。
/vla/action 把当前 RGB、robot_state 和 primitive 对应的 subtask prompt 组织成 observation,交给远程 policy,再把结果转换到客户端的动作协议。
RGB_t + robot_state_t + current_subtask
-> observation adapter
-> π0 remote policy
-> action chunk
-> action-space adapter
-> controller动作片段需要反复接收反馈
Action chunk 表示未来一小段动作。客户端可以执行其中的前缀,然后采集新观测并再次请求策略。执行多长的前缀,是延迟、动作连贯性与反馈及时性之间的选择;不能在环境变化后还无条件播完整段动作。
项目的统一接口可表达 ee_delta:末端平移增量、旋转增量和夹爪控制。它是适配后的协议示意,不意味着任意 π0 或 OpenVLA checkpoint 原生都输出同样的七个量。
ee_delta = [dx, dy, dz, droll, dpitch, dyaw, gripper]模型侧的关节动作或末端动作、归一化统计量、坐标系、单位和夹爪开合方向,都必须与执行器逐项对齐。维度相同并不能证明动作含义相同。
Grounding:笔的位置之外,还有它的长轴
VLM 计划中的 red_pen 是一个语义目标。机器人还需要知道它对应哪个图像区域,以及如何转换到世界坐标。
项目中 /vlm/ground 负责检测框、中心像素、形状和二维方向;客户端再根据 RGB-D 做反投影,把相机坐标变换到世界坐标,并通过点云 PCA 估计物体主轴。
object label -> bbox / pixels -> depth back-projection
-> camera coordinates -> world coordinates
-> point-cloud PCA -> object axis对笔、尺子这类长条物体,只取 bbox 中心容易遗漏方向。抓取需要合适的夹爪 yaw,放进盒子还需要考虑容器长轴和可用空间。二维 bbox 的长边只能提供粗略提示,透视、遮挡和背景深度都可能影响它;点云也需要有效深度和合理的目标区域。
检测侧接入了 YOLO-World,同时保留颜色与形状 fallback。PyBullet 的简化材质与真实图像有分布差异,开放词表检测器并不一定稳定识别每支仿真笔。Fallback 有助于联调,也应在日志中标明来源,避免把兜底结果误算成检测模型的能力。
两种 LoRA,学习的是两类映射
LoRA 用少量低秩参数适配冻结的基础权重。在这套系统里,VLM 与 VLA 的适配目标不同,数据也不能混为一谈。
| 适配对象 | 样本粒度 | 学习目标 | 回到系统里看什么 |
|---|---|---|---|
| Qwen2.5-VL | 图像与指令对应一份计划 | 对象命名、动作协议、任务完整性 | 合法率与语义正确率 |
| VLA | 带时间顺序的 observation-action 轨迹 | 视角、动作尺度、夹爪与操作模式 | 闭环 rollout 表现 |
VLM 一侧已经做了监督微调。一份训练记录中,底座约 8.34B 参数,LoRA 可训练参数约 47.6M,占比约 0.57%;一个 epoch 的训练 loss 从约 0.5158 降至约 0.0028。这些是该次训练的记录,不能替代留出场景评测,也不能换算成机械臂成功率。
VLA 一侧仍在轨迹数据采集、LoRA 适配与 rollout 评估阶段。Primitive controller 可以提供示范轨迹,每个时间步要把图像、机器人状态、当前子任务和实际动作对齐保存。发生重试或失败时也保留对应原因,才能分析动作误差如何累积。
我更关心的划分方式是按场景和 episode 留出测试数据,避免同一条轨迹的相邻帧同时出现在训练集与测试集。训练时把目标位置背下来,与在新布局里完成抓取,是两件不同的事。
四个接口,接成一个可回放的闭环
FastAPI 服务端提供四个接口,PyBullet 客户端负责观测采集、几何转换与执行:
| 接口 | 职责 |
|---|---|
/vlm/plan | 指令和场景到 primitive plan |
/vlm/ground | 目标名称到图像区域与几何提示 |
/vla/action | 当前观测和子任务到动作 |
/vlm/verify | 执行后图像与任务记录到验证判断 |
客户端保留 primitive-only 与 VLA-assisted 的执行路径。前者便于确认几何和动作协议,后者逐步引入学习策略。一次 episode 应串起 plan、grounding、action、executed steps 和 verify,而不是只保存最终成功截图。
任务完成,要看世界状态
红色笔进入蓝色文具盒后,/vlm/verify 根据最终图像、原始指令和执行步骤给出判断。仿真端还可以利用几何状态检查 inside、left_of、on_top_of,以及长轴对齐。
这里要区分三层结果:计划是否完整,控制器是否执行了步骤,物体关系是否真的满足目标。VLM 对遮挡或空间关系也会误判,所以仿真几何检查提供了重要的独立参照;迁移到真实场景后,则需要感知与测量替代仿真真值。
评估时,我会分别看计划合法率、复合任务完整率、定位质量、抓取与放置成功率、重规划率及碰撞情况。没有完整评测表时,训练 loss 或几段演示都不足以给出整体成功率。
从单视角仿真继续往前
当前系统主要使用 front RGB。虽然接口里有 wrist image 的 key,该位置仍由 front 图像复制占位,不能当成已经具备腕部视角。真正扩展多视角,还要处理相机标定、时间同步,以及输入分布与模型训练设置的匹配。
这次实践让我最在意的,是模型之间交接时保留了多少可检查的信息:VLM 的目标要能定位,VLA 的动作要有明确物理含义,执行结果要能回到下一次规划。大小脑的价值,就体现在这条闭环能否持续运转,并在失败时指出具体断点。