如何在服务器上运行OpenAI Gym的.render()函数

98
我可以为您翻译,以下是翻译好的内容:

我正在一个 p2.xlarge AWS 服务器上通过 Jupyter (Ubuntu 14.04) 运行一个 Python 2.7 脚本。我希望能够呈现我的模拟结果。

最简示例

import gym
env = gym.make('CartPole-v0')
env.reset()
env.render()

env.render() 会产生以下错误(除其他外):

...
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>'")
...
NoSuchDisplayException: Cannot connect to "None"

我希望能够以某种方式看到模拟结果,最好是内联显示,但任何显示方法都可以。

编辑:这只是在某些环境下出现的问题,比如经典控制。


更新 I

this的启发,我尝试了以下操作,而不是使用xvfb-run -s \"-screen 0 1400x900x24\" python <your_script.py>(我无法让它工作)。

xvfb-run -a jupyter notebook

运行原始脚本,现在我得到的是:
GLXInfoException: pyglet requires an X server with GLX

更新 II

问题#154似乎是相关的。我尝试禁用弹出窗口,并直接创建RGB颜色。

import gym
env = gym.make('CartPole-v0')
env.reset()

img = env.render(mode='rgb_array', close=True)  
print(type(img)) # <--- <type 'NoneType'>

img = env.render(mode='rgb_array', close=False) # <--- ERROR
print(type(img)) 

我获得了ImportError:无法导入名称gl_info

更新 III

在@Torxed的启发下,我尝试创建一个视频文件,然后进行渲染(这是一个完全令人满意的解决方案)。

使用'记录和上传结果'中的代码。

import gym

env = gym.make('CartPole-v0')
env.monitor.start('/tmp/cartpole-experiment-1', force=True)
observation = env.reset()
for t in range(100):
#    env.render()
    print(observation)
    action = env.action_space.sample()
    observation, reward, done, info = env.step(action)
    if done:
        print("Episode finished after {} timesteps".format(t+1))
        break

env.monitor.close()

我尝试按照你的建议操作,但在运行env.monitor.start(...时出现了ImportError: cannot import name gl_info错误。

据我所知,问题在于OpenAI使用了pyglet,而pyglet“需要”屏幕才能计算要呈现的图像的RGB颜色。因此,有必要欺骗Python认为连接了显示器。


更新 IV

需要注意的是,有一些使用 bumblebee 的在线解决方案似乎可以奏效。如果您对服务器有控制权,则应该可以使用此方法,但由于 AWS 运行在虚拟机中,我认为您不能使用此方法。


更新 V

如果你遇到了这个问题,而且不知道该怎么办(就像我一样),大多数环境的状态都很简单,你可以创建自己的渲染机制。虽然不是很令人满意,但你知道。


你试过像非常有帮助的错误消息建议的那样制作虚拟屏幕缓冲区吗? - Selali Adobor
1
那就是问题所在,我不知道如何做到这一点。在我查看的指南中,我不明白如何使其在服务器上运行。 - Toke Faurby
2
我已经更新了帖子并尝试了一下。 - Toke Faurby
1
我也卡在这里了,任何帮助都将不胜感激 :) - vgoklani
提醒一下,@TokeFaurby,你的解决方案已于2016年12月23日被弃用。你需要使用稍微不同的API和方法来使其工作。我认为你的方法可能不再适用了,因为VideoRecorder类作为env.wrapper.Monitor的一部分现在直接调用env.render - mpacer
1
我已经使用这个答案解决了这个问题:stackoverflow NameError: name 'base' is not defined, while running open AI gym in GOOGLE COLAB - Hermes Morales
14个回答

45

我已经想出了一个简单的解决方案:

CartPole

$ xvfb-run -s "-screen 0 1400x900x24" jupyter notebook

import matplotlib.pyplot as plt
%matplotlib inline
from IPython import display

def show_state(env, step=0, info=""):
    plt.figure(3)
    plt.clf()
    plt.imshow(env.render(mode='rgb_array'))
    plt.title("%s | Step: %d %s" % (env._spec.id,step, info))
    plt.axis('off')

    display.clear_output(wait=True)
    display.display(plt.gcf())

注意:如果你的环境不是unwrapped,请将env.env传递给show_state函数。

2
运行良好,谢谢,但是该环境仍会在外部窗口中打开(我是在本地而非远程服务器上运行)。有什么方法可以防止这种情况发生吗? - Lucas
我遇到了“/bin/xvfb-run: line 181: 0: command not found”和“/bin/xvfb-run: line 186: kill: (31449) - No such process”错误。有什么想法吗? - Afshin Oroojlooy
1400x900x24 中的 "24" 是什么意思?我猜 1400 是显示器宽度,900 是显示器高度。 - Nathan majicvr.com
24指定了虚拟监视器的位深度 - 请参见此处 - BrandonHoughton
我的问题发生在渲染阶段:env = gym.make('CartPole-v0');env.render(mode='rgb_array');导致我出现了ValueError: 数组长度必须>= 0,而不是-48424951659315200。 - John Jiang

