CartPole-v1 环境的代码,分为6部分。
def __init__(self): def seed(self, seed=None): def step(self, action):
def reset(self): def render(self, mode='humn'): def close(self):
import math
import gym
from gym import spaces, logger
from gym.utils import seeding
import numpy as np
from gym.envs.classic_control import rendering
class CartPoleEnv(gym.Env):
metadata = {
'render.modes': ['human', 'rgb_array'],
'video.frames_per_second': 50
}
#####①
def __init__(self):
self.gravity = 9.8 #重力加速度g
self.masscart = 1.0 #车重量
self.masspole = 0.1 #杆重量
self.total_mass = (self.masspole + self.masscart) #(0.1 + 1) = 1.1,车和杆的重量
self.length = 0.5 # 计算杆高度用
self.polemass_length = (self.masspole * self.length) #(0.1 * 0.5) = 0.05,计算加速度用
self.force_mag = 10.0 #输入动作,每次施加给车的力
self.tau = 0.02 # 状态state更新之间的秒数
self.kinematics_integrator = 'euler' #运动学积分器,为了计算下一步state
# Angle at which to fail the episode 本局失败的角度
self.theta_threshold_radians = 12 * 2 * math.pi / 360 # 12 * 2 * 180° / 360° = 12 °
self.x_threshold = 2.4
# Angle limit set to 2 * theta_threshold_radians so failing observation
# 角度限制设为 2 * theta_threshold_radians,这就是失败的观察
# is still within bounds. 仍在范围内
high = np.array([self.x_threshold * 2, # 4.8
np.finfo(np.float32).max, #取float的最大值
self.theta_threshold_radians * 2, #24°
np.finfo(np.float32).max], #取float的最大值
dtype=np.float32)
self.action_space = spaces.Discrete(2) #离散动作定义(2个动作)为0,1
self.observation_space = spaces.Box(-high, high, dtype=np.float32) #连续状态定义(四种都是连续的)
# Num Observation Min Max
# 0 Cart Position -4.8 4.8
# 1 Cart Velocity -Inf Inf
# 2 Pole Angle -0.418 rad (-24 deg) 0.418 rad (24 deg)
# 3 Pole Angular Velocity -Inf Inf
self.seed()
self.viewer = None
self.state = None
self.steps_beyond_done = None
####②
def seed(self, seed=None):
self.np_random, seed = seeding.np_random(seed)
return [seed]
###③step()源码
# 该函数在仿真器中扮演物理引擎的角色,。一个仿真环境必不可少的两部分是物理引擎和图像引擎。
# 物理引擎模拟环境中物体的运动规律;图像引擎用来显示环境中的物体图像
# 该函数描述了智能体与环境交互的所有信息。 输入:action,输出:observation,reward,done,info
def step(self, action):
err_msg = "%r (%s) invalid" % (action, type(action))
assert self.action_space.contains(action), err_msg # 判断动作空间是否包含输入的action,否则报err_msg的错
x, x_dot, theta, theta_dot = self.state # 系统当前状态,包括车位置x,车速x_dot, 杆角度theta, 杆顶端的速度theta_dot
force = self.force_mag if action == 1 else -self.force_mag # 输入动作,(1代表右),即作用到车上的力
costheta = math.cos(theta) # 计算角度cos值
sintheta = math.sin(theta) # 计算角度sin值
# 施加力对杆子和小车影响的数学公式
# temp:车摆的动力学方程式,即加速度与动作之间的关系
temp = (force + self.polemass_length * theta_dot ** 2 * sintheta) / self.total_mass
# temp=(±10 + 0.05 * 杆顶速度^2 * sin() ) / 1.1
# thetaacc:摆的角加速度
thetaacc = (self.gravity * sintheta - costheta * temp) / (
self.length * (4.0 / 3.0 - self.masspole * costheta ** 2 / self.total_mass))
# 小车的平加速度
xacc = temp - self.polemass_length * thetaacc * costheta / self.total_mass
# 计算下一步的state(x, x_dot, theta, theta_dot)
if self.kinematics_integrator == 'euler': # (欧拉)
# tau是更新步长0.02
x = x + self.tau * x_dot
x_dot = x_dot + self.tau * xacc
theta = theta + self.tau * theta_dot
theta_dot = theta_dot + self.tau * thetaacc
else: # 半隐式欧拉
x_dot = x_dot + self.tau * xacc
x = x + self.tau * x_dot
theta_dot = theta_dot + self.tau * thetaacc
theta = theta + self.tau * theta_dot
self.state = (x, x_dot, theta, theta_dot)
# state包括车位置x,车速x_dot, 杆角度theta, 杆顶端的速度theta_dot
# 结束的条件
done = bool(
x < -self.x_threshold
or x > self.x_threshold # 车的位置小于-2.4,大于2.4
or theta < -self.theta_threshold_radians
or theta > self.theta_threshold_radians # 角度小于-12°,大于12°
)
# 奖励
if not done:
reward = 1.0
elif self.steps_beyond_done is None:
# Pole just fell! 杆刚落下
self.steps_beyond_done = 0
reward = 1.0
else:
if self.steps_beyond_done == 0:
logger.warn("您正在调用‘step()’,即使此环境已返回done=True。"
"一旦收到‘done=True’,您应该始终调用‘reset()’——任何进一步的步骤都是未定义的行为。")
self.steps_beyond_done += 1 # 当前的step加一
reward = 0.0 # 奖励为0
return np.array(self.state), reward, done, {}
####④reset()源码,self.np_random从 def seed()来
def reset(self):
# 初始化环境状态,所有观察值都被赋予(-0.05,0.05)中一个均匀随机值
self.state = self.np_random.uniform(low=-0.05, high=0.05, size=(4,))
# 从一个均匀分布[low,high)中随机采样,注意定义域是左闭右开,
# 设置当前步数为None
self.steps_beyond_done = None
# 返回环境的初始化状态
return np.array(self.state)
####⑤render()源码
# render()函数在这里扮演图像引擎的角色。一个仿真环境必不可少的两部分是物理引擎和图像引擎。
# 物理引擎模拟环境中物体的运动规律;图像引擎用来显示环境中的物体图像
def render(self, mode='human'):
screen_width = 600
screen_heigt = 400 # 画布的高宽
world_width = self.x_threshold * 2 # 整个小车在x轴的位置范围(2.4*2 = 4.8)
scale = screen_width / world_width # 尺寸600/4.8 =125
carty = 100 # 小车离画布低端100
polewidth = 10.0 # 杆的宽度
polelen = scale * (2 * self.length) # 125*(2*0.5)=125
cartwidth = 50.0 # 小车宽度
cartheight = 30.0 # 小车高读
if self.viewer is None: # 如果显示器是空的
self.viewer = rendering.Viewer(screen_width, screen_heigt) # 显示画布
# 创建车
l, r, t, b = -cartwidth / 2, cartwidth / 2, cartheight / 2, -cartheight / 2
# l = -25, r = 25, t = 15, b = -15
axleoffset = cartheight / 4.0 # 车轴7.5
cart = rendering.FilledPolygon([(l, b), (l, t), (r, t), (r, b)])
# 通过长方形的四个点来画小车,以小车的中心为原点,顺时针对应四个点的顺序(从第三象限为第一个点)
# Transform给cart添加平移属性和旋转属性
self.carttrans = rendering.Transform()
cart.add_attr(self.carttrans)
self.viewer.add_geom(cart) # 在显示器上添加小车
# 创建杆
l, r, t, b = -polewidth / 2, polewidth / 2, polelen - polewidth / 2, -polewidth / 2
# l = -5, r = 5, t =120,b=-5
pole = rendering.FilledPolygon([(l, b), (l, t), (r, t), (r, b)])
# 通过长方形的四个点来画杆,但是是以小车的中心为原点
pole.set_color(.8, .6, .4) # 给杆设置颜色
# 添加摆杆装换矩阵属性
self.poletrans = rendering.Transform(translation=(0, axleoffset)) # 杆中心平移()
pole.add_attr(self.poletrans) # 对杆进行平移赋值
pole.add_attr(self.carttrans) # 对杆进行平移赋值
self.viewer.add_geom(pole) # 在显示器上添加杆
# 创建摆杆和车之间的连接
self.axle = rendering.make_circle(polewidth / 2) # 圆的直径为5
self.axle.add_attr(self.poletrans) # 对圆进行平移赋值
self.axle.add_attr(self.carttrans) # 对圆进行平移赋值
self.axle.set_color(.5, .5, .8) # 给圆设置颜色
self.viewer.add_geom(self.axle) # 在显示器上添加圆
# 创建轨道,即一条线
self.track = rendering.Line((0, carty), (screen_width, carty)) # (0,100),(600,100)
self.track.set_color(0, 0, 0) # 给线设置颜色
self.viewer.add_geom(self.track) # 在显示器上添加线
self._pole_geom = pole # ???
if self.state is None:
return None
# 编辑极点多边形顶点
pole = self._pole_geom
l, r, t, b = -polewidth / 2, polewidth / 2, polelen - polewidth / 2, -polewidth / 2
# l = -5, r = 5, t = 120, b = -5
pole.v = [(l, b), (l, t), (r, t), (r, b)]
# 设置平移属性
x = self.state #这里的x是状态,那么状态有四个属性,包括车位置x,车速x_dot, 杆角度theta, 杆顶端的速度theta_dot
cartx = x[0] * scale + screen_width / 2.0 # 车中央 车位置 *125 +600/2
self.carttrans.set_translation(cartx, carty) # ???
self.poletrans.set_rotation(-x[2]) # 杆角度的负数
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
####⑥
def close(self):
if self.viewer:
self.viewer.close()
self.viewer = None
参考
https://zhuanlan.zhihu.com/p/409284868https://zhuanlan.zhihu.com/p/409284868
https://blog.csdn.net/weixin_42421591/article/details/123291275gym render画图函数参数讲解以及深入浅出强化学习--原理入门第二章作业https://blog.csdn.net/weixin_42421591/article/details/123291275
强化学习之Gym基础入门(1) - 灰信网(软件开发博客聚合)强化学习之Gym基础入门(1),灰信网,软件开发博客聚合,程序员专属的优秀博客文章阅读平台。https://www.freesion.com/article/243529415/
https://blog.csdn.net/weixin_42301220/article/details/109843914?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109843914-blog-90696899.pc_relevant_multi_platform_whitelistv5&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109843914-blog-90696899.pc_relevant_multi_platform_whitelistv5&utm_relevant_index=1https://blog.csdn.net/weixin_42301220/article/details/109843914?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109843914-blog-90696899.pc_relevant_multi_platform_whitelistv5&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-109843914-blog-90696899.pc_relevant_multi_platform_whitelistv5&utm_relevant_index=1