在前几章机器人步态仿真系列中,我们详细介绍了如何通过PyBullet对机器人进行仿真,本章我们将搭建PyBullet环境并结合DI-engine运行SAC算法。
Pybullet运行效果:

一、PyBullet环境概述

PyBullet提供了一个物理仿真平台,广泛应用于游戏开发、视觉效果制作、机器人技术以及作为评估连续空间强化学习策略的标准测试工具。此平台包括多个不同的环境,总计有20种场景供使用者选择,包括各种运动和操纵任务,如单脚跳跃机器人(Hopper)、双足行走机器人(Walker2D)、半猎豹(Half Cheetah)、蚂蚁(Ant)、人形机器人(Humanoid)、操纵器(Manipulator)和摆(Pendulum)等。参见附图,展示了Hopper环境的实例。

安装方法

可以通过 pip 一键安装或结合 DI-engine 安装,只需安装 gym 和 pybullet-gym 两个库即可

 Install pybullet-gym
pip install gym
git clone https://github.com/benelot/pybullet-gym.git
cd pybullet-gym
pip install -e .

PyBullet原始环境

在强化学习(RL)中,智能体(agent)通过与环境交互来学习行为策略,以最大化累积奖励。环境给予的每个状态、可执行的动作和相应的奖励定义了学习的基础。在使用PyBullet这样的物理仿真库进行RL训练时,变换前的原始环境中观察空间、动作空间和奖励空间是三个关键要素:

观察空间

观察空间由物理信息组成的向量定义,这些信息可能包括:

  • 3D位置
  • 方向(通常使用四元数或欧拉角表示)
  • 关节角度
  • 可能还包括速度、加速度等动态信息

在Hopper环境中,观察空间的具体尺寸(N)由环境的复杂性和所模拟物体的自由度确定。例如,如果Hopper模型有三个可移动关节,观察向量可能包含每个关节的角度和速度,因此观察空间的维度可能是(N = 3)关节角度加上(3)关节速度,总共(6)个浮点数。

数据类型通常是float64

动作空间

动作空间定义了智能体可以采取的所有可能动作。在连续控制任务中,这些动作通常涉及施加扭矩或力到机器人的关节上。比如在Hopper环境中,假设动作空间的维度(N)也是(3),表示可以对三个关节分别施加扭矩。

动作通常被限制在一定范围内,比如在([-1, 1])区间,智能体可以为每个关节施加从最大负扭矩到最大正扭矩的任何值。这个操作可以对应于实际物理系统的操作限制。

动作的数据类型是np.float32,因为在许多机器学习框架中,float32在精度和计算效率之间提供了良好的平衡。

奖励空间

奖励空间是根据智能体的性能给予的反馈。
举个栗子,在Hopper环境中,奖励可能会基于以下因素:

  • 智能体移动的距离(鼓励前进)
  • 保持平衡的能力(惩罚倒下)
  • 能量消耗(惩罚过大的动作)
  • 达到特定速度或高度的奖励

奖励一半来说是一个浮点数值,可以使智能体通过梯度下降类的算法来调整其行为策略。这个值非常重要,他确保智能体被鼓励学习到完成任务的最优策略,而不是只为了获取奖励而游戏系统。

关键事实

  • Vector 物理信息输入,由实际经验可知,在做 norm 时不宜减去均值。

  • 连续动作空间

  • 稠密奖励

  • 奖励取值尺度变化较大

    三、PyBullet RL环境

    观察空间

    基本无变换

    动作空间

    基本无变换,依然是大小为 N 的连续动作空间,取值范围[-1, 1],尺寸为(N, ),数据类型为np.float32

    奖励空间

    基本无变换

上述环境使用 gym 环境空间定义可表示为:

import gym


obs_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(11, ), dtype=np.float64)
act_space = gym.spaces.Box(low=-1, high=1, shape=(3, ), dtype=np.float32)
rew_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(1, ), dtype=np.float32)

代码解释

这段代码使用了OpenAI Gym库来定义强化学习环境中的不同空间:观察空间、动作空间和奖励空间。

import gym

这行代码导入了OpenAI Gym库,它是一个用于开发和比较强化学习算法的工具包。

obs_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(11, ), dtype=np.float64)

这行代码创建了一个观察空间(obs_space),它是gym.spaces.Box类的一个实例。这个类用于定义一个n维的盒子空间,表示所有可能的状态或观察值的范围。在这个具体的示例中,观察空间是一个11维的空间,每个维度的取值范围是负无穷到正无穷(这表示观察值在每个维度上没有限制),并且数据类型被设置为np.float64(双精度浮点数)。

act_space = gym.spaces.Box(low=-1, high=1, shape=(3, ), dtype=np.float32)

这行代码定义了动作空间(act_space)。与观察空间类似,它也是gym.spaces.Box类的一个实例。动作空间是一个三维的空间,每个维度的取值范围是从-1到1。这种设计通常用于模拟需要精细控制的环境,例如机器人的关节角度。数据类型被设置为np.float32(单精度浮点数)。

rew_space = gym.spaces.Box(low=-np.inf, high=np.inf, shape=(1, ), dtype=np.float32)

这行代码尝试定义一个奖励空间(rew_space),尽管在标准的OpenAI Gym环境中通常不需要定义奖励空间,因为奖励通常是一个标量值而不是一个向量。如果你确实需要定义,这里的奖励空间被设置为一个一维的空间,取值范围是负无穷到正无穷,并且数据类型是np.float32

