欢迎光临
我们一直在努力

Unitree RL Gym — Legged Gym 项目深度技术解析

Unitree RL Gym — 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__ 自动将所有类类型属性转换为实例对象。

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

核心算法:递归实例化

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 仿真的核心生命周期:

  • 设备解析:根据 sim_device(如 cuda:0)和 use_gpu_pipeline 决定张量存放位置
  • 缓冲区分配:obs_buf、rew_buf、reset_buf、episode_length_buf 等核心张量
  • 仿真创建:调用 create_sim() → _create_ground_plane() → _create_envs()
  • 渲染管理:查看器创建、键盘事件订阅(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_scalea+θ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_scalea

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

    τ=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(qqdefault),sq˙q˙,aprev]

    其中:

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

    若启用噪声:

    onoisy=o+(2⋅U(0,1)−1)⊙σ
    o_{noisy} = o + (2 \\cdot U(0,1) – 1) \\odot \\sigma
    onoisy=o+(2U(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 N> 1\\,\\text{N}>1N
  • 倾覆终止:∣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
  • 超时终止:回合步数超过最大长度 Nmax=⌈Tepisode/Δt⌉N_{max} = \\lceil T_{episode} / \\Delta t \\rceilNmax=Tepisodet
  • 超时终止与碰撞终止的区别:

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

    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(σ2cxyvxy2)

    角速度跟踪:

    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=(zztarget)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(aitait1)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=ifeet(tair,i0.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=jpenalized1[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=m0.5rα

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

    其中 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[(qiqsoft,lower)+(qiqsoft,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(iwiri,0)+wtermrterm

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


    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
    μkU(μ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,ΔmU(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}
    vpushU(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=¬(contactis_stance)

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


    四、PPO 算法解析

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

    4.1 PPO 核心思想

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

    4.2 关键公式

    4.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(atst)πθ(atst):概率比
    • A^t\\hat{A}_tA^t:广义优势估计(GAE)
    • ϵ=0.2\\epsilon = 0.2ϵ=0.2:裁剪参数
    4.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 参数
    4.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)]

    4.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(θ)c2Entropy(πθ)

    其中:

    • c1=1.0c_1 = 1.0c1=1.0:价值损失系数
    • c2=0.01c_2 = 0.01c2=0.01:熵正则化系数
    4.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

    4.3 网络架构

    4.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) # 输出状态价值

    4.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 隐状态在每个回合开始时重置为零。


    五、关键文件技术说明

    5.1 配置体系(legged_robot_config.py)

    配置采用分层嵌套结构,12 个子配置类覆盖全部参数:

    子类功能关键参数示例
    env 环境维度 num_envs=4096, num_observations=48
    terrain 地形参数 mesh_type='plane', 摩擦系数
    commands 速度指令 采样范围 [-1, 1] m/s, 重采样间隔 10s
    init_state 初始状态 基座高度, 默认关节角
    control 控制器 control_type='P', PD 增益, decimation=4
    asset 机器人资产 URDF 路径, 碰撞体关键词
    domain_rand 域随机化 摩擦力/质量/推力范围
    rewards 奖励权重 17 项奖励的权重值
    normalization 归一化 观测缩放系数, 裁剪范围
    noise 噪声 各维度噪声缩放, 噪声等级
    viewer 查看器 相机位置 [10, 0, 6]
    sim 仿真参数 dt=0.005s, PhysX 求解器参数

    5.2 任务注册表(task_registry.py)

    采用注册表模式解耦任务定义与使用:

    # 注册(envs/__init__.py)
    task_registry.register("go2", LeggedRobot, GO2RoughCfg(), GO2RoughCfgPPO())

    # 使用(scripts/train.py)
    env, env_cfg = task_registry.make_env(name=args.task, args=args)
    runner, train_cfg = task_registry.make_alg_runner(env=env, name=args.task, args=args)

    5.3 程序化地形(terrain.py)

    地形生成支持三种模式:

  • 课程学习(curriculum):行号决定难度,从简单到困难
  • 随机化(randomized):每个子地形随机选择类型和难度
  • 指定类型(selected):所有子地形使用同一类型
  • 7 种地形类型及难度参数化:

    类型难度参数说明
    平滑斜坡 slope = difficulty × 0.4 50% 概率反向
    粗糙斜坡 slope + 随机高度 ±0.05m 叠加随机地形
    上/下楼梯 step_height = 0.05 + 0.18 × difficulty 正负号区分上下
    离散障碍 height = 0.05 + 0.2 × difficulty 20 个随机矩形
    踏步石 size = 1.5 × (1.05 – difficulty) 难度越高石块越小
    缝隙 gap_size = 1.0 × difficulty 中央缝隙
    坑洞 depth = 1.0 × difficulty 中央坑洞

    六、技术架构设计

    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

    七、使用方法及示例代码

    7.1 环境依赖

    # 基础依赖
    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

    7.2 训练

    # 训练 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

    7.3 推理与测试

    # 测试 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

    7.4 自定义机器人配置示例

    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 # 调整力矩惩罚

    7.5 自定义奖励函数示例

    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 角速度绝对值

    然后在配置中添加权重:

    class MyRobotCfg(LeggedRobotCfg):
    class rewards(LeggedRobotCfg.rewards):
    class scales(LeggedRobotCfg.rewards.scales):
    custom = 0.1


    八、数学工具详解

    8.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),qw2qx2qy2+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(qwqyqzqx),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+qx2qy2qz2)

    8.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=qyawvqyaw

    8.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={θnorm2πθnormif θnorm>πotherwise

    8.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,uU(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=(upperlower)rnorm+lower

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


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

    9.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
    注释意义 说明注册表模式的设计意图——解耦任务定义与使用,统一创建接口

    9.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求解器参数

    9.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参数的作用

    9.4 脚本模块(scripts/)

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

    9.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

    十、快速上手

    训练

    # 训练 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

    测试

    # 测试 GO2
    python legged_gym/scripts/play.py –task go2

    # 指定运行名称测试
    python legged_gym/scripts/play.py –task h1_2 –load_run <run_name>

    依赖

    • Isaac Gym (Preview)
    • PyTorch ≥ 1.10
    • rsl_rl (强化学习算法库)
    • numpy, scipy
    赞(0)
    未经允许不得转载:171主机测评 » Unitree RL Gym — Legged Gym 项目深度技术解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址