🤖 仿真与强化学习:MuJoCo 与 Isaac Lab

从物理仿真器选型到强化学习训练,再到 sim-to-real 部署到真机关节模组——本页打通"仿真 → 训练 → 部署"全链路。适合已读完本部分 ROS2 与固件章节、想动手训练人形机器人(如 H1/G1)运动策略的读者。
MuJoCo Isaac Lab PPO Sim-to-Real 强化学习
🎯 本页学习目标
1. 能说清 MuJoCo / Gazebo / Isaac Lab / Webots / PyBullet / RaiSim 六大仿真器的差异,并为自己的任务选对引擎
2. 能读懂 MJCF(XML)模型结构(asset / body / joint / actuator),写出"加载 → step → 渲染"的最小 MuJoCo 程序
3. 能讲清一条 PPO 训练流水线的五个环节:环境封装(观察/动作/奖励)→ 采样 → 优势估计 → 策略更新 → 导出部署
4. 能为一类任务(倒立摆、单腿弹跳、H1 行走)设计观察空间、动作空间与奖励函数,并解释每条奖励的意图
5. 能列出 sim-to-real 的五大关键技巧(域随机化、延迟注入、观测噪声、执行器模型、ZMP 约束),并说明策略部署到关节模组的链路
建议用时:约 60 分钟(其中动手跑 MuJoCo 示例约 20 分钟)

1 仿真器全景对比:选对引擎,事半功倍

人形机器人的研发离不开仿真:在真机上跑一次实验要调试硬件、担心摔机,而在仿真里可以并行跑几千个环境、一次训练几千万步。但"仿真器"之间的差异极大——有的追求物理精度,有的追求 GPU 并行训练速度,有的只为 ROS 整机联调。下表为 2025 年的最新格局(版本信息见文末参考来源):

仿真器物理引擎特点接触模型精度渲染GPU 加速强化学习支持上手难度适用场景
MuJoCo
(Google DeepMind,3.x)
广义坐标 + 约束求解;软接触、隐式时间积分,数值稳定;CPU 多线程;C/Python 双 API 高:凸体碰撞检测 + 软接触模型,默认参数即可用 内置 OpenGL(egl / osmesa / glfw),支持离屏渲染 物理本身无 GPU 加速;官方 mjx(JAX 重写)可在 GPU 上大规模并行 优秀:dm_control、gymnasium 包装器,Menagerie 提供 70+ 高质量机器人模型 ★ 低 科研、RL 训练、生物力学、快速原型;人形机器人(Unitree H1/G1 官方模型)
Gazebo
(Classic / Harmonic)
多物理后端(ODE / Bullet / DART / Simbody);与 ROS 2 生态深度集成 中:ODE 接触需要手动调 spring/damping,易抖动 OGRE 无 GPU 物理 弱:单进程串行,速度慢,主要用于 ROS 仿真而非 RL ★★ 中 ROS 2 机器人开发、激光/相机等多传感器仿真、整机系统联调
Isaac Lab
(NVIDIA,基于 Isaac Sim,2.x)
底层为 PhysX GPU 引擎;每帧可并行成百上千个环境,吞吐量远超 CPU 引擎 高:PhysX 支持软/硬接触、关节摩擦、材质参数 RTX 实时光追(Omniverse 生态) 原生 GPU 并行,这是其最大卖点 极佳:内置 rsl_rl / RLGames / skrl 等 PPO 实现,自带 H1/G1 等任务(如 Isaac-Velocity-Flat-H1-v0) ★★★ 高(需 NVIDIA GPU + Omniverse 生态) 大规模并行 RL 训练、腿式/人形机器人运动、抓取操作;Isaac Gym 的官方继任者
Webots
(Cyberbotics,R2025 系列)
基于 ODE 的自研内核,可选多物理后端;自带图形界面,一键建场景 中:ODE 接触模型,适合教学演示 OpenGL 3.3 无 GPU 物理 一般:社区 gym 包装器(如 gym-webots),单机速度慢 ★ 低 教学、ROS 课程、机器人原型演示、移动机器人
PyBullet
(Bullet 引擎,3.x)
Bullet 物理内核;Python 接口最简洁,支持软接触与约束;社区资料极多 中:接触参数可调,默认值即可跑通大部分任务 OpenGL GUI / EGL 离屏 无 GPU 物理 好:大量 gym 环境(pybullet-gym、gym-pybullet-drones 等),适合快速验证算法 ★ 低 快速原型、科研教学、抓取与四足机器人入门、算法对比实验
RaiSim
(ETH leggedrobotics,RaiSimLib 3.x)
自研接触求解(互补性条件、严格无穿透),接触稳定性著称;物理在 CPU 上非常快 高:适合密集接触场景(四足、灵巧手),不会"陷进"地面 无内置渲染(需搭配第三方查看器) RL 训练循环可用 GPU(PyTorch),物理求解为 CPU 好:配套 raisimGymTorch,四足 RL 经典组合 ★★ 中(注意许可证:科研免费、商用需授权) 接触丰富的 RL 研究、四足/灵巧操作、需要高接触稳定性的场景
💡 选型口诀:科研验证、快速原型、CPU 环境选 MuJoCo;大规模并行 RL 训练、目标是真机部署选 Isaac Lab;要跟 ROS 2 生态做传感器级整机仿真选 Gazebo;只要快速验证算法思路、笔记本也能跑选 PyBullet;接触特别多、对稳定性敏感的研究选 RaiSim。本页主线:用 MuJoCo 学原理,用 Isaac Lab / legged-gym 练流水线,最后 sim-to-real 部署到真机

