把机器人训练从 GPU 搬到昇腾 NPU:启元 Q1 起身动作 PPO 训练全记录

作者:叶毅翔(天津商业大学 · GewisLab)
平台:AtomGit CANNLab 云端昇腾 NPU 环境
关键词:启元 PrimeBot Q1 / 昇腾 Ascend 910 / torch_npu / PPO / MuJoCo / 具身智能

博客文档由 GewisLab leader lhwLHWjackl的个人 a小lo爱吃棒棒糖 的博客代发,博客撰写叶毅翔同学

在真实的华为昇腾 NPU 上,用"CPU 物理仿真 + NPU 策略训练"的异构架构,完整跑通了启元 Q1 人形机器人起身(getUp)动作的 3000 轮 PPO 训练:

指标 训练开始 训练结束(第 3000 轮)
单步奖励 mean_rew -1.54 2.24
回合回报 ep_ret -53.5 153.7
回合长度 ep_len 21 68
吞吐 sps ~2.1 万 ~2.77 万

全程 44 分钟,占用 NPU 0.74 卡时,稳态吞吐 2.8 万 env-step/s,与官方 GPU 训练结果(约 2.5 万 step/s)吻合。


一、背景:为什么要把训练搬到 NPU

启元 PrimeBot Q1 是一款面向个人与家庭场景的人形机器人,高约 88 cm、重约 16 kg、含 22 个自由度,售价约 19999 元起。它开放二次开发接口,硬件为双板结构:运动控制板用瑞芯微芯片,大脑板用 NVIDIA Jetson Orin;软件层基于 ROS2 + FastDDS。

问题在于:机器人的强化学习策略原本是绑死在 NVIDIA CUDA 生态上的,而团队手上只有昇腾 910 系列算力。于是有了这次迁移任务——把 PPO 策略网络的训练从 GPU 搬到昇腾 NPU。

先说两个重要的结论性判断:

  • 昇腾缺的不是算力,而是物理仿真的软件生态。 MuJoCo 的 Warp 后端没有 NPU 版本,所以物理仿真必须留在 CPU。
  • SuperMimic 类项目跑不了昇腾。 全项目无 torch_npu,设备逻辑只接受 cpu/cuda,底层 warp-lang 是 CUDA-only。最终选用已完成昇腾适配的 q1-task-bench(q1_ascend)实现同一目标。

二、整体架构:CPU 仿真 + NPU 策略

整体是一套异构分工架构:CPU 侧跑物理仿真,NPU 侧跑神经网络,两侧每个训练步只做两次数据搬运。

在这里插入图片描述

架构要点:

  • 左侧(CPU):原生 MuJoCo 物理仿真,以多进程(spawn)方式并行跑 1024 个环境;Warp 无 NPU 后端,仿真固定在 CPU。
  • 右侧(NPU):Actor-Critic 策略网络 + PPO 优化,前向推理输出 22 维动作,反向传播算梯度,全部张量计算由 torch_npu 驱动。
  • 每步两次拷贝:观测 obs(119 维)从 CPU 送到 NPU(H2D),动作 act(22 维)从 NPU 送回 CPU(D2H)。
  • 设备抽象层:devices.py 按 PRIMEBOT_DEVICE > CUDA_VISIBLE_DEVICES > ASCEND_RT_VISIBLE_DEVICES > 回退 cpu 的优先级自动解析,业务代码无需改动;在没有 NPU 的机器上自动回退 CPU,保证同一份代码在任何环境都能跑。

一句话:仿真交给擅长得多的 CPU,策略交给算力更强的 NPU,中间只搬两次数据。


三、运行环境与版本矩阵

本次验证在 CANNLab 提供的 A3 规格 NPU 实例上完成:

项目 实测值
实例规格 ASCEND 1×NPU,40 vCPUs,240 GiB 内存
硬件型号 Ascend910_9362(64 GB HBM)
操作系统 Linux(aarch64)
Python 3.11.4
PyTorch 2.7.1+cpu
torch_npu 2.7.1.post4
MuJoCo 3.17.0
CANN 9.1.0 系列镜像
设备可见数 torch.npu.device_count() = 2

