Unitree_RL_Gym 项目(2):Deploy 项目模块深度技术解析

本文是对宇树科技 deploy 部署模块的全面技术拆解,涵盖 MuJoCo 仿真部署、实物机器人部署(Python/C++ 双版本)、DDS 通信协议、双缓冲架构、PD 控制器以及完整的传感器到执行器闭环。


一、模块概述

1.1 定位与作用

deploy 模块是宇树 unitree_rl_gym 项目的策略部署层,负责将 legged_gym 中训练好的强化学习策略部署到仿真环境或实物机器人上运行。

1.2 目录结构

deploy/
├── deploy_mujoco/                      # MuJoCo 仿真部署
│   ├── deploy_mujoco.py                # MuJoCo 仿真运行脚本
│   └── configs/                        # 各机器人配置
│       ├── g1.yaml                     # G1 配置(12DoF)
│       ├── h1.yaml                     # H1 配置(10DoF)
│       └── h1_2.yaml                   # H1_2 配置(12DoF)
├── deploy_real/                        # 实物机器人部署
│   ├── deploy_real.py                  # Python 版控制器
│   ├── config.py                       # 配置解析模块
│   ├── configs/                        # 各机器人配置文件
│   │   ├── g1.yaml
│   │   ├── h1.yaml
│   │   └── h1_2.yaml
│   ├── common/                         # 公共服务模块
│   │   ├── command_helper.py           # 电机命令构建(HG/GO)
│   │   ├── rotation_helper.py          # IMU 姿态转换
│   │   └── remote_controller.py        # 遥控器数据解析
│   └── cpp_g1/                         # C++ 高性能部署
│       ├── main.cpp                    # 入口函数
│       ├── Controller.h / Controller.cpp  # 控制器核心
│       ├── utilities.h / utilities.cpp    # 工具函数
│       ├── AtomicLock.h                # 原子自旋锁
│       ├── DataBuffer.h                # 双缓冲机制
│       ├── joystick.h                  # 遥控器解析
│       └── CMakeLists.txt              # 构建配置
└── pre_train/                          # 预训练策略模型
    ├── g1/motion.pt
    ├── h1/motion.pt
    └── h1_2/motion.pt

1.3 部署链路全景

┌──────────────────────────────────────────────────────────────────┐
│                        训练阶段 (legged_gym)                       │
│  Isaac Gym → PPO 训练 → Actor-Critic 网络 → 导出 JIT/ONNX 模型    │
└──────────────────────────────┬───────────────────────────────────┘
                               │
                               ▼
┌──────────────────────────────────────────────────────────────────┐
│                   策略模型 (motion.pt / .onnx)                      │
└──────────────────────────────┬───────────────────────────────────┘
                               │
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│  MuJoCo 仿真部署  │  │ Python 实物部署   │  │ C++ 实物部署      │
│  deploy_mujoco   │  │  deploy_real     │  │  cpp_g1          │
│  · PyTorch JIT   │  │  · PyTorch JIT   │  │  · ONNX Runtime  │
│  · 单线程         │  │  · DDS 通信      │  │  · DDS 通信      │
│  · 无实物依赖      │  │  · Unitree SDK   │  │  · 双线程模型     │
└──────────────────┘  └──────────────────┘  └──────────────────┘

二、MuJoCo 仿真部署(deploy_mujoco.py)

2.1 模块职责

deploy_mujoco.py 是一个轻量级的策略验证脚本,使用 MuJoCo 物理引擎替代 Isaac Gym 进行训练后策略的离线验证。相比 Isaac Gym,MuJoCo 的优势在于:

  • 免费开源:无需 NVIDIA GPU
  • 安装简便pip install mujoco 即可
  • 快速验证:单环境仿真,渲染流畅,适合演示和调试

2.2 核心流程

加载 YAML 配置文件
    ↓
加载 MuJoCo 机器人模型 (XML)
    ↓
加载 TorchScript JIT 策略 (policy.pt)
    ↓
创建 MuJoCo Viewer(交互式可视化窗口)
    ↓