2 MuJoCo 入门:从 XML 到第一帧

2.1 它是什么:来自 DeepMind 的开源物理引擎

MuJoCo(Multi-Joint dynamics with Contact)由 Emanuel Todorov 团队开发,2021 年 10 月被 DeepMind 收购后以 Apache 2.0 协议开源,现在由 Google DeepMind 维护,2025 年已迭代到 3.x 系列(如 3.4.0)。它的核心优势:

2.2 安装与 mjpython 环境

MuJoCo 的 Python 绑定只需一个 pip 包(自 3.x 起内置预编译共享库,无需单独下载二进制):

pip install mujoco

# 查看版本
python -c "import mujoco; print(mujoco.__version__)"

mjpython 是随 pip 包一起安装的启动器,它会保证 Python 绑定能找到与包版本匹配的共享库,官方教程和示例都推荐用它运行:

mjpython my_script.py          # 代替 python my_script.py

2.3 XML(MJCF)模型结构:asset / body / joint / actuator

MuJoCo 的模型格式是 MJCF(XML),核心思路是"声明式建模":不写任何求解代码,只描述几何、惯性与关节,引擎自动完成动力学推导。以一个倒立摆(cartpole)为例:

<mujoco model="cartpole">
  <compiler angle="degree"/>              <!-- 编译选项:角度单位用度 -->
  <option timestep="0.002"/>              <!-- 全局仿真步长:2ms -->

  <asset>                                   <!-- 资源区:网格/纹理/材质 -->
    <mesh file="pole.stl"/>
    <material name="pole_mat" rgba="0.6 0.2 0.2 1"/>
  </asset>

  <worldbody>                               <!-- 世界根节点 -->
    <body name="cart" pos="0 0 0.3">        <!-- 连杆:小车 -->
      <joint name="slider" type="slide" axis="1 0 0" range="-2 2"/>
      <geom type="box" size="0.4 0.1 0.05" mass="1"/>   <!-- 几何:碰撞+视觉 -->

      <body name="pole" pos="0 0 0.1">      <!-- 子连杆:摆杆 -->
        <joint name="hinge" type="hinge" axis="0 1 0"/>
        <geom type="capsule" fromto="0 0 0 0 0 0.5" size="0.03" mass="0.1"/>
      </body>
    </body>
  </worldbody>

  <actuator>                                <!-- 执行器:电机 -->
    <motor joint="slider" ctrlrange="-20 20" gear="1"/>
  </actuator>
</mujoco>

