Unitree_RL_Gym项目(1): Legged Gym 项目深度技术解析

Unitree_RL_Gym项目(1): Legged Gym 项目深度技术解析

本文是对宇树科技(Unitree)开源足式机器人强化学习训练框架 legged_gym 的全面技术拆解,涵盖项目架构、核心算法、数学公式、奖励工程与使用实践。


一、项目概述

1.1 背景与定位

legged_gym 是宇树科技基于 NVIDIA Isaac Gym GPU 并行物理仿真引擎构建的足式机器人强化学习(RL)训练框架。它专为四足与双足机器人的端到端步态控制策略训练而设计,采用 PPO(Proximal Policy Optimization) 算法,在单个 GPU 上可同时并行仿真数千个环境(默认 4096 个),极大地加速了策略收敛。

1.2 核心特性

特性 说明
多机器人支持 GO2(四足 12DoF)、G1(双足 12DoF)、H1(双足 10DoF)、H1_2(双足 12DoF)
GPU 并行仿真 基于 Isaac Gym,4096 环境并行,物理计算完全在 GPU 上执行
非对称 Actor-Critic Actor 仅接收本体可观测信息;Critic 接收包含本体线速度的特权观测,提升样本效率
LSTM 循环策略 G1/H1/H1_2 使用时序策略网络,利用历史观测信息
域随机化(Domain Randomization) 摩擦力、基座质量、随机推力扰动,增强策略泛化能力
观测噪声注入 模拟真实传感器噪声,提升部署鲁棒性
程序化地形生成 支持斜坡、楼梯、离散障碍、踏步石、缝隙、坑洞等 7 种地形,支持课程学习

二、项目目录结构

复制代码
legged_gym/
├── __init__.py                    # 包初始化,定义根目录常量
├── envs/
│   ├── __init__.py                # 环境注册(注册所有机器人任务)
│   ├── base/
│   │   ├── base_config.py         # 配置基类(递归实例化机制)
│   │   ├── base_task.py           # 任务基类(仿真生命周期管理)
│   │   ├── legged_robot.py        # 足式机器人核心环境(奖励/观测/控制)
│   │   └── legged_robot_config.py # 足式机器人默认配置(含 PPO 配置)
│   ├── g1/
│   │   ├── g1_config.py           # G1 双足机器人配置
│   │   └── g1_env.py              # G1 双足机器人环境(步态相位 + 足部追踪)
│   ├── go2/
│   │   └── go2_config.py          # GO2 四足机器人配置(使用通用环境类)
│   ├── h1/
│   │   ├── h1_config.py           # H1 双足机器人配置
│   │   └── h1_env.py              # H1 双足机器人环境
│   └── h1_2/
│       ├── h1_2_config.py         # H1_2 双足机器人配置
│       └── h1_2_env.py            # H1_2 双足机器人环境
├── scripts/
│   ├── train.py                   # 训练入口脚本
│   └── play.py                    # 推理/测试入口脚本
└── utils/
    ├── __init__.py                # 工具模块导出
    ├── helpers.py                 # 通用辅助函数(配置转换/参数解析/JIT 导出)
    ├── isaacgym_utils.py          # Isaac Gym 工具(四元数→欧拉角)
    ├── logger.py                  # 训练日志记录器
    ├── math.py                    # 数学工具(角度归一化/偏航旋转)
    ├── task_registry.py           # 任务注册表(环境创建/算法创建)
    └── terrain.py                 # 程序化地形生成器

三、核心功能模块解析

3.1 配置系统(base_config.py)

设计理念:声明式配置

传统配置需要为每个类手写 __init__ 方法。BaseConfig 采用递归实例化 机制:子类只需以嵌套 class 方式声明参数,__init__ 自动将所有类类型属性转换为实例对象。

python 复制代码
class MyCfg(BaseConfig):
    class env:
        num_envs = 4096
    class control:
        class stiffness:
            joint_a = 10.0

cfg = MyCfg()  # 自动得到 cfg.env.num_envs, cfg.control.stiffness.joint_a
核心算法:递归实例化
python 复制代码
def init_member_classes(obj):
    for key in dir(obj):
        if key == "__class__": continue
        var = getattr(obj, key)
        if inspect.isclass(var):
            i_var = var()           # 实例化
            setattr(obj, key, i_var)
            init_member_classes(i_var)  # 递归处理嵌套

3.2 任务基类(base_task.py)

BaseTask 封装 Isaac Gym 仿真的核心生命周期:

  1. 设备解析 :根据 sim_device(如 cuda:0)和 use_gpu_pipeline 决定张量存放位置
  2. 缓冲区分配obs_bufrew_bufreset_bufepisode_length_buf 等核心张量
  3. 仿真创建 :调用 create_sim()_create_ground_plane()_create_envs()
  4. 渲染管理:查看器创建、键盘事件订阅(ESC 退出、V 键切换同步)

子类必须实现的抽象接口:

  • reset_idx(env_ids):重置指定环境
  • step(actions):执行一步仿真并返回 (obs, privileged_obs, reward, done, info)

3.3 足式机器人核心环境(legged_robot.py)

这是项目最核心、代码量最大的文件(约 600 行),包含完整的仿真 - 训练循环。

3.3.1 仿真步进流程
复制代码
策略网络输出 actions
    ↓
[step()] 动作裁剪到 [-clip_actions, clip_actions]
    ↓
循环 decimation 次(默认 4 次):
    ├─ _compute_torques(actions) → PD 控制器计算关节力矩
    ├─ gym.set_dof_actuation_force_tensor() → 施加力矩
    ├─ gym.simulate() → 推进物理仿真一步
    └─ gym.refresh_dof_state_tensor() → 刷新关节状态
    ↓
