TurboVLA 复现指南:从环境搭建到 100% 成功率(LIBERO 单套件最小可行方案)

论文:TurboVLA: Real-Time Vision-Language-Action Model at 32 Hz on an RTX 4090 with <1 GB VRAM
代码:https://github.com/H-EmbodVis/TurboVLA
复现环境:RTX 4090、PyTorch 2.3.1 + CUDA 12.1、Python 3.10


摘要

本文记录了 TurboVLA 在 LIBERO 基准上的完整复现过程。与官方"四套件混合、40k 步、4 卡"的论文配方不同,

本文采用最小可行方案:只下载 libero_object 单个套件(约 2.8 GB)、4090显卡上训练 10000 步,最终在 50 次仿真 rollout 中取得 100%(50/50)成功率,其中 9000 步 checkpoint 为 98%(49/50),10000 步即达到满分。

全文重点不在"照抄命令",而在踩坑记录:复现过程中共遇到 9 个典型问题(依赖版本冲突、Gated 模型授权、仿真器渲染、驱动兼容等),每个问题都给出了根因分析和解决方案,并附排查思路,供后续复现者按图索骥。

一、整体流程概览

复现路线图

1.环境

git clone 仓库

conda create python=3.10

torch 2.3.1+cu121

libero

2.数据

openvla/modified_libero_rlds

只下 libero_object 套件

compute_mixed_stats.py

3.权重

官方checkpoint ← HF

DINOv3 ← ModelScope

BERT ← 本地目录

GroundingDINO ← GitHub

4.训练

torchrun 双卡

max_steps=10000

save_steps=5000

5.评估

LIBERO 仿真器

dry_run 自检

5次/任务 冒烟

50/50 全成功

二、方法解析:模型结构与训练思路(论文框架)

本节内容从论文与仓库源码(turbovla/models/、turbovla/training/)交叉还原,帮助理解后面每条命令"为什么这么配"。

2.1 核心思路:抛弃 LLM 中枢的 V+L→A 直接映射

  • 主流 VLA(如 OpenVLA、π0)采用 LLM 中心的 V→L→A 通路:视觉和语言先汇入大语言模型,再由 LLM 输出动作,计算与显存开销大。

  • TurboVLA 的核心主张是视觉和语言各自独立编码,去除LLM,通过轻量交叉注意力直接融合,再由紧凑解码器输出动作块(V+L→A),完全绕开 LLM:

指标TurboVLA说明
参数量0.2B无 LLM,整体小一个数量级
LIBERO 平均成功率97.6%与 LLM 中心的大模型相当
推理延迟31.2 ms(32 Hz)RTX 4090 实测
推理显存0.9 GiB消费级显卡可部署

在这里插入图片描述

V+L→A 设计

视觉流

DINOv3 ViT-B

双视角独立编码

不经过LLM

语言流

BERT base

在线编码

训练时冻结

融合

双向交叉注意力

GroundingDINO初始化

仅6层

动作

ACT式解码器

12步动作块

连续动作直出

2.2 模型结构:一次前向的完整流程

Action Head(ACT 式)

Vision-Language Interaction ×6 层

编码器

输入

双视角图像
agentview + wrist
256×256

语言指令
padding 到 21 token

机器人状态
8 维

DINOv3 ViT-B
(BF16 autocast 计算
全量微调)

BERT base uncased
(冻结)

VisionProjection
LayerNorm+MLP+skip
→ 256 维

text_projection
→ 256 维

BiAttentionBlock
视觉↔文本双向交叉注意力

Text Encoder Layer
文本自注意力

StateProjection
8 维 → 2 个状态 token

TransformerDecoder ×3 层
12 个可学习 action query

MLP + tanh
→ 12×7 动作块

各模块要点(与后文命令参数的对应关系):