四个核心概念:asset 声明可复用的网格/纹理/材质;body 通过嵌套形成运动树(每个 body 是一根连杆);joint 定义连杆之间的自由度(slide 滑动 / hinge 转动 / free 六自由度);actuator 定义电机,motor 直接输出力矩,另有 position/velocity 内置伺服,可用 kp/kv 模拟 PD 控制——这与人形机器人关节模组的"位置环 + 力矩环"思路一致。

2.4 最小示例代码:加载 → step → 渲染

import mujoco

# 1) 加载并编译模型(从字符串或文件均可)
xml = open("cartpole.xml", encoding="utf-8").read()
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)

# 2) 复位并推进仿真
mujoco.mj_resetData(model, data)
data.ctrl[0] = 5.0                      # 给 0 号电机一个恒定力矩
for step in range(500):
    mujoco.mj_step(model, data)         # 每步推进一个 timestep(2ms)

# 3) 渲染一帧(离屏,返回 numpy 数组)
renderer = mujoco.Renderer(model, height=480, width=640)
frame = renderer.render(data)           # shape = (480, 640, 3)

# 4) 或者直接打开交互窗口,边看边调
mujoco.viewer.launch_passive(model, data)
while data.time < 10.0:
    mujoco.mj_step(model, data)
    mujoco.mj_syncState(model, data)    # 同步给渲染线程

理解两个关键数据对象:model(MjModel)是编译后的"静态图纸"(质量、惯量、关节约束、执行器参数),data(MjData)是"运行时状态"(关节位置 qpos、速度 qvel、控制量 ctrl、外力、接触力等)。RL 训练时读 data.qpos / data.qvel 组成观测,写 data.ctrl 作为动作,然后 mj_step 推进。

2.5 加载本站 00 部分已有的 H1 / G1 URDF

MuJoCo 从 2.3.3 起原生支持直接加载 URDF:解析器识别到 <robot> 根标签时自动完成"URDF → 内部模型"的转换,所以本站 00 部分的模型可以直接用:

import mujoco

# 本站 00 部分自带的 H1 / G1 模型
model = mujoco.MjModel.from_xml_path(
    "../00_3D解剖/models/h1/h1.urdf")     # 或 g1/g1_29dof.urdf
data = mujoco.MjData(model)
mujoco.viewer.launch_passive(model, data)
while data.time < 30.0:
    mujoco.mj_step(model, data)
    mujoco.mj_syncState(model, data)
⚠️ URDF 转换三注意: 1. URDF 的 visual/collision 网格路径是相对 URDF 文件解析的,移动文件后记得检查 mesh 是否能找到;
2. 部分 URDF 缺失 inertial(质量/惯量)会导致模型不稳或编译报错,转换后先在空场景里看是否"原地塌掉";
3. 关节阻尼、摩擦、限位等参数 URDF 常未定义,想要更贴近真机,建议直接使用 MuJoCo Menagerie 中精调过的官方 MJCF(unitree_h1 / unitree_g1),或用 urdf2mjcf 类工具转换后手工校准。

3 强化学习训练流水线:以 Isaac Lab / legged-gym 为例

单靠手写 PD 控制器很难让人形机器人稳定行走,业界主流做法是强化学习(RL):策略网络直接学习"从观测到关节指令"的映射。Isaac Lab 是 NVIDIA 官方框架(Isaac Gym 的继任者,Isaac Gym 已停止维护),底层用 PhysX 在 GPU 上并行数千个环境;legged-gym(ETH leggedrobotics)是腿式机器人 RL 的经典参考实现,其结构被 Unitree 官方项目 unitree_rl_gym(H1/G1/Go2)与 Isaac Lab 版 unitree_rl_lab 继承。

3.1 环境封装:观察空间 / 动作空间 / 奖励函数

无论用哪个框架,一个 RL 环境的核心就是这三样。以人形机器人行走为例(具体数值参考 legged-gym 系默认配置):

