diff --git a/README.md b/README.md index 769edbe1..c5f70bcf 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ If you use this in your research, please cite: author = {Rodrigo de Lazcano and Kallinteris Andreas and Jun Jet Tai and Seungjae Ryan Lee and Jordan Terry}, title = {Gymnasium Robotics}, url = {http://github.com/Farama-Foundation/Gymnasium-Robotics}, - version = {1.2.0}, + version = {1.2.3}, year = {2023}, } ``` diff --git a/docs/_scripts/gen_envs_display.py b/docs/_scripts/gen_envs_display.py index 762492e7..9904bfca 100644 --- a/docs/_scripts/gen_envs_display.py +++ b/docs/_scripts/gen_envs_display.py @@ -1,4 +1,5 @@ import os +import re import gymnasium as gym from tqdm import tqdm @@ -31,6 +32,9 @@ env_type = split_entrypoint[-1] env_name = split_entrypoint[-1] + # Remove file version from env_name + env_name = re.sub(r"(?:_v(?P\d+))", "", env_name) + if env_type not in filtered_envs_by_type: filtered_envs_by_type[env_type] = [env_name] elif env_name not in filtered_envs_by_type[env_type]: diff --git a/docs/_scripts/gen_mds.py b/docs/_scripts/gen_mds.py index 8404f61a..4960954b 100644 --- a/docs/_scripts/gen_mds.py +++ b/docs/_scripts/gen_mds.py @@ -10,6 +10,7 @@ from importlib import import_module from utils import trim +import re # REWRITE: generate md's for new environments that don't belong to Fetch or Shadow Hand @@ -43,8 +44,6 @@ docstring = trim(docstring) split_entrypoint = module.split(".") - title_env_name = split_entrypoint[-1].replace("_", " ").title() - if len(split_entrypoint) == 4: env_type = split_entrypoint[-2] env_name = split_entrypoint[-1] @@ -53,6 +52,10 @@ env_type = split_entrypoint[-1] env_name = split_entrypoint[-1] + # Remove file version from env_name + env_name = re.sub(r"(?:_v(?P\d+))", "", env_name) + title_env_name = env_name.replace("_", " ").title() + v_path = os.path.join( os.path.dirname(__file__), "..", diff --git a/docs/_static/videos/maze/ant_maze.gif b/docs/_static/videos/maze/ant_maze.gif index a560f412..198bc5ee 100644 Binary files a/docs/_static/videos/maze/ant_maze.gif and b/docs/_static/videos/maze/ant_maze.gif differ diff --git a/docs/_static/videos/maze/point_maze.gif b/docs/_static/videos/maze/point_maze.gif index f9e7ff1e..5de1d87d 100644 Binary files a/docs/_static/videos/maze/point_maze.gif and b/docs/_static/videos/maze/point_maze.gif differ diff --git a/docs/conf.py b/docs/conf.py index 826b3587..f70accd7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,7 @@ "sphinx.ext.githubpages", "sphinx.ext.viewcode", "myst_parser", + "sphinx_github_changelog", ] source_suffix = { diff --git a/docs/index.md b/docs/index.md index 8739168c..6294ee8d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,8 +26,9 @@ envs/MaMuJoCo/index ```{toctree} :hidden: :caption: Development -release_notes + Github +release_notes/index ``` ```{project-logo} _static/img/robotics-text.png diff --git a/docs/release_notes/index.md b/docs/release_notes/index.md new file mode 100644 index 00000000..668a62bc --- /dev/null +++ b/docs/release_notes/index.md @@ -0,0 +1,8 @@ +# Release Notes + +```{eval-rst} +.. changelog:: + :github: https://github.com/Farama-Foundation/Gymnasium-Robotics/releases + :pypi: https://pypi.org/project/gymnasium-robotics/ + :changelog-url: +``` diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index 6671582f..3ccb189a 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1239,7 +1239,7 @@ def _merge(a, b): ) -__version__ = "1.2.2" +__version__ = "1.2.3" try: diff --git a/gymnasium_robotics/envs/franka_kitchen/kitchen_env.py b/gymnasium_robotics/envs/franka_kitchen/kitchen_env.py index 2158b53e..beba0af9 100644 --- a/gymnasium_robotics/envs/franka_kitchen/kitchen_env.py +++ b/gymnasium_robotics/envs/franka_kitchen/kitchen_env.py @@ -355,7 +355,8 @@ def compute_reward( desired_goal: "dict[str, np.ndarray]", info: "dict[str, Any]", ): - for task in info["tasks_to_complete"]: + self.step_task_completions.clear() + for task in self.tasks_to_complete: distance = np.linalg.norm(achieved_goal[task] - desired_goal[task]) complete = distance < BONUS_THRESH if complete: @@ -394,7 +395,6 @@ def _get_obs(self, robot_obs): def step(self, action): robot_obs, _, terminated, truncated, info = self.robot_env.step(action) obs = self._get_obs(robot_obs) - info = {"tasks_to_complete": list(self.tasks_to_complete)} reward = self.compute_reward(obs["achieved_goal"], self.goal, info) @@ -405,13 +405,13 @@ def step(self, action): for element in self.step_task_completions ] + info = {"tasks_to_complete": list(self.tasks_to_complete)} info["step_task_completions"] = self.step_task_completions.copy() for task in self.step_task_completions: if task not in self.episode_task_completions: self.episode_task_completions.append(task) info["episode_task_completions"] = self.episode_task_completions - self.step_task_completions.clear() if self.terminate_on_tasks_completed: # terminate if there are no more tasks to complete terminated = len(self.episode_task_completions) == len(self.goal.keys()) @@ -423,9 +423,9 @@ def reset(self, *, seed: Optional[int] = None, **kwargs): self.episode_task_completions.clear() robot_obs, _ = self.robot_env.reset(seed=seed) obs = self._get_obs(robot_obs) - self.task_to_complete = set(self.goal.keys()) + self.tasks_to_complete = set(self.goal.keys()) info = { - "tasks_to_complete": self.task_to_complete, + "tasks_to_complete": list(self.tasks_to_complete), "episode_task_completions": [], "step_task_completions": [], } diff --git a/gymnasium_robotics/envs/maze/ant_maze_v4.py b/gymnasium_robotics/envs/maze/ant_maze_v4.py index 3155540c..bf8ec0f2 100644 --- a/gymnasium_robotics/envs/maze/ant_maze_v4.py +++ b/gymnasium_robotics/envs/maze/ant_maze_v4.py @@ -183,7 +183,8 @@ class AntMazeEnv(MazeEnv, EzPickle): ### Arguments * `maze_map` - Optional argument to initialize the environment with a custom maze map. - * `continuing_task` - If set to `True` the episode won't be terminated when reaching the goal, instead a new goal location will be generated. If `False` the environment is terminated when the ant reaches the final goal. + * `continuing_task` - If set to `True` the episode won't be terminated when reaching the goal, instead a new goal location will be generated (unless `reset_target` argument is `True`). If `False` the environment is terminated when the ant reaches the final goal. + * `reset_target` - If set to `True` and the argument `continuing_task` is also `True`, when the ant reaches the target goal the location of the goal will be kept the same and no new goal location will be generated. If `False` a new goal will be generated when reached. * `use_contact_forces` - If `True` contact forces of the ant are included in the `observation`. Note that, the maximum number of timesteps before the episode is `truncated` can be increased or decreased by specifying the `max_episode_steps` argument at initialization. For example, @@ -216,6 +217,7 @@ def __init__( maze_map: List[List[Union[str, int]]] = U_MAZE, reward_type: str = "sparse", continuing_task: bool = True, + reset_target: bool = False, **kwargs, ): # Get the ant.xml path from the Gymnasium package @@ -229,6 +231,7 @@ def __init__( maze_height=0.5, reward_type=reward_type, continuing_task=continuing_task, + reset_target=reset_target, **kwargs, ) # Create the MuJoCo environment, include position observation of the Ant for GoalEnv @@ -255,13 +258,13 @@ def __init__( ) self.render_mode = render_mode - EzPickle.__init__( self, render_mode, maze_map, reward_type, continuing_task, + reset_target, **kwargs, ) diff --git a/gymnasium_robotics/envs/maze/maze.py b/gymnasium_robotics/envs/maze/maze.py index 117a3a00..361fc81c 100644 --- a/gymnasium_robotics/envs/maze/maze.py +++ b/gymnasium_robotics/envs/maze/maze.py @@ -215,10 +215,10 @@ def reset( else: if "goal_cell" in options and options["goal_cell"] is not None: # assert that goal cell is valid - assert self.maze.map_length > options["goal_cell"][1] - assert self.maze.map_width > options["goal_cell"][0] + assert self.maze.map_length > options["goal_cell"][0] + assert self.maze.map_width > options["goal_cell"][1] assert ( - self.maze.maze_map[options["goal_cell"][1]][options["goal_cell"][0]] + self.maze.maze_map[options["goal_cell"][0]][options["goal_cell"][1]] != 1 ), f"Goal can't be placed in a wall cell, {options['goal_cell']}" @@ -231,11 +231,11 @@ def reset( if "reset_cell" in options and options["reset_cell"] is not None: # assert that goal cell is valid - assert self.maze.map_length > options["reset_cell"][1] - assert self.maze.map_width > options["reset_cell"][0] + assert self.maze.map_length > options["reset_cell"][0] + assert self.maze.map_width > options["reset_cell"][1] assert ( - self.maze.maze_map[options["reset_cell"][1]][ - options["reset_cell"][0] + self.maze.maze_map[options["reset_cell"][0]][ + options["reset_cell"][1] ] != 1 ), f"Reset can't be placed in a wall cell, {options['reset_cell']}" diff --git a/gymnasium_robotics/envs/maze/maze_v4.py b/gymnasium_robotics/envs/maze/maze_v4.py index 5edec6da..7b807677 100644 --- a/gymnasium_robotics/envs/maze/maze_v4.py +++ b/gymnasium_robotics/envs/maze/maze_v4.py @@ -221,6 +221,15 @@ def make_maze( # If there are no given "r", "g" or "c" cells in the maze data structure, # any empty cell can be a reset or goal location at initialization. maze._combined_locations = empty_locations + elif not maze._unique_reset_locations and not maze._combined_locations: + # If there are no given "r" or "c" cells in the maze data structure, + # any empty cell can be a reset location at initialization. + maze._unique_reset_locations = empty_locations + elif not maze._unique_goal_locations and not maze._combined_locations: + # If there are no given "g" or "c" cells in the maze data structure, + # any empty cell can be a gaol location at initialization. + maze._unique_goal_locations = empty_locations + maze._unique_goal_locations += maze._combined_locations maze._unique_reset_locations += maze._combined_locations @@ -238,6 +247,7 @@ def __init__( agent_xml_path: str, reward_type: str = "dense", continuing_task: bool = True, + reset_target: bool = True, maze_map: List[List[Union[int, str]]] = U_MAZE, maze_size_scaling: float = 1.0, maze_height: float = 0.5, @@ -247,6 +257,7 @@ def __init__( self.reward_type = reward_type self.continuing_task = continuing_task + self.reset_target = reset_target self.maze, self.tmp_xml_file_path = Maze.make_maze( agent_xml_path, maze_map, maze_size_scaling, maze_height ) @@ -282,6 +293,12 @@ def reset( seed: Optional[int] = None, options: Optional[Dict[str, Optional[np.ndarray]]] = None, ): + """Reset the maze simulation. + + Args: + options (dict[str, np.ndarray]): the options dictionary can contain two items, "goal_cell" and "reset_cell" that will set the initial goal and reset location (i,j) in the self.maze.map list of list maze structure. + + """ super().reset(seed=seed) if options is None: @@ -292,10 +309,10 @@ def reset( else: if "goal_cell" in options and options["goal_cell"] is not None: # assert that goal cell is valid - assert self.maze.map_length > options["goal_cell"][1] - assert self.maze.map_width > options["goal_cell"][0] + assert self.maze.map_length > options["goal_cell"][0] + assert self.maze.map_width > options["goal_cell"][1] assert ( - self.maze.maze_map[options["goal_cell"][1]][options["goal_cell"][0]] + self.maze.maze_map[options["goal_cell"][0]][options["goal_cell"][1]] != 1 ), f"Goal can't be placed in a wall cell, {options['goal_cell']}" @@ -309,11 +326,11 @@ def reset( if "reset_cell" in options and options["reset_cell"] is not None: # assert that goal cell is valid - assert self.maze.map_length > options["reset_cell"][1] - assert self.maze.map_width > options["reset_cell"][0] + assert self.maze.map_length > options["reset_cell"][0] + assert self.maze.map_width > options["reset_cell"][1] assert ( - self.maze.maze_map[options["reset_cell"][1]][ - options["reset_cell"][0] + self.maze.maze_map[options["reset_cell"][0]][ + options["reset_cell"][1] ] != 1 ), f"Reset can't be placed in a wall cell, {options['reset_cell']}" @@ -373,8 +390,10 @@ def compute_terminated( def update_goal(self, achieved_goal: np.ndarray) -> None: """Update goal position if continuing task and within goal radius.""" + if ( self.continuing_task + and self.reset_target and bool(np.linalg.norm(achieved_goal - self.goal) <= 0.45) and len(self.maze.unique_goal_locations) > 1 ): diff --git a/gymnasium_robotics/envs/maze/point_maze.py b/gymnasium_robotics/envs/maze/point_maze.py index 564c2aab..33b263e7 100644 --- a/gymnasium_robotics/envs/maze/point_maze.py +++ b/gymnasium_robotics/envs/maze/point_maze.py @@ -279,6 +279,7 @@ class PointMazeEnv(MazeEnv, EzPickle): * `maze_map` - Optional argument to initialize the environment with a custom maze map. * `continuing_task` - If set to `True` the episode won't be terminated when reaching the goal, instead a new goal location will be generated. If `False` the environment is terminated when the ball reaches the final goal. + * `reset_target` - If set to `True` and the argument `continuing_task` is also `True`, when the ant reaches the target goal the location of the goal will be kept the same and no new goal location will be generated. If `False` a new goal will be generated when reached. Note that, the maximum number of timesteps before the episode is `truncated` can be increased or decreased by specifying the `max_episode_steps` argument at initialization. For example, to increase the total number of timesteps to 100 make the environment as follows: @@ -309,6 +310,7 @@ def __init__( render_mode: Optional[str] = None, reward_type: str = "sparse", continuing_task: bool = True, + reset_target: bool = False, **kwargs, ): point_xml_file_path = path.join( @@ -321,6 +323,7 @@ def __init__( maze_height=0.4, reward_type=reward_type, continuing_task=continuing_task, + reset_target=reset_target, **kwargs, ) @@ -356,6 +359,7 @@ def __init__( render_mode, reward_type, continuing_task, + reset_target, **kwargs, ) diff --git a/gymnasium_robotics/envs/shadow_dexterous_hand/reach.py b/gymnasium_robotics/envs/shadow_dexterous_hand/reach.py index cf8d2965..984bd4d6 100644 --- a/gymnasium_robotics/envs/shadow_dexterous_hand/reach.py +++ b/gymnasium_robotics/envs/shadow_dexterous_hand/reach.py @@ -189,115 +189,115 @@ class MujocoHandReachEnv(get_base_hand_reanch_env(MujocoHandEnv)): The observation is a `goal-aware observation space`. It consists of a dictionary with information about the robot's joint and finger states, as well as information about the goal. The finger tip observations are derived from Mujoco bodies known as [sites](https://mujoco.readthedocs.io/en/latest/XMLreference.html?highlight=site#body-site) attached to the body of interest such as the finger tips. The dictionary consists of the following 3 keys: - `observation`: its value is an `ndarray` of shape `(63,)`. It consists of kinematic information of the block object and gripper. The elements of the array correspond to the following: - - | Num | Observation | Min | Max | Joint Name (in corresponding XML file) | Site Name (in corresponding XML file) |Joint Type| Unit | - |-----|-------------------------------------------------------------------|--------|--------|----------------------------------------|---------------------------------------|----------|------------------------- | - | 0 | Angular position of the horizontal wrist joint | -Inf | Inf | robot0:WRJ1 | - | hinge | angle (rad) | - | 1 | Angular position of the vertical wrist joint | -Inf | Inf | robot0:WRJ0 | - | hinge | angle (rad) | - | 2 | Horizontal angular position of the MCP joint of the forefinger | -Inf | Inf | robot0:FFJ3 | - | hinge | angle (rad) | - | 3 | Vertical angular position of the MCP joint of the forefinge | -Inf | Inf | robot0:FFJ2 | - | hinge | angle (rad) | - | 4 | Angular position of the PIP joint of the forefinger | -Inf | Inf | robot0:FFJ1 | - | hinge | angle (rad) | - | 5 | Angular position of the DIP joint of the forefinger | -Inf | Inf | robot0:FFJ0 | - | hinge | angle (rad) | - | 6 | Horizontal angular position of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ3 | - | hinge | angle (rad) | - | 7 | Vertical angular position of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ2 | - | hinge | angle (rad) | - | 8 | Angular position of the PIP joint of the middle finger | -Inf | Inf | robot0:MFJ1 | - | hinge | angle (rad) | - | 9 | Angular position of the DIP joint of the middle finger | -Inf | Inf | robot0:MFJ0 | - | hinge | angle (rad) | - | 10 | Horizontal angular position of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ3 | - | hinge | angle (rad) | - | 11 | Vertical angular position of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ2 | - | hinge | angle (rad) | - | 12 | Angular position of the PIP joint of the ring finger | -Inf | Inf | robot0:RFJ1 | - | hinge | angle (rad) | - | 13 | Angular position of the DIP joint of the ring finger | -Inf | Inf | robot0:RFJ0 | - | hinge | angle (rad) | - | 14 | Angular position of the CMC joint of the little finger | -Inf | Inf | robot0:LFJ4 | - | hinge | angle (rad) | - | 15 | Horizontal angular position of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ3 | - | hinge | angle (rad) | - | 16 | Vertical angular position of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ2 | - | hinge | angle (rad) | - | 17 | Angular position of the PIP joint of the little finger | -Inf | Inf | robot0:LFJ1 | - | hinge | angle (rad) | - | 18 | Angular position of the DIP joint of the little finger | -Inf | Inf | robot0:LFJ0 | - | hinge | angle (rad) | - | 19 | Horizontal angular position of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ4 | - | hinge | angle (rad) | - | 20 | Vertical Angular position of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ3 | - | hinge | angle (rad) | - | 21 | Horizontal angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ2 | - | hinge | angle (rad) | - | 22 | Vertical angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ1 | - | hinge | angle (rad) | - | 23 | Angular position of the IP joint of the thumb finger | -Inf | Inf | robot0:THJ0 | - | hinge | angle (rad) | - | 24 | Angular velocity of the horizontal wrist joint | -Inf | Inf | robot0:WRJ1 | - | hinge | angular velocity (rad/s) | - | 25 | Angular velocity of the vertical wrist joint | -Inf | Inf | robot0:WRJ0 | - | hinge | angular velocity (rad/s) | - | 26 | Horizontal angular velocity of the MCP joint of the forefinger | -Inf | Inf | robot0:FFJ3 | - | hinge | angular velocity (rad/s) | - | 27 | Vertical angular velocity of the MCP joint of the forefinge | -Inf | Inf | robot0:FFJ2 | - | hinge | angular velocity (rad/s) | - | 28 | Angular velocity of the PIP joint of the forefinger | -Inf | Inf | robot0:FFJ1 | - | hinge | angular velocity (rad/s) | - | 29 | Angular velocity of the DIP joint of the forefinger | -Inf | Inf | robot0:FFJ0 | - | hinge | angular velocity (rad/s) | - | 30 | Horizontal angular velocity of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ3 | - | hinge | angular velocity (rad/s) | - | 31 | Vertical angular velocity of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ2 | - | hinge | angular velocity (rad/s) | - | 32 | Angular velocity of the PIP joint of the middle finger | -Inf | Inf | robot0:MFJ1 | - | hinge | angular velocity (rad/s) | - | 33 | Angular velocity of the DIP joint of the middle finger | -Inf | Inf | robot0:MFJ0 | - | hinge | angular velocity (rad/s) | - | 34 | Horizontal angular velocity of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ3 | - | hinge | angular velocity (rad/s) | - | 35 | Vertical angular velocity of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ2 | - | hinge | angular velocity (rad/s) | - | 36 | Angular velocity of the PIP joint of the ring finger | -Inf | Inf | robot0:RFJ1 | - | hinge | angular velocity (rad/s) | - | 37 | Angular velocity of the DIP joint of the ring finger | -Inf | Inf | robot0:RFJ0 | - | hinge | angular velocity (rad/s) | - | 38 | Angular velocity of the CMC joint of the little finger | -Inf | Inf | robot0:LFJ4 | - | hinge | angular velocity (rad/s) | - | 39 | Horizontal angular velocity of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ3 | - | hinge | angular velocity (rad/s) | - | 40 | Vertical angular velocity of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ2 | - | hinge | angular velocity (rad/s) | - | 41 | Angular velocity of the PIP joint of the little finger | -Inf | Inf | robot0:LFJ1 | - | hinge | angular velocity (rad/s) | - | 42 | Angular velocity of the DIP joint of the little finger | -Inf | Inf | robot0:LFJ0 | - | hinge | angular velocity (rad/s) | - | 43 | Horizontal angular velocity of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ4 | - | hinge | angular velocity (rad/s) | - | 44 | Vertical Angular velocity of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ3 | - | hinge | angular velocity (rad/s) | - | 45 | Horizontal angular velocity of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ2 | - | hinge | angular velocity (rad/s) | - | 46 | Vertical angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ1 | - | hinge | angular velocity (rad/s) | - | 47 | Angular velocity of the IP joint of the thumb finger | -Inf | Inf | robot0:THJ0 | - | hinge | angular velocity (rad/s) | - | 48 | x coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | - | 49 | y coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | - | 50 | z coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | - | 51 | x coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | - | 52 | y coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | - | 53 | z coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | - | 54 | x coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | - | 55 | y coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | - | 56 | z coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | - | 57 | x coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | - | 58 | y coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | - | 59 | z coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | - | 60 | x coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | - | 61 | y coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | - | 62 | z coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | - - `desired_goal`: this key represents the final goal to be achieved. In this environment it is a 15-dimensional `ndarray`, `(15,)`, that consists of the 15 cartesian coordinates of the desired final finger tip position `[x,y,z]`. + * `observation`: its value is an `ndarray` of shape `(63,)`. It consists of kinematic information of the block object and gripper. The elements of the array correspond to the following: + + | Num | Observation | Min | Max | Joint Name (in corresponding XML file) | Site Name (in corresponding XML file) |Joint Type| Unit | + |-----|-------------------------------------------------------------------|--------|--------|----------------------------------------|---------------------------------------|----------|------------------------- | + | 0 | Angular position of the horizontal wrist joint | -Inf | Inf | robot0:WRJ1 | - | hinge | angle (rad) | + | 1 | Angular position of the vertical wrist joint | -Inf | Inf | robot0:WRJ0 | - | hinge | angle (rad) | + | 2 | Horizontal angular position of the MCP joint of the forefinger | -Inf | Inf | robot0:FFJ3 | - | hinge | angle (rad) | + | 3 | Vertical angular position of the MCP joint of the forefinge | -Inf | Inf | robot0:FFJ2 | - | hinge | angle (rad) | + | 4 | Angular position of the PIP joint of the forefinger | -Inf | Inf | robot0:FFJ1 | - | hinge | angle (rad) | + | 5 | Angular position of the DIP joint of the forefinger | -Inf | Inf | robot0:FFJ0 | - | hinge | angle (rad) | + | 6 | Horizontal angular position of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ3 | - | hinge | angle (rad) | + | 7 | Vertical angular position of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ2 | - | hinge | angle (rad) | + | 8 | Angular position of the PIP joint of the middle finger | -Inf | Inf | robot0:MFJ1 | - | hinge | angle (rad) | + | 9 | Angular position of the DIP joint of the middle finger | -Inf | Inf | robot0:MFJ0 | - | hinge | angle (rad) | + | 10 | Horizontal angular position of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ3 | - | hinge | angle (rad) | + | 11 | Vertical angular position of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ2 | - | hinge | angle (rad) | + | 12 | Angular position of the PIP joint of the ring finger | -Inf | Inf | robot0:RFJ1 | - | hinge | angle (rad) | + | 13 | Angular position of the DIP joint of the ring finger | -Inf | Inf | robot0:RFJ0 | - | hinge | angle (rad) | + | 14 | Angular position of the CMC joint of the little finger | -Inf | Inf | robot0:LFJ4 | - | hinge | angle (rad) | + | 15 | Horizontal angular position of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ3 | - | hinge | angle (rad) | + | 16 | Vertical angular position of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ2 | - | hinge | angle (rad) | + | 17 | Angular position of the PIP joint of the little finger | -Inf | Inf | robot0:LFJ1 | - | hinge | angle (rad) | + | 18 | Angular position of the DIP joint of the little finger | -Inf | Inf | robot0:LFJ0 | - | hinge | angle (rad) | + | 19 | Horizontal angular position of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ4 | - | hinge | angle (rad) | + | 20 | Vertical Angular position of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ3 | - | hinge | angle (rad) | + | 21 | Horizontal angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ2 | - | hinge | angle (rad) | + | 22 | Vertical angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ1 | - | hinge | angle (rad) | + | 23 | Angular position of the IP joint of the thumb finger | -Inf | Inf | robot0:THJ0 | - | hinge | angle (rad) | + | 24 | Angular velocity of the horizontal wrist joint | -Inf | Inf | robot0:WRJ1 | - | hinge | angular velocity (rad/s) | + | 25 | Angular velocity of the vertical wrist joint | -Inf | Inf | robot0:WRJ0 | - | hinge | angular velocity (rad/s) | + | 26 | Horizontal angular velocity of the MCP joint of the forefinger | -Inf | Inf | robot0:FFJ3 | - | hinge | angular velocity (rad/s) | + | 27 | Vertical angular velocity of the MCP joint of the forefinge | -Inf | Inf | robot0:FFJ2 | - | hinge | angular velocity (rad/s) | + | 28 | Angular velocity of the PIP joint of the forefinger | -Inf | Inf | robot0:FFJ1 | - | hinge | angular velocity (rad/s) | + | 29 | Angular velocity of the DIP joint of the forefinger | -Inf | Inf | robot0:FFJ0 | - | hinge | angular velocity (rad/s) | + | 30 | Horizontal angular velocity of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ3 | - | hinge | angular velocity (rad/s) | + | 31 | Vertical angular velocity of the MCP joint of the middle finger | -Inf | Inf | robot0:MFJ2 | - | hinge | angular velocity (rad/s) | + | 32 | Angular velocity of the PIP joint of the middle finger | -Inf | Inf | robot0:MFJ1 | - | hinge | angular velocity (rad/s) | + | 33 | Angular velocity of the DIP joint of the middle finger | -Inf | Inf | robot0:MFJ0 | - | hinge | angular velocity (rad/s) | + | 34 | Horizontal angular velocity of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ3 | - | hinge | angular velocity (rad/s) | + | 35 | Vertical angular velocity of the MCP joint of the ring finger | -Inf | Inf | robot0:RFJ2 | - | hinge | angular velocity (rad/s) | + | 36 | Angular velocity of the PIP joint of the ring finger | -Inf | Inf | robot0:RFJ1 | - | hinge | angular velocity (rad/s) | + | 37 | Angular velocity of the DIP joint of the ring finger | -Inf | Inf | robot0:RFJ0 | - | hinge | angular velocity (rad/s) | + | 38 | Angular velocity of the CMC joint of the little finger | -Inf | Inf | robot0:LFJ4 | - | hinge | angular velocity (rad/s) | + | 39 | Horizontal angular velocity of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ3 | - | hinge | angular velocity (rad/s) | + | 40 | Vertical angular velocity of the MCP joint of the little finger | -Inf | Inf | robot0:LFJ2 | - | hinge | angular velocity (rad/s) | + | 41 | Angular velocity of the PIP joint of the little finger | -Inf | Inf | robot0:LFJ1 | - | hinge | angular velocity (rad/s) | + | 42 | Angular velocity of the DIP joint of the little finger | -Inf | Inf | robot0:LFJ0 | - | hinge | angular velocity (rad/s) | + | 43 | Horizontal angular velocity of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ4 | - | hinge | angular velocity (rad/s) | + | 44 | Vertical Angular velocity of the CMC joint of the thumb finger | -Inf | Inf | robot0:THJ3 | - | hinge | angular velocity (rad/s) | + | 45 | Horizontal angular velocity of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ2 | - | hinge | angular velocity (rad/s) | + | 46 | Vertical angular position of the MCP joint of the thumb finger | -Inf | Inf | robot0:THJ1 | - | hinge | angular velocity (rad/s) | + | 47 | Angular velocity of the IP joint of the thumb finger | -Inf | Inf | robot0:THJ0 | - | hinge | angular velocity (rad/s) | + | 48 | x coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | + | 49 | y coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | + | 50 | z coordinate of the tip of the forefinger | -Inf | Inf | - | robot0:S_fftip | - | position (m) | + | 51 | x coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | + | 52 | y coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | + | 53 | z coordinate of the tip of the middle finger | -Inf | Inf | - | robot0:S_mftip | - | position (m) | + | 54 | x coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | + | 55 | y coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | + | 56 | z coordinate of the tip of the ring finger | -Inf | Inf | - | robot0:S_rftip | - | position (m) | + | 57 | x coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | + | 58 | y coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | + | 59 | z coordinate of the tip of the little finger | -Inf | Inf | - | robot0:S_lftip | - | position (m) | + | 60 | x coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | + | 61 | y coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | + | 62 | z coordinate of the tip of the thumb finger | -Inf | Inf | - | robot0:S_thtip | - | position (m) | + + * `desired_goal`: this key represents the final goal to be achieved. In this environment it is a 15-dimensional `ndarray`, `(15,)`, that consists of the 15 cartesian coordinates of the desired final finger tip position `[x,y,z]`. The elements of the array are the following: - | Num | Observation | Min | Max | Site Name (in corresponding XML file) |Unit | - |-----|---------------------------------------------------------------------------------------------------------------------------------------|--------|--------|---------------------------------------|--------------| - | 0 | Target x coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | - | 1 | Target y coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | - | 2 | Target z coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | - | 3 | Target x coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | - | 4 | Target y coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | - | 5 | Target z coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | - | 6 | Target x coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | - | 7 | Target y coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | - | 8 | Target z coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | - | 9 | Target x coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | - | 10 | Target y coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | - | 11 | Target z coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | - | 12 | Target x coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | - | 13 | Target y coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | - | 14 | Target z coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | - - `achieved_goal`: this key represents the current state of the fingers, as if it would have achieved a goal. This is useful for goal orientated learning algorithms such as those that use [Hindsight Experience Replay](https://arxiv.org/abs/1707.01495) (HER). + | Num | Observation | Min | Max | Site Name (in corresponding XML file) |Unit | + |-----|---------------------------------------------------------------------------------------------------------------------------------------|--------|--------|---------------------------------------|--------------| + | 0 | Target x coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | + | 1 | Target y coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | + | 2 | Target z coordinate of the tip of the forefinger | -Inf | Inf | target0 | position (m) | + | 3 | Target x coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | + | 4 | Target y coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | + | 5 | Target z coordinate of the tip of the middle finger | -Inf | Inf | target1 | position (m) | + | 6 | Target x coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | + | 7 | Target y coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | + | 8 | Target z coordinate of the tip of the ring finger | -Inf | Inf | target2 | position (m) | + | 9 | Target x coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | + | 10 | Target y coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | + | 11 | Target z coordinate of the tip of the little finger | -Inf | Inf | target3 | position (m) | + | 12 | Target x coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | + | 13 | Target y coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | + | 14 | Target z coordinate of the tip of the thumb finger | -Inf | Inf | target4 | position (m) | + + * `achieved_goal`: this key represents the current state of the fingers, as if it would have achieved a goal. This is useful for goal orientated learning algorithms such as those that use [Hindsight Experience Replay](https://arxiv.org/abs/1707.01495) (HER). The value is an `ndarray` with shape `(15,)`. The elements of the array are the following: - | Num | Observation | Min | Max | Site Name (in corresponding XML file) |Unit | - |-----|---------------------------------------------------------------------------------------------------------------------------------------|--------|--------|---------------------------------------|--------------| - | 0 | Current x coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | - | 1 | Current y coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | - | 2 | Current z coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | - | 3 | Current x coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | - | 4 | Current y coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | - | 5 | Current z coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | - | 6 | Current x coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | - | 7 | Current y coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | - | 8 | Current z coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | - | 9 | Current x coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | - | 10 | Current y coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | - | 11 | Current z coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | - | 12 | Current x coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | - | 13 | Current y coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | - | 14 | Current z coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | + | Num | Observation | Min | Max | Site Name (in corresponding XML file) |Unit | + |-----|---------------------------------------------------------------------------------------------------------------------------------------|--------|--------|---------------------------------------|--------------| + | 0 | Current x coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | + | 1 | Current y coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | + | 2 | Current z coordinate of the tip of the forefinger | -Inf | Inf | robot0:S_fftip | position (m) | + | 3 | Current x coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | + | 4 | Current y coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | + | 5 | Current z coordinate of the tip of the middle finger | -Inf | Inf | robot0:S_mftip | position (m) | + | 6 | Current x coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | + | 7 | Current y coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | + | 8 | Current z coordinate of the tip of the ring finger | -Inf | Inf | robot0:S_rftip | position (m) | + | 9 | Current x coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | + | 10 | Current y coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | + | 11 | Current z coordinate of the tip of the little finger | -Inf | Inf | robot0:S_lftip | position (m) | + | 12 | Current x coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | + | 13 | Current y coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | + | 14 | Current z coordinate of the tip of the thumb finger | -Inf | Inf | robot0:S_thtip | position (m) | ## Rewards diff --git a/pyproject.toml b/pyproject.toml index 9ebeb307..6877bbae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ classifiers = [ 'Topic :: Scientific/Engineering :: Artificial Intelligence', ] dependencies = [ - "mujoco==2.3.3", + "mujoco>=2.3.3", "numpy>=1.21.0", "gymnasium>=0.26", "PettingZoo>=1.23.0", diff --git a/tests/envs/franka_kitchen/test_kitchen_env.py b/tests/envs/franka_kitchen/test_kitchen_env.py new file mode 100644 index 00000000..e6616cf6 --- /dev/null +++ b/tests/envs/franka_kitchen/test_kitchen_env.py @@ -0,0 +1,123 @@ +from copy import deepcopy + +import gymnasium as gym +import pytest + +from gymnasium_robotics.envs.franka_kitchen.kitchen_env import ( + OBS_ELEMENT_GOALS, + OBS_ELEMENT_INDICES, +) + +TASKS = ["microwave", "kettle"] + + +@pytest.mark.parametrize( + "remove_task_when_completed, terminate_on_tasks_completed", + [[True, True], [False, False]], +) +def test_task_completion(remove_task_when_completed, terminate_on_tasks_completed): + """This test checks the different task completion configurations for the FrankaKitchen-v1 environment. + + The test checks if the info items returned in each step (`tasks_to_complete`, `step_task_completions`, `episode_task_completions`) are correct and correspond + to the behavior of the environment configured at initialization with the arguments: `remove_task_when_completed` and `terminate_on_tasks_completed`. + """ + env = gym.make( + "FrankaKitchen-v1", + tasks_to_complete=TASKS, + remove_task_when_completed=remove_task_when_completed, + terminate_on_tasks_completed=terminate_on_tasks_completed, + ) + # Test task completion for 3 consecutive episodes + for _ in range(3): + tasks_to_complete = deepcopy(TASKS) + completed_tasks = set() + _, info = env.reset() + assert set(info["tasks_to_complete"]) == set( + TASKS + ), f"The item `tasks_to_complete` returned by info when the environment is reset: {set(info['tasks_to_complete'])}, must be equal to the `task_to_complete` argument used to initialize the environment: {tasks_to_complete}." + assert ( + len(info["step_task_completions"]) == 0 + ), f"The key `step_task_completions` returned by info when the environment is reset: {set(info['step_task_completions'])}, must be empty." + assert ( + len(info["episode_task_completions"]) == 0 + ), f"The key `episode_task_completions` returned by info when the environment is reset: {set(info['episode_task_completions'])}, must be empty." + + terminated = False + + # Complete a task sequentially for each environment step + for task in TASKS: + # Force task to be achieved + env.data.qpos[OBS_ELEMENT_INDICES[task]] = OBS_ELEMENT_GOALS[task] + _, _, terminated, _, info = env.step(env.action_space.sample()) + completed_tasks.add(task) + + assert ( + set(info["episode_task_completions"]) == completed_tasks + ), f"The key `episode_task_completions` returned by info: {set(info['episode_task_completions'])}, must be equal to the tasks along the current episode: {completed_tasks}." + if remove_task_when_completed: + tasks_to_complete.remove(task) + assert set(info["tasks_to_complete"]) == set( + tasks_to_complete + ), f"If environment is initialized with `remove_task_when_completed=True` the item `tasks_to_complete` returned by info: {set(info['tasks_to_complete'])}, must be equal to the tasks that haven't been completed yet: {tasks_to_complete}." + assert set(info["step_task_completions"]) == { + task + }, f"The key `step_task_completions` returned by info: {set(info['step_task_completions'])}, must be equal to the tasks completed after the current step: {task}." + + else: + assert set(info["tasks_to_complete"]) == set( + tasks_to_complete + ), f"If environment is initialized with `remove_task_when_completed=False` the item `tasks_to_complete` returned by info: {set(info['tasks_to_complete'])}, must be equal to the set of tasks the environment was initialized with: {tasks_to_complete}." + assert ( + set(info["step_task_completions"]) == completed_tasks + ), f"The key `step_task_completions` returned by info: {set(info['step_task_completions'])}, must be equal to the tasks completed after the current step: {completed_tasks}." + + if terminate_on_tasks_completed: + assert ( + terminated + ), "If the environment is initialized with `terminate_on_tasks_complete=True`, the episode must terminate after all tasks are completed." + else: + assert ( + not terminated + ), "If the environment is initialized with `terminate_on_tasks_complete=False`, the episode must not terminate after all tasks are completed." + + # Complete a task during the same environment step + for _ in range(3): + tasks_to_complete = deepcopy(TASKS) + completed_tasks = set() + _, info = env.reset() + + terminated = False + + # Complete a task sequentially for each environment step + for task in TASKS: + # Force task to be achieved + env.data.qpos[OBS_ELEMENT_INDICES[task]] = OBS_ELEMENT_GOALS[task] + completed_tasks.add(task) + + _, _, terminated, _, info = env.step(env.action_space.sample()) + assert ( + set(info["step_task_completions"]) == completed_tasks + ), f"The key `step_task_completions` returned by info: {set(info['step_task_completions'])}, must be equal to the tasks completed after the current step: {completed_tasks}." + assert ( + set(info["episode_task_completions"]) == completed_tasks + ), f"The key `episode_task_completions` returned by info: {set(info['episode_task_completions'])}, must be equal to the tasks along the current episode: {completed_tasks}." + if remove_task_when_completed: + assert ( + len(info["tasks_to_complete"]) == 0 + ), f"If environment is initialized with `remove_task_when_completed=True` and all tasks were completed the item `tasks_to_complete` returned by info: {set(info['tasks_to_complete'])}, must be empty." + + else: + assert set(info["tasks_to_complete"]) == set( + tasks_to_complete + ), f"If environment is initialized with `remove_task_when_completed=False` the item `tasks_to_complete` returned by info: {set(info['tasks_to_complete'])}, must be equal to the set of tasks the environment was initialized with: {tasks_to_complete}." + + if terminate_on_tasks_completed: + assert ( + terminated + ), "If the environment is initialized with `terminate_on_tasks_complete=True`, the episode must terminate after all tasks are completed." + else: + assert ( + not terminated + ), "If the environment is initialized with `terminate_on_tasks_complete=False`, the episode must not terminate after all tasks are completed." + + env.close()