From 700373dba3fe0016408acad140a4e559530f1613 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:10:24 +0200 Subject: [PATCH 01/26] Update humanoid_v5.py --- gymnasium/envs/mujoco/humanoid_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium/envs/mujoco/humanoid_v5.py b/gymnasium/envs/mujoco/humanoid_v5.py index b6b324b47e..2b87c81f4c 100644 --- a/gymnasium/envs/mujoco/humanoid_v5.py +++ b/gymnasium/envs/mujoco/humanoid_v5.py @@ -38,7 +38,7 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): :name: humanoid ``` - The action space is a `Box(-1, 1, (17,), float32)`. An action represents the torques applied at the hinge joints. + The action space is a `Box(-0.4, 0.4, (17,), float32)`. An action represents the torques applied at the hinge joints. | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Type (Unit) | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | From e169b8fc8b5ae3f5006dfddeb856cebc3fdd927b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:14:53 +0200 Subject: [PATCH 02/26] Update humanoidstandup_v5.py --- gymnasium/envs/mujoco/humanoidstandup_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium/envs/mujoco/humanoidstandup_v5.py b/gymnasium/envs/mujoco/humanoidstandup_v5.py index 8477ec0ca4..6666274f7a 100644 --- a/gymnasium/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium/envs/mujoco/humanoidstandup_v5.py @@ -32,7 +32,7 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): :name: humanoid ``` - The action space is a `Box(-1, 1, (17,), float32)`. An action represents the torques applied at the hinge joints. + The action space is a `Box(-0.4, 0.4, (17,), float32)`. An action represents the torques applied at the hinge joints. | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Type (Unit) | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | From 2f656e3bd6deedd78440dc1805ace78a923ef42c Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 23 May 2024 09:41:36 +0300 Subject: [PATCH 03/26] Update mujoco_env.py --- gymnasium/envs/mujoco/mujoco_env.py | 199 ++++++++++++---------------- 1 file changed, 83 insertions(+), 116 deletions(-) diff --git a/gymnasium/envs/mujoco/mujoco_env.py b/gymnasium/envs/mujoco/mujoco_env.py index 5f2fa6396a..70a7aaf155 100644 --- a/gymnasium/envs/mujoco/mujoco_env.py +++ b/gymnasium/envs/mujoco/mujoco_env.py @@ -34,19 +34,22 @@ def expand_model_path(model_path: str) -> str: return fullpath -class BaseMujocoEnv(gym.Env[NDArray[np.float64], NDArray[np.float32]]): - """Superclass for all MuJoCo environments.""" +class MujocoEnv(BaseMujocoEnv): + """Superclass for MuJoCo based environments.""" def __init__( self, - model_path, - frame_skip, + model_path: str, + frame_skip: int, observation_space: Optional[Space], render_mode: Optional[str] = None, width: int = DEFAULT_SIZE, height: int = DEFAULT_SIZE, camera_id: Optional[int] = None, camera_name: Optional[str] = None, + default_camera_config: Optional[Dict[str, Union[float, int]]] = None, + max_geom: int = 1000, + visual_options: Dict[int, bool] = {}, ): """Base abstract class for mujoco based environments. @@ -59,6 +62,9 @@ def __init__( height: The height of the render window. camera_id: The camera ID used. camera_name: The name of the camera used (can not be used in conjunction with `camera_id`). + default_camera_config: configuration for rendering camera. + max_geom: max number of rendered geometries. + visual_options: render flag options. Raises: OSError: when the `model_path` does not exist. @@ -93,48 +99,79 @@ def __init__( self.camera_name = camera_name self.camera_id = camera_id + from gymnasium.envs.mujoco.mujoco_rendering import MujocoRenderer + + self.mujoco_renderer = MujocoRenderer( + self.model, + self.data, + default_camera_config, + self.width, + self.height, + max_geom, + camera_id, + camera_name, + visual_options, + ) + def _set_action_space(self): bounds = self.model.actuator_ctrlrange.copy().astype(np.float32) low, high = bounds.T self.action_space = spaces.Box(low=low, high=high, dtype=np.float32) return self.action_space - # methods to override: - # ---------------------------- - def step( - self, action: NDArray[np.float32] - ) -> Tuple[NDArray[np.float64], np.float64, bool, bool, Dict[str, np.float64]]: - raise NotImplementedError - - def reset_model(self) -> NDArray[np.float64]: + def _initialize_simulation( + self, + ) -> Tuple["mujoco.MjModel", "mujoco.MjData"]: """ - Reset the robot degrees of freedom (qpos and qvel). - Implement this in each subclass. + Initialize MuJoCo simulation data structures `mjModel` and `mjData`. """ - raise NotImplementedError + model = mujoco.MjModel.from_xml_path(self.fullpath) + # MjrContext will copy model.vis.global_.off* to con.off* + model.vis.global_.offwidth = self.width + model.vis.global_.offheight = self.height + data = mujoco.MjData(model) + return model, data - def _initialize_simulation(self) -> Tuple[Any, Any]: - """ - Initialize MuJoCo simulation data structures mjModel and mjData. + def set_state(self, qpos, qvel): + """Set the joints position qpos and velocity qvel of the model. + + Note: `qpos` and `qvel` is not the full physics state for all mujoco models/environments https://mujoco.readthedocs.io/en/stable/APIreference/APItypes.html#mjtstate """ - raise NotImplementedError + assert qpos.shape == (self.model.nq,) and qvel.shape == (self.model.nv,) + self.data.qpos[:] = np.copy(qpos) + self.data.qvel[:] = np.copy(qvel) + if self.model.na == 0: + self.data.act[:] = None + mujoco.mj_forward(self.model, self.data) - def _step_mujoco_simulation(self, ctrl, n_frames) -> None: + def _step_mujoco_simulation(self, ctrl, n_frames): """ Step over the MuJoCo simulation. """ - raise NotImplementedError + self.data.ctrl[:] = ctrl - def render(self) -> Union[NDArray[np.float64], None]: + mujoco.mj_step(self.model, self.data, nstep=n_frames) + + # As of MuJoCo 2.0, force-related quantities like cacc are not computed + # unless there's a force sensor in the model. + # See https://github.com/openai/gym/issues/1541 + mujoco.mj_rnePostConstraint(self.model, self.data) + + def render(self): """ Render a frame from the MuJoCo simulation as specified by the render_mode. """ - raise NotImplementedError + return self.mujoco_renderer.render(self.render_mode) + + def close(self): + """Close rendering contexts processes.""" + if self.mujoco_renderer is not None: + self.mujoco_renderer.close() + + def get_body_com(self, body_name): + """Return the cartesian position of a body frame.""" + return self.data.body(body_name).xpos - # ----------------------------- - def _get_reset_info(self) -> Dict[str, float]: - """Function that generates the `info` that is returned during a `reset()`.""" - return {} def reset( self, @@ -168,99 +205,29 @@ def do_simulation(self, ctrl, n_frames) -> None: ) self._step_mujoco_simulation(ctrl, n_frames) - def close(self): - """Close all processes like rendering contexts""" - raise NotImplementedError - - def get_body_com(self, body_name) -> NDArray[np.float64]: - """Return the cartesian position of a body frame""" - raise NotImplementedError - def state_vector(self) -> NDArray[np.float64]: - """Return the position and velocity joint states of the model""" + """Return the position and velocity joint states of the model. + + Note: `qpos` and `qvel` does not constitute the full physics state for all `mujoco` environments see https://mujoco.readthedocs.io/en/stable/computation/index.html#the-state. + """ return np.concatenate([self.data.qpos.flat, self.data.qvel.flat]) + # methods to override: + # ---------------------------- + def step( + self, action: NDArray[np.float32] + ) -> Tuple[NDArray[np.float64], np.float64, bool, bool, Dict[str, np.float64]]: + raise NotImplementedError -class MujocoEnv(BaseMujocoEnv): - """Superclass for MuJoCo environments.""" - - def __init__( - self, - model_path, - frame_skip, - observation_space: Optional[Space], - render_mode: Optional[str] = None, - width: int = DEFAULT_SIZE, - height: int = DEFAULT_SIZE, - camera_id: Optional[int] = None, - camera_name: Optional[str] = None, - default_camera_config: Optional[Dict[str, Union[float, int]]] = None, - max_geom: int = 1000, - visual_options: Dict[int, bool] = {}, - ): - super().__init__( - model_path, - frame_skip, - observation_space, - render_mode, - width, - height, - camera_id, - camera_name, - ) - - from gymnasium.envs.mujoco.mujoco_rendering import MujocoRenderer - - self.mujoco_renderer = MujocoRenderer( - self.model, - self.data, - default_camera_config, - self.width, - self.height, - max_geom, - camera_id, - camera_name, - visual_options, - ) - - def _initialize_simulation( - self, - ) -> Tuple["mujoco._structs.MjModel", "mujoco._structs.MjData"]: - model = mujoco.MjModel.from_xml_path(self.fullpath) - # MjrContext will copy model.vis.global_.off* to con.off* - model.vis.global_.offwidth = self.width - model.vis.global_.offheight = self.height - data = mujoco.MjData(model) - return model, data - - def set_state(self, qpos, qvel): - """Set the joints position qpos and velocity qvel of the model. - - Note: `qpos` and `qvel` is not the full physics state for all mujoco models/environments https://mujoco.readthedocs.io/en/stable/APIreference/APItypes.html#mjtstate + def reset_model(self) -> NDArray[np.float64]: """ - assert qpos.shape == (self.model.nq,) and qvel.shape == (self.model.nv,) - self.data.qpos[:] = np.copy(qpos) - self.data.qvel[:] = np.copy(qvel) - if self.model.na == 0: - self.data.act[:] = None - mujoco.mj_forward(self.model, self.data) - - def _step_mujoco_simulation(self, ctrl, n_frames): - self.data.ctrl[:] = ctrl - - mujoco.mj_step(self.model, self.data, nstep=n_frames) - - # As of MuJoCo 2.0, force-related quantities like cacc are not computed - # unless there's a force sensor in the model. - # See https://github.com/openai/gym/issues/1541 - mujoco.mj_rnePostConstraint(self.model, self.data) - - def render(self): - return self.mujoco_renderer.render(self.render_mode) + Reset the robot degrees of freedom (qpos and qvel). + Implement this in each environment subclass. + """ + raise NotImplementedError - def close(self): - if self.mujoco_renderer is not None: - self.mujoco_renderer.close() + def _get_reset_info(self) -> Dict[str, float]: + """Function that generates the `info` that is returned during a `reset()`.""" + return {} - def get_body_com(self, body_name): - return self.data.body(body_name).xpos + # ----------------------------- From 13f67fe44cc3ff0f3874f2059e102691e6e07868 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 23 May 2024 09:42:59 +0300 Subject: [PATCH 04/26] Update test_mujoco_v5.py --- tests/envs/mujoco/test_mujoco_v5.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py index afba1da473..c7a5fb0c01 100644 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -600,10 +600,7 @@ def test_model_object_count(version: str): else: assert env.model.nbvh == 18 assert env.model.njnt == 11 - if version == "v4": - assert env.model.ngeom == 21 - else: - assert env.model.ngeom == 20 + assert env.model.ngeom == 21 assert env.model.ntendon == 0 env = gym.make(f"Reacher-{version}").unwrapped From 93d8a0cd5ec2b931111982b9cc36f3ffbde39626 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Sat, 1 Jun 2024 08:40:16 +0000 Subject: [PATCH 05/26] `pre-commit` --- gymnasium/envs/mujoco/mujoco_env.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gymnasium/envs/mujoco/mujoco_env.py b/gymnasium/envs/mujoco/mujoco_env.py index 70a7aaf155..2f3e530503 100644 --- a/gymnasium/envs/mujoco/mujoco_env.py +++ b/gymnasium/envs/mujoco/mujoco_env.py @@ -1,5 +1,5 @@ from os import path -from typing import Any, Dict, Optional, Tuple, Union +from typing import Dict, Optional, Tuple, Union import numpy as np from numpy.typing import NDArray @@ -34,7 +34,7 @@ def expand_model_path(model_path: str) -> str: return fullpath -class MujocoEnv(BaseMujocoEnv): +class MujocoEnv(gym.Env): """Superclass for MuJoCo based environments.""" def __init__( @@ -172,7 +172,6 @@ def get_body_com(self, body_name): """Return the cartesian position of a body frame.""" return self.data.body(body_name).xpos - def reset( self, *, @@ -207,7 +206,7 @@ def do_simulation(self, ctrl, n_frames) -> None: def state_vector(self) -> NDArray[np.float64]: """Return the position and velocity joint states of the model. - + Note: `qpos` and `qvel` does not constitute the full physics state for all `mujoco` environments see https://mujoco.readthedocs.io/en/stable/computation/index.html#the-state. """ return np.concatenate([self.data.qpos.flat, self.data.qvel.flat]) From 5ca94b43b89f67cf72970fe15a3c9d5de5c073b3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Sat, 1 Jun 2024 11:48:11 +0300 Subject: [PATCH 06/26] Update test_mujoco_v5.py --- tests/envs/mujoco/test_mujoco_v5.py | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py index c7a5fb0c01..bf9c639e4a 100644 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -6,7 +6,7 @@ import pytest import gymnasium as gym -from gymnasium.envs.mujoco.mujoco_env import BaseMujocoEnv, MujocoEnv +from gymnasium.envs.mujoco.mujoco_env import MujocoEnv from gymnasium.envs.mujoco.mujoco_py_env import BaseMujocoPyEnv from gymnasium.envs.mujoco.utils import check_mujoco_reset_state from gymnasium.error import Error @@ -90,7 +90,7 @@ def test_verify_info_y_position(env_id: str): def test_verify_info_x_velocity(env_name: str, version: str): """Asserts that the environment `info['x_velocity']` is properly assigned.""" env = gym.make(f"{env_name}-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() old_x = env.data.qpos[0] @@ -107,7 +107,7 @@ def test_verify_info_x_velocity(env_name: str, version: str): def test_verify_info_y_velocity(env_id: str): """Asserts that the environment `info['y_velocity']` is properly assigned.""" env = gym.make(env_id).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() old_y = env.data.qpos[1] @@ -123,7 +123,7 @@ def test_verify_info_y_velocity(env_id: str): def test_verify_info_xy_velocity_xpos(env_id: str): """Asserts that the environment `info['x/y_velocity']` is properly assigned, for the ant environment which uses kinmatics for the velocity.""" env = gym.make(env_id).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() old_xy = env.get_body_com("torso")[:2].copy() @@ -146,7 +146,7 @@ def mass_center(model, data): return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() env = gym.make(env_id).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() old_xy = mass_center(env.model, env.data) @@ -178,7 +178,7 @@ def mass_center(model, data): def test_verify_reward_survive(env_name: str, version: str): """Assert that `reward_survive` is 0 on `terminal` states and not 0 on non-`terminal` states.""" env = gym.make(f"{env_name}-{version}", reset_noise_scale=0).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset(seed=0) env.action_space.seed(1) @@ -361,7 +361,7 @@ def test_ant_com(version: str): """Verify the kinmatic behaviour of the ant.""" # `env` contains `data : MjData` and `model : MjModel` env = gym.make(f"Ant-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() # randomly initlizies the `data.qpos` and `data.qvel`, calls mujoco.mj_forward(env.model, env.data) x_position_before = env.data.qpos[0] @@ -382,7 +382,7 @@ def test_ant_com(version: str): def test_set_state(version: str): """Simple Test to verify that `mujocoEnv.set_state()` works correctly.""" env = gym.make(f"Hopper-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() new_qpos = np.array( [0.00136962, 1.24769787, -0.00459026, -0.00483472, 0.0031327, 0.00412756] @@ -405,7 +405,7 @@ def test_set_state(version: str): def test_distance_from_origin_info(env_id: str): """Verify that `info"distance_from_origin"` is correct.""" env = gym.make(env_id).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() _, _, _, _, info = env.step(env.action_space.sample()) @@ -487,7 +487,7 @@ def test_reset_info(env_name: str, version: str): def test_inverted_double_pendulum_max_height(version: str): """Verify the max height of Inverted Double Pendulum.""" env = gym.make(f"InvertedDoublePendulum-{version}", reset_noise_scale=0).unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.reset() y = env.data.site_xpos[0][2] @@ -498,7 +498,7 @@ def test_inverted_double_pendulum_max_height(version: str): def test_inverted_double_pendulum_max_height_old(version: str): """Verify the max height of Inverted Double Pendulum (v4 does not have `reset_noise_scale` argument).""" env = gym.make(f"InvertedDoublePendulum-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) env.set_state(env.init_qpos, env.init_qvel) y = env.data.site_xpos[0][2] @@ -510,7 +510,7 @@ def test_inverted_double_pendulum_max_height_old(version: str): def test_model_object_count(version: str): """Verify that all the objects of the model are loaded, mostly useful for using non-mujoco simulator.""" env = gym.make(f"Ant-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 15 assert env.model.nv == 14 assert env.model.nu == 8 @@ -521,7 +521,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"HalfCheetah-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 9 assert env.model.nv == 9 assert env.model.nu == 6 @@ -532,7 +532,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"Hopper-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 6 assert env.model.nv == 6 assert env.model.nu == 3 @@ -543,7 +543,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"Humanoid-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 24 assert env.model.nv == 23 assert env.model.nu == 17 @@ -554,7 +554,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 2 env = gym.make(f"HumanoidStandup-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 24 assert env.model.nv == 23 assert env.model.nu == 17 @@ -565,7 +565,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 2 env = gym.make(f"InvertedDoublePendulum-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 3 assert env.model.nv == 3 assert env.model.nu == 1 @@ -576,7 +576,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"InvertedPendulum-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 2 assert env.model.nv == 2 assert env.model.nu == 1 @@ -588,7 +588,7 @@ def test_model_object_count(version: str): if not (version == "v4" and mujoco.__version__ >= "3.0.0"): env = gym.make(f"Pusher-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 11 assert env.model.nv == 11 assert env.model.nu == 7 @@ -604,7 +604,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"Reacher-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 4 assert env.model.nv == 4 assert env.model.nu == 2 @@ -616,7 +616,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"Swimmer-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 5 assert env.model.nv == 5 assert env.model.nu == 2 @@ -628,7 +628,7 @@ def test_model_object_count(version: str): assert env.model.ntendon == 0 env = gym.make(f"Walker2d-{version}").unwrapped - assert isinstance(env, (BaseMujocoEnv, BaseMujocoPyEnv)) + assert isinstance(env, (MujocoEnv, BaseMujocoPyEnv)) assert env.model.nq == 9 assert env.model.nv == 9 assert env.model.nu == 6 @@ -666,8 +666,8 @@ def test_dt(): env_b = gym.make( "Ant-v5", include_cfrc_ext_in_observation=False, frame_skip=1 ).unwrapped - assert isinstance(env_a, BaseMujocoEnv) - assert isinstance(env_b, BaseMujocoEnv) + assert isinstance(env_a, MujocoEnv) + assert isinstance(env_b, MujocoEnv) env_b.model.opt.timestep = 0.05 assert env_a.dt == env_b.dt @@ -702,7 +702,7 @@ def test_dt(): ) def test_reset_noise_scale(env_id): """Checks that when `reset_noise_scale=0` we have deterministic initialization.""" - env: BaseMujocoEnv = gym.make(env_id, reset_noise_scale=0).unwrapped + env = gym.make(env_id, reset_noise_scale=0).unwrapped env.reset() assert np.all(env.data.qpos == env.init_qpos) From 52c04fb4aae4cb70ff034cccc1e88b73d03b9c04 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 5 Jun 2024 08:10:16 +0000 Subject: [PATCH 07/26] Trigger Build From 0df947382e613972cfdc59f1478a8995dd4f495b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:25:03 +0300 Subject: [PATCH 08/26] Update test_mujoco_v5.py --- tests/envs/mujoco/test_mujoco_v5.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py index bf9c639e4a..e3ce08b119 100644 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -600,7 +600,10 @@ def test_model_object_count(version: str): else: assert env.model.nbvh == 18 assert env.model.njnt == 11 - assert env.model.ngeom == 21 + if version == "v4": + assert env.model.ngeom == 21 + else: + assert env.model.ngeom == 20 assert env.model.ntendon == 0 env = gym.make(f"Reacher-{version}").unwrapped From e8943b65de0db2d877170a660c1bb595e00da5fe Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 5 Jun 2024 09:21:29 +0000 Subject: [PATCH 09/26] `pre-commit` --- tests/envs/mujoco/test_mujoco_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py index e3ce08b119..c7277d10b7 100644 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -603,7 +603,7 @@ def test_model_object_count(version: str): if version == "v4": assert env.model.ngeom == 21 else: - assert env.model.ngeom == 20 + assert env.model.ngeom == 20 assert env.model.ntendon == 0 env = gym.make(f"Reacher-{version}").unwrapped From 63177ceba425bbecce43b063784025fb72fd4ff3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:15:52 +0300 Subject: [PATCH 10/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 7ded820638..823726c7b2 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -10,10 +10,10 @@ jobs: strategy: fail-fast: true matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] numpy-version: ['>=1.21,<2.0', '>=2.0'] exclude: - - python-version: '3.8' # numpy>=2.0 requires Python>=3.9 + - python-version: '3.9' # numpy>=2.0 requires Python>=3.9 numpy-version: '>=2.0' steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - run: | docker build -f bin/necessary-py.Dockerfile \ - --build-arg PYTHON_VERSION='3.10' \ + --build-arg PYTHON_VERSION='3.13' \ --tag gymnasium-necessary-docker . - name: Run tests run: | From 6115ccc3a096c4799e6fa598578a3537e51291e4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:17:13 +0300 Subject: [PATCH 11/26] Update pyproject.toml --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 61d5221ec5..e83807c340 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ authors = [{ name = "Farama Foundation", email = "contact@farama.org" }] license = { text = "MIT License" } keywords = ["Reinforcement Learning", "game", "RL", "AI", "gymnasium"] classifiers = [ - "Development Status :: 4 - Beta", # change to `5 - Production/Stable` when ready + "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.15", 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering :: Artificial Intelligence', ] From 7fc3ffe7f484db5269e7f7586fee0288c5111d39 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:18:54 +0300 Subject: [PATCH 12/26] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e83807c340..365c67a845 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.15", + "Programming Language :: Python :: 3.13", 'Intended Audience :: Science/Research', 'Topic :: Scientific/Engineering :: Artificial Intelligence', ] From aeb1aab1013404b54991c37f1dfc8a3007107f3e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:19:06 +0300 Subject: [PATCH 13/26] Update pyproject.toml --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 365c67a845..784f1bd216 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", From 5451d84cdcec4d9acdffb5df35376172d159f4c6 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 25 Sep 2024 16:21:33 +0300 Subject: [PATCH 14/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff2d9bb9b3..6c6cc9be7d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ To install the base Gymnasium library, use `pip install gymnasium` This does not include dependencies for all families of environments (there's a massive number, and some can be problematic to install on certain systems). You can install these dependencies for one family like `pip install "gymnasium[atari]"` or use `pip install "gymnasium[all]"` to install all dependencies. -We support and test for Python 3.8, 3.9, 3.10, 3.11 and 3.12 on Linux and macOS. We will accept PRs related to Windows, but do not officially support it. +We support and test for Linux and macOS. We will accept PRs related to Windows, but do not officially support it. ## API From ceb897a58bcd9aa8b034615120e9a5c54c913f77 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:38:59 +0300 Subject: [PATCH 15/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 823726c7b2..888eb1e7da 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - run: | docker build -f bin/necessary-py.Dockerfile \ - --build-arg PYTHON_VERSION='3.13' \ + --build-arg PYTHON_VERSION='3.12' \ --tag gymnasium-necessary-docker . - name: Run tests run: | From be728b1108cc7767cb61842764e599af9a9d2a4b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:37:42 +0300 Subject: [PATCH 16/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 888eb1e7da..823726c7b2 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - run: | docker build -f bin/necessary-py.Dockerfile \ - --build-arg PYTHON_VERSION='3.12' \ + --build-arg PYTHON_VERSION='3.13' \ --tag gymnasium-necessary-docker . - name: Run tests run: | From b3092cc44b4f3dba19d80646481c48bd140ada70 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:41:16 +0300 Subject: [PATCH 17/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 823726c7b2..4304166b32 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13-rc'] numpy-version: ['>=1.21,<2.0', '>=2.0'] exclude: - python-version: '3.9' # numpy>=2.0 requires Python>=3.9 @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - run: | docker build -f bin/necessary-py.Dockerfile \ - --build-arg PYTHON_VERSION='3.13' \ + --build-arg PYTHON_VERSION='3.12' \ --tag gymnasium-necessary-docker . - name: Run tests run: | From 0be7f34c393284d00d5d11969289c474d99778f2 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Tue, 8 Oct 2024 02:27:56 +0300 Subject: [PATCH 18/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 4304166b32..888eb1e7da 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13-rc'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] numpy-version: ['>=1.21,<2.0', '>=2.0'] exclude: - python-version: '3.9' # numpy>=2.0 requires Python>=3.9 From e6f4109d10327335b94b2c497f6d3a40a7c30962 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:39:11 +0300 Subject: [PATCH 19/26] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dd9fd7b146..d6ecb2e4ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" name = "gymnasium" description = "A standard API for reinforcement learning and a diverse set of reference environments (formerly Gym)." readme = "README.md" -requires-python = ">= 3.8" +requires-python = ">= 3.9" authors = [{ name = "Farama Foundation", email = "contact@farama.org" }] license = { text = "MIT License" } keywords = ["Reinforcement Learning", "game", "RL", "AI", "gymnasium"] From 299055616423bf29620ef5d4bccc925336f14925 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:39:59 +0300 Subject: [PATCH 20/26] Update .pre-commit-config.yaml --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 58954ef886..44fcf4bf94 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: rev: v3.17.0 hooks: - id: pyupgrade - args: ["--py38-plus"] + args: ["--py39-plus"] - repo: https://github.com/PyCQA/isort rev: 5.13.2 hooks: From dfa349b5ad317a8e6b1bc773eca16bad7ed1318d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:02:41 +0200 Subject: [PATCH 21/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56aabde589..92ad7e1fd2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