组成内容(示例维度)为什么这么设计
观察空间基座线速度(3)+ 角速度(3)+ 姿态(四元数 4)+ 关节位置(每关节)+ 关节速度(每关节)+ 上一动作(每关节)+ 指令速度(2~3)策略需要"自身状态 + 任务指令"才能决策;关节角用 sin/cos 编码避免角度环绕;速度类观测做裁剪与归一化
动作空间每个关节一个目标位置(输出后经 PD 位置伺服执行),连续、范围受限(如 ±0.5 rad)比直接输出力矩更稳、更接近真机关节模组的工作方式——模组本身有位置环
奖励函数任务奖励(前进速度跟踪)+ 惩罚项(姿态倾斜、关节超限、能耗、动作变化率)+ 可选的步态/接触正则任务奖励告诉策略"要做什么",惩罚项约束"不能乱来";每一项都要能解释、可调权重

3.2 PPO 训练流程:六步走

1定义环境
用 MJCF/URDF 建模型,在 Isaac Lab 中声明 ManagerBasedRLEnv 或继承 legged_gym 的 LeggedRobot,实现 reset / step / get_observations / compute_reward
2并行采样
GPU 上同时跑 N 个环境(如 4096),每个环境用当前策略 rollout 采集轨迹
3估计优势
用 GAE(广义优势估计)计算每个时刻的优势值,权衡偏差与方差
4更新策略
PPO 的 clip 目标 + Critic 价值网络,多 epoch 小批量梯度更新,限制单次更新幅度
5评估存档
定期跑评估环境、保存 checkpoint;观察训练曲线判断是否收敛
6导出部署
把 Actor 网络导出为 ONNX / torchscript,交给部署端推理

实际命令行(以 Isaac Lab 自带 H1 平地行走任务为例):

# Isaac Lab(rsl_rl 入口)
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py \
    --task Isaac-Velocity-Flat-H1-v0 --num_envs 4096 --headless

# legged-gym 系(unitree_rl_gym 的 H1 配置)
python legged_gym/scripts/train.py --task=unitree_h1 \
    --num_envs=4096 --headless

3.3 训练曲线怎么看

TensorBoard / wandb 上主要盯四类曲线,看趋势而不是单点数值:

曲线正常表现异常信号
mean reward先快速上升,后缓慢爬升趋于平台;曲线应单调变好(允许小波动)长期徘徊不涨 → 检查奖励尺度与观测;突然跳涨 → 警惕 reward hack
episode length随训练上升,直到接近最大步数(学会了"不摔"或完成任务)一直很短 → 初始条件太难或终止条件过严
policy entropy缓慢下降,说明策略从"乱试"收敛到"确定动作"骤降到 0 → 过早收敛(可能陷入局部最优);居高不下 → 没学会,检查奖励是否稀疏
critic loss / value价值估计与真实回报逐渐对齐剧烈震荡 → 学习率过高或 GAE lambda 不匹配

3.4 导出策略并回放验证

训练完的 Actor 网络只是"一堆权重",要用起来先导出再回放:

# rsl_rl 内置导出(生成 policy.pt 与 policy.onnx)
from rsl_rl.utils import export_policy_as_onnx
export_policy_as_onnx(agent.actor_critic, path="exported", num_obs=obs_dim)

# 或者用 torch.jit 导出为 TorchScript
traced = torch.jit.trace(actor, example_obs)
traced.save("policy.pt")

回放验证三件事:① 确定性推理下能稳定完成任务(多跑几个随机种子);② 把策略换成"固定频率推理 + 模拟执行器延迟"再测一遍,提前暴露 sim-to-real 风险;③ 检查关节指令是否超限、是否频繁抖动(抖动策略在真机上会烧电机)。

3.5 PPO 目标函数速览

第 4 步提到的 "clip 目标" 具体长这样:

L^CLIP(θ) = E[ min( r(θ)·A ,  clip( r(θ), 1−ε, 1+ε )·A ) ]

其中:
  r(θ) = π_θ(a|s) / π_θ_old(a|s)   ← 新旧策略对同一动作的概率比
  A    = GAE 优势估计               ← 这一拍动作比"平均水平"好多少
  ε    = clip 范围(典型 0.2)        ← 概率比被夹在 [0.8, 1.2]

