gym開源庫:包含一個(gè)測試問題集,每個(gè)問題成為環(huán)境(environment)荷鼠,可以用于自己的RL算法開發(fā)句携。這些環(huán)境有共享的接口,允許用戶設(shè)計(jì)通用的算法允乐。其包含了deep mind 使用的Atari游戲測試床矮嫉。
在強(qiáng)化學(xué)習(xí)中有2個(gè)基本概念,一個(gè)是環(huán)境(environment)牍疏,稱為外部世界蠢笋,另一個(gè)為智能體agent(寫的算法)。agent發(fā)送action至environment鳞陨,environment返回觀察和回報(bào)昨寞。
Hello gym
import gym
# 創(chuàng)建一個(gè)小車倒立擺模型
env = gym.make(‘CartPole-v0’)
# 初始化環(huán)境
env.reset()
# 刷新當(dāng)前環(huán)境,并顯示
for _ in range(1000):
env.render()
env.step(env.action_space.sample()) # take a random action
[圖片上傳失敗...(image-f6cf84-1570442880862)]
設(shè)計(jì)理念圖,一個(gè)環(huán)境的step函數(shù)返回需要的信息厦滤,有4種返回值
- observation
- reward
- done :判斷是否到了重新設(shè)定(reset)環(huán)境
- info :用于調(diào)試的診斷信息援岩,有時(shí)也用于學(xué)習(xí),但智能體(agent )在正式的評價(jià)中不允許使用該信息進(jìn)行學(xué)習(xí)掏导。
該進(jìn)程通過調(diào)用reset()來啟動享怀,它返回一個(gè)初始observation。 所以之前代碼的更恰當(dāng)?shù)姆椒ㄊ亲袷?strong>done的標(biāo)志:
空間(Spaces)
在上面的例子中趟咆,已經(jīng)從環(huán)境的動作空間中抽取隨機(jī)動作凹蜈。但這些行動究竟是什么呢? 每個(gè)環(huán)境都帶有action_space 和observation_space對象忍啸。這些屬性是Space類型仰坦,它們描述格式化的有效的行動和觀察。
import gym
env = gym.make('CartPole-v0')
# 離散空間允許固定范圍的非負(fù)數(shù)计雌,因此在這種情況下悄晃,有效的動作是0或1.
print(env.action_space)
#> Discrete(2)
print(env.observation_space)
#> Box(4,)
Box空間表示一個(gè)n維box,所以有效的觀察將是4個(gè)數(shù)字的數(shù)組凿滤。 也可以檢查Box的范圍:
print(env.observation_space.high)
#> array([ 2.4 , inf, 0.20943951, inf])
print(env.observation_space.low)
#> array([-2.4 , -inf, -0.20943951, -inf])
這種內(nèi)省可以幫助編寫適用于許多不同環(huán)境的通用代碼妈橄。box和discrete是最常見的空間。你可以從一個(gè)空間中取樣翁脆,或者檢查某物是否屬于它:
from gym import spaces
space = spaces.Discrete(8) # Set with 8 elements {0, 1, 2, ..., 7}
x = space.sample()
assert space.contains(x)
assert space.n == 8
Env.render畫圖
參考Gym 簡單畫圖
# 首先眷蚓,導(dǎo)入庫文件(包括gym模塊和gym中的渲染模塊)
import gym
from gym.envs.classic_control import rendering
# 我們生成一個(gè)類,該類繼承 gym.Env. 同時(shí)反番,可以添加元數(shù)據(jù)沙热,改變渲染環(huán)境時(shí)的參數(shù)
class Test(gym.Env):
# 如果你不想改參數(shù)叉钥,下面可以不用寫
metadata = {
'render.modes': ['human', 'rgb_array'],
'video.frames_per_second': 2
}
# 我們在初始函數(shù)中定義一個(gè) viewer ,即畫板
def __init__(self):
self.viewer = rendering.Viewer(600, 400) # 600x400 是畫板的長和框
# 繼承Env render函數(shù)
def render(self, mode='human', close=False):
# 下面就可以定義你要繪畫的元素了
line1 = rendering.Line((100, 300), (500, 300))
line2 = rendering.Line((100, 200), (500, 200))
# 給元素添加顏色
line1.set_color(0, 0, 0)
line2.set_color(0, 0, 0)
# 把圖形元素添加到畫板中
self.viewer.add_geom(line1)
self.viewer.add_geom(line2)
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
# 最后運(yùn)行
if __name__ == '__main__':
t = Test()
while True:
t.render()
△.值得注意的是篙贸,畫板的水平方向是 x 軸投队, 垂直方向是 y 軸, 且原點(diǎn)在左下角
畫個(gè)圓
def render(self, mode='human', close=False):
# 畫一個(gè)直徑為 30 的園
circle = rendering.make_circle(30)
# 添加一個(gè)平移操作
circle_transform = rendering.Transform(translation=(100, 200))
# 讓圓添加平移這個(gè)屬性,
circle.add_attr(circle_transform)
self.viewer.add_geom(circle)
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
△注意.是圓心在平移
RingViewr
研究rings時(shí)寫的render
import gym
from gym.envs.classic_control import rendering
import time
import numpy as np
import random
class ringViewer(rendering.Viewer):
'''
畫板,直接繼承自rendering.Viewer
'''
def __init__(self,width, height, display=None):
super(ringViewer, self).__init__(width, height, display=None)
@staticmethod
def pos2loc(pos=0):
'''
根據(jù)位置索引確定畫圖坐標(biāo)
:param pos: 位置索引0-9
:return: loc
'''
pass
@staticmethod
def getSize(size):
'''
設(shè)置畫圓的半徑
:param size:[0-2]
:return: radius
'''
pass
@staticmethod
def getColor(c=0):
'''
根據(jù)顏色索引選擇圓圈顏色
:param c:
:return: list
'''
pass
def drawNewring(self, newring:list=None):
'''
畫新生成的圓
:param newring:
:return:
'''
for i in range(len(newring)):
if newring[i] != 0:
ring = rendering.make_circle(radius=self.getSize(i),
res=50,
filled=False)
r, g, b = self.getColor(newring[i])
ring.set_color(r, g, b)
ring_transform = rendering.Transform(translation=(150,30))
ring.add_attr(ring_transform)
self.add_geom(ring)
def _drawQG(self, qgs: list=None):
'''
畫棋盤上各個(gè)棋格的圓圈
:param qgs:
:return: None
'''
for num,qg in enumerate(qgs):
for i in range(len(qg)):
if qg[i] != 0:
ring = rendering.make_circle(radius=self.getSize(i),
res = 50,
filled=False)
r, g, b = self.getColor(qg[i])
ring.set_color(r, g, b)
ring_transform = rendering.Transform(translation=self.pos2loc(num))
ring.add_attr(ring_transform)
self.add_geom(ring)
def getQG(self, qg: list=None):
'''
將len=27的list轉(zhuǎn)換為[[],[],...]
:param qg: (27,1)的list
:return: (9,1)的list
'''
qgs = []
for x in range(3):
for y in range(3):
tmp = []
for z in range(3):
tmp.append(qg[9*x+3*y+z])
qgs.append(tmp)
self._drawQG(qgs)
class Testenv(gym.Env):
# 如果你不想改參數(shù)爵川,下面可以不用寫
metadata = {
'render.modes': ['human', 'rgb_array'],
'video.frames_per_second': 2
}
def __init__(self):
self.viewer = ringViewer(300, 400) # 600x400 是畫板的長和框
self.state:list = []
self.state:list = []
def setState(self, state):
self.state = state
def setNewring(self, newring=None):
self.newring = newring
def render(self, mode='human', close=False):
# 由于沒有找到viewer源碼中刪除組件的代碼,于是每次在渲染前 清空上一次geoms和onetime_geoms列表 來達(dá)到消除的目的
if self.state.any():
self.viewer.geoms.clear()
self.viewer.onetime_geoms.clear()
self.viewer.getQG(self.state)
if self.newring:
self.viewer.drawNewring(self.newring)
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
if __name__ == '__main__':
v = Testenv()
while True:
v.setState(np.random.randint(0,6,(27)))
v.setNewring([random.randint(0,5) for x in range(3)])
print(v.state)
print(v.newring)
v.render()
time.sleep(2)
△.由于沒有找到viewer源碼中刪除組件的代碼,于是每次在渲染前 清空上一次geoms和onetime_geoms列表 來達(dá)到消除的目的
效果圖如下
深入剖析gym環(huán)境構(gòu)建[轉(zhuǎn)]
由于該博客的代碼展示實(shí)在太亂,于是重新幫他排版了一下
我們繼續(xù)講敷鸦,從第1小節(jié)的尾巴開始。有三個(gè)重要的函數(shù):
- env = gym.make(‘CartPole-v0’)
- env.reset()
- env.render()
第一個(gè)函數(shù)是創(chuàng)建環(huán)境寝贡,我們會在第3小節(jié)具體講如何創(chuàng)建自己的環(huán)境扒披,所以這個(gè)函數(shù)暫時(shí)不講。第二個(gè)函數(shù)env.reset()和第三個(gè)函數(shù)env.render()是每個(gè)環(huán)境文件都包含的函數(shù)圃泡。我們以cartpole為例碟案,對這兩個(gè)函數(shù)進(jìn)行講解。
Cartpole的環(huán)境文件在~你的gym目錄/gym/envs/classic_control/cartpole.py
.
該文件定義了一個(gè)CartPoleEnv的環(huán)境類洞焙,該類的成員函數(shù)有:seed(), step(),reset()和render(). 第1小節(jié)調(diào)用的就是CartPoleEnv的兩個(gè)成員函數(shù)reset()和render()。下面拯啦,我們先講講這兩個(gè)函數(shù)澡匪,再介紹step()函數(shù)
2.1 reset()函數(shù)詳解
reset()為重新初始化函數(shù)。那么這個(gè)函數(shù)有什么用呢褒链?
在強(qiáng)化學(xué)習(xí)算法中唁情,智能體需要一次次地嘗試,累積經(jīng)驗(yàn)甫匹,然后從經(jīng)驗(yàn)中學(xué)到好的動作甸鸟。一次嘗試我們稱之為一條軌跡或一個(gè)episode. 每次嘗試都要到達(dá)終止?fàn)顟B(tài). 一次嘗試結(jié)束后,智能體需要從頭開始兵迅,這就需要智能體具有重新初始化的功能抢韭。函數(shù)reset()就是這個(gè)作用。
reset()的源代碼為:
def _reset()
# 利用均勻隨機(jī)分布初試化環(huán)境的狀態(tài)
self.state = self.np_random.uniform(low=-0.05, high=0.05, size=(4,))
# 設(shè)置當(dāng)前步數(shù)為None
self.steps_beyond_done = None
# 返回環(huán)境的初始化狀態(tài)
return np.array(self.state)
2.2 render()函數(shù)詳解
render()函數(shù)在這里扮演圖像引擎的角色恍箭。一個(gè)仿真環(huán)境必不可少的兩部分是物理引擎和圖像引擎刻恭。物理引擎模擬環(huán)境中物體的運(yùn)動規(guī)律;圖像引擎用來顯示環(huán)境中的物體圖像扯夭。其實(shí)鳍贾,對于強(qiáng)化學(xué)習(xí)算法,該函數(shù)可以沒有交洗。但是骑科,為了便于直觀顯示當(dāng)前環(huán)境中物體的狀態(tài),圖像引擎還是有必要的构拳。另外咆爽,加入圖像引擎可以方便我們調(diào)試代碼梁棠。下面具體介紹gym如何利用圖像引擎來創(chuàng)建圖像。
我們直接看源代碼:
from gym.envs.classic_control import rendering
# 這一句導(dǎo)入rendering模塊伍掀,利用rendering模塊中的畫圖函數(shù)進(jìn)行圖形的繪制
class myenv(gym.Env)
def _render(self, mode=’human’, close=False):
if close:
pass #省略掰茶,直接看關(guān)鍵代碼部分
if self.viewer is None:
# 如繪制600*400的窗口函數(shù)為:
self.viewer = rendering.Viewer(screen_width, screen_height)
# 其中screen_width=600, screen_height=400
# 創(chuàng)建小車的代碼為:
l,r,t,b = -cartwidth/2, cartwidth/2, cartheight/2, -cartheight/2
axleoffset =cartheight/4.0
cart = rendering.FilledPolygon([(l,b), (l,t), (r,t), (r,b)])
# 其中rendering.FilledPolygon為填充一個(gè)矩形蜜笤。
創(chuàng)建完cart的形狀濒蒋,接下來給cart添加平移屬性和旋轉(zhuǎn)屬性。將車的位移設(shè)置到cart的平移屬性中把兔,cart就會根據(jù)系統(tǒng)的狀態(tài)變化左右運(yùn)動沪伙。具體代碼解釋,我已上傳到github上面了县好,gxnk/reinforcement-learning-code 围橡。想深入了解的同學(xué)可去下載學(xué)習(xí)。
2.3 step()函數(shù)詳解
該函數(shù)在仿真器中扮演物理引擎的角色缕贡。其輸入是動作a翁授,輸出是:下一步狀態(tài),立即回報(bào)晾咪,是否終止收擦,調(diào)試項(xiàng)。
該函數(shù)描述了智能體與環(huán)境交互的所有信息谍倦,是環(huán)境文件中最重要的函數(shù)塞赂。在該函數(shù)中,一般利用智能體的運(yùn)動學(xué)模型和動力學(xué)模型計(jì)算下一步的狀態(tài)和立即回報(bào)昼蛀,并判斷是否達(dá)到終止?fàn)顟B(tài)宴猾。
我們直接看源代碼:
def _step(self, action):
assert self.action_space.contains(action), "%r (%s) invalid"%(action, type(action))
state = self.state
x, x_dot, theta, theta_dot = state #系統(tǒng)的當(dāng)前狀態(tài)
force = self.force_mag if action==1 else -self.force_mag #輸入動作,即作用到車上的力
costheta = math.cos(theta) #余弦函數(shù)
sintheta = math.sin(theta) #正弦函數(shù)
#底下是車擺的動力學(xué)方程式叼旋,即加速度與動作之間的關(guān)系仇哆。
temp = (force + self.polemass_length * theta_dot * theta_dot * sintheta) / self.total_mass
thetaacc = (self.gravity * sintheta - costheta* temp) / (self.length * (4.0/3.0 - self.masspole * costheta * costheta / self.total_mass)) #擺的角加速度
xacc = temp - self.polemass_length * thetaacc * costheta / self.total_mass #小車的平移加速
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 #積分求下一步的狀態(tài)
self.state = (x,x_dot,theta,theta_dot)
2.4 一個(gè)簡單的demo
下面,我給出一個(gè)最簡單的demo夫植,讓大家體會一下上面三個(gè)函數(shù)如何使用税产。
import gym
import time
env = gym.make('CartPole-v0')
#創(chuàng)造環(huán)境observation = env.reset()
#初始化環(huán)境,observation為環(huán)境狀態(tài)
count = 0
for t in range(100):
action = env.action_space.sample()
#隨機(jī)采樣動作
observation, reward, done, info = env.step(action)
#與環(huán)境交互偷崩,獲得下一步的時(shí)刻
if done:
break
env.render()
#繪制場景
count+=1
time.sleep(0.2)
#每次等待0.2s
print(count)
#打印該次嘗試的步數(shù)
第3小節(jié):創(chuàng)建自己的gym環(huán)境并利示例qlearning的方法
在上一小節(jié)中以cartpole為例子深入剖析了gym環(huán)境文件的重要組成辟拷。我們知道,一個(gè)gym環(huán)境最少的組成需要包括reset()函數(shù)和step()函數(shù)阐斜。當(dāng)然衫冻,圖像顯示函數(shù)render()一般也是需要的。這一節(jié)谒出,我會以機(jī)器人找金幣為例給大家演示如何構(gòu)建一個(gè)全新的gym環(huán)境隅俘,并以此環(huán)境為例邻奠,示例最經(jīng)典的強(qiáng)化學(xué)習(xí)算法qlearning算法。在3.1節(jié)中为居,給出機(jī)器人找金幣的問題陳述碌宴;第3.2節(jié)中,給出構(gòu)建gym環(huán)境的過程蒙畴;第3.3節(jié)中贰镣,利用qlearning方法實(shí)現(xiàn)機(jī)器人找金幣的智能決策。全部代碼已傳到github上膳凝。
3.1 機(jī)器人找金幣的問題陳述
圖1.1 機(jī)器人找金幣
如圖1.1 為機(jī)器人在網(wǎng)格世界找金幣的示意圖碑隆。該網(wǎng)格世界一共有8個(gè)狀態(tài),其中狀態(tài)6和狀態(tài)8為死亡區(qū)域蹬音,狀態(tài)7為金幣區(qū)域上煤。機(jī)器人的初始位置為網(wǎng)格世界中任意一個(gè)狀態(tài)。機(jī)器人從初始狀態(tài)出發(fā)尋找金幣著淆。機(jī)器人進(jìn)行一次探索劫狠,進(jìn)入死亡區(qū)域或找到金幣,本次探測結(jié)束永部。機(jī)器人找到金幣的回報(bào)為1独泞,進(jìn)入死亡區(qū)域回報(bào)為-1,機(jī)器人在區(qū)域1-5之間轉(zhuǎn)換時(shí)扬舒,回報(bào)為0阐肤。我們的目標(biāo)是找到一個(gè)策略使得機(jī)器人不管處在什么狀態(tài)(1-5)都能找到金幣凫佛。對于這個(gè)機(jī)器人找金幣的游戲讲坎,我們可以利用強(qiáng)化學(xué)習(xí)的方法來實(shí)現(xiàn)。
構(gòu)建網(wǎng)格世界的gym環(huán)境
該例子的代碼,除了本篇博客有以外,OpenAI Gym構(gòu)建自定義強(qiáng)化學(xué)習(xí)環(huán)境有更仔細(xì)和規(guī)范的代碼貼出
一個(gè)gym的環(huán)境文件愧薛,其主體是個(gè)類晨炕,在這里我們定義類名為:GridEnv, 其初始化為環(huán)境的基本參數(shù),因?yàn)闄C(jī)器人找金幣的過程是一個(gè)馬爾科夫過程毫炉,我們在強(qiáng)化學(xué)習(xí)入門課程的第一講已經(jīng)介紹過了一個(gè)馬爾科夫過程應(yīng)該包括狀態(tài)空間瓮栗,動作空間,回報(bào)函數(shù)瞄勾,狀態(tài)轉(zhuǎn)移概率费奸。因此,我們在類GridEnv的初始化時(shí)便給出了相應(yīng)的定義进陡。網(wǎng)格世界的全部代碼在gxnk/reinforcement-learning-code,文件名為 grid_mdp.py. 我們看源代碼:
# 狀態(tài)空間為:
self.states = [1,2,3,4,5,6,7,8]
# 動作空間為:
self.actions = ['n','e','s','w']
# 回報(bào)函數(shù)為:
self.rewards = dict(); #回報(bào)的數(shù)據(jù)結(jié)構(gòu)為字典
self.rewards['1_s'] = -1.0
self.rewards['3_s'] = 1.0
self.rewards['5_s'] = -1.0
# 狀態(tài)轉(zhuǎn)移概率為:
self.t = dict(); #狀態(tài)轉(zhuǎn)移的數(shù)據(jù)格式為字典
self.t['1_s'] = 6
self.t['1_e'] = 2
self.t['2_w'] = 1
self.t['2_e'] = 3
self.t['3_s'] = 7
self.t['3_w'] = 2
self.t['3_e'] = 4
self.t['4_w'] = 3
self.t['4_e'] = 5
self.t['5_s'] = 8
self.t['5_w'] = 4
有了狀態(tài)空間愿阐,動作空間和狀態(tài)轉(zhuǎn)移概率,我們便可以寫step(a)函數(shù)了趾疚。這里特別注意的是缨历,step()函數(shù)的輸入是動作以蕴,輸出為:下一個(gè)時(shí)刻的動作,回報(bào)辛孵,是否終止丛肮,調(diào)試信息。尤其需要注意的是輸出的順序不要弄錯(cuò)了魄缚。對于調(diào)試信息宝与,可以為空,但不能缺少鲜滩,否則會報(bào)錯(cuò)伴鳖,常用{}來代替。我們看源代碼:
step函數(shù)的建立:
def _step(self, action):
#系統(tǒng)當(dāng)前狀態(tài)
state = self.state
#判斷系統(tǒng)當(dāng)前狀態(tài)是否為終止?fàn)顟B(tài)
if state in self.terminate_states:
return state, 0, True, {}
key = "%d_%s"%(state, action) #將狀態(tài)和動作組成字典的鍵值
#狀態(tài)轉(zhuǎn)移
if key in self.t:
next_state = self.t[key]
else:
next_state = state
self.state = next_state
is_terminal = False
if next_state in self.terminate_states:
is_terminal = True
if key not in self.rewards:
r = 0.0
else:
r = self.rewards[key]
return next_state, r,is_terminal,{}
step()函數(shù)就是這么簡單徙硅。下面我們重點(diǎn)介紹下如何寫render()函數(shù)榜聂。從圖1.1機(jī)器人找金幣的示意圖我們可以看到,網(wǎng)格世界是由一些線和圓組成的嗓蘑。因此须肆,我們可以調(diào)用rendering中的畫圖函數(shù)來繪制這些圖像。
render函數(shù)的建立:
整個(gè)圖像是一個(gè)600*400的窗口桩皿,可用如下代碼實(shí)現(xiàn):
from gym.envs.classic_control import rendering
self.viewer = rendering.Viewer(screen_width, screen_height)
# 創(chuàng)建網(wǎng)格世界豌汇,一共包括11條直線,事先算好每條直線的起點(diǎn)和終點(diǎn)坐標(biāo)泄隔,然后繪制這些直線拒贱,代碼如下:
#創(chuàng)建網(wǎng)格世界
def render(self):
self.line1 = rendering.Line((100,300),(500,300))
self.line2 = rendering.Line((100, 200), (500, 200))
self.line3 = rendering.Line((100, 300), (100, 100))
self.line4 = rendering.Line((180, 300), (180, 100))
self.line5 = rendering.Line((260, 300), (260, 100))
self.line6 = rendering.Line((340, 300), (340, 100))
self.line7 = rendering.Line((420, 300), (420, 100))
self.line8 = rendering.Line((500, 300), (500, 100))
self.line9 = rendering.Line((100, 100), (180, 100))
self.line10 = rendering.Line((260, 100), (340, 100))
self.line11 = rendering.Line((420, 100), (500, 100))
# 接下來,創(chuàng)建死亡區(qū)域佛嬉,我們用黑色的圓圈代表死亡區(qū)域逻澳,源代碼如下:
# 創(chuàng)建第一個(gè)骷髏
self.kulo1 = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(140,150))
self.kulo1.add_attr(self.circletrans)
self.kulo1.set_color(0,0,0)
# 創(chuàng)建第二個(gè)骷髏
self.kulo2 = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(460, 150))
self.kulo2.add_attr(self.circletrans)
self.kulo2.set_color(0, 0, 0)
# 創(chuàng)建金幣區(qū)域,用金色的圓來表示:
# 創(chuàng)建金條
self.gold = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(300, 150))
self.gold.add_attr(self.circletrans)
self.gold.set_color(1, 0.9, 0)
# 創(chuàng)建機(jī)器人暖呕,我們依然用圓來表示機(jī)器人斜做,為了跟死亡區(qū)域和金幣區(qū)域不同,我們可以設(shè)置不同的顏色:
# 創(chuàng)建機(jī)器人
self.robot= rendering.make_circle(30)
self.robotrans = rendering.Transform()
self.robot.add_attr(self.robotrans)
self.robot.set_color(0.8, 0.6, 0.4)
# 創(chuàng)建完之后湾揽,給11條直線設(shè)置顏色瓤逼,并將這些創(chuàng)建的對象添加到幾何中代碼如下:
self.line1.set_color(0, 0, 0)
self.line2.set_color(0, 0, 0)
self.line3.set_color(0, 0, 0)
self.line4.set_color(0, 0, 0)
self.line5.set_color(0, 0, 0)
self.line6.set_color(0, 0, 0)
self.line7.set_color(0, 0, 0)
self.line8.set_color(0, 0, 0)
self.line9.set_color(0, 0, 0)
self.line10.set_color(0, 0, 0)
self.line11.set_color(0, 0, 0)
# 添加組件到Viewer中
self.viewer.add_geom(self.line1)
self.viewer.add_geom(self.line2)
self.viewer.add_geom(self.line3)
self.viewer.add_geom(self.line4)
self.viewer.add_geom(self.line5)
self.viewer.add_geom(self.line6)
self.viewer.add_geom(self.line7)
self.viewer.add_geom(self.line8)
self.viewer.add_geom(self.line9)
self.viewer.add_geom(self.line10)
self.viewer.add_geom(self.line11)
self.viewer.add_geom(self.kulo1)
self.viewer.add_geom(self.kulo2)
self.viewer.add_geom(self.gold)
self.viewer.add_geom(self.robot)
# 接下來,開始設(shè)置機(jī)器人的位置库物。機(jī)器人的位置根據(jù)其當(dāng)前所處的狀態(tài)不同霸旗,所在的位置不同。我們事先計(jì)算出每個(gè)狀態(tài)處機(jī)器人位置的中心坐標(biāo)戚揭,并存儲到兩個(gè)向量中诱告,并在類初始化中給出:
self.x=[140,220,300,380,460,140,300,460]
self.y=[250,250,250,250,250,150,150,150]
# 根據(jù)這兩個(gè)向量和機(jī)器人當(dāng)前的狀態(tài),我們就可以設(shè)置機(jī)器人當(dāng)前的圓心坐標(biāo)了即:
if self.state is None: return None
self.robotrans.set_translation(self.x[self.state-1], self.y[self.state- 1])
# 最后還需要一個(gè)返回語句:
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
以上便完成了render()函數(shù)的建立
reset()函數(shù)的建立:
reset()函數(shù)常常用隨機(jī)的方法初始化機(jī)器人的狀態(tài)毫目,即:
def _reset(self):
self.state = self.states[int(random.random() * len(self.states))]
return self.state
環(huán)境的注冊
全部的代碼請去github上下載學(xué)習(xí)蔬啡。下面重點(diǎn)講一講如何將建好的環(huán)境進(jìn)行注冊诲侮,以便通過gym的標(biāo)準(zhǔn)形式進(jìn)行調(diào)用。其實(shí)環(huán)境的注冊很簡單箱蟆,只需要3步:
第一步:將我們自己的環(huán)境文件(我創(chuàng)建的文件名為grid_mdp.py)拷貝到你的gym安裝目錄/gym/gym/envs/classic_control文件夾中沟绪。(拷貝在這個(gè)文件夾中因?yàn)橐褂胷endering模塊。當(dāng)然空猜,也有其他辦法绽慈。該方法不唯一)
第二步:打開該文件夾(第一步中的文件夾)下的init.py文件,在文件末尾加入語句:from gym.envs.classic_control.grid_mdp import GridEnv
第三步:進(jìn)入文件夾你的gym安裝目錄/gym/gym/envs辈毯,打開該文件夾下的init.py文件坝疼,添加代碼:
register(
# gym.make(‘id’)時(shí)的id
id='GridWorld-v0',
# 函數(shù)路口
entry_point='gym.envs.classic_control:GridEnv',
max_episode_steps=200,
reward_threshold=100.0,
)
第一個(gè)參數(shù)id就是你調(diào)用gym.make(‘id’)時(shí)的id, 這個(gè)id你可以隨便選取,我取的谆沃,名字是GridWorld-v0
第二個(gè)參數(shù)就是函數(shù)路口了钝凶。
后面的參數(shù)原則上來說可以不必要寫。
經(jīng)過以上三步唁影,就完成了注冊耕陷。
下面,我們給個(gè)簡單的demo來測試下我們的環(huán)境的效果吧:
我們依然寫個(gè)終端程序:
import gym
env = gym.make('GridWorld-v0')
env.reset()
env.render()