模型: anikitakis/vla_so101_pick_n_place_full_expert — SmolVLA 视觉-语言-动作 (VLA) 策略,基于 SmolVLM2-500M-Video-Instruct 骨干 + 流匹配动作专家(约 0.45B 参数),用于 SO-101 机械臂 "put the annotated object in the box" 任务。 本项目: 将 LeRobot 框架的 SmolVLA 策略通过
torch_npu迁移到昇腾 NPU,提供 FastAPI 服务化推理 (inference.py) 与完整测试用例 (test_client.py)。
| 项目 | 说明 |
|---|---|
| 框架 | LeRobot (PyTorch + HuggingFace Transformers) |
| 骨干 | SmolVLM2-500M-Video-Instruct (Qwen2.5 架构 VLM, 16 层) |
| 动作专家 | 流匹配 (Flow Matching) 去噪器, 0.75× 隐藏宽度, 交叉注意力 |
| 输入 | 3 路相机图像 (480×640) + 6 维本体状态 + 语言指令 |
| 输出 | 6 维动作 chunk (50 步, 反归一化) |
| 训练精度 | FP32 (config.use_amp=false) |
相机命名: 模型训练时使用 annotated / front / target_patch 三个相机(预处理管线中自动映射)。
| 项目 | 要求 | 本机验证值 |
|---|---|---|
| 硬件 | Ascend910 系列 (≥1 卡) | Ascend910 × 16 |
| OS | openEuler / Ubuntu (aarch64) | openEuler 22.03 SP4 (aarch64) |
| CANN | ≥ 8.0 | 25.5.0 |
| Python | 3.8 – 3.11 | 3.11.14 |
| PyTorch | 与 CANN 配套 | 2.9.0+cpu |
| torch_npu | 与 PyTorch 版本一致 | 2.9.0.post1 |
| leRobot | ≥ 0.4.4 (smolvla 支持) | 0.4.4 |
| transformers | ≥ 4.49 | 4.57.6 |
vla_so101_pick_n_place_full_expert-npu/
├── inference.py # 服务化推理脚本 (NPU 适配, FastAPI)
├── test_client.py # 测试用例 (T1 NPU设备 / T2 离线推理 / T3 服务端到端)
├── requirements.txt # 运行环境依赖
├── README.md # 本指南
├── assets/ # 截图 (npu_device_call.png / model_result.png / agent_workflow.png)
├── logs/ # 服务与测试日志
└── vla_so101_pick_n_place_full_expert/ # 模型文件 (权重为符号链接)
├── config.json # 已适配: device="npu:0", 剔除 0.5.2 不兼容字段
├── model.safetensors # 策略权重 (含完整 VLM + 动作专家)
├── policy_preprocessor.json / policy_postprocessor.json # 归一化管线
├── policy_*_normalizer_processor.safetensors # 归一化统计
└── train_config.json权重 (906MB) 以符号链接方式引用原目录;
config.json已解引用并适配(见第 5 节)。
# 1) 初始化 CANN 环境
source /usr/local/Ascend/ascend-toolkit/set_env.sh
# 2) 指定可见 NPU
export ASCEND_RT_VISIBLE_DEVICES=0
# 3) 安装依赖 (推荐华为镜像源)
export PIP_INDEX_URL=https://repo.huaweicloud.com/repository/pypi/simple/
pip install -r requirements.txt
# 4) 离线加载: SmolVLM2-500M-Video-Instruct 配置已本地缓存
export HF_HUB_OFFLINE=1
# 5) 验证 torch_npu 基础可用
python3 -c "import torch, torch_npu; a = torch.randn(3,4).npu(); print(a + a)"
# 期望输出: tensor(..., device='npu:0')原模型为 LeRobot (lerobot 0.5.2) 训练的 CUDA 策略,迁移到昇腾 NPU 的关键点(模型权重零修改):
| # | 适配项 | 原始 (CUDA) | 适配后 (NPU) | 位置 |
|---|---|---|---|---|
| 1 | NPU 后端注入 | — | import torch_npu + transfer_to_npu | 入口脚本首部 |
| 2 | 设备控制 | config.device="cuda" | config.device="npu:0" (leRobot 用其控制设备与 safetensors 加载) | config.json |
| 3 | 设备校验 | leRobot 仅认 cuda/mps/xpu/cpu | monkeypatch is_torch_device_available 扩展 npu | inference.py |
| 4 | VLM 加载 | AutoModelForImageTextToText.from_pretrained (需权重文件) | from_config (VLM 权重已含在 policy safetensors 中, 本地无权重文件) | inference.py |
| 5 | 输入管线 | 数据集输出 CHW float [0,1] Tensor | observation_to_tensors: numpy HWC → CHW float [0,1] (管线仅处理 Tensor) | inference.py |
| 6 | 推理接口 | select_action (单步队列) | predict_action_chunk (整 chunk, 50×6) + 逐动作反归一化 | inference.py |
| 7 | 配置兼容 | lerobot 0.5.2 训练 | 剔除 0.4.4 不识别字段 (push_checkpoints_to_hub/revision) | config.json |
| 8 | HTTP 客户端 | — | test_client 禁用代理 (requests 走系统代理会卡死) | test_client.py |
draccus ParsingError: 0.4.4 的 SmolVLAConfig 不识别 push_checkpoints_to_hub/revision → 剔除字段;Unknown device npu:0: leRobot 硬校验设备类型 → monkeypatch 扩展;checkpoint_files[0].endswith 崩溃: 本地无 SmolVLM2 权重文件(已合并入 policy safetensors)→ from_pretrained 改 from_config;(b,c,h,w) expected / 480 channels: 预处理管线仅处理 torch.Tensor,numpy 被跳过;HWC 需转 CHW → observation_to_tensors;requests 卡死: 环境代理导致本机 HTTP 请求走代理 → 禁用代理。# 启动服务 (默认 npu:0, 端口 8001; 8000 被其他服务占用时调整)
python inference.py --policy_path ./vla_so101_pick_n_place_full_expert --port 8001
# 可选参数
# --no-warmup 跳过预热 (默认开启, 触发算子编译避免首请求超时)服务启动后自动预热(一次 dummy 推理),提供:
| 接口 | 方法 | 说明 |
|---|---|---|
/healthz | GET | 服务健康状态 + 设备信息 |
/act | POST | 3 路图像 + 状态 + 指令 → 50×6 动作 chunk |
/docs | GET | OpenAPI 交互式文档 |
/act 请求示例curl -X POST http://127.0.0.1:8001/act -H "Content-Type: application/json" -d '{
"image1": "<json_numpy 编码的 annotated 相机图 480x640>",
"image2": "<json_numpy 编码的 front 相机图>",
"image3": "<json_numpy 编码的 target_patch 相机图>",
"state": "<json_numpy 编码的 6 维本体状态>",
"task": "put the annotated object in the box"
}'响应: {"action": [[...]×50], "latency_s": 0.33}(50 步 6 维动作 chunk,已反归一化)。
# 仅离线测试 (NPU 设备 + 直接推理, 无需服务)
python test_client.py --offline-only --policy_path ./vla_so101_pick_n_place_full_expert
# 完整测试 (需先启动 inference.py 服务)
python test_client.py --server http://127.0.0.1:8001| 用例 | 验证内容 | 通过标准 |
|---|---|---|
| T1 NPU 设备调用 | torch_npu.npu.is_available()、设备名称、显存、张量落盘 | 张量 device 以 npu: 开头 |
| T2 离线推理 | 策略加载到 NPU + 标准管线 (preprocessor → predict_action_chunk → postprocessor) | 输出 shape=(50, 6),数值有限且幅度合理 |
| T3 服务端到端 | HTTP POST /act(json_numpy 协议) | 服务端 device 为 NPU,返回 action shape=(50, 6) |
测试结束后打印汇总 测试结果: N 通过 / M 失败,有失败时退出码为 1。
本机实际运行结果 (2026-08-19, npu:0, FP32, 0.45B 参数)。3/3 全部通过 ✅(完整日志见
logs/full_test_result.log)
| 用例 | 结果 | 关键数据 |
|---|---|---|
| T1 NPU 设备调用 | ✅ PASS | Ascend910_9382 × 16, 单卡 61.3GB HBM, 张量 device='npu:0' |
| T2 离线推理 | ✅ PASS | action shape=(50, 6), chunk 推理耗时 0.72s |
| T3 服务端到端 | ✅ PASS | HTTP 200, action shape=(50, 6), 响应延迟 0.33s |
实际输出示例:
[T1] NPU 设备调用验证
NPU 可用: True | NPU 名称: Ascend910_9382 | NPU 数量: 16
张量设备: npu:0 | 求和示例: 6.5292
[PASS] T1 NPU 设备调用 device=npu:0
[T2] 离线推理验证 (策略加载到 NPU)
策略设备: npu:0 | dtype: torch.float32
预处理后 batch: state=(1, 6) images=(1, 3, 480, 640)
动作输出 shape: (50, 6) | 耗时: 0.72s
[PASS] T2 离线 NPU 推理 shape=(50, 6) expected=(50, 6) finite=True
[T3] 服务化推理验证 (HTTP -> http://127.0.0.1:8001)
/healthz: {'status': 'ok', 'device': 'npu:0', 'dtype': 'torch.float32'}
HTTP 状态码: 200 | 响应耗时: 0.33s | 返回动作 shape: (50, 6)
[PASS] T3 服务化端到端 action_shape=(50, 6) expected=(50, 6)
测试结果: 3 通过 / 0 失败截图见 assets/ 目录:NPU 设备调用 (npu_device_call.png)、测试结果 (model_result.png)、适配过程 (agent_workflow.png)。
| 问题 | 原因 | 解决 |
|---|---|---|
Unknown device npu:0. Supported: cuda, mps, xpu or cpu | leRobot 0.4.4 设备硬校验 | 本项目 monkeypatch 已扩展 (inference.py apply_npu_patches) |
draccus ParsingError: push_checkpoints_to_hub/revision | 0.4.4 不识别 0.5.2 训练字段 | 本项目 config.json 已剔除 |
checkpoint_files[0].endswith 崩溃 | SmolVLM2 本地无权重文件 | 本项目 patch VLM 加载为 from_config |
(b,c,h,w) expected / 480 channels | 输入格式错误 | 图像需 CHW float [0,1] Tensor (observation_to_tensors) |
| HTTP 请求卡死无响应 | requests 走系统代理 | proxies={"http": None, "https": None} |
| 首个请求耗时数分钟 | 算子首次编译 | 服务预热默认开启 (--warmup) |
Port 8000 already in use | 其他服务占用 | 换端口 (--port 8001) |
📦 本仓库由 ascend-model-agent-plugin (ai4s-basic) 自动迁移适配生成