开动

强烈推荐张老师系列:张伟楠老师的动手学强化学习- PPO
目前完成了基于宇树rl_lab的RL仿真训练复现,踩了一些比较低级错误的坑后,发现纯RL的效果比预期的要好,算是pipeline流程通了,开始了解实现原理。
在智驾中,RL也已经实现了量产落地,能够在专家数据基础上实现性能拓展,效果还是很显著的。
unitree_rl_lab作为学习入口:
RSL-RL 做什么、PPO 和 Distillation 两条训练链路怎么跑、它如何接入 Unitree/Isaac Lab 任务,以及应该从哪些文件开始读。
好家伙:用的GPT5.5直出的图,很奈斯

在这里插入图片描述

1. 一句话理解 RSL-RL

extern/rsl_rl 是一个面向机器人强化学习的轻量 PyTorch 训练库。它不负责创建机器人环境、写奖励函数或管理仿真资产;这些由 Isaac Lab、Unitree RL Lab、Legged Gym 等环境库负责。RSL-RL 的职责是:

  1. 从环境批量采样交互数据。
  2. 把采样结果存入 rollout buffer。
  3. 用 PPO 或蒸馏算法更新策略网络。
  4. 记录日志、保存 checkpoint、导出 JIT/ONNX 策略。

换句话说:

环境库负责“机器人世界”
RSL-RL 负责“怎么学习”

2. 训练系统总览:两条主线

RSL-RL 不只有 PPO。当前源码里最重要的两条主线是:

主线 算法 典型用途
On-policy RL PPO 直接通过环境 reward 学策略,例如 locomotion 速度跟踪
Student-Teacher Distillation 让 student 模仿 teacher,把 privileged policy 压到可部署观测上

PPO 主流程:

get_observations

obs

Actor 采样 action

obs, reward, done, extras

更新 Actor / Critic

Isaac Lab / Unitree 环境

OnPolicyRunner

PPO.act

PPO.process_env_step

RolloutStorage

PPO.compute_returns
GAE

PPO.update

Logger / checkpoint / export

Distillation 主流程:

get_observations

obs

student action

teacher target action

obs, reward, done, extras

只更新 student

Isaac Lab / Unitree 环境

DistillationRunner

Distillation.act

RolloutStorage

Distillation.process_env_step

Distillation.update
behavior cloning loss

这个图对应的核心源码是:

职责 文件 应先读的方法
训练主循环 extern/rsl_rl/rsl_rl/runners/on_policy_runner.py OnPolicyRunner.__init__()learn()
PPO 算法 extern/rsl_rl/rsl_rl/algorithms/ppo.py act()process_env_step()compute_returns()update()
蒸馏 Runner extern/rsl_rl/rsl_rl/runners/distillation_runner.py learn()
蒸馏算法 extern/rsl_rl/rsl_rl/algorithms/distillation.py act()process_env_step()update()
Rollout 缓冲区 extern/rsl_rl/rsl_rl/storage/rollout_storage.py add_transition()mini_batch_generator()
默认 MLP 网络 extern/rsl_rl/rsl_rl/models/mlp_model.py __init__()forward()get_latent()
环境接口 extern/rsl_rl/rsl_rl/env/vec_env.py VecEnv.get_observations()VecEnv.step()

3. RSL-RL 的目录结构

extern/rsl_rl/
├── rsl_rl/
│   ├── algorithms/      # PPO、Distillation 等算法
│   ├── runners/         # 训练入口和主循环
│   ├── storage/         # rollout buffer
│   ├── models/          # MLP / RNN / CNN 模型封装
│   ├── modules/         # MLP、分布、归一化、RNN 等网络积木
│   ├── env/             # VecEnv 抽象接口
│   ├── extensions/      # RND、Symmetry 等扩展
│   └── utils/           # 日志、配置解析、导出辅助
├── docs/                # 官方文档
├── tests/               # 单元测试
├── pyproject.toml       # Python 包定义
└── README.md

初学者先不要从 modules/ 开始读。PPO 推荐路径是:

OnPolicyRunner.learn()
  -> PPO.act()
  -> PPO.process_env_step()
  -> RolloutStorage.add_transition()
  -> PPO.compute_returns()
  -> PPO.update()
  -> MLPModel.forward()

Distillation 推荐路径是:

