PyBullet¶
概述¶
PyBullet 是用于游戏,视觉效果,机器人和强化学习的物理模拟,常来作为连续空间强化学习算法的基准测试环境。它是一系列环境的集合(共有 20 个子环境),常用的子环境有 locomotion(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 .
验证安装¶
安装完成后,可以通过在 Python 命令行中运行如下命令验证安装成功:
import gym # open ai gym
import pybulletgym # register PyBullet enviroments with open ai gym
env = gym.make('HopperMuJoCoEnv-v0')
# env.render() # call this before env.reset, if you want a window showing the environment
obs = env.reset() # should return a state vector if everything worked
print(obs.shape) # (11, )
镜像¶
DI-engine 的镜像配备框架本身,可通过docker pull opendilab/ding:nightly-mujoco
获取,或访问docker
hub 获取更多镜像
变换前的空间(原始环境)¶
观察空间¶
物理信息组成的向量(3D position, orientation, and joint angles etc.),具体尺寸为
(N, )
,其中N
根据环境决定,数据类型为float64
动作空间¶
物理信息组成的向量(torque etc.),一般是大小为 N 的连续动作空间(N 随具体子环境变化),数据类型为
np.float32
,需要传入np数组(例如动作为array([-0.9266078 , -0.4958926 , 0.46242517], dtype=np.float32)
)如在 Hopper 环境中,N 的大小为 3,动作在
[-1, 1]
中取值
奖励空间¶
游戏得分,根据具体游戏内容不同会有非常大的差异,一般是一个
float
数值,具体的数值可以参考最下方的基准算法性能部分。
其他¶
游戏结束即为当前环境 episode 结束
关键事实¶
Vector 物理信息输入,由实际经验可知,在做 norm 时不宜减去均值。
连续动作空间
稠密奖励
奖励取值尺度变化较大
变换后的空间(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)
其他¶
环境
step
方法返回的info
必须包含eval_episode_return
键值对,表示整个 episode 的评测指标,在 Pybullet 中为整个 episode 的奖励累加和
其他¶
惰性初始化¶
为了便于支持环境向量化等并行操作,环境实例一般实现惰性初始化,即__init__
方法不初始化真正的原始环境实例,只是设置相关参数和配置值,在第一次调用reset
方法时初始化具体的原始环境实例。
随机种子¶
环境中有两部分随机种子需要设置,一是原始环境的随机种子,二是各种环境变换使用到的随机库的随机种子(例如
random
,np.random
)对于环境调用者,只需通过环境的
seed
方法进行设置这两个种子,无需关心具体实现细节环境内部的具体实现:对于原始环境的种子,在调用环境的
reset
方法内部,具体的原始环境reset
之前设置环境内部的具体实现:对于随机库种子,则在环境的
seed
方法中直接设置该值
训练和测试环境的区别¶
训练环境使用动态随机种子,即每个 episode 的随机种子都不同,都是由一个随机数发生器产生,但这个随机数发生器的种子是通过环境的
seed
方法固定的;测试环境使用静态随机种子,即每个 episode 的随机种子相同,通过seed
方法指定。
存储录像¶
在环境创建之后,重置之前,调用enable_save_replay
方法,指定游戏录像保存的路径。环境会在每个 episode 结束之后自动保存本局的录像文件。(默认调用gym.wrappers.RecordVideo
实现),下面所示的代码将运行一个环境 episode,并将这个 episode 的结果保存在./video/
中:
from easydict import EasyDict
from dizoo.pybullet.envs import PybulletEnv
env = PybulletEnv(EasyDict({'env_id': 'Hoopper-v3' }))
env.enable_save_replay(replay_path='./video')
obs = env.reset()
while True:
action = env.random_action()
timestep = env.step(action)
if timestep.done:
print('Episode is over, eval episode return is: {}'.format(timestep.info['eval_episode_return']))
break
DI-zoo 可运行代码示例¶
完整的训练配置文件在 github
链接
内,对于具体的配置文件,例如hopper_sac_default_config.py
,使用如下的 demo 即可运行:
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)
注:对于某些特殊的算法,比如 PPO,需要使用专门的入口函数,示例可以参考 link 也可以使用 serial_pipeline_onpolicy 一键进入
基准算法性能¶
Hopper-v3
Hopper-v3 + SAC