[post_physics_step()] 后处理:
    ├─ 刷新根状态和接触力张量
    ├─ 提取基座位置/姿态/速度(世界坐标→机体坐标)
    ├─ 计算重力投影向量(用于姿态感知)
    ├─ 检测终止条件(碰撞/倾覆/超时)
    ├─ 计算奖励
    ├─ 重置已终止环境
    ├─ 域随机化:随机推力
    └─ 构建观测向量
3.3.2 PD 控制器

支持三种控制模式:

P 模式(位置控制,默认):

τ=Kp⋅(θtarget−θ)−Kd⋅θ˙ \tau = K_p \cdot (\theta_{target} - \theta) - K_d \cdot \dot{\theta} τ=Kp⋅(θtarget−θ)−Kd⋅θ˙

其中:

  • θtarget=action_scale⋅a+θdefault\theta_{target} = \text{action\scale} \cdot a + \theta{default}θtarget=action_scale⋅a+θdefault
  • KpK_pKp:刚度(stiffness)
  • KdK_dKd:阻尼(damping)

V 模式(速度控制):

τ=Kp⋅(θ˙target−θ˙)−Kd⋅Δθ˙Δt \tau = K_p \cdot (\dot{\theta}_{target} - \dot{\theta}) - K_d \cdot \frac{\Delta \dot{\theta}}{\Delta t} τ=Kp⋅(θ˙target−θ˙)−Kd⋅ΔtΔθ˙

T 模式(力矩控制):

τ=action_scale⋅a \tau = \text{action\_scale} \cdot a τ=action_scale⋅a

最终力矩裁剪到关节力矩限制范围内:

τ=clip(τ,−τlimit,τlimit) \tau = \text{clip}(\tau, -\tau_{limit}, \tau_{limit}) τ=clip(τ,−τlimit,τlimit)

3.3.3 观测空间构建

默认观测向量(以 GO2 为例,48 维):

o=[vbodysv,ωbodysω,gproj,csc,(q−qdefault)sq,q˙sq˙,aprev] o = \left[ \frac{v_{body}}{s_v}, \frac{\omega_{body}}{s_{\omega}}, g_{proj}, \frac{c}{s_c}, \frac{(q - q_{default})}{s_q}, \frac{\dot{q}}{s_{\dot{q}}}, a_{prev} \right] o=[svvbody,sωωbody,gproj,scc,sq(q−qdefault),sq˙q˙,aprev]

其中:

  • vbody∈R3v_{body} \in \mathbb{R}^3vbody∈R3:机体坐标系线速度
  • ωbody∈R3\omega_{body} \in \mathbb{R}^3ωbody∈R3:机体坐标系角速度
  • gproj∈R3g_{proj} \in \mathbb{R}^3gproj∈R3:重力向量在机体坐标系的投影
  • c∈R3c \in \mathbb{R}^3c∈R3:速度指令(x 线速度,y 线速度,yaw 角速度)
  • q∈RNdofq \in \mathbb{R}^{N_{dof}}q∈RNdof:关节位置
  • q˙∈RNdof\dot{q} \in \mathbb{R}^{N_{dof}}q˙∈RNdof:关节速度
  • aprev∈RNacta_{prev} \in \mathbb{R}^{N_{act}}aprev∈RNact:上一步动作
  • sss 为各维度的观测缩放系数

若启用噪声:

onoisy=o+(2⋅U(0,1)−1)⊙σ o_{noisy} = o + (2 \cdot U(0,1) - 1) \odot \sigma onoisy=o+(2⋅U(0,1)−1)⊙σ

其中 U(0,1)U(0,1)U(0,1) 为均匀分布,σ\sigmaσ 为噪声缩放向量,⊙\odot⊙ 为逐元素乘法。

非对称观测(G1/H1/H1_2):

  • Actor 观测:不含线速度 vbodyv_{body}vbody(部署时无法直接测量)
  • Critic 特权观测:包含线速度 vbodyv_{body}vbody,用于更准确地估计价值函数
3.3.4 终止条件

环境在以下任一条件满足时终止:

  1. 碰撞终止 :终止碰撞体(如躯干)接触力 >1 N> 1\,\text{N}>1N
  2. 倾覆终止 :∣pitch∣>1.0 rad|\text{pitch}| > 1.0\,\text{rad}∣pitch∣>1.0rad 或 ∣roll∣>0.8 rad|\text{roll}| > 0.8\,\text{rad}∣roll∣>0.8rad
  3. 超时终止 :回合步数超过最大长度 Nmax=⌈Tepisode/Δt⌉N_{max} = \lceil T_{episode} / \Delta t \rceilNmax=⌈Tepisode/Δt⌉

超时终止与碰撞终止的区别:

  • 碰撞终止:机器人倒地,给予负奖励(惩罚)
  • 超时终止:正常完成回合,不给予终止惩罚

3.4 奖励工程详解

奖励设计是足式机器人 RL 训练的核心。本项目采用多目标加权奖励体系,共 17 个奖励/惩罚项。

3.4.1 速度跟踪奖励(高斯核)

线速度跟踪:

rlin=exp⁡(−∥cxy−vxy∥2σ2) r_{lin} = \exp\left(-\frac{\|c_{xy} - v_{xy}\|^2}{\sigma^2}\right) rlin=exp(−σ2∥cxy−vxy∥2)

角速度跟踪:

rang=exp⁡(−(cyaw−ωz)2σ2) r_{ang} = \exp\left(-\frac{(c_{yaw} - \omega_z)^2}{\sigma^2}\right) rang=exp(−σ2(cyaw−ωz)2)

其中 σ=0.25\sigma = 0.25σ=0.25 为高斯核宽度。误差越小,奖励越接近 1;误差越大,奖励趋近 0。