模块关键配置训练策略初始化来源
视觉编码器DINOv3 ViT-B,双视角分别编码,patch token + 视角 embedding全量解冻微调,BF16 autocast(权重保持 FP32)facebook/dinov3 预训练
文本编码器BERT base,在线编码(无文本特征缓存),固定 padding 长度 21冻结(freeze_text_encoder=True)google-bert 预训练
交互模块6 层 ×(双向交叉注意力 + 文本自注意力),hidden 256 / inner 1024训练GroundingDINO 的 feature enhancer(--pretrained_init_ckpt 的作用)
动作头状态 8 维→2 token;12 个 action query;3 层 Decoder训练随机初始化

这就解释了为什么训练必须提供 --pretrained_init_ckpt(GroundingDINO 权重):交互模块和文本投影层都从它做迁移初始化,这是 TurboVLA 小数据量下仍能快速收敛的关键之一。

2.3 训练方法:从源码还原的配方

损失函数:对 12 步动作块做 masked L1 loss(masked_l1_loss),padding 步不计入。

优化器(pi0.5 风格):

  • AdamW,betas=(0.9, 0.95)、eps=1e-8,梯度裁剪 1.0;
  • EMA 权重(decay=0.999)随优化器同步维护,checkpoint 中同时保存 model_state_dict 与 ema_model_state_dict,评估用 EMA 权重;
  • 参数分四组:DINOv3(decay / no_decay)与头部(decay / no_decay),学习率均 5e-5,weight_decay 1e-10(LayerNorm、bias 等一维参数不衰减)。

学习率调度:线性 warmup(0.1→1.0)后接余弦衰减;默认 min_lr_ratio=1.0,即 warmup 结束后保持恒定学习率——所以 warmup 步数是这个配方里最重要的调度参数(前文坑 4 的根源)。

精度策略:策略网络参数与优化器状态保持 FP32,仅 DINOv3 前向用 BF16 autocast(--dinov3_precision bf16_autocast),兼顾稳定性与速度。

数据管线:RLDS/TFDS 流式读取;episode 级 shuffle buffer 512;episode 内 step 级打乱;多套件时按 step 级混合(step_mix_buffer_size=64);每次从一条轨迹采 12 步连续动作块作为监督。

论文完整配方(对照本文的测试配方):

项目论文配方本文测试配方
数据四套件混合libero_object 单套件
global batch128(4 卡 × 8 × 4 累积)64(2 卡 × 32 × 1)
训练步数40k(warmup 10k)10k(warmup 1k)
学习率 / seed5e-5 / 42同左(默认)
结果四套件平均 97.6%libero_object 100%(50/50)

三、环境搭建(从零开始)

3.1 克隆仓库

git clone https://github.com/H-EmbodVis/TurboVLA.git
cd TurboVLA

注意:LIBERO 和 RoboTwin 使用不同的仿真器与数据栈,官方建议分开建 Python 3.10 环境。本文只涉及 LIBERO。

3.2 创建 LIBERO 环境

conda create -n turbovla-libero python=3.10 -y
conda activate turbovla-libero

# 先安装与系统 CUDA 匹配的 PyTorch
pip install torch==2.3.1 torchvision==0.18.1 --index-url https://download.pytorch.org/whl/cu121
pip install -e ".[libero]"

LIBERO 仿真器本体需要在同一环境中单独安装(本文在第七部分评估环节安装,见 7.1 节——它的依赖较"脏",建议用到时再装,且用 --no-deps 避免污染训练环境)。

⚠️ 坑 1:huggingface_hub 版本冲突(重点)

现象:按 README 执行 pip install -U huggingface_hub 后,训练启动即崩溃:

ImportError: huggingface-hub>=0.34.0,<1.0 is required ... but found huggingface-hub==1.32.0

根因:README 让你升级 huggingface_hub 到最新(1.x),但 transformers 4.56/4.57 只兼容 <1.0。两个进程都在 import transformers 阶段就死了,与 GPU、torchrun 无关。

解决:降回 0.x 最新版(0.34+ 仍支持 hf CLI,下载功能不受影响):

pip install "huggingface_hub>=0.34,<1"

经验:此后不要再执行 pip install -U huggingface_hub。


四、数据准备:单套件最小方案

4.1 下载数据

export HF_ENDPOINT=https://hf-mirror.com   # 国内镜像,可选