31

这个 GitHub 问题给出了一个对我非常有效的答案。它很好,因为不需要任何额外的依赖项(我假设你已经有matplotlib)或服务器的配置。

只需运行,例如:

import gym
import matplotlib.pyplot as plt
%matplotlib inline

env = gym.make('Breakout-v0') # insert your favorite environment
render = lambda : plt.imshow(env.render(mode='rgb_array'))
env.reset()
render()

使用mode='rgb_array'会返回一个numpy.ndarray,其中包含每个位置的RGB值,matplotlibimshow(或其他方法)可以很好地显示这些值。

请注意,如果您在同一单元格中进行了多次渲染,则此解决方案将每次绘制单独的图像。这可能不是您想要的结果。如果我找到一个好的解决方法,我会尝试更新它。

更新以在同一单元格中多次渲染

根据这个StackOverflow答案,这里有一个可行的代码片段(请注意,在交互式图中,可能有更有效的方法来完成此操作;在我的机器上,这种方式似乎有点滞后):

import gym
from IPython import display
import matplotlib.pyplot as plt
%matplotlib inline

env = gym.make('Breakout-v0')
env.reset()
for _ in range(100):
    plt.imshow(env.render(mode='rgb_array'))
    display.display(plt.gcf())
    display.clear_output(wait=True)
    action = env.action_space.sample()
    env.step(action)

更新以提高效率

在我的电脑上,这样做快了大约3倍。不同之处在于,我们不是每次呈现图像时都调用imshow,而是只需更改原始图形的 RGB 数据。

import gym
from IPython import display
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

env = gym.make('Breakout-v0')
env.reset()
img = plt.imshow(env.render(mode='rgb_array')) # only call this once
for _ in range(100):
    img.set_data(env.render(mode='rgb_array')) # just update the data
    display.display(plt.gcf())
    display.clear_output(wait=True)
    action = env.action_space.sample()
    env.step(action)

你是否真的尝试在服务器上运行过这个程序?还是只在本地机器上测试过?你所建议的与 Update II 中的本质相同。问题在于,即使窗口没有打开,env.render() 仍会在后台导入 gl_infomode=rgb_array 并不能改变这一点。 - Toke Faurby
啊,我没有在 AWS 服务器上尝试过;我使用的那个可能有一些不同的配置,导致我没有遇到相同的错误。很抱歉它没有帮助到你。 - Nathan
8
@Nathan,你的解决方案存在一个额外的问题。一些环境似乎有绕过窗口创建的方式,而另一些则没有。例如,如果您想尝试这个解决方案,请在示例中将 env = gym.make('Breakout-v0') 替换为 env = gym.make('CartPole-v0'),您就会明白我的意思了。'Breakout' 是一个使用 atari_py.ALEInterface() 作为其渲染器并返回图像的 Atari 游戏。请参见 https://github.com/openai/gym/blob/master/gym/envs/atari/atari_env.py#L113 - mpacer

16

我认为我们应该使用OpenAI Gym wrappers.Monitor将渲染内容录制成视频,然后在Notebook中显示。

示例:

依赖项

!apt install python-opengl
!apt install ffmpeg
!apt install xvfb
!pip3 install pyvirtualdisplay

# Virtual display
from pyvirtualdisplay import Display

virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()

视频捕获

import gym
from gym import wrappers

env = gym.make("SpaceInvaders-v0")
env = wrappers.Monitor(env, "/tmp/SpaceInvaders-v0")

for episode in range(2):
    observation = env.reset()
    step = 0
    total_reward = 0

    while True:
        step += 1
        env.render()
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        total_reward += reward
        if done:
            print("Episode: {0},\tSteps: {1},\tscore: {2}"
                  .format(episode, step, total_reward)
            )
            break
env.close()

在笔记本中显示

import os
import io
import base64
from IPython.display import display, HTML

def ipython_show_video(path):
    """Show a video at `path` within IPython Notebook
    """
    if not os.path.isfile(path):
        raise NameError("Cannot access: {}".format(path))

    video = io.open(path, 'r+b').read()
    encoded = base64.b64encode(video)

    display(HTML(
        data="""
        <video alt="test" controls>
        <source src="data:video/mp4;base64,{0}" type="video/mp4" />
        </video>
        """.format(encoded.decode('ascii'))
    ))

ipython_show_video("/tmp/SpaceInvaders-v0/openaigym.video.4.10822.video000000.mp4")

希望这有所帮助。 ;)