┌─ 仿真循环 ─────────────────────────────────────┐
│  循环每 simulation_dt 秒执行:                     │
│    1. PD 控制器 → 计算力矩 τ = Kp*(θt-θ) - Kd*θ̇  │
│    2. mj_step() → 推进物理一步                     │
│    3. 每 control_decimation 步执行策略推理:        │
│       · 构建观测向量(47/41维)                     │
│       · TorchScript 推理                           │
│       · action → θ_target = θ_default + a*scale   │
│    4. viewer.sync() → 渲染并处理输入                │
└────────────────────────────────────────────────┘

2.3 关键函数

PD 控制器
def pd_control(target_q, q, kp, target_dq, dq, kd):
    return (target_q - q) * kp + (target_dq - dq) * kd
重力方向计算
def get_gravity_orientation(quaternion):
    # 四元数 [qw, qx, qy, qz] → 重力投影 [gx, gy, gz]
    gx = 2 * (-qz * qx + qw * qy)
    gy = -2 * (qz * qy + qw * qx)
    gz = 1 - 2 * (qw * qw + qz * qz)
观测向量构建

观测向量与训练时保持完全一致(47维示例):

维度范围 内容 预处理
[0:3] 角速度 ω × ang_vel_scale
[3:6] 重力投影 直接使用
[6:9] 速度指令 cmd × cmd_scale
[9:21] 关节位置偏差 (q - q_default) × dof_pos_scale
[21:33] 关节速度 dq × dof_vel_scale
[33:45] 上一步动作 直接使用
[45:47] 步态相位 [sin(2πφ), cos(2πφ)]

2.4 MuJoCo 配置文件对比

参数 G1 H1 H1_2
策略频率 50Hz 50Hz 50Hz
仿真步长 0.002s 0.002s 0.002s
decimation 10 10 10
Kp (膝关节) 150 200 300
Kd (膝关节) 4 4 4
默认蹲姿 0.3 rad 0.3 rad 0.36 rad

三、实物机器人部署 — Python 版(deploy_real/)

3.1 模块架构