直觉上:概率比 r(θ) 衡量"这次更新把策略改动了多少",clip 把它强制夹在 1±ε 之间——好动作(A>0)最多把该动作概率放大 1+ε 倍,坏动作(A<0)最多压低到 1−ε 倍,min 项再取两者中较保守的那个。这样单次更新的步幅被硬性限制住,允许 PPO 在同一批数据上多跑几个 epoch 而不会把策略"改飞",这正是它比原始策略梯度稳定的根源。也因此,训练日志里如果看到概率比频繁贴着 0.8/1.2 的上下限,说明学习率偏大或数据已被更新得"太旧"。

3.6 奖励权重数值算例:一拍 reward 怎么算出来的

以最简单的倒立摆为例,奖励 = 存活奖励 − 角度惩罚 − 控制量惩罚:

reward_t = w1·存活项 + w2·角度惩罚项 + w3·控制量惩罚项
         = w1·1  −  w2·|θ|  −  w3·|u|

取权重  w = [w1, w2, w3] = [1.0, 4.0, 0.01]
某一拍: 摆角 θ = 0.2 rad, 输出控制量 u = 2 N

代入:
  存活项   = 1.0 × 1            = +1.00
  角度惩罚 = 4.0  × |0.2|       = −0.80
  控制惩罚 = 0.01 × |2|         = −0.02
  ─────────────────────────────────────
  当拍 reward = 1.0 − 0.8 − 0.02 = 0.18

从这个算例能看出权重的杠杆效应:

调参经验:先让"当拍各项数量级"大致可比(像本例 1.0 / 0.8 / 0.02),再按训练表现微调;改一个权重就重跑一次对照实验,不要一次改三个。

4 经典入门实验设计:三个里程碑

学习 RL 控制人形机器人,建议按"复杂度递增"的顺序做三个实验,每个都自己写一遍观察/动作/奖励设计。下面给出设计要点与收敛提示。

实验观察空间动作空间奖励设计要点收敛提示
① 倒立摆 Swing-up
(单关节,最简)
摆角 θ(建议 sinθ、cosθ 编码)+ 角速度 θ̇ 电机力矩(连续)或 bang-bang(离散) 任务奖励用 cosθ(竖立时最大)+ 角速度惩罚 + 控制量惩罚;episode 在"竖立持续 N 步"或"倒下"时终止 几分钟内收敛;若只停留在"小角度附近"说明能量整形不足,可加"角速度符号"引导先甩起来
② 单腿弹跳
(Hopper 类,多关节+接触)
关节角/角速度 + 基座高度与竖直速度 + 足底接触标志 髋、膝力矩(或目标位置+PD) 高度保持奖励 + 竖直速度平滑 + 姿态(躯干水平)惩罚 + 接触时间正则;初始高度随机化 接触奖励(如落地缓冲惩罚)能让步态更自然;训练不稳先调接触摩擦的域随机化范围
③ H1 站立/行走
(19 自由度人形)
基座线/角速度 + 姿态四元数 + 关节位置/速度 + 指令速度 + 上一动作 各关节目标位置(PD 位置伺服执行) 前进速度跟踪(与指令速度的误差)+ 姿态惩罚 + 关节超限/能耗/动作平滑惩罚;可加分阶段课程 先学"站立不动"(指令速度=0),再学"慢走";reward 量级控制在 1e-2~1e1,权重冲突时逐项消融

以倒立摆为例的奖励伪代码(思路同样适用于 H1 行走):

function reward(state, action, next_state):
    # 1) 任务项:竖立程度(θ=0 为竖立)
    upright = cos(state.theta)
    # 2) 平滑项:动作变化率惩罚,避免抖动
    smooth  = -w1 * (action - last_action)^2
    # 3) 能耗项:控制量惩罚
    effort  = -w2 * action^2
    # 4) 姿态项:角速度惩罚,避免"原地乱甩"拿奖励
    sway    = -w3 * state.theta_dot^2
    return upright + smooth + effort + sway