3.4.2 姿态与运动惩罚
奖励项 公式 物理含义
Z 轴线速度惩罚 r=−vz2r = -v_z^2r=−vz2 防止跳跃/下蹲
XY 轴角速度惩罚 r=−(ωx2+ωy2)r = -(\omega_x^2 + \omega_y^2)r=−(ωx2+ωy2) 防止翻滚
姿态倾斜惩罚 r=−(gproj,x2+gproj,y2)r = -(g_{proj,x}^2 + g_{proj,y}^2)r=−(gproj,x2+gproj,y2) 保持基座水平
基座高度惩罚 r=−(z−ztarget)2r = -(z - z_{target})^2r=−(z−ztarget)2 维持目标行走高度
3.4.3 关节相关惩罚
奖励项 公式 物理含义
力矩惩罚 r=−∑iτi2r = -\sum_i \tau_i^2r=−∑iτi2 节能,减少电机磨损
关节速度惩罚 r=−∑iq˙i2r = -\sum_i \dot{q}_i^2r=−∑iq˙i2 平滑运动
关节加速度惩罚 r=−∑i(Δq˙iΔt)2r = -\sum_i \left(\frac{\Delta \dot{q}_i}{\Delta t}\right)^2r=−∑i(ΔtΔq˙i)2 减少抖动
动作变化率惩罚 r=−∑i(ait−ait−1)2r = -\sum_i (a_i^{t} - a_i^{t-1})^2r=−∑i(ait−ait−1)2 动作连贯性
3.4.4 接触与步态奖励

足部腾空时间奖励:

rair=∑i∈feet(tair,i−0.5)⋅1[first_contacti]⋅1[∥cxy∥>0.1] r_{air} = \sum_{i \in feet} (t_{air,i} - 0.5) \cdot \mathbb{1}[first\contact_i] \cdot \mathbb{1}[\|c{xy}\| > 0.1] rair=i∈feet∑(tair,i−0.5)⋅1[first_contacti]⋅1[∥cxy∥>0.1]

其中:

  • tair,it_{air,i}tair,i:第 iii 只脚的腾空时间
  • 仅在首次触地时计算奖励
  • 零指令时不奖励(避免原地踏步获得奖励)

碰撞惩罚:

rcollision=−∑j∈penalized1[∥fcontact,j∥>0.1] r_{collision} = -\sum_{j \in penalized} \mathbb{1}[\|f_{contact,j}\| > 0.1] rcollision=−j∈penalized∑1[∥fcontact,j∥>0.1]

终止惩罚:

rterm=1[reset]⋅1[¬timeout] r_{term} = \mathbb{1}[reset] \cdot \mathbb{1}[\neg timeout] rterm=1[reset]⋅1[¬timeout]

仅对非超时的终止(即碰撞倒地)施加惩罚。

3.4.5 软限制惩罚

为防止关节到达物理硬极限,引入软限制

qsoft,lower=m−0.5⋅r⋅α q_{soft,lower} = m - 0.5 \cdot r \cdot \alpha qsoft,lower=m−0.5⋅r⋅α

qsoft,upper=m+0.5⋅r⋅α q_{soft,upper} = m + 0.5 \cdot r \cdot \alpha qsoft,upper=m+0.5⋅r⋅α

其中 mmm 为限制中点,rrr 为限制范围,α∈[0,1]\alpha \in [0,1]α∈[0,1] 为软限制系数(默认 0.9)。

当关节超出软限制时:

rpos_limit=−∑i[(qi−qsoft,lower)−+(qi−qsoft,upper)+] r_{pos\limit} = -\sum_i \left[(q_i - q{soft,lower})- + (q_i - q{soft,upper})_+\right] rpos_limit=−i∑[(qi−qsoft,lower)−+(qi−qsoft,upper)+]

其中 (x)−=min⁡(x,0)(x)- = \min(x, 0)(x)−=min(x,0),(x)+=max⁡(x,0)(x)+ = \max(x, 0)(x)+=max(x,0)。

3.4.6 奖励裁剪策略

若启用 only_positive_rewards

rtotal=max⁡(∑iwi⋅ri,0)+wterm⋅rterm r_{total} = \max\left(\sum_i w_i \cdot r_i, 0\right) + w_{term} \cdot r_{term} rtotal=max(i∑wi⋅ri,0)+wterm⋅rterm

终止奖励在正奖励裁剪后单独添加,确保终止惩罚始终生效。


3.5 域随机化(Domain Randomization)

域随机化通过在训练时随机化物理参数,使策略对参数不确定性具有鲁棒性。

3.5.1 摩擦力随机化

预生成 64 个摩擦系数桶:

μk∼U(μmin,μmax),k=1,...,64 \mu_k \sim U(\mu_{min}, \mu_{max}), \quad k = 1, ..., 64 μk∼U(μmin,μmax),k=1,...,64

每个环境随机分配一个桶:

μenv=μbucket[randint(0,63)] \mu_{env} = \mu_{bucket[randint(0, 63)]} μenv=μbucket[randint(0,63)]

3.5.2 基座质量随机化

mbase=mbase,original+Δm,Δm∼U(−1,3) kg m_{base} = m_{base,original} + \Delta m, \quad \Delta m \sim U(-1, 3)\,\text{kg} mbase=mbase,original+Δm,Δm∼U(−1,3)kg

3.5.3 随机推力扰动

按固定间隔(默认 15 秒)为所有环境设置随机的基座水平速度:

vpush∼U(−vmax,vmax),vmax=1.0 m/s v_{push} \sim U(-v_{max}, v_{max}), \quad v_{max} = 1.0\,\text{m/s} vpush∼U(−vmax,vmax),vmax=1.0m/s


3.6 步态相位控制(G1/H1/H1_2)

双足机器人需要显式的步态相位控制以实现交替行走。

3.6.1 相位计算

设步态周期 T=0.8 sT = 0.8\,\text{s}T=0.8s,则当前相位:

ϕ=(t⋅Δt)mod  TT∈[0,1) \phi = \frac{(t \cdot \Delta t) \mod T}{T} \in [0, 1) ϕ=T(t⋅Δt)modT∈[0,1)