版本兼容说明:PyTorch、torch_npu、CANN 三者需保持同系列严格对应。本次镜像的 torch_npu 为 post4,与版本矩阵文档中的 post10 同属 2.7.1 系列,小规模与完整训练均正常,证明同系列兼容。


四、先验证 NPU 真的可用

启动训练前,先确认 NPU 不是"看得见"而是"能算":

import torch
import torch_npu

print(torch.npu.is_available())      # True
print(torch.npu.device_count())      # 2
print(torch.npu.get_device_name(0))  # Ascend910_9362

同时用 npu-smi 确认硬件健康、HBM 占用正常、无异常进程。然后在 NPU 上跑一个矩阵乘法并 synchronize,验证算子计算与同步链路通畅。

运行中出现的 Permission mismatch UserWarning 与 NPUCachingAllocator padding size 提示均为无害告警,不影响训练结果。


五、核心:3000 轮起身动作训练

5.1 训练配置

参数 值
num_envs 1024
num_workers 32(1024 能被 32 整除)
max_iterations 3000
steps_per_env 24
device npu:0
观测维度 OBS_DIM 119
动作维度 act_dim 22

5.2 训练曲线

下图为真实 train.csv 关键节点采样,双 Y 轴分别展示回合回报与单步奖励:

在这里插入图片描述

可以清楚看到两个阶段的上升:前 500 轮快速拉起来(机器人从"躺平"到能发力),中段进入平台期打磨细节,最后 500 轮再次冲刺,回合回报从 83 冲到 153.7——对应的是从"勉强撑起"到"稳定站住"的过程。

5.3 起点与终点对比

指标 第 1 步 第 3000 步
单步奖励 mean_rew -1.54 2.24
回合回报 ep_ret -53.5 153.7
回合长度 ep_len 21 68
吞吐 sps 约 2.1 万 约 2.77 万

回合长度从 21 步涨到 68 步是最直观的信号:早期机器人撑不起来、动作早早结束,后期能完整跟完整个起身序列。

5.4 资源与耗时

  • 总耗时约 44 分钟,NPU 占用约 0.74 卡时。
  • 稳态吞吐约 2.8 万 env-step/s,与仓库官方 GPU 结果(约 2.5 万 step/s)吻合,反向验证了昇腾适配的正确性。
  • 训练每 50 步自动存档,CSV 实时 flush,中断也不丢数据。

5.5 训练产物

运行目录 logs/getup-full/2026-11-06_11-56-50/:

文件 说明
args.yaml 完整训练配置
train.csv 3000 行指标 + 9 项奖励分项
model_3000.pt 最终收敛策略权重
model_best.pt 最优策略权重

5.6 起身动作演示

通过 MuJoCo 离屏渲染对 350 帧起身参考动作逐帧回放,合成 640×480、50fps、时长 7 秒的 H.264 视频,完整呈现机器人从平躺、蜷缩翻身、半蹲撑起到完全站立的过程:

q1-getup-mujoco

视频文件为 video/q1-getup-mujoco.mp4,由 diagram-src/render_getup_video.py 离屏渲染生成。


六、协作流程:Fork → PR

所有代码改动通过个人 fork + Pull Request 提交,保证主干稳定、改动可评审:

在这里插入图片描述

本次实际提交:

  • 个人 fork:https://atomgit.com/gcw_Gc5ycp3R/q1-task-bench
  • 分支:fix/vec-env-obs-env-obs-dim
  • PR #1:修复 vec_env 测试中观测维度硬编码(122)的问题,统一使用 OBS_DIM(实际 119)。无冲突,等待团队审核。

几个容易被忽略的工程细节:

  • Git 身份使用 UTF-8 无 BOM 配置,避免中文用户名乱码;
  • 凭据通过令牌(PAT)写入凭据管理器,不在命令行暴露敏感信息;
  • 对外提交遵循"先 issue 后 PR"的团队约定。

七、如何训练一个新动作

跑通 getUp 之后,真正有价值的沉淀是:训练任意新动作时,无需改动训练脚本与 PPO 配置,只需换一个动作文件。

在这里插入图片描述

六个阶段:

阶段 核心动作 产出物
1 资产准备 把动作做成合格的 .npz 并回放校验 50fps npz
2 环境配置 设置 Q1_XML、Q1_MOTION,确认 NPU 可用 环境就绪
3 冒烟验证 小规模跑通,确认 device 是 npu:0 [DONE]
4 完整训练 放大规模训练到策略收敛 model + train.csv
5 评估部署 evaluate 定量评估,sim2sim 回放,导出 ONNX eval.json + ONNX
6 沉淀协作 fork → PR,写文档,打包归档 可复用资产

核心认知:新动作"新"在一个参考动作文件(.npz)。训练"起身"就让 Q1_MOTION 指向 q1_getUp.npz,训练新动作就指向新 npz——训练代码一行不用改。


八、踩坑记录

这部分对复现的人最有价值:

  1. spawn RuntimeError:多进程环境下主脚本缺少 if __name__ == "__main__" 保护,子进程重新导入时会递归创建进程。
  2. 长命令被终端拆分:复杂的内联 python -c 在 xterm 中易被截断,应写成独立 .py 再用短命令运行。
  3. 中文路径报错:MuJoCo C 库无法直接打开中文路径,需先切换到模型目录再用英文文件名。
  4. SuperMimic 不支持 NPU:仅支持 cpu/cuda,不能用于昇腾训练。
  5. torch.logcumsumexp 走 CPU fallback:个别算子在 NPU 上回退到 CPU,属正常现象。
  6. NPU 奖励算子的 H2D/D2H 开销:环境规模较小时自定义 NPU 奖励算子未必更快,默认用 CPU 奖励,大规模环境才体现优势。
  7. num_envs 必须能被 num_workers 整除,否则启动即报错。
  8. 指标出现 NaN/Inf:先查动作数据是否有异常帧、学习率是否过大,不要带着 NaN 继续训练。
  9. NPU 相关 UserWarning(padding size、owner 提示等)多为镜像或分配器的无害提示。

九、复现指南

在 CANNLab WebIDE 终端中:

# 1. 设置模型与起身动作资产
export Q1_XML=/home/developer/primebot_rl_mjlab/src/assets/robots/q1_v3/xml/q1_v3_mc_without_scene.xml
export Q1_MOTION=/home/developer/primebot_rl_mjlab/src/assets/motions/q1/q1_getUp.npz

# 2. 进入项目目录
cd /mnt/workspace/q1-task-bench

# 3a. 小规模验证(约 1 分钟)
python3 scripts/run_getup_npu.py

# 3b. 完整训练(约 44 分钟,建议后台运行)
nohup python3 scripts/run_getup_full.py > full_train.log 2>&1 &

换动作只需把 Q1_MOTION 指向新的 .npz。


十、总结

  • 在真实昇腾 NPU(Ascend910)上完整跑通了 Q1 起身动作的 PPO 训练,策略收敛,指标与官方 GPU 结果吻合。
  • 验证了"CPU 物理仿真 + NPU 策略训练"异构架构的可行性与性能:44 分钟、0.74 卡时、2.8 万 env-step/s。
  • 沉淀出"资产准备 → NPU 验证 → 完整训练 → 评估部署 → fork/PR 协作"的标准流程,可直接复用于后续任意新动作。

昇腾适配的真正门槛不在算力,而在软件生态的补齐——把 MuJoCo、PPO、设备抽象一层层对到 torch_npu 上。这一步走通之后,剩下的就是换 npz 文件而已。


附录:奖励函数与观测维度

奖励函数共 9 项:

类别 项目 说明
跟踪类 root_pos、root_ori、body_pos、body_ori、body_lin_vel、body_ang_vel 指数形式,衡量与参考动作的贴合度
约束类 action_rate(-0.1) 惩罚动作剧烈变化
约束类 joint_limit(-10) 惩罚关节越限
约束类 self_collision(-10) 惩罚自碰撞

观测维度 OBS_DIM = 119,由以下部分组成:

  • 参考关节位置 22 + 参考关节速度 22
  • 参考锚点重力 3 + 投影重力 3 + 基座角速度 3
  • 当前关节位置(相对)22 + 关节速度 22 + 上一动作 22

动作维度 act_dim = 22。模型实测 nq = 29、nv = 28、nu = 22。

至于 sim2real……奈何小编目前还没能把 Q1 请到实验室,暂时只能在数字世界里反复折腾。真机验证这一步,就先留个 TODO 了。🥲启元机器人开发者社区 https://dev.primebot.com/

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