在运行完所有内容后,我得到了“NameError:无法访问:/tmp/SpaceInvaders-v0/openaigym.video.4.10822.video000000.mp4”的错误。有什么想法吗? - user2997154
检查/tmp/SpaceInvaders-v0文件夹下的文件名。视频名称可能不同。或者,为env = wrappers.Monitor(env, "/tmp/SpaceInvaders-v0")提供已知的文件夹。 - Senthilkumar Gopal
这也不适用于经典控制,如“CartPole-v0”:ValueError:数组长度必须>= 0,而不是-48424951659315200。 - John Jiang
我确认它可以使用 retro import 和 SpaceInvaders-Atari2600 进行工作:/nimport retro/n 从 gym 中导入 wrappers/n env = retro.make(game="SpaceInvaders-Atari2600")/n env = wrappers.Monitor(env, "SpaceInvaders-Atari2600")/n ......../n ipython_show_video("./SpaceInvaders-Atari2600/openaigym.video.0.2383.video000000.mp4")/n - Gediz GÜRSU

14

我成功地在一个无头服务器上远程运行和渲染了openai/gym(包括mujoco)。

# Install and configure X window with virtual screen
sudo apt-get install xserver-xorg libglu1-mesa-dev freeglut3-dev mesa-common-dev libxmu-dev libxi-dev
# Configure the nvidia-x
sudo nvidia-xconfig -a --use-display-device=None --virtual=1280x1024
# Run the virtual screen in the background (:0)
sudo /usr/bin/X :0 &
# We only need to setup the virtual screen once

# Run the program with vitural screen
DISPLAY=:0 <program>

# If you dont want to type `DISPLAY=:0` everytime
export DISPLAY=:0

使用方法:

DISPLAY=:0 ipython2

例子:

import gym
env = gym.make('Ant-v1')
arr = env.render(mode='rgb_array')
print(arr.shape)
# plot or save wherever you want
# plt.imshow(arr) or scipy.misc.imsave('sample.png', arr)

所以只是为了确认,这个也适用于经典控件吗? - Toke Faurby
使用像CartPole这样的传统控制方式,您可以尝试“ssh -X username@hostname”并直接呈现。 - Van
1
我一直遇到错误 ContextException: Could not create GL context remote,而其他解决方案都不起作用,但这个方法完美解决了我的问题。 - Omegastick

12

还有这种解决方案,使用pyvirtualdisplay(一个Xvfb包装器)。我喜欢这个解决方案的一件事是你可以从脚本内部启动它,而不是必须在启动时进行包装:

from pyvirtualdisplay import Display
display = Display(visible=0, size=(1400, 900))
display.start()

1
我得到了这个错误:ValueError: 无效的字面值,无法转换为十进制整数:'' - Crispy13

11

我也遇到了这个问题。使用Xvfb作为X服务器会与Nvidia驱动程序产生冲突。但最终这篇文章指出了正确的方向。

如果您使用-no-opengl-files选项安装Nvidia驱动程序和--no-opengl-libs选项安装CUDA,那么Xvfb就可以正常工作了。

如果您知道这一点,它应该可以工作了。但是,由于我花费了相当长的时间才找到解决方法,而且似乎我不是唯一遇到Xvfb和Nvidia驱动程序问题的人。

我在一个带有Ubuntu 16.04 LTS的AWS EC2实例上写下了所有必要的设置步骤,详情请看这里


我尽力按照指南操作,但无法使其正常工作。唯一的区别是我没有从源代码构建TF,但由于这个问题与此无关,我认为这并不重要。 - Toke Faurby
很抱歉听到这个消息。由于Amazon深度学习AMI现在包含支持GPU的TensorFlow版本,我建议从那里开始: https://aws.amazon.com/marketplace/pp/B01M0AXXQB但是,如果您仍然想从头开始设置,请提供一些更多关于您遇到的问题的信息,以便我可以帮助您。 - I_like_foxes

3
参考我的另一个答案:仅在Jupyter笔记本中显示OpenAI gym 我在这里制作了一个快速的实例,您可以复制:https://kyso.io/eoin/openai-gym-jupyter,其中包含两个在Jupyter中呈现示例的示例-一个是mp4格式,另一个是实时gif。
.mp4示例非常简单。
import gym
from gym import wrappers

env = gym.make('SpaceInvaders-v0')
env = wrappers.Monitor(env, "./gym-results", force=True)
env.reset()
for _ in range(1000):
    action = env.action_space.sample()
    observation, reward, done, info = env.step(action)
    if done: break
env.close()

然后在一个新的Jupyter单元格中,或者将其从服务器下载到某个可以查看视频的地方。

import io
import base64
from IPython.display import HTML

