diff --git a/pyrobolearn/envs/env.py b/pyrobolearn/envs/env.py index a2b2016..2ece6ef 100644 --- a/pyrobolearn/envs/env.py +++ b/pyrobolearn/envs/env.py @@ -32,7 +32,7 @@ __email__ = "briandelhaisse@gmail.com" __status__ = "Development" -class Env(object): # gym.Env): +class Env(object): # gym.Env): # TODO: make it inheriting the gym.Env r"""Environment class. This class defines the environment as it described in a reinforcement learning setting [1]. That is, given an @@ -51,11 +51,10 @@ class Env(object): # gym.Env): [1] "Reinforcement Learning: An Introduction", Sutton and Barto, 1998 [2] "Wikipedia: Composition over Inheritance", https://en.wikipedia.org/wiki/Composition_over_inheritance [3] "OpenAI gym": https://gym.openai.com/ and https://github.com/openai/gym - """ def __init__(self, world, states, rewards=None, terminal_conditions=None, initial_state_generators=None, - physics_randomizers=None, extra_info=None): + physics_randomizers=None, extra_info=None, actions=None): """ Initialize the environment. @@ -72,6 +71,9 @@ class Env(object): # gym.Env): physics_randomizers (None, PhysicsRandomizer, list of PhysicsRandomizer): physics randomizers. This will be called each time you reset the environment. extra_info (None, callable): Extra info returned by the environment at each time step. + actions (Action): actions that are given to the environment. Note that this is not used here in the current + environment as it should be the policy that performs the action. This is useful when creating policies + after the environment (that is, the policy can uses the environment's states and actions). """ # Check and set parameters (see corresponding properties) self.world = world @@ -81,6 +83,7 @@ class Env(object): # gym.Env): self.physics_randomizers = physics_randomizers self.state_generators = initial_state_generators self.extra_info = extra_info if extra_info is not None else lambda: False + self.actions = actions self.rendering = False # check with simulator @@ -115,20 +118,54 @@ class Env(object): # gym.Env): return self._states @states.setter - def states(self, states): # TODO: make it a list of states + def states(self, states): """Set the states.""" if isinstance(states, State): states = [states] elif isinstance(states, (list, tuple)): for idx, state in enumerate(states): if not isinstance(state, State): - raise TypeError("The {} item is not an instance of `State`, but instead: " + raise TypeError("The {}th item is not an instance of `State`, but instead: " "{}".format(idx, type(state))) else: raise TypeError("Expecting the 'states' argument to be an instance of `State` or a list of `State`, " "instead got: {}".format(type(states))) self._states = states + @property + def state(self): + """Return the first (combined) state.""" + return self._states[0] + + @property + def actions(self): + """Return the actions.""" + return self._actions + + @actions.setter + def actions(self, actions): + """Set the actions.""" + if actions is not None: + # if actions is not a list/tuple, make it a list + if not isinstance(actions, (list, tuple)): + actions = [actions] + + # verify the type of each action + for idx, action in enumerate(actions): + if not isinstance(action, Action): + raise TypeError("The {}th item in 'actions' is not an instance of `Action`, instead got: " + "{}".format(idx, type(action))) + + # set the actions + self._actions = actions + + @property + def action(self): + """Return the first (combined) action.""" + if self.actions is None: + return None + return self.actions[0] + @property def rewards(self): """Return the rewards.""" @@ -144,6 +181,11 @@ class Env(object): # gym.Env): rewards = lambda: None self._rewards = rewards + @property + def reward_range(self): + """Return the range of the reward function; a tuple corresponding to the min and max possible rewards""" + return self._rewards.range + @property def terminal_conditions(self): """Return the terminal condition.""" @@ -304,22 +346,15 @@ class Env(object): # gym.Env): def render(self, mode='human'): """Renders the environment (show the GUI).""" - # This is dependent on the simulator. Some simulators allow to show the GUI at any point in time, - # while others like pybullet requires to specify it at the beginning (thus see SimuRealInterface). - - # Bullet: do nothing - # if isinstance(self.sim, Bullet): pass - # self.sim.configureDebugVisualizer(self.sim.COV_ENABLE_RENDERING, 1) - pass + self.sim.render() def hide(self): """hide the GUI.""" - # self.sim.configureDebugVisualizer(self.sim.COV_ENABLE_RENDERING, 0) - pass + self.sim.hide() def close(self): """Close the environment.""" - pass + self.sim.close() def seed(self, seed=None): """ @@ -343,10 +378,10 @@ class BasicEnv(Env): """ def __init__(self, sim, states=None, rewards=None, terminal_conditions=None, initial_state_generators=None, - physics_randomizers=None, extra_info=None): + physics_randomizers=None, extra_info=None, actions=None): world = BasicWorld(sim) super(BasicEnv, self).__init__(world, states, rewards, terminal_conditions, initial_state_generators, - physics_randomizers, extra_info) + physics_randomizers, extra_info, actions) # Tests diff --git a/pyrobolearn/envs/gym_wrapper.py b/pyrobolearn/envs/gym_wrapper.py index 634a64c..2097c84 100644 --- a/pyrobolearn/envs/gym_wrapper.py +++ b/pyrobolearn/envs/gym_wrapper.py @@ -154,6 +154,11 @@ class GymEnvWrapper(gym.Env): """Return the reward instance.""" return self._reward + @property + def reward_range(self): + """Return the range of the reward function; a tuple corresponding to the min and max possible rewards""" + return self.env.reward_range + @property def state_processors(self): """Return the processors that have to be applied on the state.""" @@ -227,13 +232,14 @@ class GymEnvWrapper(gym.Env): return states def reset(self): + """Reset the gym environment.""" observation = self.env.reset() self.state.data = observation self.reward_processors.reset() return self._convert_state_to_data(self.state) def step(self, actions): - """perform a step in the environment and set the data for the GymState and GymAction""" + """perform a step in the gym environment and set the data for the GymState and GymAction""" if isinstance(actions, Action): # convert from `Action` to numpy actions = actions.merged_data[0] elif isinstance(actions, torch.Tensor): # convert from torch to numpy @@ -263,11 +269,21 @@ class GymEnvWrapper(gym.Env): return observations, reward, done, info def render(self, mode='human'): + """Render the gym environment.""" self.env.render(mode) def hide(self): + """Hide the gym environment (not used).""" pass + def close(self): + """Close the gym environment.""" + self.env.close() + + def seed(self, seed=None): + """Set the given seed to the gym environment.""" + self.env.seed(seed) + ############# # Operators # #############