DistillationRunner.learn()
  -> OnPolicyRunner.learn()
  -> Distillation.act()
  -> Distillation.process_env_step()
  -> RolloutStorage.add_transition()
  -> Distillation.update()
  -> MLPModel.forward()

4. RSL-RL 需要环境提供什么

RSL-RL 只依赖一个抽象环境接口:VecEnv

环境必须提供这些属性:

num_envs              并行环境数量
num_actions           动作维度
max_episode_length    episode 最大长度
episode_length_buf    每个环境当前 episode 长度
device                环境所在设备
cfg                   环境配置

环境必须实现两个方法:

obs = env.get_observations()
obs, rewards, dones, extras = env.step(actions)

其中 obs 不是普通 Tensor,而是 TensorDict。它通常包含多个 observation group,例如:

obs["policy"]   # actor 可见观测
obs["critic"]   # critic 可见观测,通常包含特权信息

RSL-RL 再通过 obs_groups 决定 actor 和 critic 各自读取哪些组:

obs_groups = {
    "actor": ["policy"],
    "critic": ["critic"],
}

这是理解 Isaac Lab 接入 RSL-RL 的关键点:环境产生多个观测组,RSL-RL 的模型按配置选择并拼接这些观测组。


5. Runner:训练总控

OnPolicyRunner 是 RSL-RL 的通用 on-policy 训练入口;DistillationRunner 继承它,只额外检查 teacher 参数是否已经加载。创建 runner 时,它会:

  1. 保存环境、训练配置和训练设备。
  2. 读取一次 env.get_observations(),用来推断观测维度。
  3. 根据配置创建算法、模型和 rollout storage。PPO 创建 actor/critic,Distillation 创建 student/teacher。
  4. 创建 logger。

主循环在 learn() 中:

开始一个 iteration

循环 num_steps_per_env 次

PPO.act 采样动作

env.step 执行动作

PPO.process_env_step 写入 transition

PPO.compute_returns

PPO.update

log / save

关键配置:

num_steps_per_env = 24
save_interval = 100
max_iterations = 50000

在 Unitree RL Lab 的 G1 locomotion PPO 配置中,这些值位于:

extern/unitree_rl_lab/source/unitree_rl_lab/unitree_rl_lab/tasks/locomotion/agents/rsl_rl_ppo_cfg.py

Isaac Lab 也提供 Distillation 配置示例:

extern/isaac_lab/source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_d/agents/rsl_rl_distillation_cfg.py

6. PPO:一次训练迭代内部发生了什么

PPO 在 RSL-RL 中分成四个核心步骤。

6.1 act():采样动作并记录旧策略信息

PPO.act(obs) 做四件事:

  1. actor 根据当前 obs 采样动作。
  2. critic 估计当前状态价值 V(s)
  3. 记录动作在旧策略下的 log_prob
  4. 记录 actor 分布参数,供后续 PPO ratio/KL 使用。

示意:

obs
 ├─ actor  -> action, log_prob, distribution params
 └─ critic -> value

注意:PPO 更新时需要比较“采样动作时的旧策略”和“当前更新后的新策略”,所以 rollout 阶段必须存 old_log_prob 和旧分布参数。

6.2 process_env_step():处理环境返回

process_env_step(obs, rewards, dones, extras) 在每次 env.step() 后执行,主要负责:

  1. 更新 actor/critic 的 observation normalizer。
  2. 记录 reward 和 done。
  3. 如果启用 RND,加入 intrinsic reward。
  4. 如果 extras 中有 time_outs,对时间截断做 bootstrap。
  5. 将 transition 写入 RolloutStorage
  6. 根据 done 重置 actor/critic 的 recurrent state。

time_outs 很重要:如果 episode 是因为时间上限结束,而不是机器人摔倒或任务失败结束,那么理论上不应该把它当作真实 terminal。RSL-RL 会用 critic value 对 reward 做补偿:

reward += gamma * V(s) * time_out

6.3 compute_returns():计算 GAE

rollout 收集完成后,PPO 从最后一步往前计算:

delta_t = r_t + gamma * V(s_{t+1}) - V(s_t)
adv_t   = delta_t + gamma * lambda * adv_{t+1}
return  = adv_t + V(s_t)

其中:

参数 含义
gamma 折扣因子,越接近 1 越看重长期回报
lam GAE 平滑系数,控制 bias/variance 权衡