左右脚相位偏移 0.5(半周期,交替步态):

ϕleft=ϕ \phi_{left} = \phi ϕleft=ϕ

ϕright=(ϕ+0.5)mod  1 \phi_{right} = (\phi + 0.5) \mod 1 ϕright=(ϕ+0.5)mod1

3.6.2 相位编码

将相位以 sin/cos 编码注入观测:

ophase=[sin⁡(2πϕ),cos⁡(2πϕ)] o_{phase} = [\sin(2\pi\phi), \cos(2\pi\phi)] ophase=[sin(2πϕ),cos(2πϕ)]

这种连续编码比原始相位值更利于神经网络学习周期性模式。

3.6.3 接触一致性奖励

检查每只脚的接触状态是否与步态相位一致:

is_stance=ϕleg<0.55 is\stance = \phi{leg} < 0.55 is_stance=ϕleg<0.55

contact=fz,foot>1 N contact = f_{z,foot} > 1\,\text{N} contact=fz,foot>1N

consistent=¬(contact⊕is_stance) consistent = \neg(contact \oplus is\_stance) consistent=¬(contact⊕is_stance)

其中 ⊕\oplus⊕ 为异或(XOR),¬\neg¬ 为取反。stance 阶段应接触,swing 阶段应离地。


四、数学工具详解

4.1 四元数到欧拉角(ZYX 内旋)

给定四元数 q=[qx,qy,qz,qw]q = [q_x, q_y, q_z, q_w]q=[qx,qy,qz,qw]:

Roll(X 轴旋转):

ϕ=arctan⁡2(2(qwqx+qyqz),qw2−qx2−qy2+qz2) \phi = \arctan2(2(q_w q_x + q_y q_z), q_w^2 - q_x^2 - q_y^2 + q_z^2) ϕ=arctan2(2(qwqx+qyqz),qw2−qx2−qy2+qz2)

Pitch(Y 轴旋转):

θ=arcsin⁡(clip(2(qwqy−qzqx),−1,1)) \theta = \arcsin(\text{clip}(2(q_w q_y - q_z q_x), -1, 1)) θ=arcsin(clip(2(qwqy−qzqx),−1,1))

Yaw(Z 轴旋转):

ψ=arctan⁡2(2(qwqz+qxqy),qw2+qx2−qy2−qz2) \psi = \arctan2(2(q_w q_z + q_x q_y), q_w^2 + q_x^2 - q_y^2 - q_z^2) ψ=arctan2(2(qwqz+qxqy),qw2+qx2−qy2−qz2)

4.2 仅偏航旋转的四元数变换

将四元数的 roll 和 pitch 分量置零,只保留 yaw:

qyaw=normalize([0,0,qz,qw]) q_{yaw} = \text{normalize}([0, 0, q_z, q_w]) qyaw=normalize([0,0,qz,qw])

然后对向量施加旋转:

v′=qyaw⊗v⊗qyaw∗ v' = q_{yaw} \otimes v \otimes q_{yaw}^* v′=qyaw⊗v⊗qyaw∗

4.3 角度归一化到 [−π,π][-\pi, \pi][−π,π]

θnorm=((θmod  2π)+2π)mod  2π \theta_{norm} = ((\theta \mod 2\pi) + 2\pi) \mod 2\pi θnorm=((θmod2π)+2π)mod2π

θnorm={θnorm−2πif θnorm>πθnormotherwise \theta_{norm} = \begin{cases} \theta_{norm} - 2\pi & \text{if } \theta_{norm} > \pi \\ \theta_{norm} & \text{otherwise} \end{cases} θnorm={θnorm−2πθnormif θnorm>πotherwise

4.4 平方根分布随机数

生成概率密度向中间集中的随机数:

r=sign(u)⋅∣u∣,u∼U(−1,1) r = \text{sign}(u) \cdot \sqrt{|u|}, \quad u \sim U(-1, 1) r=sign(u)⋅∣u∣ ,u∼U(−1,1)

rnorm=r+12∈[0,1] r_{norm} = \frac{r + 1}{2} \in [0, 1] rnorm=2r+1∈[0,1]

x=(upper−lower)⋅rnorm+lower x = (upper - lower) \cdot r_{norm} + lower x=(upper−lower)⋅rnorm+lower

相比均匀分布,减少了极端值的出现概率。


五、PPO 算法解析

本项目使用 rsl_rl 库实现的 PPO 算法进行策略训练。

5.1 PPO 核心思想

PPO 通过裁剪替代目标(Clipped Surrogate Objective)限制策略更新幅度,避免传统策略梯度方法中步长过大导致策略崩溃的问题。

5.2 关键公式

5.2.1 策略目标

LCLIP(θ)=E^t[min⁡(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)] L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \left[ \min\left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right] LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)]

其中:

  • rt(θ)=πθ(at∣st)πθold(at∣st)r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}rt(θ)=πθold(at∣st)πθ(at∣st):概率比
  • A^t\hat{A}_tA^t:广义优势估计(GAE)
  • ϵ=0.2\epsilon = 0.2ϵ=0.2:裁剪参数
5.2.2 广义优势估计(GAE)

A^tGAE(γ,λ)=∑l=0∞(γλ)lδt+lV \hat{A}t^{GAE(\gamma,\lambda)} = \sum{l=0}^{\infty} (\gamma\lambda)^l \delta_{t+l}^V A^tGAE(γ,λ)=l=0∑∞(γλ)lδt+lV

其中:

δtV=rt+γV(st+1)−V(st) \delta_t^V = r_t + \gamma V(s_{t+1}) - V(s_t) δtV=rt+γV(st+1)−V(st)

  • γ=0.99\gamma = 0.99γ=0.99:折扣因子
  • λ=0.95\lambda = 0.95λ=0.95:GAE 参数