hf download openvla/modified_libero_rlds --repo-type dataset \
  --include "libero_object_no_noops/*" \
  --local-dir data/libero

验证完整性:目录下应有 dataset_info.json、features.json 和 32 个 .tfrecord 分片(共 34 个文件)。

⚠️ 坑 2:镜像下载末尾限速

现象:进度 97% 后速度从 MB/s 掉到 200 KB/s。

解决:hf download 支持断点续传,Ctrl+C 后原命令重跑即可;顽固情况用多线程:

pip install hf_transfer
export HF_HUB_ENABLE_HF_TRANSFER=1
# 重跑下载命令

4.2 计算单套件归一化统计(重点)

为什么不能直接用官方 libero_all4_stats.json:其中的 libero_all4_no_noops 是四套件混合的统计量,单套件训练直接复用会导致动作归一化/反归一化不一致,输出全错。必须用仓库自带脚本重算:

python scripts/libero/compute_mixed_stats.py \
  --dataset_dir data/libero/libero_object_no_noops/1.0.0 \
  --output experiments/libero/configs/libero_object_stats.json \
  --stats_key libero_object_no_noops

验证基准:正常输出 454 条 episode、66984 帧动作,episode 平均长度约 147.5。

日志中 TensorFlow 的 cuDNN / “Skipping registering GPU devices” 警告是预期行为——脚本主动禁用了 TF 的 GPU,TF 只负责读 TFRecord,GPU 计算全走 PyTorch。


五、模型权重准备

四份权重

官方预训练checkpoint

先评估后训练的关键

含归一化元数据

DINOv3 ViT-B

视觉编码器

HF Gated仓库 ⚠️

改从ModelScope下载

BERT base

语言编码器

评估时必须本地目录 ⚠️

GroundingDINO

交互模块初始化

GitHub release直链

5.1 官方预训练 checkpoint(建议先下)

模型权重与基准数据属于外部资产,不随仓库发布,需从 Hugging Face 单独下载完整发布包(含模型权重和归一化元数据):

hf download H-EmbodVis/TurboVLA \
  --local-dir pretrained/TurboVLA

为什么建议先下它:拿到官方 checkpoint 后,可以在不下载任何训练数据的情况下先跑评估(见第七部分),零成本验证环境是否正确——这就是"先评估后训练"策略的基础。

⚠️ 注意:官方 README 此处给出的命令是 pip install -U huggingface_hub && hf download ...,不要照抄第一行——它会把 huggingface_hub 升到 1.x,直接引发坑 1 的版本冲突。当前环境(0.34+)的 hf download 完全可用。如果已经误执行,重新 pip install "huggingface_hub>=0.34,<1" 即可。

5.2 DINOv3:⚠️ 坑 3——Gated 仓库,403 Forbidden(重点)

现象:

403 Forbidden: Please enable access to public gated repositories in your fine-grained token settings
Cannot access content at: https://hf-mirror.com/facebook/dinov3-vitb16-pretrain-lvd1689m/...

根因:facebook/dinov3-* 需要先在 HF 网页登录并同意协议,且 token 需具备 gated 仓库访问权限(fine-grained token 默认不勾选)。

解决(推荐,免授权):从 ModelScope 官方镜像下载到本地,约 330 MB:

pip install modelscope
modelscope download --model facebook/dinov3-vitb16-pretrain-lvd1689m \
  --local_dir pretrained/dinov3-vitb16

训练参数相应改为 --dinov3_path pretrained/dinov3-vitb16(代码用 AutoModel.from_pretrained 加载,本地目录即可)。

备选方案:HF 网页同意协议 + 创建 Read 类型 token + hf login。

5.3 其余两份权重

# GroundingDINO 初始化权重(训练必需)
mkdir -p pretrained/groundingdino
wget -O pretrained/groundingdino/groundingdino_swint_ogc.pth \
  https://github.com/IDEA-Research/GroundingDINO/releases/download/v0.1.0-alpha/groundingdino_swint_ogc.pth

# BERT:下载到本地目录(评估脚本强制要求本地路径)
hf download google-bert/bert-base-uncased --local-dir pretrained/bert-base-uncased