注意事项

  • 数据类型(dtype):观察空间使用np.float64,而动作空间和奖励空间使用np.float32。一般来说在机器学习任务中,使用np.float32已经足够啦,他在计算上更加高效,而且占用内存更少。使用np.float64可能会增加计算的精度。
  • 无穷范围(-np.inf, np.inf):用负无穷和正无穷来定义空间的上下界是表示这些维度理论上没有限制,但在实际使用中,模型会受到浮点数表示的限制。
  • Gym空间的shape属性shape属性定义了空间的维度,例如观察空间是11维的,动作空间是3维的。这个属性对于建立模型和算法非常重要,它告诉算法需要预期的输入和输出的大小。
  • 盒子空间(Box)gym.spaces.Box用于定义连续的值,这与离散动作空间(如gym.spaces.Discrete)形成对比,后者用于定义有限数量的离散动作。

    Di-engine运行SAC算法示例

       from easydict import EasyDict
    
       hopper_sac_default_config = dict(
       env=dict(env_id='HopperMuJoCoEnv-v0',
       norm_obs=dict(use_norm=False, ),
       norm_reward=dict(use_norm=False, ),
       collector_env_num=1,
       evaluator_env_num=8,
       use_act_scale=True,
       n_evaluator_episode=8,
       stop_value=6000,),
       policy=dict(cuda=True,
       on_policy=False,
       random_collect_size=10000,
       model=dict(
           obs_shape=11,
           action_shape=3,
           twin_critic=True,
           actor_head_type='reparameterization',
           actor_head_hidden_size=256,
           critic_head_hidden_size=256,
       ),
       learn=dict(
           update_per_collect=1,
           batch_size=256,
           learning_rate_q=1e-3,
           learning_rate_policy=1e-3,
           learning_rate_alpha=3e-4,
           ignore_done=False,
           target_theta=0.005,
           discount_factor=0.99,
           alpha=0.2,
           reparameterization=True,
           auto_alpha=False,
       ),
       collect=dict(
           n_sample=1,
           unroll_len=1,
       ),
       command=dict(),
       eval=dict(),
       other=dict(replay_buffer=dict(replay_buffer_size=1000000, ), ),
       ),
       )
       hopper_sac_default_config = EasyDict(hopper_sac_default_config)
       main_config = hopper_sac_default_config
       hopper_sac_default_create_config = dict(
       env=dict(
       type='pybullet',
       import_names=['dizoo.pybullet.envs.pybullet_env'],
       ),
       env_manager=dict(type='base'),
       policy=dict(
       type='sac',
       import_names=['ding.policy.sac'],
       ),
       replay_buffer=dict(type='naive', ),
       )
       hopper_sac_default_create_config = EasyDict(hopper_sac_default_create_config)
       create_config = hopper_sac_default_create_config
       if __name__ == '__main__':
       from ding.entry import serial_pipeline
       serial_pipeline((main_config, create_config), seed=0) 

代码分析

这段 Python 代码使用了 DI-engine 强化学习框架,配置并运行了针对 PyBullet 中的 HopperMuJoCoEnv-v0 环境的 Soft Actor-Critic (SAC) 算法。下面是代码的分步解析:

1. 导入 EasyDict

from easydict import EasyDict

EasyDict 允许通过属性的方式访问字典的值,这使得配置管理变得更加方便。

2. 配置主要参数

hopper_sac_default_config = dict(
    env=dict(
        # 环境配置
        ...
    ),
    policy=dict(
        # 策略配置,包括模型结构、学习参数等
        ...
    ),
    ...
)

这部分定义了主要的配置字典,其中包含了环境和策略的参数。

  • env_id 指定了使用的环境 ID。
  • norm_obsnorm_reward 设置为 False,意味着不对观测和奖励进行标准化处理。
  • collector_env_numevaluator_env_num 分别指定了用于采集数据和评估的环境实例数量。
  • use_act_scale 表明动作值是否需要缩放。
  • stop_value 是训练停止的目标奖励值。

在策略配置方面:

  • cuda 设置为 True,表示使用 GPU 加速。
  • on_policy 设置为 False,因为 SAC 是一个 off-policy 算法。
  • model 定义了模型的观测空间维度、动作空间维度和网络结构。
  • learn 包含了学习过程中的一些关键参数,例如每次数据采集后的模型更新次数、批处理大小、学习率等。

3. 配置创建参数

hopper_sac_default_create_config = dict(
    env=dict(
        # 环境的类型和导入路径
        ...
    ),
    env_manager=dict(type='base'),
    policy=dict(
        # 策略类型和导入路径
        ...
    ),
    replay_buffer=dict(type='naive', ),
    ...
)

这部分定义了创建强化学习实验所需组件的配置,如环境类型、策略类型以及经验回放缓冲区类型。

4. 将配置转换为 EasyDict 对象

hopper_sac_default_config = EasyDict(hopper_sac_default_config)
main_config = hopper_sac_default_config
hopper_sac_default_create_config = EasyDict(hopper_sac_default_create_config)
create_config = hopper_sac_default_create_config

将之前定义的字典转换为 EasyDict 对象,以方便后续通过属性访问。

5. 运行训练流程

if __name__ == '__main__':
    from ding.entry import serial_pipeline
    serial_pipeline((main_config, create_config), seed=0)

当脚本作为主程序执行时,导入 DI-engine 中的 serial_pipeline 函数,并将之前定义的主配置和创建配置作为参数传入,以及一个随机种子,开始训练流程。

这段代码用以在 PyBullet 提供的 HopperMuJoCoEnv-v0 环境中应用 SAC 强化学习算法。整个流程包括了环境的设置、策略的选择和配置以及训练的执行,并且充分利用了 DI-engine 框架的功能来简化强化学习算法的应用和实验。

基准算法性能