2019-06-07 14:43:05 -07:00
from collections import OrderedDict
2016-04-30 22:47:51 -07:00
import os
2016-04-27 08:00:58 -07:00
2019-06-07 14:43:05 -07:00
2016-04-30 22:47:51 -07:00
from gym import error , spaces
2016-05-29 09:07:09 -07:00
from gym . utils import seeding
2016-04-27 08:00:58 -07:00
import numpy as np
2016-04-30 22:47:51 -07:00
from os import path
2016-04-27 08:00:58 -07:00
import gym
try :
import mujoco_py
except ImportError as e :
2016-04-28 14:01:24 +02:00
raise error . DependencyNotInstalled ( " {} . (HINT: you need to install mujoco_py, and also perform the setup instructions here: https://github.com/openai/mujoco-py/.) " . format ( e ) )
2016-04-27 08:00:58 -07:00
2018-05-30 01:39:49 -07:00
DEFAULT_SIZE = 500
2019-06-07 14:43:05 -07:00
def convert_observation_to_space ( observation ) :
if isinstance ( observation , dict ) :
space = spaces . Dict ( OrderedDict ( [
( key , convert_observation_to_space ( value ) )
for key , value in observation . items ( )
] ) )
elif isinstance ( observation , np . ndarray ) :
2020-02-29 01:11:29 +01:00
low = np . full ( observation . shape , - float ( ' inf ' ) , dtype = np . float32 )
high = np . full ( observation . shape , float ( ' inf ' ) , dtype = np . float32 )
2019-06-07 14:43:05 -07:00
space = spaces . Box ( low , high , dtype = observation . dtype )
else :
raise NotImplementedError ( type ( observation ) , observation )
return space
2016-04-27 08:00:58 -07:00
class MujocoEnv ( gym . Env ) :
2016-05-29 09:07:09 -07:00
""" Superclass for all MuJoCo environments.
2016-04-30 22:47:51 -07:00
"""
2019-08-23 15:02:33 -07:00
def __init__ ( self , model_path , frame_skip ) :
2016-04-27 08:00:58 -07:00
if model_path . startswith ( " / " ) :
fullpath = model_path
else :
fullpath = os . path . join ( os . path . dirname ( __file__ ) , " assets " , model_path )
2016-04-30 22:47:51 -07:00
if not path . exists ( fullpath ) :
2017-02-22 17:24:27 -08:00
raise IOError ( " File %s does not exist " % fullpath )
self . frame_skip = frame_skip
2018-01-24 15:42:29 -08:00
self . model = mujoco_py . load_model_from_path ( fullpath )
self . sim = mujoco_py . MjSim ( self . model )
self . data = self . sim . data
2016-04-27 08:00:58 -07:00
self . viewer = None
2018-05-30 01:39:49 -07:00
self . _viewers = { }
2016-04-27 08:00:58 -07:00
self . metadata = {
2018-09-21 01:51:58 +01:00
' render.modes ' : [ ' human ' , ' rgb_array ' , ' depth_array ' ] ,
2017-02-22 17:24:27 -08:00
' video.frames_per_second ' : int ( np . round ( 1.0 / self . dt ) )
2016-04-27 08:00:58 -07:00
}
2018-01-24 15:42:29 -08:00
self . init_qpos = self . sim . data . qpos . ravel ( ) . copy ( )
self . init_qvel = self . sim . data . qvel . ravel ( ) . copy ( )
2019-06-07 14:43:05 -07:00
self . _set_action_space ( )
action = self . action_space . sample ( )
observation , _reward , done , _info = self . step ( action )
2016-04-30 22:47:51 -07:00
assert not done
2019-06-07 14:43:05 -07:00
self . _set_observation_space ( observation )
self . seed ( )
def _set_action_space ( self ) :
2020-02-29 01:11:29 +01:00
bounds = self . model . actuator_ctrlrange . copy ( ) . astype ( np . float32 )
2019-06-07 14:43:05 -07:00
low , high = bounds . T
2018-09-17 10:27:31 -07:00
self . action_space = spaces . Box ( low = low , high = high , dtype = np . float32 )
2019-06-07 14:43:05 -07:00
return self . action_space
2016-04-30 22:47:51 -07:00
2019-06-07 14:43:05 -07:00
def _set_observation_space ( self , observation ) :
self . observation_space = convert_observation_to_space ( observation )
return self . observation_space
2016-05-30 18:07:59 -07:00
Cleanup, removal of unmaintained code (#836)
* add dtype to Box
* remove board_game, debugging, safety, parameter_tuning environments
* massive set of breaking changes
- remove python logging module
- _step, _reset, _seed, _close => non underscored method
- remove benchmark and scoring folder
* Improve render("human"), now resizable, closable window.
* get rid of default step and reset in wrappers, so it doesn’t silently fail for people with underscore methods
* CubeCrash unit test environment
* followup fixes
* MemorizeDigits unit test envrionment
* refactored spaces a bit
fixed indentation
disabled test_env_semantics
* fix unit tests
* fixes
* CubeCrash, MemorizeDigits tested
* gym backwards compatibility patch
* gym backwards compatibility, followup fixes
* changelist, add spaces to main namespaces
* undo_logger_setup for backwards compat
* remove configuration.py
2018-01-25 18:20:14 -08:00
def seed ( self , seed = None ) :
2016-05-30 18:07:59 -07:00
self . np_random , seed = seeding . np_random ( seed )
2016-05-29 09:07:09 -07:00
return [ seed ]
2016-04-30 22:47:51 -07:00
# methods to override:
# ----------------------------
def reset_model ( self ) :
"""
Reset the robot degrees of freedom ( qpos and qvel ) .
Implement this in each subclass .
"""
raise NotImplementedError
def viewer_setup ( self ) :
"""
2018-09-24 14:53:23 -07:00
This method is called when the viewer is initialized .
2016-04-30 22:47:51 -07:00
Optionally implement this method , if you need to tinker with camera position
and so forth .
"""
pass
# -----------------------------
Cleanup, removal of unmaintained code (#836)
* add dtype to Box
* remove board_game, debugging, safety, parameter_tuning environments
* massive set of breaking changes
- remove python logging module
- _step, _reset, _seed, _close => non underscored method
- remove benchmark and scoring folder
* Improve render("human"), now resizable, closable window.
* get rid of default step and reset in wrappers, so it doesn’t silently fail for people with underscore methods
* CubeCrash unit test environment
* followup fixes
* MemorizeDigits unit test envrionment
* refactored spaces a bit
fixed indentation
disabled test_env_semantics
* fix unit tests
* fixes
* CubeCrash, MemorizeDigits tested
* gym backwards compatibility patch
* gym backwards compatibility, followup fixes
* changelist, add spaces to main namespaces
* undo_logger_setup for backwards compat
* remove configuration.py
2018-01-25 18:20:14 -08:00
def reset ( self ) :
2018-01-24 15:42:29 -08:00
self . sim . reset ( )
2016-04-30 22:47:51 -07:00
ob = self . reset_model ( )
return ob
def set_state ( self , qpos , qvel ) :
assert qpos . shape == ( self . model . nq , ) and qvel . shape == ( self . model . nv , )
2018-01-24 15:42:29 -08:00
old_state = self . sim . get_state ( )
new_state = mujoco_py . MjSimState ( old_state . time , qpos , qvel ,
old_state . act , old_state . udd_state )
self . sim . set_state ( new_state )
self . sim . forward ( )
2016-04-30 22:47:51 -07:00
2016-04-27 08:00:58 -07:00
@property
def dt ( self ) :
return self . model . opt . timestep * self . frame_skip
def do_simulation ( self , ctrl , n_frames ) :
2018-01-24 15:42:29 -08:00
self . sim . data . ctrl [ : ] = ctrl
2016-04-27 08:00:58 -07:00
for _ in range ( n_frames ) :
2018-01-24 15:42:29 -08:00
self . sim . step ( )
2016-04-27 08:00:58 -07:00
2019-08-23 15:02:33 -07:00
def render ( self ,
mode = ' human ' ,
width = DEFAULT_SIZE ,
height = DEFAULT_SIZE ,
camera_id = None ,
camera_name = None ) :
2020-06-20 06:42:26 +09:00
if mode == ' rgb_array ' or mode == ' depth_array ' :
2019-08-23 15:02:33 -07:00
if camera_id is not None and camera_name is not None :
raise ValueError ( " Both `camera_id` and `camera_name` cannot be "
" specified at the same time. " )
no_camera_specified = camera_name is None and camera_id is None
if no_camera_specified :
camera_name = ' track '
2019-08-23 15:39:03 -07:00
if camera_id is None and camera_name in self . model . _camera_name2id :
2019-03-25 12:17:38 -07:00
camera_id = self . model . camera_name2id ( camera_name )
2019-08-23 15:02:33 -07:00
2019-03-25 12:17:38 -07:00
self . _get_viewer ( mode ) . render ( width , height , camera_id = camera_id )
2020-06-20 06:42:26 +09:00
if mode == ' rgb_array ' :
2018-02-26 01:48:44 -08:00
# window size used for old mujoco-py:
2018-05-30 01:39:49 -07:00
data = self . _get_viewer ( mode ) . read_pixels ( width , height , depth = False )
2018-02-26 01:48:44 -08:00
# original image is upside-down, so flip it
return data [ : : - 1 , : , : ]
2018-09-21 01:51:58 +01:00
elif mode == ' depth_array ' :
self . _get_viewer ( mode ) . render ( width , height )
# window size used for old mujoco-py:
# Extract depth part of the read_pixels() tuple
data = self . _get_viewer ( mode ) . read_pixels ( width , height , depth = True ) [ 1 ]
# original image is upside-down, so flip it
return data [ : : - 1 , : ]
2016-05-09 20:51:04 -04:00
elif mode == ' human ' :
2018-05-30 01:39:49 -07:00
self . _get_viewer ( mode ) . render ( )
2016-04-27 08:00:58 -07:00
Cleanup, removal of unmaintained code (#836)
* add dtype to Box
* remove board_game, debugging, safety, parameter_tuning environments
* massive set of breaking changes
- remove python logging module
- _step, _reset, _seed, _close => non underscored method
- remove benchmark and scoring folder
* Improve render("human"), now resizable, closable window.
* get rid of default step and reset in wrappers, so it doesn’t silently fail for people with underscore methods
* CubeCrash unit test environment
* followup fixes
* MemorizeDigits unit test envrionment
* refactored spaces a bit
fixed indentation
disabled test_env_semantics
* fix unit tests
* fixes
* CubeCrash, MemorizeDigits tested
* gym backwards compatibility patch
* gym backwards compatibility, followup fixes
* changelist, add spaces to main namespaces
* undo_logger_setup for backwards compat
* remove configuration.py
2018-01-25 18:20:14 -08:00
def close ( self ) :
if self . viewer is not None :
2018-05-14 17:39:56 -07:00
# self.viewer.finish()
Cleanup, removal of unmaintained code (#836)
* add dtype to Box
* remove board_game, debugging, safety, parameter_tuning environments
* massive set of breaking changes
- remove python logging module
- _step, _reset, _seed, _close => non underscored method
- remove benchmark and scoring folder
* Improve render("human"), now resizable, closable window.
* get rid of default step and reset in wrappers, so it doesn’t silently fail for people with underscore methods
* CubeCrash unit test environment
* followup fixes
* MemorizeDigits unit test envrionment
* refactored spaces a bit
fixed indentation
disabled test_env_semantics
* fix unit tests
* fixes
* CubeCrash, MemorizeDigits tested
* gym backwards compatibility patch
* gym backwards compatibility, followup fixes
* changelist, add spaces to main namespaces
* undo_logger_setup for backwards compat
* remove configuration.py
2018-01-25 18:20:14 -08:00
self . viewer = None
2018-05-30 01:39:49 -07:00
self . _viewers = { }
Cleanup, removal of unmaintained code (#836)
* add dtype to Box
* remove board_game, debugging, safety, parameter_tuning environments
* massive set of breaking changes
- remove python logging module
- _step, _reset, _seed, _close => non underscored method
- remove benchmark and scoring folder
* Improve render("human"), now resizable, closable window.
* get rid of default step and reset in wrappers, so it doesn’t silently fail for people with underscore methods
* CubeCrash unit test environment
* followup fixes
* MemorizeDigits unit test envrionment
* refactored spaces a bit
fixed indentation
disabled test_env_semantics
* fix unit tests
* fixes
* CubeCrash, MemorizeDigits tested
* gym backwards compatibility patch
* gym backwards compatibility, followup fixes
* changelist, add spaces to main namespaces
* undo_logger_setup for backwards compat
* remove configuration.py
2018-01-25 18:20:14 -08:00
2018-05-30 01:39:49 -07:00
def _get_viewer ( self , mode ) :
self . viewer = self . _viewers . get ( mode )
2016-04-27 08:00:58 -07:00
if self . viewer is None :
2018-05-30 01:39:49 -07:00
if mode == ' human ' :
self . viewer = mujoco_py . MjViewer ( self . sim )
2018-09-21 01:51:58 +01:00
elif mode == ' rgb_array ' or mode == ' depth_array ' :
2018-09-17 09:54:11 -07:00
self . viewer = mujoco_py . MjRenderContextOffscreen ( self . sim , - 1 )
2019-06-07 14:43:05 -07:00
2016-04-27 08:00:58 -07:00
self . viewer_setup ( )
2018-05-30 01:39:49 -07:00
self . _viewers [ mode ] = self . viewer
2016-04-27 08:00:58 -07:00
return self . viewer
def get_body_com ( self , body_name ) :
2018-01-24 15:42:29 -08:00
return self . data . get_body_xpos ( body_name )
2016-04-27 08:00:58 -07:00
2016-04-30 22:47:51 -07:00
def state_vector ( self ) :
2016-04-27 08:00:58 -07:00
return np . concatenate ( [
2018-01-24 15:42:29 -08:00
self . sim . data . qpos . flat ,
self . sim . data . qvel . flat
2016-04-27 08:00:58 -07:00
] )