Unitree G1 默认配置一般是:

gamma = 0.99
lam = 0.95

6.4 update():PPO 多轮小批量更新

PPO 更新时会把 rollout buffer 展平:

[num_steps_per_env, num_envs, ...]
       -> [num_steps_per_env * num_envs, ...]

然后分成 num_mini_batches,重复 num_learning_epochs 轮。

核心 loss:

ratio = exp(new_log_prob - old_log_prob)

surrogate_1 = -advantage * ratio
surrogate_2 = -advantage * clamp(ratio, 1 - clip, 1 + clip)
actor_loss  = max(surrogate_1, surrogate_2)

value_loss  = MSE(V(s), return)
loss        = actor_loss + value_coef * value_loss - entropy_coef * entropy

这就是 PPO 的“保守更新”:如果新策略相对旧策略变化太大,clip_param 会限制收益,避免一步把策略推坏。


7. Distillation:学生-老师蒸馏

Distillation 是 RSL-RL 的第二条重要训练链路。它不是通过 reward 优化策略,而是让 student 模仿 teacher 的动作。典型用途是:先训练一个能看到 privileged observation 的强 teacher,再把行为蒸馏到只看部署可用观测的 student 上。

核心关系:

obs
 ├─ student -> action,用于 env.step
 └─ teacher -> privileged_actions,作为监督目标

loss = mse(student_action, teacher_action)
# 或 huber loss

7.1 Distillation.act():student 行动,teacher 给标签

Distillation.act(obs) 做三件事:

  1. student 根据当前 obs 生成动作,并用这个动作推进环境。
  2. teacher 根据当前 obs 生成目标动作 privileged_actions
  3. 把 obs、student action、teacher target 暂存在 transition 中。

这里的关键约束是:环境里真正执行的是 student action,teacher 只提供监督信号。

7.2 process_env_step():记录 transition

Distillation 仍然会接收 rewardsdonesextras,但它不使用 reward 来计算 return 或 advantage。reward 主要用于日志和环境侧统计;done 用来重置 student/teacher 的 recurrent state。

RolloutStorage 在 distillation 模式下会额外保存:

privileged_actions  # teacher 输出的动作标签

7.3 compute_returns():空操作

Distillation 不做 GAE,也不需要 critic value target,所以 compute_returns() 是 no-op。

7.4 update():行为克隆更新 student

更新阶段从 storage 按时间步取数据:

student_actions = student(observations)
behavior_loss   = mse(student_actions, privileged_actions)

然后只更新 student。teacher 保持 eval mode,不参与梯度更新。

关键参数:

参数 作用
num_learning_epochs 每批采样数据重复训练几轮
gradient_length 累积多少步 loss 后做一次反向传播,常用于 recurrent student
learning_rate student 优化器学习率
loss_type msehuber
max_grad_norm student 梯度裁剪

7.5 teacher 从哪里来

DistillationRunner.learn() 会先检查 teacher 是否加载。如果没有加载 teacher checkpoint,会直接报错。

Distillation.load() 支持两种来源:

  1. 加载 distillation checkpoint:读取 student_state_dictteacher_state_dict
  2. 加载 PPO checkpoint:读取 actor_state_dict 作为 teacher。

因此常见流程是:

先用 PPO 训练 teacher policy
再用 Distillation 加载 PPO actor 作为 teacher
最后导出 student policy 用于部署

Distillation 不兼容 RND 和 Symmetry 扩展;源码会显式检查并报错。


8. Actor、Critic、Student、Teacher 与 Observation Groups

RSL-RL 默认使用 MLPModel。它的输入不是环境给出的全部 obs,而是按 obs_groups 选择指定 group 后拼接:

latent = torch.cat([obs[obs_group] for obs_group in self.obs_groups], dim=-1)

PPO 典型配置:

actor = MLPModel(obs_set="actor")
critic = MLPModel(obs_set="critic")

Distillation 典型配置:

student = MLPModel(obs_set="student")
teacher = MLPModel(obs_set="teacher")

含义:

网络 所属训练方式 训练时输入 部署时是否需要
actor PPO policy observation 需要,PPO 导出的就是 actor
critic PPO privileged / critic observation 不需要,只训练时评估 value
student Distillation 部署可用 observation 需要,蒸馏导出的就是 student
teacher Distillation policy 或 privileged observation 不需要,只训练时提供动作标签

