16 minute read

快速且简洁的强化学习算法实现

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 类是一个用于基于策略梯度的强化学习算法的运行器类,管理智能体与环境的交互过程,执行训练和评估任务

  1. __init__:完成 runner 实例的初始化,初始化 actor_critic 策略网络(教师网络),estimator 网络(估计特权信息),在使用深度相机时初始化深度编码器和基于深度相机的策略网络(学生模型)
  2. learn:学习函数。包括初始化观测、训练损失;运行训练循环,执行动作收集反馈,更新策略;记录训练信息与定期保存模型
  3. log:日志函数。用于记录训练信息,例如各种损失和训练奖励

__init__:初始化各个参数,关键参数与操作如下:

  • env: 环境接口,其由LeggedRobot及继承自LeggedRobot的具体类实现,包含机器人仿真中和环境交互的方法;以及从环境中获取观察的方法
  • train_cfg: LeggedRobotCfgPPO类转换而来的字典,包含训练算法所需的参数,例如: 网络结构、策略类型、激活函数,PPO 算法的相关参数;深度编码器的相关配置
  • 初始化 actor_critic, estimator, depth_encoder, depth_actor;具体网络结构见后文对应文件解析
  • 初始化 RolloutStorage, 收集、管理和处理智能体与环境交互的经验数据,为策略更新提供支持

learn_RL: 教师模型的训练函数

  • 初始化各类 lossreward;一方面用于训练的迭代,另一方面用于日志记录
  • 主循环:循环次数实际为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_latentactions_teacher,再用由 depth_encoder 处理后的 depth_latentyaw 对应的 obs 输入的策略模型 depth_actor 得到 action_student,使用 action_student 和环境交互得到新的观察

log 类函数不再赘述,主要用于记录日志和进行可视化分析

save 用于保存模型,load 用于加载先前预训练的模型

get_xxx_policy 类函数用于运行训练好的模型进行推理

actor_critic.py

  1. Actor

  2. ActorCriticRMA 类是一个基于 Actor-Critic 架构的递归记忆增强网络(Recurrent Memory-Augmented)

    • __init__: 初始化 actorcritic 网络,以及动作分布 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

actor_critic_recurrent.py

在项目中实际并没有使用该类,而是只使用了原始的 Actor

depth_backbone.py

该文件定义了处理深度图像得到潜在特征的网络

输入:[batch_size, frames, height, width],其中 frames=1 为深度图像,batch_size 一般为 num_envs

  1. 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)
  2. 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 类暂时没有启用

ppo

Updated: