Files
dog_train_deploy_guide_doc/四足机器狗训练到实机部署全流程指南.md
2026-07-31 16:32:11 +08:00

35 KiB
Raw Blame History

四足机器狗从训练到实机部署全流程指南

本教程基于西安交通大学 RoboGauge 团队的研究成果 Toward Reliable Sim-to-Real Predictability for MoE-based Robust Quadrupedal Locomotion 及其项目主页,将原始 Go2 训练任务适配为 Unitree Go1并串联 IsaacLab 训练、RoboGauge 评估、MuJoCo sim2sim、ONNX 导出、RDK X5/S100 BPU 量化和 sim2real。

本文依据当前本地仓库代码整理。编写文档的计算机不是训练机或部署机,因此下面的安装、训练、量化和实机命令尚未在本文编写环境中执行验证。实际操作时应逐条执行、逐条记录,不要整段复制后无人值守运行。

1. 教程范围与版本约定

1.1 仓库

用途 仓库或目录
IsaacLab 训练与自带 MuJoCo 部署 go2_rl_robotlabgo1 分支
自动化 sim2sim 评估 RoboGaugego1 分支
独立 MuJoCo、ONNX、X5/S100 BPU 部署 go1_pro_deploy/deploy_45dim_rl_gym
官方公开 SDK unitree_legged_sdk
私有 Go1 PRO SDK 仅在内部部署机使用,本文不公开协议、密钥和专有接口

本教程统一推荐 26000 轮检查点,示例名称如下:

训练检查点: model_26000.pt
浮点模型:   policy_robotlab_26000.onnx
X5 模型:    policy_robotlab_26000_int16_gemm.bin
S100 模型:  policy_robotlab_26000_s100_int16_gemm.hbm

26000 只是当前推荐基线,不表示轮数越大一定越好。最终模型仍应根据 RoboGauge、MuJoCo 回放和真机日志共同选择。

1.2 全流程

Go1 模型与任务适配
  -> IsaacLab + MoE-CTS 训练
  -> TensorBoard 观察收敛
  -> 导出 TorchScript / ONNX
  -> IsaacLab play
  -> RoboGauge 自动评估
  -> 独立 MuJoCo sim2sim
  -> ONNX 实机只读和悬空测试
  -> 用真实日志制作 BPU 校准集
  -> X5/S100 量化与离线对齐
  -> BPU 悬空对照
  -> 低速地面测试
  -> 复杂地形测试

任何阶段不通过,都应退回上一阶段定位问题,不能用放宽真机保护阈值来掩盖观测、关节顺序或模型接口错误。

2. 研究方法解读

2.1 为什么使用 MoE-CTS

该流程的核心不是只追求仿真中的高回报,而是提高策略在未知动力学和地形上的泛化能力,并用标准化指标提前判断 sim2real 风险。

  • CTSConcurrent Teacher-Studentteacher 可使用地形高度、接触力、关节力矩等特权信息student 仅使用真机可获得的本体感知历史。两者在训练期间并行优化,使 student 学会从历史观测中估计隐含环境信息。
  • MoEMixture of Experts:策略包含多个 expert由门控网络根据隐变量组合不同 expert。不同 expert 可以分别覆盖平地、坡面、台阶、低摩擦和动力学扰动等状态。
  • 历史观测:单帧传感信息不能直接给出摩擦、延迟和外部扰动,连续历史为 student 提供了估计这些量的依据。
  • 域随机化与课程学习:训练中改变地形、摩擦、质量、执行器延迟和外力,并随能力提升逐渐增加地形和指令难度。

2.2 奖励、评估和部署的职责不同

训练奖励用于产生梯度,并不是最终安全指标。高总奖励可能由速度跟踪项主导,同时仍存在极端关节、力矩尖峰或急停不稳。因此本流程分三层判断:

  1. 训练奖励:判断优化是否收敛、是否发生策略坍塌。
  2. RoboGauge 指标:在跨地形、跨摩擦、多随机种子下比较跟踪、安全和运动质量。
  3. 真机保护与日志:判断实时观测、动作、关节目标和硬件状态是否处于允许范围。

RoboGauge 当前关注的指标包括关节软限位、线/角速度误差、电机功耗、姿态稳定性、力矩平滑度、摩擦裕度和 ZMP 裕度。归一化后均按“越大越好”解释,但任何综合分数都不能替代分项检查。

3. 训练环境

3.1 硬件与软件前提

  • NVIDIA GPU 和匹配的驱动。
  • Linux 训练环境;图形界面训练不是必须。
  • Python 3.11。
  • IsaacLab/Isaac Sim 2.3.2 对应依赖。
  • 足够的磁盘空间保存检查点、TensorBoard 日志和导出模型。

3.2 RoboGo 镜像

此处由维护者补充平台镜像信息:

TODO维护者填写
RoboGo 镜像名称:
镜像拉取命令:
容器启动参数:
训练数据持久化目录:

若镜像已包含 IsaacLab、项目仓库和依赖可跳过下一节的手工安装但仍需核对代码分支和版本。

3.3 手工安装

conda create -n go2_rl_robotlab python=3.11 -y
conda activate go2_rl_robotlab

python -m pip install --upgrade pip
python -m pip install "isaaclab[isaacsim,all]==2.3.2.post1" --extra-index-url https://pypi.nvidia.com
python -m pip install torch==2.7.0 torchvision==0.22.0 --index-url https://download.pytorch.org/whl/cu128

拉取 Go1 分支并安装项目内定制包:

git clone --branch go1 --single-branch https://github.com/wertyuilife2/go2_rl_robotlab.git
cd go2_rl_robotlab

python -m pip install -e source/robot_lab
python -m pip install -e source/rsl_rl
python -m pip install mujoco pygame onnx onnxruntime tensorboard

记录环境,便于在部署前复现:

git rev-parse HEAD
python --version
python -m pip freeze > requirements-lock.txt
nvidia-smi

4. 从 Go2 适配到 Go1

本地 go1 分支的关键提交为 0d2afbf feat: add Go1 training and MuJoCo deployment。适配不只是替换 URDF至少包括以下内容

  1. 添加完整的 Go1 URDF、MuJoCo XML 和 mesh。
  2. 定义 Go1 执行器、默认关节角、PD 参数和力矩限制。
  3. 新建 RobotLab-Go1-v0 的场景、观测、动作、奖励、随机化和课程配置。
  4. 注册 Go1 任务及 MoE-CTS runner 配置。
  5. 添加 Go1 的 MuJoCo 配置和部署入口。
  6. 在所有模块中统一关节顺序与策略输入输出。

可用下面的命令查看完整改动:

git show --stat 0d2afbf
git show 0d2afbf -- source/robot_lab/robot_lab/tasks/go1
git show 0d2afbf -- source/robot_lab/robot_lab/assets/unitree.py
git show 0d2afbf -- source/robot_lab/robot_lab/assets/unitree_actuator.py
git show 0d2afbf -- deploy/deploy_mujoco

4.1 必须固定的策略接口

项目 RobotLab Go1 约定
关节顺序 FR, FL, RR, RL,每条腿依次为 hip、thigh、calf
单帧观测 45 维
历史长度 10 帧
ONNX 输入 450 维,由部署端维护历史
TorchScript 输入 当前 45 维,模型内部维护历史
动作 12 维归一化关节位置偏移
动作缩放 0.25 rad,再加默认关节角
物理步长 0.005 s
decimation 4
策略周期 0.02 s,即 50 Hz
训练 PD Kp=28Kd=0.7
仿真力矩上限 33.5 Nm

45 维单帧观测顺序为:

机身角速度(3)
+ 投影重力(3)
+ 速度指令 vx/vy/wz(3)
+ 相对默认角度的关节位置(12)
+ 关节速度(12)
+ 上一时刻动作(12)
= 45

RobotLab ONNX 的历史按“观测项分组后堆叠”,不是简单地将 10 个 45 维帧首尾拼接:

[角速度 10 帧, 重力 10 帧, 指令 10 帧,
 关节位置 10 帧, 关节速度 10 帧, 上一动作 10 帧]

不要把旧 RL-Gym 的 5 帧/225 维 ONNX、RobotLab 的 10 帧/450 维 ONNX、内部维护历史的 45 维 TorchScript 混用。即使程序没有立即报错,错误的历史排列也会让输出失去意义。

5. 奖励函数与环境设计

Go1 奖励配置位于:

source/robot_lab/robot_lab/tasks/go1/env_cfg.py

当前主要奖励项如下:

奖励项 权重 目的
track_lin_vel_xy_exp 2.0 跟踪平面线速度
track_ang_vel_z_exp 1.0 跟踪 yaw 角速度
lin_vel_z_l2 -2.0,课程中趋近 0 抑制竖直跳动
ang_vel_xy_l2 -0.05 抑制 roll/pitch 角速度
joint_acc_l2 -1e-7 抑制关节加速度尖峰
joint_power -2e-5 降低机械功率
joint_torques_l2 -1e-4 降低力矩
base_height_l2 -1.0,课程中到 -10.0 保持约 0.30 m 机身高度
action_rate_l2 -0.01 抑制相邻动作突变
action_smoothness_l2 -0.01 抑制动作二阶不平滑
undesired_contacts -1.0 惩罚大腿、小腿触地
joint_pos_limits -2.0 避免关节接近或越过限制
feet_regulation -0.05 约束足端运动
hip_pos_penalty_l1 -0.05 约束髋关节偏移
joint_pos_penalty_l1 -0.01 约束大腿和小腿关节偏移

线速度和角速度跟踪项采用指数形式,误差为零时单项接近 1再乘权重其余多数为非负代价乘负权重。IsaacLab 奖励管理器还会按环境步长累计,因此不要根据权重直接猜测 TensorBoard 总回报的绝对值。

奖励判断原则:

  • 先看 teacher 和 student 的平均回报是否持续上升并进入平台期。
  • student 长期显著低于 teacher通常表示历史编码器未学到足够的隐变量信息。
  • 总回报上升时,还要检查关节限制、力矩、功率、动作平滑和 episode length。
  • 单项奖励尺度相差较大,不能要求每条曲线数值接近。
  • 奖励短时波动是 on-policy 训练的正常现象连续大幅下降、episode length 同时缩短才更值得警惕。
  • 修改奖励后必须新建 run不能把不同配置的曲线当作同一实验连续比较。

环境通过随机初始状态、摩擦/质量等动力学变化、外力扰动、执行器延迟和连续地形难度降低过拟合。Go1 训练中的执行器延迟为 0 到 4 个物理步,即 0 到 20 ms独立 MuJoCo 部署中的延迟参数按策略步计量,两者单位不同。

6. 开始训练

6.1 默认参数的含义

当前默认配置:

num_envs = 4096
num_steps_per_env = 24
save_interval = 500
max_iterations = 300000通常通过命令行覆盖

每次 PPO 更新的 rollout 数量约为:

4096 * 24 = 98304 个 transition

num_envs 主要影响仿真吞吐、显存和每轮采样量;num_steps_per_env 同时影响 rollout 时间跨度、buffer 显存和更新统计。优先保持 num_steps_per_env=24,通过 --num_envs 适配 GPU。

建议从小到大试探:

python scripts/rsl_rl/train.py --task=RobotLab-Go1-v0 --num_envs=512 --max_iterations=20 --headless
python scripts/rsl_rl/train.py --task=RobotLab-Go1-v0 --num_envs=1024 --max_iterations=20 --headless
python scripts/rsl_rl/train.py --task=RobotLab-Go1-v0 --num_envs=2048 --max_iterations=20 --headless
python scripts/rsl_rl/train.py --task=RobotLab-Go1-v0 --num_envs=4096 --max_iterations=20 --headless

另开终端观察:

watch -n 1 nvidia-smi

选择能稳定运行且留有显存余量的最大环境数。若 OOM先减小 num_envs;只有明确理解 PPO batch 变化后,再修改 rsl_rl_cfg.py 中的 num_steps_per_env、mini-batch 和 epoch 数。

6.2 正式训练到 26000 轮

cd go2_rl_robotlab
conda activate go2_rl_robotlab

python scripts/rsl_rl/train.py \
  --task=RobotLab-Go1-v0 \
  --num_envs=4096 \
  --max_iterations=26000 \
  --run_name=go1_26000 \
  --logger=tensorboard \
  --headless

日志默认位于:

logs/rsl_rl/go1_moe_cts/<时间戳>_go1_26000/

检查点每 500 轮保存一次。确认 26000 轮文件存在:

find logs/rsl_rl/go1_moe_cts -name 'model_26000.pt' -print

6.3 断点续训

python scripts/rsl_rl/train.py \
  --task=RobotLab-Go1-v0 \
  --num_envs=4096 \
  --max_iterations=3000 \
  --resume \
  --load_run=<RUN_DIRECTORY_NAME> \
  --checkpoint=model_23000.pt \
  --headless

当前 runner 在恢复训练时将 --max_iterations 解释为额外训练轮数。例如从 23000 继续到 26000应填写 26000 - 23000 = 3000,若仍填写 26000 会继续训练到约 49000。恢复前先确认 params/env.yamlparams/agent.yaml 与预期一致。加载旧优化器状态后改变网络结构、观测维数或 expert 数量通常不可行。

7. 使用 TensorBoard 观察训练

训练机执行:

tensorboard --logdir logs/rsl_rl --port 6006

从本机通过 SSH 查看远端 TensorBoard

ssh -L 6006:127.0.0.1:6006 <USER>@<TRAIN_HOST>

浏览器打开 http://127.0.0.1:6006。重点观察:

曲线 判断方式
Train/mean_teacher_reward teacher 是否稳定学习并进入平台期
Train/mean_student_reward student 是否跟随 teacher部署时主要关心 student
Train/*episode_length 是否频繁因机身触地提前终止
Loss/* 是否出现 NaN、爆炸或长期异常震荡
Policy/mean_noise_std 探索强度是否异常塌缩或持续过大
Perf/total_fps 并行环境设置是否有效利用硬件
Episode/* 各奖励项、课程难度和终止统计

不要只选平均回报最高的 checkpoint。建议将 15000、20000、23000、26000 等候选同时送入 RoboGauge比较综合分数和分项指标后再决定。

8. 评估与导出模型

8.1 IsaacLab play 与导出

play.py 加载 checkpoint 后,会先在同一 run 的 exported/ 下生成 policy.ptpolicy.onnx,再进入可视化 rollout

python scripts/rsl_rl/play.py \
  --task=RobotLab-Go1-v0 \
  --num_envs=64 \
  --checkpoint=/absolute/path/to/model_26000.pt

输出:

<RUN_DIR>/exported/policy.pt
<RUN_DIR>/exported/policy.onnx

导出文件出现后才能终止 play。为了后续名称统一

cp <RUN_DIR>/exported/policy.onnx /path/to/go1_pro_deploy/deploy_45dim_rl_gym/policy_robotlab_26000.onnx
cp <RUN_DIR>/exported/policy.pt /path/to/RoboGauge/resources/models/go1/policy_robotlab_26000.pt

8.2 不打断训练地导出

训练 runner 保存的是可恢复训练的 checkpoint不会默认在每次保存时生成 ONNX。要保持主训练进程运行可在另一块 GPU、另一台机器或训练显存允许时启动独立 play.py

CUDA_VISIBLE_DEVICES=1 python scripts/rsl_rl/play.py \
  --task=RobotLab-Go1-v0 \
  --num_envs=1 \
  --checkpoint=/absolute/path/to/model_26000.pt \
  --headless

注意:

  • 只处理已经完整写完的 checkpoint不要复制正在写入的文件。
  • 同一 GPU 显存不足时会影响训练,应在第二块 GPU 或离线机器导出。
  • play.py 导出后仍会进入仿真循环;看到导出文件后用 Ctrl+C 结束该独立进程,不会中断训练进程。

8.3 检查 ONNX 接口

python -c "import onnxruntime as ort; s=ort.InferenceSession('policy_robotlab_26000.onnx'); print([(x.name,x.shape,x.type) for x in s.get_inputs()]); print([(x.name,x.shape,x.type) for x in s.get_outputs()])"

预期输入为 450 个 float32,输出为 12 个动作。输入输出不匹配时不要继续 sim2sim 或量化。

8.4 独立 MuJoCo 快速检查

如果你要做的是“和部署链路一致”的快速检查,直接使用部署仓库里的 ONNX 入口。它读取当前 45 维单帧观测,并在脚本内部维护 10 帧历史,适合在进入完整 RoboGauge 评估前核对观测、动作缩放和地形 XML

cd /path/to/go1_pro_deploy
MUJOCO_GL=glfw mjpython deploy_45dim_rl_gym/deploy_go1_onnx_mujoco_lab.py \
  --onnx deploy_45dim_rl_gym/policy_robotlab_26000.onnx

楼梯测试同样直接切 terrain

MUJOCO_GL=glfw mjpython deploy_45dim_rl_gym/deploy_go1_onnx_mujoco_lab.py \
  --onnx deploy_45dim_rl_gym/policy_robotlab_26000.onnx \
  --terrain deploy_45dim_rl_gym/terrains/stairs/stairs_3.xml \
  --spawn-x -0.6 \
  --spawn-z 0.34

脚本默认 --clip-actions 100.0--max-target-step 0.0。如果你要做更保守的起步测试,可以再按日志逐步收紧这些参数;不要把旧版本的示例值当成固定默认。

9. RoboGauge 自动评估

9.1 安装

建议使用独立环境,因为当前 RoboGauge 固定了 mujoco==3.2.3dm_control==1.0.23numpy<2

conda create -n robogauge python=3.10 -y
conda activate robogauge

git clone https://github.com/wty-yy/RoboGauge.git
cd RoboGauge
python -m pip install -e .

公开仓库作为基线使用。本文所需的 go1_lab.* 任务来自本地修改版的 go1 分支;在新评估机上还必须同步该分支或应用对应提交。安装后执行下面的命令确认任务注册,输出中必须出现 go1_lab.flatgo1_lab.stairs_fd 等任务:

git branch --show-current
python -c "import robogauge.tasks; from robogauge.utils.task_register import task_register; print('\\n'.join(sorted(task_register.pipeline_classes)))"

若不存在 go1_lab 任务,不要用 go2_lab 代替,应先同步本地 Go1 适配代码。

9.2 单模型与压力测试

直接通过 --model-path 指定 26000 对应的 TorchScript

python robogauge/scripts/run.py \
  --task-name go1_lab.flat \
  --model-path resources/models/go1/policy_robotlab_26000.pt \
  --experiment-name go1_robotlab_26000_flat \
  --headless

跨地形压力测试:

python robogauge/scripts/run.py \
  --task-name go1_lab \
  --model-path resources/models/go1/policy_robotlab_26000.pt \
  --experiment-name go1_robotlab_26000_stress \
  --stress-benchmark \
  --stress-terrain-names flat slope_fd slope_bd stairs_fd stairs_bd wave obstacle \
  --num-processes 32 \
  --seeds 0 1 2 \
  --search-seeds 0 1 2 3 4 \
  --frictions 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 \
  --compress-logs \
  --headless

--num-processes 应根据 CPU 核数和内存调整,不宜盲目设为最大逻辑核心数。

9.3 训练期间异步评估

先启动服务端:

conda activate robogauge
cd RoboGauge
python robogauge/scripts/server.py --port 9973 --num-processes 32

再启动训练:

conda activate go2_rl_robotlab
cd go2_rl_robotlab
python scripts/rsl_rl/train.py \
  --task=RobotLab-Go1-v0 \
  --num_envs=4096 \
  --max_iterations=26000 \
  --robogauge \
  --robogauge_port=9973 \
  --headless

当前 OnPolicyRunnerCTS.update_robogauge() 每 500 轮自动导出一个 JIT 模型并提交评估,但本地版本中 task_name 曾硬编码为 go2_lab。训练 Go1 前必须确认该处为:

task_name="go1_lab"

检查文件为 source/rsl_rl/rsl_rl/runners/on_policy_runner_cts.py。否则会用错误的机器人任务评估 Go1 模型。异步评估生成的是 jit_models/policy_jit_<ITER>.ptONNX 仍需通过 play.py 导出。

9.4 如何选择 26000 模型

至少比较:

  • Tracking线速度、角速度是否准确。
  • Safety关节范围、功耗、力矩平滑度是否合理。
  • Quality姿态、急停、运动质量是否稳定。
  • Level坡面、台阶、波浪和障碍的最高稳定难度。
  • 多 seed 方差:均值高但方差很大的策略不适合直接上机。

10. 独立 MuJoCo sim2sim

这里推荐使用 go1_pro_deploy/deploy_45dim_rl_gym/deploy_go1_onnx_mujoco_lab.py,因为它与 RobotLab 的 10 帧/450 维 ONNX 接口一致。

cd /path/to/go1_pro_deploy
conda activate <DEPLOY_ENV>

MUJOCO_GL=glfw mjpython deploy_45dim_rl_gym/deploy_go1_onnx_mujoco_lab.py \
  --onnx deploy_45dim_rl_gym/policy_robotlab_26000.onnx

楼梯测试:

MUJOCO_GL=glfw mjpython deploy_45dim_rl_gym/deploy_go1_onnx_mujoco_lab.py \
  --onnx deploy_45dim_rl_gym/policy_robotlab_26000.onnx \
  --terrain deploy_45dim_rl_gym/terrains/stairs/stairs_3.xml \
  --spawn-x -0.6 \
  --spawn-z 0.34

键盘映射:

按键 功能
Up / Down 前进 / 后退
Left / Right 左转 / 右转,即 yaw
, / . 左移 / 右移,即 vy
K 速度指令清零
R 重置机器人
Esc 退出

sim2sim 重点检查:

  • 初始零历史时是否平稳进入站姿。
  • 四条腿是否对应正确,左右/前后没有交换。
  • 正负 vxvywz 的方向是否正确。
  • 50 Hz 策略周期、PD 参数、默认角和动作缩放是否与训练一致。
  • 平地、急停、转向、侧移、斜坡和台阶是否均无明显动作尖峰。
  • action、关节速度、机身 roll/pitch 是否出现持续增长。

11. BPU 量化

11.1 量化前提

BPU 量化不是把 ONNX 改一个后缀。至少需要:

  1. 已通过接口检查的 policy_robotlab_26000.onnx
  2. 来自相同观测构建逻辑的真实部署日志。
  3. 从 RL 状态提取的 10 帧/450 维 float32 校准样本。
  4. 浮点 ONNX、图变换 ONNX、量化模型三者的逐样本输出对比。

当前量化脚本会保留 actions 输出、降级 opset、包装为 BPU 需要的 4D 输入,并把 MoE grouped Conv 等价替换为 Gemm以减少 CPU fallback。中间 norm 节点位于 actor 之前,不能随意删除或移到后处理。

11.2 RDK X5

量化机执行:

cd /path/to/go1_pro_deploy

bash deploy_45dim_rl_gym/bpu_quantization/quantize_policy_x5.sh \
  --policy ../policy_robotlab_26000.onnx \
  --round 26000 \
  --name policy_robotlab_26000 \
  --history-len 10 \
  --samples 64 \
  --min-samples 32 \
  --log-prefix robotlab_go1_deploy \
  --cal-tag robotlab \
  --quant int16

预期产物:

deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/
  policy_robotlab_26000_int16_gemm.bin

板端先做完全离线自检,不连接电机控制:

cd /root/go1_pro_deploy
PYTHONPATH=/root/go1_pro_deploy \
python3 deploy_45dim_rl_gym/bpu_deploy_x5/test_bpu_policy.py \
  --bpu-model deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_gemm/policy_robotlab_26000_int16_gemm.bin \
  --input-bin deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
  --repeat 1000

X5 的 featuremap 模型不要直接改用 hobot_dnn.pyeasy_dnn.forward()。当前项目默认通过 C++ DNN API并按 infer、wait、release 的完整生命周期执行,以保证输出和 hrt_model_exec infer 对齐。

11.3 S100

cd /path/to/go1_pro_deploy

bash deploy_45dim_rl_gym/bpu_quantization/quantize_policy_s100.sh \
  --policy ../policy_robotlab_26000.onnx \
  --round 26000 \
  --name policy_robotlab_26000 \
  --history-len 10 \
  --samples 64 \
  --min-samples 32 \
  --log-prefix robotlab_go1_deploy \
  --cal-tag robotlab \
  --march nash-e \
  --quant int16

预期产物:

deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/
  policy_robotlab_26000_s100_int16_gemm.hbm

板端测速:

/usr/hobot/bin/hrt_model_exec perf \
  --model_file deploy_45dim_rl_gym/bpu_quantization/mapper_output_26000_s100_gemm/policy_robotlab_26000_s100_int16_gemm.hbm \
  --model_name policy_robotlab_26000_s100_int16_gemm \
  --input_file deploy_45dim_rl_gym/bpu_quantization/calibration_data_26000_robotlab_fast64/00000.bin \
  --frame_count 1000 \
  --thread_num 1

量化验收不能只看平均延迟,还要记录:

  • 输入/输出名称、shape 和 dtype。
  • held-out 样本的最大绝对误差、平均绝对误差和余弦相似度。
  • 最大延迟和抖动是否满足 20 ms 控制周期。
  • 是否存在 CPU fallback 或频繁 BPU/CPU 子图切换。
  • 连续推理是否有资源泄漏、错误日志或偶发异常动作。

12. 实机部署边界

12.1 SDK 保密说明

实际部署使用私有 Go1 PRO SDK。本文只描述公开的控制契约和安全设计不记录私有协议格式、加密材料、逆向过程或专有实现。需要公开复现时应以 Unitree 官方 unitree_legged_sdkUDPLowStateLowCmdSafety 接口为基线重新实现适配层。

无论 SDK 如何实现,策略层与硬件层之间必须显式完成:

  • 状态包有效性、时间戳和丢包检查。
  • 电机顺序到 FR, FL, RR, RL 策略顺序的映射。
  • IMU 四元数、角速度坐标系和单位转换。
  • 关节位置、速度、上一动作和指令的缩放。
  • ONNX 历史缓存的初始化、更新和复位。
  • 动作缩放、默认角叠加、目标限幅和变化率限制。
  • PD 控制、安全检查、发送周期和退出阻尼。

公开 SDK 可用于核对通用 API、结构体字段和安全调用方式但不要直接运行带位置目标的官方示例。先只编译

git clone https://github.com/unitreerobotics/unitree_legged_sdk.git
cd unitree_legged_sdk
mkdir build
cd build
cmake ..
cmake --build . --parallel

本地 walk-these-ways/go1_gym_deploy/unitree_legged_sdk_bin/lcm_position.cpp 可作为官方 SDK 接入策略控制环的公开参考。阅读时重点关注 UDP 收发线程、LowState 到策略观测的转换、Safety 调用和退出流程,不要照搬其网络地址、增益或速度范围到另一台机器人。

12.2 硬件、接线与网络

本节按维护者要求留空,由实际设备负责人填写。部署前不得跳过。

TODO维护者填写

1. Go1 具体型号与固件版本:
2. S100/RDK X5 型号、系统镜像与供电:
3. 机器人、主控、交换机/网卡接线图:
4. 网口名称、静态 IP、子网掩码和路由
5. 遥控器型号及按键映射:
6. 吊架/保护绳安装方式和承重:
7. 物理急停、断电和现场人员分工:
8. 启停 sport mode 的设备专用步骤:

13. 软件安全策略

13.1 控制权互斥

低层控制前必须确认原厂 sport 进程已停止,避免两个控制源同时发送命令。不要在机器人站立或行走时直接停止 sport mode先让机器人处于受支撑或安全趴卧状态。

检查示例:

pgrep -fl 'keep_sport_alive|Legged_sport|appTransit'

恢复原厂控制前,先结束自定义低层进程并确认已经发送阻尼停机,再恢复 sport mode。

13.2 启动守卫

  • 默认模式只能读取状态,不能发送 RL 关节目标。
  • 必须有显式 --enable-rl 才允许进入 RL 状态。
  • 收到第一帧有效、完整且时间连续的 LowState 前,只发送停止哨兵或保持阻尼。
  • 检查电机顺序、模型 shape、NaN/Inf、历史长度和控制频率。
  • RL 刚启用时先预热历史;预热期间保持默认站姿。
  • 从默认站姿向 RL 目标渐变,限制每周期目标变化量。

13.3 每周期保护

每个 20 ms 策略周期至少检查:

  • 状态包是否超时、丢失或校验失败。
  • 关节位置是否超硬限位或安全软限位。
  • 关节速度是否超过部署阈值。
  • 估计力矩和命令力矩是否超过电机限制。
  • action 是否有限、是否超过软/硬跳闸阈值。
  • 新关节目标与当前目标、实测位置的差值是否过大。
  • IMU 角速度以及 roll/pitch 是否超过阈值。
  • 推理时间和整个控制循环是否超过 20 ms deadline。
  • 电量、温度、通信状态和错误码是否正常。

任一关键检查失败应进入 FAULTIDLE damping,清零历史和上一动作。禁止捕获异常后静默继续运行。

13.4 力矩、速度与位置限制

训练中的 33.5 Nm 是仿真执行器上限,不等于真机调试时应直接允许的命令。首次测试应从官方安全接口允许的保守比例开始,并根据日志逐级放开。

位置控制命令即使命令前馈力矩为零,也会通过 PD 误差产生实际力矩,因此:

  • 增大 Kp、目标跳变量或目标与实测位置差都可能造成力矩尖峰。
  • power_factor 不能替代实测 tauEst 保护。
  • 触发力矩保护时,应优先减小目标变化、速度指令或 PD 增益,不能只提高保护阈值。
  • 关节硬限位、位置偏差保护、速度保护和力矩保护必须同时存在。

13.5 停机路径

至少准备三条独立路径:

  1. 遥控器软急停:当前状态机使用 L2 回到 IDLE damping
  2. Ctrl+C:捕获后连续发送多帧阻尼命令再退出。
  3. 物理断电:现场人员随时可以执行,且不依赖已被停止的原厂进程。

急停触发后不得自动恢复到 RL。必须重新检查故障原因从校准或保持状态重新开始。

14. 分级实机测试流程

以下步骤必须按顺序通过。任何异常都应保存日志并停止,不要连续试错。

14.1 第 0 级:不上电机的离线检查

  • 用相同 450 维输入比较 PyTorch、ONNX 和 BPU 输出。
  • 检查 12 维动作的顺序、符号、范围和有限性。
  • 测量平均、P99 和最大推理时间。
  • 对零输入、真实日志输入和边界输入做回归。

14.2 第 1 级:只读 LowState

只连接 MCU 并记录 IMU、12 个关节位置/速度/力矩、电量、错误码和遥控器,不发送电机目标。手动缓慢移动悬空的腿,确认索引、符号和单位。

14.3 第 2 级:只构建观测

构建 45 维单帧和 450 维历史,但不运行策略。逐项核对:

  • 静止水平时投影重力方向正确。
  • 手动旋转机身时角速度轴和符号正确。
  • 手动移动单腿时仅对应三个关节变化。
  • 零速度命令为 [0, 0, 0]
  • 历史从零初始化,并按正确顺序滑动更新。

14.4 第 3 级:只推理不发命令

运行 ONNX/BPU 推理并记录 action仍不发送电机命令。检查 action 无 NaN/Inf、无持续饱和、静止状态不发散ONNX 和 BPU 的动作差在离线验收范围内。

X5 示例:

cd /root/go1_pro_deploy
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
python3 deploy_45dim_rl_gym/bpu_deploy_x5/deploy_go1_robotlab_bpu_x5_fastcpp.py \
  --infer-check \
  --log-dir logs \
  --print-every 50 \
  --max-steps 500

S100 示例:

cd /root/go1_pro_deploy
PYTHONPATH=/root/go1_pro_sdk:/root/go1_pro_deploy \
python3 deploy_45dim_rl_gym/bpu_deploy_s100/deploy_go1_robotlab_bpu_s100_fastcpp.py \
  --infer-check \
  --log-dir logs \
  --print-every 50 \
  --max-steps 500

14.5 第 4 级:悬空状态机,不启用 RL

机器人必须可靠悬挂,腿下和侧面无人。先不加 --enable-rl,验证状态机:

IDLE -> CALIBRATE -> HOLD -> OBS_TEST -> INFER_TEST
  • R2 每次只前进一层。
  • L2 在任意激活状态回到 IDLE damping
  • 未加 --enable-rl 时,INFER_TEST -> RL 必须被守卫拒绝。
  • Ctrl+C 必须进入阻尼后退出。

14.6 第 5 级:悬空 RL零速度命令

只有前四级通过后才能增加 --enable-rl。第一轮禁用摇杆速度输入,只验证站姿、动作方向、关节速度、力矩和急停。观察 5 到 10 秒后主动回到 HOLD不要长时间连续运行。

14.7 第 6 级:悬空 RL小幅指令

一次只测试一个方向:小 vx、小 vy、小 wz。每次测试后回零,检查正负方向和动作对称性。逐个参数变化,不要同时修改 PD、action clip、目标变化率和遥控比例。

14.8 第 7 级:平整地面低速

  • 使用保护绳,现场一人操控、一人负责断电。
  • 从零指令站立开始,再给短时小速度。
  • 首轮只测试前进、停止和软急停。
  • 之后再测试后退、侧移和转向。
  • 每轮结束检查电机温度、日志和机械结构。

14.9 第 8 级ONNX/BPU 对照

先用已经通过地面测试的 ONNX 版本确定基线,再在相同软件保护、相同指令、相同场地上测试 BPU。两次测试只替换推理后端。若 BPU 行为异常,回到离线输入逐帧对比,不能通过放宽关节或姿态保护继续测试。

14.10 第 9 级:坡面、台阶和扰动

复杂地形是最后阶段。先从低难度、低速度和正向通过开始,再逐步测试侧向、反向、急停和摩擦变化。每次增加难度前必须确认前一级多次可复现。

15. 日志与验收

每次实机运行至少保存:

metadata.json
steps.jsonl

元数据应包含 Git commit、模型哈希、推理后端、控制周期、PD 参数、动作限制、保护阈值、设备型号、测试人员和场景。逐步日志至少包含:

timestamp / state / state_reason
commands_raw / commands
obs_single / obs_history
action_raw / action_safe
joint_targets
dof_pos / dof_vel / tau_est
base_ang_vel / projected_gravity / imu_rpy
inference_ms / loop_ms
battery / temperature / communication_status

一次测试通过的最低条件:

  • 无 NaN/Inf、状态超时和控制周期持续超期。
  • 电机顺序、坐标系和指令方向全部正确。
  • 无硬限位、速度、力矩、姿态或动作异常跳闸。
  • L2Ctrl+C 和物理断电路径均已由现场演练确认。
  • ONNX/BPU 输出差异与离线结果一致。
  • 行为可在相同条件下重复,而不是偶然成功一次。

16. 常见问题

16.1 策略在 IsaacLab 正常MuJoCo 立即摔倒

优先检查关节顺序、默认关节角、观测历史排列、IMU 四元数格式、动作缩放、PD 参数和策略周期。不要先调奖励或增加力矩。

16.2 ONNX 输出维度正确但动作异常

检查输入是 450 维按观测项分组的历史,而不是 10 个单帧直接拼接;同时确认输入已经应用训练时的缩放,上一动作使用的是实际送入策略链路的动作定义。

16.3 BPU 很快,但真机表现比 ONNX 差

速度合格不代表数值合格。使用同一批真实 450 维输入逐帧比较 ONNX/BPU重点查 featuremap 输入、量化校准分布、输出 reshape、CPU fallback 和 runtime 调用生命周期。

16.4 TensorBoard 总回报上涨,但 RoboGauge 下降

可能是训练奖励偏向速度跟踪,牺牲了力矩、姿态或关节裕度,也可能是后期 checkpoint 对训练域过拟合。比较分项指标和多个 checkpoint不要只保留最后一轮。

16.5 真机一进入 RL 就力矩保护

立即停机。依次检查动作方向、目标角、目标单步变化、PD 增益、当前位置偏差、观测缩放和历史初始化。不要把提高 power_factor 作为第一反应。

17. 发布前检查清单

  • RoboGo 镜像和容器启动信息已补全。
  • 硬件、接线、网络和急停章节已由设备负责人补全。
  • 所有仓库 commit 和 Python 依赖已记录。
  • 26000 checkpoint 已完成 TensorBoard、IsaacLab play 和导出检查。
  • ONNX 输入 450、输出 12关节顺序为 FR, FL, RR, RL
  • RoboGauge 使用 go1_lab 而非 go2_lab
  • MuJoCo 平地、转向、侧移、急停和台阶测试通过。
  • 真实日志校准集与模型版本一致。
  • X5/S100 量化模型通过离线精度和延迟验收。
  • 私有 SDK 的协议、密钥和专有实现未写入公开文档。
  • 实机测试严格完成只读、观测、推理、状态机、悬空、地面分级流程。
  • 每条停机路径均已实际演练。

18. 参考资料