设计奖励的三条铁律:可解释(每项都能说出物理含义)、可消融(关掉某一项看行为变化)、防 hack(所有"正奖励"都要想清楚策略会不会用怪异方式刷分)。

5 Sim-to-Real 关键技巧:让策略"活"到真机

仿真训练出的策略直接上真机,十有八九会摔——因为仿真与真机之间有"现实差距"(sim-to-real gap)。业界总结出五大类技巧,每一项都是在训练时故意"加难",让策略学会在不确定中保持鲁棒:

技巧一句话解释示例做法
域随机化
(Domain Randomization)
训练时随机改变物理参数,让策略见过"各种世界",部署时自然适应真机参数偏差每个 episode 随机化:足底摩擦 μ∈[0.4, 1.2]、连杆质量 ±30%、关节阻尼 ±50%、地面不平度;Isaac Lab 用 randomization 模块声明
动作延迟注入
(Action Delay)
真机的控制链路有通信与执行延迟,仿真里不模拟,策略就会"对过去的自己"下指令训练时把动作缓冲 1~3 个控制周期再送入环境:action_buffer 队列,每步 pop 旧动作执行
观测噪声
(Observation Noise)
真机传感器(编码器、IMU)有噪声,训练时给观测加噪,策略才不会"过度依赖"精确值关节角加 N(0, 0.01 rad)、角速度加 N(0, 0.1 rad/s)、IMU 姿态加随机游走噪声
执行器模型
(Actuator Model)
真机电机不是"指令即达",有带宽、力矩限幅、摩擦;把执行器特性建模进仿真用一阶/二阶低通模拟电机响应:target 经 τ·dθ/dt + θ = cmd 平滑;同时加力矩饱和(如 ±扭矩)与死区
零力矩点约束
(ZMP Constraint)
行走稳定性的物理本质:支撑反力合力作用点(ZMP)必须落在支撑多边形内;策略学坏会"边走边滑"训练中实时计算 ZMP(由足底接触力合成),奖励里加"ZMP 到支撑多边形边缘距离"的惩罚项,防止翻倒步态
💡 工程经验:上述技巧建议分阶段开启——先用"理想环境"把策略训到能走,再逐步加入噪声、延迟、域随机化(即课程化 randomization),每加一项重测训练曲线。一次全开会导致任务过难、训练不收敛。

6 硬件在环与部署链路:从策略到关节指令

训练完成只是开始,策略最终要跑在真机的控制回路里。典型链路:训练框架(Python)导出 → 部署端加载推理 → 生成关节指令 → 发给关节模组执行。人形机器人控制频率常见 50~200 Hz,推理延迟必须远小于控制周期。

1导出 ONNX
把 Actor 网络导出为 ONNX 或 TorchScript,脱离 PyTorch 训练栈
2部署端推理
上位机(C++/Rust/Python)用 ONNX Runtime 等加载模型,每周期推理一次得到目标关节角
3指令下发给关节
把目标位置+PD 参数打包,经 CAN/FDCAN 等总线发给各关节模组(衔接 04-02 固件层)
4状态回传
关节模组把实际位置/速度/电流回传,构成"推理 → 执行 → 反馈"闭环
5落吊测试
真机先用吊架支撑,从站立、慢走到快走逐级验证,监控关节温度与电流

C++ 端 ONNX Runtime 推理的核心代码(仅示意关键步骤):

#include <onnxruntime/core/session/onnxruntime_cxx_api.h>

Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "rl-policy");
Ort::Session session(env, "policy.onnx",
                     Ort::SessionOptions{nullptr});

// 观测向量:基座姿态/速度 + 关节位置/速度 + 指令
std::vector<float> obs = build_observation(imu, encoders, cmd);
std::array<int64_t, 2> shape{1, OBS_DIM};
Ort::Value in = Ort::Value::CreateTensor<float>(
    Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault),
    obs.data(), obs.size(), shape.data(), shape.size());

// 推理得到目标关节角
auto out = session.Run(Ort::RunOptions{nullptr},
                       {"obs"}, &in, 1, {"action"}, 1);
