Rsl Rl
快速且简洁的强化学习算法实现
RSL_RL
rsl_rl
是由苏黎世联邦理工学院(ETH Zurich)的机器人系统实验室(Robotic Systems Lab)与 NVIDIA 合作开发的强化学习框架,旨在提供快速且简洁的强化学习算法实现,设计上完全在 GPU 上运行。
目前,RSL-RL 实现了近端策略优化算法(PPO),并在此基础上添加了研究中的新特性,如随机网络蒸馏(RND)和基于对称性的增强等。
目录结构
rsl_rl/
│
└─rsl_rl
│ __init__.py
│
├─algorithms # 算法
│ ppo.py
│ __init__.py
│
├─env # 环境接口设置
│ vec_env.py
│ __init__.py
│
├─modules # 模型结构等
│ actor_critic.py
│ actor_critic_recurrent.py
│ __init__.py
│
├─runners # 运行器,与环境交互、进行训练的核心
│ on_policy_runner.py
│ __init__.py
│
├─storage #
│ rollout_storage.py
│ __init__.py
│
└─utils # 工具
utils.py
__init__.py
重点文件解析
vec_env.py
VecEnv
类是一个继承自 ABC (abstract base class)
的类,用于表示向量化环境的最小接口(必须实现的方法和属性),向量化环境可以通过并行学习来提供训练的效率
在 rsl_rl
中,所有环境被抽象为 VecEnv
这样的类,环境的具体性质(包括机器人的属性和任务的属性等)以及Agent与环境的交互方法的实现细节对 rsl_rl
不可见;通过 VecEnv
提供的接口,runner
将策略网络生成的动作运用于机器人,并收集环境的数据以进行策略评估和训练
step
方法:与环境交互的一步reset
方法:重置机器人get_observations
方法:获取观察get_privileged_observations
方法:用于教师模型
on_policy_runner.py
OnPolicyRunner
类是一个用于基于策略梯度的强化学习算法的运行器类,管理智能体与环境的交互过程,执行训练和评估任务
__init__
:完成runner
实例的初始化,初始化 actor_critic 策略网络(教师网络),estimator 网络(估计特权信息),在使用深度相机时初始化深度编码器和基于深度相机的策略网络(学生模型)learn
:学习函数。包括初始化观测、训练损失;运行训练循环,执行动作收集反馈,更新策略;记录训练信息与定期保存模型log
:日志函数。用于记录训练信息,例如各种损失和训练奖励
__init__:初始化各个参数,关键参数与操作如下:
env
: 环境接口,其由LeggedRobot
及继承自LeggedRobot
的具体类实现,包含机器人仿真中和环境交互的方法;以及从环境中获取观察的方法train_cfg
:LeggedRobotCfgPPO
类转换而来的字典,包含训练算法所需的参数,例如: 网络结构、策略类型、激活函数,PPO 算法的相关参数;深度编码器的相关配置- 初始化
actor_critic
,estimator
,depth_encoder
,depth_actor
;具体网络结构见后文对应文件解析 - 初始化
RolloutStorage
, 收集、管理和处理智能体与环境交互的经验数据,为策略更新提供支持
learn_RL: 教师模型的训练函数
- 初始化各类
loss
及reward
;一方面用于训练的迭代,另一方面用于日志记录 - 主循环:循环次数实际为
num_learning_iterations
,在内循环结束后计算一次奖励并进行更新策略(这里有个问题,critic实际上用的并不是特权观察信息),也就是说策略更新的频率是1/num_steps_per_env
,与iteration
同频 - 内循环:每次
iteration
中,根据上一轮观察产生动作,应用动作与环境交互,获取新的观察和奖励,根据当前step
奖励和信息处理rollout
,记录日志
learn_vision: 学生模型的训练函数 (这里特指使用深度相机策略)
- 初始化各类缓冲区及
loss
- 主循环:与
learn_RL
相同,在内循环结束后更新策略,不同的是,并非用强化学习的方法更新策略,而是使用蒸馏的方式,是学生模型depth_actor
与教师模型actor_critic.actor
对齐 - 内循环:循环次数为
update_interval * num_steps_per_env
,与learn_RL
不同的是,根据obs
首先用教师模型actor_critic
获得scandot_latent
和actions_teacher
,再用由depth_encoder
处理后的depth_latent
和yaw
对应的obs
输入的策略模型depth_actor
得到action_student
,使用action_student
和环境交互得到新的观察
log 类函数不再赘述,主要用于记录日志和进行可视化分析
save 用于保存模型,load 用于加载先前预训练的模型
get_xxx_policy 类函数用于运行训练好的模型进行推理
actor_critic.py
-
Actor
类 -
ActorCriticRMA
类是一个基于 Actor-Critic 架构的递归记忆增强网络(Recurrent Memory-Augmented)- __init__: 初始化
actor
和critic
网络,以及动作分布distribution
(正态分布) - critic网络结构:一个
MLP
,输入(num_obs) -> 激活函数 -> 线性隐藏层1(256) -> 激活函数 -> 线性隐藏层2(256) -> 激活函数 -> 输出层(256) -> 输出(1);用于根据当前的环境观察(状态空间大小为num_obs=800维)得到值函数的估计 - actor网络结构:由编码器,主干MLP,和输出层构成,其中编码器分为:
scan_encoder
,priv_encoder
,hist_encoder
前两种编码器均为 MLP,hist_encoder 为 CNN 与 MLP 结合
scan_encoder
: 输入(num_scan) -> 线性隐藏层1(256) + ELU -> 线性隐藏层(256) + ELU -> 线性隐藏层3(256) + Tanh = 输出(256)priv_encoder
: 输入(num_priv_latent) -> 线性隐藏层1(64) + ELU -> 线性隐藏层2(20) + ELU = 输出(20)hist_encoder
: 输入([envs * history_len, num_prop]) -> FC([envs * history_len, 3 * channel_size]) + ELU -> Conv1d_1([envs, 3 * channel_size, history_len] -> [envs, 2 * channel_size, (history_len-4)/2+1]) -> Conv1d_2([envs, channel_size, 3]) -> Flatten([envs, 30]) -> 输出层(20=priv_encoder_output) + ELU; 注意,实际上的输入形状为 [envs, history_len, num_prop], 被重塑为 [envs * history_len, num_prop]backbone
: 输入(input) -> 线性隐藏层1(256) + ELU -> 线性隐藏层2(256) + ELU -> 线性隐藏层3(256) + ELU -> 输出层(4)- backbone的输入:num_prop + scan_latent + priv_explicit + priv_latent, 其中
priv_latent
在 hist_encoding 为 True 时为经过 hist_encoder 编码的 latent,否则为经过 priv_encoder 编码的 latent
- __init__: 初始化
actor_critic_recurrent.py
在项目中实际并没有使用该类,而是只使用了原始的 Actor
类
depth_backbone.py
该文件定义了处理深度图像得到潜在特征的网络
输入:[batch_size, frames, height, width],其中 frames=1 为深度图像,batch_size 一般为 num_envs
- DepthOnlyFCBackbone58x87 类:该类为处理深度图像的第一层,只通过卷积层、池化层和全连接层提取单帧图片的信息,将图像编码后交给
RecurrentDepthBackbone
做进一步处理- input([1, 58, 87]) -> Conv2d_1([32, 54, 83]) -> MaxPool2d([32, 27, 41]) -> Conv2d_2([64, 25, 39]) -> Flatten(64 * 25 * 39) -> Linear(128) -> Output(32)
- RecurrentDepthBackbone 类:该类为处理深度图像提取潜在特征的主干网络,想通过
DepthOnlyFCBackbone58x87
将深度图像编码然后经过GRU
进行时序处理- input([1, 58, 87]) -> DepthOnlyFCBackbone(depth_code[32]) -> Linear(32+n_prop) -> Linear(128) + ELU -> Linear(32) -> GRU(hidden_size=512) -> Linear(512) -> Linear(34) + Tanh -> output_latent([34])
StackDepthEncoder 类暂时没有启用