? 本文主要环境版本:gym: 0.23.0, ale-py: 0.7.4, atari-py: 1.2.2,python:3.7
? 在安装了Gym环境后,成功运行了基础Demo(CartPole-v0)后,发现Atari,有更好玩的经典游戏,比如打砖块、乒乓球等。
? 在经历了N多苦难后,终于安装好了Atari, 导入了roms后,并且环境(env)已经加载成功了,主要代码如下:
import time
import gym
import matplotlib.pyplot as pltenv = gym.make('BreakoutNoFrameskip-v4')
# env = gym.make('ALE/Breakout-v4')
print("Observation Space: ", env.observation_space)
print("Action Space ", env.action_space)obs = env.reset()for i in range(1000):env.render()action = env.action_space.sample()obs, reward, done, info = env.step(action)time.sleep(0.01)
env.close()
运行后,Output如下:
Observation Space: Box(0, 255, (210, 160, 3), uint8)
Action Space Discrete(4)
Traceback (most recent call last):File "C:/Users/31661/PycharmProjects/AILearning/gym/BreakOut.py", line 15, in <module>env.render()File "F:\miniconda3\envs\Gym\lib\site-packages\gym\core.py", line 286, in renderreturn self.env.render(mode, **kwargs)File "F:\miniconda3\envs\Gym\lib\site-packages\gym\core.py", line 286, in renderreturn self.env.render(mode, **kwargs)File "F:\miniconda3\envs\Gym\lib\site-packages\gym\envs\atari\environment.py", line 264, in renderfrom gym.envs.classic_control import rendering
ImportError: cannot import name 'rendering' from 'gym.envs.classic_control' (F:\miniconda3\envs\Gym\lib\site-packages\gym\envs\classic_control\__init__.py)
? 环境都已加载好,并且输出了观察空间和动作空间,怎么渲染也页面(render)的时候会报错。
? 在经历过重装Atari、更改为Linux上安装等一些列的尝试,并且失败后,简直要崩溃。最后的所有尝试,都归于一个错误:
? ImportError: cannot import name ‘rendering’ from ‘gym.envs.classic_control’
? 山重水复疑无路,柳暗花明又一村。在看GitHub上非maser分支的源码时,发现了一处不对的地方,主要区别如下图:
? 上面两张图是Gym不同分支下的classic_control文件夹,rendering.py文件应该是Gym在某个版本,更新是被去除了。但是不知道为啥,Atari的游戏环境加载却无法渲染页面。。
? 找到了问题,那就好办了,只要吧rendering.py复制到你电脑中gym->envs->classic_control中,新建rendering.py文件,然后将内容复制进去即可。Github链接.
""" 2D rendering framework """
from __future__ import division
import os
import six
import sysif "Apple" in sys.version:if 'DYLD_FALLBACK_LIBRARY_PATH' in os.environ:os.environ['DYLD_FALLBACK_LIBRARY_PATH'] += ':/usr/lib'# (JDS 2016/04/15): avoid bug on Anaconda 2.3.0 / Yosemitefrom gym import errortry:import pyglet
except ImportError as e:raise ImportError('''Cannot import pyglet.HINT: you can install pyglet directly via 'pip install pyglet'.But if you really just want to install all Gym dependencies and not have to think about it,'pip install -e .[all]' or 'pip install gym[all]' will do it.''')try:from pyglet.gl import *
except ImportError as e:raise ImportError('''Error occurred while running `from pyglet.gl import *`HINT: make sure you have OpenGL install. On Ubuntu, you can run 'apt-get install python-opengl'.If you're running on a server, you may need a virtual frame buffer; something like this should work:'xvfb-run -s \"-screen 0 1400x900x24\" python <your_script.py>'''')import math
import numpy as npRAD2DEG = 57.29577951308232def get_display(spec):"""Convert a display specification (such as :0) into an actual Displayobject.Pyglet only supports multiple Displays on Linux."""if spec is None:return Noneelif isinstance(spec, six.string_types):return pyglet.canvas.Display(spec)else:raise error.Error('Invalid display specification: {}. (Must be a string like :0 or None.)'.format(spec))class Viewer(object):def __init__(self, width, height, display=None):display = get_display(display)self.width = widthself.height = heightself.window = pyglet.window.Window(width=width, height=height, display=display)self.window.on_close = self.window_closed_by_userself.isopen = Trueself.geoms = []self.onetime_geoms = []self.transform = Transform()glEnable(GL_BLEND)glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)def close(self):self.window.close()def window_closed_by_user(self):self.isopen = Falsedef set_bounds(self, left, right, bottom, top):assert right > left and top > bottomscalex = self.width/(right-left)scaley = self.height/(top-bottom)self.transform = Transform(translation=(-left*scalex, -bottom*scaley),scale=(scalex, scaley))def add_geom(self, geom):self.geoms.append(geom)def add_onetime(self, geom):self.onetime_geoms.append(geom)def render(self, return_rgb_array=False):glClearColor(1,1,1,1)self.window.clear()self.window.switch_to()self.window.dispatch_events()self.transform.enable()for geom in self.geoms:geom.render()for geom in self.onetime_geoms:geom.render()self.transform.disable()arr = Noneif return_rgb_array:buffer = pyglet.image.get_buffer_manager().get_color_buffer()image_data = buffer.get_image_data()arr = np.frombuffer(image_data.data, dtype=np.uint8)# In https://github.com/openai/gym-http-api/issues/2, we# discovered that someone using Xmonad on Arch was having# a window of size 598 x 398, though a 600 x 400 window# was requested. (Guess Xmonad was preserving a pixel for# the boundary.) So we use the buffer height/width rather# than the requested one.arr = arr.reshape(buffer.height, buffer.width, 4)arr = arr[::-1,:,0:3]self.window.flip()self.onetime_geoms = []return arr if return_rgb_array else self.isopen# Conveniencedef draw_circle(self, radius=10, res=30, filled=True, **attrs):geom = make_circle(radius=radius, res=res, filled=filled)_add_attrs(geom, attrs)self.add_onetime(geom)return geomdef draw_polygon(self, v, filled=True, **attrs):geom = make_polygon(v=v, filled=filled)_add_attrs(geom, attrs)self.add_onetime(geom)return geomdef draw_polyline(self, v, **attrs):geom = make_polyline(v=v)_add_attrs(geom, attrs)self.add_onetime(geom)return geomdef draw_line(self, start, end, **attrs):geom = Line(start, end)_add_attrs(geom, attrs)self.add_onetime(geom)return geomdef get_array(self):self.window.flip()image_data = pyglet.image.get_buffer_manager().get_color_buffer().get_image_data()self.window.flip()arr = np.fromstring(image_data.data, dtype=np.uint8, sep='')arr = arr.reshape(self.height, self.width, 4)return arr[::-1,:,0:3]def __del__(self):self.close()def _add_attrs(geom, attrs):if "color" in attrs:geom.set_color(*attrs["color"])if "linewidth" in attrs:geom.set_linewidth(attrs["linewidth"])class Geom(object):def __init__(self):self._color=Color((0, 0, 0, 1.0))self.attrs = [self._color]def render(self):for attr in reversed(self.attrs):attr.enable()self.render1()for attr in self.attrs:attr.disable()def render1(self):raise NotImplementedErrordef add_attr(self, attr):self.attrs.append(attr)def set_color(self, r, g, b):self._color.vec4 = (r, g, b, 1)class Attr(object):def enable(self):raise NotImplementedErrordef disable(self):passclass Transform(Attr):def __init__(self, translation=(0.0, 0.0), rotation=0.0, scale=(1,1)):self.set_translation(*translation)self.set_rotation(rotation)self.set_scale(*scale)def enable(self):glPushMatrix()glTranslatef(self.translation[0], self.translation[1], 0) # translate to GL loc ppintglRotatef(RAD2DEG * self.rotation, 0, 0, 1.0)glScalef(self.scale[0], self.scale[1], 1)def disable(self):glPopMatrix()def set_translation(self, newx, newy):self.translation = (float(newx), float(newy))def set_rotation(self, new):self.rotation = float(new)def set_scale(self, newx, newy):self.scale = (float(newx), float(newy))class Color(Attr):def __init__(self, vec4):self.vec4 = vec4def enable(self):glColor4f(*self.vec4)class LineStyle(Attr):def __init__(self, style):self.style = styledef enable(self):glEnable(GL_LINE_STIPPLE)glLineStipple(1, self.style)def disable(self):glDisable(GL_LINE_STIPPLE)class LineWidth(Attr):def __init__(self, stroke):self.stroke = strokedef enable(self):glLineWidth(self.stroke)class Point(Geom):def __init__(self):Geom.__init__(self)def render1(self):glBegin(GL_POINTS) # draw pointglVertex3f(0.0, 0.0, 0.0)glEnd()class FilledPolygon(Geom):def __init__(self, v):Geom.__init__(self)self.v = vdef render1(self):if len(self.v) == 4 : glBegin(GL_QUADS)elif len(self.v) > 4 : glBegin(GL_POLYGON)else: glBegin(GL_TRIANGLES)for p in self.v:glVertex3f(p[0], p[1],0) # draw each vertexglEnd()def make_circle(radius=10, res=30, filled=True):points = []for i in range(res):ang = 2*math.pi*i / respoints.append((math.cos(ang)*radius, math.sin(ang)*radius))if filled:return FilledPolygon(points)else:return PolyLine(points, True)def make_polygon(v, filled=True):if filled: return FilledPolygon(v)else: return PolyLine(v, True)def make_polyline(v):return PolyLine(v, False)def make_capsule(length, width):l, r, t, b = 0, length, width/2, -width/2box = make_polygon([(l,b), (l,t), (r,t), (r,b)])circ0 = make_circle(width/2)circ1 = make_circle(width/2)circ1.add_attr(Transform(translation=(length, 0)))geom = Compound([box, circ0, circ1])return geomclass Compound(Geom):def __init__(self, gs):Geom.__init__(self)self.gs = gsfor g in self.gs:g.attrs = [a for a in g.attrs if not isinstance(a, Color)]def render1(self):for g in self.gs:g.render()class PolyLine(Geom):def __init__(self, v, close):Geom.__init__(self)self.v = vself.close = closeself.linewidth = LineWidth(1)self.add_attr(self.linewidth)def render1(self):glBegin(GL_LINE_LOOP if self.close else GL_LINE_STRIP)for p in self.v:glVertex3f(p[0], p[1],0) # draw each vertexglEnd()def set_linewidth(self, x):self.linewidth.stroke = xclass Line(Geom):def __init__(self, start=(0.0, 0.0), end=(0.0, 0.0)):Geom.__init__(self)self.start = startself.end = endself.linewidth = LineWidth(1)self.add_attr(self.linewidth)def render1(self):glBegin(GL_LINES)glVertex2f(*self.start)glVertex2f(*self.end)glEnd()class Image(Geom):def __init__(self, fname, width, height):Geom.__init__(self)self.width = widthself.height = heightimg = pyglet.image.load(fname)self.img = imgself.flip = Falsedef render1(self):self.img.blit(-self.width/2, -self.height/2, width=self.width, height=self.height)# ================================================================class SimpleImageViewer(object):def __init__(self, display=None, maxwidth=500):self.window = Noneself.isopen = Falseself.display = displayself.maxwidth = maxwidthdef imshow(self, arr):if self.window is None:height, width, _channels = arr.shapeif width > self.maxwidth:scale = self.maxwidth / widthwidth = int(scale * width)height = int(scale * height)self.window = pyglet.window.Window(width=width, height=height,display=self.display, vsync=False, resizable=True)self.width = widthself.height = heightself.isopen = True@self.window.eventdef on_resize(width, height):self.width = widthself.height = height@self.window.eventdef on_close():self.isopen = Falseassert len(arr.shape) == 3, "You passed in an image with the wrong number shape"image = pyglet.image.ImageData(arr.shape[1], arr.shape[0],'RGB', arr.tobytes(), pitch=arr.shape[1]*-3)gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST)texture = image.get_texture()texture.width = self.widthtexture.height = self.heightself.window.clear()self.window.switch_to()self.window.dispatch_events()texture.blit(0, 0) # drawself.window.flip()def close(self):if self.isopen and sys.meta_path:# ^^^ check sys.meta_path to avoid 'ImportError: sys.meta_path is None, Python is likely shutting down'self.window.close()self.isopen = Falsedef __del__(self):self.close()
? 然后在classic_control中的init.py文件中,增加如下一行代码
from gym.envs.classic_control import rendering
? 想运行Atari游戏的话,可能你还缺少pyglet包,也简单,pip install pyglet
? 下面执行测试代码:
import time
import gymenv = gym.make('BreakoutNoFrameskip-v4')
# env = gym.make('ALE/Breakout-v4')
print("Observation Space: ", env.observation_space)
print("Action Space ", env.action_space)obs = env.reset()for i in range(1000):env.render()action = env.action_space.sample()obs, reward, done, info = env.step(action)time.sleep(0.01)
env.close()
? 运行后,终于出现了梦中的场景,完美!