float* action = out[0].GetTensorMutableData<float>();

// 打包为关节指令,经 CAN 总线下发(与 04-02 固件层衔接)
for (int j = 0; j < NUM_JOINTS; j++) {
    send_joint_command(j, action[j], kp, kd, torque_limit);
}
⚠️ 部署侧三个易错点: 1. 观测对齐:训练时的观测顺序/单位(弧度 vs 度、速度是否归一化)必须与部署端完全一致,否则策略"看不懂"输入;
2. 关节顺序映射:RL 关节顺序 ≠ 电机 CAN ID 顺序,必须显式建立映射表(Unitree 的 H1 尤其如此,电机顺序与 RL 关节顺序不一致);
3. PD 参数一致性:训练时仿真里的 PD 增益要与真机关节模组固件配置一致,否则动作变形——这正是本页 04-02 固件层(FOC 三环)与训练配置要对齐的原因。

7 常见坑与避坑指南

🕳️ 仿真与训练五大坑:
1. 仿真时间步长不当:步长过大 → 接触穿透、能量发散、训练"爆 NaN";步长过小 → 训练极慢。建议物理步长 1~5 ms,控制周期 20~50 Hz(如 2 ms 物理步长 × 每 10 步给一次动作)。
2. 接触参数瞎调:刚度/阻尼/摩擦系数拍脑袋改,导致机器人"陷进地面"或"溜冰"。先从仿真器默认值起步,只调与真机明显不符的参数(如足底摩擦)。
3. 奖励 hack:策略找到奖励函数漏洞——例如原地抖腿拿"速度奖励"、故意摔倒重置来刷分。对策:加动作平滑/能耗惩罚、限制动作范围、检查 episode 长度与熵曲线、对可疑行为做消融。
4. 训练不收敛的排查顺序:① 环境冒烟测试(随机动作能否跑通、无 NaN)→ ② 检查观测是否归一化(量级差异过大会让网络学不动)→ ③ 检查奖励量级(目标项应在 1e-2~1e1)→ ④ 调超参数(学习率、clip、batch、GAE λ)→ ⑤ 看熵/价值曲线判断是"没学会"还是"过拟合"。
5. 模型与单位不一致:URDF 惯性缺失、质量单位 kg 与 g 混用、角度 rad 与 deg 混用——这类低级错误会让"看起来正常"的仿真在真机上完全失效,建议写单元测试核对模型总质量与关节限位。
📌 本节小结:仿真器选型要匹配任务:科研与快速原型用 MuJoCo(Google DeepMind 开源、3.x 版本、原生支持 URDF 与 Menagerie 模型库),大规模并行 RL 用 Isaac Lab(Isaac Gym 的继任者,PhysX GPU 引擎,自带 H1 任务),ROS 整机联调用 Gazebo;RL 流水线的核心是"观察/动作/奖励"三件套 + PPO 六步流程,训练曲线重点看 reward、episode length、entropy、critic loss;经典实验按"倒立摆 → 单腿弹跳 → H1 行走"递进;sim-to-real 用域随机化、动作延迟注入、观测噪声、执行器模型、ZMP 约束五大技巧缩小差距;部署链路是"训练导出 ONNX → 部署端推理 → 关节指令经总线下发 → 状态回传闭环",并与 04-02 固件层的 FOC 三环与 PD 参数严格对齐。
🤔 思考题: 1. 如果只允许你在"观测噪声"和"动作延迟注入"中选一个先做 sim-to-real,你会选哪个?为什么?
2. 你的策略在仿真里"原地踏步"刷到了很高的速度跟踪奖励,但根本不前进——你会怎么改奖励函数?列出至少两种方案并说明各自代价。
3. 为什么大规模 RL 训练要用 GPU 并行几千个环境,而不是像传统仿真那样一次跑一个?瓶颈在哪里?
4. 假设真机关节模组的 CAN 通信周期是 1 ms,而策略推理要 5 ms——你的控制架构该怎么设计(状态预估、缓冲、多线程)?与 04-02 固件层的三环结构如何配合?

9 本节自测

1. 关于 MuJoCo,下列说法正确的是?
💡 解析:MuJoCo 由 DeepMind(现 Google DeepMind)在 2021 年收购后开源,采用 Apache 2.0 协议,3.x 版本可直接 MjModel.from_xml_path() 加载 URDF;它的原生模型格式是 MJCF(XML),主要运行在 CPU 上(GPU 并行需借助 mjx)。
2. Isaac Lab 与 Isaac Gym 的关系是?
💡 解析:Isaac Gym 停止维护后,其能力并入 Isaac Lab。Isaac Lab 构建在 Isaac Sim(Omniverse)之上,用 PhysX GPU 引擎并行运行数千个环境,并内置 rsl_rl 等 PPO 实现与 H1 等任务;legged-gym 这类基于 Isaac Gym 的项目也纷纷迁移(如 unitree_rl_lab)。
3. PPO 训练"不收敛"时,正确的排查顺序是?
💡 解析:排查顺序应是"环境本身没问题 → 输入没问题 → 奖励没问题 → 再动超参数"。环境里有 NaN 或观测量级差 1e6 时,再怎么调学习率都不会收敛;直接调超参数或删惩罚项属于盲猜,容易掩盖真正原因。
4. 下列哪一项不是缩小 sim-to-real 差距的常用技巧?
💡 解析:sim-to-real 的核心思想是"在训练时主动加入不确定性",让策略学会鲁棒——A(域随机化)、B(延迟注入)、D(执行器模型)都是标准做法;而"追求理想仿真"会让策略过度依赖精确信息,部署到有噪声、有延迟的真机上必然失败。
5. 在 MuJoCo 里,model(MjModel)与 data(MjData)两个对象的分工是?
💡 解析:见 2.4 小节。MjModel 是编译后的静态模型(质量、惯量、关节约束、执行器参数),MjData 是运行时状态(qpos 位置、qvel 速度、ctrl 控制量、接触力等);RL 训练时读 data.qpos/qvel 组成观测、写 data.ctrl 作为动作,再 mj_step 推进。
6. 想在笔记本上快速验证算法思路、不追求大规模并行,按本页选型口诀应优先选?
💡 解析:见 1 节选型口诀——科研验证/快速原型/CPU 环境选 MuJoCo;大规模并行 RL 训练选 Isaac Lab(需 NVIDIA GPU);ROS 传感器级整机仿真选 Gazebo;「只要快速验证算法思路、笔记本也能跑」选 PyBullet;接触密集、稳定性敏感的研究选 RaiSim。
7. 关于策略部署到真机关节模组,下列说法错误的是?
💡 解析:见 6 节部署侧三个易错点。RL 关节顺序 ≠ 电机 CAN ID 顺序,必须显式建立映射表(Unitree H1 尤其如此),所以 B 错误;A(观测对齐)、C(PD 参数一致)、D(控制频率与延迟)都是页面明确列出的正确要求。
📌 本节要点速查:① 选型:科研/快速原型用 MuJoCo(DeepMind 开源、原生支持 URDF 与 Menagerie),大规模并行 RL 用 Isaac Lab(PhysX GPU、Isaac Gym 继任者),ROS 联调用 Gazebo,快速验证算法用 PyBullet,接触密集研究用 RaiSim;② MuJoCo 三件套:MJCF 四概念 asset/body/joint/actuator,model=静态图纸、data=运行时状态,读 qpos/qvel 作观测、写 ctrl 作动作、mj_step 推进;③ RL 流水线:观察/动作/奖励三件套 + PPO 六步(环境→采样→GAE→clip 更新→评估→导出 ONNX);④ 训练曲线看趋势:reward 上升、episode length 增长、entropy 缓降、critic loss 收敛;⑤ sim-to-real 五技巧:域随机化、动作延迟注入、观测噪声、执行器模型、ZMP 约束;⑥ 部署对齐:观测顺序/单位一致、关节顺序≠CAN ID 需建映射表、PD 增益与 04-02 固件一致。

10 参考来源