video = io.open('./gym-results/openaigym.video.%s.video000000.mp4' % env.file_infix, 'r+b').read()
encoded = base64.b64encode(video)
HTML(data='''
    <video width="360" height="auto" alt="test" controls><source src="data:video/mp4;base64,{0}" type="video/mp4" /></video>'''
.format(encoded.decode('ascii')))

如果你在一个公共访问的服务器上,你可以在 gym-results 文件夹中运行 python -m http.server 命令,然后只需在那里观看视频即可。


3

我也遇到了同样的问题,后来在这里找到了答案。将它们混合使用帮助我解决了这个问题。

以下是逐步解决方案:

安装以下内容:

apt-get install -y python-opengl xvfb

通过以下命令启动您的Jupyter笔记本:

xvfb-run -s "-screen 0 1400x900x24" jupyter notebook

笔记本内部:

import gym
import matplotlib.pyplot as plt
%matplotlib inline

env = gym.make('MountainCar-v0') # insert your favorite environment
env.reset()
plt.imshow(env.render(mode='rgb_array')

现在,您可以将同样的内容放入循环中以多次呈现。
from IPython import display

for _ in range(100):
    plt.imshow(env.render(mode='rgb_array'))
    display.display(plt.gcf())
    display.clear_output(wait=True)
    action = env.action_space.sample()
    env.step(action)

希望这对仍然遇到问题的任何人都有用。感谢AndrewsNathan提供的答案。


这个解决方案最终对我有用,但我遇到了一个错误 AttributeError: 'ImageData' object has no attribute 'data',安装 pyglet-v1.3.2 并从这个 链接 的解决方案中修复了它。 - David

2
我正在寻找在Colaboratory中可用的解决方案,最终得到了这个。
from IPython import display
import numpy as np
import time

import gym
env = gym.make('SpaceInvaders-v0')
env.reset()

import PIL.Image
import io


def showarray(a, fmt='png'):
    a = np.uint8(a)
    f = io.BytesIO()
    ima = PIL.Image.fromarray(a).save(f, fmt)
    return f.getvalue()

imagehandle = display.display(display.Image(data=showarray(env.render(mode='rgb_array')), width=450), display_id='gymscr')

while True:
    time.sleep(0.01)
    env.step(env.action_space.sample()) # take a random action
    display.update_display(display.Image(data=showarray(env.render(mode='rgb_array')), width=450), display_id='gymscr')

编辑1:
您可以使用xvfbwrapper来运行Cartpole环境。
from IPython import display
from xvfbwrapper import Xvfb
import numpy as np
import time
import pyglet
import gym
import PIL.Image
import io    

vdisplay = Xvfb(width=1280, height=740)
vdisplay.start()

env = gym.make('CartPole-v0')
env.reset()

def showarray(a, fmt='png'):
    a = np.uint8(a)
    f = io.BytesIO()
    ima = PIL.Image.fromarray(a).save(f, fmt)
    return f.getvalue()

imagehandle = display.display(display.Image(data=showarray(env.render(mode='rgb_array')), width=450), display_id='gymscr')


for _ in range(1000):
  time.sleep(0.01)
  observation, reward, done, info = env.step(env.action_space.sample()) # take a random action
  display.update_display(display.Image(data=showarray(env.render(mode='rgb_array')), width=450), display_id='gymscr')


vdisplay.stop()

如果你正在使用标准Jupyter,有一个更好的解决方案。你可以使用CommManager将带有更新数据URL的消息发送到你的HTML输出。

IPython Inline Screen Example

在Colab中,CommManager不可用。更严格的输出模块有一个叫做eval_js()的方法,似乎有点慢。


2
它可以与SpaceInvaders-v0一起使用,但是当我调用CartPole-v0Pendulum-v0时,我会得到NameError:name 'base' is not defined。有什么想法吗? - Afshin Oroojlooy
@AfshinOroojlooy 请尝试执行 !apt-get install python-pyglet && pip install pyglet 命令,然后重新启动运行时。 - Shadi
@shadi,感谢您的回复。我一段时间前已经解决了这个问题。 - Afshin Oroojlooy

2

我避免使用matplotlib的问题,只需简单地使用PIL,即Python图像库:

import gym, PIL
env = gym.make('SpaceInvaders-v0')
array = env.reset()
PIL.Image.fromarray(env.render(mode='rgb_array'))

我发现我不需要设置XV帧缓冲区。

我非常怀疑这个在无头服务器上能否正常工作。问题出在env.render函数上,PIL与此问题无关。 - Toke Faurby
抱歉,我没有注意到您使用了“SpaceInvaders-v0”。这个问题只会在某些环境下出现,比如经典控制。我已经适当地编辑了原始帖子。 - Toke Faurby
是的,我现在明白我的答案只适用于一些模拟。这很遗憾,也是 gym 的限制。 - Doug Blank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接