Pendulum 是一个强化学习的经典游戏,游戏目标是希望控制红色的杆竖直向上。
Pendulum环境定义了坐标系、动力学方程等,还有state,action,reward。需要自己写的部分是网络结构和更新算法。
gym 源码:https://github.com/openai/gym/blob/master/gym/envs/classic_control/pendulum.py
倒立摆摆动问题是基于控制理论中的经典问题。该系统由一端连接到固定点的钟摆组成,另一端是自由的。钟摆从一个随机位置开始,目标是在自由端施加扭矩使其摆动。其重心位于固定点的正上方。
x,y:钟摆末端的笛卡尔坐标,单位为米。
θ:以弧度表示的角度。
tau:以‘N m’为单位的扭矩。定义为顺时针
action space 定义为(1,),物理意义是作用于倒立摆自由端的扭矩,值域[-2,2]
observation space 定义为(3,)
reward:r = -(theta2 + 0.1 * theta_dt2 + 0.001 * torque2)
其中 θ \theta θ 倒立摆角度,以弧度表示, θ ∈ [ − p i , p i ] \theta \in [-pi, pi] θ∈[−pi,pi] (0是竖直向上).
基于以上公式,最小的reward是:-(pi2 + 0.1 * 82 + 0.001 * 22) = -16.2736044,
最大的reward是0 (竖直向上,速度是0,没有扭矩).
初始状态:角度随机 in [-pi, pi] ,角速度随机 in [-1,1].
代码参考了全网最多的那份pytorch版,最后跑出来的结果:
(前50 episode 在攒数据,没有训练)
reward波动有点大,不过看动画效果,模型是学到了的。
# 源码
from os import path
from typing import Optional
import numpy as np
import gym
from gym import spaces
from gym.envs.classic_control import utils
from gym.error import DependencyNotInstalled
from gym.utils.renderer import Renderer
DEFAULT_X = np.pi
DEFAULT_Y = 1.0
class PendulumEnv(gym.Env):
def __init__(self, render_mode: Optional[str] = None, g=10.0):
self.max_speed = 8
self.max_torque = 2.0
self.dt = 0.05
self.g = g
self.m = 1.0
self.l = 1.0
self.render_mode = render_mode
self.renderer = Renderer(self.render_mode, self._render) #gym自带渲染器
self.screen_dim = 500
self.screen = None
self.clock = None
self.isopen = True
high = np.array([1.0, 1.0, self.max_speed], dtype=np.float32)
#定义动作空间和观察空间
self.action_space = spaces.Box(
low=-self.max_torque, high=self.max_torque, shape=(1,), dtype=np.float32
)
self.observation_space = spaces.Box(low=-high, high=high, dtype=np.float32)
def step(self, u):
#step 函数返回四个值observation、reward、done、info
#observation (object):一个与环境相关的对象描述你观察到的环境,如相机的像素信息,机器人的角速度和角加速度,棋盘游戏中的棋盘状态。
#reward (float):先前行为获得的所有回报之和,不同环境的计算方式不一,但目标总是增加自己的总回报。
#done (boolean): 判断是否到了重新设定(reset)环境,大多数任务分为明确定义的episodes,并且完成为True表示episode已终止。
#info (dict):用于调试的诊断信息,有时也用于学习,但正式的评价不允许使用该信息进行学习。 这是一个典型的agent-environment loop 的实现。
th, thdot = self.state # th := theta
g = self.g
m = self.m
l = self.l
dt = self.dt
u = np.clip(u, -self.max_torque, self.max_torque)[0]
self.last_u = u # for rendering
costs = angle_normalize(th) ** 2 + 0.1 * thdot**2 + 0.001 * (u**