六、训练

6.1 启动命令(双卡、10000 步)

torchrun --nproc_per_node=2 experiments/libero/train.py \
  --dataset_dir data/libero/libero_object_no_noops/1.0.0 \
  --dataset_dirs "data/libero/libero_object_no_noops/1.0.0" \
  --stats_path experiments/libero/configs/libero_object_stats.json \
  --stats_key libero_object_no_noops \
  --dinov3_path pretrained/dinov3-vitb16 \
  --bert_path google-bert/bert-base-uncased \
  --allow_hf_download \
  --pretrained_init_ckpt pretrained/groundingdino/groundingdino_swint_ogc.pth \
  --batch_size 32 \
  --max_steps 10000 \
  --warmup_steps 1000 \
  --save_steps 5000 \
  --checkpoint_dir outputs/libero_object_smoke

⚠️ 坑 4:max_steps 与 warmup_steps 的约束

现象:

ValueError: lr_schedule_steps=10000 must be greater than warmup_steps=10000

根因:--max_steps 会同步设为学习率调度总步数,而 warmup 默认沿用论文值 10000,两者相等触发校验。

解决:warmup 取总步数 5~10%,如 10000 步配 --warmup_steps 1000。

6.2 训练效率与工程细节(重点)

项目默认值说明
--max_steps80000论文配方;单套件冒烟 10000 足够
--save_steps1000每 1000 步存一次;单 checkpoint 约 3 GB(含优化器+EMA),冒烟建议调大到 5000
--resume_modenone支持断点续训:--resume_mode all 自动从 checkpoint_dir 最新权重恢复
--num_workers4数据加载线程,已够用
--batch_size824 GB 显存只用 6 GB,提到 32 吞吐显著提升

实测双卡 batch 32、2.05 it/s,10000 步约 1.5 小时。checkpoint 内含 ema_model_state_dict(EMA 权重),评估时自动优先使用。

训练日志:

training:  10%|█████████▋                                                                                       | 1000/10000 [12:27<1:54:14,  1.31it/s, loss=0.09440, avg=0.11275, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_1000.pth
training:  20%|███████████████████▍                                                                             | 2000/10000 [24:54<1:39:39,  1.34it/s, loss=0.09160, avg=0.09025, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_2000.pth
training:  30%|█████████████████████████████                                                                    | 3000/10000 [37:19<1:26:18,  1.35it/s, loss=0.09752, avg=0.08808, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_3000.pth
training:  40%|██████████████████████████████████████▊                                                          | 4000/10000 [49:45<1:14:37,  1.34it/s, loss=0.09131, avg=0.08005, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_4000.pth
training:  50%|███████████████████████████████████████████████▌                                               | 5000/10000 [1:02:10<1:02:36,  1.33it/s, loss=0.06846, avg=0.07372, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_5000.pth
training:  60%|██████████████████████████████████████████████████████████▏                                      | 6000/10000 [1:14:36<49:42,  1.34it/s, loss=0.05579, avg=0.06795, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_6000.pth
training:  70%|███████████████████████████████████████████████████████████████████▉                             | 7000/10000 [1:27:01<37:16,  1.34it/s, loss=0.05440, avg=0.05951, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_7000.pth
training:  80%|█████████████████████████████████████████████████████████████████████████████▌                   | 8000/10000 [1:39:27<24:53,  1.34it/s, loss=0.05592, avg=0.05342, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_8000.pth
training:  90%|███████████████████████████████████████████████████████████████████████████████████████▎         | 9000/10000 [1:51:52<12:26,  1.34it/s, loss=0.04837, avg=0.04656, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_9000.pth
training: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 10000/10000 [2:04:18<00:00,  1.32it/s, loss=0.04338, avg=0.04533, lr=5.00e-05]saved: outputs/libero_object_smoke/turbovla_step_10000.pth
training: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 10000/10000 [2:04:19<00:00,  1.34it/s, loss=0.04338, avg=0.04533, lr=5.00e-05]

七、评估验证(踩坑密集区,重点)

评估五连坑

坑5 BERT路径

必须本地目录

不接受HF仓库名

坑6 libero模块

editable安装失效

PYTHONPATH兜底

坑7 渲染平台

PYOPENGL_PLATFORM=egl

MUJOCO_GL=egl

坑8 缺依赖

matplotlib等

缺啥装啥

坑9 mujoco版本

robosuite1.4.1

只兼容mujoco2.3.x ⚠️

7.1 安装 LIBERO 仿真器

cd ~/lgp_dev/project
git clone https://github.com/Lifelong-Robot-Learning/LIBERO.git
pip install -e LIBERO --no-deps   # 避免依赖污染主环境

⚠️ 坑 5:评估要求 BERT 为本地目录

现象:FileNotFoundError: local BERT directory not found: google-bert/bert-base-uncased

解决:训练脚本支持 HF 仓库名,但评估脚本强制本地路径,改为 --bert_path pretrained/bert-base-uncased。

⚠️ 坑 6:libero 模块安装后仍 import 失败

现象:pip install -e LIBERO 显示成功,import libero 却报 ModuleNotFoundError(editable wheel 仅 7 KB,包发现失效)。

解决:PYTHONPATH 兜底,并写入 conda 环境变量持久化:

export PYTHONPATH=$HOME/lgp_dev/project/LIBERO:$PYTHONPATH
conda env config vars set PYTHONPATH=$HOME/lgp_dev/project/LIBERO -n turbovla-libero

首次 import libero 会交互式询问数据集目录,输入 N 使用默认路径(生成 ~/.libero/config.yaml 后不再询问)。

⚠️ 坑 7:无显示器服务器的渲染平台

现象:ImportError: Cannot use EGL rendering platform. The PYOPENGL_PLATFORM environment variable is set to ...

解决:robosuite 要求两个变量同时设置:

export MUJOCO_GL=egl
export PYOPENGL_PLATFORM=egl
conda env config vars set MUJOCO_GL=egl PYOPENGL_PLATFORM=egl -n turbovla-libero

⚠️ 坑 8:逐个补齐依赖

--no-deps 安装的代价:matplotlib、transforms3d、scipy、imageio 等缺什么装什么:

pip install matplotlib transforms3d scipy imageio imageio-ffmpeg

⚠️ 坑 9:mujoco 3.x 与 robosuite 1.4.1 不兼容(重点)

现象:环境创建成功但 reset 时崩溃:

File ".../robosuite/utils/binding_utils.py", line 521, in get_joint_qpos_addr
    assert joint_type in (mujoco.mjtJoint.mjJNT_HINGE, mujoco.mjtJoint.mjJNT_SLIDE)
AssertionError

根因:pip 自动装了 mujoco 3.x,而 LIBERO 配套的 robosuite 1.4.1 只兼容 mujoco 2.3.x。

解决:

pip install mujoco==2.3.2
# 若随后出现 numpy 2.x 相关的 AttributeError(np.float_ 等),再降:
pip install numpy==1.26.4

每次动完依赖都确认一下:python -c "import torch; print(torch.__version__, torch.cuda.is_available())",期望 2.3.1+cu121 True。

7.2 评估三步走

第 1 步:干跑自检(不起仿真,只验证 checkpoint 严格加载):

python experiments/libero/evaluate.py \
  --ckpt_path outputs/libero_object_smoke/turbovla_step_10000.pth \
  --dinov3_path pretrained/dinov3-vitb16 \
  --bert_path pretrained/bert-base-uncased \
  --stats_path experiments/libero/configs/libero_object_stats.json \
  --stats_key libero_object_no_noops \
  --task_suite_name libero_object \
  --dry_run_model_load true

通过标志:strict checkpoint load ... (672 tensors)。

第 2 步:小规模冒烟(每任务 5 次,约 2 分钟):

python experiments/libero/evaluate.py \
  --ckpt_path outputs/libero_object_smoke/turbovla_step_10000.pth \
  --dinov3_path pretrained/dinov3-vitb16 \
  --bert_path pretrained/bert-base-uncased \
  --stats_path experiments/libero/configs/libero_object_stats.json \
  --stats_key libero_object_no_noops \
  --task_suite_name libero_object \
  --num_trials_per_task 5 \
  --chunk_size 12 \
  --num_open_loop_steps 12 \
  --seed 7 \
  --precision bf16 \
  --result_json_path outputs/evaluation/libero_object_smoke.json \
  --save_video true \
  --video_out_path outputs/evaluation

第 3 步:正式评估:--num_trials_per_task 50(论文标准)。

7.3 评估参数速查

参数作用
--task_suite_name切换套件:libero_object / libero_spatial / libero_goal / libero_10 / libero_90
--task_ids "0,3,5"只跑套件内指定任务(每套件 10 个,编号 0~9)
--max_tasks 3只跑前 N 个任务
--save_video true保存 rollout 视频(默认关闭!)

视频输出位置:outputs/evaluation/<checkpoint名>/<套件名>/taskXX_epYYY_success|failure.mp4,主视角 + 手腕相机左右拼接,20 fps。失败样本的视频务必人工查看,可快速区分抓取滑脱、目标误识别、轨迹跑偏等失败模式。

示例效果1:
在这里插入图片描述
示例效果2:
在这里插入图片描述

八、复现结果

配置数值
训练数据libero_object 单套件(454 episodes / 66984 帧)
训练步数10000 步(warmup 1000)
硬件2 × RTX 4090,batch 32/卡,约 1.5 小时
评估10 任务 × 5 trials = 50 次 rollout,seed 7
最终成功率100%(50/50),10 个任务全部 5/5

补充对比:中间 checkpoint turbovla_step_9000.pth 为 98%(49/50),最终 step_10000 达到 100%,成功率随步数单调上升,训练趋势健康。

100% 成功率

10个任务

全部 5/5 成功

步数对比

9000步 49/50

10000步 50/50

结论

数据-stats-训练-评估

全链路验证通过

单套件10000步≈论文水平

单套件 10000 步即达到论文四套件 40k 步在 libero_object 上的成绩水平,说明:单套件最小方案足以验证全链路正确性;若要复现论文完整指标(四套件平均 97.6%),再下载其余三套件、按官方配方(global batch 128、40k 步、10k warmup)训练即可。


九、问题速查表

#阶段报错关键词根因可行解决
1训练启动huggingface-hub>=0.34.0,<1.0 is requiredhub 1.x 与 transformers 冲突pip install "huggingface_hub>=0.34,<1"
2数据下载末尾 200 KB/s镜像限速Ctrl+C 重跑续传 / 开 hf_transfer
3模型加载403 Forbidden ... gated repositoriesDINOv3 需授权ModelScope 下载到本地
4训练启动lr_schedule_steps must be greater than warmup_stepswarmup 默认 10000显式设 --warmup_steps
5评估加载local BERT directory not found评估要求本地路径改 pretrained/bert-base-uncased
6评估启动No module named 'libero'editable 安装失效export PYTHONPATH=.../LIBERO
7环境创建Cannot use EGL rendering platform渲染变量未设export PYOPENGL_PLATFORM=egl
8环境创建No module named 'matplotlib'–no-deps 后遗症缺啥装啥
9环境 resetget_joint_qpos_addr AssertionErrormujoco 3.x 不兼容pip install mujoco==2.3.2

十、总结与建议

  1. 先评估后训练:评估不需要数据集,拿到官方 checkpoint 后先用 --dry_run_model_load + 小规模 rollout 验证环境,成本为零。
  2. 单套件是最佳冒烟路径:2.8 GB 数据 + 1.5 小时训练即可验证全链路,且能达到接近论文的成功率。
  3. 统计文件必须与训练数据匹配:训练用单套件就要重算 stats;评估时 stats 又要与所选 checkpoint 的训练数据一致,这是最容易被忽视却最致命的一点。
  4. 仿真器环境的版本锁定:robosuite==1.4.1 + mujoco==2.3.2 + EGL 双环境变量 是 LIBERO 评估的稳定组合。
  5. 善用录像:--save_video true 的失败样本视频是诊断策略行为的第一手材料。

分享完成~

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