机器人强化学习里常见做法是:critic 或 teacher 训练时看更多真值信息,比如 base linear velocity;actor 或 student 只能看部署时真实可获得的信息,比如 IMU、关节状态、命令、上一帧动作。


9. Unitree G1 29DOF 接入示例

Unitree 训练入口:

extern/unitree_rl_lab/scripts/rsl_rl/train.py

关键接入链路:

Hydra task config

gym.make 创建 Isaac Lab 环境

RslRlVecEnvWrapper

OnPolicyRunner

runner.learn

对应代码逻辑:

env = gym.make(args_cli.task, cfg=env_cfg, ...)
env = RslRlVecEnvWrapper(env, clip_actions=agent_cfg.clip_actions)
runner = OnPolicyRunner(env, agent_cfg.to_dict(), log_dir=log_dir, device=agent_cfg.device)
runner.learn(num_learning_iterations=agent_cfg.max_iterations, init_at_random_ep_len=True)

G1 29DOF locomotion 的 actor observation 通常包括:

base_ang_vel
projected_gravity
velocity_commands
joint_pos_rel
joint_vel_rel
last_action

并设置:

history_length = 5
enable_corruption = True
concatenate_terms = True

critic observation 通常额外包含:

base_lin_vel

这说明 critic 使用训练时特权信息,actor 保持部署可用输入。


10. 最快上手命令

10.1 确认 Python 实际 import 的 RSL-RL

如果想使用本工作区源码版本,先在对应 Python 环境中安装 editable 包:

python -m pip install -e extern/rsl_rl

验证 import 路径:

python -c "import rsl_rl, inspect; print(inspect.getfile(rsl_rl))"

期望输出应指向:

/home/nio/wangbin/unitree_workspace/extern/rsl_rl/rsl_rl/__init__.py

如果输出在 site-packages 下,说明当前环境用的是 pip 安装版本,而不是工作区源码。

10.2 Unitree RL Lab 训练入口

常见入口在:

extern/unitree_rl_lab/scripts/rsl_rl/train.py

具体任务名需要从 Unitree RL Lab 注册的 gym task 中选择。脚本会读取 task 对应的 rsl_rl_cfg_entry_point,创建 env 和 runner。

10.3 回放与导出

常见入口:

extern/unitree_rl_lab/scripts/rsl_rl/play.py
extern/unitree_rl_lab/scripts/rsl_rl/play_keyboard.py

RSL-RL 自身也提供导出接口:

runner.export_policy_to_jit(path, filename="policy.pt")
runner.export_policy_to_onnx(path, filename="policy.onnx")

PPO 部署时通常只需要 actor,不需要 critic。Distillation 部署时通常只需要 student,不需要 teacher。


11. 常用调参入口

Unitree locomotion PPO 配置:

extern/unitree_rl_lab/source/unitree_rl_lab/unitree_rl_lab/tasks/locomotion/agents/rsl_rl_ppo_cfg.py

Isaac Lab Distillation 配置示例:

extern/isaac_lab/source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_d/agents/rsl_rl_distillation_cfg.py

常调参数:

参数 作用 现象与调参方向
num_steps_per_env 每轮每个环境采样步数 太小可能估计不稳,太大更新滞后
num_learning_epochs 每批数据重复训练轮数 太大可能过拟合旧数据
num_mini_batches 每轮分多少 mini-batch 影响梯度噪声和显存
learning_rate 学习率 不稳定先降学习率
schedule 学习率调度 adaptive 会根据 KL 自动调
desired_kl 目标 KL KL 太大策略变化激进,太小学习慢
clip_param PPO ratio 裁剪范围 常用 0.2
entropy_coef 探索强度 探索不足可增大,动作抖可减小
gamma 长期回报折扣 locomotion 常见 0.99
lam GAE 平滑 常见 0.95
max_grad_norm 梯度裁剪 防止梯度爆炸

网络结构配置:

actor.hidden_dims = [512, 256, 128]
critic.hidden_dims = [512, 256, 128]
activation = "elu"

Distillation 常调参数:

参数 作用 现象与调参方向
gradient_length 累积多少步再反传 RNN 蒸馏常需要关注,太长显存压力更大
loss_type msehuber 动作标签有异常值时可考虑 huber
student.hidden_dims student 容量 部署算力有限时先压 student
teacher.hidden_dims teacher 结构 通常要匹配已加载 checkpoint
student.obs_groups student 输入 必须只包含部署可用观测
teacher.obs_groups teacher 输入 可包含 privileged observation

观测和奖励配置通常在机器人任务文件中,例如 G1 29DOF:

extern/unitree_rl_lab/source/unitree_rl_lab/unitree_rl_lab/tasks/locomotion/robots/g1/29dof/velocity_env_cfg.py

12. Debug 时优先看什么

12.1 训练直接 NaN

优先检查:

  1. 环境返回的 obsrewardsdones 是否有 NaN。
  2. reward 权重是否过大。
  3. action scale 或 PD gain 是否过激。
  4. learning_rate 是否过大。

OnPolicyRunner.learn() 中默认会执行 check_nan(obs, rewards, dones)

12.2 策略学不会走路

优先检查:

  1. actor observation 是否包含命令、关节状态、上一帧动作。
  2. critic 是否有合理 privileged information。
  3. reward 中 tracking 项和姿态/能耗惩罚是否失衡。
  4. command curriculum 或 terrain curriculum 是否太难。

12.3 训练能走,部署不行

优先检查:

  1. actor 是否使用了部署拿不到的观测。
  2. 训练观测归一化是否正确导出。
  3. joint order、action scale、default joint pose 是否和部署端一致。
  4. history buffer 拼接顺序是否一致。

13. 建议阅读顺序

第一遍读 PPO 主干:

1. extern/rsl_rl/rsl_rl/runners/on_policy_runner.py
2. extern/rsl_rl/rsl_rl/algorithms/ppo.py
3. extern/rsl_rl/rsl_rl/storage/rollout_storage.py
4. extern/rsl_rl/rsl_rl/models/mlp_model.py
5. extern/rsl_rl/rsl_rl/env/vec_env.py

第二遍读 Distillation 主干:

1. extern/rsl_rl/rsl_rl/runners/distillation_runner.py
2. extern/rsl_rl/rsl_rl/algorithms/distillation.py
3. extern/rsl_rl/rsl_rl/storage/rollout_storage.py
4. extern/rsl_rl/rsl_rl/models/mlp_model.py
5. extern/isaac_lab/source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_d/agents/rsl_rl_distillation_cfg.py

第三遍结合 Unitree:

1. extern/unitree_rl_lab/scripts/rsl_rl/train.py
2. extern/unitree_rl_lab/source/unitree_rl_lab/unitree_rl_lab/tasks/locomotion/agents/rsl_rl_ppo_cfg.py
3. extern/unitree_rl_lab/source/unitree_rl_lab/unitree_rl_lab/tasks/locomotion/robots/g1/29dof/velocity_env_cfg.py

第四遍再看扩展能力:

extern/rsl_rl/rsl_rl/extensions/rnd.py
extern/rsl_rl/rsl_rl/extensions/symmetry.py
extern/rsl_rl/rsl_rl/runners/distillation_runner.py
extern/rsl_rl/rsl_rl/algorithms/distillation.py

14. 版本注意事项

当前子模块源码的 pyproject.toml 中包版本为 5.4.1。但 extern/unitree_rl_lab/scripts/rsl_rl/train.py 里对分布式训练有最低版本检查,要求 rsl-rl-lib >= 2.3.1

因此:

  1. 单卡训练通常重点确认实际 import 路径即可。
  2. 分布式训练前必须确认当前环境安装版本和 Unitree RL Lab 脚本要求一致。
  3. 如果修改了 extern/rsl_rl 源码,记得在训练环境中使用 editable install,否则改动不会生效。

15. 最小心智模型

把 RSL-RL 压缩成一句话:

OnPolicyRunner / DistillationRunner 负责循环;
PPO / Distillation 负责算法;
RolloutStorage 负责存数据;
Model 负责把 obs 变成 action/value;
VecEnv 是环境和算法之间的接口。

把 PPO 压缩成一句话:

先用旧策略采一批数据,再用裁剪后的目标函数小步更新新策略,避免策略变化过猛。

把 Distillation 压缩成一句话:

student 负责行动,teacher 负责给动作标签,训练时只更新 student。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