-Gymnasium is an open source Python library for developing and comparing reinforcement learning algorithms by providing a standard API to communicate between learning algorithms and environments, as well as a standard set of environments compliant with that API. This is a fork of OpenAI's [Gym](https://github.com/openai/gym) library by its maintainers (OpenAI handed over maintenance a few years ago to an outside team), and is where future maintenance will occur going forward. +Gymnasium is an open source Python library for developing and comparing reinforcement learning algorithms by providing a standard API to communicate between learning algorithms and environments, as well as a standard set of environments compliant with that API. This is a fork of OpenAI's [Gym](https://github.com/openai/gym) library by its maintainers (OpenAI handed over maintenance a few years ago to an outside team), and is where future maintenance will occur going forward. The documentation website is at [gymnasium.farama.org](https://gymnasium.farama.org), and we have a public discord server (which we also use to coordinate development work) that you can join here: https://discord.gg/bnJ6kubTg6 From 37240a9bc0984490ee7982b2a0a956de848412df Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Wed, 4 Dec 2024 00:02:50 +0200 Subject: [PATCH 22/26] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92ad7e1fd2..56aabde589 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@

-Gymnasium is an open source Python library for developing and comparing reinforcement learning algorithms by providing a standard API to communicate between learning algorithms and environments, as well as a standard set of environments compliant with that API. This is a fork of OpenAI's [Gym](https://github.com/openai/gym) library by its maintainers (OpenAI handed over maintenance a few years ago to an outside team), and is where future maintenance will occur going forward. +Gymnasium is an open source Python library for developing and comparing reinforcement learning algorithms by providing a standard API to communicate between learning algorithms and environments, as well as a standard set of environments compliant with that API. This is a fork of OpenAI's [Gym](https://github.com/openai/gym) library by its maintainers (OpenAI handed over maintenance a few years ago to an outside team), and is where future maintenance will occur going forward. The documentation website is at [gymnasium.farama.org](https://gymnasium.farama.org), and we have a public discord server (which we also use to coordinate development work) that you can join here: https://discord.gg/bnJ6kubTg6 From 4254d956b897d87a8686f4c33b9943bd79f3349e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:21:40 +0200 Subject: [PATCH 23/26] Update run-pytest.yml --- .github/workflows/run-pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 888eb1e7da..de93aba043 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -8,7 +8,7 @@ jobs: build-all: runs-on: ubuntu-latest strategy: - fail-fast: true + fail-fast: false matrix: python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] numpy-version: ['>=1.21,<2.0', '>=2.0'] From a657582a87121b8f0920f98c629176604b824c3b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:56:35 +0200 Subject: [PATCH 24/26] testing upgrading cython --- pyproject.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a8003e92c9..2ee945d93d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,8 +39,8 @@ atari = ["ale_py >=0.9"] box2d = ["box2d-py ==2.3.5", "pygame >=2.1.3", "swig ==4.*"] classic-control = ["pygame >=2.1.3"] classic_control = ["pygame >=2.1.3"] # kept for backward compatibility -mujoco-py = ["mujoco-py >=2.1,<2.2", "cython<3"] -mujoco_py = ["mujoco-py >=2.1,<2.2", "cython<3"] # kept for backward compatibility +mujoco-py = ["mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git"] +mujoco_py = ["mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git"] # kept for backward compatibility mujoco = ["mujoco >=2.1.5", "imageio >=2.14.1"] toy-text = ["pygame >=2.1.3"] toy_text = ["pygame >=2.1.3"] # kept for backward compatibility @@ -59,8 +59,7 @@ all = [ # classic-control "pygame >=2.1.3", # mujoco-py - "mujoco-py >=2.1,<2.2", - "cython <3", + "mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git" # mujoco "mujoco >=2.1.5", "imageio >=2.14.1", From 1d9e9a8cc48c2c7289e8973c4a749ac5da15bd5b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 19 Dec 2024 19:59:38 +0200 Subject: [PATCH 25/26] testing upgrading cython --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2ee945d93d..f5c35abb8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ all = [ # classic-control "pygame >=2.1.3", # mujoco-py - "mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git" + "mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git", # mujoco "mujoco >=2.1.5", "imageio >=2.14.1", From bf71195e2deb58cb063aef501b692fa034e4e96c Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Thu, 19 Dec 2024 20:10:25 +0200 Subject: [PATCH 26/26] Update pyproject.toml --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f5c35abb8b..bada529713 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,8 +39,8 @@ atari = ["ale_py >=0.9"] box2d = ["box2d-py ==2.3.5", "pygame >=2.1.3", "swig ==4.*"] classic-control = ["pygame >=2.1.3"] classic_control = ["pygame >=2.1.3"] # kept for backward compatibility -mujoco-py = ["mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git"] -mujoco_py = ["mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git"] # kept for backward compatibility +mujoco-py = ["mujoco-py >=2.1,<2.2", "cython<3"] +mujoco_py = ["mujoco-py >=2.1,<2.2", "cython<3"] # kept for backward compatibility mujoco = ["mujoco >=2.1.5", "imageio >=2.14.1"] toy-text = ["pygame >=2.1.3"] toy_text = ["pygame >=2.1.3"] # kept for backward compatibility @@ -59,7 +59,7 @@ all = [ # classic-control "pygame >=2.1.3", # mujoco-py - "mujoco-py@git+https://github.com/Kallinteris-Andreas/mujoco-py.git", + "mujoco-py >=2.1,<2.2", "cython<3", # mujoco "mujoco >=2.1.5", "imageio >=2.14.1",