5.2.3 价值函数损失

LVF(θ)=E^t[(Vθ(st)−Vttarget)2] L^{VF}(\theta) = \hat{\mathbb{E}}t \left[ (V\theta(s_t) - V_t^{target})^2 \right] LVF(θ)=E^t[(Vθ(st)−Vttarget)2]

若启用裁剪价值损失:

LVF,clip(θ)=E^t[max⁡((Vθ−Vtarget)2,(Vθold+clip(Vθ−Vθold,−ϵ,ϵ)−Vtarget)2)] L^{VF,clip}(\theta) = \hat{\mathbb{E}}t \left[ \max\left( (V\theta - V^{target})^2, (V_{\theta_{old}} + \text{clip}(V_\theta - V_{\theta_{old}}, -\epsilon, \epsilon) - V^{target})^2 \right) \right] LVF,clip(θ)=E^t[max((Vθ−Vtarget)2,(Vθold+clip(Vθ−Vθold,−ϵ,ϵ)−Vtarget)2)]

5.2.4 总损失

LTOTAL(θ)=−LCLIP(θ)+c1LVF(θ)−c2⋅Entropy(πθ) L^{TOTAL}(\theta) = -L^{CLIP}(\theta) + c_1 L^{VF}(\theta) - c_2 \cdot \text{Entropy}(\pi_\theta) LTOTAL(θ)=−LCLIP(θ)+c1LVF(θ)−c2⋅Entropy(πθ)

其中:

  • c1=1.0c_1 = 1.0c1=1.0:价值损失系数
  • c2=0.01c_2 = 0.01c2=0.01:熵正则化系数
5.2.5 自适应学习率

若学习率调度为 adaptive

  • 当 KL 散度 <0.5×target_KL< 0.5 \times target\_KL<0.5×target_KL 时:学习率 ×1.5\times 1.5×1.5
  • 当 KL 散度 >1.5×target_KL> 1.5 \times target\_KL>1.5×target_KL 时:学习率 ×0.5\times 0.5×0.5
  • 目标 KL 散度:0.010.010.01

5.3 网络架构

5.3.1 标准 Actor-Critic(GO2)
复制代码
Actor:
  Input: obs (48-dim)
    → Linear(48, 512) + ELU
    → Linear(512, 256) + ELU
    → Linear(256, 128) + ELU
    → Linear(128, 12)  # 输出动作均值
    → 可学习噪声标准差

Critic:
  Input: privileged_obs (48-dim)
    → Linear(48, 512) + ELU
    → Linear(512, 256) + ELU
    → Linear(256, 128) + ELU
    → Linear(128, 1)   # 输出状态价值
5.3.2 LSTM Actor-Critic(G1/H1/H1_2)
复制代码
Actor:
  Input: obs (47-dim or 41-dim)
    → Linear(input_dim, 32) + ELU
    → LSTM(32, 64, num_layers=1)
    → Linear(64, num_actions)

Critic:
  Input: privileged_obs (50-dim or 44-dim)
    → Linear(input_dim, 32) + ELU
    → LSTM(32, 64, num_layers=1)
    → Linear(64, 1)

LSTM 隐状态在每个回合开始时重置为零。


六、技术架构设计

6.1 类继承关系

复制代码
BaseConfig
  └── LeggedRobotCfg
        ├── GO2RoughCfg
        ├── H1RoughCfg
        ├── H1_2RoughCfg
        └── G1RoughCfg

BaseConfig
  └── LeggedRobotCfgPPO
        ├── GO2RoughCfgPPO
        ├── H1RoughCfgPPO
        ├── H1_2RoughCfgPPO
        └── G1RoughCfgPPO

BaseTask
  └── LeggedRobot
        ├── (GO2 直接使用 LeggedRobot)
        ├── H1Robot
        ├── H1_2Robot
        └── G1Robot

6.2 训练数据流

复制代码
┌─────────────┐     actions      ┌──────────────┐     torques     ┌──────────────┐
│  PPO 算法    │ ──────────────→ │  LeggedRobot  │ ──────────────→ │  Isaac Gym   │
│  (rsl_rl)   │                  │  _compute_    │                  │  物理仿真     │
│             │ ←────────────── │  torques()    │ ←────────────── │              │
│             │  obs, rew, done │  step()       │  状态更新        │              │
└─────────────┘                  └──────────────┘                  └──────────────┘
       ↑                                ↑
       │                                │
  策略网络更新                    奖励计算/终止检测/域随机化

6.3 控制频率链

复制代码
仿真频率:1/dt (默认 200Hz, H1_2 为 400Hz)
    │
    │ ÷ decimation (默认 4, H1_2 为 8)
    ↓
策略频率:50Hz (所有机器人统一)

6.4 观测空间对比

机器人 Actor 观测维度 特权观测维度 动作维度 策略类型
GO2 48 None 12 MLP
G1 47 50 12 LSTM
H1 41 44 10 LSTM
H1_2 47 50 12 LSTM

七、4096 并行环境机制详解

7.1 什么是并行环境?

4096 个并行环境 指的是在 GPU 上同时运行 4096 个独立的仿真世界,每个世界都有一个机器人实例,它们共享同一个策略网络(权重参数)但各自拥有完全不同的初始状态 (关节位置在默认值 0.5~1.5 倍随机、基座位置和速度随机、摩擦系数随机、地形类型随机、速度指令随机)和完全不同的动作 (因为观测不同导致策略输出不同),这种设计通过多样性训练让策略网络能够从 4096 个"平行宇宙"中同时收集经验,从而在约 1-2 小时内收敛出能在各种场景(冰面/水泥地、平地/楼梯、受外力扰动等)下都能稳定行走的鲁棒控制器,相比传统串行训练效率提升数千倍。

7.2 初始状态的差异化设计

关节位置随机化

每个环境的关节位置在默认值的 0.5~1.5 倍之间随机采样:

