# 强化学习运控策略 Sim-to-Real 全链路实操：从 Isaac Lab / Legged Gym 训练导出 ONNX 到真机 500Hz C++ 闭环

> 原文链接：https://robotics.hellobike.com/tutorials/rl-locomotion-isaaclab-onnx-sim2real-guide
> 频道：tutorials | 更新日期：2026-10-21 | 标签：Sim2Real, Isaac Lab, ONNX, 强化学习, 步态控制, 四足机器人, 人形机器人
> 核心摘要：拆解四足与双足人形机器人 Sim-to-Real 部署中最关键的 6 个对齐环节：观测向量归一化、关节索引映射、四元数重力投影、PD 刚度转换与真机安全保护看门狗。

# 强化学习运控策略 Sim-to-Real 全链路实操：从 Isaac Lab 导出 ONNX 到真机 500Hz 闭环

在 Isaac Lab 或 Isaac Gym 中训练出一个能走能跑的 Actor-Critic 策略只需几十分钟，但把 `.pt` 权重搬上真机却常常遇到**「一上电就劈叉、起立后疯狂抽搐、或者一迈步就后仰摔倒」**。本文总结从仿真到真机（Sim-to-Real）必须严丝合缝对齐的 6 个工程契约。

---

## 1. Sim-to-Real 双频控制架构

```text
[50Hz 策略推理线程 (ONNX Runtime / LibTorch, 周期 20ms)]
  输入：观测向量 obs_t (角速度、重力投影、速度指令、关节位置偏差、关节速度、上一帧动作)
  输出：归一化动作向量 action_t ∈ [-clip_actions, +clip_actions]
  目标角度计算：q_target = q_default + action_scale * action_t
         │ (无锁环形缓冲区 / 原子指针交换)
         ▼
[500Hz–1000Hz 实时关节伺服线程 (DDS / EtherCAT / CAN, 周期 1–2ms)]
  底层电机驱动器执行：tau = kp * (q_target - q) + kd * (0 - dq) + tau_ff
```

---

## 2. 上真机前必须逐项核对的 6 张对齐表

### 2.1 四元数顺序与重力投影向量（`projected_gravity`）
- **仿真器差异**：Isaac Gym 使用 `(x, y, z, w)`，而 Isaac Lab、MuJoCo 以及宇树/智身底层 IMU 消息通常使用 **`(w, x, y, z)`**。如果四元数实部 `w` 位置搞反，算出的重力投影向量会旋转 180°，机器人在真机上一激活就会猛烈翻滚！
- **标准重力投影计算公式**（输入单位四元数 `q = [w, x, y, z]`）：
  ```python
  import numpy as np

  def quat_rotate_inverse(q_wxyz: np.ndarray, v: np.ndarray) -> np.ndarray:
      q_w = q_wxyz[0]
      q_vec = q_wxyz[1:4]
      a = v * (2.0 * q_w**2 - 1.0)
      b = np.cross(q_vec, v) * q_w * 2.0
      c = q_vec * np.dot(q_vec, v) * 2.0
      return a - b + c

  # 静止水平放置时，projected_gravity 必须近似为 [0.0, 0.0, -1.0]
  projected_gravity = quat_rotate_inverse(imu_quat_wxyz, np.array([0.0, 0.0, -1.0]))
  ```

### 2.2 关节索引双向映射表（`sim2real_idx` 与 `real2sim_idx`）
仿真器的关节顺序由 URDF/USD 解析树决定，而真机 SDK 的电机数组常按控制器硬件通道排列。必须显式定义两个映射数组：
```python
## 从真机传感器读入后，重排为仿真顺序再构建 obs
q_sim = q_real[real2sim_idx]
dq_sim = dq_real[real2sim_idx]

## 策略算出仿真顺序的 target_q_sim 后，重排回真机顺序再下发
target_q_real = target_q_sim[sim2real_idx]
```

### 2.3 观测缩放因子（`obs_scales`）与历史帧栈（History Buffer）
训练配置中的每一项缩放系数必须在真机 C++/Python 代码里 1:1 复刻：
- `ang_vel * 0.25`
- `dof_pos_err = (q_sim - default_dof_pos) * 1.0`
- `dof_vel * 0.05`
- 若策略使用了 `frame_stack = 5` 或特权估计器（Estimator），切入 RL 模式前必须先用当前静止状态填充全部历史帧缓冲区，禁止用全 0 历史帧直接推理第一拍。

---

## 3. Sim-to-Sim 交叉验证（MuJoCo 守门员机制）

**严禁把 Isaac Lab 导出的 ONNX 直接拿到真机上试错！** 标准工程流程是先过 **Sim-to-Sim（Isaac Lab → MuJoCo）** 关口：
1. 将导出的 `.onnx` 策略加载到独立的 MuJoCo Python/C++ 脚本中；
2. 如果策略在 MuJoCo 中能稳定站立并响应 `vx, vy, wz` 指令，说明你的 **ONNX 导出图、观测拼装顺序、四元数约定、默认关节角 `default_dof_pos` 与 `kp/kd`** 已经 100% 正确；
3. 随后只需替换底层的 `read_sensors()` 与 `write_actuators()` 为真机 SDK 接口，并在真机推理环中加入 **倾角超限保护（`|roll| > 45°` 或 `|pitch| > 45°` 立即切回全阻尼模式 `Damp()`）** 即可安全落地。

## 原始参考来源 (Sources)

- [GitHub isaac-sim/IsaacLab](https://github.com/isaac-sim/IsaacLab)
- [GitHub unitreerobotics/unitree_rl_gym](https://github.com/unitreerobotics/unitree_rl_gym)
- [GitHub DeepRoboticsLab/Lite3_rl_training](https://github.com/DeepRoboticsLab/Lite3_rl_training)