LeRobot documentation
LingBot-VLA 2.0
LingBot-VLA 2.0
LingBot-VLA 2.0 是 Robbyant 的视觉-语言-动作模型,是 LingBot-VLA 1.0 的后续版本。它采用 Qwen3-VL-4B 作为骨干网络,并搭配一个稀疏的混合专家(Mixture-of-Experts)Qwen2 动作专家(32 个专家,top-4 路由),在跨具身共享的 55 维规范状态/动作空间中,通过流匹配(flow matching)生成动作块(action chunks)。
LeRobot 移植版本是 lingbot_vla_v2 策略。它使用标准的 LeRobot 数据集、处理器、lerobot-train、lerobot-eval 和 lerobot-rollout,包括上游的双查询深度和视频蒸馏。
安装
pip install -e ".[training,lingbot_vla_v2]"在构建处理器时,Qwen3-VL 的分词器和图像处理器会从 Qwen/Qwen3-VL-4B-Instruct(--policy.tokenizer_path)加载。无需 flash-attn;注意力机制使用 PyTorch SDPA。
检查点
| 检查点 | 源代码 | 使用 |
|---|---|---|
lerobot/lingbot_vla_v2_base | robbyant/lingbot-vla-v2-6b,仅格式转换 | 微调的起点。特征为完整的 55 维标准布局;请传入适用于您机器人的槽位映射。 |
lerobot/lingbot_vla_v2_robotwin | robbyant/lingbot-vla-v2-6b-robotwin,第 50000 步 | RoboTwin 2.0 评估。14 维状态/动作,相机 cam_high、cam_left_wrist、cam_right_wrist,QUANTILES(q01/q99)归一化。 |
使用 --policy.path 加载其中任意一个;策略类型将从检查点的 config.json 中读取。
将机器人映射到标准布局
模型接收 55 维状态并预测 55 维动作。布局由配置中的 canonical_joints 设置:
| 槽 | 维度 | 内容 |
|---|---|---|
arm.position | 14 | 手臂关节位置 |
end.position | 14 | 末端执行器位姿 |
effector.position | 2 | 夹爪位置 |
hand.position | 12 | 灵巧手关节 |
waist.position | 4 | 腰部关节 |
head.position | 2 | 头部关节 |
base.velocity | 3 | 移动基础速度 |
reserved.slots | 4 | 保留 |
--policy.state_slots 和 --policy.action_slots 以 JSON 格式将数据集中的 observation.state 和 action 片段映射到这些槽位:
{
"observation.state.arm.position": {
"origin_keys": [{ "observation.state": { "start": 0, "end": 5 } }]
}
}- 槽位名称是上述的规范名称,可选择性地添加
observation.state.或action.前缀。未知名称会引发错误。 - 每个槽位从
observation.state(状态槽位)或action(动作槽位)中读取一个或多个{start, end}跨度。 这些跨度从槽位起始处开始拼接并打包;槽位的其余部分进行零填充,并排除在损失计算之外。 - 在没有槽位映射的情况下,原始特征被视为 55 维的规范布局,与基础检查点相同。
- 相对动作默认处于关闭状态。
--policy.use_relative_actions=true会从动作中减去当前状态,但名称包含--policy.relative_exclude_joints(默认值为["gripper"])中任意条目的维度除外。
后处理器会反转映射,因此 select_action 返回的动作位于机器人的原始动作空间中。
微调后的检查点会将其处理器的槽位映射和数据集统计信息一并保存。
相机
图像步骤会按照检查点中 input_features 列出的顺序读取相机。对于基础检查点,它们分别是 camera_top、camera_wrist_left 和 camera_wrist_right。请使用 --rename_map 将数据集中的相机映射到这些相机上;机器人不具备的视角会被屏蔽。
微调
以 maximellerbach/omx_multicubes 为例,这是一个带有 5 个关节和夹爪的单臂机械臂,配有两个摄像头(top、wrist):
lerobot-train \
--dataset.repo_id=maximellerbach/omx_multicubes \
--policy.path=lerobot/lingbot_vla_v2_base \
--policy.state_slots='{"observation.state.arm.position": {"origin_keys": [{"observation.state": {"start": 0, "end": 5}}]}, "observation.state.effector.position": {"origin_keys": [{"observation.state": {"start": 5, "end": 6}}]}}' \
--policy.action_slots='{"action.arm.position": {"origin_keys": [{"action": {"start": 0, "end": 5}}]}, "action.effector.position": {"origin_keys": [{"action": {"start": 5, "end": 6}}]}}' \
--rename_map='{"observation.images.top": "observation.images.camera_top", "observation.images.wrist": "observation.images.camera_wrist_left"}' \
--policy.dtype=float32 \
--accelerator.mixed_precision=bf16 \
--policy.repo_id=your_repo_id \
--batch_size=1 \
--steps=30000 \
--save_freq=5000 \
--output_dir=outputs/train/lingbot_vla_v2_omx将权重保持在 fp32,并在 bf16 下进行计算(--policy.dtype=float32 --accelerator.mixed_precision=bf16),与上游实现保持一致。如果使用 --policy.dtype=bfloat16,优化器会直接更新 bf16 权重,而大多数更新量过小,无法改变这些权重:在该数据集上,66% 的权重与基础检查点完全一致(逐位相同),且微调后的策略明显更加抖动。推理时也应使用 fp32(以这种方式训练的检查点的默认设置);使用 bf16 进行推理会使动作块(action chunks)的平滑度明显下降。
归一化统计量来自数据集。多 GPU 训练使用标准的 torchrun 启动器,并配合 --parallelism.* 和 --accelerator.* 标志。fp32 权重、梯度和优化器状态大约占用 96 GB,因此当单个 GPU 容量不足时,请使用 --parallelism.dp_shard=<num_gpus> 进行分片。
双查询蒸馏
LingBot-VLA 2.0 在语言标记之后追加了学习到的查询标记(当前及未来的深度/视频),动作专家会对这些标记进行注意力计算。两个检查点都在 align_params 中保留了上游的训练配方,因此在推理时始终会使用这些查询标记。在训练期间,小型头部会读取这些标记,并从冻结的教师模型(LingBot-Depth 搭配 MoGe-2,以及 DINO-Video)中进行蒸馏,损失权重沿用上游的设置;额外的损失会记录为 depth_loss、future_depth_loss、current_video_loss 和 future_video_loss。
教师模型会在第一个训练步骤时下载(约 3 GB,来自 robbyant/lingbot-vla-v2-6b 和 Ruicheng/moge-2-vitb-normal),占用约 2 GB 的 GPU 显存,并为每个步骤的每个样本增加约 130 ms 的开销。
每个样本还会提前加载 chunk_size - 1 步的帧,这是未来目标所需的。推理时不会运行任何额外内容。
关键训练参数
| 参数 | 默认 | 描述 |
|---|---|---|
state_slots / action_slots | null | 原始跨度到规范槽位(见上文)。 |
use_relative_actions | false | 针对相对于当前状态的动作进行训练。 |
loss_type | L1_fm | 速度上的流匹配损失。 |
normalization_mapping | MEAN_STD 状态和动作 | RoboTwin 检查点使用 QUANTILES。 |
train_expert_only | false | 冻结 Qwen3-VL 主干网络,仅训练动作专家。 |
freeze_vision_encoder | false | 冻结视觉塔。 |
gradient_checkpointing | false | 在反向传播中重新计算激活值,以牺牲步长为代价来节省内存。 |
use_moe_expert_lr | true | 路由专家的训练学习率为 lr * sqrt(num_experts / top_k),与上游实现一致。 |
optimizer_lr / scheduler_decay_lr | 1e-5 / 1e-5 | AdamW 峰值学习率和最终学习率。 |
resize_imgs_with_padding | [256, 256] | 在 Qwen3-VL 图像处理器之前,上游的帧大小。 |
image_max_pixels / image_min_pixels | 65536 / 65536 | Qwen3-VL 像素预算:每个摄像头 64 个 token,与上游保持一致。 |
chunk_size / n_action_steps | 50 / 50 | 预测的时间范围以及每个块中执行的动作数量。 |
num_steps | 10 | 推理时的去噪步骤。 |
tokenizer_path | Qwen/Qwen3-VL-4B-Instruct | Qwen3-VL 分词器和图像处理器。离线时使用本地路径。 |
为了适配更小的 GPU,请将 --policy.train_expert_only=true 和 --policy.gradient_checkpointing=true 与较小的 --batch_size 结合使用。
评估
在 LeRobot 的 RoboTwin 环境中评估 RoboTwin 检查点(设置请参阅 RoboTwin)。重命名映射将环境中的相机名称与检查点中的名称对齐:
lerobot-eval \
--policy.path=lerobot/lingbot_vla_v2_robotwin \
--env.type=robotwin \
--env.task=beat_block_hammer \
--eval.batch_size=1 \
--eval.n_episodes=100 \
--rename_map='{"observation.images.head_camera": "observation.images.cam_high", "observation.images.left_camera": "observation.images.cam_left_wrist", "observation.images.right_camera": "observation.images.cam_right_wrist"}'在 50 个 RoboTwin 2.0 任务(每个任务 100 个回合)上的平均成功率,数据引自已发布的 RoboTwin 检查点的上游 README(使用 Muon 在干净数据和随机化数据上进行后训练,使用上游 RoboTwin 启动器进行 FP32 推理)。这些是上游报告的数值,并非通过此移植版本测量得出。
| 设置 | π0.5 | LingBot-VLA 1.0 | LingBot-VLA 2.0 |
|---|---|---|---|
| Clean | 82.74% | 88.56% | 93.52% |
| 随机化 | 76.76% | 86.68% | 92.80% |
推理加速
四个标志位,默认均为关闭状态,可加速 select_action。它们需要 CUDA 和固定的输入形状,且无需额外的依赖项:
--policy.use_cudagraph_denoise=true:将去噪循环捕获为单个 CUDA 图。--policy.use_cudagraph_prefix=true:捕获视觉-语言前缀的 KV 填充。--policy.use_cudagraph_prefix_full=true:同时在 prefix graph 中捕获 vision tower。--policy.compile_predict_velocity=true:对每步的速度预测使用torch.compile。首次调用大约需要一分钟进行编译。
使用 RoboTwin 检查点,在单张 RTX PRO 6000 上执行一个包含 10 个去噪步骤的 50 步动作块所需时间:
| 标志 | bf16 | fp32 |
|---|---|---|
| none | ~756 毫秒 | ~614 毫秒 |
| 所有三个 CUDA 图标志 | ~180 毫秒 | ~203 毫秒 |
| 所有三个 CUDA 图标志 + 编译 | ~120 毫秒 | ~156 毫秒 |
在 bf16 中,CUDA graphs 产生的输出与 eager 模式相同;torch.compile 会略微改变数值。
部署
在机器人上运行微调后的检查点,使用 lerobot-rollout,并传入与训练时相同的 --rename_map:
lerobot-rollout \
--strategy.type=base \
--policy.path=outputs/train/lingbot_vla_v2_omx/checkpoints/last/pretrained_model \
--robot.type=<your_robot_type> \
--robot.port=<port> \
--task="pick up the cube" \
--fps=30 \
--device=cuda实时分块功能可通过 --inference.type=rtc 使用,与其他流匹配策略相同。
引用
@article{lingbotvla2,
title={From Foundation to Application: Improving VLA Models in Practice},
author={Wei Wu and Fangjing Wang and Fan Lu and He Sun and Shi Liu and Yunnan Wang and Yibin Yan and Yong Wang and Shuailei Ma and Xinyang Wang and Yibin Liu and Shuai Yang and Tianxiang Zhou and Kejia Zhang and Lei Zhou and Cheng Su and Nan Xue and Bin Tan and Han Zhang and Youchao Zhang and Fei Liao and Xing Zhu and Yujun Shen and Kecheng Zheng},
journal={arXiv preprint arXiv:2607.06403},
year={2026}
}许可证
代码和转换后的检查点采用 Apache-2.0 许可,与上游的 LingBot-VLA 2.0 和 Qwen3-VL 发布版本一致。
蒸馏训练会下载三个冻结的教师模型,它们各自遵循自己的许可证(推理过程中不会加载它们):
| 教师 | 权重 | 许可证 |
|---|---|---|
| MoGe-2 | Ruicheng/moge-2-vitb-normal | MIT |
| LingBot-Depth | robbyant/lingbot-vla-v2-6b (depth/) | Apache-2.0 |
| DINO-Video | robbyant/lingbot-vla-v2-6b (dino_video/) | DINOv3 许可证 |
DINO-Video 的权重源自 Meta 的 DINOv3,因此使用蒸馏进行训练意味着需在 DINOv3 许可证下使用这些权重。该许可证除其他条款外,还要求您在出版物中注明 DINOv3,并限制某些最终用途。
Update on GitHub