让机械臂听懂“把红色笔放进蓝色文具盒”,第一眼看起来像一道视觉问答题。但当夹爪真正靠近笔,问题马上变得具体:抓取点在哪里,夹爪朝向是否合适,笔抬起来后下一步怎么走,最后又凭什么判断已经放好了?

我在 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。一个子任务的动作链可以写成:

text
locate_object -> move_to_pregrasp -> grasp -> lift
              -> move_to_target -> place -> release

JSON 是接口,还需要语义检查

下面只展示计划中的一个步骤,不是完整可直接执行的任务:

json
{
  "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,再把结果转换到客户端的动作协议。

text
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 原生都输出同样的七个量。

text
ee_delta = [dx, dy, dz, droll, dpitch, dyaw, gripper]

模型侧的关节动作或末端动作、归一化统计量、坐标系、单位和夹爪开合方向,都必须与执行器逐项对齐。维度相同并不能证明动作含义相同。

Grounding:笔的位置之外,还有它的长轴

VLM 计划中的 red_pen 是一个语义目标。机器人还需要知道它对应哪个图像区域,以及如何转换到世界坐标。

项目中 /vlm/ground 负责检测框、中心像素、形状和二维方向;客户端再根据 RGB-D 做反投影,把相机坐标变换到世界坐标,并通过点云 PCA 估计物体主轴。

text
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 的动作要有明确物理含义,执行结果要能回到下一次规划。大小脑的价值,就体现在这条闭环能否持续运转,并在失败时指出具体断点。