X-VLA-WidowX 是一个基于 Soft-Prompted Transformer 的跨本体 Vision-Language-Action (VLA) 策略模型,参数量约 0.9B。该模型由清华大学 2toINF 团队提出,论文发表于 Zheng et al., 2025, "X-VLA: Soft-Prompted Transformer as Scalable Cross-Embodiment Vision-Language-Action Model" (arXiv:2510.10274)。
| 组件 | 角色 | 参数 |
|---|---|---|
| Florence-2 编码器 | 视觉-语言表征骨干(encoder-only) | 232M |
| SoftPromptedTransformer | Flow-matching 动作去噪器(24层 Transformer) | 700M |
| Action Hub (ee6d) | 动作空间定义(20维 ee6d 动作空间,含 xyz + 6D 旋转 + 夹爪) | — |
/act 接口,支持真实机器人评估| 组件 | 规格 |
|---|---|
| NPU | Ascend 910B(Atlas 800T A2)× 2 |
| NPU 显存 | 64 GB HBM × 2 |
| CPU | 鲲鹏 64 核 aarch64 |
| 内存 | 512 GB |
| 组件 | 版本 | 说明 |
|---|---|---|
| CANN | 8.5.1 | /usr/local/Ascend/cann-8.5.1 |
| Python | 3.11.14 | 系统 Python |
| torch | 2.9.0 (CPU build) | 从昇腾社区安装,配合 torch_npu 使用 |
| torch-npu | 2.9.0.post1+gitee7ba04 | 与 torch 2.9.0 配对 |
| transformers | 4.57.6 | XVLA 远端代码 / XVLAProcessor / Florence2 依赖 |
| vllm-ascend | — | 此模型不使用(非自回归架构)1 |
| timm | 1.0.15 | modeling_florence2.py 依赖(DropPath / trunc_normal_) |
| einops | 0.8.2 | modeling_florence2.py 依赖(rearrange) |
| json-numpy | 2.1.1 | FastAPI /act 请求体解码 |
| Pillow | 12.2.0 | 图像加载/合成 |
| opencv-python-headless | 4.11.0.86 | FastAPI 图像解码 |
| fastapi | 0.123.10 | —serve 模式服务 |
| uvicorn | 0.46.0 | FastAPI 服务容器 |
cd /opt/atomgit/model_adapt/X-VLA-WidowX-NPU
python3 -m venv --system-site-packages venv
--system-site-packages继承系统级 torch / torch-npu / transformers / vllm 等基础包,避免重复安装。
cd /opt/atomgit/model_adapt/X-VLA-WidowX-NPU
# 激活虚拟环境
source venv/bin/activate
# 使用清华镜像安装依赖
pip install -r requirements.txt \
-i https://pypi.tuna.tsinghua.edu.cn/simple
# 或使用阿里云镜像
pip install -r requirements.txt \
-i https://mirrors.aliyun.com/pypi/simple/python -c "import torch; import torch_npu; print(f'torch={torch.__version__}, npu_avail={torch.npu.is_available()}')"
# 预期输出: torch=2.9.0, npu_avail=Truecd /opt/atomgit/model_adapt/X-VLA-WidowX-NPU
./venv/bin/python inference.py \
--model /data/models/2toINF/X-VLA-WidowX \
--device npu:0 \
--task "Pick up the red block and place it in the green bin." \
--domain 0 --steps 10 --seed 42 \
--output-dir output./venv/bin/python inference.py --help| 参数 | 默认值 | 说明 |
|---|---|---|
--model | /data/models/2toINF/X-VLA-WidowX | 模型权重目录 |
--device | npu:0 | 昇腾 NPU 设备 |
--dtype | bf16 | 推理精度(bf16/fp16/fp32) |
--task | "Pick up the red block..." | 语言指令 |
--state | 0,...,0 (20 维) | 本体状态 ee6d |
--domain | 0 | 域标识(0~29) |
--steps | 10 | flow-matching 去噪步数 |
--seed | 42 | 随机种子 |
--views | 3 | 合成图像视角数 |
--image | "" | 真实图像路径(逗号分隔) |
--output-dir | output | 输出目录 |
--serve | — | 启动 FastAPI 服务 |
./venv/bin/python inference.py \
--model /data/models/2toINF/X-VLA-WidowX \
--image /path/cam0.jpg,/path/cam1.jpg,/path/cam2.jpg \
--task "Move the blue cube to the right side." \
--domain 5 --steps 10 --seed 7 \
--output-dir output_test_realimg./venv/bin/python inference.py \
--model /data/models/2toINF/X-VLA-WidowX \
--serve --host 0.0.0.0 --port 8000服务启动后,客户端可通过 POST /act 接口发送推理请求(详见模型官方 client_widowx.py)。
./venv/bin/python run_test_cases.py \
--model /data/models/2toINF/X-VLA-WidowX \
--device npu:0 \
--output-dir output
# 运行指定子集
./venv/bin/python run_test_cases.py \
--model /data/models/2toINF/X-VLA-WidowX \
--cases 1,2,3 \
--output-dir output本模型适配昇腾 NPU 过程中解决的 6 个关键问题:234
| # | 问题 | 根因 | 解决方案 |
|---|---|---|---|
| 1 | 推理引擎选择 | Florence-2 非自回归架构,vLLM-ascend 不支持 | 使用 torch_npu 直接加载权重推理 |
| 2 | _attn_implementation 校验 | transformers 4.57.x 对远端代码校验误判 | 在 XVLAConfig 与 florence_config 上显式设置 _attn_implementation="eager" |
| 3 | DaViT Conv2d dtype 不匹配 | 处理器输出 float32 图像,模型权重为 bf16 | 图像输入显式转为 bf16 |
| 4 | encoder.embed_tokens 解绑 | transformers 加载时权重独立出来 | 将 encoder.embed_tokens 重绑到 shared |
| 5 | tie_word_embeddings 崩溃 | WidowX 远端代码缺失 tie 禁用 | 加载时设置 tie_word_embeddings=False |
| 6 | 模型权重强制 float32 | 远端代码 __init__ 中 .to(torch.float32) | 加载后显式 .to(dtype=bf16) |
共 50 组 测试用例,覆盖 domain_id 0~29 全部 30 个域、8 组语言指令、4 组本体状态、3 组图像配置、3 组去噪步数、2 组随机种子。全部在 Ascend 910B NPU 上通过验证。
| # | 名称 | Domain | 任务 | mean_abs | std | 夹爪(首步) | 参数 | 耗时 | 状态 |
|---|---|---|---|---|---|---|---|---|---|
| 01 | domain_00 | 0 | Pick up the red block and place it in the green bin. | 0.1742 | 0.3231 | [0.875, 0.0] | s=42,v=3,st=10 | 0.595s | ✅ |
| 02 | domain_01 | 1 | Pick up the red block and place it in the green bin. | 0.2805 | 0.3201 | [0.461, 0.527] | s=42,v=3,st=10 | 0.134s | ✅ |
| 03 | domain_02 | 2 | Pick up the red block and place it in the green bin. | 0.2575 | 0.3043 | [0.410, 0.594] | s=42,v=3,st=10 | 0.131s | ✅ |
| 04 | domain_03 | 3 | Pick up the red block and place it in the green bin. | 0.2161 | 0.2725 | [0.480, 0.371] | s=42,v=3,st=10 | 0.131s | ✅ |
| 05 | domain_04 | 4 | Pick up the red block and place it in the green bin. | 0.1874 | 0.2412 | [0.416, 0.490] | s=42,v=3,st=10 | 0.132s | ✅ |
| 06 | domain_05 | 5 | Pick up the red block and place it in the green bin. | 0.2096 | 0.2653 | [0.508, 0.469] | s=42,v=3,st=10 | 0.133s | ✅ |
| 07 | domain_06 | 6 | Pick up the red block and place it in the green bin. | 0.2419 | 0.2701 | [0.539, 0.457] | s=42,v=3,st=10 | 0.131s | ✅ |
| 08 | domain_07 | 7 | Pick up the red block and place it in the green bin. | 0.2461 | 0.2890 | [0.477, 0.590] | s=42,v=3,st=10 | 0.131s | ✅ |
| 09 | domain_08 | 8 | Pick up the red block and place it in the green bin. | 0.1744 | 0.1995 | [0.471, 0.523] | s=42,v=3,st=10 | 0.133s | ✅ |
| 10 | domain_09 | 9 | Pick up the red block and place it in the green bin. | 0.1762 | 0.2278 | [0.504, 0.516] | s=42,v=3,st=10 | 0.133s | ✅ |
| 11 | domain_10 | 10 | Pick up the red block and place it in the green bin. | 0.2856 | 0.3258 | [0.543, 0.555] | s=42,v=3,st=10 | 0.132s | ✅ |
| 12 | domain_11 | 11 | Pick up the red block and place it in the green bin. | 0.2191 | 0.2606 | [0.520, 0.563] | s=42,v=3,st=10 | 0.133s | ✅ |
| 13 | domain_12 | 12 | Pick up the red block and place it in the green bin. | 0.2143 | 0.2629 | [0.477, 0.590] | s=42,v=3,st=10 | 0.133s | ✅ |
| 14 | domain_13 | 13 | Pick up the red block and place it in the green bin. | 0.2398 | 0.2693 | [0.484, 0.504] | s=42,v=3,st=10 | 0.132s | ✅ |
| 15 | domain_14 | 14 | Pick up the red block and place it in the green bin. | 0.1909 | 0.2464 | [0.570, 0.621] | s=42,v=3,st=10 | 0.131s | ✅ |
| 16 | domain_15 | 15 | Pick up the red block and place it in the green bin. | 0.2060 | 0.2386 | [0.516, 0.570] | s=42,v=3,st=10 | 0.131s | ✅ |
| 17 | domain_16 | 16 | Pick up the red block and place it in the green bin. | 0.1865 | 0.2408 | [0.494, 0.430] | s=42,v=3,st=10 | 0.130s | ✅ |
| 18 | domain_17 | 17 | Pick up the red block and place it in the green bin. | 0.2478 | 0.2475 | [0.621, 0.422] | s=42,v=3,st=10 | 0.130s | ✅ |
| 19 | domain_18 | 18 | Pick up the red block and place it in the green bin. | 0.2173 | 0.2611 | [0.496, 0.441] | s=42,v=3,st=10 | 0.131s | ✅ |
| 20 | domain_19 | 19 | Pick up the red block and place it in the green bin. | 0.2440 | 0.2889 | [0.531, 0.477] | s=42,v=3,st=10 | 0.130s | ✅ |
| 21 | domain_20 | 20 | Pick up the red block and place it in the green bin. | 0.2161 | 0.2548 | [0.543, 0.455] | s=42,v=3,st=10 | 0.132s | ✅ |
| 22 | domain_21 | 21 | Pick up the red block and place it in the green bin. | 0.2444 | 0.2737 | [0.566, 0.436] | s=42,v=3,st=10 | 0.131s | ✅ |
| 23 | domain_22 | 22 | Pick up the red block and place it in the green bin. | 0.2281 | 0.2723 | [0.395, 0.482] | s=42,v=3,st=10 | 0.130s | ✅ |
| 24 | domain_23 | 23 | Pick up the red block and place it in the green bin. | 0.1820 | 0.2419 | [0.459, 0.543] | s=42,v=3,st=10 | 0.132s | ✅ |
| 25 | domain_24 | 24 | Pick up the red block and place it in the green bin. | 0.2235 | 0.2560 | [0.455, 0.531] | s=42,v=3,st=10 | 0.132s | ✅ |
| 26 | domain_25 | 25 | Pick up the red block and place it in the green bin. | 0.2674 | 0.3166 | [0.551, 0.516] | s=42,v=3,st=10 | 0.133s | ✅ |
| 27 | domain_26 | 26 | Pick up the red block and place it in the green bin. | 0.2488 | 0.2901 | [0.436, 0.590] | s=42,v=3,st=10 | 0.131s | ✅ |
| 28 | domain_27 | 27 | Pick up the red block and place it in the green bin. | 0.2215 | 0.2756 | [0.471, 0.586] | s=42,v=3,st=10 | 0.135s | ✅ |
| 29 | domain_28 | 28 | Pick up the red block and place it in the green bin. | 0.2412 | 0.2847 | [0.523, 0.555] | s=42,v=3,st=10 | 0.132s | ✅ |
| 30 | domain_29 | 29 | Pick up the red block and place it in the green bin. | 0.2168 | 0.2547 | [0.531, 0.410] | s=42,v=3,st=10 | 0.129s | ✅ |
| 31 | task_01 | 0 | Pick up the red block and place it in the green bin. | 0.1742 | 0.3231 | [0.875, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 32 | task_02 | 0 | Move the blue cube to the right side. | 0.1623 | 0.3082 | [0.887, 0.0] | s=42,v=3,st=10 | 0.130s | ✅ |
| 33 | task_03 | 0 | Place the mug on the saucer. | 0.1768 | 0.3247 | [0.918, 0.0] | s=42,v=3,st=10 | 0.132s | ✅ |
| 34 | task_04 | 0 | Push the yellow button. | 0.1681 | 0.3122 | [0.941, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 35 | task_05 | 0 | Open the drawer and take out the spoon. | 0.1678 | 0.2945 | [0.672, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 36 | task_06 | 0 | Tuck in the chair. | 0.1782 | 0.3281 | [0.949, 0.0] | s=42,v=3,st=10 | 0.133s | ✅ |
| 37 | task_07 | 0 | Knock over the cup. | 0.1707 | 0.3307 | [0.922, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 38 | task_08 | 0 | Wipe the whiteboard. | 0.1664 | 0.3262 | [0.930, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 39 | state_01 | 0 | Pick up the red block and place it in the green bin. | 0.1742 | 0.3231 | [0.875, 0.0] | s=42,v=3,st=10 | 0.131s | ✅ |
| 40 | state_02 | 0 | Pick up the red block and place it in the green bin. | 0.1861 | 0.3477 | [0.256, 0.0] | s=42,v=3,st=10 | 0.132s | ✅ |
| 41 | state_03 | 0 | Pick up the red block and place it in the green bin. | 0.1929 | 0.3306 | [0.949, 0.0] | s=42,v=3,st=10 | 0.132s | ✅ |
| 42 | state_04 | 0 | Pick up the red block and place it in the green bin. | 0.1885 | 0.3372 | [0.047, 0.0] | s=42,v=3,st=10 | 0.132s | ✅ |
| 43 | img_01 | 0 | Pick up the red block and place it in the green bin. | 0.1675 | 0.3173 | [0.824, 0.0] | s=42,v=1,st=10 | 0.131s | ✅ |
| 44 | img_02 | 0 | Pick up the red block and place it in the green bin. | 0.1733 | 0.3235 | [0.863, 0.0] | s=42,v=2,st=10 | 0.142s | ✅ |
| 45 | img_03 | 0 | Pick up the red block and place it in the green bin. | 0.1758 | 0.3217 | [0.879, 0.0] | s=42,v=3,st=10 | 0.133s | ✅ |
| 46 | steps_05 | 0 | Pick up the red block and place it in the green bin. | 0.1584 | 0.3030 | [0.863, 0.0] | s=42,v=3,st=5 | 0.085s | ✅ |
| 47 | steps_10 | 0 | Pick up the red block and place it in the green bin. | 0.1742 | 0.3231 | [0.875, 0.0] | s=42,v=3,st=10 | 0.132s | ✅ |
| 48 | steps_20 | 0 | Pick up the red block and place it in the green bin. | 0.1853 | 0.3318 | [0.875, 0.0] | s=42,v=3,st=20 | 0.226s | ✅ |
| 49 | seed_000 | 0 | Pick up the red block and place it in the green bin. | 0.1681 | 0.3203 | [0.945, 0.0] | s=0,v=3,st=10 | 0.132s | ✅ |
| 50 | seed_123 | 0 | Pick up the red block and place it in the green bin. | 0.1880 | 0.3200 | [0.926, 0.0] | s=123,v=3,st=10 | 0.132s | ✅ |
参数列说明:
s=seed,v=图像视角数,st=去噪步数。 状态列:✅ = 无 NaN,推理正常。
验证 30 个不同 soft prompt(对应 30 个机器人本体域)在相同视觉/指令输入下的动作分布差异。各域输出均无 NaN,动作 mean_abs 分布在 0.17~0.29 之间,表明各域 soft prompt 激活了不同的动作先验。
验证模型对不同语言指令的语义理解能力。涵盖抓取、放置、推、拉、擦、翻倒等 8 种典型机器人操作指令,所有指令均产生有效动作输出。
验证模型对不同初始本体状态的响应能力。从零状态到不同偏移量(±0.1~0.6),模型均能根据初始状态调整动作输出。
验证模型对 1~3 视角图像输入的处理能力。单视角(v=1)到三视角(v=3)均正常工作,动作分布保持稳定。
验证 flow-matching 不同去噪步数的影响。5 步推理最快(0.085s),20 步最平滑但耗时约 2.6 倍。步数越多,动作 mean_abs 略高,表明去噪更充分。
验证不同随机种子对动作生成的影响。同一输入下不同种子产生不同的动作分布(符合预期,flow-matching 初始噪声随机)。
使用真实相机图像(2 视角)推理:
./venv/bin/python inference.py \
--model /data/models/2toINF/X-VLA-WidowX \
--image /path/cam0.jpg,/path/cam1.jpg \
--task "Move the blue cube to the right side." \
--domain 5 --steps 10 --seed 7 \
--output-dir output_test_realimg| 项目 | 值 |
|---|---|
| 引擎 | torch_npu |
| 设备 | npu:0 |
| 图像视角 | 2 |
| 任务 | Move the blue cube to the right side. |
| Domain | 5 |
| 动作 shape | (1, 30, 20) |
| 动作 mean_abs | 0.2106 |
| 动作 std | 0.2644 |
| 首步夹爪(gripper) | [0.508, 0.471] |
| 推理耗时 | 0.59s |
| 状态 | ✅ 无 NaN |
X-VLA-WidowX-NPU/
├── README.md # 本部署文档
├── inference.py # 昇腾 NPU 推理脚本(torch_npu 引擎)
├── run_test_cases.py # 批量测试用例生成脚本
├── requirements.txt # 依赖清单及版本对照表
├── assets/ # 资源目录
│ └── .gitkeep # 占位文件
├── venv/ # Python 虚拟环境
├── output/ # 测试用例输出
│ ├── testcase_01/ ~ testcase_50/
│ │ ├── action.npy # 动作输出 (1, 30, 20) float32
│ │ └── report.json # 推理报告
│ └── testcases_summary.json # 50 组测试汇总
└── output_test_realimg/ # 真实图像推理输出
├── action.npy
└── report.json_attn_implementation 相关错误原因:transformers 4.57.x 在模型构造时自动执行 _check_and_adjust_attn_implementation,对本地 Florence2 远端代码误判。
解决:在 inference.py 中已自动处理,确保 config._attn_implementation = "eager" 和 config.florence_config._attn_implementation = "eager"。
AttributeError: '...' object has no attribute 'lm_head'原因:X-VLA-WidowX 远端代码与 X-VLA-Pt 不同,未在 modeling_xvla.py 中禁用 Florence2 的 tie_weights 钩子。tie_word_embeddings=True 时,from_pretrained 末尾会调用 tie_weights() 访问已删除的 lm_head。
解决:加载时设置 config.tie_word_embeddings = False(已在 inference.py 中自动处理)。
原因:DaViT 卷积输入/权重 dtype 不匹配(处理器输出 float32 图像,模型权重为 bf16)。
解决:在预处理中将 image_input 转为与模型同 dtype(已在 inference.py 中自动处理)。
原因:X-VLA-WidowX 输出为连续动作(flow-matching 去噪),而非自回归文本 token。vLLM-ascend 和 sglang 面向自回归大语言模型,Florence-2 架构也不在 vllm-ascend 支持列表中。因此使用 torch_npu 直接加载权重完成推理。1
@article{zheng2025x,
title = {X-VLA: Soft-Prompted Transformer as Scalable Cross-Embodiment Vision-Language-Action Model},
author = {Zheng, Jinliang and Li, Jianxiong and Wang, Zhihao and Liu, Dongxiu and Kang, Xirui
and Feng, Yuchun and Zheng, Yinan and Zou, Jiayin and Chen, Yilun and Zeng, Jia and others},
journal = {arXiv preprint arXiv:2510.10274},
year = {2025}
}贡献者: z_studio | 赛道: 模型适配赛道
X-VLA-WidowX 输出为连续动作(flow-matching 去噪),而非自回归文本 token,因此使用 torch_npu 推理引擎,不经过 vLLM-ascend / sglang。Florence-2 架构不在 vllm-ascend 官方支持列表中,且 vLLM 面向 token 级自回归生成,与 VLA 策略模型的连续动作输出不兼容。 ↩ ↩2 ↩3
transformers 4.57.x 在模型构造时调用 _check_and_adjust_attn_implementation,对本地 Florence2 远端代码会误判。通过在顶层 XVLAConfig 与嵌套 florence_config 上显式设置 _attn_implementation="eager" 绕过此校验。 ↩
X-VLA 将 Florence2 的 tie_weights 钩子置空,且 transformers 4.57 加载时会把 _tied_weights_keys 中的 encoder.embed_tokens.weight 独立出来(保持随机初始化),导致该模块与 shared 解绑。XVLA 推理路径通过 get_input_embeddings()(=shared)计算 inputs_embeds 并直接传入 encoder,因此该随机模块本不参与计算;此处仍显式重绑以保证完全正确。 ↩
X-VLA-WidowX 的远端代码与 X-VLA-Pt 不同,未在 modeling_xvla.py 中禁用 Florence2 的 tie_weights 钩子,而 XVLA.__init__ 已删除 lm.model.decoder / lm.lm_head。若保持默认 tie_word_embeddings=True,transformers 在 from_pretrained 末尾调用 tie_weights() 时会访问已删除的 lm_head 而抛 AttributeError。加载时显式关闭 tie_word_embeddings 可绕过此问题。 ↩