python 复制代码
self.dof_pos[env_ids] = self.default_dof_pos * torch_rand_float(
    0.5, 1.5, (len(env_ids), self.num_dof), device=self.device)
基座状态随机化
  • 位置:环境原点 + [-1, 1] 米随机偏移(崎岖地形模式)
  • 速度:[-0.5, 0.5] 范围内随机扰动
速度指令随机化

每个环境独立采样:

  • 线速度:lin_vel_x ∈ [-1.0, 1.0] m/s
  • 线速度:lin_vel_y ∈ [-1.0, 1.0] m/s
  • 角速度:ang_vel_yaw ∈ [-1.0, 1.0] rad/s(或由航向角计算)
摩擦系数随机化

预生成 64 个摩擦系数桶,每个环境随机分配:

python 复制代码
friction_range = [0.5, 1.25]  # 从冰面到粗糙水泥地
地形差异(课程学习)
  • 第 0-2 行:最简单(平地、缓坡 5cm)
  • 第 3-5 行:中等(楼梯 8-15cm)
  • 第 6-9 行:最难(楼梯 18-23cm + 崎岖地形)

7.3 动作为什么不同?

动作由策略网络根据观测生成:

python 复制代码
actions = policy(obs.detach())

由于每个环境的观测向量完全不同(基座速度、姿态、关节状态、指令等都不同),即使是同一个策略网络,输出也会不同。

观测空间构成(以 GO2 为例,48 维):

复制代码
obs = [
    base_lin_vel (3),      # 机体坐标系线速度
    base_ang_vel (3),      # 机体坐标系角速度
    projected_gravity (3), # 重力投影向量
    commands (3),          # 速度指令
    dof_pos (12),          # 关节位置
    dof_vel (12),          # 关节速度
    prev_actions (12)      # 上一步动作
]

八、关键文件与注释详解

8.1 包初始化模块

__init__.py(legged_gym/)
项目 说明
功能 定义项目根目录 LEGGED_GYM_ROOT_DIR 和环境目录 LEGGED_GYM_ENVS_DIR 的全局路径常量
注释意义 阐明路径常量的用途,便于理解其他模块中资源文件的引用方式
envs/__init__.py
项目 说明
功能 导入所有机器人配置和环境类,将它们注册到全局任务注册表 task_registry
注册任务 go2→LeggedRobot, h1→H1Robot, h1_2→H1_2Robot, g1→G1Robot
注释意义 说明注册表模式的设计意图------解耦任务定义与使用,统一创建接口

8.2 基类模块(envs/base/)

base_config.py
项目 说明
核心类 BaseConfig
功能 配置系统基类,实现递归实例化机制------将嵌套的类定义自动转换为实例对象
设计理念 声明式配置:子类只需声明 class env: num_envs=4096,无需手动写 __init__
注释意义 解释递归实例化的规则和注意事项,帮助理解整个配置体系的工作原理

关键方法:

  • init_member_classes(obj): 遍历对象属性,将类类型属性替换为实例,递归处理嵌套
base_task.py
项目 说明
核心类 BaseTask
功能 强化学习任务基类,封装 Isaac Gym 仿真的核心生命周期
核心职责 仿真环境创建、GPU/CPU 张量缓冲区分配、渲染管理、键盘事件处理
注释意义 阐明数据流(策略→动作→仿真→观测/奖励)和子类必须实现的接口

关键方法:

  • __init__(): 解析设备、分配缓冲区、创建仿真、注册键盘事件
  • step(): 抽象方法,子类实现一步仿真
  • reset(): 重置所有环境并返回初始观测
  • render(): 渲染当前帧,处理 ESC/V 键盘事件
