环境配置
我的 torch 版本是 2.3.0,然后 gym 版本是 0.22.0,python 版本是 3.8 ,pygame 版本是 2.6.0 。
首先安装一下 gym:
pip install gym==0.22.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
然后安装一下 pygame:
pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple
都安装好之后,可以写上下面的测试代码看是否能正常弹出一个游戏画面:
import gym
env = gym.make('CartPole-v1')
for _ in range(1000): env.reset() for _ in range(100): env.render() env.step(env.action_space.sample())
env.close()
如果一切顺利,你将看到一个倒立摆小车在屏幕上移动,并随着时间步的推移而逐渐失去平衡。
环境介绍:以 CartPole-v0 为例
import gym# 1、介绍倒立摆基本的游戏环境env = gym.make('CartPole-v0')# 打印此时的游戏环境是什么 :<TimeLimit<OrderEnforcing<CartPoleEnv<CartPole-v0>>>>
print(env)# 打印其 action space:Discrete(2)
print(env.action_space)# 对该动作空间做随机采样,也就是随机取出其中一个动作,输出为 0 或 1,也就是往左或者往右
print(env.action_space.sample())# 打印观测空间,也就算 state space 状态空间,是一个意思
# 对于这个游戏环境,其 state space 有四个:
# 下标0表示小车位置,1表示小车速度,2表示锤的偏转角度,3表示锤的偏转角速度
# 补充:状态空间通常包含环境的完整信息,而观测空间可能只是部分信息;
# 在某些情况下,观测空间和状态空间可能是相同的,即智能体可以完全观察到环境的状态,
# 这样的环境被称为完全可观测(Fully Observable)。
# 在这个例子中,二者等价。
# 输出:Box([-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38],
# [4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38], (4,), float32)
print(env.observation_space)# 然后可以打印看一下其边界情况
# 输出:[-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38]
print(env.observation_space.low)
# 输出:[4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38]
print(env.observation_space.high)# 我们同样可以对状态空间进行随机采样
print(env.observation_space.sample())"""
我们并不直接操作状态,我们操作或者说选择的是 action,我们在 action space 中基于一定策略会选择一个 action。
然后作用在这个环境上使得环境状态转移到一个新的环境状态。
状态空间和动作空间环境已经给定了,此外还需要什么?我们还需要做下面两件事情:当我们在当前 t 时刻(意味着此时状态为 st)选择了一个 action at,那么
我们将会得到一个 reward,称为 rt,同时因为在 st 状态下采取了 at 行为,状态 st 也将转移到 s(t+1) 状态下。而实际上环境已经给了一个 step 函数,在这个函数内完成了上述两个步骤。
"""# 来查看一下 step 函数内容
help(env.step)
# 输出如下
"""
step(action) method of gym.wrappers.time_limit.TimeLimit instanceRun one timestep of the environment's dynamics. When end ofepisode is reached, you are responsible for calling `reset()`to reset this environment's state.Accepts an action and returns a tuple (observation, reward, done, info).Args:action (object): an action provided by the agentReturns:observation (object): agent's observation of the current environmentreward (float) : amount of reward returned after previous actiondone (bool): whether the episode has ended, in which case further step() calls will return undefined resultsinfo (dict): contains auxiliary diagnostic information (helpful for debugging, logging, and sometimes learning)
"""
上面的代码是 CartPole 环境的一些介绍和简单使用,而下面的代码则是采用上面介绍的内容让 action 和 environment 交互实战感受一下:
# 2、尝试用 action 和 env 交互
done = False
score = 0
# env.reset()函数的主要作用是将环境重置到其初始状态,并返回这个初始状态给调用者
state = env.reset()while not done:# 可视化窗口展现env.render()# 从 action space 中随机采取一个 actionaction = env.action_space.sample()# 将该 action 传入 env 的 step 中完成 reward 的计算和状态转移observation, reward, done, info = env.step(action)# 统计一下这一轮下来总共获得了多少分数score += reward
print(f'total reward:{score}')
运行结果如下(两部分代码的运行结果一起打印了):
另外如果够专注的话,是能够看到有个画面在屏幕上一闪而过的,对于这个闪退的问题我们后面会解决。
解决闪退问题:将env保存为一个 mp4 / gif
之前的代码闪退太快,来看一下一个比较缓慢的效果:
import gym
import time
env_name = "CartPole-v0"
env = gym.make(env_name)state = env.reset()
done = False
total_reward = 0while not done:env.render()action = env.action_space.sample()# 打印随机采样的actionprint(action)observation, reward, done, info = env.step(action)total_reward += reward# 加一点延迟time.sleep(0.2)
print(total_reward)
运行效果如下:
因为渲染出来的摆锤效果是动态的,因此这里就不再展示,但是你应该和我的效果一样,是能够看到它摆的过程的。
接下来我们要做的事情就是将这么一个动态变化的过程给保存下来,怎么保存?其实重点在 env 中的 render 方法,默认情况下,我们调用 render 方法其有一个参数 mode,默认值为 “human”,其返回值为一个个 bool 值,即是否打开。
此时我们可以将这个默认值改成 “rgb_array”,此时 render 的返回将是当前时刻图像的 rgb 数值数组,这样的话我们就实现了将其放入内存里面的操作。
import gym
import time
env_name = "CartPole-v0"
env = gym.make(env_name)state = env.reset()
done = False
total_reward = 0
# 定义一个帧数组,用来存下每一帧的 rgb 数据
frames = []
while not done:frames.append(env.render(mode="rgb_array"))action = env.action_space.sample()# 打印随机采样的actionprint(action)observation, reward, done, info = env.step(action)total_reward += reward# 加一点延迟time.sleep(0.2)
print(total_reward)
# 再打印一下 frames 数组的一些信息,以更形象化的理解它
print(len(frames))
print(frames[0].shape)
运行结果如下:
可以看到这一次只进行了十四轮小锤就倒了,因此 frames 数组的大小为 14,然后每一帧图片的形状为 (400, 600, 3),一个彩色图像。
接下来我们就可以通过一些第三方的工具包,来实现保存为 mp4 的功能了:
# 引入将 env 保存为 mp4 所需要的包,主要是一个图表包 pyplot 和一个动画包 animation
import matplotlib.pyplot as plt
from matplotlib import animationdef display_frames_to_video(frames):plt.figure(figsize=(frames[0].shape[0]/72, frames[0].shape[1]/72), dpi=72)plt.axis("off")patch = plt.imshow(frames[0])def animate(i):patch.set_data(frames[i])anim = animation.FuncAnimation(plt.gcf(), animate, frames=range(len(frames)),interval=50)anim.save("cartpole.mp4")# 保存为 gif 也很简单:# anim.save("cartpole.gif", writer="imagemagick")
display_frames_to_video(frames)
提一嘴:plot 是英文图表的意思,animation 是英文定格动画的意思。
运行结果如下:
可以看见已经生成了我们想要的 mp4 文件。
对于上面的这一段代码,使用 GPT 给出了一个比较详尽的解释,知道怎么用就行:
对于动画处理这种标准工具代码的流程是比较固定的,大概知道怎么回事就行,用的时候直接 CV。