deploy_real/
├── deploy_real.py          # 控制器主程序 (Controller 类)
├── config.py               # Config 类: YAML → 配置对象
├── configs/*.yaml          # 各机器人配置文件
└── common/
    ├── command_helper.py    # 电机命令构建 (HG/GO 两种协议)
    ├── rotation_helper.py   # IMU 姿态坐标变换
    └── remote_controller.py # 遥控器字节流解析

3.2 Controller 类核心流程

class Controller:
    def __init__():    # 加载 YAML 配置 + JIT 策略 + 初始化 DDS
    def init_lowcmd(): # 创建零力矩/阻尼命令
    def run():         # 主循环: 传感器 → 观测 → 策略 → PD → 电机
    def low_state_update(): # 接收 LowState DDS 消息

主循环控制时序:

每隔 control_dt/2 秒执行一次:
    ↓
low_state_update()           # 接收 DDS LowState 消息
    ↓
remote_ctrl.set()            # 解析遥控器按键/摇杆
    ↓
按键处理:
  · L2+R2   → 切换 行走/站立 模式
  · start   → 紧急停止
  · select  → 阻尼模式
  · up/down → 增减期望速度
    ↓
摇杆 → 速度指令:
  · ly (左Y) → X 方向速度
  · lx (左X) → Y 方向速度
  · rx (右X) → Yaw 角速度
    ↓
状态预处理:
  · IMU 四元数 → 重力投影
  · IMU 四元数 → 角速度 (如需 torso→pelvis 转换)
  · 关节位置/速度 → 归一化
    ↓
观测构建 (47维):
  [ω, g, cmd, Δq, dq, a_prev, sinφ, cosφ]
    ↓
JIT 策略推理 → action (12/10维)
    ↓
θ_target = θ_default + action × action_scale
    ↓
电机命令编码:
  · HG: LowCmdHG (G1/H1_2)
  · GO: LowCmdGo (H1)
  · 手臂/腰部电机: 固定目标位置 (不参与RL)
    ↓
run_lowcmd() → DDS写入 → 电机执行

3.3 电机命令辅助模块(command_helper.py)

本项目需要处理两种 Unitree DDS 消息格式:

消息类型 使用机器人 电机模式编码 特点
HG G1, H1_2 mode=1 (使能) 包含 mode_machine / mode_pr
GO H1 mode=0x0A (强电机) 含帧头 0xFE 0xEF、GPIO、弱电机列表

四种命令模式:

模式 Kp Kd τ 用途
零力矩 0 0 0 初始化、关节松弛
阻尼 0 8 0 安全停机、缓慢下垂
运行 (HG) 配置值 配置值 0 正常运行,由 PD 控制器产生力矩
运行 (GO) 配置值 配置值 0 同上

3.4 IMU 姿态坐标变换(rotation_helper.py)

H1 和 H1_2 机器人的 IMU 安装在**躯干(torso)**上,而非骨盆(pelvis)。训练时的观测空间基准是骨盆坐标系,因此需要坐标变换。

变换算法:

输入: waist_yaw (腰部角度), waist_yaw_omega (腰部角速度),
      imu_quat (躯干IMU四元数), imu_omega (躯干IMU角速度)

步骤1: 构建腰部Z轴旋转矩阵 RzWaist = R_z(waist_yaw)
步骤2: IMU四元数 → 躯干旋转矩阵 R_torso
步骤3: 骨盆旋转矩阵 R_pelvis = R_torso · RzWaist^T
步骤4: 骨盆角速度 ω_pelvis = RzWaist · ω_imu - [0, 0, ω_waist]

输出: pelvis_quat (骨盆四元数 [w,x,y,z]), pelvis_omega (骨盆角速度)

G1 的 IMU 在骨盆上,imu_type="pelvis",直接使用,无需变换。

3.5 遥控器解析(remote_controller.py)

宇树遥控器的 wireless_remote 字段是一个 24 字节的原始字节流:

字节偏移 类型 内容
[2:4] uint16 16位按键掩码(每bit对应一个按键)
[4:8] float 左摇杆 X (lx)
[8:12] float 右摇杆 X (rx)
[12:16] float 右摇杆 Y (ry)
[20:24] float 左摇杆 Y (ly)

按键位掩码映射(KeyMap):

按键 按键 按键 按键
0 R1 4 R2 8 A 12
1 L1 5 L2 9 B 13
2 start 6 F1 10 X 14
3 select 7 F2 11 Y 15

3.6 配置文件结构

每个机器人有一个 YAML 文件,关键字段对比如下:

字段 G1 H1 H1_2
msg_type hg go hg
imu_type pelvis torso torso
num_actions 12 10 12
num_obs 47 41 47
Kp (膝关节) 150 200 300
leg_joint2motor_idx [0…11] [4,5,0,1,2,9,10,3,6,7] [0…11]

H1 的 leg_joint2motor_idx 最复杂:电机顺序与 RL 关节顺序不匹配,需要显式映射。索引 4 是 waist_yaw 关节,同时用于 IMU 变换和观测构建。


四、实物机器人部署 — C++ 版(cpp_g1/)

4.1 设计动机

Python 部署方案虽然开发效率高,但在以下场景受限:

  • 实时性要求:DDS 通信 + 策略推理 + PD 控制的 Python 回路延迟约 5-10ms,C++ 可压至 1-2ms
  • 资源占用:Python 解释器和 PyTorch 运行时占用大量内存
  • 部署简化:C++ 编译为独立可执行文件,无需 Python 环境
  • 推理加速:使用 ONNX Runtime 替代 PyTorch JIT,推理延迟更低

4.2 架构设计:双线程模型

Python 版是单线程(一个线程处理 DDS 收发包 + 策略推理),C++ 版升级为双线程

┌──────────────────────┐         ┌──────────────────────┐
│    主控制线程          │         │    推理线程            │
│  (Main Thread)        │ 双缓冲   │  (Inference Thread)   │
│                        │◄═══════▶│                       │
│  · DDS LowState 接收   │ DataBuff│  · ONNX Runtime      │
│  · 遥控器解析          │ er[2][N]│  · 观测 → 策略 → 动作  │
│  · 观测构建            │         │  · 目标位置计算        │
│  · PD 控制             │         │                       │
│  · DDS LowCmd 发送     │         │                       │
└──────────────────────┘         └──────────────────────┘

同步机制:

  1. 主线程构建观测 → 写入 _obsBuffer.active → swapBuffers()
  2. 推理线程读取 _obsBuffer.nonActive → ONNX 推理
  3. 推理线程写入 _actionBuffer.active / _qpBuffer.active → swapBuffers()
  4. 主线程读取 _qpBuffer.nonActive → PD 控制 → 发送电机命令

4.3 双缓冲机制(DataBuffer.h)

template <class T, int NUM_BUFFER, int NUM_ELEMENTS>
class DataBuffer {
    T* getActive();           // 获取写入缓冲区
    T* getNonActive();        // 获取读取缓冲区
    void swapBuffers();       // 交换活跃/非活跃索引
    void lockSwap();          // 加锁
    void unlockSwap();        // 解锁
};

工作原理:

  • 维护两个缓冲区 _buffer[0]_buffer[1]
  • _activeIdx 指向当前正在写入的缓冲区
  • swapBuffers() 切换索引,使写入者切换到另一个缓冲区,读取者获得最新数据
  • AtomicLock 保护 swap 操作的原子性

4.4 原子锁(AtomicLock.h)

class AtomicLock {
    void lock() {
        while (_lock.test_and_set(std::memory_order_acquire)) {}
    }
    void unlock() {
        _lock.clear(std::memory_order_release);
    }
    std::atomic_flag _lock;
};

使用 C++11 std::atomic_flag 实现无锁自旋锁:

  • test_and_set():原子地设为 true 并返回旧值
  • 若旧值为 true(已被锁定),忙等直到解锁
  • 适用于锁持有时间极短(< 100ns)的场景

4.5 策略推理:PyTorch JIT → ONNX Runtime

对比项 PyTorch JIT ONNX Runtime
Python 依赖 需要 PyTorch 不需要
C++ 集成 需要 libtorch 只需 onnxruntime
推理延迟 ~2-5ms ~1-3ms
内存占用 ~500MB ~50MB
模型转换 直接 export torch.onnx.export()

4.6 编译与运行

# 编译
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

# 运行
./cpp_g1 config.json

依赖:

  • Unitree SDK (unitree_sdk2):DDS 通信、LowState/LowCmd 消息
  • ONNX Runtime:策略模型推理
  • Eigen3:矩阵/四元数运算

五、各文件功能详解与注释意义

5.1 deploy_mujoco/deploy_mujoco.py

项目 说明
功能 MuJoCo 仿真部署脚本,替代 Isaac Gym 进行离线策略验证
核心类 无(函数式脚本)
关键函数 get_gravity_orientation(), pd_control()
注释意义 解释 PD 控制律、四元数到重力投影的公式推导、观测向量维度映射

5.2 deploy_real/deploy_real.py

项目 说明
功能 实物机器人 Python 部署控制器
核心类 Controller
关键方法 run() → 主控制循环,low_state_update() → 状态读取
注释意义 完整的传感器→执行器闭环流程说明、遥控器操作映射、异常恢复机制

5.3 deploy_real/config.py

项目 说明
功能 YAML 配置文件解析
核心类 Config
注释意义 逐一说明每个配置字段的物理含义和使用场景

5.4 deploy_real/common/command_helper.py

项目 说明
功能 电机命令构建(HG/GO 双协议支持)
关键函数 init_cmd_hg(), init_cmd_go(), create_damping_cmd()
注释意义 解释 HG 和 GO 两种消息格式的差异、电机模式编码规则

5.5 deploy_real/common/rotation_helper.py

项目 说明
功能 IMU 姿态坐标变换
关键函数 get_gravity_orientation(), transform_imu_data()
注释意义 完整推导重力投影的数学公式、躯干→骨盆坐标变换的旋转矩阵运算

5.6 deploy_real/common/remote_controller.py

项目 说明
功能 宇树遥控器原始字节流解析
核心类 RemoteController, KeyMap
注释意义 遥控器 24 字节数据布局、16 个按键位掩码映射、摇杆到指令的对应关系

5.7 cpp_g1/Controller.h / Controller.cpp

项目 说明
功能 C++ 控制器核心实现
核心类 RobotController
注释意义 双线程模型的设计动机、双缓冲数据流、ONNX Runtime 推理集成

5.8 cpp_g1/utilities.h / utilities.cpp

项目 说明
功能 重力投影和 PD 控制的 C++ 实现
注释意义 与 Python 版本的函数对应关系、Eigen 库的四元数/向量运算

5.9 cpp_g1/DataBuffer.h

项目 说明
功能 通用双缓冲模板类
注释意义 双缓冲读写的线程安全模型、使用场景说明

5.10 cpp_g1/AtomicLock.h

项目 说明
功能 C++11 原子自旋锁
注释意义 std::atomic_flag 的 test_and_set 语义、适用场景(短锁持有时长)

5.11 cpp_g1/joystick.h

项目 说明
功能 遥控器指令解析(C++ 版)
注释意义 与 Python remote_controller.py 的功能对应

5.12 cpp_g1/main.cpp

项目 说明
功能 C++ 部署程序入口
注释意义 生命周期管理(Init→Start→Run→Stop→Join)

5.13 cpp_g1/CMakeLists.txt

项目 说明
功能 CMake 构建配置
注释意义 说明三个依赖库的作用、构建命令、编译选项

5.14 YAML 配置文件(6个)

项目 说明
功能 为每种机器人定义部署参数
注释意义 每个字段的物理含义、单位、取值范围、设计意图

六、关键设计模式与技术决策

6.1 双缓冲线程解耦

问题:策略推理的输入是观测,输出是动作。如果主线程等待推理完成再发送命令,会导致控制回路延迟增加。

方案:双缓冲异步解耦

  • 主线程:构建观测 → 写入 Buffer[0] → swap → 读取 Buffer[0]’ 中的上次动作 → PD → 发送命令
  • 推理线程:读取 Buffer[0] 中的观测 → 推理 → 写入 Buffer[1] 动作 → swap

优势:

  • 推理延迟不直接累加到控制延迟上
  • 推理可以比控制快/慢而不影响系统稳定性
  • 允许使用更复杂的策略网络而不牺牲控制频率

6.2 双臂统一部署架构

G1 和 H1_2 共享 HG 消息协议,H1 使用 GO 消息协议。command_helper.py 通过统一的 API 抽象了协议差异:

if msg_type == "hg":
    cmd.motor_cmd[i].mode = 1          # HG: mode=1 (使能)
elif msg_type == "go":
    cmd.motor_cmd[i].mode = 0x0A       # GO: mode=0x0A (强电机)

6.3 IMU 类型适配

G1 的 IMU 在骨盆 → 直接使用 → imu_type="pelvis"
H1/H1_2 的 IMU 在躯干 → 需要 transform_imu_data()imu_type="torso"

这种设计允许同一套 Controller 代码适配不同机器人,仅需修改 YAML 配置文件。

6.4 观测空间与训练一致性

部署时必须保证观测空间的构建方式与训练时完全一致

  • 相同的维度顺序
  • 相同的归一化缩放系数
  • 相同的步态相位编码(sin/cos)
  • 相同的关节位置偏差计算(q - q_default)

任何差异都会导致 domain gap,使策略在实物上性能大幅下降。


七、快速上手

7.1 MuJoCo 仿真部署

pip install mujoco torch

# G1 仿真
python deploy/deploy_mujoco/deploy_mujoco.py g1.yaml
# H1_2 仿真
python deploy/deploy_mujoco/deploy_mujoco.py h1_2.yaml

7.2 Python 实物部署

# 安装 Unitree SDK
pip install unitree_sdk2py

# 部署 G1
export DEPLOY_CONFIG_DIR=$(pwd)/deploy/deploy_real
python deploy/deploy_real/deploy_real.py --robot g1

7.3 C++ 实物部署

# 编译
cd deploy/deploy_real/cpp_g1
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

# 运行
./cpp_g1 ../../configs/g1.json

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