legged_robot.py
项目 说明
核心类 LeggedRobot(继承 BaseTask
功能 足式机器人强化学习的核心实现,包含完整的仿真 - 训练循环
代码量 项目最大文件(~600 行),涵盖控制、奖励、观测、域随机化等全部逻辑
注释意义 逐函数解释物理含义和设计决策,帮助理解奖励工程和域随机化的实现细节

核心功能模块:

模块 方法 功能
仿真步进 step() 动作裁剪→PD 控制→物理仿真→后处理
后处理 post_physics_step() 状态更新→终止检测→奖励计算→域随机化→观测构建
PD 控制 _compute_torques() 支持 P(位置)/V(速度)/T(力矩) 三种控制模式
观测构建 compute_observations() 拼接速度 + 重力 + 指令 + 关节 + 动作,可选噪声注入
终止检测 check_termination() 碰撞/倾覆/超时三种终止条件
域随机化 _process_rigid_shape_props() 摩擦力随机化(64 桶预生成)
_process_rigid_body_props() 基座质量随机化
_push_robots() 随机推力扰动
奖励函数 17 个 _reward_* 方法 速度跟踪/姿态保持/力矩惩罚/碰撞惩罚等

奖励函数体系:

奖励名称 类型 物理含义
tracking_lin_vel 奖励 高斯核线速度跟踪
tracking_ang_vel 奖励 高斯核角速度跟踪
lin_vel_z 惩罚 Z 轴线速度(防止跳跃)
ang_vel_xy 惩罚 XY 轴角速度(防止翻滚)
orientation 惩罚 基座倾斜(重力投影偏差)
base_height 惩罚 高度偏离目标值
torques 惩罚 关节力矩(节能)
dof_vel 惩罚 关节速度(平滑运动)
dof_acc 惩罚 关节加速度(减少抖动)
action_rate 惩罚 动作变化率(连贯性)
collision 惩罚 非足部碰撞
termination 惩罚 非超时终止(倒地)
dof_pos_limits 惩罚 关节位置超限
feet_air_time 奖励 足部腾空时间(步态周期)
stumble 惩罚 绊倒检测
stand_still 惩罚 无指令时关节偏移
feet_contact_forces 惩罚 高接触力(柔和着地)
legged_robot_config.py
项目 说明
核心类 LeggedRobotCfg + LeggedRobotCfgPPO
功能 定义完整的默认配置体系,所有机器人配置的父类
配置层次 12 个子配置类,覆盖仿真/控制/奖励/域随机化等全部参数
注释意义 为每个参数标注物理单位、取值范围和设计意图,便于调参和理解

配置子类一览:

子类 功能 关键参数
env 环境维度 num_envs=4096, num_actions=12
terrain 地形参数 mesh_type, 摩擦系数,高度场分辨率
commands 速度指令 采样范围,重采样间隔,课程学习
init_state 初始状态 基座位置/姿态,默认关节角
control 控制器 control_type='P', PD 增益,decimation
asset 机器人资产 URDF 路径,碰撞配置,物理属性
domain_rand 域随机化 摩擦力/质量/推力随机范围
rewards 奖励权重 各奖励项权重,软限制系数
normalization 归一化 观测缩放系数,裁剪范围
noise 噪声 各维度噪声缩放,噪声等级
viewer 查看器 相机位置和注视点
sim 仿真参数 dt=0.005s, PhysX 求解器参数

8.3 机器人特定模块

g1/g1_config.py + g1/g1_env.py
项目 说明
机器人 G1 双足人形机器人,12 自由度(含脚踝 roll)
配置特点 LSTM 策略、步态相位奖励、强姿态/高度惩罚
环境特点 步态相位注入观测 (sin/cos 编码)、足部状态追踪、5 个额外奖励函数
观测空间 47 维 = 角速度 (3) + 重力 (3) + 指令 (3) + 关节 (12×3) + 相位 (2)
特权观测 50 维 = 线速度 (3) + Actor 观测 (47)
注释意义 解释步态相位计算逻辑和足部追踪机制,阐明 G1 特有的奖励函数设计

G1 特有奖励函数:

函数 功能
_reward_contact 步态接触一致性(XOR 检测)
_reward_feet_swing_height 摆腿高度(目标 0.08m)
_reward_alive 存活奖励(鼓励不倒)
_reward_contact_no_vel 接触时速度惩罚(减少滑步)
_reward_hip_pos 髋关节位置惩罚(索引 [1,2,7,8])
go2/go2_config.py
项目 说明
机器人 GO2 四足机器人,12 自由度
特殊之处 无自定义环境类,直接使用通用 LeggedRobot
配置特点 标准 ActorCritic 策略(非 LSTM)、站立姿态初始关节角
注释意义 说明为何四足机器人无需自定义环境------默认实现已足够
h1/h1_config.py + h1/h1_env.py
项目 说明
机器人 H1 双足人形机器人,10 自由度(单自由度踝关节)
与 G1 区别 无脚踝 roll、含手臂关节 (4 个)、更高 PD 刚度
观测空间 41 维 = 角速度 (3) + 重力 (3) + 指令 (3) + 关节 (10×3) + 相位 (2)
注释意义 对比 H1 与 G1 的关节索引差异,解释髋关节惩罚索引 [0,1,5,6] 的来源
h1_2/h1_2_config.py + h1_2/h1_2_env.py
项目 说明
机器人 H1_2 双足人形机器人,12 自由度(双自由度踝关节)
与 H1 区别 增加踝 roll、更高 PD 刚度 (髋 200/膝 300)、更小仿真步 (0.0025s)、非零 armature
策略频率 50Hz = 0.0025s × 8(decimation)
注释意义 解释高刚度下需要更小仿真步的原因(数值稳定性),armature 参数的作用

8.4 脚本模块(scripts/)

train.py
项目 说明
功能 训练入口脚本
流程 解析参数 → 创建环境 → 创建 PPO 运行器 → 训练循环
使用 python train.py --task go2
注释意义 说明训练脚本的简洁设计------核心逻辑由任务注册表和运行器封装
play.py
项目 说明
功能 推理/测试入口脚本
流程 加载配置 → 覆盖测试参数 → 创建环境 → 加载策略 → 推理循环 → 可选 JIT 导出
测试参数覆盖 减少环境数 (≤100)、关闭噪声/随机化、启用实时同步
JIT 导出 支持标准 MLP 和 LSTM 策略,导出为 TorchScript 用于 C++ 部署
注释意义 解释测试模式与训练模式的参数差异,JIT 导出的部署意义

8.5 工具模块(utils/)

helpers.py
项目 说明
核心函数 7 个辅助函数 + 1 个 JIT 导出器类
功能 配置转换、参数解析、模型加载、JIT 导出
注释意义 详细说明每个函数的输入输出和设计决策

关键函数:

函数 功能
class_to_dict() 配置类→字典(递归,用于序列化)
update_class_from_dict() 字典→配置类(递归更新)
set_seed() 全局随机种子(Python/NumPy/PyTorch)
parse_sim_params() 合并命令行和配置文件的仿真参数
get_load_path() 查找最新模型检查点路径
get_args() 解析命令行参数(集成 Isaac Gym 参数)
export_policy_as_jit() 导出策略为 JIT(支持 MLP 和 LSTM)
PolicyExporterLSTM LSTM 策略 JIT 导出器(封装隐状态管理)
isaacgym_utils.py
项目 说明
核心函数 get_euler_xyz()
功能 四元数→欧拉角(ZYX 内旋顺序)
注释意义 解释与 Isaac Gym 自带函数的区别------TorchScript 兼容、GPU 加速
logger.py
项目 说明
核心类 Logger
功能 训练日志记录,支持状态量和奖励统计
注释意义 说明按回合聚合奖励的加权平均计算方式
math.py
项目 说明
核心函数 3 个数学工具函数
注释意义 解释每个函数的物理含义和应用场景
函数 功能 应用场景
quat_apply_yaw() 仅偏航旋转的四元数变换 航向指令计算
wrap_to_pi() 角度归一化到 [-π,π] 航向误差最短路径
torch_rand_sqrt_float() 平方根分布随机数 减少极端值采样
task_registry.py
项目 说明
核心类 TaskRegistry(全局单例 task_registry
功能 任务的注册、查询、环境创建、算法创建
设计模式 注册表模式------解耦任务定义与使用
注释意义 阐明统一创建接口的设计意图和完整创建流程

核心方法:

  • register(): 注册任务(名称→环境类 + 配置)
  • make_env(): 创建仿真环境(解析参数→设置种子→创建实例)
  • make_alg_runner(): 创建 PPO 运行器(设置日志→创建运行器→可选恢复)
terrain.py
项目 说明
核心类 Terrain + 2 个辅助函数
功能 程序化地形生成,支持 7 种地形类型和课程学习
注释意义 详细说明地形类型的选择逻辑和难度参数化

地形类型:

类型 函数 参数
平滑斜坡 pyramid_sloped_terrain() slope=difficulty×0.4
粗糙斜坡 pyramid_sloped_terrain() + random_uniform_terrain() slope + 随机高度±0.05m
上楼梯 pyramid_stairs_terrain() step_height=0.05+0.18×difficulty
下楼梯 pyramid_stairs_terrain() step_height=-(0.05+0.18×difficulty)
离散障碍 discrete_obstacles_terrain() height=0.05+0.2×difficulty
踏步石 stepping_stones_terrain() size=1.5×(1.05-difficulty)
缝隙 gap_terrain() gap_size=1.0×difficulty
坑洞 pit_terrain() depth=1.0×difficulty

九、快速上手

9.1 环境依赖

bash 复制代码
# 基础依赖
pip install torch>=1.10 numpy scipy

# Isaac Gym (需从 NVIDIA 官网下载)
# https://developer.nvidia.com/isaac-gym

# rsl_rl (强化学习算法库)
pip install git+https://github.com/leggedrobotics/rsl_rl.git

9.2 训练

bash 复制代码
# 训练 GO2 四足机器人
python legged_gym/scripts/train.py --task go2

# 训练 G1 双足机器人
python legged_gym/scripts/train.py --task g1

# 无头模式训练(服务器环境)
python legged_gym/scripts/train.py --task h1 --headless

# 指定随机种子
python legged_gym/scripts/train.py --task go2 --seed 42

# 指定最大迭代次数
python legged_gym/scripts/train.py --task g1 --max_iterations 5000

9.3 推理与测试

bash 复制代码
# 测试 GO2
python legged_gym/scripts/play.py --task go2

# 指定运行名称加载模型
python legged_gym/scripts/play.py --task h1_2 --load_run <run_name>

# 指定检查点编号
python legged_gym/scripts/play.py --task g1 --load_run <run_name> --checkpoint 1000

9.4 自定义机器人配置示例

python 复制代码
from legged_gym.envs.base.legged_robot_config import LeggedRobotCfg

class MyRobotCfg(LeggedRobotCfg):
    class env(LeggedRobotCfg.env):
        num_envs = 2048
        num_observations = 48
        num_actions = 12

    class control(LeggedRobotCfg.control):
        control_type = 'P'
        stiffness = {'joint': 50.}
        damping = {'joint': 2.}
        action_scale = 0.25
        decimation = 4

    class asset(LeggedRobotCfg.asset):
        file = '{LEGGED_GYM_ROOT_DIR}/resources/robots/my_robot/robot.urdf'
        name = "my_robot"
        foot_name = "foot"

    class rewards(LeggedRobotCfg.rewards):
        class scales(LeggedRobotCfg.rewards.scales):
            tracking_lin_vel = 2.0  # 增大线速度跟踪权重
            torques = -0.0005       # 调整力矩惩罚

9.5 自定义奖励函数示例

python 复制代码
from legged_gym.envs.base.legged_robot import LeggedRobot

class MyRobot(LeggedRobot):
    def _reward_custom(self):
        """自定义奖励:惩罚基座前后晃动"""
        return torch.abs(self.base_ang_vel[:, 1])  # pitch 角速度绝对值

然后在配置中添加权重:

python 复制代码
class MyRobotCfg(LeggedRobotCfg):
    class rewards(LeggedRobotCfg.rewards):
        class scales(LeggedRobotCfg.rewards.scales):
            custom = -0.1

9.6 依赖清单

  • Isaac Gym (Preview)
  • PyTorch ≥ 1.10
  • rsl_rl (强化学习算法库)
  • numpy, scipy
相关推荐
litble1 天前
如何速成LLM以伪装成一个AI研究者(4)——PPO,GRPO,DAPO,GSPO
人工智能·llm·ppo·grpo·gspo·dapo
星马梦缘6 天前
强化学习实战8——用PPO打赢星际争霸【整合版】
强化学习·ppo·星际争霸·sc2·starcraft2·sb3
非社会人士16 天前
RL 系统 Infra 笔记:区分不同模型
强化学习·rlhf·rl·ppo·verl·infra
@BangBang2 个月前
Hyper-Diffusion-Planner(1): 论文解读
自动驾驶·ppo
大傻^2 个月前
强化学习与大模型融合:从理论到机器人实践全解析
机器人·llm·大语言模型·强化学习·urdf·ppo·奖励设计
njsgcs3 个月前
ppo可以不需要提取特征,直接训练ac吗。ppo不知道自己现在在第几步吗
人工智能·ppo
_pinnacle_3 个月前
多维回报与多维价值矢量化预测的PPO算法
神经网络·算法·强化学习·ppo·多维价值预测
njsgcs3 个月前
ppo 游戏导航视觉 基于cnn 两个动作空间 训练120轮记录
游戏·ppo
njsgcs4 个月前
ppo靠近门模型 试训练 yolo评分
yolo·ppo