From 32dc22db79ed7192f35fceeb05c41c10e8dd6e31 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Jan 2023 01:39:21 +0200 Subject: [PATCH 001/160] (temp) make mamujoco requirement mandatory --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f156d5a8..b03f3477 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,7 +46,7 @@ repos: rev: 6.1.1 hooks: - id: pydocstyle - exclude: ^(tests)|(docs)|(gymnasium_robotics/utils)|(gymnasium_robotics/envs/fetch)|(gymnasium_robotics/envs/shadow_dexterous_hand)|(gymnasium_robotics/envs/maze)|(gymnasium_robotics/envs/adroit_hand)|(gymnasium_robotics/envs/franka_kitchen) + exclude: ^(tests/envs/hand)|(tests/test_envs.py)|(tests/__init__.py)|(tests/utils.py)|(tests/envs/__init__.py)|(docs)|(gymnasium_robotics/utils)|(gymnasium_robotics/envs/fetch)|(gymnasium_robotics/envs/shadow_dexterous_hand)|(gymnasium_robotics/envs/maze)|(gymnasium_robotics/envs/adroit_hand)|(gymnasium_robotics/envs/franka_kitchen) args: - --convention=google - --add-ignore=D100 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 66cf108b..97b87e34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,8 @@ dependencies = [ "mujoco>=2.3.1.post1", "numpy>=1.21.0,<1.24.0", "gymnasium>=0.26", + "pettingzoo>=1.22.2", + "Jinja2>=3.0.3", ] dynamic = ["version"] From 510f1c46d08b6a07877e0734e90833c6857dc586 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Jan 2023 20:41:46 +0200 Subject: [PATCH 002/160] MaMuJoCo Doc update --- docs/envs/MaMuJoCo/index.md | 2 +- docs/envs/MaMuJoCo/ma_ant.md | 15 +++++----- docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md | 21 +++++++------ docs/envs/MaMuJoCo/ma_half_cheetah.md | 13 ++++---- .../MaMuJoCo/{ma_hooper.md => ma_hopper.md} | 30 +++++++++---------- docs/envs/MaMuJoCo/ma_humanoid.md | 15 +++++----- docs/envs/MaMuJoCo/ma_humanoid_standup.md | 15 +++++----- docs/envs/MaMuJoCo/ma_pusher.md | 3 +- docs/envs/MaMuJoCo/ma_reacher.md | 12 ++++---- docs/envs/MaMuJoCo/ma_single.md | 8 ++--- docs/envs/MaMuJoCo/ma_swimmer.md | 12 ++++---- docs/envs/MaMuJoCo/ma_walker2d.md | 12 ++++---- .../multiagent_mujoco/coupled_half_cheetah.py | 2 +- .../envs/multiagent_mujoco/obsk.py | 6 ++-- 14 files changed, 81 insertions(+), 85 deletions(-) rename docs/envs/MaMuJoCo/{ma_hooper.md => ma_hopper.md} (69%) diff --git a/docs/envs/MaMuJoCo/index.md b/docs/envs/MaMuJoCo/index.md index 59319a26..3fa59215 100644 --- a/docs/envs/MaMuJoCo/index.md +++ b/docs/envs/MaMuJoCo/index.md @@ -90,7 +90,7 @@ v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama ma_ant.md ma_coupled_half_cheetah.md ma_half_cheetah.md -ma_hooper.md +ma_hopper.md ma_humanoid_standup.md ma_humanoid.md ma_reacher.md diff --git a/docs/envs/MaMuJoCo/ma_ant.md b/docs/envs/MaMuJoCo/ma_ant.md index c2e784a6..3b47c794 100644 --- a/docs/envs/MaMuJoCo/ma_ant.md +++ b/docs/envs/MaMuJoCo/ma_ant.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (8,), float32)}` | | Part partition | `[(hip4, ankle4, hip1, ankle1, hip2, ankle2, hip3, ankle3)]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Ant](https://gymnasium.farama.org/environments/mujoco/ant/) +If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Ant](https://gymnasium.farama.org/environments/mujoco/ant/). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -40,7 +40,7 @@ If partitioning, is None then the environment contains a single agent with the s | 5 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | -### if partitioning == "2x4": # neighbouring legs together +### if partitioning == "2x4": # neighboring legs together (front and back) ```{figure} Ant_2x4.png :name: Ant_2x4 ``` @@ -141,31 +141,32 @@ right back leg ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the ant's torso. See more at the [Gymnasium's Ant](https://gymnasium.farama.org/environments/mujoco/ant/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Ant](https://gymnasium.farama.org/environments/mujoco/ant/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Ant](https://gymnasium.farama.org/environments/mujoco/ant/#starting-state). ## Episode End +All agent terminate and truncate at the same time given the same conditions as [Gymnasium's Ant](https://gymnasium.farama.org/environments/mujoco/ant/#episode-end). -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Ant](https://gymnasium.farama.org/environments/mujoco/ant/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Fixed diagonal factorization ("2x4d") not being diagonal. + - Fixed Global observations (The Ant's Torso: `rootx`, `rooty`, `rootz`) not being observed. + - Changed action ordering to be same as [Gymnasium/MuJoCo/Ant](https://gymnasium.farama.org/environments/mujoco/ant/#action-space) diff --git a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md index e571552c..03987e9f 100644 --- a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md @@ -22,14 +22,14 @@ The action spaces is depended on the partitioning. | Action Spaces | `{'agent_0' : Box(-1, 1, (12,), float32)}` | | Part partition | `(bfoot0, bshin0, bthigh0, ffoot0, fshin0, fthigh0, bfoot1, bshin1, bthigh1, ffoot1, fshin1, fthigh1,),` | -If partitioning, is None then the environment contains a single agent with same action space. +If partitioning, is `None`, then the environment contains a single agent with the same action space. | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the back thigh rotor of the first cheetah | -1 | 1 | bthigh0 | hinge | torque (N m) | | 1 | Torque applied on the back shin rotor of the first cheetah | -1 | 1 | bshin0 | hinge | torque (N m) | | 2 | Torque applied on the back foot rotor of the first cheetah | -1 | 1 | bfoot0 | hinge | torque (N m) | -| 3 | Torque applied on the front thigh rotorof the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | +| 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | | 4 | Torque applied on the front shin rotor of the first cheetah | -1 | 1 | fshin0 | hinge | torque (N m) | | 5 | Torque applied on the front foot rotor of the first cheetah | -1 | 1 | ffoot0 | hinge | torque (N m) | | 6 | Torque applied on the back thigh rotor of the second cheetah | -1 | 1 | bthigh1 | hinge | torque (N m) | @@ -58,7 +58,7 @@ first cheetah | 0 | Torque applied on the back thigh rotor of the first cheetah | -1 | 1 | bthigh0 | hinge | torque (N m) | | 1 | Torque applied on the back shin rotor of the first cheetah | -1 | 1 | bshin0 | hinge | torque (N m) | | 2 | Torque applied on the back foot rotor of the first cheetah | -1 | 1 | bfoot0 | hinge | torque (N m) | -| 3 | Torque applied on the front thigh rotorof the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | +| 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | | 4 | Torque applied on the front shin rotor of the first cheetah | -1 | 1 | fshin0 | hinge | torque (N m) | | 5 | Torque applied on the front foot rotor of the first cheetah | -1 | 1 | ffoot0 | hinge | torque (N m) | #### Agent 1 action space @@ -76,30 +76,33 @@ second cheetah ## Observation Space -Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items each cheetah's top. +Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items in each cheetah's top. ## Rewards - All agents receive the same average reward of each cheetah. ## Starting state - The starting state of the environment is the as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#starting-state) (but with 2 cheetahs). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Fixed action mapping of the second cheetah (It would previously not work) + - Fixed tendon Jacobean observations + - Added/Fixed Global observations (The Cheetahes's front tips: `rootx`s, `rooty`s, `rootz`s) not being observed. + - Improved node naming + - Changed action ordering to be same as [Gymnasium/MuJoCo/HalfCheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#action-space) diff --git a/docs/envs/MaMuJoCo/ma_half_cheetah.md b/docs/envs/MaMuJoCo/ma_half_cheetah.md index 7998b434..aadbc84c 100644 --- a/docs/envs/MaMuJoCo/ma_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_half_cheetah.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (6,), float32)}` | | Part partition | `[(bthigh, bshin, bfoot, fthigh, fshin, ffoot)]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Half_Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/) +If partitioning, is `None`, then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Half_Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -104,31 +104,30 @@ The environment is partitioned in 6 parts, each part corresponding to a single j ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the half cheetah's tip. See more at the [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Cheetah's front tip: `rootx`, `rooty`, `rootz`) not being observed. + - Changed action ordering to be same as [Gymnasium/MuJoCo/HalfCheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#action-space) diff --git a/docs/envs/MaMuJoCo/ma_hooper.md b/docs/envs/MaMuJoCo/ma_hopper.md similarity index 69% rename from docs/envs/MaMuJoCo/ma_hooper.md rename to docs/envs/MaMuJoCo/ma_hopper.md index 9244ee0b..4b26c07d 100644 --- a/docs/envs/MaMuJoCo/ma_hooper.md +++ b/docs/envs/MaMuJoCo/ma_hopper.md @@ -4,15 +4,15 @@ lastpage: --- -# Hooper +# Hopper

- Hooper + Hopper

This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Hooper](https://gymnasium.farama.org/environments/mujoco/hooper/) +The task is [Gymansium's MuJoCo/Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) @@ -21,14 +21,14 @@ The action spaces is depended on the partitioning ### if partitioning is None: -| Instantiate | `env = mamujoco_v0.parallel_env("Hooper", None)` | +| Instantiate | `env = mamujoco_v0.parallel_env("Hopper", None)` | |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0']` | | Number of Agents | 1 | | Action Spaces | `{'agent_0' : Box(-1, 1, (3,), float32)}` | | Part partition | `[(thigh_joint, leg_joint, foot_joint,)]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Half_Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/) +If partitioning, is `None`, then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Half_Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | @@ -41,7 +41,7 @@ If partitioning, is None then the environment contains a single agent with the s ### elif partitioning == "3x1": # each joint -| Instantiate | `env = mamujoco_v0.parallel_env("Hooper", "3x1")`| +| Instantiate | `env = mamujoco_v0.parallel_env("Hopper", "3x1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1', 'agent_2']` | | Number of Agents | 3 | @@ -64,31 +64,29 @@ The environment is partitioned in 3 parts, each part corresponding to a single j ## Observation Space - -Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the hooper's top. -See more at the [Gymnasium's Hooper](https://gymnasium.farama.org/environments/mujoco/hooper/#observation-space). +Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the hopper's top. +See more at the [Gymnasium's Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/#observation-space). ## Rewards - -All agents receive the same [Gymnasium's Hooper](https://gymnasium.farama.org/environments/mujoco/hooper/#observation-space) reward. +All agents receive the same [Gymnasium's Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/#observation-space) reward. ## Starting state - -The starting state of the environment is the as [Gymnasium's Hooper](https://gymnasium.farama.org/environments/mujoco/hooper/#starting-state). +The starting state of the environment is the as [Gymnasium's Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Hooper](https://gymnasium.farama.org/environments/mujoco/hooper/#episode-end). +All agent terminate and truncate at same time given the same conditions as [Gymnasium's Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Fixed Global observations (The Hopper's top: `rootx`, `rooty`, `rootz`) not being observed. diff --git a/docs/envs/MaMuJoCo/ma_humanoid.md b/docs/envs/MaMuJoCo/ma_humanoid.md index 74507ccb..8894a06c 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid.md +++ b/docs/envs/MaMuJoCo/ma_humanoid.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (17,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,),]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -92,31 +92,32 @@ Lower Body ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes all the items of the humanoid's torso. See more at the [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Humanoids's torso: `rootx`, `rooty`, `rootz`) not being observed. + - Fixed abdomen observations ordering. + - Added support for body observations (`cvel`, `cinert`, `cfrc_ext`) + - Changed action ordering to be same as [Gymnasium/MuJoCo/Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#action-space) diff --git a/docs/envs/MaMuJoCo/ma_humanoid_standup.md b/docs/envs/MaMuJoCo/ma_humanoid_standup.md index 341ead48..c65ca921 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid_standup.md +++ b/docs/envs/MaMuJoCo/ma_humanoid_standup.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (17,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,),]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -92,31 +92,32 @@ Lower Body ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes all the items of the Humanoid's torso. See more at the [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Humanoids's torso: `rootx`, `rooty`, `rootz`) not being observed. + - Fixed abdomen observations ordering. + - Added support for body observations (`cvel`, `cinert`, `cfrc_ext`) + - Changed action ordering to be same as [Gymnasium/MuJoCo/Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#action-space) diff --git a/docs/envs/MaMuJoCo/ma_pusher.md b/docs/envs/MaMuJoCo/ma_pusher.md index 0a39d319..449e06a6 100644 --- a/docs/envs/MaMuJoCo/ma_pusher.md +++ b/docs/envs/MaMuJoCo/ma_pusher.md @@ -98,8 +98,7 @@ All agent terminate and truncate at same time given the same conditions as [Gymn ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), first implemented here. - +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), first implemented here. ```{toctree} diff --git a/docs/envs/MaMuJoCo/ma_reacher.md b/docs/envs/MaMuJoCo/ma_reacher.md index 993a0bd5..c40551d9 100644 --- a/docs/envs/MaMuJoCo/ma_reacher.md +++ b/docs/envs/MaMuJoCo/ma_reacher.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (2,), float32)}` | | Part partition | `[(joint0,), (joint1,),]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#action-space) +If partitioning, is `None`, then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|---------------------------------------------------------------------------------|-------------|-------------|--------------------------|-------|------| @@ -58,31 +58,29 @@ The environment is partitioned in 2 parts, one part corresponding to the first j ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position of the reacher's target object. See more at the [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Targets's coordinates: `targetx`, `targety`) not being observed. diff --git a/docs/envs/MaMuJoCo/ma_single.md b/docs/envs/MaMuJoCo/ma_single.md index e874b81e..119f6c1c 100644 --- a/docs/envs/MaMuJoCo/ma_single.md +++ b/docs/envs/MaMuJoCo/ma_single.md @@ -11,7 +11,7 @@ And can be instantiated (without a partition) `env = mamujoco_v0.parallel_env("InvertedDoublePendulum", None)` `env = mamujoco_v0.parallel_env("InvertedPendulum", None)` -In which case the simply the same environments with a single agents using the `PettingZoo` APIs. +In which case, they simply are the same environments with a single agent using the `PettingZoo` APIs. ## Action Space @@ -33,13 +33,13 @@ The starting state of the environment is the same as single agent Gymnasium envi ## Episode End - -The agent terminates and truncates at same time given the same conditions as the single agent Gymnasium environment. +The agent terminates and truncates at the same time, given the same conditions as the single agent Gymnasium environment. ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco). +No Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)). diff --git a/docs/envs/MaMuJoCo/ma_swimmer.md b/docs/envs/MaMuJoCo/ma_swimmer.md index 438f36f3..7bd894ae 100644 --- a/docs/envs/MaMuJoCo/ma_swimmer.md +++ b/docs/envs/MaMuJoCo/ma_swimmer.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (2,), float32)}` | | Part partition | `(joint0, joint1,)` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| @@ -56,31 +56,29 @@ The environment is partitioned in 2 parts, one part corresponding to the first j | 0 | Torque applied on the second rotor | -1 | 1 | motor2_rot | hinge | torque (N m) | ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the swimmer's tip. See more at the [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#episode-end). +All agent terminate and truncate at the same time, given the same conditions as [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Swimmer's front tip: `free_body_rot`) not being observed. diff --git a/docs/envs/MaMuJoCo/ma_walker2d.md b/docs/envs/MaMuJoCo/ma_walker2d.md index 51c23334..4923954c 100644 --- a/docs/envs/MaMuJoCo/ma_walker2d.md +++ b/docs/envs/MaMuJoCo/ma_walker2d.md @@ -28,7 +28,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (6,), float32)}` | | Part partition | `(foot_joint, leg_joint, thigh_joint, foot_left_joint, leg_left_joint, thigh_left_joint,),` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Walker2d](https://gymnasium.farama.org/environments/mujoco/walker2d/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Walker2d](https://gymnasium.farama.org/environments/mujoco/walker2d/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| @@ -70,32 +70,30 @@ left leg ## Observation Space - Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the walker's top. See more at the [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#starting-state). ## Episode End - -All agent terminate and truncate at same time given the same conditions as [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#episode-end). +All agent terminate and truncate at the same time given the same conditions as [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#episode-end). ## Version History -v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of [the original multiagent_mujuco](https://github.com/schroederdewitt/multiagent_mujoco) +- v0: Initial version release, uses [Gymnasium.MuJoCo-v4](https://gymnasium.farama.org/environments/mujoco/), and is a fork of the original MaMuJoCo [schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco). +Changes from the original `MaMuJoCo` ([schroederdewitt/multiagent_mujoco](https://github.com/schroederdewitt/multiagent_mujoco)): + - Added/Fixed Global observations (The Walker's top: `rootx`, `rooty`, `rootz`) not being observed. diff --git a/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py b/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py index a71e4002..07b27c25 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py @@ -38,7 +38,7 @@ class CoupledHalfCheetahEnv(mujoco_env.MujocoEnv, EzPickle): | 0 | Torque applied on the back thigh rotor of the first cheetah | -1 | 1 | bthigh0 | hinge | torque (N m) | | 1 | Torque applied on the back shin rotor of the first cheetah | -1 | 1 | bshin0 | hinge | torque (N m) | | 2 | Torque applied on the back foot rotor of the first cheetah | -1 | 1 | bfoot0 | hinge | torque (N m) | - | 3 | Torque applied on the front thigh rotorof the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | + | 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | | 4 | Torque applied on the front shin rotor of the first cheetah | -1 | 1 | fshin0 | hinge | torque (N m) | | 5 | Torque applied on the front foot rotor of the first cheetah | -1 | 1 | ffoot0 | hinge | torque (N m) | | 6 | Torque applied on the back thigh rotor of the second cheetah | -1 | 1 | bthigh1 | hinge | torque (N m) | diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index e0cf4dae..35389e4c 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -9,13 +9,13 @@ - General code cleanup, factorization, type hinting, adding documentation and comments - `build_obs`: fixed global observations, fixed body observations (cvel, cinert, cfrc_ext), how uses mujoco.data, instead of gym.env - `HalfCheetah`: fix action ordering - - `Ant`: Fix global observation, fix "2x4d" factorization how having diogonal observations + - `Ant`: Fix global observation, fix "2x4d" factorization how having diagonal observations - `Humanoid`s: Added Body support, fixed abdomen observations, added/fixed missing global torso observations, fixed action ordering - `Reacher`: Fixxed body mapping - `Pusher`: Added support for `Pusher` - `Swimmer`: Added Front tip to global observations - `Walker2D`: Added missing Global Observations - - `CoupledHalfCheetah`: improved node naming, fixxed tendon facobian observations, fixed action mapping of the second cheetah, added missing global observations + - `CoupledHalfCheetah`: improved node naming, fixed tendon Jacobian observations, fixed action mapping of the second cheetah, added missing global observationsm, fixed action ordering - `ManySegmentAnt`: Fixed Global Observations - added new functions: `_observation_structure` @@ -381,7 +381,7 @@ def get_parts_and_edges( # noqa: C901 if partitioning is None: parts = [(hip4, ankle4, hip1, ankle1, hip2, ankle2, hip3, ankle3)] - elif partitioning == "2x4": # neighbouring legs together (front and back) + elif partitioning == "2x4": # neighboring legs together (front and back) parts = [(hip1, ankle1, hip2, ankle2), (hip3, ankle3, hip4, ankle4)] elif partitioning == "2x4d": # diagonal legs together parts = [(hip1, ankle1, hip4, ankle4), (hip2, ankle2, hip3, ankle3)] From 1a845a0ff2c8a1a35cc5202086dc6d1a194de3d4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Jan 2023 00:27:34 +0200 Subject: [PATCH 003/160] add ant2x4 image --- docs/envs/MaMuJoCo/Ant_2x4.png | Bin 0 -> 31399 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/envs/MaMuJoCo/Ant_2x4.png diff --git a/docs/envs/MaMuJoCo/Ant_2x4.png b/docs/envs/MaMuJoCo/Ant_2x4.png new file mode 100644 index 0000000000000000000000000000000000000000..be51c6af5b5c50bc3fe234cc30d51469f35e9d1f GIT binary patch literal 31399 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 Date: Wed, 11 Jan 2023 22:25:14 +0200 Subject: [PATCH 004/160] `MaMuJoCo` DOC Update (adding action space PICs) --- docs/envs/MaMuJoCo/figures/ant_2x4.png | Bin 0 -> 31399 bytes docs/envs/MaMuJoCo/figures/ant_2x4d.png | Bin 0 -> 29862 bytes docs/envs/MaMuJoCo/figures/ant_4x2.png | Bin 0 -> 30836 bytes .../figures/coupled_half_cheetah_1p1.png | Bin 0 -> 47477 bytes docs/envs/MaMuJoCo/figures/credits.md | 2 ++ .../envs/MaMuJoCo/figures/half_cheetah_2x3.png | Bin 0 -> 28132 bytes .../envs/MaMuJoCo/figures/half_cheetah_6x1.png | Bin 0 -> 29245 bytes docs/envs/MaMuJoCo/figures/hopper_3x1.png | Bin 0 -> 20156 bytes docs/envs/MaMuJoCo/figures/humanoid_9|8.png | Bin 0 -> 42163 bytes docs/envs/MaMuJoCo/figures/mamujoco.png | Bin 0 -> 306724 bytes .../envs/MaMuJoCo/figures/many_segment_ant.png | Bin 0 -> 26048 bytes .../MaMuJoCo/figures/many_segment_swimmer.png | Bin 0 -> 18654 bytes docs/envs/MaMuJoCo/figures/pusher_3p.png | Bin 0 -> 18761 bytes docs/envs/MaMuJoCo/figures/reacher_2x1.png | Bin 0 -> 16538 bytes docs/envs/MaMuJoCo/figures/swimmer_2x1.png | Bin 0 -> 17790 bytes docs/envs/MaMuJoCo/figures/walker2d_2x3.png | Bin 0 -> 26200 bytes docs/envs/MaMuJoCo/index.md | 9 +-------- docs/envs/MaMuJoCo/ma_ant.md | 10 ++++++++-- docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md | 4 ++++ docs/envs/MaMuJoCo/ma_half_cheetah.md | 6 ++++++ docs/envs/MaMuJoCo/ma_hopper.md | 3 +++ docs/envs/MaMuJoCo/ma_humanoid.md | 4 ++++ docs/envs/MaMuJoCo/ma_humanoid_standup.md | 4 ++++ docs/envs/MaMuJoCo/ma_pusher.md | 9 +++++---- docs/envs/MaMuJoCo/ma_reacher.md | 4 ++++ docs/envs/MaMuJoCo/ma_swimmer.md | 4 ++++ docs/envs/MaMuJoCo/ma_walker2d.md | 4 ++++ 27 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 docs/envs/MaMuJoCo/figures/ant_2x4.png create mode 100644 docs/envs/MaMuJoCo/figures/ant_2x4d.png create mode 100644 docs/envs/MaMuJoCo/figures/ant_4x2.png create mode 100644 docs/envs/MaMuJoCo/figures/coupled_half_cheetah_1p1.png create mode 100644 docs/envs/MaMuJoCo/figures/credits.md create mode 100644 docs/envs/MaMuJoCo/figures/half_cheetah_2x3.png create mode 100644 docs/envs/MaMuJoCo/figures/half_cheetah_6x1.png create mode 100644 docs/envs/MaMuJoCo/figures/hopper_3x1.png create mode 100644 docs/envs/MaMuJoCo/figures/humanoid_9|8.png create mode 100644 docs/envs/MaMuJoCo/figures/mamujoco.png create mode 100644 docs/envs/MaMuJoCo/figures/many_segment_ant.png create mode 100644 docs/envs/MaMuJoCo/figures/many_segment_swimmer.png create mode 100644 docs/envs/MaMuJoCo/figures/pusher_3p.png create mode 100644 docs/envs/MaMuJoCo/figures/reacher_2x1.png create mode 100644 docs/envs/MaMuJoCo/figures/swimmer_2x1.png create mode 100644 docs/envs/MaMuJoCo/figures/walker2d_2x3.png diff --git a/docs/envs/MaMuJoCo/figures/ant_2x4.png b/docs/envs/MaMuJoCo/figures/ant_2x4.png new file mode 100644 index 0000000000000000000000000000000000000000..333ea71cf2c4595ac5e43f5fcd89124144bd57f5 GIT binary patch literal 31399 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/ant_2x4d.png b/docs/envs/MaMuJoCo/figures/ant_2x4d.png new file mode 100644 index 0000000000000000000000000000000000000000..40902c4dc9ee4ea20e7bb74134f15fa701a529f4 GIT binary patch literal 29862 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 DWX+g= literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/ant_4x2.png b/docs/envs/MaMuJoCo/figures/ant_4x2.png new file mode 100644 index 0000000000000000000000000000000000000000..ccda5a1a1f87aae4b2c6702f3f03f8ab5df6bb03 GIT binary patch literal 30836 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�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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/credits.md b/docs/envs/MaMuJoCo/figures/credits.md new file mode 100644 index 00000000..6529e36d --- /dev/null +++ b/docs/envs/MaMuJoCo/figures/credits.md @@ -0,0 +1,2 @@ +The figures were first made my Christian Schroeder de Witt (under no license), and he provided permission to use them on this repo +Then modified by @Kallinteris-Andreas diff --git a/docs/envs/MaMuJoCo/figures/half_cheetah_2x3.png b/docs/envs/MaMuJoCo/figures/half_cheetah_2x3.png new file mode 100644 index 0000000000000000000000000000000000000000..49262213155e72a2d334ae9d9f005d25e8d411cb GIT binary patch literal 28132 zcmYg&Wmr_*_x6z%kP-pumhSHE?r!OnZX_RsA*4%0x*G%u1qJEumImqWcl-N)d5>Nr z*MP%0XZBv}t~JqWDzYz72~i;kdLb_-r2#?DyusIHC~)9$&NKZl_=nt5PD2@j{AeL4 zBpiZn!BZhS5ah`QL3?HpB#;h4_^#Ql>O$ZfNEV8+Qqa@CKcCx+lfW~`9`ef4$lK4* zaPbi(IkY~3r?4!|RlFbw{yPN05rf}|_3yd<*050sax9UT64&yXKgjVm!j&cJ-S#^| zmOW~JPHC+5ch(YL(ZZ$H;Y;mL9PU~g_Dko#J z9CwgC@p;+U*c=Y842hiKBA|DFkA1DFsh8i8!f=Qu=2qX6;m7p!ak<@pVSfH_rE_Hx z-i|mzVysv-_Zd9WU>0vRqH`42MZi${Ggh>X32(ek292Rb<9BPNzbCtI(uDlZcW%zM zi%6xDM(rChNriq74LM~j`QQh?P^m%G@=t$k8pNopNF7{`jkni8cc(R=MhPTf)-?ilA z-|<~3%^)g9bEJFCZ%=D_E_Kh5mm;P60? zj#O{njYOHq7yKCTpr4|^ZP|m4i(2RsB9o8LVpK9^e_>-Yn7+2UT4OUpDnwfCB1W4O zPbTWTBScT%M{ZAmZar0|4TbDZ6gLkFS~d-&v%d%^_3Ek8Fnv>3%v?H5kBc+LoX}Ua z*0EraZJ5V#A(s`k-W#u)#luWXOUuiv%aWEWlhcsm(1YHLNJv}~bzPkah6d|z&epB$%!xdgmz(9ZdYfd)wZi^PL zpOK-y$P9ZG7JK)G52{Xo%ND=brXz`*!Q+n_K8UKvb>|KWc^XsAI_7JhOMqBA{Yg-E(JxTr@@7v{~h0F zX0E`AV?M?&JHLB+41~V7`(MGq!NoFjQ^~}#wRHqPi6DrpsHkAZ*3{Q)(&0u&M@L%c z2zYX|d3kssKs&mUV3~hkb8Yv#;K(Ks@Sww)d-rrTG;PYs{6Pk7WKK7-`*T;j9gL?6 zuVf{;f9ujR!*B8_<--S5==-l2d{&b-y=^UR?N`v}x;hR@%C76Lg@uJbQ1d$jz}xTV z-|z3D?%BA;opP(v_+n6ORZ>QlE!F&LcNo3?g)Y zHX;hny~MrcL}j9vJW&oc;>)l&OlHDjf>)G1!;XTSfJxBgN6Jns5*s$`KrO~gw1KITzPC*nm9}XV)i|-40}AWRRG- zdEUt;xWes`Tzu3B*s?G=xfmTT;oa)y_V)gZZssukw}TB>ou4WhN;IxetSAy;wy{!q z?T^`afu=FT9nAaFCv%@QO2LwD4SKlU97wy6B}d8Rv>8?veW1odfuMSuk<@|Bi6Ui9 zh7>4dd|bnF<>_$g8z^Va^9`k+KZ^y2_pZapL?5?vgK2T1VapE-E}hc(W1^4O`9W?7 z-NzqZF&ezPwN7E5z;SHX$jYqVAsLj(Crl)0G4OuicQMkn&Ry+!!D~ubZQ3al5E3M5 zvp-!~RaFH}X>XzV=x6jxczF1cTv5@$+jCN3KL~2|+;6h@_2K0k6+M0Zo4dPjEiL-` z`p=+q9s^KzA>iHibsGUIQYqPe>z7rxFhiH z61>9S(Y)@7V%024)uD(v7R#-A_hXhLlgxtPbi^WdQf-Dkz+0xV7>|#SdmYSQ{GH<@h(vxt$mO{=d3ms~KN1-gHL&@v{YECfynNq# zCD6vwGXKea?29xg2P>Vy!Q$fLQc^$b-VW$gn~;!_E^{2A!}0W5W!+?F`%?1Vhlr97 zhW;nXy{J(hRwcYG?fcO>I8a*KF(>q^3{Pakfg z`vz?`Zqdfxe{1WY_$xyrgZ!ge!{=Gtb+wuW1Wip%iQXS~H#sekgI2wK1Dd~VT=ed_*$X^qKq50bKX0IALxqa#lyZ z!z9mQQ*?ZM@a5kOCEBx-lijKEwAwV|@}3F~>&)C(oXxJV!X>!5>A@b7JyIJKGlE<9 zkHQ#d+KNgunS6T7Yn-7q8cJnvax3z0)yXpusCMZf<2APpOO&`oh&X!XH- zm0L7+U*k-!`!;4x?72OfeyIquVk(oos;bMfh|6-D-JabuGk`0eGFMoq+UT_}P%OR> zq6vSb_ni6UR(x)8h2luXa+Vtg-daNg10KsLLf97AAsVH-?)6#PJ%gR*Bh1bcsTI0x zL@@ge#sW1--6RKD8BU~#F=b(U&5sEQY(B@U>FMdEn#CWazEo6H0PrJ;RiH*^J)GUQ zegbyRPm5Y5+AMx|YbB+@@aHejqkEsYX=rFrBPc~gmM1lN!R=AqmJ)ipb$2_$_$$LW zSk`!aVdY$XImvEJsJc5HIcDFOFB|`OF=Oho(!rcIfJw}^y1A*XrPTm-Mg$suogNcd zpWDMZdwY9M3r)3p6?a!h$Os6;!hYwSiw!k3SYbbmzIhEz*7M#Cq~A!f(8R>`qTNR6 zW7WX!rR+RMVfbyXs()gaO)dKgEJtlUyu7>u0`7AJy%iJ{jg5>T$p7;1b2PO2`ucC) zN1yTvzkR#Q=6ClHFm`lw?2RLN8c;TE_bVK;-FWA#qm!DJmPTLf(&e9J=eZb2GZ&cO z} z3=a<{CMGJ;a#B-=k8J0P27i*HR!L_QFV(u-8KdIno*Ey=iSJd-<~so$aH%8kn@s*3 zsMejNL`Y%$e&@DEMvEuwy`n;bzNZ_4-iJ!yzq;J!P3*e4xe0px)$0iG)z(hQ$uU0b zPzT)-9)bd*bt)s|eW)YUM)K9XTHf^~*0CJurJnb6!{Km52*_Q{Aowd~sRX>ii3R{u zqEJJ(LT}7=3L72C{qpae0P+PQ`mxVFSnUuL8ATPi`FBLw$LIR-;SLQA?Sf`=YdD7p z8+)w9ef#(C-`1ToZ%Sef%KymQn-fg$Oj-%0NZ-sW`&AO6z^S^wm7g1*43Ji@{2(J? zsfJQWs8L3NY1r0DMbVS{$1)xn!ksDECZmf#HJ{~hgL!3TGvX<5``ZYtvOD>{roxHV zsnEkFB%Ci+CDV-GTyZ^I24|}OWr~&17ggl>#JhVn%(9ooCp9565#x0RzdDXcH6k_$5f^N_4z=C{#EFGn0_4X}^NPu2v*x|-TgJo|VA{rh96)9(>CMSnq zqenKxZ#`%`XxrdmkOkHxshH0jEU1^_5=O*v`3+C+dI(XZF_th_Gr_Z2SoKCrQuSJ@ zZRtI-X4q`UqMVhDO|dQ|an%X;s?8ruuMEF`EAIMa z{dMeCb+hgh{vSJ)`}trzgL)@{2v3S6`imyEC&I6^+?$Zf;_di5NB5j&((#kq{Fr z2_Hw6R91q*6Dmf2O^gFCqV<{}(tG*(!l1w9_&bU~&hk$aJr5nNsA2B5J>|4ik^Z)a zZr=UlbXLEy>&Cq4E;Ni}_h0ial+2=i7NKI0sO(3?SAmQvWQX$Z&6T}_`WpF4gE)`e zeaoMgE;W0y1w70AuMUyit5u2@zIn4oNc0U1#Kgp$U0v}uc(}R}fXxSZ!*rFgpv^FG zd*a1_6hjIg9v+Kn$35r8(=t9rXr8AH9?jPB5#e0rE{^5*kH2=YNwNl%bB}dJ^I}~- zG!G~V+j<_iJ#2EkTI8MlV~LYLCC>2Tyq1&?-RMaz<|mW7yt1)sfy@vjGkgb5*HbJIC+mgli5QB5K>-9@%RX;fp=7xQjxI{gNrnK90e6?HC6*>1*LaVtJ8iq~&y_+8s;@ z*Mf=g#&kXUBuqQAVrbj@h85tRI zW#H$Z>+5?SxfT)vLEocazB%uu!I8JNE&|;v@2!~`If8g((D`6C|HNdH0|mOMf_A5= z!4%CG##?yxk}d6aIsf52{rH$quMWo|#I2t}T7+-?u1P}PP{HWy)UIYj<$C9 z*RHs@Pev3O@q_B8C*AM;O0n&)7RyMmto7fBN-BKfD>iDfWh+j?M1<66Mhoyt3KVww zIn`lm3%uc#YP+PJ*v3j_2UDNk64z}UNwJh@ZD{vb63mwOHFDnR{k+x`@zkos2U3cY zlhd_ku16VoTW&@3^YcL$c=__B&B&)E(Cr2Xl{Dj<-M9#q;aLWTei^3BxjQ5^*Rw`w zT@jtp^0t4i@Yzj(VVkf&*S~rUsxkh8^$c39Xv=T=#!S@Y`!4<_i7$xy| z*~P%W6T_R-bpQ3aa)>6fW@m;eeHN?s9bdxhjVrw3K7PtPr9hv!m)KE4q$8qFS_N9W zdbqJjp%FNh67a%G_~Cw|e#rGEujE*$r!i1$h)5H$P_Xvo1Jh|C6t*U`@?eyqNliy} zDayp!_PVAfJm@oK4Nr~pQY%1gV5bNJst6<)-irhxa{vRl2#027W`>4_mX?-=hY#KV z!ytsz`k4dHw{(tJ0{62ONBn5g7#=^l=PRi31X$W|A+4Joe1<24!jSoIkPjc$9LO4M zM_XU4knml)&P4b6=SvDE{dW{v!A77IqPv=^C`y*?x#|A(Hr+10%l=|t;@9utqX?<( z;FYsA8fK|8wOAXjh|WCTFtg{n3nf<>gP$CsCJF)*L-@AqZw$WBk(PQ;XamUmsldanG{n+{A2 zl*ZZyZs*nYwKU`Oi-YOq&Yw=C)sL3J(P0ZjJR#x?Jn_ z_VvQ43VuAP|EN^zpGmbWJVa5h)UxNH?GYHqsmJvtOmHd=)|t_DfN#IHFZV~0E1ol) z{b1SdLzJ*QxptOc5N(JYb^qo&h6^VX_4bb6Rvhby7+FJAB_*!j-qlrAJQqIV`B@Ww zFC@sZ=x`Z$crLSCI_#Eh9ffV7?~Tcv6z}tTWH(Xg_6O7MtWA>XeksO}{GFx99IBC1 z{oNk85v@>l$WGZq zsBuc&DA9;WLbRix+?S@uQKL_zflA8Hmc7h-yAr^=WmwlznNF@NI zR4s;^QTYLQDnEEA^vmtyw}zFk!f?-wDxT9A6(W-CJ4-6{<3(Emt=*}!$zH*VW3Fa1 zRw55jJ-yk5+0}%E1jA-$b-Lt+1}>mWQAkGa!~^02Sm81hvbt*a06Ho_y?c(v&EJEM ziUz9?4?|hj@up91%Kzy2%-+tj9b7HV{oUKi<6YFjKU)kVzIefOpnqbM1YLq4s?yXS3x>ihc8EREr}zwj1k2is4z9n0cv` z$AXIUI2}hsoJ0VtCc17#UeXu?ceoVgIjJ{S6jG;ZBt|=p04K z

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 Kz*^E~FaHlQX8$4p literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/half_cheetah_6x1.png b/docs/envs/MaMuJoCo/figures/half_cheetah_6x1.png new file mode 100644 index 0000000000000000000000000000000000000000..ae9016cca25fa695daab70f7e348d64fe426e130 GIT binary patch literal 29245 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 z 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 KE@sOb-TyzCU^Auw literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/hopper_3x1.png b/docs/envs/MaMuJoCo/figures/hopper_3x1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8f022cbdb871dee5afa3965568489d075d8322 GIT binary patch literal 20156 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 MSX>gXmy@md69og6umAu6 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/humanoid_9|8.png b/docs/envs/MaMuJoCo/figures/humanoid_9|8.png new file mode 100644 index 0000000000000000000000000000000000000000..eb35405f7ec6751db7189b6bb59b685ba3f39973 GIT binary patch literal 42163 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/mamujoco.png b/docs/envs/MaMuJoCo/figures/mamujoco.png new file mode 100644 index 0000000000000000000000000000000000000000..91abbc162fc455974bcc005367308fe463b3aa80 GIT binary patch literal 306724 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�t3d2X&WJbbAYG<4O2xhir?J|O#g68ad{%lx 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‡ThTH!}sgUMtR2qgZm5aB-`dnBf*NSQLu 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

TTxR-w{_&QUOvM) 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

(0SFVc?xdQwcZXImABw%C^F%Hfa6Boa}Tmk3T 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/pusher_3p.png b/docs/envs/MaMuJoCo/figures/pusher_3p.png new file mode 100644 index 0000000000000000000000000000000000000000..4c2cb3fb75c44753084f23643c137e06eba6cd42 GIT binary patch literal 18761 zcmZ{M1yogQ*YyDr5orktr8`AHKpLb(8l<~hkWNtqL_k15N@qBoH-DPKL!W{|5pTp$RV*tNdSIZ;S-^7ahssQOE|`c@-7I(ttJElnGDXz1FsERXyn1!r7>kB zL{;6Ux06goRIJIHl5dkfe(-6SyGHc6D2ABG?Gl2bbv327I+s+vd~02T3q4ZOIwmSS z7hKsOKdzC-2FCdhSOzZ^PY3P>WupaMc=LM>sH`CS7P9g3x*h!PzE3B&{&w|q{bil9$Nfj)uKP>vQsIw|U8HJI-ZfXb?Hjqc zxD0a9M`JQ7^Gh<3D%VJ;sHnVr*}t~K^YrOcF0MDqiE?yLd3anRQyUt-$!EE`<^A~a zqoJW;ja(D{dV`3US6zL*;9HTQv9YnyQ4CDX=(>csI6kja2QsXz)Ko)bkaHMyV% zK0ZDQ0u2l6!){D;^y0pouI_9I5xWpv(Lf?!X=y3K@7*2j7UBiktF5WB-T6k}-8sQm zuU_G!6OfYLn@lQ2`Rv@)J1_p*i+AskD0S?M-6qJq0s_j2F3mEt;9bXR9cydrCr_T7uE8fGAts9SiOjzWdGjMuYHCRQQ!X$5 z-a>p&OKW$HWz(IGDy2+PkvU`0)YOCzzPh?{baY&5gAclkg6w96fB*gyqc*e`FJA2H zp3W1|(a}lL;IV2HizNCt8)UvrP*=Sp>SZXvq5ySsaPLw-JffPoNUz;Ai^S(*j~nX5<`L;g= zLd3veAqbax`d4X1g(Ou38p`({Kh!vrI+uTRyYDTCQbkPCDG7{9Uu{+*g-W)HQK1HJ z4O@n(@}zqay5UgZpgFA#5YR_<|Ni&S3oZnfkM{R*=~q`* zM>gJYbN^tku(Pr{-D78}xTwD@IdY3ZAn*M^69k9kRDB}90G!=nH1?vC^0O+f(x z&L>Zh5Ql4n>`Y8G4vWZMA^m38#}oQXiLjqUQWY;wwwDjj9AurG4!%2A_ck*Xwx@fGhpYYf z(F4{7li}L<-4AFUgoh|&ZP?Dv zZf{31(9Oexj+PdanDh5gs<^1AC@n25+)J&?Hi;KeNV|~tITsffeyHTc_;{h#k3av; z4iSjcFSiglczAfYxNn;ccxykuv|CCS@-o~LPPtzBB*H-e*XOFxW+`|(@72|s zs;c6tE%*S#lF9P&a#;5lv26WcAE#B;j$=xjiubq<4-MfE5~?dIB1Aujg~8T$>g(_K ztW}wP2O%WA@cA%0uv5}4#d8|D3nJU9Ly_E8~Z0CPI zwZ!|v1wCM7rv8-jKmVUvP!KBVT1KGw{P}ZxVL}AFVT2D>6?Th8o!euZvi(0}%%}dH z%iFWHL@sE^*Z=;``^K{CKO!WA1lW-EoIk0-xHD|wV!K#cW13H z?$Gu!I5?ORFIY`Y&4P=cj?V04YidawVjMz%8ZUTfX9xZ+8K;S~iwjR|ud}oBkFv67 z&VRmq`qcN4fVoXxrh$AT@edK4ACmo>v0Sx{jSWIV!k?Khtt~Arqt+H%17&4pu^$PX zUhFnfqWRZ9XVok<>5XB9$Ktv>N3bT~wzu$^QXDdBkzT#W#z=vvBwreDJ?BxLdiu%Me{>L9AOBpy$7^b8N=izg3bV4Zo}Zu3{>${}*e?7cgq$RgefKV(+n#~5GxxR%T*#D=FKkT6 z(Fy%mM*SG5Y5Dn|+Ks$@>7Q-g?bG+YrH7-Ox*S%GF0h zeTR#OXJlswiNs5Wy*!D6on3({0;+4nR}~qVUD0Qsl#!89=6Ek=sH3AJCgvX%6_uQv z3;}Kbx8#ZBrw2;P%Eg>E>Ps5e#JVYven_T)Z^ zI0ODSKBtbZE;N6N$=w2oi14 zL>TF1RKPtVz+tTu7F^RE9g-|isHq=6j*y^&kPm$%=rLJr7%BP$0D%9yce}H-PYnQk zHm&w2%yc=13tvAps!EUHxQX-#PdsFJ@NO{y`}O%c(hlqLxoh%TzyVX+0? zv$eGaxrCX4Av-ts#KpG}1EMVT4kO1%S>qMAV+JFD~Gd**8$q9KI3WTqr;Uc!}bAG!cUtY4X-;BI|JZgLOMJRAgU?;4u_Vo68dU(JppGaX)D^IOoclGu<@shzdKc&w{jQsk=@<5YRzS{$g%z7TXL1RnHjosbrb{pikex1wDB_%uB+g~Q}kIl@~dfw`mHBGyI*SWm$ zrbdHjbZiU>0Uw2hh2?;b{C4m7814fT17mB^E$ZRHTC%UpH$zBVZUS>XJw2m0hw7_W zf2GJ&Zazeu2Cv(sR3l)fqa(ApcyBE46?}{aiwmFvgr9gI)PbRWju^E_}pW zT3S37`t&?bCWP;zqvJ6@WMpEB|N0dL6}3f}7^CBxpr-@xueRX(&w~B$A}lN|r-}{5 zN$1fT2hKjI@|+8A54HnxhO4l$wuU`OSzSN#0TwkUJG(FGS!HE%itFc{#S>o#-b6s| zCJuJ?_Vy9dCPqe%TazV_4}Y*z;iKEn)lE%K@;a^d&F%OUQ9?$$+huZavOOa%Cf1fv z-QVB8HjpGv6#-QTVpd*WUQ$vL1^KOHTD|Y}6~GiLD=SE_o8RxMHcG(8LpL-u3=Ith z07aCSb1$If;_q&w+rGa9)$3}VNzNE)8WIwcn(AsyGT!3K+F!lWrpvEHDR8!aRsa0; z>sM!|6lYSmNoHmyRCVPXRYyP6G@$9{66JWkp4)knZD@*-Vu-q>#-? zeJ*VGrQCD-SEyTye(&C3vhd)dgGcQ%?}v~-v=;VHKYzwiQyWWq<|^cI6g0T@*CqU% zZG>-(9X(DC3mbc|zyAlIh#x=3hK8D!_o2>44CJ)IWyOBzH_OV(GB+`43d4_&kB1U{ z(+sU-B(l{IItsrKK&Z`rFj+Ma>_-DJ}h;sc6f0Fzw zC8aabM{gREljX2ok6g4 z(}OuU6!VJv_z2mU9Q;7JbBCFU$;Hk3cUOe#&aX3_X22>%+LeVbjr7mPeQrt9MEcQx zK9Zp(lfo!=3Pd6)@q=1SNHKlVV~B^K&xt^S$5<02XD;ChI+)<{LoESO3_!L+&dF<&!5WUVowO($muu z5)x8UQnIy@mzM4t9K3@V9Un)%eY?80cJP~EZB^9+grc&t{%4=t*(Adj#g5?UT2*Ib-L?2T= zGjqG@aldpL=9t)=T;Z*bu+9Vsia?;Dp*?tP^gbym2})({&Ec8wl@n0dY>BaykB^U{ zqN0Sv`?|V+`I=>)LPL=dcd*EU!oq|+PgtV56S=LS7<~^646HIRGBUEZ&Pzy0h>e|q zo4Wy2q@9_L%(@)TSZy9 zN@oWk$#bfR7*=gFTiaPP9&XPQo2O4J0XcfQxqXR^WoBmXg+ymzQGPdLsL13Nf`a-T zvCw1yZA#JIgkGYnJ#qbSi1?CSL4_cl^*rXl?Nd|q_utjO1^w#!K`YYGzVxs(U^060G97#(Yx>HW)Z)Z3fz% z6ZTQAa~eN-uYXtajM2t?W3XSPS`8bPxYFMzl|&KUm7Usn-uFG%>>I+s1c6LpgXyEI zjV8lm_lwuG2cNeA18UE~!JoZDz+SR1UMz0*^vIryNlDSu(4dS*dtIEcMs?fhD@sXa zrKh9#Q)rMjR#i>@{COBsZ^cd{9B0!OXwpp7U$FJ5^mm}9>#}~Eh4r85xT)S=Z!#QQ zwD-$?sS0@Hb1!Ni`*eLGAB|&HApQ9T)9SRjw}@} z1qT6N&?|h?QBpEAH}5nm2_xehSuEUa3AhJMoe)3>s)#D{!AJM+-$yh7_Mgbtd?!J5 z8@U-)IsB2}bVCXNLC7}WpNFhMGq66K2Do^CxwBH!yi!z2X@6(77ScW>|0Ri0NM4>7 zy58LQl1T_WjX#s2rNmGB-5+HHlSSb(($g2^AMJDD1dHI^{qyvj?$IOO=b{xC4?V|t z4pzxnFb~F5BCmg6;3dm?f0h)iQuLh{5WI8$b|TqFxn(D<=T&xsr`qKf5%1o;b87^c zcy_ST%f`m``t@tTro{3$*u^+Ta)%7#JQ_Qc;1{J>l%^thu?l zI12wUlAo6vBwn>+=_l%Oeqhs zZIy=eJ8k>1$i{{CD@8@=7ca_6OZ|OYO6sA<0q%qWU)I%KI4C?EKrqquvh2v{C_WjP z*PqcGtb5SW6Nh&~7h0mNe-<0ZWjO-iO-4nsv3&dQA@P>j2_7}A_(?h93h0A?bh(YlE-WNOLlH~m06-0qGc;73nwpxD zBJ%QOL3#OTSJ%3ffLY)tGQQH{;^J1#@yh-CU8s2hC40#-9`WbYfi*38%vgD9dP?em zsPsEU6tsmaIp`Ovb^})aimmDE9qp<*tFgLQV_%~`ASM>qT|WJ!{q~!68^OrrXycW3 z)QOPH(2r0SE-ka((ObrMUgBoyc!)WdHW`}=u|0WUBl^a%wRM)T2R-|xp;}z^N~0!0 zV4%Kkt#;El5>EbnO+zod(D#A+2A~Y6i;z|8eY+7b1#xnE z(24?2VDQuyKp?Qj&s=v{*x1ZUH1N^eXJ==pr?s9xH-#2we}8|e1Py_Mi@Oh$BE+_o zghakhwd48G#?;gl13r_GP(w+{@XSm?zZvYG3&;x}KYm0-MFlzvI$(8WWdJbQ`T2dY zb}~~{zSlyi?=~cp?S$Xj*o153Rs70vpeD@P6-#Guwk)r zLNDdf*M4KP7&`o}V-3IFx%}E~AUCgoD8WZXnaAa_vSx=?A$)E0SV%|6@m>kZW3vSl z1Onx45K(9^oi|=T16wq|Pxi;^LK~0QhoMUIN`NDSwv-8p-A2g=kZra@_F%sqjD!4jRhG zpy5TW)G0S+bKzU}p6+c(s(2b*>sARGt(*i!BM^`DW7&DLKU|-BFC9qOhiK9hY9k}U zeif!|`jvy1LqSoYa=H$=U)1IW;r0 zW5sb%27l|NrdQ0r^J8n0(AAwCEQA4P-0|`8HYpQ3``Y0dtcHW_o|l|54|JIe`#R#{ z?_@K44)1ewLC$a6IsW_W8#J=Cv>AmM?B2ROJLx)QkAGY*@Syr5yU2}Ij-nzMa+b6v z$rT!)%*92V^`{?F7u3BOlQv<}R5(oIu)yICJ$`vyKHX8kJiyv3dyl#MVkC-4k7qp-Zb~VGE(G1g;jEr<;pGLjmrp+xHps zi;%y3YbW~czTq}r5j@u>Fl$(1FgG%O%|%l0w!b73Mb|8>l#Qh>%E`%z{8q$Er)kA; zMuLhcR?KVT{?EGrhM;W=4GY`ZVgQ^6&ERJiC4aFeftd@>iQh zjSc^h8_x_qpkG%Gx&P&hu+43%&uF)pZPEOV285nfuB?`;8Ph)5v{*NLRfik=-Xk}i z?wo!trRQT$-zpYXuv`IVl3Cn77qgzQz!yBv>^ME_8boM2{gUw6A60G#);B8%1b3(a zQta*58$hBkFb405W7$hftXIJ43JFI)^3zH zHwOx6o7>PA1!^zcE`A`>?t39tQZ61fLh6u+GZTmVdni^lD8|n&i7Hjx3&@FnT9jj; zhT2+XZsZYHItt&VdfVQ0;8ur~0N9f^l*EMpu71CQT+pwG1h=F?+nu^sWQB^4F1r6l zk5O?9cOWCa_Ivr^&qg-kmCWckhc&VaeXkVK{P?BYGRB1a@1E}w&YPC-I2;42@@hjgwTo_ZmN zX~?*yaWrZYQHp}1g@sqH0S%X?W_w%Pn+JFAQoa*USsC3E%#!zSH!>gmRu97z;Id!> z035Z1S6-|8KNwB|GfmsyDMO`cSpC|=2fB2 zT?~x((K#^PNXy8$y<1!WgmzmHF2ax7dK_x*?MKWuwziD;A3uDMgc?QqgBdSaB10Ko zh>zEzum6+gl&kL3dqT=QAi$qpkgBb9Y-p7&(bTZ6V8{m1f2S-Z>oxGHI9 z?~MA0TgY#-zsKP2CI2gN7hpsEN$V++C@|1JrZ)Rru_!Vw7VnDbkG-V0dx8K;B1XVf zH^xf^-HPH&VAUllgVs^Yrww2d?C0mVv}APZkS54E&32AHx~oy^!wobxOgIpJux0Mk z%jawT$UXcA)d1@0rlNSoj3H;-9#*~xMT&Bc(e^ZtcwD+aYlL*00SD~8$SL4%7B-Ba zCI9wq4v0eg`G!!Da&!G~K>0P5(SBaU_?#h31d`;-EtQZh<9BkqrYWSCeSzG@W`> z&Rh41TEmo=Bg#L!dla(Nl}47rs|Tk;A~}B0T6TA4c$KPHX689uzGjVPq+m)lcefzV z|E3dDZU*@cw#&mJCmds8woCHxt0eaxQUSwqmjd;06&+41sGjd}YrVEJ(|R2_xl_=+Dt4r8}$MiWx!#x=QnHKNZg?;xvfc5Gw!n*Uul8IrH?B zI-cAO%v+RD{(k$;ofq<*?OjuB^p;)y#O6~oMRi3S7DE)T-97^kJ_uwTEEnATO(kFl zImO$zZ=v|8tElYimO-v+1EF}A0mHhaz;#dt0XjoWS$D)J4BVfZ_i!UTG zP=9tbTk7e3OHExQx=c8_t1FJ?AJ(}WCWB4R*QWbC7?BkuK}Caiw`g#Vz$|9$OEt#p zSL&*E`aHD@tkKhM?kks)gr?0h^N*>$+xe-X;DsEY%qApk>VH(Ru=&#_D6gyf;B9kH zm!zF6-GY#pWrc1*chTxgva8{_32VGrUV7?B} zp_s(T!omV}Ai!?^UA5g_$$Lfa%h3VV?tB%Qx8!VOA}fK^<;8eq=`15DAJ5qRt8YjQ zi}?<8qqn)8)o#G~hijRaYBWpypHf)$Q=C$*<1)#YKExH@}l zHqmeOeCE!NOEk>&JJV{$ly9VZF$dOZvn!pA*^cW2ryZQ>v zG&ecEsi_IdAMkR9P}%MgvOEy<;Ck0wTv>TD2lI6Px{KtWLMi@1LQ1M~KGs^Cy;qX8 zz^dKdmkaN5uW3d!EuW6n+B%sQKUncvcpl32%WfHe3TZ;>J4m<*trbtn{>=n6;l}8_ z-0Bb>7;NiFyQ75}!!kgGMqU?GT`VA(kTTKE<{S|q)jK8des`#X|IcXVc!IWEuSrg;^6+2r-rCcLqM|>>U+pTOXGn+xBnId?_f*Z#E8UR{87dxz^2fRm@%OEB6?u zh~HpIC7XDcxRa3B4Xf=ILINi{^8g32o@3ZhlA({v6rG!&k1>e<0HXu@z*Ra`5&G7^ z>BqL~+*7CQ8!b~(fgiBQ)T2fFWqcGym2me^woPv#Q_yY_Bs4#;;dGo$Z!|F#s`-;r zN}@+3)EMWL`c}rJ*m;zuBGk3Vou7sZPOHLox?r^q%XFK1q__azM;Z3a!Blm6oN-mN6^ zK_Nx0L7ahy1+$snxG1KvJm!Q?<|nwEs;G#$@B+l}lgnCDJohBhdOEVMNJNK(<$4I2iZ{@KU7y8?V;{BdcHRIn7NqkwLqVTXHSGBljUCQww zu7s^Y8YTC>sTrC-)R=7OU6tFdGQ5=87oozr_31VI3&{%yPju*Q z(81EQy7gtl%J+|Qbo0R6Lz;qw2FxqqUt!F`-6|bMeJ|{8}EZa#B<{DJ)qv zG_`-u8y+A;?&T36vg4*WnNRO;p2nZU2rc*ZPoY!v?xOwK2-S_R91Q2>ALM%#<^=@!!fOIC<-m`krD4RnRsgRGj_W^h)`6yY->3>mI8C! z*y#H-zW12aL!L)=GbASYvVYF%bHn(ZUUWgH0$bePg-v?gqT61nyJAnfsZ7T*s&7vP zuIc0Ma@H61b~3vVYWdCdy>L71>+kSvuM+m`*v=v&B(80>xjf4if7+8(YZ<@koNtiz zylGXn+{<)U7#)VpBqX`%=`_Tzb!2Z&8+nkdv928gOuk=?C~od(nwj!Hw*HaxuVG;Ea7bd+fGsr2}+r-y{f3l-dn zw;wXgb`k5>f7WqOOxMhL%CrX9!zr1397z;sGzyA#S*{rI(KlE;o`k5Vyc%n>N?a*r zc(WsrFSlDYengzVWmxovsc_N8t00l3rw3tPNMH1_si%tq2@#LdxBOJ__xH;C{~3u} zcs4`J%gD$GGit!7HIsg4Z!c7-44D%mY4OS_h7T<2x+}D;t=Ct??_liyqR^nhtM8VN zl45+ANHjjTHvRQsg;)gIyBHXq;UMw}Gb3tpeq2$wl9JysUi)^GG&e;pvhbYeP!#p> zIZs;t+dxrj{9+Sn z7;wCCb~Ze-TweSayQFQBwzv4}yk)Z@iack%#`){p-a*%`h<|^Xtje-?;ZjXAd$2DLnJwHj>W|By##H! zKt(Jo@_(m|v=a|t)Cjovd8#4+JF19mn63*7CdttOdVR*ro1L9)GgqfMg1O4xKGBU@ z8JFn&6BjLkVBcy~vS%*<6U}5sQ`5M+z+Vc9-(!`Z#yaj=!5#(K&iT2Wsk>grH*8G% zw-PSJ{qrrh?n&PSeziD_o!WZSi)i{T?Jc%KQ)|@OEta}>y1xwh8x%co0QF-LrLV6) z55WSB+y%F}rhds>_F&`D)szwD!~4{SB?yu7HQd|bw^*d~Bl>=-x@4{Idp*0u{`?<3 zAt+p8Oay|m?fm3wv2mpN=kh~mNO=O25>Fhbi_e^Xd9-|8C3@d)270i*Z-Uy1c;w_D zqJfit6L6q_?em)}xSC?<1B5h>Z(7KVnD2aMP%eN-;Wz#HXIacW z;3)#}N~&Pn*NdaU+74Gv=b&j$jGHlSr;m-uJCY!4$31nR%Djkt;%OuS!52Rqn~Uvr zju55#OP+I58y?b%Y*a+zjc#EzZ9e$u=nZHO(F3S>n9j+7nxLjOzP`GIkh#o=S=Ij> z`k{rneK2N&RBMwM%hu_hKSknq^dxod_~N@CF3ux-PT~>o8!zLHhg$Al z%w_2@)6-oG;rd?l&p3p#PSYl$Ms3i`;)Gp9f8_+#rJnqGkWYped~Q8GXBd-aGW39z zKsU$W2!VkL8Z6C!WvQueWi!l7Ox~1$kk2Df>FoUR!*J64^YVXH!EUs5t;I6g3bS0I zI7dR)YPZhn-*!_x^tMJpHZ*+3a=nkNQrX%gE9*I~am_Pus*B5N@H3=*hP>f%zL}4+ z?&0O3P#~Mh?z5zU9TW|OUAM;dD2-c@o$GsI{5dF;aNVuupG`+IY z&1DXI!GSmO4`p#+-t=_g2LW@=H~>BMmpFp57mb3d(KYo4{gf)!=Fa^USWDVk+V>7P z`SMZyYa$Ap1G_;=np+d5IW}XApa{eqMIa{QaV;mki;j{37I~+StBj!P zl*@K*Zua`Dj|-8T0O+bid~@^i?gvYFUKD=&nGhc@+i`JDaLPbh9;t0#sKL}7g35c= z$?(6HvbrI5EGLp)Id0fxrMl#>!tZ?W1 zx_&}Iv^C~i89CJJ9+{n!*b999UclBkI-grXr9YegH}(xkHMFX$MXxWj_K1r*ZuRz< z3A#I4%?k-#2X-3mAVGT+Cr5Y<+K6_el5^ch1l!x&hRI5eSQZFB=`LpPMDocZoz;+WPajFDs86jFe89SMDt(ZAOSi4oj{-?85kHah&gEh4jg*Gi0&?me_7d+(`XSVlCndi z&R#>McYz9`ysXSN3c>Rm9h3i5Fix|Hz1+CO6YZm8`(z4xkMv zddVVG*GBuKfF`8)!59CnwQ=Jj3Z;4hr28#IucZ}lSm zkwg`pO${Y`ZgJ;j+{$dQVSfO_26XGkJm1S1>VF)!b~7WtEp|Ghd$l^D-e@1CNrSIi z=bMmp)#NpLSHKc|-4?ht*2Xuhw~dE}6n~DqvWaZCLxyE;Ys>0B@)V?~Aou|xAJDUd ztOBq!Fuw)Kz0s4hZzqb=n<-b*uH}V$VVPk$8qReAt$p3H3wtXz^c{JTbeqG~sR&Hz-dpmm3tzO{t~2+_NHdH@I? zoO!vsyTh-B)1+pmrV2F6ItB)IYDurpM69)Lk1+2Ntu)`&r@#px@Z4>VcUhFzjd z5PdOpzEfz+Qmw74nQgvDN%u9T0ylJ;q9szx6ytUQrZftkIadUi2P$FKMfhv}qp#13^DTeig&Wz`&y4AOQ2K+}w{6RM2uy&&>SX zcmuT<^fxe-*xLG+_cEi6f&vW>otJomWo(SgqTIu8xcq@reXGpq8Wc#A_t>RQ4+2zZ zni#!&tUVkPGX_~rA5P5qG=-gk1OrvSc6Bn~-5m16qzCLp&W48dvnxCQD}}17pVbGt z4<;d?IsmCjQO=h+R2o1~F_j0Q1qF;MWqN!D=XphV7zn@o?{9~@T5t0)QVjj zu8xb}d-RWO;W3c7kBqxMQ{L50pf@3XpyBgUE=CB8Bvh}~#TJCJApA6`iHwZQ%38}+ z%X8VDb_S8++>TlDLTkQ^=vaq4<#X4C_`<%l=`>3|DS2lHO_^`J0V1R7v{v=L4Rw{| z#l|q6iVtpkN=TSwZrt0`!1IK(!R@vE-^ypr%ln`$XJTOy01YYW+J!RB2G1e?lfg2~ z-<%#l0~PadP`Z1la(1utdIY9t1}BG&sUw_j-D(vS$Q2?_K|x-K&)W<_y7lFnTrzte zrm~<$TiC+~8Nna&z=TkMVmkC>xpV5^)_D- z_Rr7HXEq<`^E&EUydL&zL;Kw2;QF_QQe1s^dMud>Yes}FikgZFh!qxAR$E(JnCKWZ zet@vt-7fTgKe!gv{ToJ`B%$N~(fEOzqe_R3?8~Qala|tgloSV$0Pfn#`P-d5Q9iP{ z8-DP9Sc7(aq<_$-!@%*wz;0xc$e@an((C^Ez4KSVtV1UnEz;8^;K0zhlWIG@W2yeavkpPkBlDR?@V5z z!Gih@36Uu1S>gG-=9y_)fSv}EAMo?nz|gg+$xoUF)XiWmks$j1;Y10* zrc!@`E`g;l=~oQDzf-#kTeYpi$U24 z?D5Ssj)$jEzj3aqDW$UZ?0%U+vpLR^S`<)AMMiHo680J>qRdP)d;56^9X&nyv|;G7AiVnf z`kcFn0}yT?o_lkK|8To>xP^hSN*HsGfv|#l27?F(MeWO~dcDHWM|*+$ zUE+d&t$b;K-TXk&)!v>FRc#O0Q%tNSf?6s)D$l-kejc#-7+9&;85k(QRnX8-tm-xP zbq|bJ4()&GUDA4 ziG->~zdh&oerR{~^nj}Q+hb$NwGc57wEX+`?}lF`{rgja!t*vf@|(81H?~Y8-~T@c z{E~-@%L4KhaEzdpKtmCD6yCWEs~sVo4yJ<|=gk{gm*v@GjoOfqdxRmg6}B@V3~AKl zXnOjIl=V}j|D8r8z50OuJ)YLvh?LaSOx~xOzFq3|qIpQ`A^Wurp&%iI_;PM%D2EZ+ zvuB>5Y=nH790@H7DCEOn@-sfJE}*{WCNRm29xZEYWn~1KaGqMwZ}?LXhQO3DLi#r( zIhyvg@84U-#<-c8H$9Q9eA=s!M8C)Pr4caBV1BZ7KI*KPk;JKE?=c-B-KG;(S0x{v zVXsbrMxB_$K;)Gp+@N&)H`y~LFE1}Jyn&tx=9v)3aDYGMY%sex~ZjtCi38b*-1_gBa~9ON+zb< z>}-k%X)>(9b|Yw`1qB7KVSWrlCM3kc;o;Ab5ztRVc2Qy&#f}Z_OK9c#aU|oSw@v>G z>><&+Kjow@)9n6C%&?zGrKYEkj*NWI&OU8V1Ugms3l(EhN_skIx8XoXEI3w!SnlR- zot;&ge$>?^wi^Ynslt34D9{^=T(u*u&iKekX@36J#l?`?)KEoye{S7RNe;KV82>sO zO0nkV>(G27-?<6;2x%Di=tf|o2Yk=W91mcs4GaoGgB@G_vKofxtlAYY^@r8_;2bq2 zV{N?|N&8|TS$N)1Cl#UsbcH6Mg?|_W5}b&m)(cF75)Ihnz;t8no?l#m=-mgDDUq9@ zcBy$DRUY!5@q9WYKV%cd(uU{iJW|qyUpJM_M!zKk` zzex{Ff-Sj7@PfBWj0vI1nY^##`7tD<^V+}(mbjms@yD1wkU`lLC~jb8 z0O_>YlNqF)kZL2Ok3d!d?lcfygI+R>T=>zuW-y{vl$V3!q9Ui5^e!gmr6{*=z$I#P zleTF8zISuNr*a~lh=T=9&Bh>@ihh><4I0g4A@Av61r0R+N5Vc^OH1E#bEl)cjXC31 z@R|}1Y(RmBU_)rExx$OEsJI|2d&Zy|169fmFMK`924NRShOrRD*m_|3)$ z7{j7tV?iTo*B{Rv`O<-x%+=L3RZ&Al#TW*ZVBw33dIBu^Ehzz#x}@3 zr$Ds?QapD7Jp~s_P$2;3p}7l_6EicqtvMAMJeV^L%*{O-sPnzPn)kid1-GZAB@CYl z{V9Z!?Lj9GxpiyH29{OBIlOfd!VqBnYv$sN_aYP;^&ZEiro9VoP!M1)Esk?@)8Jrf ztp(4GCJqW%VqqgfUltW*Y+x`n<6CL}dvl=OlmHXJ0sWO>BNDVOuyUYiU;8f=4D(O8 zKrrfn{2dB5gdZ%`w!3WvsJB}@V>x(rW)^Ce$_$a;Hmx0QZf-)axUfvZMY6rVj)nk{ z(Zasl(y|v@>;(uZPkQH}D&7Vk!9-zUA($D8ggz5`_>Oz-yIWWSsOZT1b}GdPGH*b2tezMntKoLEJ;NM=Vyz^vda zN&gC*0M#I%v|PU1+|-14*BtcM;WcmKY29r9$ zZsz%#QTR5!eBxI@Pad)**qWerRDp@)UIEC=C$*#_Gc)NRN`#Wxh5lUgv2eW&%$lQVaJ|kHZg4n%r+G)A}hqNAgiM84kqd=2Mb1O*=q z7nfwgr1Zmj0^*LI5cMWS`W)x+-?B#mS#^+#d?GK>om*3|S3ojTyO{$vpc;q`EPEJn zZ>+C-6&h%1O@mnQttyYtZNPS!a&gFru7Ls27s5t@QvvMrIzM702|d~0cfb0_7t&6` zZG~^cMz7wbTV~b|XGTK6HNpjgyAYxt6!U>qtBfj{I#mv-wdceS9#mNjf8WWqZT7z% zB7C0=%qTFL+jP1ePA;qmj_9UWe->rWHgM1bAedE|2(G|Iw*a)FD9-Qg?T3#ZVTleE zXjjU~$(ey_!=xJz%^&_A4S^$^6Ef^L2=cAQn5sU&s1}G^(gyHCE2^tsLnjo8L_gv1D|=HgN@Xk@Z6=QQ!h$Je-`Y4`v4|t3LqWz-j}Rt`F$&*DnFHzOT3LV0}$U zDAFkTUQq#H@Ce+BdFloHplu=y(Ji;YO&*MWbA5HPt^Ft*i~>)cR^_1BYKXr9cLq4x zKoABo{0vMY>gwa*PRAnSUD()w>Td*#3=}+UX&3{*t_DM!)9>HE--}U(cV7HL(syuRw`Z4B5WpI4@|L{XXfNlwth%Y|? zuBZZuL3Q=^R9RMT?jLX(!2H=y@BuI1%%hav@|s3!A~#Vad2?p?4}Z2 z3wOnQ_|O3s^0oeiI|v45WX<&T>W!vKqz**Jq+*XC;lnmPj20O5?&k%_H zm^`;}E{Kg&BM{8*wL^~pNtdDeqX<<55LW_@ztNyO9_7Wr#@?O#Oq9F~w<-K)eF(BA zG+1Il5wxYGqzGUCi;|!M=_foq`FO7Gsj2FhqTb$@i>-k$(s5hv2nD1JCR)gb*1-Fu zv);ZP07A7v7DTo7}0-O+JvgKGg$(jPo|!Qpc4Z+nEY)g*0VB9Y$d!u${KNzY8)FsB19v z1YYRSR2)1np`=d%n}L-5sy|c~?D7gXXc+?J)%On2p&D|0J<)E}5BMJ|yioL)hUbCW zCG{gv8Pbm^K?mP%$TIUeK7P8Jq_wY4E?@E2T_d%50B0a^%s=cKfpdG`j*u>g2;b-X zi){)rE(m|XRY_!jUVy{jI{?1)0OmN8?+D*D!*_K+Qs?tHS_fUq4LWmz`|})uv`4QH z_VFVgeP%`mz(!kGHW0r!$U^P`w+U#@fl5`@(qhuQwH2yd>gEaNyo@3oVq(Ve>m>O0 zj3oK_AJ(tHYYgCzc6LI)!OI;k12(EYH0qH04X$4!{NP(jz@y#V+G-5@+=R2bv~;Ai zvvX$V0-_E;(pJZ-Pen!c^y*{?>FuZKN5s&`!`)M&F)=e!iDKDL16&+|k4BmWG$Obc*b6c+SL17P1wz#88k-?@+ z)lY3_ar5IxblCBy5PMpQ*?D=M@I@++%HU3b*#sK9A{LWBY3^(><>sLiz-!PvA|Zfk zfrNFVQMa+VQ6=T5=B6Lm10xF7J}^B4kp>n&H%jo1M-gyC3i2QL37^Et zmE^J`7Qm(I5RjZvNo1g$?gh#~sE+Vu7kh4Xz@jKK;G_B9tQjxayXN(|3J~9ghGHfU z?$U(6k*Ccl@)JviVpMEDF9fzP$k&inCn5gE#*QIO;C-_*|M6L_I{%_C!je(NShwC0fk4o{d9Lxs#PE$NzpR+>oE*#?9Q+&{3N#%y|JNIA?M*Dr-2eY?5OVh!f;TYy?-|}$ m+L}7OF|>8~f8N9Sgr9@!zbAQdZbc98LC8Fpmnal7@c%zEu5VcY literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/reacher_2x1.png b/docs/envs/MaMuJoCo/figures/reacher_2x1.png new file mode 100644 index 0000000000000000000000000000000000000000..505eef7be1f63bb740e59e5b3783327fed662a4c GIT binary patch literal 16538 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 MDN7bUe){@<02y+lTmS$7 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/swimmer_2x1.png b/docs/envs/MaMuJoCo/figures/swimmer_2x1.png new file mode 100644 index 0000000000000000000000000000000000000000..cb87f23362d14805440437526e48483de1cdf834 GIT binary patch literal 17790 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/walker2d_2x3.png b/docs/envs/MaMuJoCo/figures/walker2d_2x3.png new file mode 100644 index 0000000000000000000000000000000000000000..827ad9dea1d1ceb3878332f34a12fe91b2ac49f2 GIT binary patch literal 26200 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/index.md b/docs/envs/MaMuJoCo/index.md index 3fa59215..fc48611c 100644 --- a/docs/envs/MaMuJoCo/index.md +++ b/docs/envs/MaMuJoCo/index.md @@ -6,7 +6,7 @@ lastpage: # MaMuJoCo (Multi-Agent MuJoCo) -```{figure} mamujoco.png +```{figure} figures/mamujoco.png :name: mamujoco ``` @@ -16,13 +16,6 @@ There are 2 types of Environments, included (1) multi-agent factorizations of [G Gymansium-Robotics/MaMuJoCo Represents the first, easy to use Framework for research of agent factorization -The unique dependencies for this set of environments can be installed via: - -```sh -pip install gymnasium-robotics[mamujoco] -``` - - ## API MaMuJoCo mainly uses the [PettingZoo.ParallelAPI](https://pettingzoo.farama.org/api/parallel/), but also supports a few extra functions diff --git a/docs/envs/MaMuJoCo/ma_ant.md b/docs/envs/MaMuJoCo/ma_ant.md index 3b47c794..b31f2994 100644 --- a/docs/envs/MaMuJoCo/ma_ant.md +++ b/docs/envs/MaMuJoCo/ma_ant.md @@ -41,8 +41,8 @@ If partitioning, is None then the environment contains a single agent with the s | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | ### if partitioning == "2x4": # neighboring legs together (front and back) -```{figure} Ant_2x4.png - :name: Ant_2x4 +```{figure} figures/ant_2x4.png + :name: ant_2x4 ``` | Instantiate | `env = mamujoco_v0.parallel_env("Ant", "2x4")` | @@ -73,6 +73,9 @@ back legs | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | ### elif partitioning == "2x4d": # diagonal legs together +```{figure} figures/ant_2x4d.png + :name: ant_2x4d +``` | Instantiate | `env = mamujoco_v0.parallel_env("Ant", "2x4d")` | |-----------------------|------------------------------------------------------| @@ -97,6 +100,9 @@ The environment is partitioned in 2 parts, the front part (containing the front | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | ### elif partitioning == "4x2": +```{figure} figures/ant_4x2.png + :name: ant_4x2 +``` | Instantiate | `env = mamujoco_v0.parallel_env("Ant", "4x2")` | |-----------------------|------------------------------------------------------| diff --git a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md index 03987e9f..efcb8ebb 100644 --- a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md @@ -42,6 +42,10 @@ If partitioning, is `None`, then the environment contains a single agent with th ### elif partitioning == "1p1": # isolate the cheetahs +```{figure} figures/coupled_half_cheetah_1p1.png + :name: coupled_half_cheetah_1p1 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("CoupledHalfCheetah", "1p1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | diff --git a/docs/envs/MaMuJoCo/ma_half_cheetah.md b/docs/envs/MaMuJoCo/ma_half_cheetah.md index aadbc84c..1347b76c 100644 --- a/docs/envs/MaMuJoCo/ma_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_half_cheetah.md @@ -40,6 +40,9 @@ If partitioning, is `None`, then the environment contains a single agent with th | 5 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | ### if partitioning == "2x3": # front and back +```{figure} figures/half_cheetah_2x3.png + :name: half_cheetah_2x3 +``` | Instantiate | `env = mamujoco_v0.parallel_env("HalfCheetah", "2x3")`| |-----------------------|------------------------------------------------------| @@ -68,6 +71,9 @@ back leg | 2 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | ### elif partitioning == "6x1": # each joint +```{figure} figures/half_cheetah_6x1.png + :name: half_cheetah_6x1 +``` | Instantiate | `env = mamujoco_v0.parallel_env("HalfCheetah", "6x1")`| |-----------------------|------------------------------------------------------| diff --git a/docs/envs/MaMuJoCo/ma_hopper.md b/docs/envs/MaMuJoCo/ma_hopper.md index 4b26c07d..bd8788ba 100644 --- a/docs/envs/MaMuJoCo/ma_hopper.md +++ b/docs/envs/MaMuJoCo/ma_hopper.md @@ -40,6 +40,9 @@ If partitioning, is `None`, then the environment contains a single agent with th ### elif partitioning == "3x1": # each joint +```{figure} figures/hopper_3x1.png + :name: hopper_3x1 +``` | Instantiate | `env = mamujoco_v0.parallel_env("Hopper", "3x1")`| |-----------------------|------------------------------------------------------| diff --git a/docs/envs/MaMuJoCo/ma_humanoid.md b/docs/envs/MaMuJoCo/ma_humanoid.md index 8894a06c..dba79494 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid.md +++ b/docs/envs/MaMuJoCo/ma_humanoid.md @@ -53,6 +53,10 @@ If partitioning, is `None` then the environment contains a single agent with the ### elif partitioning == "9|8": # isolate upper and lower body +```{figure} figures/humanoid_9|8.png + :name: humanoid_9|8 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("Humanoid", "3x1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | diff --git a/docs/envs/MaMuJoCo/ma_humanoid_standup.md b/docs/envs/MaMuJoCo/ma_humanoid_standup.md index c65ca921..77e4805d 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid_standup.md +++ b/docs/envs/MaMuJoCo/ma_humanoid_standup.md @@ -53,6 +53,10 @@ If partitioning, is `None` then the environment contains a single agent with the ### elif partitioning == "9|8": # isolate upper and lower body +```{figure} figures/humanoid_9|8.png + :name: humanoid_9|8 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("HumanoidStandup", "3x1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | diff --git a/docs/envs/MaMuJoCo/ma_pusher.md b/docs/envs/MaMuJoCo/ma_pusher.md index 449e06a6..80a57109 100644 --- a/docs/envs/MaMuJoCo/ma_pusher.md +++ b/docs/envs/MaMuJoCo/ma_pusher.md @@ -43,6 +43,10 @@ If partitioning, is None then the environment contains a single agent with the s ### elif partitioning == "3p": +```{figure} figures/pusher_3p.png + :name: pusher_3p +``` + | Instantiate | `env = mamujoco_v0.parallel_env("Pusher", "3p")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | @@ -73,27 +77,24 @@ Wrist | 0 | Rotation of hinge that rolls the forearm | -2 | 2 | r_forearm_roll_joint | hinge | torque (N m) | | 1 | Rotation of flexing the wrist | -2 | 2 | r_wrist_flex_joint | hinge | torque (N m) | | 2 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | -## Observation Space +## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position of the pusher's object and the position of the goal. See more at the [Gymnasium's Pusher](https://gymnasium.farama.org/environments/mujoco/reacher/#observation-space). ## Rewards - All agents receive the same [Gymnasium's Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/#observation-space) reward. ## Starting state - The starting state of the environment is the as [Gymnasium's Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/#starting-state). ## Episode End - All agent terminate and truncate at same time given the same conditions as [Gymnasium's Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/#episode-end). diff --git a/docs/envs/MaMuJoCo/ma_reacher.md b/docs/envs/MaMuJoCo/ma_reacher.md index c40551d9..2d86f508 100644 --- a/docs/envs/MaMuJoCo/ma_reacher.md +++ b/docs/envs/MaMuJoCo/ma_reacher.md @@ -38,6 +38,10 @@ If partitioning, is `None`, then the environment contains a single agent with th ### elif partitioning == "2x1": +```{figure} figures/reacher_2x1.png + :name: reacher_2x1 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("Reacher", "2x1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | diff --git a/docs/envs/MaMuJoCo/ma_swimmer.md b/docs/envs/MaMuJoCo/ma_swimmer.md index 7bd894ae..780dad3d 100644 --- a/docs/envs/MaMuJoCo/ma_swimmer.md +++ b/docs/envs/MaMuJoCo/ma_swimmer.md @@ -38,6 +38,10 @@ If partitioning, is `None` then the environment contains a single agent with the ### elif partitioning == "2x1": # isolate upper and lower body +```{figure} figures/swimmer_2x1.png + :name: swimmer_2x1 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("Swimmer", "2x1")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | diff --git a/docs/envs/MaMuJoCo/ma_walker2d.md b/docs/envs/MaMuJoCo/ma_walker2d.md index 4923954c..c890dd53 100644 --- a/docs/envs/MaMuJoCo/ma_walker2d.md +++ b/docs/envs/MaMuJoCo/ma_walker2d.md @@ -42,6 +42,10 @@ If partitioning, is `None` then the environment contains a single agent with the ### elif partitioning == "2x3": # isolate right and left foot +```{figure} figures/walker_2x3.png + :name: walker_2x3 +``` + | Instantiate | `env = mamujoco_v0.parallel_env("Walker2d", "2x3")`| |-----------------------|------------------------------------------------------| | Agents | `agents= ['agent_0', 'agent_1']` | From e6e49f1b2c2d1bf6654fd5df9236f4a5c19c472b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Jan 2023 22:25:25 +0200 Subject: [PATCH 005/160] more pics --- docs/envs/MaMuJoCo/Ant_2x4.png | Bin 31399 -> 0 bytes docs/envs/MaMuJoCo/mamujoco.png | Bin 306724 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/envs/MaMuJoCo/Ant_2x4.png delete mode 100644 docs/envs/MaMuJoCo/mamujoco.png diff --git a/docs/envs/MaMuJoCo/Ant_2x4.png b/docs/envs/MaMuJoCo/Ant_2x4.png deleted file mode 100644 index be51c6af5b5c50bc3fe234cc30d51469f35e9d1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31399 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%0Dbus*n9%h3n};aIT&;y2S9peD5~my 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

    d=sws%jZFyGxyexe%AA4SSN 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

    lZ5%v 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/humanoid_9|8.png b/docs/envs/MaMuJoCo/figures/humanoid_9|8.png index eb35405f7ec6751db7189b6bb59b685ba3f39973..352be596f93c9dcb4c23777fcfef34466fcba9a1 100644 GIT binary patch delta 156 zcmdmdl4%A22WQuKbLh*2~7Y-(JV3m delta 156 zcmdmdl4!LG9I0#JhnTti7_ZfbE!Vr~JN9wTEb0~3fP V-oLWcfqED`UD^Cx);T3K0RY4fD=7c~ diff --git a/docs/envs/MaMuJoCo/figures/mamujoco.png b/docs/envs/MaMuJoCo/figures/mamujoco.png index 91abbc162fc455974bcc005367308fe463b3aa80..008adb9280b2b7306884ebca4eef37873cea31a6 100644 GIT binary patch delta 141 zcmZ2-M`+0%p$YXIJS@ygp6P2d8{1pk8C%<#THBeowzEjAbu|hxFt#!^v@$Z)HZZU< zFu3TXwupg&LAAs+q9i4;B-JW6KP5A*lEKKp$XM6FP}dM$!vgtva?_uzWl>N7%9vOI ZO*1xu7&Vpc+!>%z44$rjF6*2UngAFxDMSDO delta 141 zcmZ2-M`+0%p$YXI+#=$v&PE~g8rxgj8C%<#THBeowzEjAbu|nzGO#iN7%NQA3 Y8JI$hdfS^g8E6!Pr>mdKI;Vst07x?`9{>OV diff --git a/docs/envs/MaMuJoCo/figures/many_segment_ant.png b/docs/envs/MaMuJoCo/figures/many_segment_ant.png index 633cd44db47d81d34c06b6325d004e0e0a3cfc22..b987f5f798d112a734d5f85011f5266540ff1899 100644 GIT binary patch delta 104 zcmX?bn(@GC#tB&*JS@ygp6P2dHx`{t5itreFt#!^v@$Z)HZZUxS?UZx;OXk;vd$@?2>=$w9Mk{+ diff --git a/docs/envs/MaMuJoCo/figures/pusher.png b/docs/envs/MaMuJoCo/figures/pusher.png new file mode 100644 index 0000000000000000000000000000000000000000..fb78bbc1eb628afd2dfe29f965df7e97477d82bd GIT binary patch literal 18746 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 H=>Pu!@$;sn literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/pusher_3p.png b/docs/envs/MaMuJoCo/figures/pusher_3p.png index 4c2cb3fb75c44753084f23643c137e06eba6cd42..4fa325ec7648d6489f8b42f1ee53b11b60842a13 100644 GIT binary patch delta 104 zcmX>(iSgtl#tB&*JS@ygp6P2dHx>nXh!}+!7+aYdS{a#Y8yHv_7+iExTQvEGhb)G~ m0{MAzlQ}&V6o5KRtbm$~O&~g^vYk7_00f?{elF{r5}E)mnH=~4 delta 104 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 UBoXt+ZFf delta 104 zcmey@#rUs_aY7acw}?2avr)*rjYaJ)B8DMG23CfqRz`-}1_o9J1}}rp-k8kmDvKd8 lgReAYvYo4f0$7KUv6X=-M916S#K{am;OXk;vd$@?2>@KL9l-zq diff --git a/docs/envs/MaMuJoCo/figures/walker2d.png b/docs/envs/MaMuJoCo/figures/walker2d.png new file mode 100644 index 0000000000000000000000000000000000000000..da312187d2e48ea079af66d96d9a0001b2801d0b GIT binary patch literal 26438 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/walker2d_2x3.png b/docs/envs/MaMuJoCo/figures/walker2d_2x3.png index 827ad9dea1d1ceb3878332f34a12fe91b2ac49f2..577d44ae7195579983ec29c6c3973d79100d9dd5 100644 GIT binary patch delta 104 zcmca{hVjN3#tB&*JS@ygp6P2dHx?zPiWr3$7+aYdS{a#Y8yHv_7+iExTQvE3sw{@Y m0{MAzlO@s=6o5KRtbm$~O&~g^vYk7_00f?{elF{r5}E*qiyjUD delta 104 zcmca{hVjN3#tB&*+#=$v&PE~gHWnqOiWr6%8CV&bS{WH=8yHv_82GCk%bENo|tiIW+Cz|+;wWt~$(698_n9x(s_ diff --git a/docs/envs/MaMuJoCo/index.md b/docs/envs/MaMuJoCo/index.md index 5bbffd54..4df890ea 100644 --- a/docs/envs/MaMuJoCo/index.md +++ b/docs/envs/MaMuJoCo/index.md @@ -5,19 +5,18 @@ lastpage: # MaMuJoCo (Multi-Agent MuJoCo) - ```{figure} figures/mamujoco.png :name: mamujoco ``` -MaMuJoCo was introduced in ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709) +MaMuJoCo was introduced in ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709). -There are 2 types of Environments, included (1) multi-agent factorizations of [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) tasks and (2) new complex MuJoCo tasks meant to me solved with multi-agent Algorithms +There are 2 types of Environments, included (1) multi-agent factorizations of [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) tasks and (2) new complex MuJoCo tasks meant to me solved with multi-agent Algorithms. -Gymansium-Robotics/MaMuJoCo Represents the first, easy to use Framework for research of agent factorization +Gymansium-Robotics/MaMuJoCo Represents the first, easy to use Framework for research of agent factorization. -The `mamujoco` are not included in the current `1.2.0` release of `Gymnasium-Robotics` since we are performing some evaluation tests. If you want to try the current implementation of these environments please install them from source: +The `mamujoco` framework is not included in the current `1.2.0` release of `Gymnasium-Robotics` since we are performing some evaluation tests. If you want to try the current implementation of these environments please install them from source: ```sh git clone https://github.com/Farama-Foundation/Gymnasium-Robotics.git @@ -26,8 +25,7 @@ pip install -e. ``` ## API - -MaMuJoCo mainly uses the [PettingZoo.ParallelAPI](https://pettingzoo.farama.org/api/parallel/), but also supports a few extra functions +MaMuJoCo mainly uses the [PettingZoo.ParallelAPI](https://pettingzoo.farama.org/api/parallel/), but also supports a few extra functions: ```{eval-rst} .. autofunction:: gymnasium_robotics.mamujoco_v0.parallel_env.map_local_actions_to_global_action @@ -48,6 +46,8 @@ MaMuJoCo mainly uses the [PettingZoo.ParallelAPI](https://pettingzoo.farama.org/ MaMuJoCo also supports the [PettingZoo.AECAPI](https://pettingzoo.farama.org/api/aec/) but does not expose extra functions. + + ### Arguments ```{eval-rst} .. autofunction:: gymnasium_robotics.mamujoco_v0.parallel_env.__init__ @@ -57,20 +57,18 @@ MaMuJoCo also supports the [PettingZoo.AECAPI](https://pettingzoo.farama.org/api ## How to create new agent factorizations ### example 'Ant-v4', '8x1' +In this example, we will create an agent factorization not present in Gymnasium-Robotics/MaMuJoCo the "Ant"/'8x1', where each agent controls a single joint/action (first implemented by [safe-MaMuJoCo](https://github.com/chauncygu/Safe-Multi-Agent-Mujoco)). -In this example, we will create an agent factorization not present in MaMuJoCo the "Ant"/'8x1', where each agent controls a single joint/action (first implemented by [safe-MaMuJoCo](https://github.com/chauncygu/Safe-Multi-Agent-Mujoco)) - -first we will load the graph of MaMuJoCo +first we will load the graph of MaMuJoCo: ```python >>> from gymnasium_robotics.mamujoco_v0 import get_parts_and_edges >>> unpartioned_nodes, edges, global_nodes = get_parts_and_edges('Ant-v4', None) ``` -the `unpartioned_nodes` contain the nodes of the MaMuJoCo graph -the `edges` well, contain the edges of the graph -and the `global_nodes` a set of observations for all agents - -To create our '8x1' partition we will need to partition the `unpartioned_nodes` +The `unpartioned_nodes` contain the nodes of the MaMuJoCo graph. +The `edges` well, contain the edges of the graph. +And the `global_nodes` a set of observations for all agents. +To create our '8x1' partition we will need to partition the `unpartioned_nodes`: ```python >>> unpartioned_nodes [(hip1, ankle1, hip2, ankle2, hip3, ankle3, hip4, ankle4)] @@ -78,7 +76,7 @@ To create our '8x1' partition we will need to partition the `unpartioned_nodes` >>> partioned_nodes [(hip1,), (ankle1,), (hip2,), (ankle2,), (hip3,), (ankle3,), (hip4,), (ankle4,)] ``` -finally package the partitions and create our environment +Finally package the partitions and create our environment: ```python >>> my_agent_factorization = {"partition": partioned_nodes, "edges": edges, "globals": global_nodes} >>> gym_env = mamujoco_v0('Ant', '8x1', agent_factorization=my_agent_factorization) diff --git a/docs/envs/MaMuJoCo/ma_ant.md b/docs/envs/MaMuJoCo/ma_ant.md index 126cf4ab..a8184fa2 100644 --- a/docs/envs/MaMuJoCo/ma_ant.md +++ b/docs/envs/MaMuJoCo/ma_ant.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Ant](https://gymnasium.farama.org/environments/mujoco/ant/) +The task is [Gymansium's MuJoCo/Ant](https://gymnasium.farama.org/environments/mujoco/ant/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Ant](https://gymnasium.farama.org/environments/m The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/ant.png + :name: ant +``` | Instantiate | `env = mamujoco_v0.parallel_env("Ant", None)` | |-----------------------|------------------------------------------------------| @@ -52,27 +55,23 @@ If partitioning, is None then the environment contains a single agent with the s | Action Spaces | `{'agent_0' : Box(-1, 1, (4,), float32), 'agent_1' : Box(-1, 1, (4,), float32)}`| | Part partition | `[(hip1, ankle1, hip2, ankle2), (hip3, ankle3, hip4, ankle4)]` | -The environment is partitioned in 2 parts, the front part (containing the front legs) and the back part (containing the back legs) - -#### Agent 0 action space -front legs +The environment is partitioned in 2 parts, the front part (containing the front legs) and the back part (containing the back legs). +#### Agent 0 action space (front legs) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and front left hip | -1 | 1 | hip_1 (front_left_leg) | hinge | torque (N m) | | 1 | Torque applied on the rotor between the front left two links | -1 | 1 | angle_1 (front_left_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | -#### Agent 1 action space -back legs - +#### Agent 1 action space (back legs) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and back right hip | -1 | 1 | hip_4 (right_back_leg) | hinge | torque (N m) | | 1 | Torque applied on the rotor between the back right two links | -1 | 1 | angle_4 (right_back_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | -### elif partitioning == "2x4d": # diagonal legs together +### if partitioning == "2x4d": # diagonal legs together ```{figure} figures/ant_2x4d.png :name: ant_2x4d ``` @@ -84,7 +83,7 @@ back legs | Action Spaces | `{'agent_0' : Box(-1, 1, (4,), float32), 'agent_1' : Box(-1, 1, (4,), float32)}`| | Part partition | `[(hip1, ankle1, hip4, ankle4), (hip2, ankle2, hip3, ankle3)]` | -The environment is partitioned in 2 parts, the front part (containing the front legs) and the back part (containing the back legs) +The environment is partitioned in 2 parts, split diagonally. #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -99,7 +98,7 @@ The environment is partitioned in 2 parts, the front part (containing the front | 1 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | -### elif partitioning == "4x2": +### if partitioning == "4x2": ```{figure} figures/ant_4x2.png :name: ant_4x2 ``` @@ -111,34 +110,25 @@ The environment is partitioned in 2 parts, the front part (containing the front | Action Spaces | `{'agent_0' : Box(-1, 1, (2,), float32), 'agent_1' : Box(-1, 1, (2,), float32)}, 'agent_2' : Box(-1, 1, (2,), float32), 'agent_3' : Box(-1, 1, (2,), float32)},`| | Part partition | `[(hip1, ankle1), (hip2, ankle2), (hip3, ankle3), (hip4, ankle4)]` | -The environment is partitioned in 4 parts, with each part corresponding to a leg of the ant -#### Agent 0 action space -front left leg - +The environment is partitioned in 4 parts, with each part corresponding to a leg of the ant. +#### Agent 0 action space (front left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and front left hip | -1 | 1 | hip_1 (front_left_leg) | hinge | torque (N m) | | 1 | Torque applied on the rotor between the front left two links | -1 | 1 | angle_1 (front_left_leg) | hinge | torque (N m) | -#### Agent 1 action space -front right leg - +#### Agent 1 action space (front right leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | | 1 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | - -#### Agent 2 action space -right left leg - +#### Agent 2 action space (right left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | -#### Agent 3 action space -right back leg - +#### Agent 3 action space (right back leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and back right hip | -1 | 1 | hip_4 (right_back_leg) | hinge | torque (N m) | diff --git a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md index 7719f0fe..ba6a4d44 100644 --- a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md @@ -5,7 +5,7 @@ lastpage: # Coupled Half Cheetah -This Environment is one of the new environments introduced with MaMuJoCo +This Environment is one of the new environments introduced with MaMuJoCo. The environment consists of 2 half cheetahs coupled by an elastic tendon. @@ -14,6 +14,9 @@ The environment consists of 2 half cheetahs coupled by an elastic tendon. The action spaces is depended on the partitioning. ### if partitioning is None: +```{figure} figures/coupled_half_cheetah.png + :name: coupled_half_cheetah +``` | Instantiate | `env = mamujoco_v0.parallel_env("CoupledHalfCheetah", None)` | |-----------------------|------------------------------------------------------| @@ -41,7 +44,7 @@ If partitioning, is `None`, then the environment contains a single agent with th -### elif partitioning == "1p1": # isolate the cheetahs +### if partitioning == "1p1": # isolate the cheetahs ```{figure} figures/coupled_half_cheetah_1p1.png :name: coupled_half_cheetah_1p1 ``` @@ -54,9 +57,7 @@ If partitioning, is `None`, then the environment contains a single agent with th | Part partition | `[(bfoot0, bshin0, bthigh0, ffoot0, fshin0, fthigh0), (bfoot1, bshin1, bthigh1, ffoot1, fshin1, fthigh1),]`| The environment is partitioned in 2 parts, one part corresponding to the first cheetah and second part corresponding to the second cheetah. -#### Agent 0 action space -first cheetah - +#### Agent 0 action space (first cheetah) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the back thigh rotor of the first cheetah | -1 | 1 | bthigh0 | hinge | torque (N m) | @@ -65,10 +66,8 @@ first cheetah | 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | | 4 | Torque applied on the front shin rotor of the first cheetah | -1 | 1 | fshin0 | hinge | torque (N m) | | 5 | Torque applied on the front foot rotor of the first cheetah | -1 | 1 | ffoot0 | hinge | torque (N m) | -#### Agent 1 action space -second cheetah - +#### Agent 1 action space (second cheetah) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the back thigh rotor of the second cheetah | -1 | 1 | bthigh1 | hinge | torque (N m) | diff --git a/docs/envs/MaMuJoCo/ma_half_cheetah.md b/docs/envs/MaMuJoCo/ma_half_cheetah.md index d4cb4ea8..0facc367 100644 --- a/docs/envs/MaMuJoCo/ma_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_half_cheetah.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/) +The task is [Gymansium's MuJoCo/Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Half Cheetah](https://gymnasium.farama.org/envir The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/half_cheetah.png + :name: half_cheetah +``` | Instantiate | `env = mamujoco_v0.parallel_env("HalfCheetah", None)` | |-----------------------|------------------------------------------------------| @@ -53,24 +56,21 @@ If partitioning, is `None`, then the environment contains a single agent with th The environment is partitioned in 2 parts, the front part (containing the front leg) and the back part (containing the back leg) -#### Agent 0 action space -front leg - +#### Agent 0 action space (front leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the back thigh rotor | -1 | 1 | bthigh | hinge | torque (N m) | | 1 | Torque applied on the back shin rotor | -1 | 1 | bshin | hinge | torque (N m) | | 2 | Torque applied on the back foot rotor | -1 | 1 | bfoot | hinge | torque (N m) | -#### Agent 1 action space -back leg +#### Agent 1 action space (back leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the front thigh rotor | -1 | 1 | fthigh | hinge | torque (N m) | | 1 | Torque applied on the front shin rotor | -1 | 1 | fshin | hinge | torque (N m) | | 2 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | -### elif partitioning == "6x1": # each joint +### if partitioning == "6x1": # each joint ```{figure} figures/half_cheetah_6x1.png :name: half_cheetah_6x1 ``` @@ -83,6 +83,7 @@ back leg | Part partition | `[(bthigh,), (bshin,), (bfoot,), (fthigh,), (fshin,), (ffoot,)]`| The environment is partitioned in 6 parts, each part corresponding to a single joint + #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | diff --git a/docs/envs/MaMuJoCo/ma_hopper.md b/docs/envs/MaMuJoCo/ma_hopper.md index 2fe5123f..5c4d3312 100644 --- a/docs/envs/MaMuJoCo/ma_hopper.md +++ b/docs/envs/MaMuJoCo/ma_hopper.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) +The task is [Gymansium's MuJoCo/Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Hopper](https://gymnasium.farama.org/environment The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/hopper.png + :name: hopper +``` | Instantiate | `env = mamujoco_v0.parallel_env("Hopper", None)` | |-----------------------|------------------------------------------------------| @@ -39,7 +42,7 @@ If partitioning, is `None`, then the environment contains a single agent with th -### elif partitioning == "3x1": # each joint +### if partitioning == "3x1": # each joint ```{figure} figures/hopper_3x1.png :name: hopper_3x1 ``` @@ -51,7 +54,8 @@ If partitioning, is `None`, then the environment contains a single agent with th | Action Spaces | `{Box(-1, 1, (1,), float32)}`| | Part partition | `[(thigh_joint,), (leg_joint,), (foot_joint,)]`| -The environment is partitioned in 3 parts, each part corresponding to a single joint +The environment is partitioned in 3 parts, each part corresponding to a single joint. + #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| diff --git a/docs/envs/MaMuJoCo/ma_humanoid.md b/docs/envs/MaMuJoCo/ma_humanoid.md index 9be38d9f..bc872809 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid.md +++ b/docs/envs/MaMuJoCo/ma_humanoid.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/) +The task is [Gymansium's MuJoCo/Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Humanoid](https://gymnasium.farama.org/environme The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/humanoid.png + :name: humanoid +``` | Instantiate | `env = mamujoco_v0.parallel_env("Humanoid", None)` | |-----------------------|------------------------------------------------------| @@ -28,7 +31,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (17,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,),]` | -If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -52,7 +55,7 @@ If partitioning, is `None` then the environment contains a single agent with the -### elif partitioning == "9|8": # isolate upper and lower body +### if partitioning == "9|8": # isolate upper and lower body ```{figure} figures/humanoid_9|8.png :name: humanoid_9|8 ``` @@ -64,10 +67,9 @@ If partitioning, is `None` then the environment contains a single agent with the | Action Spaces | `{'agent_0': Box(-1, 1, (9,), float32), 'agent_1' : Box(-1, 1, (8,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,), (right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee,)]`| -The environment is partitioned in 2 parts, one part corresponding to the upper body and one part corresponding to the lower body -#### Agent 0 action space -Upper Body +The environment is partitioned in 2 parts, one part corresponding to the upper body and one part corresponding to the lower body. +#### Agent 0 action space (Upper Body) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | @@ -80,9 +82,7 @@ Upper Body | 7 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | | 8 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | -#### Agent 1 action space -Lower Body - +#### Agent 1 action space (Lower Body) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | @@ -95,6 +95,7 @@ Lower Body | 7 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes all the items of the humanoid's torso. See more at the [Gymnasium's Humanoid](https://gymnasium.farama.org/environments/mujoco/humanoid/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_humanoid_standup.md b/docs/envs/MaMuJoCo/ma_humanoid_standup.md index 257e6819..46061503 100644 --- a/docs/envs/MaMuJoCo/ma_humanoid_standup.md +++ b/docs/envs/MaMuJoCo/ma_humanoid_standup.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/) +The task is [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/e The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/humanoid.png + :name: humanoid +``` | Instantiate | `env = mamujoco_v0.parallel_env("HumanoidStandup", None)` | |-----------------------|------------------------------------------------------| @@ -28,7 +31,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (17,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,),]` | -If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space) +If partitioning, is `None` then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -52,7 +55,7 @@ If partitioning, is `None` then the environment contains a single agent with the -### elif partitioning == "9|8": # isolate upper and lower body +### if partitioning == "9|8": # isolate upper and lower body ```{figure} figures/humanoid_9|8.png :name: humanoid_9|8 ``` @@ -64,10 +67,9 @@ If partitioning, is `None` then the environment contains a single agent with the | Action Spaces | `{'agent_0': Box(-1, 1, (9,), float32), 'agent_1' : Box(-1, 1, (8,), float32)}` | | Part partition | `[(abdomen_x, abdomen_y, abdomen_z, right_shoulder1, right_shoulder2, right_elbow, left_shoulder1, left_shoulder2, left_elbow,), (right_hip_x, right_hip_y, right_hip_z, right_knee, left_hip_x, left_hip_y, left_hip_z, left_knee,)]`| -The environment is partitioned in 2 parts, one part corresponding to the upper body and one part corresponding to the lower body -#### Agent 0 action space -Upper Body +The environment is partitioned in 2 parts, one part corresponding to the upper body and one part corresponding to the lower body. +#### Agent 0 action space (Upper Body) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | @@ -80,9 +82,7 @@ Upper Body | 7 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | | 8 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | -#### Agent 1 action space -Lower Body - +#### Agent 1 action space (Lower Body) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | @@ -95,6 +95,7 @@ Lower Body | 7 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes all the items of the Humanoid's torso. See more at the [Gymnasium's Humanoid Standup](https://gymnasium.farama.org/environments/mujoco/humanoid_standup/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_pusher.md b/docs/envs/MaMuJoCo/ma_pusher.md index 161da9f6..eda2b629 100644 --- a/docs/envs/MaMuJoCo/ma_pusher.md +++ b/docs/envs/MaMuJoCo/ma_pusher.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/) +The task is [Gymansium's MuJoCo/Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Pusher](https://gymnasium.farama.org/environment The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/pusher.png + :name: pusher +``` | Instantiate | `env = mamujoco_v0.parallel_env("Pusher", None)` | |-----------------------|------------------------------------------------------| @@ -28,7 +31,7 @@ The action spaces is depended on the partitioning | Action Spaces | `{'agent_0' : Box(-1, 1, (7,), float32)}` | | Part partition | `[(r_shoulder_pan_joint, r_shoulder_lift_joint, r_upper_arm_roll_joint, r_elbow_flex_joint, r_forearm_roll_joint, r_wrist_flex_joint, r_wrist_roll_joint,),]` | -If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/#action-space) +If partitioning, is None then the environment contains a single agent with the same action space as [Gymansium's MuJoCo/Pusher](https://gymnasium.farama.org/environments/mujoco/pusher/#action-space). | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| @@ -42,7 +45,7 @@ If partitioning, is None then the environment contains a single agent with the s -### elif partitioning == "3p": +### if partitioning == "3p": ```{figure} figures/pusher_3p.png :name: pusher_3p ``` @@ -54,30 +57,28 @@ If partitioning, is None then the environment contains a single agent with the s | Action Spaces | `{'agent_0': Box(-1, 1, (3,), float32), 'agent_1' : Box(-1, 1, (1,), float32), 'agent_2': Box(-1, 1, (1,), float32)}` | | Part partition | `[(r_shoulder_pan_joint, r_shoulder_lift_joint, r_upper_arm_roll_joint,), (r_elbow_flex_joint,), (r_forearm_roll_joint, r_wrist_flex_joint, r_wrist_roll_joint),]`| -The environment is partitioned in 3 parts, one part corresponding to the shoulder one part corresponding to the elbow and one part to the wrist -#### Agent 0 action space -Shoulder - +The environment is partitioned in 3 parts, one part corresponding to the shoulder, one part corresponding to the elbow, and one part to the wrist. +#### Agent 0 action space (Shoulder) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Rotation of the panning the shoulder | -2 | 2 | r_shoulder_pan_joint | hinge | torque (N m) | | 1 | Rotation of the shoulder lifting joint | -2 | 2 | r_shoulder_lift_joint | hinge | torque (N m) | | 2 | Rotation of the shoulder rolling joint | -2 | 2 | r_upper_arm_roll_joint | hinge | torque (N m) | -#### Agent 1 action space -Elbow +#### Agent 1 action space (Elbow) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Rotation of hinge joint that flexed the elbow | -2 | 2 | r_elbow_flex_joint | hinge | torque (N m) | -#### Agent w action space -Wrist +#### Agent 2 action space (Wrist) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Rotation of hinge that rolls the forearm | -2 | 2 | r_forearm_roll_joint | hinge | torque (N m) | | 1 | Rotation of flexing the wrist | -2 | 2 | r_wrist_flex_joint | hinge | torque (N m) | | 2 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | + + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position of the pusher's object and the position of the goal. See more at the [Gymnasium's Pusher](https://gymnasium.farama.org/environments/mujoco/reacher/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_reacher.md b/docs/envs/MaMuJoCo/ma_reacher.md index 0ea29c0f..a0f1406d 100644 --- a/docs/envs/MaMuJoCo/ma_reacher.md +++ b/docs/envs/MaMuJoCo/ma_reacher.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/) +The task is [Gymansium's MuJoCo/Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Reacher](https://gymnasium.farama.org/environmen The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/reacher.png + :name: reacher +``` | Instantiate | `env = mamujoco_v0.parallel_env("Reacher", None)` | |-----------------------|------------------------------------------------------| @@ -37,7 +40,7 @@ If partitioning, is `None`, then the environment contains a single agent with th -### elif partitioning == "2x1": +### if partitioning == "2x1": ```{figure} figures/reacher_2x1.png :name: reacher_2x1 ``` @@ -49,7 +52,7 @@ If partitioning, is `None`, then the environment contains a single agent with th | Action Spaces | `{'agent_0': Box(-1, 1, (1,), float32), 'agent_1' : Box(-1, 1, (1,), float32)}` | | Part partition | `[(joint0,), (joint1,)]`| -The environment is partitioned in 2 parts, one part corresponding to the first joint and one part corresponding to the second joint +The environment is partitioned in 2 parts, one part corresponding to the first joint, and one part corresponding to the second joint. #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|---------------------------------------------------------------------------------|-------------|-------------|--------------------------|-------|------| diff --git a/docs/envs/MaMuJoCo/ma_single.md b/docs/envs/MaMuJoCo/ma_single.md index 712803ad..dab60914 100644 --- a/docs/envs/MaMuJoCo/ma_single.md +++ b/docs/envs/MaMuJoCo/ma_single.md @@ -5,9 +5,9 @@ lastpage: # Single Action Environments -MaMuJoCo also support single action [Gymansium/MuJoCo/](https://gymnasium.farama.org/environments/mujoco/) environment such as [Gymnasium/Mujoco/InvertedPendulum](https://gymnasium.farama.org/environments/mujoco/inverted_pendulum/) and [Gymnasium/Mujoco/InvertedDoublePendulum](https://gymnasium.farama.org/environments/mujoco/inverted_double_pendulum/) +MaMuJoCo also supports single action [Gymansium/MuJoCo/](https://gymnasium.farama.org/environments/mujoco/) environments such as [Gymnasium/Mujoco/InvertedPendulum](https://gymnasium.farama.org/environments/mujoco/inverted_pendulum/) and [Gymnasium/Mujoco/InvertedDoublePendulum](https://gymnasium.farama.org/environments/mujoco/inverted_double_pendulum/). -And can be instantiated (without a partition) +And can be instantiated (without a partition): `env = mamujoco_v0.parallel_env("InvertedDoublePendulum", None)` @@ -15,9 +15,12 @@ And can be instantiated (without a partition) In which case, they simply are the same environments with a single agent using the `PettingZoo` APIs. +The Purpose of these is to allow researchers to debug multi-agent learning algorithms. + + ## Action Space -The action spaces is depended on the partitioning +The action spaces is depended on the partitioning. ## Observation Space The agent receives the same observations as the single agent Gymnasium environment. diff --git a/docs/envs/MaMuJoCo/ma_swimmer.md b/docs/envs/MaMuJoCo/ma_swimmer.md index 7fbfb269..6c4a2bb4 100644 --- a/docs/envs/MaMuJoCo/ma_swimmer.md +++ b/docs/envs/MaMuJoCo/ma_swimmer.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/) +The task is [Gymansium's MuJoCo/Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Swimmer](https://gymnasium.farama.org/environmen The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/swimmer.png + :name: swimmer +``` | Instantiate | `env = mamujoco_v0.parallel_env("Swimmer", None)` | |-----------------------|------------------------------------------------------| @@ -37,7 +40,7 @@ If partitioning, is `None` then the environment contains a single agent with the -### elif partitioning == "2x1": # isolate upper and lower body +### if partitioning == "2x1": # isolate upper and lower body ```{figure} figures/swimmer_2x1.png :name: swimmer_2x1 ``` @@ -49,7 +52,8 @@ If partitioning, is `None` then the environment contains a single agent with the | Action Spaces | `{'agent_0': Box(-1, 1, (1,), float32), 'agent_1' : Box(-1, 1, (1,), float32)}` | | Part partition | `[(joint0,), (joint1,)]`| -The environment is partitioned in 2 parts, one part corresponding to the first joint and one part corresponding to the second joint +The environment is partitioned in 2 parts, one part corresponding to the first joint, and one part corresponding to the second joint. + #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| @@ -59,6 +63,8 @@ The environment is partitioned in 2 parts, one part corresponding to the first j |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Torque applied on the second rotor | -1 | 1 | motor2_rot | hinge | torque (N m) | + + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the swimmer's tip. See more at the [Gymnasium's Swimmer](https://gymnasium.farama.org/environments/mujoco/swimmer/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_walker2d.md b/docs/envs/MaMuJoCo/ma_walker2d.md index 5f8a386c..359af19c 100644 --- a/docs/envs/MaMuJoCo/ma_walker2d.md +++ b/docs/envs/MaMuJoCo/ma_walker2d.md @@ -12,7 +12,7 @@ lastpage: This Environment is part of [MaMuJoCo](https://robotics.farama.org/envs/MaMuJoCo/) environments. Please read that page first for general information. -The task is [Gymansium's MuJoCo/Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/) +The task is [Gymansium's MuJoCo/Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/). @@ -20,6 +20,9 @@ The task is [Gymansium's MuJoCo/Walker2D](https://gymnasium.farama.org/environme The action spaces is depended on the partitioning ### if partitioning is None: +```{figure} figures/walker2d.png + :name: walker2d +``` | Instantiate | `env = mamujoco_v0.parallel_env("Walker2D", None)` | |-----------------------|------------------------------------------------------| @@ -41,7 +44,7 @@ If partitioning, is `None` then the environment contains a single agent with the -### elif partitioning == "2x3": # isolate right and left foot +### if partitioning == "2x3": # isolate right and left foot ```{figure} figures/walker2d_2x3.png :name: walker2d_2x3 ``` @@ -53,18 +56,15 @@ If partitioning, is `None` then the environment contains a single agent with the | Action Spaces | `{'agent_0': Box(-1, 1, (3,), float32), 'agent_1' : Box(-1, 1, (3,), float32)}` | | Part partition | `[(foot_joint, leg_joint, thigh_joint), (foot_left_joint, leg_left_joint, thigh_left_joint,),]`| -The environment is partitioned in 2 parts, one part corresponding to the right leg and one part corresponding to the left leg -#### Agent 0 action space -right leg +The environment is partitioned in 2 parts, one part corresponding to the right leg, and one part corresponding to the left leg. +#### Agent 0 action space (right leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | -#### Agent 1 action space -left leg - +#### Agent 1 action space (left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| | 0 | Torque applied on the left thigh rotor | -1 | 1 | thigh_left_joint | hinge | torque (N m) | From e0d7e06b557212f3f22c981b16ee9d4676025d76 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 12 Jan 2023 06:15:39 +0200 Subject: [PATCH 010/160] minor formating changes --- docs/envs/MaMuJoCo/ma_ant.md | 6 ++++++ docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md | 2 ++ docs/envs/MaMuJoCo/ma_half_cheetah.md | 1 + docs/envs/MaMuJoCo/ma_hopper.md | 1 + docs/envs/MaMuJoCo/ma_pusher.md | 1 + docs/envs/MaMuJoCo/ma_reacher.md | 2 ++ docs/envs/MaMuJoCo/ma_walker2d.md | 1 + 7 files changed, 14 insertions(+) diff --git a/docs/envs/MaMuJoCo/ma_ant.md b/docs/envs/MaMuJoCo/ma_ant.md index a8184fa2..d15b3ae7 100644 --- a/docs/envs/MaMuJoCo/ma_ant.md +++ b/docs/envs/MaMuJoCo/ma_ant.md @@ -43,6 +43,7 @@ If partitioning, is None then the environment contains a single agent with the s | 5 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | + ### if partitioning == "2x4": # neighboring legs together (front and back) ```{figure} figures/ant_2x4.png :name: ant_2x4 @@ -64,6 +65,7 @@ The environment is partitioned in 2 parts, the front part (containing the front | 1 | Torque applied on the rotor between the front left two links | -1 | 1 | angle_1 (front_left_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | + #### Agent 1 action space (back legs) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -71,6 +73,7 @@ The environment is partitioned in 2 parts, the front part (containing the front | 1 | Torque applied on the rotor between the back right two links | -1 | 1 | angle_4 (right_back_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | + ### if partitioning == "2x4d": # diagonal legs together ```{figure} figures/ant_2x4d.png :name: ant_2x4d @@ -98,6 +101,7 @@ The environment is partitioned in 2 parts, split diagonally. | 1 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | | 2 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 3 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | + ### if partitioning == "4x2": ```{figure} figures/ant_4x2.png :name: ant_4x2 @@ -111,6 +115,7 @@ The environment is partitioned in 2 parts, split diagonally. | Part partition | `[(hip1, ankle1), (hip2, ankle2), (hip3, ankle3), (hip4, ankle4)]` | The environment is partitioned in 4 parts, with each part corresponding to a leg of the ant. + #### Agent 0 action space (front left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -122,6 +127,7 @@ The environment is partitioned in 4 parts, with each part corresponding to a leg | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | | 0 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | | 1 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | + #### Agent 2 action space (right left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | diff --git a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md index ba6a4d44..1daed6a4 100644 --- a/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md @@ -57,6 +57,7 @@ If partitioning, is `None`, then the environment contains a single agent with th | Part partition | `[(bfoot0, bshin0, bthigh0, ffoot0, fshin0, fthigh0), (bfoot1, bshin1, bthigh1, ffoot1, fshin1, fthigh1),]`| The environment is partitioned in 2 parts, one part corresponding to the first cheetah and second part corresponding to the second cheetah. + #### Agent 0 action space (first cheetah) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | @@ -78,6 +79,7 @@ The environment is partitioned in 2 parts, one part corresponding to the first c | 5 | Torque applied on the front foot rotor of the second cheetah | -1 | 1 | ffoot1 | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items in each cheetah's top. diff --git a/docs/envs/MaMuJoCo/ma_half_cheetah.md b/docs/envs/MaMuJoCo/ma_half_cheetah.md index 0facc367..fbcba92d 100644 --- a/docs/envs/MaMuJoCo/ma_half_cheetah.md +++ b/docs/envs/MaMuJoCo/ma_half_cheetah.md @@ -110,6 +110,7 @@ The environment is partitioned in 6 parts, each part corresponding to a single j | 0 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the half cheetah's tip. See more at the [Gymnasium's Half Cheetah](https://gymnasium.farama.org/environments/mujoco/half_cheetah/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_hopper.md b/docs/envs/MaMuJoCo/ma_hopper.md index 5c4d3312..da94f359 100644 --- a/docs/envs/MaMuJoCo/ma_hopper.md +++ b/docs/envs/MaMuJoCo/ma_hopper.md @@ -70,6 +70,7 @@ The environment is partitioned in 3 parts, each part corresponding to a single j | 0 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position and velocity items of the hopper's top. See more at the [Gymnasium's Hopper](https://gymnasium.farama.org/environments/mujoco/hopper/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_pusher.md b/docs/envs/MaMuJoCo/ma_pusher.md index eda2b629..d00e2f6c 100644 --- a/docs/envs/MaMuJoCo/ma_pusher.md +++ b/docs/envs/MaMuJoCo/ma_pusher.md @@ -58,6 +58,7 @@ If partitioning, is None then the environment contains a single agent with the s | Part partition | `[(r_shoulder_pan_joint, r_shoulder_lift_joint, r_upper_arm_roll_joint,), (r_elbow_flex_joint,), (r_forearm_roll_joint, r_wrist_flex_joint, r_wrist_roll_joint),]`| The environment is partitioned in 3 parts, one part corresponding to the shoulder, one part corresponding to the elbow, and one part to the wrist. + #### Agent 0 action space (Shoulder) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| diff --git a/docs/envs/MaMuJoCo/ma_reacher.md b/docs/envs/MaMuJoCo/ma_reacher.md index a0f1406d..77943140 100644 --- a/docs/envs/MaMuJoCo/ma_reacher.md +++ b/docs/envs/MaMuJoCo/ma_reacher.md @@ -53,6 +53,7 @@ If partitioning, is `None`, then the environment contains a single agent with th | Part partition | `[(joint0,), (joint1,)]`| The environment is partitioned in 2 parts, one part corresponding to the first joint, and one part corresponding to the second joint. + #### Agent 0 action space | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|---------------------------------------------------------------------------------|-------------|-------------|--------------------------|-------|------| @@ -64,6 +65,7 @@ The environment is partitioned in 2 parts, one part corresponding to the first j | 0 | Torque applied at the second hinge (connecting the two links) | -1 | 1 | joint1 | hinge | torque (N m) | + ## Observation Space Besides the local observation of each agent (which depend on their parts of the agent, the observation categories and the observation depth), each agent also observes the position of the reacher's target object. See more at the [Gymnasium's Reacher](https://gymnasium.farama.org/environments/mujoco/reacher/#observation-space). diff --git a/docs/envs/MaMuJoCo/ma_walker2d.md b/docs/envs/MaMuJoCo/ma_walker2d.md index 359af19c..2c67d53b 100644 --- a/docs/envs/MaMuJoCo/ma_walker2d.md +++ b/docs/envs/MaMuJoCo/ma_walker2d.md @@ -64,6 +64,7 @@ The environment is partitioned in 2 parts, one part corresponding to the right l | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | + #### Agent 1 action space (left leg) | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| From a631fbea8cd0d0212aab0e75bb3741230a884b3d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 12 Jan 2023 18:54:38 +0200 Subject: [PATCH 011/160] add `kwargs` --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 95fd642e..a8a2fa25 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -79,6 +79,7 @@ def __init__( local_categories: list[list[str]] | None = None, global_categories: tuple[str, ...] | None = None, render_mode: str | None = None, + **kwargs, ): """Init. @@ -101,6 +102,8 @@ def __init__( The default is; local_categories[0] render_mode: see [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/), valid values: 'human', 'rgb_array', 'depth_array' + kwargs: Additional arguments passed to the [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) environment, + Note: arguments that change the observation space will not work. Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values) """ @@ -109,7 +112,7 @@ def __init__( # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` if scenario in _MUJOCO_GYM_ENVIROMENTS: self.single_agent_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv = ( - gymnasium.make(scenario, render_mode=render_mode) + gymnasium.make(scenario, **kwargs, render_mode=render_mode) ) elif scenario in ["ManySegmentAnt-v4"]: assert isinstance(agent_conf, str) From 22d54bc962a5909e846cb6351966e4ea125f0e13 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 25 Jan 2023 01:35:41 +0200 Subject: [PATCH 012/160] add `mujuco-v5` (init) --- gymnasium_robotics/envs/mujoco/ant_v5.py | 363 +++++++++++++++++ gymnasium_robotics/envs/mujoco/humanoid_v5.py | 374 ++++++++++++++++++ .../envs/mujoco/humanoidstandup_v5.py | 276 +++++++++++++ .../mujoco/inverted_double_pendulum_v5.py | 179 +++++++++ gymnasium_robotics/envs/mujoco/reacher_v5.py | 191 +++++++++ 5 files changed, 1383 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/ant_v5.py create mode 100644 gymnasium_robotics/envs/mujoco/humanoid_v5.py create mode 100644 gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py create mode 100644 gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py create mode 100644 gymnasium_robotics/envs/mujoco/reacher_v5.py diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py new file mode 100644 index 00000000..1e618f58 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -0,0 +1,363 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "distance": 4.0, +} + + +class AntEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is based on the environment introduced by Schulman, + Moritz, Levine, Jordan and Abbeel in ["High-Dimensional Continuous Control + Using Generalized Advantage Estimation"](https://arxiv.org/abs/1506.02438). + The ant is a 3D robot consisting of one torso (free rotational body) with + four legs attached to it with each leg having two links. The goal is to + coordinate the four legs to move in the forward (right) direction by applying + torques on the eight hinges connecting the two links of each leg and the torso + (nine parts and eight hinges). + + ## Action Space + The action space is a `Box(-1, 1, (8,), float32)`. An action represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | + | 0 | Torque applied on the rotor between the torso and back right hip | -1 | 1 | hip_4 (right_back_leg) | hinge | torque (N m) | + | 1 | Torque applied on the rotor between the back right two links | -1 | 1 | angle_4 (right_back_leg) | hinge | torque (N m) | + | 2 | Torque applied on the rotor between the torso and front left hip | -1 | 1 | hip_1 (front_left_leg) | hinge | torque (N m) | + | 3 | Torque applied on the rotor between the front left two links | -1 | 1 | angle_1 (front_left_leg) | hinge | torque (N m) | + | 4 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | + | 5 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | + | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | + | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | + + ## Observation Space + + Observations consist of positional values of different body parts of the ant, + followed by the velocities of those individual parts (their derivatives) with all + the positions ordered before all the velocities. + + By default, observations do not include the x- and y-coordinates of the ant's torso. These may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will have 29 dimensions where the first two dimensions + represent the x- and y- coordinates of the ant's torso. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates + of the torso will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. + + However, by default, an observation is a `ndarray` with shape `(27,)` + where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + |-----|--------------------------------------------------------------|--------|--------|----------------------------------------|-------|--------------------------| + | 0 | z-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | + | 1 | x-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | + | 2 | y-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | + | 3 | z-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | + | 4 | w-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | + | 5 | angle between torso and first link on front left | -Inf | Inf | hip_1 (front_left_leg) | hinge | angle (rad) | + | 6 | angle between the two links on the front left | -Inf | Inf | ankle_1 (front_left_leg) | hinge | angle (rad) | + | 7 | angle between torso and first link on front right | -Inf | Inf | hip_2 (front_right_leg) | hinge | angle (rad) | + | 8 | angle between the two links on the front right | -Inf | Inf | ankle_2 (front_right_leg) | hinge | angle (rad) | + | 9 | angle between torso and first link on back left | -Inf | Inf | hip_3 (back_leg) | hinge | angle (rad) | + | 10 | angle between the two links on the back left | -Inf | Inf | ankle_3 (back_leg) | hinge | angle (rad) | + | 11 | angle between torso and first link on back right | -Inf | Inf | hip_4 (right_back_leg) | hinge | angle (rad) | + | 12 | angle between the two links on the back right | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | + | 13 | x-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | + | 14 | y-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | + | 15 | z-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | + | 16 | x-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | + | 17 | y-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | + | 18 | z-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | + | 19 | angular velocity of angle between torso and front left link | -Inf | Inf | hip_1 (front_left_leg) | hinge | angle (rad) | + | 20 | angular velocity of the angle between front left links | -Inf | Inf | ankle_1 (front_left_leg) | hinge | angle (rad) | + | 21 | angular velocity of angle between torso and front right link | -Inf | Inf | hip_2 (front_right_leg) | hinge | angle (rad) | + | 22 | angular velocity of the angle between front right links | -Inf | Inf | ankle_2 (front_right_leg) | hinge | angle (rad) | + | 23 | angular velocity of angle between torso and back left link | -Inf | Inf | hip_3 (back_leg) | hinge | angle (rad) | + | 24 | angular velocity of the angle between back left links | -Inf | Inf | ankle_3 (back_leg) | hinge | angle (rad) | + | 25 | angular velocity of angle between torso and back right link | -Inf | Inf | hip_4 (right_back_leg) | hinge | angle (rad) | + | 26 |angular velocity of the angle between back right links | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | + + + If `use_contact_forces` is `True` then the observation space is extended by 14*6 = 84 elements, which are contact forces + (external forces - force x, y, z and torque x, y, z) applied to the + center of mass of each of the links. The 14 links are: the ground link, + the torso link, and 3 links for each leg (1 + 1 + 12) with the 6 external forces. + + The (x,y,z) coordinates are translational DOFs while the orientations are rotational + DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). + + + **Note:** Ant-v4 environment no longer has the following contact forces issue. + If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 results + in the contact forces always being 0. As such we recommend to use a Mujoco-Py version < 2.0 + when using the Ant environment if you would like to report results with contact forces (if + contact forces are not used in your experiments, you can use version > 2.0). + + ## Rewards + The reward consists of three parts: + - *healthy_reward*: Every timestep that the ant is healthy (see definition in section "Episode Termination"), it gets a reward of fixed value `healthy_reward` + - *forward_reward*: A reward of moving forward which is measured as + *(x-coordinate before action - x-coordinate after action)/dt*. *dt* is the time + between actions and is dependent on the `frame_skip` parameter (default is 5), + where the frametime is 0.01 - making the default *dt = 5 * 0.01 = 0.05*. + This reward would be positive if the ant moves forward (in positive x direction). + - *ctrl_cost*: A negative reward for penalising the ant if it takes actions + that are too large. It is measured as *`ctrl_cost_weight` * sum(action2)* + where *`ctr_cost_weight`* is a parameter set for the control and has a default value of 0.5. + - *contact_cost*: A negative reward for penalising the ant if the external contact + force is too large. It is calculated *`contact_cost_weight` * sum(clip(external contact + force to `contact_force_range`)2)*. + + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*. + + But if `use_contact_forces=True` + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost*. + + In either case `info` will also contain the individual reward terms. + + ## Starting State + All observations start in state + (0.0, 0.0, 0.75, 1.0, 0.0 ... 0.0) with a uniform noise in the range + of [-`reset_noise_scale`, `reset_noise_scale`] added to the positional values and standard normal noise + with mean 0 and standard deviation `reset_noise_scale` added to the velocity values for + stochasticity. Note that the initial z coordinate is intentionally selected + to be slightly high, thereby indicating a standing up ant. The initial orientation + is designed to make it face forward as well. + + ## Episode End + The ant is said to be unhealthy if any of the following happens: + + 1. Any of the state space values is no longer finite + 2. The z-coordinate of the torso is **not** in the closed interval given by `healthy_z_range` (defaults to [0.2, 1.0]) + + If `terminate_when_unhealthy=True` is passed during construction (which is the default), + the episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 1000 timesteps + 2. Termination: The ant is unhealthy + + If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + env = gym.make('Ant-v2') + ``` + + v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Ant-v4', ctrl_cost_weight=0.1, ...) + ``` + + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|-------------------------------| + | `xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | + | `ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | + | `use_contact_forces` | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | + | `contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | + | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + | `terminate_when_unhealthy` | **bool**| `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | + | `healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | + | `contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | + | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation`| **bool** | `True`| Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + ## Version History + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3, also removed contact forces from the default observation space (new variable `use_contact_forces=True` can restore them) + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 20, + } + + def __init__( + self, + xml_file="ant.xml", + ctrl_cost_weight=0.5, + use_contact_forces=True, + contact_cost_weight=5e-4, + healthy_reward=1.0, + terminate_when_unhealthy=True, + healthy_z_range=(0.2, 1.0), + contact_force_range=(-1.0, 1.0), + reset_noise_scale=0.1, + exclude_current_positions_from_observation=True, + **kwargs + ): + utils.EzPickle.__init__( + self, + xml_file, + ctrl_cost_weight, + use_contact_forces, + contact_cost_weight, + healthy_reward, + terminate_when_unhealthy, + healthy_z_range, + contact_force_range, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs + ) + + self._ctrl_cost_weight = ctrl_cost_weight + self._contact_cost_weight = contact_cost_weight + + self._healthy_reward = healthy_reward + self._terminate_when_unhealthy = terminate_when_unhealthy + self._healthy_z_range = healthy_z_range + + self._contact_force_range = contact_force_range + + self._reset_noise_scale = reset_noise_scale + + self._use_contact_forces = use_contact_forces + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + obs_shape = 27 + if not exclude_current_positions_from_observation: + obs_shape += 2 + if use_contact_forces: + obs_shape += 78 + + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 + ) + + MujocoEnv.__init__( + self, + xml_file, + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs + ) + + @property + def healthy_reward(self): + return ( + float(self.is_healthy or self._terminate_when_unhealthy) + * self._healthy_reward + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) + return control_cost + + @property + def contact_forces(self): + raw_contact_forces = self.data.cfrc_ext + min_value, max_value = self._contact_force_range + contact_forces = np.clip(raw_contact_forces, min_value, max_value) + return contact_forces + + @property + def contact_cost(self): + contact_cost = self._contact_cost_weight * np.sum( + np.square(self.contact_forces) + ) + return contact_cost + + @property + def is_healthy(self): + state = self.state_vector() + min_z, max_z = self._healthy_z_range + is_healthy = np.isfinite(state).all() and min_z <= state[2] <= max_z + return is_healthy + + @property + def terminated(self): + terminated = not self.is_healthy if self._terminate_when_unhealthy else False + return terminated + + def step(self, action): + xy_position_before = self.get_body_com("torso")[:2].copy() + self.do_simulation(action, self.frame_skip) + xy_position_after = self.get_body_com("torso")[:2].copy() + + xy_velocity = (xy_position_after - xy_position_before) / self.dt + x_velocity, y_velocity = xy_velocity + + forward_reward = x_velocity + healthy_reward = self.healthy_reward + + rewards = forward_reward + healthy_reward + + costs = ctrl_cost = self.control_cost(action) + + terminated = self.terminated + observation = self._get_obs() + info = { + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, + "reward_survive": healthy_reward, + "x_position": xy_position_after[0], + "y_position": xy_position_after[1], + "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), + "x_velocity": x_velocity, + "y_velocity": y_velocity, + "forward_reward": forward_reward, + } + # if self._use_contact_forces: + contact_cost = self.contact_cost + costs += contact_cost + info["reward_ctrl"] = -contact_cost + + reward = rewards - costs + + if self.render_mode == "human": + self.render() + return observation, reward, terminated, False, info + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = self.data.qvel.flat.copy() + + if self._exclude_current_positions_from_observation: + position = position[2:] + + if self._use_contact_forces: + contact_force = self.contact_forces[1:].flat.copy() + assert len(contact_force) == 78 + return np.concatenate((position, velocity, contact_force)) + else: + return np.concatenate((position, velocity)) + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = ( + self.init_qvel + + self._reset_noise_scale * self.np_random.standard_normal(self.model.nv) + ) + self.set_state(qpos, qvel) + + observation = self._get_obs() + + return observation diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py new file mode 100644 index 00000000..d827d7b9 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -0,0 +1,374 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 1, + "distance": 4.0, + "lookat": np.array((0.0, 0.0, 2.0)), + "elevation": -20.0, +} + + +def mass_center(model, data): + mass = np.expand_dims(model.body_mass, axis=1) + xpos = data.xipos + return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() + + +class HumanoidEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is based on the environment introduced by Tassa, Erez and Todorov + in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). + The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a pair of + legs and arms. The legs each consist of two links, and so the arms (representing the knees and + elbows respectively). The goal of the environment is to walk forward as fast as possible without falling over. + + ## Action Space + The action space is a `Box(-1, 1, (17,), float32)`. An action represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|----------------------|---------------|----------------|---------------------------------------|-------|------| + | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | + | 1 | Torque applied on the hinge in the z-coordinate of the abdomen | -0.4 | 0.4 | abdomen_z | hinge | torque (N m) | + | 2 | Torque applied on the hinge in the x-coordinate of the abdomen | -0.4 | 0.4 | abdomen_x | hinge | torque (N m) | + | 3 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | + | 4 | Torque applied on the rotor between torso/abdomen and the right hip (z-coordinate) | -0.4 | 0.4 | right_hip_z (right_thigh) | hinge | torque (N m) | + | 5 | Torque applied on the rotor between torso/abdomen and the right hip (y-coordinate) | -0.4 | 0.4 | right_hip_y (right_thigh) | hinge | torque (N m) | + | 6 | Torque applied on the rotor between the right hip/thigh and the right shin | -0.4 | 0.4 | right_knee | hinge | torque (N m) | + | 7 | Torque applied on the rotor between torso/abdomen and the left hip (x-coordinate) | -0.4 | 0.4 | left_hip_x (left_thigh) | hinge | torque (N m) | + | 8 | Torque applied on the rotor between torso/abdomen and the left hip (z-coordinate) | -0.4 | 0.4 | left_hip_z (left_thigh) | hinge | torque (N m) | + | 9 | Torque applied on the rotor between torso/abdomen and the left hip (y-coordinate) | -0.4 | 0.4 | left_hip_y (left_thigh) | hinge | torque (N m) | + | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | + | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | + | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | + | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | + | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | + | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | + | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | + + ## Observation Space + + Observations consist of positional values of different body parts of the Humanoid, + followed by the velocities of those individual parts (their derivatives) with all the + positions ordered before all the velocities. + + By default, observations do not include the x- and y-coordinates of the torso. These may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will have 378 dimensions where the first two dimensions + represent the x- and y-coordinates of the torso. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates + will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. + + However, by default, the observation is a `ndarray` with shape `(376,)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | + | 0 | z-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | + | 1 | x-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 2 | y-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 3 | z-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 4 | w-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 5 | z-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | angle (rad) | + | 6 | y-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | angle (rad) | + | 7 | x-angle of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | angle (rad) | + | 8 | x-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | angle (rad) | + | 9 | z-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | angle (rad) | + | 19 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | + | 11 | angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | angle (rad) | + | 12 | x-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | angle (rad) | + | 13 | z-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | angle (rad) | + | 14 | y-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | angle (rad) | + | 15 | angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | angle (rad) | + | 16 | coordinate-1 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | angle (rad) | + | 17 | coordinate-2 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | angle (rad) | + | 18 | angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | angle (rad) | + | 19 | coordinate-1 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | angle (rad) | + | 20 | coordinate-2 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | angle (rad) | + | 21 | angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | angle (rad) | + | 22 | x-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 23 | y-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 24 | z-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 25 | x-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 26 | y-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 27 | z-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 28 | z-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | anglular velocity (rad/s) | + | 29 | y-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | anglular velocity (rad/s) | + | 30 | x-coordinate of angular velocity of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | aanglular velocity (rad/s) | + | 31 | x-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | anglular velocity (rad/s) | + | 32 | z-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | anglular velocity (rad/s) | + | 33 | y-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | anglular velocity (rad/s) | + | 34 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | + | 35 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | + | 36 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | + | 37 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | + | 38 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | + | 39 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | + | 40 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | + | 41 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | + | 42 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | + | 43 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | + | 44 | angular velocitty of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | + + Additionally, after all the positional and velocity based values in the table, + the observation contains (in order): + - *cinert:* Mass and inertia of a single rigid body relative to the center of mass + (this is an intermediate result of transition). It has shape 14*10 (*nbody * 10*) + and hence adds to another 140 elements in the state space. + - *cvel:* Center of mass based velocity. It has shape 14 * 6 (*nbody * 6*) and hence + adds another 84 elements in the state space + - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape + `(23,)` *(nv * 1)* and hence adds another 23 elements to the state space. + - *cfrc_ext:* This is the center of mass based external force on the body. It has shape + 14 * 6 (*nbody * 6*) and hence adds to another 84 elements in the state space. + where *nbody* stands for the number of bodies in the robot and *nv* stands for the + number of degrees of freedom (*= dim(qvel)*) + + The (x,y,z) coordinates are translational DOFs while the orientations are rotational + DOFs expressed as quaternions. One can read more about free joints on the + [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). + + **Note:** Humanoid-v4 environment no longer has the following contact forces issue. + If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 + results in the contact forces always being 0. As such we recommend to use a Mujoco-Py + version < 2.0 when using the Humanoid environment if you would like to report results + with contact forces (if contact forces are not used in your experiments, you can use + version > 2.0). + + ## Rewards + The reward consists of three parts: + - *healthy_reward*: Every timestep that the humanoid is alive (see section Episode Termination for definition), it gets a reward of fixed value `healthy_reward` + - *forward_reward*: A reward of walking forward which is measured as *`forward_reward_weight` * + (average center of mass before action - average center of mass after action)/dt*. + *dt* is the time between actions and is dependent on the frame_skip parameter + (default is 5), where the frametime is 0.003 - making the default *dt = 5 * 0.003 = 0.015*. + This reward would be positive if the humanoid walks forward (in positive x-direction). The calculation + for the center of mass is defined in the `.py` file for the Humanoid. + - *ctrl_cost*: A negative reward for penalising the humanoid if it has too + large of a control force. If there are *nu* actuators/controls, then the control has + shape `nu x 1`. It is measured as *`ctrl_cost_weight` * sum(control2)*. + - *contact_cost*: A negative reward for penalising the humanoid if the external + contact force is too large. It is calculated by clipping + *`contact_cost_weight` * sum(external contact force2)* to the interval specified by `contact_cost_range`. + + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost* and `info` will also contain the individual reward terms + + ## Starting State + All observations start in state + (0.0, 0.0, 1.4, 1.0, 0.0 ... 0.0) with a uniform noise in the range + of [-`reset_noise_scale`, `reset_noise_scale`] added to the positional and velocity values (values in the table) + for stochasticity. Note that the initial z coordinate is intentionally + selected to be high, thereby indicating a standing up humanoid. The initial + orientation is designed to make it face forward as well. + + ## Episode End + The humanoid is said to be unhealthy if the z-position of the torso is no longer contained in the + closed interval specified by the argument `healthy_z_range`. + + If `terminate_when_unhealthy=True` is passed during construction (which is the default), + the episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 1000 timesteps + 3. Termination: The humanoid is unhealthy + + If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + env = gym.make('Humanoid-v4') + ``` + + v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Humanoid-v4', ctrl_cost_weight=0.1, ....) + ``` + + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"humanoid.xml"` | Path to a MuJoCo model | + | `forward_reward_weight` | **float** | `1.25` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ term (see section on reward) | + | `contact_cost_weight` | **float** | `5e-7` | Weight for _contact_cost_ term (see section on reward) | + | `healthy_reward` | **float** | `5.0` | Constant reward given if the humanoid is "healthy" after timestep | + | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | + | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | + | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + ## Version History + + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 67, + } + + def __init__( + self, + forward_reward_weight=1.25, + ctrl_cost_weight=0.1, + healthy_reward=5.0, + terminate_when_unhealthy=True, + healthy_z_range=(1.0, 2.0), + reset_noise_scale=1e-2, + exclude_current_positions_from_observation=True, + **kwargs + ): + utils.EzPickle.__init__( + self, + forward_reward_weight, + ctrl_cost_weight, + healthy_reward, + terminate_when_unhealthy, + healthy_z_range, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs + ) + + self._forward_reward_weight = forward_reward_weight + self._ctrl_cost_weight = ctrl_cost_weight + self._healthy_reward = healthy_reward + self._terminate_when_unhealthy = terminate_when_unhealthy + self._healthy_z_range = healthy_z_range + + self._reset_noise_scale = reset_noise_scale + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + if exclude_current_positions_from_observation: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 + ) + else: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(350,), dtype=np.float64 + ) + + MujocoEnv.__init__( + self, + "humanoid.xml", + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs + ) + + @property + def healthy_reward(self): + return ( + float(self.is_healthy or self._terminate_when_unhealthy) + * self._healthy_reward + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(self.data.ctrl)) + return control_cost + + @property + def is_healthy(self): + min_z, max_z = self._healthy_z_range + is_healthy = min_z < self.data.qpos[2] < max_z + + return is_healthy + + @property + def terminated(self): + terminated = (not self.is_healthy) if self._terminate_when_unhealthy else False + return terminated + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = self.data.qvel.flat.copy() + + com_inertia = self.data.cinert[1:].flat.copy() + com_velocity = self.data.cvel[1:].flat.copy() + + actuator_forces = self.data.qfrc_actuator[6:].flat.copy() + external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + + if self._exclude_current_positions_from_observation: + position = position[2:] + + return np.concatenate( + ( + position, + velocity, + com_inertia, + com_velocity, + actuator_forces, + external_contact_forces, + ) + ) + + def step(self, action): + xy_position_before = mass_center(self.model, self.data) + self.do_simulation(action, self.frame_skip) + xy_position_after = mass_center(self.model, self.data) + + xy_velocity = (xy_position_after - xy_position_before) / self.dt + x_velocity, y_velocity = xy_velocity + + ctrl_cost = self.control_cost(action) + + forward_reward = self._forward_reward_weight * x_velocity + healthy_reward = self.healthy_reward + + rewards = forward_reward + healthy_reward + + observation = self._get_obs() + reward = rewards - ctrl_cost + terminated = self.terminated + info = { + "reward_linvel": forward_reward, + "reward_quadctrl": -ctrl_cost, + "reward_alive": healthy_reward, + "x_position": xy_position_after[0], + "y_position": xy_position_after[1], + "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), + "x_velocity": x_velocity, + "y_velocity": y_velocity, + "forward_reward": forward_reward, + } + + if self.render_mode == "human": + self.render() + return observation, reward, terminated, False, info + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = self.init_qvel + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nv + ) + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py new file mode 100644 index 00000000..9f215056 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -0,0 +1,276 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 1, + "distance": 4.0, + "lookat": np.array((0.0, 0.0, 0.8925)), + "elevation": -20.0, +} + + +class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is based on the environment introduced by Tassa, Erez and Todorov + in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). + The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a + pair of legs and arms. The legs each consist of two links, and so the arms (representing the + knees and elbows respectively). The environment starts with the humanoid laying on the ground, + and then the goal of the environment is to make the humanoid standup and then keep it standing + by applying torques on the various hinges. + + ## Action Space + The agent take a 17-element vector for actions. + + The action space is a continuous `(action, ...)` all in `[-1, 1]`, where `action` + represents the numerical torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | + | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | + | 1 | Torque applied on the hinge in the z-coordinate of the abdomen | -0.4 | 0.4 | abdomen_z | hinge | torque (N m) | + | 2 | Torque applied on the hinge in the x-coordinate of the abdomen | -0.4 | 0.4 | abdomen_x | hinge | torque (N m) | + | 3 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | + | 4 | Torque applied on the rotor between torso/abdomen and the right hip (z-coordinate) | -0.4 | 0.4 | right_hip_z (right_thigh) | hinge | torque (N m) | + | 5 | Torque applied on the rotor between torso/abdomen and the right hip (y-coordinate) | -0.4 | 0.4 | right_hip_y (right_thigh) | hinge | torque (N m) | + | 6 | Torque applied on the rotor between the right hip/thigh and the right shin | -0.4 | 0.4 | right_knee | hinge | torque (N m) | + | 7 | Torque applied on the rotor between torso/abdomen and the left hip (x-coordinate) | -0.4 | 0.4 | left_hip_x (left_thigh) | hinge | torque (N m) | + | 8 | Torque applied on the rotor between torso/abdomen and the left hip (z-coordinate) | -0.4 | 0.4 | left_hip_z (left_thigh) | hinge | torque (N m) | + | 9 | Torque applied on the rotor between torso/abdomen and the left hip (y-coordinate) | -0.4 | 0.4 | left_hip_y (left_thigh) | hinge | torque (N m) | + | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | + | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | + | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | + | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | + | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | + | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | + | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | + + ## Observation Space + + The state space consists of positional values of different body parts of the Humanoid, + followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. + + **Note:** The x- and y-coordinates of the torso are being omitted to produce position-agnostic behavior in policies + + The observation is a `ndarray` with shape `(376,)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | + | 0 | z-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | + | 1 | x-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 2 | y-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 3 | z-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 4 | w-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | + | 5 | z-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | angle (rad) | + | 6 | y-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | angle (rad) | + | 7 | x-angle of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | angle (rad) | + | 8 | x-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | angle (rad) | + | 9 | z-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | angle (rad) | + | 10 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | + | 11 | angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | angle (rad) | + | 12 | x-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | angle (rad) | + | 13 | z-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | angle (rad) | + | 14 | y-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | angle (rad) | + | 15 | angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | angle (rad) | + | 16 | coordinate-1 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | angle (rad) | + | 17 | coordinate-2 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | angle (rad) | + | 18 | angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | angle (rad) | + | 19 | coordinate-1 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | angle (rad) | + | 20 | coordinate-2 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | angle (rad) | + | 21 | angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | angle (rad) | + | 22 | x-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 23 | y-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 24 | z-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | + | 25 | x-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 26 | y-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 27 | z-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | + | 28 | z-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | anglular velocity (rad/s) | + | 29 | y-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | anglular velocity (rad/s) | + | 30 | x-coordinate of angular velocity of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | aanglular velocity (rad/s) | + | 31 | x-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | anglular velocity (rad/s) | + | 32 | z-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | anglular velocity (rad/s) | + | 33 | y-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | anglular velocity (rad/s) | + | 35 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | + | 36 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | + | 37 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | + | 38 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | + | 39 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | + | 40 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | + | 41 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | + | 42 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | + | 43 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | + | 44 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | + | 45 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | + + + Additionally, after all the positional and velocity based values in the table, + the state_space consists of (in order): + - *cinert:* Mass and inertia of a single rigid body relative to the center of mass + (this is an intermediate result of transition). It has shape 14*10 (*nbody * 10*) + and hence adds to another 140 elements in the state space. + - *cvel:* Center of mass based velocity. It has shape 14 * 6 (*nbody * 6*) and hence + adds another 84 elements in the state space + - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape + `(23,)` *(nv * 1)* and hence adds another 23 elements to the state space. + - *cfrc_ext:* This is the center of mass based external force on the body. It has shape + 14 * 6 (*nbody * 6*) and hence adds to another 84 elements in the state space. + where *nbody* stands for the number of bodies in the robot and *nv* stands for the number + of degrees of freedom (*= dim(qvel)*) + + The (x,y,z) coordinates are translational DOFs while the orientations are rotational + DOFs expressed as quaternions. One can read more about free joints on the + [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). + + **Note:** HumanoidStandup-v4 environment no longer has the following contact forces issue. + If using previous HumanoidStandup versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 results + in the contact forces always being 0. As such we recommend to use a Mujoco-Py version < 2.0 + when using the Humanoid environment if you would like to report results with contact forces + (if contact forces are not used in your experiments, you can use version > 2.0). + + ## Rewards + The reward consists of three parts: + - *uph_cost*: A reward for moving upward (in an attempt to stand up). This is not a relative + reward which measures how much upward it has moved from the last timestep, but it is an + absolute reward which measures how much upward the Humanoid has moved overall. It is + measured as *(z coordinate after action - 0)/(atomic timestep)*, where *z coordinate after + action* is index 0 in the state/index 2 in the table, and *atomic timestep* is the time for + one frame of movement even though the simulation has a framerate of 5 (done in order to inflate + rewards a little for faster learning). + - *quad_ctrl_cost*: A negative reward for penalising the humanoid if it has too large of + a control force. If there are *nu* actuators/controls, then the control has shape `nu x 1`. + It is measured as *0.1 **x** sum(control2)*. + - *quad_impact_cost*: A negative reward for penalising the humanoid if the external + contact force is too large. It is calculated as *min(0.5 * 0.000001 * sum(external + contact force2), 10)*. + + The total reward returned is ***reward*** *=* *uph_cost + 1 - quad_ctrl_cost - quad_impact_cost* + + ## Starting State + All observations start in state + (0.0, 0.0, 0.105, 1.0, 0.0 ... 0.0) with a uniform noise in the range of + [-0.01, 0.01] added to the positional and velocity values (values in the table) + for stochasticity. Note that the initial z coordinate is intentionally selected + to be low, thereby indicating a laying down humanoid. The initial orientation is + designed to make it face forward as well. + + ## Episode End + The episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 1000 timesteps + 2. Termination: Any of the state space values is no longer finite + + ## Arguments + + No additional arguments are currently supported. + + ```python + import gymnasium as gym + env = gym.make('HumanoidStandup-v4') + ``` + + There is no v3 for HumanoidStandup, unlike the robot environments where a v3 and + beyond take gymnasium.make kwargs such as xml_file, ctrl_cost_weight, reset_noise_scale etc. + + ```python + import gymnasium as gym + env = gym.make('HumanoidStandup-v2') + ``` + + ## Version History + + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 67, + } + + def __init__(self, **kwargs): + observation_space = Box( + low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 + ) + MujocoEnv.__init__( + self, + "humanoidstandup.xml", + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs + ) + utils.EzPickle.__init__(self, **kwargs) + + def _get_obs(self): + position = self.data.qpos.flat.copy() + position = position[2:] + velocity = self.data.qvel.flat.copy() + + com_inertia = self.data.cinert[1:].flat.copy() + com_velocity = self.data.cvel[1:].flat.copy() + + actuator_forces = self.data.qfrc_actuator[6:].flat.copy() + external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + + + return np.concatenate( + ( + position, + velocity, + com_inertia, + com_velocity, + actuator_forces, + external_contact_forces, + ) + ) + + def step(self, a): + self.do_simulation(a, self.frame_skip) + pos_after = self.data.qpos[2] + data = self.data + uph_cost = (pos_after - 0) / self.model.opt.timestep + + quad_ctrl_cost = 0.1 * np.square(data.ctrl).sum() + quad_impact_cost = 0.5e-6 * np.square(data.cfrc_ext).sum() + quad_impact_cost = min(quad_impact_cost, 10) + reward = uph_cost - quad_ctrl_cost - quad_impact_cost + 1 + + if self.render_mode == "human": + self.render() + return ( + self._get_obs(), + reward, + False, + False, + dict( + reward_linup=uph_cost, + reward_quadctrl=-quad_ctrl_cost, + reward_impact=-quad_impact_cost, + ), + ) + + def reset_model(self): + c = 0.01 + self.set_state( + self.init_qpos + self.np_random.uniform(low=-c, high=c, size=self.model.nq), + self.init_qvel + + self.np_random.uniform( + low=-c, + high=c, + size=self.model.nv, + ), + ) + return self._get_obs() diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py new file mode 100644 index 00000000..7e823d2c --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -0,0 +1,179 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 0, + "distance": 4.1225, + "lookat": np.array((0.0, 0.0, 0.12250000000000005)), +} + + +class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment originates from control theory and builds on the cartpole + environment based on the work done by Barto, Sutton, and Anderson in + ["Neuronlike adaptive elements that can solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), + powered by the Mujoco physics simulator - allowing for more complex experiments + (such as varying the effects of gravity or constraints). This environment involves a cart that can + moved linearly, with a pole fixed on it and a second pole fixed on the other end of the first one + (leaving the second pole as the only one with one free end). The cart can be pushed left or right, + and the goal is to balance the second pole on top of the first pole, which is in turn on top of the + cart, by applying continuous forces on the cart. + + ## Action Space + The agent take a 1-element vector for actions. + The action space is a continuous `(action)` in `[-1, 1]`, where `action` represents the + numerical force applied to the cart (with magnitude representing the amount of force and + sign representing the direction) + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| + | 0 | Force applied on the cart | -1 | 1 | slider | slide | Force (N) | + + ## Observation Space + + The state space consists of positional values of different body parts of the pendulum system, + followed by the velocities of those individual parts (their derivatives) with all the + positions ordered before all the velocities. + + The observation is a `ndarray` with shape `(11,)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ----------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | + | 0 | position of the cart along the linear surface | -Inf | Inf | slider | slide | position (m) | + | 1 | sine of the angle between the cart and the first pole | -Inf | Inf | sin(hinge) | hinge | unitless | + | 2 | sine of the angle between the two poles | -Inf | Inf | sin(hinge2) | hinge | unitless | + | 3 | cosine of the angle between the cart and the first pole | -Inf | Inf | cos(hinge) | hinge | unitless | + | 4 | cosine of the angle between the two poles | -Inf | Inf | cos(hinge2) | hinge | unitless | + | 5 | velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | + | 6 | angular velocity of the angle between the cart and the first pole | -Inf | Inf | hinge | hinge | angular velocity (rad/s) | + | 7 | angular velocity of the angle between the two poles | -Inf | Inf | hinge2 | hinge | angular velocity (rad/s) | + | 8 | constraint force - 1 | -Inf | Inf | | | Force (N) | + | 9 | constraint force - 2 | -Inf | Inf | | | Force (N) | + | 10 | constraint force - 3 | -Inf | Inf | | | Force (N) | + + + There is physical contact between the robots and their environment - and Mujoco + attempts at getting realistic physics simulations for the possible physical contact + dynamics by aiming for physical accuracy and computational efficiency. + + There is one constraint force for contacts for each degree of freedom (3). + The approach and handling of constraints by Mujoco is unique to the simulator + and is based on their research. Once can find more information in their + [*documentation*](https://mujoco.readthedocs.io/en/latest/computation.html) + or in their paper + ["Analytically-invertible dynamics with contacts and constraints: Theory and implementation in MuJoCo"](https://homes.cs.washington.edu/~todorov/papers/TodorovICRA14.pdf). + + + ## Rewards + + The reward consists of two parts: + - *alive_bonus*: The goal is to make the second inverted pendulum stand upright + (within a certain angle limit) as long as possible - as such a reward of +10 is awarded + for each timestep that the second pole is upright. + - *distance_penalty*: This reward is a measure of how far the *tip* of the second pendulum + (the only free end) moves, and it is calculated as + *0.01 * x2 + (y - 2)2*, where *x* is the x-coordinate of the tip + and *y* is the y-coordinate of the tip of the second pole. + - *velocity_penalty*: A negative reward for penalising the agent if it moves too + fast *0.001 * v12 + 0.005 * v2 2* + + The total reward returned is ***reward*** *=* *alive_bonus - distance_penalty - velocity_penalty* + + ## Starting State + All observations start in state + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise in the range + of [-0.1, 0.1] added to the positional values (cart position and pole angles) and standard + normal force with a standard deviation of 0.1 added to the velocity values for stochasticity. + + ## Episode End + The episode ends when any of the following happens: + + 1.Truncation: The episode duration reaches 1000 timesteps. + 2.Termination: Any of the state space values is no longer finite. + 3.Termination: The y_coordinate of the tip of the second pole *is less than or equal* to 1. The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other). + + ## Arguments + + No additional arguments are currently supported. + + ```python + import gymnasium as gym + env = gym.make('InvertedDoublePendulum-v4') + ``` + There is no v3 for InvertedPendulum, unlike the robot environments where a v3 and + beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('InvertedDoublePendulum-v2') + ``` + + ## Version History + + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum) + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 20, + } + + def __init__(self, **kwargs): + observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) + MujocoEnv.__init__( + self, + "inverted_double_pendulum.xml", + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs + ) + utils.EzPickle.__init__(self, **kwargs) + + def step(self, action): + self.do_simulation(action, self.frame_skip) + ob = self._get_obs() + x, _, y = self.data.site_xpos[0] + dist_penalty = 0.01 * x**2 + (y - 2) ** 2 + v1, v2 = self.data.qvel[1:3] + vel_penalty = 1e-3 * v1**2 + 5e-3 * v2**2 + alive_bonus = 10 + r = alive_bonus - dist_penalty - vel_penalty + terminated = bool(y <= 1) + if self.render_mode == "human": + self.render() + return ob, r, terminated, False, {} + + def _get_obs(self): + return np.concatenate( + [ + self.data.qpos[:1], # cart x pos + np.sin(self.data.qpos[1:]), # link angles + np.cos(self.data.qpos[1:]), + np.clip(self.data.qvel, -10, 10), + np.clip(self.data.qfrc_constraint, -10, 10)[:1], + ] + ).ravel() + + def reset_model(self): + self.set_state( + self.init_qpos + + self.np_random.uniform(low=-0.1, high=0.1, size=self.model.nq), + self.init_qvel + self.np_random.standard_normal(self.model.nv) * 0.1, + ) + return self._get_obs() diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py new file mode 100644 index 00000000..ff5c9771 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -0,0 +1,191 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = {"trackbodyid": 0} + + +class ReacherEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + "Reacher" is a two-jointed robot arm. The goal is to move the robot's end effector (called *fingertip*) close to a + target that is spawned at a random position. + + ## Action Space + The action space is a `Box(-1, 1, (2,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|---------------------------------------------------------------------------------|-------------|-------------|--------------------------|-------|------| + | 0 | Torque applied at the first hinge (connecting the link to the point of fixture) | -1 | 1 | joint0 | hinge | torque (N m) | + | 1 | Torque applied at the second hinge (connecting the two links) | -1 | 1 | joint1 | hinge | torque (N m) | + + ## Observation Space + + Observations consist of + + - The cosine of the angles of the two arms + - The sine of the angles of the two arms + - The coordinates of the target + - The angular velocities of the arms + - The vector between the target and the reacher's fingertip (3 dimensional with the last element being 0) + + The observation is a `ndarray` with shape `(11,)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ---------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | + | 0 | cosine of the angle of the first arm | -Inf | Inf | cos(joint0) | hinge | unitless | + | 1 | cosine of the angle of the second arm | -Inf | Inf | cos(joint1) | hinge | unitless | + | 2 | sine of the angle of the first arm | -Inf | Inf | sin(joint0) | hinge | unitless | + | 3 | sine of the angle of the second arm | -Inf | Inf | sin(joint1) | hinge | unitless | + | 4 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | + | 5 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | + | 6 | angular velocity of the first arm | -Inf | Inf | joint0 | hinge | angular velocity (rad/s) | + | 7 | angular velocity of the second arm | -Inf | Inf | joint1 | hinge | angular velocity (rad/s) | + | 8 | x-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | + | 9 | y-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | + | 10 | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d and z is same for both) | -Inf | Inf | NA | slide | position (m) | + + + Most Gym environments just return the positions and velocity of the + joints in the `.xml` file as the state of the environment. However, in + reacher the state is created by combining only certain elements of the + position and velocity, and performing some function transformations on them. + If one is to read the `.xml` for reacher then they will find 4 joints: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + |-----|-----------------------------|----------|----------|----------------------------------|-------|--------------------| + | 0 | angle of the first arm | -Inf | Inf | joint0 | hinge | angle (rad) | + | 1 | angle of the second arm | -Inf | Inf | joint1 | hinge | angle (rad) | + | 2 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | + | 3 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | + + + ## Rewards + The reward consists of two parts: + - *reward_distance*: This reward is a measure of how far the *fingertip* + of the reacher (the unattached end) is from the target, with a more negative + value assigned for when the reacher's *fingertip* is further away from the + target. It is calculated as the negative vector norm of (position of + the fingertip - position of target), or *-norm("fingertip" - "target")*. + - *reward_control*: A negative reward for penalising the walker if + it takes actions that are too large. It is measured as the negative squared + Euclidean norm of the action, i.e. as *- sum(action2)*. + + The total reward returned is ***reward*** *=* *reward_distance + reward_control* + + Unlike other environments, Reacher does not allow you to specify weights for the individual reward terms. + However, `info` does contain the keys *reward_dist* and *reward_ctrl*. Thus, if you'd like to weight the terms, + you should create a wrapper that computes the weighted reward from `info`. + + + ## Starting State + All observations start in state + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + with a noise added for stochasticity. A uniform noise in the range + [-0.1, 0.1] is added to the positional attributes, while the target position + is selected uniformly at random in a disk of radius 0.2 around the origin. + Independent, uniform noise in the + range of [-0.005, 0.005] is added to the velocities, and the last + element ("fingertip" - "target") is calculated at the end once everything + is set. The default setting has a framerate of 2 and a *dt = 2 * 0.01 = 0.02* + + ## Episode End + + The episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 50 timesteps (with a new random target popping up if the reacher's fingertip reaches it before 50 timesteps) + 2. Termination: Any of the state space values is no longer finite. + + ## Arguments + + No additional arguments are currently supported (in v2 and lower), + but modifications can be made to the XML file in the assets folder + (or by changing the path to a modified XML file in another folder).. + + ```python + import gymnasium as gym + env = gym.make('Reacher-v4') + ``` + + There is no v3 for Reacher, unlike the robot environments where a v3 and + beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ## Version History + + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 50, + } + + def __init__(self, **kwargs): + utils.EzPickle.__init__(self, **kwargs) + observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) + MujocoEnv.__init__( + self, + "reacher.xml", + 2, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs + ) + + def step(self, a): + vec = self.get_body_com("fingertip") - self.get_body_com("target") + reward_dist = -np.linalg.norm(vec) + reward_ctrl = -np.square(a).sum() + reward = reward_dist + reward_ctrl + + self.do_simulation(a, self.frame_skip) + if self.render_mode == "human": + self.render() + + ob = self._get_obs() + return ( + ob, + reward, + False, + False, + dict(reward_dist=reward_dist, reward_ctrl=reward_ctrl), + ) + + def reset_model(self): + qpos = ( + self.np_random.uniform(low=-0.1, high=0.1, size=self.model.nq) + + self.init_qpos + ) + while True: + self.goal = self.np_random.uniform(low=-0.2, high=0.2, size=2) + if np.linalg.norm(self.goal) < 0.2: + break + qpos[-2:] = self.goal + qvel = self.init_qvel + self.np_random.uniform( + low=-0.005, high=0.005, size=self.model.nv + ) + qvel[-2:] = 0 + self.set_state(qpos, qvel) + return self._get_obs() + + def _get_obs(self): + theta = self.data.qpos.flat[:2] + return np.concatenate( + [ + np.cos(theta), + np.sin(theta), + self.data.qpos.flat[2:], + self.data.qvel.flat[:2], + (self.get_body_com("fingertip") - self.get_body_com("target"))[:2], + ] + ) From 81d98e2c4711698a6e55a4e02f89f456fb8e43f0 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 25 Jan 2023 01:38:24 +0200 Subject: [PATCH 013/160] `pre-commit` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 -- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 -- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 3 --- gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py | 2 -- gymnasium_robotics/envs/mujoco/reacher_v5.py | 2 -- 5 files changed, 11 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 1e618f58..28f718cd 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "distance": 4.0, } diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index d827d7b9..06c2d40d 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 1, "distance": 4.0, diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 9f215056..1745681f 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 1, "distance": 4.0, @@ -225,7 +223,6 @@ def _get_obs(self): actuator_forces = self.data.qfrc_actuator[6:].flat.copy() external_contact_forces = self.data.cfrc_ext[1:].flat.copy() - return np.concatenate( ( position, diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 7e823d2c..efd4a191 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 0, "distance": 4.1225, diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index ff5c9771..0a735dd8 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = {"trackbodyid": 0} From e9a1643e227e4ce3c77850a1dc858cce78f7a752 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 12:04:24 +0300 Subject: [PATCH 014/160] add hopper_v5 --- .../envs/mujoco/assets/hopper_v5.xml | 53 +++ gymnasium_robotics/envs/mujoco/hopper_v5.py | 311 ++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml create mode 100644 gymnasium_robotics/envs/mujoco/hopper_v5.py diff --git a/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml b/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml new file mode 100644 index 00000000..a0b7273e --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml @@ -0,0 +1,53 @@ + + + + + + + + + diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py new file mode 100644 index 00000000..b2722c6c --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -0,0 +1,311 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 2, + "distance": 3.0, + "lookat": np.array((0.0, 0.0, 1.15)), + "elevation": -20.0, +} + + +class HopperEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is based on the work done by Erez, Tassa, and Todorov in + ["Infinite Horizon Model Predictive Control for Nonlinear Periodic Tasks"](http://www.roboticsproceedings.org/rss07/p10.pdf). The environment aims to + increase the number of independent state and control variables as compared to + the classic control environments. The hopper is a two-dimensional + one-legged figure that consist of four main body parts - the torso at the + top, the thigh in the middle, the leg in the bottom, and a single foot on + which the entire body rests. The goal is to make hops that move in the + forward (right) direction by applying torques on the three hinges + connecting the four body parts. + + ## Action Space + The action space is a `Box(-1, 1, (3,), float32)`. An action represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| + | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | + | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | + | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | + + ## Observation Space + Observations consist of positional values of different body parts of the + hopper, followed by the velocities of those individual parts + (their derivatives) with all the positions ordered before all the velocities. + + By default, observations do not include the x-coordinate of the hopper. It may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will be `Box(-Inf, Inf, (12,), float64)` where the first observation + represents the x-coordinate of the hopper. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x-coordinate + will be returned in `info` with key `"x_position"`. + + However, by default, the observation is a `Box(-Inf, Inf, (11,), float64)` where the elements + correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | -------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | + | 0 | z-coordinate of the torso (height of hopper) | -Inf | Inf | rootz | slide | position (m) | + | 1 | angle of the torso | -Inf | Inf | rooty | hinge | angle (rad) | + | 2 | angle of the thigh joint | -Inf | Inf | thigh_joint | hinge | angle (rad) | + | 3 | angle of the leg joint | -Inf | Inf | leg_joint | hinge | angle (rad) | + | 4 | angle of the foot joint | -Inf | Inf | foot_joint | hinge | angle (rad) | + | 5 | velocity of the x-coordinate of the torso | -Inf | Inf | rootx | slide | velocity (m/s) | + | 6 | velocity of the z-coordinate (height) of the torso | -Inf | Inf | rootz | slide | velocity (m/s) | + | 7 | angular velocity of the angle of the torso | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | + | 8 | angular velocity of the thigh hinge | -Inf | Inf | thigh_joint | hinge | angular velocity (rad/s) | + | 9 | angular velocity of the leg hinge | -Inf | Inf | leg_joint | hinge | angular velocity (rad/s) | + | 10 | angular velocity of the foot hinge | -Inf | Inf | foot_joint | hinge | angular velocity (rad/s) | + | excluded | x-coordinate of the torso | -Inf | Inf | rootx | slide | position (m) | + + + ## Rewards + The reward consists of three parts: + - *healthy_reward*: Every timestep that the hopper is healthy (see definition in section "Episode Termination"), it gets a reward of fixed value `healthy_reward`. + - *forward_reward*: A reward of hopping forward which is measured + as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is + the time between actions and is dependent on the frame_skip parameter + (fixed to 4), where the frametime is 0.002 - making the + default *dt = 4 * 0.002 = 0.008*. This reward would be positive if the hopper + hops forward (positive x direction). + - *ctrl_cost*: A cost for penalising the hopper if it takes + actions that are too large. It is measured as *`ctrl_cost_weight` * + sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the + control and has a default value of 0.001 + + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. + + ## Starting State + All observations start in state + (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise + in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. + + ## Episode End + The hopper is said to be unhealthy if any of the following happens: + + 1. An element of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) is no longer contained in the closed interval specified by the argument `healthy_state_range` + 2. The height of the hopper (`observation[0]` if `exclude_current_positions_from_observation=True`, else `observation[1]`) is no longer contained in the closed interval specified by the argument `healthy_z_range` (usually meaning that it has fallen) + 3. The angle (`observation[1]` if `exclude_current_positions_from_observation=True`, else `observation[2]`) is no longer contained in the closed interval specified by the argument `healthy_angle_range` + + If `terminate_when_unhealthy=True` is passed during construction (which is the default), + the episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 1000 timesteps + 2. Termination: The hopper is unhealthy + + If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + env = gym.make('Hopper-v2') + ``` + + v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Hopper-v4', ctrl_cost_weight=0.1, ....) + ``` + + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"hopper.xml"` | Path to a MuJoCo model | + | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `0.001` | Weight for _ctrl_cost_ reward (see section on reward) | + | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the hopper is no longer healthy | + | `healthy_state_range` | **tuple** | `(-100, 100)` | The elements of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) must be in this range for the hopper to be considered healthy | + | `healthy_z_range` | **tuple** | `(0.7, float("inf"))` | The z-coordinate must be in this range for the hopper to be considered healthy | + | `healthy_angle_range` | **tuple** | `(-0.2, 0.2)` | The angle given by `observation[1]` (if `exclude_current_positions_from_observation=True`, else `observation[2]`) must be in this range for the hopper to be considered healthy | + | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + ## Version History + + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Hopper gets a new model, the `xml_file` is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50. + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0). + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 125, + } + + def __init__( + self, + xml_file="hopper-v5.xml", + forward_reward_weight=1.0, + ctrl_cost_weight=1e-3, + healthy_reward=1.0, + terminate_when_unhealthy=True, + healthy_state_range=(-100.0, 100.0), + healthy_z_range=(0.7, float("inf")), + healthy_angle_range=(-0.2, 0.2), + reset_noise_scale=5e-3, + exclude_current_positions_from_observation=True, + **kwargs, + ): + utils.EzPickle.__init__( + self, + forward_reward_weight, + ctrl_cost_weight, + healthy_reward, + terminate_when_unhealthy, + healthy_state_range, + healthy_z_range, + healthy_angle_range, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs, + ) + + self._forward_reward_weight = forward_reward_weight + + self._ctrl_cost_weight = ctrl_cost_weight + + self._healthy_reward = healthy_reward + self._terminate_when_unhealthy = terminate_when_unhealthy + + self._healthy_state_range = healthy_state_range + self._healthy_z_range = healthy_z_range + self._healthy_angle_range = healthy_angle_range + + self._reset_noise_scale = reset_noise_scale + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + if exclude_current_positions_from_observation: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(11,), dtype=np.float64 + ) + else: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(12,), dtype=np.float64 + ) + + MujocoEnv.__init__( + self, + xml_file, + 4, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs, + ) + + @property + def healthy_reward(self): + return ( + float(self.is_healthy or self._terminate_when_unhealthy) + * self._healthy_reward + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) + return control_cost + + @property + def is_healthy(self): + z, angle = self.data.qpos[1:3] + state = self.state_vector()[2:] + + min_state, max_state = self._healthy_state_range + min_z, max_z = self._healthy_z_range + min_angle, max_angle = self._healthy_angle_range + + healthy_state = np.all(np.logical_and(min_state < state, state < max_state)) + healthy_z = min_z < z < max_z + healthy_angle = min_angle < angle < max_angle + + is_healthy = all((healthy_state, healthy_z, healthy_angle)) + + return is_healthy + + @property + def terminated(self): + terminated = not self.is_healthy if self._terminate_when_unhealthy else False + return terminated + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = np.clip(self.data.qvel.flat.copy(), -10, 10) + + if self._exclude_current_positions_from_observation: + position = position[1:] + + observation = np.concatenate((position, velocity)).ravel() + return observation + + def step(self, action): + x_position_before = self.data.qpos[0] + self.do_simulation(action, self.frame_skip) + x_position_after = self.data.qpos[0] + x_velocity = (x_position_after - x_position_before) / self.dt + + ctrl_cost = self.control_cost(action) + + forward_reward = self._forward_reward_weight * x_velocity + healthy_reward = self.healthy_reward + + rewards = forward_reward + healthy_reward + costs = ctrl_cost + + observation = self._get_obs() + reward = rewards - costs + terminated = self.terminated + info = { + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, + "reward_survive": healthy_reward, + "x_position": x_position_after, + "x_velocity": x_velocity, + } + + if self.render_mode == "human": + self.render() + return observation, reward, terminated, False, info + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = self.init_qvel + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nv + ) + + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation + + def viewer_setup(self): + assert self.viewer is not None + for key, value in DEFAULT_CAMERA_CONFIG.items(): + if isinstance(value, np.ndarray): + getattr(self.viewer.cam, key)[:] = value + else: + setattr(self.viewer.cam, key, value) From ba585c890e6fc81fbeba8cfee1917f2041930a85 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 17:38:36 +0300 Subject: [PATCH 015/160] add walker2d --- .../envs/mujoco/assets/walker2d_v5.xml | 68 ++++ .../envs/mujoco/assets/walker2d_v5_old.xml | 68 ++++ gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d.py | 301 ++++++++++++++++++ 4 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml create mode 100644 gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml create mode 100644 gymnasium_robotics/envs/mujoco/walker2d.py diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml new file mode 100644 index 00000000..06b83107 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml @@ -0,0 +1,68 @@ + + + + + + + + diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml new file mode 100644 index 00000000..d89a00f0 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml @@ -0,0 +1,68 @@ + + + + + + + + diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index b2722c6c..6c4a592b 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -134,7 +134,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Hopper gets a new model, the `xml_file` is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Hopper gets a new model, the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) * v2: All continuous control environments now use mujoco-py >= 1.50. diff --git a/gymnasium_robotics/envs/mujoco/walker2d.py b/gymnasium_robotics/envs/mujoco/walker2d.py new file mode 100644 index 00000000..f8a3c962 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/walker2d.py @@ -0,0 +1,301 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 2, + "distance": 4.0, + "lookat": np.array((0.0, 0.0, 1.15)), + "elevation": -20.0, +} + + +class Walker2dEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment builds on the [hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) environment + by adding another set of legs making it possible for the robot to walk forward instead of + hop. Like other Mujoco environments, this environment aims to increase the number of independent state + and control variables as compared to the classic control environments. The walker is a + two-dimensional two-legged figure that consist of seven main body parts - a single torso at the top + (with the two legs splitting after the torso), two thighs in the middle below the torso, two legs + in the bottom below the thighs, and two feet attached to the legs on which the entire body rests. + The goal is to walk in the in the forward (right) + direction by applying torques on the six hinges connecting the seven body parts. + + ## Action Space + The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| + | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | + | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | + | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | + | 3 | Torque applied on the left thigh rotor | -1 | 1 | thigh_left_joint | hinge | torque (N m) | + | 4 | Torque applied on the left leg rotor | -1 | 1 | leg_left_joint | hinge | torque (N m) | + | 5 | Torque applied on the left foot rotor | -1 | 1 | foot_left_joint | hinge | torque (N m) | + + ## Observation Space + Observations consist of positional values of different body parts of the walker, + followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. + + By default, observations do not include the x-coordinate of the torso. It may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will be `Box(-Inf, Inf, (18,), float64)` where the first observation + represent the x-coordinates of the torso of the walker. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x-coordinate + of the torso will be returned in `info` with key `"x_position"`. + + By default, observation is a `Box(-Inf, Inf, (17,), float64)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | -------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | + | excluded | x-coordinate of the torso | -Inf | Inf | rootx | slide | position (m) | + | 0 | z-coordinate of the torso (height of Walker2d) | -Inf | Inf | rootz | slide | position (m) | + | 1 | angle of the torso | -Inf | Inf | rooty | hinge | angle (rad) | + | 2 | angle of the thigh joint | -Inf | Inf | thigh_joint | hinge | angle (rad) | + | 3 | angle of the leg joint | -Inf | Inf | leg_joint | hinge | angle (rad) | + | 4 | angle of the foot joint | -Inf | Inf | foot_joint | hinge | angle (rad) | + | 5 | angle of the left thigh joint | -Inf | Inf | thigh_left_joint | hinge | angle (rad) | + | 6 | angle of the left leg joint | -Inf | Inf | leg_left_joint | hinge | angle (rad) | + | 7 | angle of the left foot joint | -Inf | Inf | foot_left_joint | hinge | angle (rad) | + | 8 | velocity of the x-coordinate of the torso | -Inf | Inf | rootx | slide | velocity (m/s) | + | 9 | velocity of the z-coordinate (height) of the torso | -Inf | Inf | rootz | slide | velocity (m/s) | + | 10 | angular velocity of the angle of the torso | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | + | 11 | angular velocity of the thigh hinge | -Inf | Inf | thigh_joint | hinge | angular velocity (rad/s) | + | 12 | angular velocity of the leg hinge | -Inf | Inf | leg_joint | hinge | angular velocity (rad/s) | + | 13 | angular velocity of the foot hinge | -Inf | Inf | foot_joint | hinge | angular velocity (rad/s) | + | 14 | angular velocity of the thigh hinge | -Inf | Inf | thigh_left_joint | hinge | angular velocity (rad/s) | + | 15 | angular velocity of the leg hinge | -Inf | Inf | leg_left_joint | hinge | angular velocity (rad/s) | + | 16 | angular velocity of the foot hinge | -Inf | Inf | foot_left_joint | hinge | angular velocity (rad/s) | + + ## Rewards + The reward consists of three parts: + - *healthy_reward*: Every timestep that the walker is alive, it receives a fixed reward of value `healthy_reward`, + - *forward_reward*: A reward of walking forward which is measured as + *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. + *dt* is the time between actions and is dependeent on the frame_skip parameter + (default is 4), where the frametime is 0.002 - making the default + *dt = 4 * 0.002 = 0.008*. This reward would be positive if the walker walks forward (positive x direction). + - *ctrl_cost*: A cost for penalising the walker if it + takes actions that are too large. It is measured as + *`ctrl_cost_weight` * sum(action2)* where *`ctrl_cost_weight`* is + a parameter set for the control and has a default value of 0.001 + + The total reward returned is ***reward*** *=* *healthy_reward bonus + forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. + + ## Starting State + All observations start in state + (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + with a uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. + + ## Episode End + The walker is said to be unhealthy if any of the following happens: + + 1. Any of the state space values is no longer finite + 2. The height of the walker is ***not*** in the closed interval specified by `healthy_z_range` + 3. The absolute value of the angle (`observation[1]` if `exclude_current_positions_from_observation=False`, else `observation[2]`) is ***not*** in the closed interval specified by `healthy_angle_range` + + If `terminate_when_unhealthy=True` is passed during construction (which is the default), + the episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 1000 timesteps + 2. Termination: The walker is unhealthy + + If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + env = gym.make('Walker2d-v4') + ``` + + v3 and beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Walker2d-v4', ctrl_cost_weight=0.1, ....) + ``` + + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"walker2d.xml"` | Path to a MuJoCo model | + | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | + | `healthy_reward` | **float** | `1.0` | Constant reward given if the ant is "healthy" after timestep | + | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | + | `healthy_z_range` | **tuple** | `(0.8, 2)` | The z-coordinate of the torso of the walker must be in this range to be considered healthy | + | `healthy_angle_range` | **tuple** | `(-1, 1)` | The angle must be in this range to be considered healthy | + | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + + ## Version History + + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Walker2d gets a new model (feet friction set to 1.9), the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v2: All continuous control environments now use mujoco-py >= 1.50 + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 125, + } + + def __init__( + self, + xml_file="walker2d_v5.xml", + forward_reward_weight=1.0, + ctrl_cost_weight=1e-3, + healthy_reward=1.0, + terminate_when_unhealthy=True, + healthy_z_range=(0.8, 2.0), + healthy_angle_range=(-1.0, 1.0), + reset_noise_scale=5e-3, + exclude_current_positions_from_observation=True, + **kwargs, + ): + utils.EzPickle.__init__( + self, + forward_reward_weight, + ctrl_cost_weight, + healthy_reward, + terminate_when_unhealthy, + healthy_z_range, + healthy_angle_range, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs, + ) + + self._forward_reward_weight = forward_reward_weight + self._ctrl_cost_weight = ctrl_cost_weight + + self._healthy_reward = healthy_reward + self._terminate_when_unhealthy = terminate_when_unhealthy + + self._healthy_z_range = healthy_z_range + self._healthy_angle_range = healthy_angle_range + + self._reset_noise_scale = reset_noise_scale + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + if exclude_current_positions_from_observation: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(17,), dtype=np.float64 + ) + else: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 + ) + + MujocoEnv.__init__( + self, + xml_file, + 4, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs, + ) + + @property + def healthy_reward(self): + return ( + float(self.is_healthy or self._terminate_when_unhealthy) + * self._healthy_reward + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) + return control_cost + + @property + def is_healthy(self): + z, angle = self.data.qpos[1:3] + + min_z, max_z = self._healthy_z_range + min_angle, max_angle = self._healthy_angle_range + + healthy_z = min_z < z < max_z + healthy_angle = min_angle < angle < max_angle + is_healthy = healthy_z and healthy_angle + + return is_healthy + + @property + def terminated(self): + terminated = not self.is_healthy if self._terminate_when_unhealthy else False + return terminated + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = np.clip(self.data.qvel.flat.copy(), -10, 10) + + if self._exclude_current_positions_from_observation: + position = position[1:] + + observation = np.concatenate((position, velocity)).ravel() + return observation + + def step(self, action): + x_position_before = self.data.qpos[0] + self.do_simulation(action, self.frame_skip) + x_position_after = self.data.qpos[0] + x_velocity = (x_position_after - x_position_before) / self.dt + + ctrl_cost = self.control_cost(action) + + forward_reward = self._forward_reward_weight * x_velocity + healthy_reward = self.healthy_reward + + rewards = forward_reward + healthy_reward + costs = ctrl_cost + + observation = self._get_obs() + reward = rewards - costs + terminated = self.terminated + info = { + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, + "reward_survive": healthy_reward, + "x_position": x_position_after, + "x_velocity": x_velocity, + } + + if self.render_mode == "human": + self.render() + + return observation, reward, terminated, False, info + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = self.init_qvel + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nv + ) + + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation From 1b4a0a3288562e5d673a54e1008c518f254109dc Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 18:25:44 +0300 Subject: [PATCH 016/160] add Half Cheetah --- .../envs/mujoco/half_cheetah_v5.py | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/half_cheetah_v5.py diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py new file mode 100644 index 00000000..7bd88238 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -0,0 +1,246 @@ +__credits__ = ["Rushiv Arora"] + +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "distance": 4.0, +} + + +class HalfCheetahEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is based on the work by P. Wawrzyński in + ["A Cat-Like Robot Real-Time Learning to Run"](http://staff.elka.pw.edu.pl/~pwawrzyn/pub-s/0812_LSCLRR.pdf). + The HalfCheetah is a 2-dimensional robot consisting of 9 body parts and 8 + joints connecting them (including two paws). The goal is to apply a torque + on the joints to make the cheetah run forward (right) as fast as possible, + with a positive reward allocated based on the distance moved forward and a + negative reward allocated for moving backward. The torso and head of the + cheetah are fixed, and the torque can only be applied on the other 6 joints + over the front and back thighs (connecting to the torso), shins + (connecting to the thighs) and feet (connecting to the shins). + + ## Action Space + The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | + | 0 | Torque applied on the back thigh rotor | -1 | 1 | bthigh | hinge | torque (N m) | + | 1 | Torque applied on the back shin rotor | -1 | 1 | bshin | hinge | torque (N m) | + | 2 | Torque applied on the back foot rotor | -1 | 1 | bfoot | hinge | torque (N m) | + | 3 | Torque applied on the front thigh rotor | -1 | 1 | fthigh | hinge | torque (N m) | + | 4 | Torque applied on the front shin rotor | -1 | 1 | fshin | hinge | torque (N m) | + | 5 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | + + + ## Observation Space + Observations consist of positional values of different body parts of the + cheetah, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. + + By default, observations do not include the cheetah's `rootx`. It may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will be a `Box(-Inf, Inf, (18,), float64)` where the first element + represents the `rootx`. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the + will be returned in `info` with key `"x_position"`. + + However, by default, the observation is a `Box(-Inf, Inf, (17,), float64)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ------------------------------------ | ---- | --- | -------------------------------- | ----- | ------------------------ | + | 0 | z-coordinate of the front tip | -Inf | Inf | rootz | slide | position (m) | + | 1 | angle of the front tip | -Inf | Inf | rooty | hinge | angle (rad) | + | 2 | angle of the second rotor | -Inf | Inf | bthigh | hinge | angle (rad) | + | 3 | angle of the second rotor | -Inf | Inf | bshin | hinge | angle (rad) | + | 4 | velocity of the tip along the x-axis | -Inf | Inf | bfoot | hinge | angle (rad) | + | 5 | velocity of the tip along the y-axis | -Inf | Inf | fthigh | hinge | angle (rad) | + | 6 | angular velocity of front tip | -Inf | Inf | fshin | hinge | angle (rad) | + | 7 | angular velocity of second rotor | -Inf | Inf | ffoot | hinge | angle (rad) | + | 8 | x-coordinate of the front tip | -Inf | Inf | rootx | slide | velocity (m/s) | + | 9 | y-coordinate of the front tip | -Inf | Inf | rootz | slide | velocity (m/s) | + | 10 | angle of the front tip | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | + | 11 | angle of the second rotor | -Inf | Inf | bthigh | hinge | angular velocity (rad/s) | + | 12 | angle of the second rotor | -Inf | Inf | bshin | hinge | angular velocity (rad/s) | + | 13 | velocity of the tip along the x-axis | -Inf | Inf | bfoot | hinge | angular velocity (rad/s) | + | 14 | velocity of the tip along the y-axis | -Inf | Inf | fthigh | hinge | angular velocity (rad/s) | + | 15 | angular velocity of front tip | -Inf | Inf | fshin | hinge | angular velocity (rad/s) | + | 16 | angular velocity of second rotor | -Inf | Inf | ffoot | hinge | angular velocity (rad/s) | + | excluded | x-coordinate of the front tip | -Inf | Inf | rootx | slide | position (m) | + + ## Rewards + The reward consists of two parts: + - *forward_reward*: A reward of moving forward which is measured + as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is + the time between actions and is dependent on the frame_skip parameter + (fixed to 5), where the frametime is 0.01 - making the + default *dt = 5 * 0.01 = 0.05*. This reward would be positive if the cheetah + runs forward (right). + - *ctrl_cost*: A cost for penalising the cheetah if it takes + actions that are too large. It is measured as *`ctrl_cost_weight` * + sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the + control and has a default value of 0.1 + + The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms + + ## Starting State + All observations start in state (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,) with a noise added to the + initial state for stochasticity. As seen before, the first 8 values in the + state are positional and the last 9 values are velocity. A uniform noise in + the range of [-`reset_noise_scale`, `reset_noise_scale`] is added to the positional values while a standard + normal noise with a mean of 0 and standard deviation of `reset_noise_scale` is added to the + initial velocity values of all zeros. + + ## Episode End + The episode truncates when the episode length is greater than 1000. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + env = gym.make('HalfCheetah-v2') + ``` + + v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('HalfCheetah-v4', ctrl_cost_weight=0.1, ....) + ``` + + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"half_cheetah.xml"` | Path to a MuJoCo model | + | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ weight (see section on reward) | + | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + ## Version History + + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. The `xml_file` argument is re-added, renamed `info`'s `reward_run` to `reward_forward`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen). + * v2: All continuous control environments now use mujoco-py >= 1.50. + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 20, + } + + def __init__( + self, + xml_file="half_cheetah.xml", + forward_reward_weight=1.0, + ctrl_cost_weight=0.1, + reset_noise_scale=0.1, + exclude_current_positions_from_observation=True, + **kwargs, + ): + utils.EzPickle.__init__( + self, + forward_reward_weight, + ctrl_cost_weight, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs, + ) + + self._forward_reward_weight = forward_reward_weight + + self._ctrl_cost_weight = ctrl_cost_weight + + self._reset_noise_scale = reset_noise_scale + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + if exclude_current_positions_from_observation: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(17,), dtype=np.float64 + ) + else: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 + ) + + MujocoEnv.__init__( + self, + xml_file, + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs, + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) + return control_cost + + def step(self, action): + x_position_before = self.data.qpos[0] + self.do_simulation(action, self.frame_skip) + x_position_after = self.data.qpos[0] + x_velocity = (x_position_after - x_position_before) / self.dt + + ctrl_cost = self.control_cost(action) + + forward_reward = self._forward_reward_weight * x_velocity + + observation = self._get_obs() + reward = forward_reward - ctrl_cost + terminated = False + info = { + "x_position": x_position_after, + "x_velocity": x_velocity, + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, + } + + if self.render_mode == "human": + self.render() + return observation, reward, terminated, False, info + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = self.data.qvel.flat.copy() + + if self._exclude_current_positions_from_observation: + position = position[1:] + + observation = np.concatenate((position, velocity)).ravel() + return observation + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = ( + self.init_qvel + + self._reset_noise_scale * self.np_random.standard_normal(self.model.nv) + ) + + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation \ No newline at end of file From 479c3e1e88e4e714df8cb08d65677da4e532debd Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 18:32:20 +0300 Subject: [PATCH 017/160] typo fix --- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 7bd88238..eaf69bbd 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -243,4 +243,4 @@ def reset_model(self): self.set_state(qpos, qvel) observation = self._get_obs() - return observation \ No newline at end of file + return observation From bc2de82ce93ab31f54dfc3b89701d293a19ed513 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 19:30:07 +0300 Subject: [PATCH 018/160] add pusher --- gymnasium_robotics/envs/mujoco/pusher_v5.py | 222 ++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/pusher_v5.py diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py new file mode 100644 index 00000000..7aebbd5b --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -0,0 +1,222 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": -1, + "distance": 4.0, +} + + +class PusherEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + "Pusher" is a multi-jointed robot arm which is very similar to that of a human. + The goal is to move a target cylinder (called *object*) to a goal position using the robot's end effector (called *fingertip*). + The robot consists of shoulder, elbow, forearm, and wrist joints. + + ## Action Space + The action space is a `Box(-2, 2, (7,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| + | 0 | Rotation of the panning the shoulder | -2 | 2 | r_shoulder_pan_joint | hinge | torque (N m) | + | 1 | Rotation of the shoulder lifting joint | -2 | 2 | r_shoulder_lift_joint | hinge | torque (N m) | + | 2 | Rotation of the shoulder rolling joint | -2 | 2 | r_upper_arm_roll_joint | hinge | torque (N m) | + | 3 | Rotation of hinge joint that flexed the elbow | -2 | 2 | r_elbow_flex_joint | hinge | torque (N m) | + | 4 | Rotation of hinge that rolls the forearm | -2 | 2 | r_forearm_roll_joint | hinge | torque (N m) | + | 5 | Rotation of flexing the wrist | -2 | 2 | r_wrist_flex_joint | hinge | torque (N m) | + | 6 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | + + ## Observation Space + + Observations consist of + + - Angle of rotational joints on the pusher + - Angular velocities of rotational joints on the pusher + - The coordinates of the fingertip of the pusher + - The coordinates of the object to be moved + - The coordinates of the goal position + + The observation is a `Box(-Inf, Inf, (23,), float64)` where the elements correspond to the table below. + An analogy can be drawn to a human arm in order to help understand the state space, with the words flex and roll meaning the + same as human joints. + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | -------------------------------------------------------- | ---- | --- | -------------------------------- | -------- | ------------------------ | + | 0 | Rotation of the panning the shoulder | -Inf | Inf | r_shoulder_pan_joint | hinge | angle (rad) | + | 1 | Rotation of the shoulder lifting joint | -Inf | Inf | r_shoulder_lift_joint | hinge | angle (rad) | + | 2 | Rotation of the shoulder rolling joint | -Inf | Inf | r_upper_arm_roll_joint | hinge | angle (rad) | + | 3 | Rotation of hinge joint that flexed the elbow | -Inf | Inf | r_elbow_flex_joint | hinge | angle (rad) | + | 4 | Rotation of hinge that rolls the forearm | -Inf | Inf | r_forearm_roll_joint | hinge | angle (rad) | + | 5 | Rotation of flexing the wrist | -Inf | Inf | r_wrist_flex_joint | hinge | angle (rad) | + | 6 | Rotation of rolling the wrist | -Inf | Inf | r_wrist_roll_joint | hinge | angle (rad) | + | 7 | Rotational velocity of the panning the shoulder | -Inf | Inf | r_shoulder_pan_joint | hinge | angular velocity (rad/s) | + | 8 | Rotational velocity of the shoulder lifting joint | -Inf | Inf | r_shoulder_lift_joint | hinge | angular velocity (rad/s) | + | 9 | Rotational velocity of the shoulder rolling joint | -Inf | Inf | r_upper_arm_roll_joint | hinge | angular velocity (rad/s) | + | 10 | Rotational velocity of hinge joint that flexed the elbow | -Inf | Inf | r_elbow_flex_joint | hinge | angular velocity (rad/s) | + | 11 | Rotational velocity of hinge that rolls the forearm | -Inf | Inf | r_forearm_roll_joint | hinge | angular velocity (rad/s) | + | 12 | Rotational velocity of flexing the wrist | -Inf | Inf | r_wrist_flex_joint | hinge | angular velocity (rad/s) | + | 13 | Rotational velocity of rolling the wrist | -Inf | Inf | r_wrist_roll_joint | hinge | angular velocity (rad/s) | + | 14 | x-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | + | 15 | y-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | + | 16 | z-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | + | 17 | x-coordinate of the object to be moved | -Inf | Inf | object (obj_slidex) | slide | position (m) | + | 18 | y-coordinate of the object to be moved | -Inf | Inf | object (obj_slidey) | slide | position (m) | + | 19 | z-coordinate of the object to be moved | -Inf | Inf | object | cylinder | position (m) | + | 20 | x-coordinate of the goal position of the object | -Inf | Inf | goal (goal_slidex) | slide | position (m) | + | 21 | y-coordinate of the goal position of the object | -Inf | Inf | goal (goal_slidey) | slide | position (m) | + | 22 | z-coordinate of the goal position of the object | -Inf | Inf | goal | sphere | position (m) | + + + ## Rewards + The reward consists of two parts: + - *reward_near *: This reward is a measure of how far the *fingertip* + of the pusher (the unattached end) is from the object, with a more negative + value assigned for when the pusher's *fingertip* is further away from the + target. It is calculated as the negative vector norm of (position of + the fingertip - position of target), or *-norm("fingertip" - "target")*. + - *reward_dist *: This reward is a measure of how far the object is from + the target goal position, with a more negative value assigned for object is + further away from the target. It is calculated as the negative vector norm of + (position of the object - position of goal), or *-norm("object" - "target")*. + - *reward_control*: A negative reward for penalising the pusher if + it takes actions that are too large. It is measured as the negative squared + Euclidean norm of the action, i.e. as *- sum(action2)*. + + The total reward returned is ***reward*** *=* *reward_dist + 0.1 * reward_ctrl + 0.5 * reward_near* + + Unlike other environments, Pusher does not allow you to specify weights for the individual reward terms. + However, `info` does contain the keys *reward_dist* and *reward_ctrl*. Thus, if you'd like to weight the terms, + you should create a wrapper that computes the weighted reward from `info`. + + + ## Starting State + All pusher (not including object and goal) states start in + (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0). A uniform noise in the range + [-0.005, 0.005] is added to the velocity attributes only. The velocities of + the object and goal are permanently set to 0. The object's x-position is selected uniformly + between [-0.3, 0] while the y-position is selected uniformly between [-0.2, 0.2], and this + process is repeated until the vector norm between the object's (x,y) position and origin is not greater + than 0.17. The goal always have the same position of (0.45, -0.05, -0.323). + + The default framerate is 5 with each frame lasting for 0.01, giving rise to a *dt = 5 * 0.01 = 0.05* + + ## Episode End + + The episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches a 100 timesteps. + 2. Termination: Any of the state space values is no longer finite. + + ## Arguments + + No additional arguments are currently supported (in v2 and lower), + but modifications can be made to the XML file in the assets folder + (or by changing the path to a modified XML file in another folder).. + + ```python + import gymnasium as gym + env = gym.make('Pusher-v4') + ``` + + There is no v3 for Pusher, unlike the robot environments where a v3 and + beyond take `gymnasmium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Pusher-v2') + ``` + + ## Version History + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. "reward_near" is added to the `info`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v2: All continuous control environments now use mujoco-py >= 1.50. + * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. + * v0: Initial versions release (1.0.0). + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 20, + } + + def __init__(self, **kwargs): + utils.EzPickle.__init__(self, **kwargs) + observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) + MujocoEnv.__init__( + self, + "pusher.xml", + 5, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs, + ) + + def step(self, a): + vec_1 = self.get_body_com("object") - self.get_body_com("tips_arm") + vec_2 = self.get_body_com("object") - self.get_body_com("goal") + + reward_near = -np.linalg.norm(vec_1) + reward_dist = -np.linalg.norm(vec_2) + reward_ctrl = -np.square(a).sum() + reward = reward_dist + 0.1 * reward_ctrl + 0.5 * reward_near + + self.do_simulation(a, self.frame_skip) + if self.render_mode == "human": + self.render() + + ob = self._get_obs() + info = { + "reward_dist": reward_dist, + "reward_ctrl": reward_ctrl, + "reward_near": reward_near, + } + return ( + ob, + reward, + False, + False, + info + ) + + def reset_model(self): + qpos = self.init_qpos + + self.goal_pos = np.asarray([0, 0]) + while True: + self.cylinder_pos = np.concatenate( + [ + self.np_random.uniform(low=-0.3, high=0, size=1), + self.np_random.uniform(low=-0.2, high=0.2, size=1), + ] + ) + if np.linalg.norm(self.cylinder_pos - self.goal_pos) > 0.17: + break + + qpos[-4:-2] = self.cylinder_pos + qpos[-2:] = self.goal_pos + qvel = self.init_qvel + self.np_random.uniform( + low=-0.005, high=0.005, size=self.model.nv + ) + qvel[-4:] = 0 + self.set_state(qpos, qvel) + return self._get_obs() + + def _get_obs(self): + return np.concatenate( + [ + self.data.qpos.flat[:7], + self.data.qvel.flat[:7], + self.get_body_com("tips_arm"), + self.get_body_com("object"), + self.get_body_com("goal"), + ] + ) From 2ec47a52e88a0cff365c0383f118101671ac9132 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 10 May 2023 19:40:35 +0300 Subject: [PATCH 019/160] add swimmer --- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 232 +++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/swimmer_v5.py diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py new file mode 100644 index 00000000..d3c7ed7f --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -0,0 +1,232 @@ +__credits__ = ["Rushiv Arora"] + +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +class SwimmerEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment corresponds to the Swimmer environment described in Rémi Coulom's PhD thesis + ["Reinforcement Learning Using Neural Networks, with Applications to Motor Control"](https://tel.archives-ouvertes.fr/tel-00003985/document). + The environment aims to increase the number of independent state and control + variables as compared to the classic control environments. The swimmers + consist of three or more segments ('***links***') and one less articulation + joints ('***rotors***') - one rotor joint connecting exactly two links to + form a linear chain. The swimmer is suspended in a two dimensional pool and + always starts in the same position (subject to some deviation drawn from an + uniform distribution), and the goal is to move as fast as possible towards + the right by applying torque on the rotors and using the fluids friction. + + ## Notes + + The problem parameters are: + Problem parameters: + * *n*: number of body parts + * *mi*: mass of part *i* (*i* ∈ {1...n}) + * *li*: length of part *i* (*i* ∈ {1...n}) + * *k*: viscous-friction coefficient + + While the default environment has *n* = 3, *li* = 0.1, + and *k* = 0.1. It is possible to pass a custom MuJoCo XML file during construction to increase the + number of links, or to tweak any of the parameters. + + ## Action Space + The action space is a `Box(-1, 1, (2,), float32)`. An action represents the torques applied between *links* + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| + | 0 | Torque applied on the first rotor | -1 | 1 | motor1_rot | hinge | torque (N m) | + | 1 | Torque applied on the second rotor | -1 | 1 | motor2_rot | hinge | torque (N m) | + + ## Observation Space + By default, observations consists of: + * θi: angle of part *i* with respect to the *x* axis + * θi': its derivative with respect to time (angular velocity) + + In the default case, observations do not include the x- and y-coordinates of the front tip. These may + be included by passing `exclude_current_positions_from_observation=False` during construction. + Then, the observation space will be `Box(-Inf, Inf, (10,), float64)` where the first two observations + represent the x- and y-coordinates of the front tip. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates + will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. + + By default, the observation is a `Box(-Inf, Inf, (8,), float64)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | ------------------------------------ | ---- | --- | -------------------------------- | ----- | ------------------------ | + | 0 | angle of the front tip | -Inf | Inf | free_body_rot | hinge | angle (rad) | + | 1 | angle of the first rotor | -Inf | Inf | motor1_rot | hinge | angle (rad) | + | 2 | angle of the second rotor | -Inf | Inf | motor2_rot | hinge | angle (rad) | + | 3 | velocity of the tip along the x-axis | -Inf | Inf | slider1 | slide | velocity (m/s) | + | 4 | velocity of the tip along the y-axis | -Inf | Inf | slider2 | slide | velocity (m/s) | + | 5 | angular velocity of front tip | -Inf | Inf | free_body_rot | hinge | angular velocity (rad/s) | + | 6 | angular velocity of first rotor | -Inf | Inf | motor1_rot | hinge | angular velocity (rad/s) | + | 7 | angular velocity of second rotor | -Inf | Inf | motor2_rot | hinge | angular velocity (rad/s) | + | excluded | position of the tip along the x-axis | -Inf | Inf | slider1 | slide | position (m) | + | excluded | position of the tip along the y-axis | -Inf | Inf | slider2 | slide | position (m) | + + ## Rewards + The reward consists of two parts: + - *forward_reward*: A reward of moving forward which is measured + as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is + the time between actions and is dependent on the frame_skip parameter + (default is 4), where the frametime is 0.01 - making the + default *dt = 4 * 0.01 = 0.04*. This reward would be positive if the swimmer + swims right as desired. + - *ctrl_cost*: A cost for penalising the swimmer if it takes + actions that are too large. It is measured as *`ctrl_cost_weight` * + sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the + control and has a default value of 1e-4 + + The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms + + ## Starting State + All observations start in state (0,0,0,0,0,0,0,0) with a Uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] is added to the initial state for stochasticity. + + ## Episode End + The episode truncates when the episode length is greater than 1000. + + ## Arguments + + No additional arguments are currently supported in v2 and lower. + + ```python + import gymnasium as gym + gym.make('Swimmer-v4') + ``` + + v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + + ```python + import gymnasium as gym + env = gym.make('Swimmer-v4', ctrl_cost_weight=0.1, ....) + ``` + + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"swimmer.xml"` | Path to a MuJoCo model | + | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `1e-4` | Weight for _ctrl_cost_ term (see section on reward) | + | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + + + ## Version History + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. The `xml_file` argument is re-added, replaced "reward_fwd"/ "forward_reward" with "reward_forward" in `info`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen). + * v2: All continuous control environments now use mujoco-py >= 1.50. + * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 25, + } + + def __init__( + self, + xml_file="swimmer.xml", + forward_reward_weight=1.0, + ctrl_cost_weight=1e-4, + reset_noise_scale=0.1, + exclude_current_positions_from_observation=True, + **kwargs, + ): + utils.EzPickle.__init__( + self, + forward_reward_weight, + ctrl_cost_weight, + reset_noise_scale, + exclude_current_positions_from_observation, + **kwargs, + ) + + self._forward_reward_weight = forward_reward_weight + self._ctrl_cost_weight = ctrl_cost_weight + + self._reset_noise_scale = reset_noise_scale + + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + if exclude_current_positions_from_observation: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(8,), dtype=np.float64 + ) + else: + observation_space = Box( + low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64 + ) + MujocoEnv.__init__( + self, xml_file, 4, observation_space=observation_space, **kwargs + ) + + def control_cost(self, action): + control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) + return control_cost + + def step(self, action): + xy_position_before = self.data.qpos[0:2].copy() + self.do_simulation(action, self.frame_skip) + xy_position_after = self.data.qpos[0:2].copy() + + xy_velocity = (xy_position_after - xy_position_before) / self.dt + x_velocity, y_velocity = xy_velocity + + forward_reward = self._forward_reward_weight * x_velocity + + ctrl_cost = self.control_cost(action) + + observation = self._get_obs() + reward = forward_reward - ctrl_cost + info = { + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, + "x_position": xy_position_after[0], + "y_position": xy_position_after[1], + "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), + "x_velocity": x_velocity, + "y_velocity": y_velocity, + } + + if self.render_mode == "human": + self.render() + + return observation, reward, False, False, info + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = self.data.qvel.flat.copy() + + if self._exclude_current_positions_from_observation: + position = position[2:] + + observation = np.concatenate([position, velocity]).ravel() + return observation + + def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = self.init_qvel + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nv + ) + + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation From a3e89066e171df37837358f7b232de6113532d63 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 11 May 2023 11:22:34 +0300 Subject: [PATCH 020/160] pre-commit --- gymnasium_robotics/envs/mujoco/ant_v5.py | 45 +++++++++++++------ .../envs/mujoco/half_cheetah_v5.py | 2 - gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 - gymnasium_robotics/envs/mujoco/pusher_v5.py | 10 +---- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 1 - gymnasium_robotics/envs/mujoco/walker2d.py | 2 - 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 28f718cd..cbe85871 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -16,10 +16,10 @@ class AntEnv(MujocoEnv, utils.EzPickle): Moritz, Levine, Jordan and Abbeel in ["High-Dimensional Continuous Control Using Generalized Advantage Estimation"](https://arxiv.org/abs/1506.02438). The ant is a 3D robot consisting of one torso (free rotational body) with - four legs attached to it with each leg having two links. The goal is to + four legs attached to it with each leg having two body parts. The goal is to coordinate the four legs to move in the forward (right) direction by applying - torques on the eight hinges connecting the two links of each leg and the torso - (nine parts and eight hinges). + torques on the eight hinges connecting the two body parts of each leg and the torso + (nine body parts and eight hinges). ## Action Space The action space is a `Box(-1, 1, (8,), float32)`. An action represents the torques applied at the hinge joints. @@ -36,20 +36,18 @@ class AntEnv(MujocoEnv, utils.EzPickle): | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | ## Observation Space - Observations consist of positional values of different body parts of the ant, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. By default, observations do not include the x- and y-coordinates of the ant's torso. These may be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will have 29 dimensions where the first two dimensions + In that case, the observation space will be a `Box(-Inf, Inf, (29,), float64)` where the first two observations represent the x- and y- coordinates of the ant's torso. Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates of the torso will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - However, by default, an observation is a `ndarray` with shape `(27,)` - where the elements correspond to the following: + However, by default, observation Space is a `Box(-Inf, Inf, (27,), float64)` where the elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------|--------|--------|----------------------------------------|-------|--------------------------| @@ -79,13 +77,32 @@ class AntEnv(MujocoEnv, utils.EzPickle): | 23 | angular velocity of angle between torso and back left link | -Inf | Inf | hip_3 (back_leg) | hinge | angle (rad) | | 24 | angular velocity of the angle between back left links | -Inf | Inf | ankle_3 (back_leg) | hinge | angle (rad) | | 25 | angular velocity of angle between torso and back right link | -Inf | Inf | hip_4 (right_back_leg) | hinge | angle (rad) | - | 26 |angular velocity of the angle between back right links | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | + | 26 | angular velocity of the angle between back right links | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | + | excluded | x-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | + | excluded | y-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | - If `use_contact_forces` is `True` then the observation space is extended by 14*6 = 84 elements, which are contact forces + If version < `v4` or `use_contact_forces` is `True` then the observation space is extended by 14*6 = 84 elements, which are contact forces (external forces - force x, y, z and torque x, y, z) applied to the - center of mass of each of the links. The 14 links are: the ground link, - the torso link, and 3 links for each leg (1 + 1 + 12) with the 6 external forces. + center of mass of each of the body parts. The 14 body parts are: + + | id (for `v2`, `v3`, `v4)` | body parts | + | --- | ------------ | + | 0 | worldbody (note: forces are always full of zeros) | + | 1 | torso | + | 2 | front_left_leg | + | 3 | aux_1 (front left leg) | + | 4 | ankle_1 (front left leg) | + | 5 | front_right_leg | + | 6 | aux_2 (front right leg) | + | 7 | ankle_2 (front right leg) | + | 8 | back_leg (back left leg) | + | 9 | aux_3 (back left leg) | + | 10 | ankle_3 (back left leg) | + | 11 | right_back_leg | + | 12 | aux_4 (back right leg) | + | 13 | ankle_4 (back right leg) | + The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). @@ -114,7 +131,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*. - But if `use_contact_forces=True` + But if `use_contact_forces=True` or version < `v4` The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost*. In either case `info` will also contain the individual reward terms. @@ -316,7 +333,6 @@ def step(self, action): "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, - "forward_reward": forward_reward, } # if self._use_contact_forces: contact_cost = self.contact_cost @@ -337,6 +353,9 @@ def _get_obs(self): position = position[2:] if self._use_contact_forces: + assert ( + self.contact_forces[0].flat.copy() == 0 + ).all() # TODO remove after validation contact_force = self.contact_forces[1:].flat.copy() assert len(contact_force) == 78 return np.concatenate((position, velocity, contact_force)) diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index eaf69bbd..1bd3e654 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -1,12 +1,10 @@ __credits__ = ["Rushiv Arora"] import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "distance": 4.0, } diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 6c4a592b..b03bec4d 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 2, "distance": 3.0, diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 7aebbd5b..9591279e 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": -1, "distance": 4.0, @@ -179,13 +177,7 @@ def step(self, a): "reward_ctrl": reward_ctrl, "reward_near": reward_near, } - return ( - ob, - reward, - False, - False, - info - ) + return (ob, reward, False, False, info) def reset_model(self): qpos = self.init_qpos diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index d3c7ed7f..7c12eabf 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -1,7 +1,6 @@ __credits__ = ["Rushiv Arora"] import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box diff --git a/gymnasium_robotics/envs/mujoco/walker2d.py b/gymnasium_robotics/envs/mujoco/walker2d.py index f8a3c962..fd9d8100 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d.py +++ b/gymnasium_robotics/envs/mujoco/walker2d.py @@ -1,10 +1,8 @@ import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 2, "distance": 4.0, From a3454bf70a12db91d2d1688c8c962fc14c9514a0 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 11 May 2023 15:26:08 +0300 Subject: [PATCH 021/160] typo fix --- gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml index d89a00f0..e7337607 100644 --- a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml +++ b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml @@ -2,7 +2,7 @@ Walker2D model for `Walker2d-v5`, based on openai/gym/Walker2d modified by @kallinteris-Andreas - To not require `coordinate="global"` - - kep feet friction to 0.9/1.9 + - keep feet friction to 0.9/1.9 --> From ef626cff6f04e194f4f0f35cf78222509607ad2d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 11 May 2023 16:40:12 +0300 Subject: [PATCH 022/160] `xml_file` arg fixes --- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 1 + gymnasium_robotics/envs/mujoco/hopper_v5.py | 1 + gymnasium_robotics/envs/mujoco/humanoid_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 8 ++++++-- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 1 + gymnasium_robotics/envs/mujoco/walker2d.py | 1 + 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 1bd3e654..ec5720e5 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -153,6 +153,7 @@ def __init__( ): utils.EzPickle.__init__( self, + xml_file, forward_reward_weight, ctrl_cost_weight, reset_noise_scale, diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index b03bec4d..6a350984 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -165,6 +165,7 @@ def __init__( ): utils.EzPickle.__init__( self, + xml_file, forward_reward_weight, ctrl_cost_weight, healthy_reward, diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 06c2d40d..5db32622 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -224,6 +224,7 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): def __init__( self, + xml_file="humanoid.xml", forward_reward_weight=1.25, ctrl_cost_weight=0.1, healthy_reward=5.0, @@ -235,6 +236,7 @@ def __init__( ): utils.EzPickle.__init__( self, + xml_file, forward_reward_weight, ctrl_cost_weight, healthy_reward, @@ -268,7 +270,7 @@ def __init__( MujocoEnv.__init__( self, - "humanoid.xml", + xml_file, 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 1745681f..aed217cc 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -198,13 +198,17 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): "render_fps": 67, } - def __init__(self, **kwargs): + def __init__( + self, + xml_file="humanoidstandup.xml", + **kwargs, + ): observation_space = Box( low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 ) MujocoEnv.__init__( self, - "humanoidstandup.xml", + xml_file, 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 7c12eabf..5cf3c289 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -144,6 +144,7 @@ def __init__( ): utils.EzPickle.__init__( self, + xml_file, forward_reward_weight, ctrl_cost_weight, reset_noise_scale, diff --git a/gymnasium_robotics/envs/mujoco/walker2d.py b/gymnasium_robotics/envs/mujoco/walker2d.py index fd9d8100..b96db7cb 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d.py +++ b/gymnasium_robotics/envs/mujoco/walker2d.py @@ -169,6 +169,7 @@ def __init__( ): utils.EzPickle.__init__( self, + xml_file, forward_reward_weight, ctrl_cost_weight, healthy_reward, From 3de59d9aee10824be90151e163e54ad0806b3c80 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 13 May 2023 10:01:02 +0300 Subject: [PATCH 023/160] added `InvertedPendulum-v5` --- .../envs/mujoco/inverted_pendulum_v5.py | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py new file mode 100644 index 00000000..e86d5277 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -0,0 +1,141 @@ +import numpy as np + +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.spaces import Box + + +DEFAULT_CAMERA_CONFIG = { + "trackbodyid": 0, + "distance": 2.04, +} + + +class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): + """ + ## Description + + This environment is the cartpole environment based on the work done by + Barto, Sutton, and Anderson in ["Neuronlike adaptive elements that can + solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), + just like in the classic environments but now powered by the Mujoco physics simulator - + allowing for more complex experiments (such as varying the effects of gravity). + This environment involves a cart that can moved linearly, with a pole fixed on it + at one end and having another end free. The cart can be pushed left or right, and the + goal is to balance the pole on the top of the cart by applying forces on the cart. + + ## Action Space + The agent take a 1-element vector for actions. + + The action space is a continuous `(action)` in `[-3, 3]`, where `action` represents + the numerical force applied to the cart (with magnitude representing the amount of + force and sign representing the direction) + + | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | + |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| + | 0 | Force applied on the cart | -3 | 3 | slider | slide | Force (N) | + + ## Observation Space + + The state space consists of positional values of different body parts of + the pendulum system, followed by the velocities of those individual parts (their derivatives) + with all the positions ordered before all the velocities. + + The observation is a `ndarray` with shape `(4,)` where the elements correspond to the following: + + | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | + | --- | --------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------- | + | 0 | position of the cart along the linear surface | -Inf | Inf | slider | slide | position (m) | + | 1 | vertical angle of the pole on the cart | -Inf | Inf | hinge | hinge | angle (rad) | + | 2 | linear velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | + | 3 | angular velocity of the pole on the cart | -Inf | Inf | hinge | hinge | anglular velocity (rad/s) | + + + ## Rewards + The goal is to make the inverted pendulum stand upright (within a certain angle limit) + as long as possible - as such a reward of +1 is awarded for each timestep that + the pole is upright. + + ## Starting State + All observations start in state + (0.0, 0.0, 0.0, 0.0) with a uniform noise in the range + of [-0.01, 0.01] added to the values for stochasticity. + + ## Episode End + The episode ends when any of the following happens: + + 1. Truncation: The episode duration reaches 1000 timesteps. + 2. Termination: Any of the state space values is no longer finite. + 3. Termination: The absolute value of the vertical angle between the pole and the cart is greater than 0.2 radian. + + ## Arguments + No additional arguments are currently supported. + + ```python + import gymnasium as gym + env = gym.make('InvertedPendulum-v4') + ``` + There is no v3 for InvertedPendulum, unlike the robot environments where a + v3 and beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + ```python + import gymnasium as gym + env = gym.make('InvertedPendulum-v2') + ``` + + ## Version History + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward survive" being 1 on every step (even on terminal steps) and added "reward_survive" to `info`. + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.. + * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen. + * v2: All continuous control environments now use mujoco-py >= 1.5. + * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum). + * v0: Initial versions release (1.0.0) + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 25, + } + + def __init__(self, **kwargs): + utils.EzPickle.__init__(self, **kwargs) + observation_space = Box(low=-np.inf, high=np.inf, shape=(4,), dtype=np.float64) + MujocoEnv.__init__( + self, + "inverted_pendulum.xml", + 2, + observation_space=observation_space, + default_camera_config=DEFAULT_CAMERA_CONFIG, + **kwargs, + ) + + def step(self, a): + self.do_simulation(a, self.frame_skip) + + ob = self._get_obs() + + terminated = bool(not np.isfinite(ob).all() or (np.abs(ob[1]) > 0.2)) + + reward = int(not terminated) + + info = {"reward_survive": reward} + + if self.render_mode == "human": + self.render() + return ob, reward, terminated, False, info + + def reset_model(self): + qpos = self.init_qpos + self.np_random.uniform( + size=self.model.nq, low=-0.01, high=0.01 + ) + qvel = self.init_qvel + self.np_random.uniform( + size=self.model.nv, low=-0.01, high=0.01 + ) + self.set_state(qpos, qvel) + return self._get_obs() + + def _get_obs(self): + return np.concatenate([self.data.qpos, self.data.qvel]).ravel() From a94519647a0d39ee3d37fe34f622c659a86245e5 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 13 May 2023 10:03:45 +0300 Subject: [PATCH 024/160] cleanup --- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 6a350984..0016f198 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -102,7 +102,6 @@ class HopperEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. ```python @@ -131,7 +130,6 @@ class HopperEnv(MujocoEnv, utils.EzPickle): | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Hopper gets a new model, the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) From a1b589964ccf1ddf9f7f8bdcd98eb2e39f1dc353 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 13 May 2023 15:36:17 +0300 Subject: [PATCH 025/160] rename --- gymnasium_robotics/envs/mujoco/{walker2d.py => walker2d_v5.py} | 2 -- 1 file changed, 2 deletions(-) rename gymnasium_robotics/envs/mujoco/{walker2d.py => walker2d_v5.py} (99%) diff --git a/gymnasium_robotics/envs/mujoco/walker2d.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py similarity index 99% rename from gymnasium_robotics/envs/mujoco/walker2d.py rename to gymnasium_robotics/envs/mujoco/walker2d_v5.py index b96db7cb..f7de2369 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -107,7 +107,6 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. ```python @@ -136,7 +135,6 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Walker2d gets a new model (feet friction set to 1.9), the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) From e3c89391979cb1895808ae8432f351632bb5b7a9 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 13 May 2023 16:17:40 +0300 Subject: [PATCH 026/160] fix camera on new models --- gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml | 2 +- gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml | 2 +- gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml b/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml index a0b7273e..cc3fbc84 100644 --- a/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml +++ b/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml @@ -18,7 +18,7 @@ modified by @saran_t - + diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml index 06b83107..12baef14 100644 --- a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml +++ b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml @@ -15,7 +15,7 @@ modified by @kallinteris-Andreas - + diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml index e7337607..180bfaec 100644 --- a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml +++ b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml @@ -15,7 +15,7 @@ modified by @kallinteris-Andreas - + From b0f5edffbf3ff96079c98a6dffc26bb9c49e856c Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 15:08:23 +0300 Subject: [PATCH 027/160] add inv_double_pend --- .../mujoco/inverted_double_pendulum_v5.py | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index efd4a191..22cae563 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -13,7 +13,6 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment originates from control theory and builds on the cartpole environment based on the work done by Barto, Sutton, and Anderson in ["Neuronlike adaptive elements that can solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), @@ -35,12 +34,11 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): | 0 | Force applied on the cart | -1 | 1 | slider | slide | Force (N) | ## Observation Space - The state space consists of positional values of different body parts of the pendulum system, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. - The observation is a `ndarray` with shape `(11,)` where the elements correspond to the following: + The observation is a `ndarray` with shape `(10,)` where the elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | @@ -52,9 +50,9 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): | 5 | velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | | 6 | angular velocity of the angle between the cart and the first pole | -Inf | Inf | hinge | hinge | angular velocity (rad/s) | | 7 | angular velocity of the angle between the two poles | -Inf | Inf | hinge2 | hinge | angular velocity (rad/s) | - | 8 | constraint force - 1 | -Inf | Inf | | | Force (N) | - | 9 | constraint force - 2 | -Inf | Inf | | | Force (N) | - | 10 | constraint force - 3 | -Inf | Inf | | | Force (N) | + | 8 | constraint force - x | -Inf | Inf | slider | slide | Force (N) | + | 9 | constraint force - y | -Inf | Inf | slider | slide | Force (N) | + | excluded | constraint force - z | -Inf | Inf | slider | slide | Force (N) | There is physical contact between the robots and their environment - and Mujoco @@ -70,7 +68,6 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): ## Rewards - The reward consists of two parts: - *alive_bonus*: The goal is to make the second inverted pendulum stand upright (within a certain angle limit) as long as possible - as such a reward of +10 is awarded @@ -98,7 +95,6 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): 3.Termination: The y_coordinate of the tip of the second pole *is less than or equal* to 1. The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other). ## Arguments - No additional arguments are currently supported. ```python @@ -114,7 +110,6 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): ``` ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) * v2: All continuous control environments now use mujoco-py >= 1.50 @@ -132,7 +127,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): } def __init__(self, **kwargs): - observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) + observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) MujocoEnv.__init__( self, "inverted_double_pendulum.xml", @@ -145,19 +140,31 @@ def __init__(self, **kwargs): def step(self, action): self.do_simulation(action, self.frame_skip) + ob = self._get_obs() + x, _, y = self.data.site_xpos[0] - dist_penalty = 0.01 * x**2 + (y - 2) ** 2 v1, v2 = self.data.qvel[1:3] + + terminated = bool(y <= 1) + + dist_penalty = 0.01 * x**2 + (y - 2) ** 2 vel_penalty = 1e-3 * v1**2 + 5e-3 * v2**2 - alive_bonus = 10 + alive_bonus = 10 * int(not terminated) r = alive_bonus - dist_penalty - vel_penalty - terminated = bool(y <= 1) + + info = { + "reward_survive": alive_bonus, + "distance_penalty": -dist_penalty, + "velocity_penalty": -vel_penalty, + } + if self.render_mode == "human": self.render() - return ob, r, terminated, False, {} + return ob, r, terminated, False, info def _get_obs(self): + assert self.data.qfrc_constraint[2] == 0 # TODO remove after validation return np.concatenate( [ self.data.qpos[:1], # cart x pos From 2567cd471006961dc5447009f70e372b6e42d61e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 17:23:32 +0300 Subject: [PATCH 028/160] fix `humanoid` `info` --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 5db32622..d1613406 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -343,15 +343,14 @@ def step(self, action): reward = rewards - ctrl_cost terminated = self.terminated info = { - "reward_linvel": forward_reward, - "reward_quadctrl": -ctrl_cost, - "reward_alive": healthy_reward, + "reward_survive": healthy_reward, + "reward_forward": forward_reward, + "reward_ctrl": -ctrl_cost, "x_position": xy_position_after[0], "y_position": xy_position_after[1], "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, - "forward_reward": forward_reward, } if self.render_mode == "human": From 7debcd5b26dc4e964e860894ce0bd192578aff35 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 17:23:53 +0300 Subject: [PATCH 029/160] `humanoid` add include obs arguments --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index d1613406..13c4c1ba 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -232,7 +232,11 @@ def __init__( healthy_z_range=(1.0, 2.0), reset_noise_scale=1e-2, exclude_current_positions_from_observation=True, - **kwargs + include_cinert_from_observation=True, + include_cvel_from_observation=True, + include_qfrc_actuator_from_observation=True, + include_cfrc_ext_from_observation=True, + **kwargs, ): utils.EzPickle.__init__( self, @@ -244,6 +248,10 @@ def __init__( healthy_z_range, reset_noise_scale, exclude_current_positions_from_observation, + include_cinert_from_observation, + include_cvel_from_observation, + include_qfrc_actuator_from_observation, + include_cfrc_ext_from_observation, **kwargs ) @@ -259,6 +267,11 @@ def __init__( exclude_current_positions_from_observation ) + self._include_cinert_from_observation = include_cinert_from_observation + self._include_cvel_from_observation = include_cvel_from_observation + self._include_qfrc_actuator_from_observation = include_qfrc_actuator_from_observation + self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation + if exclude_current_positions_from_observation: observation_space = Box( low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 @@ -304,11 +317,29 @@ def _get_obs(self): position = self.data.qpos.flat.copy() velocity = self.data.qvel.flat.copy() - com_inertia = self.data.cinert[1:].flat.copy() - com_velocity = self.data.cvel[1:].flat.copy() + if self._include_cinert_from_observation is True: + com_inertia = self.data.cinert[1:].flat.copy() + else: + com_inertia = np.array([]) + if self._include_cvel_from_observation is True: + com_velocity = self.data.cvel[1:].flat.copy() + else: + com_velocity = np.array([]) + + if self._include_qfrc_actuator_from_observation is True: + actuator_forces = self.data.qfrc_actuator[6:].flat.copy() + else: + actuator_forces = np.array([]) + if self._include_cfrc_ext_from_observation is True: + external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + else: + external_contact_forces = np.array([]) - actuator_forces = self.data.qfrc_actuator[6:].flat.copy() - external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + # TODO remove after validation + assert (self.data.cinert[0].flat.copy == 0).all() + assert (self.data.cvel[0].flat.copy == 0).all() + assert (self.data.qfrc_actuator[:6].flat.copy == 0).all() + assert (self.data.cfrc_ext[0].flat.copy == 0).all() if self._exclude_current_positions_from_observation: position = position[2:] From 29ce9a89a767da61c0d1ae42e8651eec7934ac75 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 17:25:31 +0300 Subject: [PATCH 030/160] update reacher DOC --- gymnasium_robotics/envs/mujoco/reacher_v5.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 0a735dd8..86787cac 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -21,7 +21,6 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): | 1 | Torque applied at the second hinge (connecting the two links) | -1 | 1 | joint1 | hinge | torque (N m) | ## Observation Space - Observations consist of - The cosine of the angles of the two arms @@ -30,7 +29,7 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): - The angular velocities of the arms - The vector between the target and the reacher's fingertip (3 dimensional with the last element being 0) - The observation is a `ndarray` with shape `(11,)` where the elements correspond to the following: + The observation is a `Box(-Inf, Inf, (10,), float64)` where the elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | ---------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | @@ -38,13 +37,13 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): | 1 | cosine of the angle of the second arm | -Inf | Inf | cos(joint1) | hinge | unitless | | 2 | sine of the angle of the first arm | -Inf | Inf | sin(joint0) | hinge | unitless | | 3 | sine of the angle of the second arm | -Inf | Inf | sin(joint1) | hinge | unitless | - | 4 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | - | 5 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | + | 4 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | + | 5 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | | 6 | angular velocity of the first arm | -Inf | Inf | joint0 | hinge | angular velocity (rad/s) | | 7 | angular velocity of the second arm | -Inf | Inf | joint1 | hinge | angular velocity (rad/s) | | 8 | x-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | | 9 | y-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | - | 10 | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d and z is same for both) | -Inf | Inf | NA | slide | position (m) | + | excluded | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d and z is same for both) | -Inf | Inf | NA | slide | position (m) | Most Gym environments just return the positions and velocity of the @@ -178,6 +177,7 @@ def reset_model(self): def _get_obs(self): theta = self.data.qpos.flat[:2] + assert (self.get_body_com("fingertip") - self.get_body_com("target"))[2] == 0 # TODO remove after validation return np.concatenate( [ np.cos(theta), From 8978413013b7cc537960ad313cf09fe27a12124d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 17:29:41 +0300 Subject: [PATCH 031/160] update obs shape (humanoid) --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 13c4c1ba..97403932 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -272,14 +272,12 @@ def __init__( self._include_qfrc_actuator_from_observation = include_qfrc_actuator_from_observation self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation - if exclude_current_positions_from_observation: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 - ) - else: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(350,), dtype=np.float64 - ) + obs_shape = 348 + if not exclude_current_positions_from_observation: + obs_shape += 2 + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 + ) MujocoEnv.__init__( self, From a857d9c4bc83361a6047e5636655b722085bc3d4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 18:04:51 +0300 Subject: [PATCH 032/160] `Ant` cleanup and fix `info` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index cbe85871..86762547 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -82,6 +82,9 @@ class AntEnv(MujocoEnv, utils.EzPickle): | excluded | y-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | + Additionally, after all the positional and velocity based values in the table, + the observation contains: + - *cfrc_ext:* If version < `v4` or `use_contact_forces` is `True` then the observation space is extended by 14*6 = 84 elements, which are contact forces (external forces - force x, y, z and torque x, y, z) applied to the center of mass of each of the body parts. The 14 body parts are: @@ -321,12 +324,15 @@ def step(self, action): rewards = forward_reward + healthy_reward costs = ctrl_cost = self.control_cost(action) + contact_cost = self.contact_cost + costs += contact_cost terminated = self.terminated observation = self._get_obs() info = { "reward_forward": forward_reward, "reward_ctrl": -ctrl_cost, + "reward_contact": -contact_cost, "reward_survive": healthy_reward, "x_position": xy_position_after[0], "y_position": xy_position_after[1], @@ -335,9 +341,6 @@ def step(self, action): "y_velocity": y_velocity, } # if self._use_contact_forces: - contact_cost = self.contact_cost - costs += contact_cost - info["reward_ctrl"] = -contact_cost reward = rewards - costs From 8ac23b09cc3c2d6773f8f396ccaa562b7a28e77d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sun, 14 May 2023 20:45:23 +0300 Subject: [PATCH 033/160] update `Ant` doc --- gymnasium_robotics/envs/mujoco/ant_v5.py | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 86762547..d132ce74 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -89,22 +89,22 @@ class AntEnv(MujocoEnv, utils.EzPickle): (external forces - force x, y, z and torque x, y, z) applied to the center of mass of each of the body parts. The 14 body parts are: - | id (for `v2`, `v3`, `v4)` | body parts | - | --- | ------------ | - | 0 | worldbody (note: forces are always full of zeros) | - | 1 | torso | - | 2 | front_left_leg | - | 3 | aux_1 (front left leg) | - | 4 | ankle_1 (front left leg) | - | 5 | front_right_leg | - | 6 | aux_2 (front right leg) | - | 7 | ankle_2 (front right leg) | - | 8 | back_leg (back left leg) | - | 9 | aux_3 (back left leg) | - | 10 | ankle_3 (back left leg) | - | 11 | right_back_leg | - | 12 | aux_4 (back right leg) | - | 13 | ankle_4 (back right leg) | + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body parts | + | --- | --- | ------------ | + | 0 | excluded | worldbody (note: forces are always full of zeros) | + | 1 | 0 | torso | + | 2 | 1 | front_left_leg | + | 3 | 2 | aux_1 (front left leg) | + | 4 | 3 | ankle_1 (front left leg) | + | 5 | 4 | front_right_leg | + | 6 | 5 | aux_2 (front right leg) | + | 7 | 6 | ankle_2 (front right leg) | + | 8 | 7 | back_leg (back left leg) | + | 9 | 8 | aux_3 (back left leg) | + | 10 | 9 | ankle_3 (back left leg) | + | 11 | 10 | right_back_leg | + | 12 | 11 | aux_4 (back right leg) | + | 13 | 12 | ankle_4 (back right leg) | The (x,y,z) coordinates are translational DOFs while the orientations are rotational From 567d0fe710cae772e0fa1bac008f6cc1ade44aa7 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 15 May 2023 09:54:45 +0300 Subject: [PATCH 034/160] re-add to humanoid `contact_cost` --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 97403932..71b194e1 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -227,6 +227,8 @@ def __init__( xml_file="humanoid.xml", forward_reward_weight=1.25, ctrl_cost_weight=0.1, + contact_cost_weight=5e-7, + contact_cost_range=(-np.inf, 10.0), healthy_reward=5.0, terminate_when_unhealthy=True, healthy_z_range=(1.0, 2.0), @@ -257,6 +259,8 @@ def __init__( self._forward_reward_weight = forward_reward_weight self._ctrl_cost_weight = ctrl_cost_weight + self._contact_cost_weight = contact_cost_weight + self._contact_cost_range = contact_cost_range self._healthy_reward = healthy_reward self._terminate_when_unhealthy = terminate_when_unhealthy self._healthy_z_range = healthy_z_range @@ -273,6 +277,7 @@ def __init__( self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation obs_shape = 348 + # TODO update for v5 shape if not exclude_current_positions_from_observation: obs_shape += 2 observation_space = Box( @@ -299,6 +304,14 @@ def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(self.data.ctrl)) return control_cost + @property + def contact_cost(self): + contact_forces = self.sim.data.cfrc_ext + contact_cost = self._contact_cost_weight * np.sum(np.square(contact_forces)) + min_cost, max_cost = self._contact_cost_range + contact_cost = np.clip(contact_cost, min_cost, max_cost) + return contact_cost + @property def is_healthy(self): min_z, max_z = self._healthy_z_range @@ -362,6 +375,8 @@ def step(self, action): x_velocity, y_velocity = xy_velocity ctrl_cost = self.control_cost(action) + contact_cost = self.contact_cost + costs = ctrl_cost + contact_cost forward_reward = self._forward_reward_weight * x_velocity healthy_reward = self.healthy_reward @@ -369,12 +384,13 @@ def step(self, action): rewards = forward_reward + healthy_reward observation = self._get_obs() - reward = rewards - ctrl_cost + reward = rewards - costs terminated = self.terminated info = { "reward_survive": healthy_reward, "reward_forward": forward_reward, "reward_ctrl": -ctrl_cost, + "reward_contact": -contact_cost, "x_position": xy_position_after[0], "y_position": xy_position_after[1], "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), From cb1c32dbbe58f60bf419623b80d66e76a6663ed1 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 15 May 2023 10:05:44 +0300 Subject: [PATCH 035/160] fix obs shape --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 71b194e1..a067a0f5 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -276,10 +276,13 @@ def __init__( self._include_qfrc_actuator_from_observation = include_qfrc_actuator_from_observation self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation - obs_shape = 348 - # TODO update for v5 shape - if not exclude_current_positions_from_observation: - obs_shape += 2 + obs_shape = 45 + obs_shape += 130 * self._include_cinert_from_observation + obs_shape += 78 * self._include_cvel_from_observation + obs_shape += 17 * self._include_qfrc_actuator_from_observation + obs_shape += 78 * self._include_cfrc_ext_from_observation_from_observation + obs_shape += 2 * (not self._exclude_current_positions_from_observation) + observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 ) From 081236bfe64224d214aee1dfe1f986e828c3b92e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 15 May 2023 10:47:42 +0300 Subject: [PATCH 036/160] inv_double_pend update doc --- .../envs/mujoco/inverted_double_pendulum_v5.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 22cae563..9aee9272 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -38,7 +38,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. - The observation is a `ndarray` with shape `(10,)` where the elements correspond to the following: + The observation is a `ndarray` with shape `(9,)` where the elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | ----------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | @@ -50,9 +50,9 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): | 5 | velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | | 6 | angular velocity of the angle between the cart and the first pole | -Inf | Inf | hinge | hinge | angular velocity (rad/s) | | 7 | angular velocity of the angle between the two poles | -Inf | Inf | hinge2 | hinge | angular velocity (rad/s) | - | 8 | constraint force - x | -Inf | Inf | slider | slide | Force (N) | - | 9 | constraint force - y | -Inf | Inf | slider | slide | Force (N) | - | excluded | constraint force - z | -Inf | Inf | slider | slide | Force (N) | + | 8 | constraint force | -Inf | Inf | slider | slide | Force (N) | + | excluded | constraint force | -Inf | Inf | hinge | slide | Force (N) | + | excluded | constraint force | -Inf | Inf | hinge2 | slide | Force (N) | There is physical contact between the robots and their environment - and Mujoco @@ -110,6 +110,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): ``` ## Version History + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward_survive" being 10 on every step (even on terminal steps). Removed "constraint force" of the hinges from the observation space. Added `info` "reward_survive", "distance_penalty", "velocity_penalty". * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) * v2: All continuous control environments now use mujoco-py >= 1.50 @@ -127,7 +128,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): } def __init__(self, **kwargs): - observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) + observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) MujocoEnv.__init__( self, "inverted_double_pendulum.xml", @@ -165,6 +166,7 @@ def step(self, action): def _get_obs(self): assert self.data.qfrc_constraint[2] == 0 # TODO remove after validation + assert self.data.qfrc_constraint[1] == 0 # TODO remove after validation return np.concatenate( [ self.data.qpos[:1], # cart x pos From 22a35ce76570afddf54766309b3b255f0c264914 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 15:43:33 +0300 Subject: [PATCH 037/160] Ant doc update --- gymnasium_robotics/envs/mujoco/ant_v5.py | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index d132ce74..e5f96e11 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -82,30 +82,30 @@ class AntEnv(MujocoEnv, utils.EzPickle): | excluded | y-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | - Additionally, after all the positional and velocity based values in the table, - the observation contains: - - *cfrc_ext:* - If version < `v4` or `use_contact_forces` is `True` then the observation space is extended by 14*6 = 84 elements, which are contact forces + Additionally, after all the positional and velocity based values in the table, the observation contains: + - *cfrc_ext:* 13*6 = 78 elements, which are contact forces (external forces - force x, y, z and torque x, y, z) applied to the - center of mass of each of the body parts. The 14 body parts are: + center of mass of each of the body parts. + The 13 body parts are: | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body parts | - | --- | --- | ------------ | - | 0 | excluded | worldbody (note: forces are always full of zeros) | - | 1 | 0 | torso | - | 2 | 1 | front_left_leg | - | 3 | 2 | aux_1 (front left leg) | - | 4 | 3 | ankle_1 (front left leg) | - | 5 | 4 | front_right_leg | - | 6 | 5 | aux_2 (front right leg) | - | 7 | 6 | ankle_2 (front right leg) | - | 8 | 7 | back_leg (back left leg) | - | 9 | 8 | aux_3 (back left leg) | - | 10 | 9 | ankle_3 (back left leg) | - | 11 | 10 | right_back_leg | - | 12 | 11 | aux_4 (back right leg) | - | 13 | 12 | ankle_4 (back right leg) | - + | ---| --- | ------------ | + | 0 |excluded| worldbody (note: forces are always full of zeros) | + | 1 |0 | torso | + | 2 |1 | front_left_leg | + | 3 |2 | aux_1 (front left leg) | + | 4 |3 | ankle_1 (front left leg) | + | 5 |4 | front_right_leg | + | 6 |5 | aux_2 (front right leg) | + | 7 |6 | ankle_2 (front right leg) | + | 8 |7 | back_leg (back left leg) | + | 9 |8 | aux_3 (back left leg) | + | 10 |9 | ankle_3 (back left leg) | + | 11 |10 | right_back_leg | + | 12 |11 | aux_4 (back right leg) | + | 13 |12 | ankle_4 (back right leg) | + + ###If version < `v4` or `use_contact_forces` is `True` then the observation space is extended by The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). From 361cb0fb111ab49da170bfba9d4c65364b38b19b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 15:45:14 +0300 Subject: [PATCH 038/160] reacher add changelogg --- gymnasium_robotics/envs/mujoco/reacher_v5.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 86787cac..bc6b3172 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -90,14 +90,12 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): is set. The default setting has a framerate of 2 and a *dt = 2 * 0.01 = 0.02* ## Episode End - The episode ends when any of the following happens: 1. Truncation: The episode duration reaches a 50 timesteps (with a new random target popping up if the reacher's fingertip reaches it before 50 timesteps) 2. Termination: Any of the state space values is no longer finite. ## Arguments - No additional arguments are currently supported (in v2 and lower), but modifications can be made to the XML file in the assets folder (or by changing the path to a modified XML file in another folder).. @@ -111,7 +109,7 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ## Version History - + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Remove "z - position_fingertip" from the observation space. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v2: All continuous control environments now use mujoco-py >= 1.50 * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. From ac7a0a2e224b4131928af58b1d4b260a1e584dd6 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 16:06:05 +0300 Subject: [PATCH 039/160] `xml_file` doc fix for hopper and walker --- gymnasium_robotics/envs/mujoco/hopper_v5.py | 4 ++-- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 0016f198..a6941a62 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -118,7 +118,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"hopper.xml"` | Path to a MuJoCo model | + | `xml_file` | **str** | `"hopper_v5.xml"` | Path to a MuJoCo model | | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `0.001` | Weight for _ctrl_cost_ reward (see section on reward) | | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | @@ -149,7 +149,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): def __init__( self, - xml_file="hopper-v5.xml", + xml_file="hopper_v5.xml", forward_reward_weight=1.0, ctrl_cost_weight=1e-3, healthy_reward=1.0, diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index f7de2369..415d6e91 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -123,7 +123,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"walker2d.xml"` | Path to a MuJoCo model | + | `xml_file` | **str** |`"walker2d_v5.xml"`| Path to a MuJoCo model | | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | | `healthy_reward` | **float** | `1.0` | Constant reward given if the ant is "healthy" after timestep | From 0bb5798f87e6ac3d83f59266d4d5679ff24fd0b2 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 16:06:37 +0300 Subject: [PATCH 040/160] update humanoid doc --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 84 +++++++++++++++---- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index a067a0f5..e020f654 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -24,7 +24,7 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): This environment is based on the environment introduced by Tassa, Erez and Todorov in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a pair of - legs and arms. The legs each consist of two links, and so the arms (representing the knees and + legs and arms. The legs each consist of three body parts, and the arms 2 body parts (representing the knees and elbows respectively). The goal of the environment is to walk forward as fast as possible without falling over. ## Action Space @@ -42,28 +42,27 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | 7 | Torque applied on the rotor between torso/abdomen and the left hip (x-coordinate) | -0.4 | 0.4 | left_hip_x (left_thigh) | hinge | torque (N m) | | 8 | Torque applied on the rotor between torso/abdomen and the left hip (z-coordinate) | -0.4 | 0.4 | left_hip_z (left_thigh) | hinge | torque (N m) | | 9 | Torque applied on the rotor between torso/abdomen and the left hip (y-coordinate) | -0.4 | 0.4 | left_hip_y (left_thigh) | hinge | torque (N m) | - | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | - | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | - | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | - | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | - | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | - | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | - | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | + | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | + | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | + | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | + | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | + | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | + | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | + | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | ## Observation Space - Observations consist of positional values of different body parts of the Humanoid, - followed by the velocities of those individual parts (their derivatives) with all the - positions ordered before all the velocities. + followed by the velocities of those individual parts (their derivatives) with all the + positions ordered before all the velocities. By default, observations do not include the x- and y-coordinates of the torso. These may be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will have 378 dimensions where the first two dimensions + In that case, the observation space will be a `Box(-Inf, Inf, (378,), float64)` where the first two observations represent the x- and y-coordinates of the torso. Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - However, by default, the observation is a `ndarray` with shape `(376,)` where the elements correspond to the following: + However, by default, the observation is a `Box(-Inf, Inf, (376,), float64)`. The elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | @@ -77,7 +76,7 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | 7 | x-angle of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | angle (rad) | | 8 | x-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | angle (rad) | | 9 | z-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | angle (rad) | - | 19 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | + | 10 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | | 11 | angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | angle (rad) | | 12 | x-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | angle (rad) | | 13 | z-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | angle (rad) | @@ -111,7 +110,9 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | 41 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | | 42 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | | 43 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | - | 44 | angular velocitty of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | + | 44 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | + | excluded | x-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | + | excluded | y-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | Additionally, after all the positional and velocity based values in the table, the observation contains (in order): @@ -127,6 +128,53 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): where *nbody* stands for the number of bodies in the robot and *nv* stands for the number of degrees of freedom (*= dim(qvel)*) + The body parts are: + + | id (for `v2`,`v3`,`v4`) | body part | + | --- | ------------ | + | 0 | worldBody (note: all values are constant 0) | + | 1 | torso | + | 2 | lwaist | + | 3 | pelvis | + | 4 | right_thigh | + | 5 | right_sin | + | 6 | right_foot | + | 7 | left_thigh | + | 8 | left_sin | + | 9 | left_foot | + | 10 | right_upper_arm | + | 11 | right_lower_arm | + | 12 | left_upper_arm | + | 13 | left_lower_arm | + + The joints are: + + | id (for `v2`,`v3`,`v4`) | joint | + | --- | ------------ | + | 0 | root | + | 1 | root | + | 2 | root | + | 3 | root | + | 4 | root | + | 5 | root | + | 6 | abdomen_z | + | 7 | abdomen_y | + | 8 | abdomen_x | + | 9 | right_hip_x | + | 10 | right_hip_z | + | 11 | right_hip_y | + | 12 | right_knee | + | 13 | left_hip_x | + | 14 | left_hiz_z | + | 15 | left_hip_y | + | 16 | left_knee | + | 17 | right_shoulder1 | + | 18 | right_shoulder2 | + | 19 | right_elbow| + | 20 | left_shoulder1 | + | 21 | left_shoulder2 | + | 22 | left_elfbow | + The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). @@ -177,7 +225,6 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. ```python @@ -198,11 +245,16 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | `forward_reward_weight` | **float** | `1.25` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ term (see section on reward) | | `contact_cost_weight` | **float** | `5e-7` | Weight for _contact_cost_ term (see section on reward) | + | `contact_cost_range` | **float** | `(-np.inf, 10.0) | | | `healthy_reward` | **float** | `5.0` | Constant reward given if the humanoid is "healthy" after timestep | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + | `include_cinert_from_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| + | `include_cvel_from_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | + | `include_qfrc_actuator_from_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | + | `include_cfrc_ext_from_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History From eade7e2860f600a498be548ed21590a12f84b993 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 16:12:05 +0300 Subject: [PATCH 041/160] Ant obs doc fix --- gymnasium_robotics/envs/mujoco/ant_v5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index e5f96e11..0e06ae69 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -42,12 +42,12 @@ class AntEnv(MujocoEnv, utils.EzPickle): By default, observations do not include the x- and y-coordinates of the ant's torso. These may be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (29,), float64)` where the first two observations + In that case, the observation space will be a `Box(-Inf, Inf, (107,), float64)` where the first two observations represent the x- and y- coordinates of the ant's torso. Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates of the torso will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - However, by default, observation Space is a `Box(-Inf, Inf, (27,), float64)` where the elements correspond to the following: + However, by default, observation Space is a `Box(-Inf, Inf, (105,), float64)` where the elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | |-----|--------------------------------------------------------------|--------|--------|----------------------------------------|-------|--------------------------| From ed32b61db70920b3a0abfcceff1b25d0436ea09b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 16:16:23 +0300 Subject: [PATCH 042/160] add `__credits__` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 +- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/pusher_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/reacher_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/swimmer_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 ++ 10 files changed, 18 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 0e06ae69..681ede21 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index ec5720e5..eac891d8 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -1,4 +1,4 @@ -__credits__ = ["Rushiv Arora"] +__credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] import numpy as np from gymnasium import utils diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index a6941a62..02e30669 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index e020f654..c0e7fc35 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 9aee9272..3e699c55 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index e86d5277..35574a4a 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 9591279e..c4d2a7c5 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index bc6b3172..4652b8b0 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 5cf3c289..49abd58c 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -1,4 +1,4 @@ -__credits__ = ["Rushiv Arora"] +__credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] import numpy as np from gymnasium import utils diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 415d6e91..fc32f707 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv From 177e321a8dfd668da71c642b62bbaacd887333be Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 16:30:46 +0300 Subject: [PATCH 043/160] `pre-commit` --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 8 +++++--- gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py | 2 -- gymnasium_robotics/envs/mujoco/reacher_v5.py | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index c0e7fc35..a42c710f 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -308,7 +308,7 @@ def __init__( include_cvel_from_observation, include_qfrc_actuator_from_observation, include_cfrc_ext_from_observation, - **kwargs + **kwargs, ) self._forward_reward_weight = forward_reward_weight @@ -327,7 +327,9 @@ def __init__( self._include_cinert_from_observation = include_cinert_from_observation self._include_cvel_from_observation = include_cvel_from_observation - self._include_qfrc_actuator_from_observation = include_qfrc_actuator_from_observation + self._include_qfrc_actuator_from_observation = ( + include_qfrc_actuator_from_observation + ) self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation obs_shape = 45 @@ -347,7 +349,7 @@ def __init__( 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs + **kwargs, ) @property diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 35574a4a..a66f6297 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -1,12 +1,10 @@ __credits__ = ["Kallinteris-Andreas"] import numpy as np - from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv from gymnasium.spaces import Box - DEFAULT_CAMERA_CONFIG = { "trackbodyid": 0, "distance": 2.04, diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 4652b8b0..081a0977 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -177,7 +177,9 @@ def reset_model(self): def _get_obs(self): theta = self.data.qpos.flat[:2] - assert (self.get_body_com("fingertip") - self.get_body_com("target"))[2] == 0 # TODO remove after validation + assert (self.get_body_com("fingertip") - self.get_body_com("target"))[ + 2 + ] == 0 # TODO remove after validation return np.concatenate( [ np.cos(theta), From c57b0dc6005db03e191e94beac61f23cb0314a76 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 17 May 2023 19:56:22 +0300 Subject: [PATCH 044/160] doc update --- gymnasium_robotics/envs/mujoco/ant_v5.py | 1 - gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 +- gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py | 1 + gymnasium_robotics/envs/mujoco/pusher_v5.py | 3 --- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 681ede21..30de978b 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -165,7 +165,6 @@ class AntEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. ```python diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index eac891d8..2b21a78a 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -85,7 +85,7 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the control and has a default value of 0.1 - The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms + The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. ## Starting State All observations start in state (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 3e699c55..844d2e74 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -82,6 +82,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): fast *0.001 * v12 + 0.005 * v2 2* The total reward returned is ***reward*** *=* *alive_bonus - distance_penalty - velocity_penalty* + and `info` will also contain the individual reward terms. ## Starting State All observations start in state diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index c4d2a7c5..225ea27c 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -32,7 +32,6 @@ class PusherEnv(MujocoEnv, utils.EzPickle): | 6 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | ## Observation Space - Observations consist of - Angle of rotational joints on the pusher @@ -106,14 +105,12 @@ class PusherEnv(MujocoEnv, utils.EzPickle): The default framerate is 5 with each frame lasting for 0.01, giving rise to a *dt = 5 * 0.01 = 0.05* ## Episode End - The episode ends when any of the following happens: 1. Truncation: The episode duration reaches a 100 timesteps. 2. Termination: Any of the state space values is no longer finite. ## Arguments - No additional arguments are currently supported (in v2 and lower), but modifications can be made to the XML file in the assets folder (or by changing the path to a modified XML file in another folder).. From 404481253fea69f1e67a14d39301a8365cc270b5 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 18 May 2023 11:34:51 +0300 Subject: [PATCH 045/160] doc args cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 11 ++--------- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 12 ++---------- gymnasium_robotics/envs/mujoco/hopper_v5.py | 11 ++--------- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 11 ++--------- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 12 ++---------- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 11 ++--------- 6 files changed, 12 insertions(+), 56 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 30de978b..05bdfb48 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -165,18 +165,11 @@ class AntEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('Ant-v2') - ``` - - v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Ant-v4', ctrl_cost_weight=0.1, ...) + env = gym.make('Ant-v5', ctrl_cost_weight=0.1, ...) ``` | Parameter | Type | Default |Description | diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 2b21a78a..f8f2e431 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -100,19 +100,11 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): The episode truncates when the episode length is greater than 1000. ## Arguments - - No additional arguments are currently supported in v2 and lower. - - ```python - import gymnasium as gym - env = gym.make('HalfCheetah-v2') - ``` - - v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('HalfCheetah-v4', ctrl_cost_weight=0.1, ....) + env = gym.make('HalfCheetah-v5', ctrl_cost_weight=0.1, ....) ``` | Parameter | Type | Default | Description | diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 02e30669..6e92c61e 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -104,18 +104,11 @@ class HopperEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('Hopper-v2') - ``` - - v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Hopper-v4', ctrl_cost_weight=0.1, ....) + env = gym.make('Hopper-v5', ctrl_cost_weight=0.1, ....) ``` | Parameter | Type | Default | Description | diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index a42c710f..373ccb17 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -227,18 +227,11 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('Humanoid-v4') - ``` - - v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Humanoid-v4', ctrl_cost_weight=0.1, ....) + env = gym.make('Humanoid-v5', ctrl_cost_weight=0.1, ....) ``` | Parameter | Type | Default | Description | diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 49abd58c..d4bd1f0a 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -91,19 +91,11 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): The episode truncates when the episode length is greater than 1000. ## Arguments - - No additional arguments are currently supported in v2 and lower. - - ```python - import gymnasium as gym - gym.make('Swimmer-v4') - ``` - - v3 and v4 take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('Swimmer-v4', ctrl_cost_weight=0.1, ....) + env = gym.make('Swimmer-v5', ctrl_cost_weight=0.1, ....) ``` | Parameter | Type | Default | Description | diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index fc32f707..5b9ac187 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -109,18 +109,11 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments - No additional arguments are currently supported in v2 and lower. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('Walker2d-v4') - ``` - - v3 and beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Walker2d-v4', ctrl_cost_weight=0.1, ....) + env = gym.make('Walker2d-v5', ctrl_cost_weight=0.1, ....) ``` | Parameter | Type | Default | Description | From 5ccaca81e31109608279cf7cd75c113ae5f9590f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 18 May 2023 14:01:36 +0300 Subject: [PATCH 046/160] fix pusher changelogs --- gymnasium_robotics/envs/mujoco/pusher_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 225ea27c..f9bc9883 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -132,7 +132,7 @@ class PusherEnv(MujocoEnv, utils.EzPickle): * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. "reward_near" is added to the `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. + * v1: max_time_steps raised to 1000 for robot based tasks (not including pusher, which has a max_time_steps of 100). Added reward_threshold to environments. * v0: Initial versions release (1.0.0). """ From 18bada33331e496c1649a4db677423d5e1c3019b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 18 May 2023 14:01:58 +0300 Subject: [PATCH 047/160] `reacher` & `pusher` add xml_file argument --- gymnasium_robotics/envs/mujoco/pusher_v5.py | 10 +++++++--- gymnasium_robotics/envs/mujoco/reacher_v5.py | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index f9bc9883..e0fe7177 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -129,7 +129,7 @@ class PusherEnv(MujocoEnv, utils.EzPickle): ``` ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. "reward_near" is added to the `info`. + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. "reward_near" is added to the `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. * v2: All continuous control environments now use mujoco-py >= 1.50. * v1: max_time_steps raised to 1000 for robot based tasks (not including pusher, which has a max_time_steps of 100). Added reward_threshold to environments. @@ -145,12 +145,16 @@ class PusherEnv(MujocoEnv, utils.EzPickle): "render_fps": 20, } - def __init__(self, **kwargs): + def __init__( + self, + xml_file="pusher.xml", + **kwargs, + ): utils.EzPickle.__init__(self, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) MujocoEnv.__init__( self, - "pusher.xml", + xml_file, 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 081a0977..5a2afdc5 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -111,7 +111,7 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Remove "z - position_fingertip" from the observation space. + * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. Remove "z - position_fingertip" from the observation space. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v2: All continuous control environments now use mujoco-py >= 1.50 * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. @@ -127,12 +127,16 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): "render_fps": 50, } - def __init__(self, **kwargs): + def __init__( + self, + xml_file="reacher.xml", + **kwargs, + ): utils.EzPickle.__init__(self, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) MujocoEnv.__init__( self, - "reacher.xml", + xml_file, 2, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, From 711a9ca92d02d8603973321277a873abf2dec801 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 18 May 2023 18:20:38 +0300 Subject: [PATCH 048/160] fix typos in `humanoid` --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 373ccb17..fae59d60 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -329,7 +329,7 @@ def __init__( obs_shape += 130 * self._include_cinert_from_observation obs_shape += 78 * self._include_cvel_from_observation obs_shape += 17 * self._include_qfrc_actuator_from_observation - obs_shape += 78 * self._include_cfrc_ext_from_observation_from_observation + obs_shape += 78 * self._include_cfrc_ext_from_observation obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( @@ -358,7 +358,7 @@ def control_cost(self, action): @property def contact_cost(self): - contact_forces = self.sim.data.cfrc_ext + contact_forces = self.data.cfrc_ext contact_cost = self._contact_cost_weight * np.sum(np.square(contact_forces)) min_cost, max_cost = self._contact_cost_range contact_cost = np.clip(contact_cost, min_cost, max_cost) From 6e99ff3eedd4a66a839bb7a711cb0c3737084b22 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 14:38:23 +0300 Subject: [PATCH 049/160] rename obs args --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index fae59d60..edde993a 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -246,10 +246,10 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - | `include_cinert_from_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| - | `include_cvel_from_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | - | `include_qfrc_actuator_from_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | - | `include_cfrc_ext_from_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | + | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| + | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | + | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | + | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History @@ -280,11 +280,11 @@ def __init__( terminate_when_unhealthy=True, healthy_z_range=(1.0, 2.0), reset_noise_scale=1e-2, - exclude_current_positions_from_observation=True, - include_cinert_from_observation=True, - include_cvel_from_observation=True, - include_qfrc_actuator_from_observation=True, - include_cfrc_ext_from_observation=True, + exclude_current_positions_in_observation=True, + include_cinert_in_observation=True, + include_cvel_in_observation=True, + include_qfrc_actuator_in_observation=True, + include_cfrc_ext_in_observation=True, **kwargs, ): utils.EzPickle.__init__( @@ -296,11 +296,11 @@ def __init__( terminate_when_unhealthy, healthy_z_range, reset_noise_scale, - exclude_current_positions_from_observation, - include_cinert_from_observation, - include_cvel_from_observation, - include_qfrc_actuator_from_observation, - include_cfrc_ext_from_observation, + exclude_current_positions_in_observation, + include_cinert_in_observation, + include_cvel_in_observation, + include_qfrc_actuator_in_observation, + include_cfrc_ext_in_observation, **kwargs, ) @@ -314,23 +314,23 @@ def __init__( self._reset_noise_scale = reset_noise_scale - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation + self._exclude_current_positions_in_observation = ( + exclude_current_positions_in_observation ) - self._include_cinert_from_observation = include_cinert_from_observation - self._include_cvel_from_observation = include_cvel_from_observation - self._include_qfrc_actuator_from_observation = ( - include_qfrc_actuator_from_observation + self._include_cinert_in_observation = include_cinert_in_observation + self._include_cvel_in_observation = include_cvel_in_observation + self._include_qfrc_actuator_in_observation = ( + include_qfrc_actuator_in_observation ) - self._include_cfrc_ext_from_observation = include_cfrc_ext_from_observation + self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation obs_shape = 45 - obs_shape += 130 * self._include_cinert_from_observation - obs_shape += 78 * self._include_cvel_from_observation - obs_shape += 17 * self._include_qfrc_actuator_from_observation - obs_shape += 78 * self._include_cfrc_ext_from_observation - obs_shape += 2 * (not self._exclude_current_positions_from_observation) + obs_shape += 130 * self._include_cinert_in_observation + obs_shape += 78 * self._include_cvel_in_observation + obs_shape += 17 * self._include_qfrc_actuator_in_observation + obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_shape += 2 * (not self._exclude_current_positions_in_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 @@ -380,31 +380,31 @@ def _get_obs(self): position = self.data.qpos.flat.copy() velocity = self.data.qvel.flat.copy() - if self._include_cinert_from_observation is True: + if self._include_cinert_in_observation is True: com_inertia = self.data.cinert[1:].flat.copy() else: com_inertia = np.array([]) - if self._include_cvel_from_observation is True: + if self._include_cvel_in_observation is True: com_velocity = self.data.cvel[1:].flat.copy() else: com_velocity = np.array([]) - if self._include_qfrc_actuator_from_observation is True: + if self._include_qfrc_actuator_in_observation is True: actuator_forces = self.data.qfrc_actuator[6:].flat.copy() else: actuator_forces = np.array([]) - if self._include_cfrc_ext_from_observation is True: + if self._include_cfrc_ext_in_observation is True: external_contact_forces = self.data.cfrc_ext[1:].flat.copy() else: external_contact_forces = np.array([]) # TODO remove after validation - assert (self.data.cinert[0].flat.copy == 0).all() - assert (self.data.cvel[0].flat.copy == 0).all() - assert (self.data.qfrc_actuator[:6].flat.copy == 0).all() - assert (self.data.cfrc_ext[0].flat.copy == 0).all() + assert (self.data.cinert[0].flat.copy() == 0).all() + assert (self.data.cvel[0].flat.copy() == 0).all() + assert (self.data.qfrc_actuator[:6].flat.copy() == 0).all() + assert (self.data.cfrc_ext[0].flat.copy() == 0).all() - if self._exclude_current_positions_from_observation: + if self._exclude_current_positions_in_observation: position = position[2:] return np.concatenate( From caa8fe966724d1b731e1a1619bba1d0c6673adc2 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 15:36:12 +0300 Subject: [PATCH 050/160] ant add `include_cfrc_ext_in_observation` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 26 +++++++++--------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 05bdfb48..88752087 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -107,8 +107,6 @@ class AntEnv(MujocoEnv, utils.EzPickle): | 12 |11 | aux_4 (back right leg) | | 13 |12 | ankle_4 (back right leg) | - ###If version < `v4` or `use_contact_forces` is `True` then the observation space is extended by - The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). @@ -134,11 +132,11 @@ class AntEnv(MujocoEnv, utils.EzPickle): force is too large. It is calculated *`contact_cost_weight` * sum(clip(external contact force to `contact_force_range`)2)*. - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*. - - But if `use_contact_forces=True` or version < `v4` The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost*. + But if `use_contact_forces=false` on `v4` + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*. + In either case `info` will also contain the individual reward terms. ## Starting State @@ -176,7 +174,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): |-------------------------|------------|--------------|-------------------------------| | `xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | | `ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | - | `use_contact_forces` | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | + | `use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | | `contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | | `terminate_when_unhealthy` | **bool**| `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | @@ -206,7 +204,6 @@ def __init__( self, xml_file="ant.xml", ctrl_cost_weight=0.5, - use_contact_forces=True, contact_cost_weight=5e-4, healthy_reward=1.0, terminate_when_unhealthy=True, @@ -214,13 +211,13 @@ def __init__( contact_force_range=(-1.0, 1.0), reset_noise_scale=0.1, exclude_current_positions_from_observation=True, + include_cfrc_ext_in_observation=True, **kwargs ): utils.EzPickle.__init__( self, xml_file, ctrl_cost_weight, - use_contact_forces, contact_cost_weight, healthy_reward, terminate_when_unhealthy, @@ -228,6 +225,7 @@ def __init__( contact_force_range, reset_noise_scale, exclude_current_positions_from_observation, + include_cfrc_ext_in_observation, **kwargs ) @@ -242,17 +240,14 @@ def __init__( self._reset_noise_scale = reset_noise_scale - self._use_contact_forces = use_contact_forces - self._exclude_current_positions_from_observation = ( exclude_current_positions_from_observation ) + self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation obs_shape = 27 - if not exclude_current_positions_from_observation: - obs_shape += 2 - if use_contact_forces: - obs_shape += 78 + obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_shape += 2 * (not self._exclude_current_positions_in_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 @@ -334,7 +329,6 @@ def step(self, action): "x_velocity": x_velocity, "y_velocity": y_velocity, } - # if self._use_contact_forces: reward = rewards - costs @@ -349,7 +343,7 @@ def _get_obs(self): if self._exclude_current_positions_from_observation: position = position[2:] - if self._use_contact_forces: + if self._include_cfrc_ext_in_observation: assert ( self.contact_forces[0].flat.copy() == 0 ).all() # TODO remove after validation From e9d8ba6e3542188e6e26a63ccfa5482a1f9d7218 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 18:18:20 +0300 Subject: [PATCH 051/160] register new envs --- gymnasium_robotics/__init__.py | 294 +++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index 679ca59c..10f06ae1 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1103,6 +1103,300 @@ def _merge(a, b): max_episode_steps=280, ) + # Mujoco + # ---------------------------------------- + + # Manipulation + + """ + register( + id="Reacher-v2", + entry_point="gymnasium_robotics.envs.mujoco:ReacherEnv", + max_episode_steps=50, + reward_threshold=-3.75, + ) + + register( + id="Reacher-v4", + entry_point="gymnasium_robotics.envs.mujoco.reacher_v4:ReacherEnv", + max_episode_steps=50, + reward_threshold=-3.75, + ) + """ + + register( + id="Reacher-v5", + entry_point="gymnasium_robotics.envs.mujoco.reacher_v5:ReacherEnv", + max_episode_steps=50, + reward_threshold=-3.75, + ) + + """ + register( + id="Pusher-v2", + entry_point="gymnasium_robotics.envs.mujoco:PusherEnv", + max_episode_steps=100, + reward_threshold=0.0, + ) + + register( + id="Pusher-v4", + entry_point="gymnasium_robotics.envs.mujoco.pusher_v4:PusherEnv", + max_episode_steps=100, + reward_threshold=0.0, + ) + """ + + register( + id="Pusher-v5", + entry_point="gymnasium_robotics.robotics.envs.mujoco.pusher_v5:PusherEnv", + max_episode_steps=100, + reward_threshold=0.0, + ) + + # Balance + + """ + register( + id="InvertedPendulum-v2", + entry_point="gymnasium_robotics.envs.mujoco:InvertedPendulumEnv", + max_episode_steps=1000, + reward_threshold=950.0, + ) + + register( + id="InvertedPendulum-v4", + entry_point="gymnasium_robotics.envs.mujoco.inverted_pendulum_v4:InvertedPendulumEnv", + max_episode_steps=1000, + reward_threshold=950.0, + ) + """ + + register( + id="InvertedPendulum-v5", + entry_point="gymnasium_robotics.envs.mujoco.inverted_pendulum_v5:InvertedPendulumEnv", + max_episode_steps=1000, + reward_threshold=950.0, + ) + + """ + register( + id="InvertedDoublePendulum-v2", + entry_point="gymnasium_robotics.envs.mujoco:InvertedDoublePendulumEnv", + max_episode_steps=1000, + reward_threshold=9100.0, + ) + + register( + id="InvertedDoublePendulum-v4", + entry_point="gymnasium_robotics.envs.mujoco.inverted_double_pendulum_v4:InvertedDoublePendulumEnv", + max_episode_steps=1000, + reward_threshold=9100.0, + ) + """ + + register( + id="InvertedDoublePendulum-v5", + entry_point="gymnasium_robotics.envs.mujoco.inverted_double_pendulum_v5:InvertedDoublePendulumEnv", + max_episode_steps=1000, + reward_threshold=9100.0, + ) + + # Running + + """ + register( + id="HalfCheetah-v2", + entry_point="gymnasium_robotics.envs.mujoco:HalfCheetahEnv", + max_episode_steps=1000, + reward_threshold=4800.0, + ) + + register( + id="HalfCheetah-v3", + entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v3:HalfCheetahEnv", + max_episode_steps=1000, + reward_threshold=4800.0, + ) + + register( + id="HalfCheetah-v4", + entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v4:HalfCheetahEnv", + max_episode_steps=1000, + reward_threshold=4800.0, + ) + """ + + register( + id="HalfCheetah-v5", + entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v5:HalfCheetahEnv", + max_episode_steps=1000, + reward_threshold=4800.0, + ) + + """ + register( + id="Hopper-v2", + entry_point="gymnasium_robotics.envs.mujoco:HopperEnv", + max_episode_steps=1000, + reward_threshold=3800.0, + ) + + register( + id="Hopper-v3", + entry_point="gymnasium_robotics.envs.mujoco.hopper_v3:HopperEnv", + max_episode_steps=1000, + reward_threshold=3800.0, + ) + + register( + id="Hopper-v4", + entry_point="gymnasium_robotics.envs.mujoco.hopper_v4:HopperEnv", + max_episode_steps=1000, + reward_threshold=3800.0, + ) + """ + + register( + id="Hopper-v5", + entry_point="gymnasium_robotics.envs.mujoco.hopper_v5:HopperEnv", + max_episode_steps=1000, + reward_threshold=3800.0, + ) + + """ + register( + id="Swimmer-v2", + entry_point="gymnasium_robotics.envs.mujoco:SwimmerEnv", + max_episode_steps=1000, + reward_threshold=360.0, + ) + + register( + id="Swimmer-v3", + entry_point="gymnasium_robotics.envs.mujoco.swimmer_v3:SwimmerEnv", + max_episode_steps=1000, + reward_threshold=360.0, + ) + + register( + id="Swimmer-v4", + entry_point="gymnasium_robotics.envs.mujoco.swimmer_v4:SwimmerEnv", + max_episode_steps=1000, + reward_threshold=360.0, + ) + """ + + register( + id="Swimmer-v5", + entry_point="gymnasium_robotics.envs.mujoco.swimmer_v5:SwimmerEnv", + max_episode_steps=1000, + reward_threshold=360.0, + ) + + """ + register( + id="Walker2d-v2", + max_episode_steps=1000, + entry_point="gymnasium_robotics.envs.mujoco:Walker2dEnv", + ) + + register( + id="Walker2d-v3", + max_episode_steps=1000, + entry_point="gymnasium_robotics.envs.mujoco.walker2d_v3:Walker2dEnv", + ) + + register( + id="Walker2d-v4", + max_episode_steps=1000, + entry_point="gymnasium_robotics.envs.mujoco.walker2d_v4:Walker2dEnv", + ) + """ + + register( + id="Walker2d-v5", + max_episode_steps=1000, + entry_point="gymnasium_robotics.envs.mujoco.walker2d_v5:Walker2dEnv", + ) + + """ + register( + id="Ant-v2", + entry_point="gymnasium_robotics.envs.mujoco:AntEnv", + max_episode_steps=1000, + reward_threshold=6000.0, + ) + + register( + id="Ant-v3", + entry_point="gymnasium_robotics.envs.mujoco.ant_v3:AntEnv", + max_episode_steps=1000, + reward_threshold=6000.0, + ) + + register( + id="Ant-v4", + entry_point="gymnasium_robotics.envs.mujoco.ant_v4:AntEnv", + max_episode_steps=1000, + reward_threshold=6000.0, + ) + """ + + register( + id="Ant-v5", + entry_point="gymnasium_robotics.envs.mujoco.ant_v4:AntEnv", + max_episode_steps=1000, + reward_threshold=6000.0, + ) + + """ + register( + id="Humanoid-v2", + entry_point="gymnasium_robotics.envs.mujoco:HumanoidEnv", + max_episode_steps=1000, + ) + + register( + id="Humanoid-v3", + entry_point="gymnasium_robotics.envs.mujoco.humanoid_v3:HumanoidEnv", + max_episode_steps=1000, + ) + + register( + id="Humanoid-v4", + entry_point="gymnasium_robotics.envs.mujoco.humanoid_v4:HumanoidEnv", + max_episode_steps=1000, + ) + """ + + register( + id="Humanoid-v5", + entry_point="gymnasium_robotics.envs.mujoco.humanoid_v5:HumanoidEnv", + max_episode_steps=1000, + ) + + """ + register( + id="HumanoidStandup-v2", + entry_point="gymnasium_robotics.envs.mujoco:HumanoidStandupEnv", + max_episode_steps=1000, + ) + + register( + id="HumanoidStandup-v4", + entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v4:HumanoidStandupEnv", + max_episode_steps=1000, + ) + """ + + register( + id="HumanoidStandup-v5", + entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", + max_episode_steps=1000, + ) + + __version__ = "1.2.1" From eb838b550486e074fe1fcca58be071e88e356920 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 19:13:47 +0300 Subject: [PATCH 052/160] fix register --- gymnasium_robotics/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index 10f06ae1..82bd2ad5 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1345,7 +1345,7 @@ def _merge(a, b): register( id="Ant-v5", - entry_point="gymnasium_robotics.envs.mujoco.ant_v4:AntEnv", + entry_point="gymnasium_robotics.envs.mujoco.ant_v5:AntEnv", max_episode_steps=1000, reward_threshold=6000.0, ) @@ -1390,12 +1390,11 @@ def _merge(a, b): ) """ - register( - id="HumanoidStandup-v5", - entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", - max_episode_steps=1000, - ) - + #register( + #id="HumanoidStandup-v5", + #entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", + #max_episode_steps=1000, + #) __version__ = "1.2.1" From 571d057f0b61a6d9546ff3432fa7c8886e4de9fc Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 19:29:14 +0300 Subject: [PATCH 053/160] typo fix --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 88752087..65cde7eb 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -247,7 +247,7 @@ def __init__( obs_shape = 27 obs_shape += 78 * self._include_cfrc_ext_in_observation - obs_shape += 2 * (not self._exclude_current_positions_in_observation) + obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 From 8ef1c90b3db77c5cba7ad0da61b4f18b3a1159ec Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 19:39:00 +0300 Subject: [PATCH 054/160] fix humanoid pickle --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index edde993a..00880485 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -292,6 +292,7 @@ def __init__( xml_file, forward_reward_weight, ctrl_cost_weight, + contact_cost_range, healthy_reward, terminate_when_unhealthy, healthy_z_range, From 670e4853c69832cf442e2f5e71e05fba2eb37581 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 19 May 2023 19:59:24 +0300 Subject: [PATCH 055/160] fix humanoid pickle (for real this time) --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 00880485..bccbd060 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -292,6 +292,7 @@ def __init__( xml_file, forward_reward_weight, ctrl_cost_weight, + contact_cost_weight, contact_cost_range, healthy_reward, terminate_when_unhealthy, From f02f7b402d7381147833428c10148bcd6d5d12d0 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 24 May 2023 15:53:48 +0300 Subject: [PATCH 056/160] add `reset_noise_scale` to double_pend --- .../mujoco/inverted_double_pendulum_v5.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 844d2e74..5650b3d2 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -130,8 +130,15 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): "render_fps": 20, } - def __init__(self, **kwargs): + def __init__( + self, + reset_noise_scale=0.1, + **kwargs, + ): + self._reset_noise_scale = reset_noise_scale + observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) + MujocoEnv.__init__( self, "inverted_double_pendulum.xml", @@ -140,7 +147,8 @@ def __init__(self, **kwargs): default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs ) - utils.EzPickle.__init__(self, **kwargs) + + utils.EzPickle.__init__(self, reset_noise_scale, **kwargs) def step(self, action): self.do_simulation(action, self.frame_skip) @@ -181,9 +189,12 @@ def _get_obs(self): ).ravel() def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + self.set_state( self.init_qpos - + self.np_random.uniform(low=-0.1, high=0.1, size=self.model.nq), - self.init_qvel + self.np_random.standard_normal(self.model.nv) * 0.1, + + self.np_random.uniform(low=noise_low, high=noise_high, size=self.model.nq), + self.init_qvel + self.np_random.standard_normal(self.model.nv) * self._reset_noise_scale, ) return self._get_obs() From 8f75f2454ccabd14980dd0e7489a125fa9114f95 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 24 May 2023 17:32:57 +0300 Subject: [PATCH 057/160] add new inv_double_pend args --- .../envs/mujoco/inverted_double_pendulum_v5.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 5650b3d2..734af487 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -72,7 +72,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): ## Rewards The reward consists of two parts: - *alive_bonus*: The goal is to make the second inverted pendulum stand upright - (within a certain angle limit) as long as possible - as such a reward of +10 is awarded + (within a certain angle limit) as long as possible - as such a reward of `` is awarded for each timestep that the second pole is upright. - *distance_penalty*: This reward is a measure of how far the *tip* of the second pendulum (the only free end) moves, and it is calculated as @@ -112,6 +112,12 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): env = gym.make('InvertedDoublePendulum-v2') ``` + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|-------------------------------| + | `xml_file` | **str** | `"inverted_double_pendulum.xml"` | Path to a MuJoCo model | + | `healthy_reward` | **float** | `10 | Constant reward given if the pendulum is "healthy" (upright) | + | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward_survive" being 10 on every step (even on terminal steps). Removed "constraint force" of the hinges from the observation space. Added `info` "reward_survive", "distance_penalty", "velocity_penalty". * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 @@ -132,23 +138,26 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): def __init__( self, + xml_file="inverted_double_pendulum.xml", + healthy_reward=10.0, reset_noise_scale=0.1, **kwargs, ): + self._healthy_reward = healthy_reward self._reset_noise_scale = reset_noise_scale observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) MujocoEnv.__init__( self, - "inverted_double_pendulum.xml", + xml_file, 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs ) - utils.EzPickle.__init__(self, reset_noise_scale, **kwargs) + utils.EzPickle.__init__(self, xml_file, reset_noise_scale, **kwargs) def step(self, action): self.do_simulation(action, self.frame_skip) @@ -162,7 +171,7 @@ def step(self, action): dist_penalty = 0.01 * x**2 + (y - 2) ** 2 vel_penalty = 1e-3 * v1**2 + 5e-3 * v2**2 - alive_bonus = 10 * int(not terminated) + alive_bonus = self._healthy_reward * int(not terminated) r = alive_bonus - dist_penalty - vel_penalty info = { From a0506365eb3f14b5579ca05be53a47909c3d9c9d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 24 May 2023 22:04:09 +0300 Subject: [PATCH 058/160] inv_double_pend re_order ez pickle init to the start --- gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 734af487..dc1ae6f6 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -143,6 +143,8 @@ def __init__( reset_noise_scale=0.1, **kwargs, ): + utils.EzPickle.__init__(self, xml_file, reset_noise_scale, **kwargs) + self._healthy_reward = healthy_reward self._reset_noise_scale = reset_noise_scale @@ -157,8 +159,6 @@ def __init__( **kwargs ) - utils.EzPickle.__init__(self, xml_file, reset_noise_scale, **kwargs) - def step(self, action): self.do_simulation(action, self.frame_skip) From 4d5b052e4729bc251951b3b727f5a1ab3a7cd1e5 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 24 May 2023 23:38:55 +0300 Subject: [PATCH 059/160] add humanoidstandup --- gymnasium_robotics/__init__.py | 10 +- .../envs/mujoco/humanoidstandup_v5.py | 237 ++++++++++++++---- 2 files changed, 195 insertions(+), 52 deletions(-) diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index 82bd2ad5..dd94bdc4 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1390,11 +1390,11 @@ def _merge(a, b): ) """ - #register( - #id="HumanoidStandup-v5", - #entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", - #max_episode_steps=1000, - #) + register( + id="HumanoidStandup-v5", + entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", + max_episode_steps=1000, + ) __version__ = "1.2.1" diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index aed217cc..243f23a3 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -1,3 +1,5 @@ +__credits__ = ["Kallinteris-Andreas"] + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv @@ -11,6 +13,12 @@ } +def mass_center(model, data): + mass = np.expand_dims(model.body_mass, axis=1) + xpos = data.xipos + return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() + + class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): """ ## Description @@ -50,13 +58,18 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | ## Observation Space + Observations consist of positional values of different body parts of the Humanoid, + followed by the velocities of those individual parts (their derivatives) with all the + positions ordered before all the velocities. - The state space consists of positional values of different body parts of the Humanoid, - followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. + By default, observations do not include the x- and y-coordinates of the torso. These may + be included by passing `exclude_current_positions_from_observation=False` during construction. + In that case, the observation space will be a `Box(-Inf, Inf, (378,), float64)` where the first two observations + represent the x- and y-coordinates of the torso. + Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates + will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - **Note:** The x- and y-coordinates of the torso are being omitted to produce position-agnostic behavior in policies - - The observation is a `ndarray` with shape `(376,)` where the elements correspond to the following: + However, by default, the observation is a `Box(-Inf, Inf, (376,), float64)`. The elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | @@ -94,21 +107,22 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): | 31 | x-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | anglular velocity (rad/s) | | 32 | z-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | anglular velocity (rad/s) | | 33 | y-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | anglular velocity (rad/s) | - | 35 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | - | 36 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | - | 37 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | - | 38 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | - | 39 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | - | 40 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | - | 41 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | - | 42 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | - | 43 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | - | 44 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | - | 45 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | - + | 34 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | + | 35 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | + | 36 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | + | 37 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | + | 38 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | + | 39 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | + | 40 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | + | 41 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | + | 42 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | + | 43 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | + | 44 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | + | excluded | x-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | + | excluded | y-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | Additionally, after all the positional and velocity based values in the table, - the state_space consists of (in order): + the observation contains (in order): - *cinert:* Mass and inertia of a single rigid body relative to the center of mass (this is an intermediate result of transition). It has shape 14*10 (*nbody * 10*) and hence adds to another 140 elements in the state space. @@ -118,8 +132,55 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): `(23,)` *(nv * 1)* and hence adds another 23 elements to the state space. - *cfrc_ext:* This is the center of mass based external force on the body. It has shape 14 * 6 (*nbody * 6*) and hence adds to another 84 elements in the state space. - where *nbody* stands for the number of bodies in the robot and *nv* stands for the number - of degrees of freedom (*= dim(qvel)*) + where *nbody* stands for the number of bodies in the robot and *nv* stands for the + number of degrees of freedom (*= dim(qvel)*) + + The body parts are: + + | id (for `v2`,`v3`,`v4`) | body part | + | --- | ------------ | + | 0 | worldBody (note: all values are constant 0) | + | 1 | torso | + | 2 | lwaist | + | 3 | pelvis | + | 4 | right_thigh | + | 5 | right_sin | + | 6 | right_foot | + | 7 | left_thigh | + | 8 | left_sin | + | 9 | left_foot | + | 10 | right_upper_arm | + | 11 | right_lower_arm | + | 12 | left_upper_arm | + | 13 | left_lower_arm | + + The joints are: + + | id (for `v2`,`v3`,`v4`) | joint | + | --- | ------------ | + | 0 | root | + | 1 | root | + | 2 | root | + | 3 | root | + | 4 | root | + | 5 | root | + | 6 | abdomen_z | + | 7 | abdomen_y | + | 8 | abdomen_x | + | 9 | right_hip_x | + | 10 | right_hip_z | + | 11 | right_hip_y | + | 12 | right_knee | + | 13 | left_hip_x | + | 14 | left_hiz_z | + | 15 | left_hip_y | + | 16 | left_knee | + | 17 | right_shoulder1 | + | 18 | right_shoulder2 | + | 19 | right_elbow| + | 20 | left_shoulder1 | + | 21 | left_shoulder2 | + | 22 | left_elfbow | The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the @@ -182,11 +243,11 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) - * v2: All continuous control environments now use mujoco-py >= 1.50 + * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v3: This environment does not have a v3 release. + * v2: All continuous control environments now use mujoco-py >= 1.50. * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) + * v0: Initial versions release (1.0.0). """ metadata = { @@ -201,31 +262,100 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): def __init__( self, xml_file="humanoidstandup.xml", + uph_cost_weight=1, + ctrl_cost_weight=0.1, + impact_cost_weight=0.5e-6, + impact_cost_range=(-np.inf, 10.0), + reset_noise_scale=1e-2, + exclude_current_positions_from_observation=True, + include_cinert_in_observation=True, + include_cvel_in_observation=True, + include_qfrc_actuator_in_observation=True, + include_cfrc_ext_in_observation=True, **kwargs, ): + utils.EzPickle.__init__( + self, + xml_file, + uph_cost_weight, + ctrl_cost_weight, + impact_cost_weight, + impact_cost_range, + reset_noise_scale, + exclude_current_positions_from_observation, + include_cinert_in_observation, + include_cvel_in_observation, + include_qfrc_actuator_in_observation, + include_cfrc_ext_in_observation, + **kwargs, + ) + + self._uph_cost_weight = uph_cost_weight + self._ctrl_cost_weight = ctrl_cost_weight + self._impact_cost_weight = impact_cost_weight + self._impact_cost_range = impact_cost_range + self._reset_noise_scale = reset_noise_scale + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation + ) + + self._include_cinert_in_observation = include_cinert_in_observation + self._include_cvel_in_observation = include_cvel_in_observation + self._include_qfrc_actuator_in_observation = ( + include_qfrc_actuator_in_observation + ) + self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation + + obs_shape = 45 + obs_shape += 130 * self._include_cinert_in_observation + obs_shape += 78 * self._include_cvel_in_observation + obs_shape += 17 * self._include_qfrc_actuator_in_observation + obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_shape += 2 * (not self._exclude_current_positions_from_observation) + observation_space = Box( - low=-np.inf, high=np.inf, shape=(348,), dtype=np.float64 + low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 ) + MujocoEnv.__init__( self, xml_file, 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs + **kwargs, ) - utils.EzPickle.__init__(self, **kwargs) def _get_obs(self): position = self.data.qpos.flat.copy() - position = position[2:] velocity = self.data.qvel.flat.copy() - com_inertia = self.data.cinert[1:].flat.copy() - com_velocity = self.data.cvel[1:].flat.copy() - - actuator_forces = self.data.qfrc_actuator[6:].flat.copy() - external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + if self._include_cinert_in_observation is True: + com_inertia = self.data.cinert[1:].flat.copy() + else: + com_inertia = np.array([]) + if self._include_cvel_in_observation is True: + com_velocity = self.data.cvel[1:].flat.copy() + else: + com_velocity = np.array([]) + + if self._include_qfrc_actuator_in_observation is True: + actuator_forces = self.data.qfrc_actuator[6:].flat.copy() + else: + actuator_forces = np.array([]) + if self._include_cfrc_ext_in_observation is True: + external_contact_forces = self.data.cfrc_ext[1:].flat.copy() + else: + external_contact_forces = np.array([]) + + # TODO remove after validation + assert (self.data.cinert[0].flat.copy() == 0).all() + assert (self.data.cvel[0].flat.copy() == 0).all() + assert (self.data.qfrc_actuator[:6].flat.copy() == 0).all() + assert (self.data.cfrc_ext[0].flat.copy() == 0).all() + + if self._exclude_current_positions_from_observation: + position = position[2:] return np.concatenate( ( @@ -241,14 +371,25 @@ def _get_obs(self): def step(self, a): self.do_simulation(a, self.frame_skip) pos_after = self.data.qpos[2] - data = self.data + uph_cost = (pos_after - 0) / self.model.opt.timestep - quad_ctrl_cost = 0.1 * np.square(data.ctrl).sum() - quad_impact_cost = 0.5e-6 * np.square(data.cfrc_ext).sum() - quad_impact_cost = min(quad_impact_cost, 10) + quad_ctrl_cost = self._ctrl_cost_weight * np.square(self.data.ctrl).sum() + + quad_impact_cost = self._impact_cost_weight * np.square(self.data.cfrc_ext).sum() + min_impact_cost, max_impact_cost = self._impact_cost_range + quad_impact_cost = np.clip(quad_impact_cost, min_impact_cost, max_impact_cost) + reward = uph_cost - quad_ctrl_cost - quad_impact_cost + 1 + info = { + "reward_linup": uph_cost, + "reward_quadctrl": -quad_ctrl_cost, + "reward_impact": -quad_impact_cost, + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], + } + if self.render_mode == "human": self.render() return ( @@ -264,14 +405,16 @@ def step(self, a): ) def reset_model(self): - c = 0.01 - self.set_state( - self.init_qpos + self.np_random.uniform(low=-c, high=c, size=self.model.nq), - self.init_qvel - + self.np_random.uniform( - low=-c, - high=c, - size=self.model.nv, - ), + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + + qpos = self.init_qpos + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ) + qvel = self.init_qvel + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nv ) - return self._get_obs() + self.set_state(qpos, qvel) + + observation = self._get_obs() + return observation From 867cfc961d2e58ca476e5decdc4732ca2d5905e7 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 25 May 2023 10:32:57 +0300 Subject: [PATCH 060/160] update humanoid standup `info` --- .../envs/mujoco/humanoidstandup_v5.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 243f23a3..1b6f041c 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -13,12 +13,6 @@ } -def mass_center(model, data): - mass = np.expand_dims(model.body_mass, axis=1) - xpos = data.xipos - return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() - - class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): """ ## Description @@ -392,17 +386,7 @@ def step(self, a): if self.render_mode == "human": self.render() - return ( - self._get_obs(), - reward, - False, - False, - dict( - reward_linup=uph_cost, - reward_quadctrl=-quad_ctrl_cost, - reward_impact=-quad_impact_cost, - ), - ) + return self._get_obs(), reward, False, False, info def reset_model(self): noise_low = -self._reset_noise_scale From b6e199622363d7224af404e695677ac439198026 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 25 May 2023 11:18:59 +0300 Subject: [PATCH 061/160] `pre-commit` --- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 4 +++- .../envs/mujoco/inverted_double_pendulum_v5.py | 9 ++++++--- gymnasium_robotics/envs/mujoco/reacher_v5.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 1b6f041c..9b8fd55e 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -370,7 +370,9 @@ def step(self, a): quad_ctrl_cost = self._ctrl_cost_weight * np.square(self.data.ctrl).sum() - quad_impact_cost = self._impact_cost_weight * np.square(self.data.cfrc_ext).sum() + quad_impact_cost = ( + self._impact_cost_weight * np.square(self.data.cfrc_ext).sum() + ) min_impact_cost, max_impact_cost = self._impact_cost_range quad_impact_cost = np.clip(quad_impact_cost, min_impact_cost, max_impact_cost) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index dc1ae6f6..f16c8a21 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -156,7 +156,7 @@ def __init__( 5, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs + **kwargs, ) def step(self, action): @@ -203,7 +203,10 @@ def reset_model(self): self.set_state( self.init_qpos - + self.np_random.uniform(low=noise_low, high=noise_high, size=self.model.nq), - self.init_qvel + self.np_random.standard_normal(self.model.nv) * self._reset_noise_scale, + + self.np_random.uniform( + low=noise_low, high=noise_high, size=self.model.nq + ), + self.init_qvel + + self.np_random.standard_normal(self.model.nv) * self._reset_noise_scale, ) return self._get_obs() diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 5a2afdc5..c1951267 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -140,7 +140,7 @@ def __init__( 2, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs + **kwargs, ) def step(self, a): From b89e50ad17c1e9c0b7e0a1911bafcc69a4deda7d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 14:28:06 +0300 Subject: [PATCH 062/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 65cde7eb..8d0f9163 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -246,8 +246,8 @@ def __init__( self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation obs_shape = 27 + obs_shape -= 2 * self._exclude_current_positions_from_observation obs_shape += 78 * self._include_cfrc_ext_in_observation - obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 From ce82a8e20d55f64d0aefb4adbe19fecf755a9965 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 14:36:12 +0300 Subject: [PATCH 063/160] hopper add `info` "z_distance_from_origin" --- gymnasium_robotics/envs/mujoco/hopper_v5.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 6e92c61e..f9973d05 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -271,6 +271,7 @@ def step(self, action): "reward_ctrl": -ctrl_cost, "reward_survive": healthy_reward, "x_position": x_position_after, + "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], "x_velocity": x_velocity, } From b879488466e1b1d214620d20ea3de4b9e91ff892 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 15:19:10 +0300 Subject: [PATCH 064/160] fix --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 8d0f9163..450417d9 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -245,7 +245,7 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 27 + obs_shape = 29 obs_shape -= 2 * self._exclude_current_positions_from_observation obs_shape += 78 * self._include_cfrc_ext_in_observation From ff585f797a6df6f65aecc7530e93b11689e15d54 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 15:31:09 +0300 Subject: [PATCH 065/160] add `frame_skip` argument --- gymnasium_robotics/envs/mujoco/ant_v5.py | 22 ++++++++-------- .../envs/mujoco/half_cheetah_v5.py | 22 ++++++++-------- gymnasium_robotics/envs/mujoco/hopper_v5.py | 22 ++++++++-------- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 22 ++++++++-------- .../envs/mujoco/humanoidstandup_v5.py | 22 ++++++++-------- .../mujoco/inverted_double_pendulum_v5.py | 23 +++++++++-------- .../envs/mujoco/inverted_pendulum_v5.py | 25 ++++++++++--------- gymnasium_robotics/envs/mujoco/pusher_v5.py | 15 +++++++++-- gymnasium_robotics/envs/mujoco/reacher_v5.py | 24 ++++++++++-------- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 23 +++++++++-------- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 22 ++++++++-------- 11 files changed, 136 insertions(+), 106 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 450417d9..f35f38ae 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -191,18 +191,10 @@ class AntEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 20, - } - def __init__( self, xml_file="ant.xml", + frame_skip=5, ctrl_cost_weight=0.5, contact_cost_weight=5e-4, healthy_reward=1.0, @@ -217,6 +209,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, ctrl_cost_weight, contact_cost_weight, healthy_reward, @@ -253,10 +246,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index f8f2e431..2d070fd7 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -125,18 +125,10 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 20, - } - def __init__( self, xml_file="half_cheetah.xml", + frame_skip=5, forward_reward_weight=1.0, ctrl_cost_weight=0.1, reset_noise_scale=0.1, @@ -146,6 +138,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, forward_reward_weight, ctrl_cost_weight, reset_noise_scale, @@ -172,10 +165,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index f9973d05..a6389b2b 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -133,18 +133,10 @@ class HopperEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0). """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 125, - } - def __init__( self, xml_file="hopper_v5.xml", + frame_skip=4, forward_reward_weight=1.0, ctrl_cost_weight=1e-3, healthy_reward=1.0, @@ -159,6 +151,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, forward_reward_weight, ctrl_cost_weight, healthy_reward, @@ -197,10 +190,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(12,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 500 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 4, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index bccbd060..b16ebb56 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -260,18 +260,10 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 67, - } - def __init__( self, xml_file="humanoid.xml", + frame_skip=5, forward_reward_weight=1.25, ctrl_cost_weight=0.1, contact_cost_weight=5e-7, @@ -290,6 +282,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, forward_reward_weight, ctrl_cost_weight, contact_cost_weight, @@ -338,10 +331,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 335 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 9b8fd55e..a590945e 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -244,18 +244,10 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0). """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 67, - } - def __init__( self, xml_file="humanoidstandup.xml", + frame_skip=5, uph_cost_weight=1, ctrl_cost_weight=0.1, impact_cost_weight=0.5e-6, @@ -271,6 +263,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, uph_cost_weight, ctrl_cost_weight, impact_cost_weight, @@ -311,10 +304,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 335 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index f16c8a21..d57ba790 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -127,33 +127,34 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 20, - } - def __init__( self, xml_file="inverted_double_pendulum.xml", + frame_skip=5, healthy_reward=10.0, reset_noise_scale=0.1, **kwargs, ): - utils.EzPickle.__init__(self, xml_file, reset_noise_scale, **kwargs) + utils.EzPickle.__init__(self, xml_file, frame_skip, reset_noise_scale, **kwargs) self._healthy_reward = healthy_reward self._reset_noise_scale = reset_noise_scale observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index a66f6297..9ef327e1 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -91,22 +91,23 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 25, - } - - def __init__(self, **kwargs): - utils.EzPickle.__init__(self, **kwargs) + def __init__(self, frame_skip=2, **kwargs): + utils.EzPickle.__init__(self, frame_skip, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(4,), dtype=np.float64) + + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 50 / frame_skip, + } + MujocoEnv.__init__( self, "inverted_pendulum.xml", - 2, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index e0fe7177..ad229d40 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -148,14 +148,25 @@ class PusherEnv(MujocoEnv, utils.EzPickle): def __init__( self, xml_file="pusher.xml", + frame_skip=5, **kwargs, ): - utils.EzPickle.__init__(self, **kwargs) + utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) + + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 5, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index c1951267..081b6667 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -118,26 +118,28 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 50, - } - def __init__( self, xml_file="reacher.xml", + frame_skip=2, **kwargs, ): - utils.EzPickle.__init__(self, **kwargs) + utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) + + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 2, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index d4bd1f0a..2e7f7760 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -116,18 +116,10 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 25, - } - def __init__( self, xml_file="swimmer.xml", + frame_skip=4, forward_reward_weight=1.0, ctrl_cost_weight=1e-4, reset_noise_scale=0.1, @@ -137,6 +129,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, forward_reward_weight, ctrl_cost_weight, reset_noise_scale, @@ -160,8 +153,18 @@ def __init__( observation_space = Box( low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64 ) + + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 100 / frame_skip, + } + MujocoEnv.__init__( - self, xml_file, 4, observation_space=observation_space, **kwargs + self, xml_file, frame_skip, observation_space=observation_space, **kwargs ) def control_cost(self, action): diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 5b9ac187..6f2a4924 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -138,18 +138,10 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 125, - } - def __init__( self, xml_file="walker2d_v5.xml", + frame_skip=4, forward_reward_weight=1.0, ctrl_cost_weight=1e-3, healthy_reward=1.0, @@ -163,6 +155,7 @@ def __init__( utils.EzPickle.__init__( self, xml_file, + frame_skip, forward_reward_weight, ctrl_cost_weight, healthy_reward, @@ -198,10 +191,19 @@ def __init__( low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": 500 / frame_skip, + } + MujocoEnv.__init__( self, xml_file, - 4, + frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, From 9bdc5df5c3c5da4e28ab82f381b0f72223d40d5f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 18:23:29 +0300 Subject: [PATCH 066/160] add tendon `info` --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index b16ebb56..36068f00 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -449,6 +449,8 @@ def step(self, action): "reward_contact": -contact_cost, "x_position": xy_position_after[0], "y_position": xy_position_after[1], + "tendon_lenght": self.data.ten_length, + "tendon_velocity": self.data.ten_velocity, "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index a590945e..b3719899 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -386,6 +386,8 @@ def step(self, a): "reward_impact": -quad_impact_cost, "x_position": self.data.qpos[0], "y_position": self.data.qpos[1], + "tendon_lenght": self.data.ten_length, + "tendon_velocity": self.data.ten_velocity, } if self.render_mode == "human": From d4271aa6b0be9ffa125544e3746b1b39cacd447b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 18:29:31 +0300 Subject: [PATCH 067/160] fix exclude refactor --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 36068f00..0ecc325e 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -272,7 +272,7 @@ def __init__( terminate_when_unhealthy=True, healthy_z_range=(1.0, 2.0), reset_noise_scale=1e-2, - exclude_current_positions_in_observation=True, + exclude_current_positions_from_observation=True, include_cinert_in_observation=True, include_cvel_in_observation=True, include_qfrc_actuator_in_observation=True, @@ -291,7 +291,7 @@ def __init__( terminate_when_unhealthy, healthy_z_range, reset_noise_scale, - exclude_current_positions_in_observation, + exclude_current_positions_from_observation, include_cinert_in_observation, include_cvel_in_observation, include_qfrc_actuator_in_observation, @@ -309,8 +309,8 @@ def __init__( self._reset_noise_scale = reset_noise_scale - self._exclude_current_positions_in_observation = ( - exclude_current_positions_in_observation + self._exclude_current_positions_from_observation = ( + exclude_current_positions_from_observation ) self._include_cinert_in_observation = include_cinert_in_observation @@ -325,7 +325,7 @@ def __init__( obs_shape += 78 * self._include_cvel_in_observation obs_shape += 17 * self._include_qfrc_actuator_in_observation obs_shape += 78 * self._include_cfrc_ext_in_observation - obs_shape += 2 * (not self._exclude_current_positions_in_observation) + obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 @@ -408,7 +408,7 @@ def _get_obs(self): assert (self.data.qfrc_actuator[:6].flat.copy() == 0).all() assert (self.data.cfrc_ext[0].flat.copy() == 0).all() - if self._exclude_current_positions_in_observation: + if self._exclude_current_positions_from_observation: position = position[2:] return np.concatenate( @@ -450,7 +450,7 @@ def step(self, action): "x_position": xy_position_after[0], "y_position": xy_position_after[1], "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_velocity, + "tendon_velocity": self.data.ten_l, "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, From 42f5439ba75a2509c7db4da02d7ba41d17835fe6 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 19:55:05 +0300 Subject: [PATCH 068/160] update doc --- gymnasium_robotics/envs/mujoco/ant_v5.py | 35 ++--- gymnasium_robotics/envs/mujoco/hopper_v5.py | 4 +- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 107 ++++++------- .../envs/mujoco/humanoidstandup_v5.py | 140 +++++++++--------- .../mujoco/inverted_double_pendulum_v5.py | 11 +- .../envs/mujoco/inverted_pendulum_v5.py | 12 +- gymnasium_robotics/envs/mujoco/pusher_v5.py | 16 +- gymnasium_robotics/envs/mujoco/reacher_v5.py | 11 +- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 14 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 22 +-- 10 files changed, 178 insertions(+), 194 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index f35f38ae..9a8b8b44 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -13,7 +13,6 @@ class AntEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is based on the environment introduced by Schulman, Moritz, Levine, Jordan and Abbeel in ["High-Dimensional Continuous Control Using Generalized Advantage Estimation"](https://arxiv.org/abs/1506.02438). @@ -88,11 +87,12 @@ class AntEnv(MujocoEnv, utils.EzPickle): - *cfrc_ext:* 13*6 = 78 elements, which are contact forces (external forces - force x, y, z and torque x, y, z) applied to the center of mass of each of the body parts. - The 13 body parts are: - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body parts | + The body parts are: + + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | | ---| --- | ------------ | - | 0 |excluded| worldbody (note: forces are always full of zeros) | + | 0 |excluded| worldbody (note: all values are constant 0) | | 1 |0 | torso | | 2 |1 | front_left_leg | | 3 |2 | aux_1 (front left leg) | @@ -167,21 +167,22 @@ class AntEnv(MujocoEnv, utils.EzPickle): ```python import gymnasium as gym - env = gym.make('Ant-v5', ctrl_cost_weight=0.1, ...) + env = gym.make('Ant-v5', ctrl_cost_weight=0.5, ...) ``` - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|-------------------------------| - | `xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | - | `ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | - | `use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | - | `contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | - | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | - | `terminate_when_unhealthy` | **bool**| `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | - | `healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | - | `contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | - | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation`| **bool** | `True`| Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + | Parameter | Type | Default |Description | + |--------------------------------------------|------------|--------------|-------------------------------| + |`xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | + |`ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | + |`use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | + |`contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | + |`healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | + |`healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | + |`contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | + |`reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + |`exclude_current_positions_from_observation`| **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + |`include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3, also removed contact forces from the default observation space (new variable `use_contact_forces=True` can restore them) diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index a6389b2b..d8b2dfd1 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -108,14 +108,14 @@ class HopperEnv(MujocoEnv, utils.EzPickle): ```python import gymnasium as gym - env = gym.make('Hopper-v5', ctrl_cost_weight=0.1, ....) + env = gym.make('Hopper-v5', ctrl_cost_weight=1e-3, ....) ``` | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xml_file` | **str** | `"hopper_v5.xml"` | Path to a MuJoCo model | | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `0.001` | Weight for _ctrl_cost_ reward (see section on reward) | + | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctrl_cost_ reward (see section on reward) | | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the hopper is no longer healthy | | `healthy_state_range` | **tuple** | `(-100, 100)` | The elements of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) must be in this range for the hopper to be considered healthy | diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 0ecc325e..209f2e9a 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -59,12 +59,12 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): By default, observations do not include the x- and y-coordinates of the torso. These may be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (378,), float64)` where the first two observations + In that case, the observation space will be a `Box(-Inf, Inf, (350,), float64)` where the first two observations represent the x- and y-coordinates of the torso. Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - However, by default, the observation is a `Box(-Inf, Inf, (376,), float64)`. The elements correspond to the following: + However, by default, the observation is a `Box(-Inf, Inf, (348,), float64)`. The elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | @@ -119,63 +119,63 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): Additionally, after all the positional and velocity based values in the table, the observation contains (in order): - *cinert:* Mass and inertia of a single rigid body relative to the center of mass - (this is an intermediate result of transition). It has shape 14*10 (*nbody * 10*) - and hence adds to another 140 elements in the state space. - - *cvel:* Center of mass based velocity. It has shape 14 * 6 (*nbody * 6*) and hence - adds another 84 elements in the state space + (this is an intermediate result of transition). It has shape 13*10 (*nbody * 10*) + and hence adds to another 130 elements in the state space. + - *cvel:* Center of mass based velocity. It has shape 13 * 6 (*nbody * 6*) and hence + adds another 78 elements in the state space - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape - `(23,)` *(nv * 1)* and hence adds another 23 elements to the state space. + `(17,)` *(nv * 1)* and hence adds another 17 elements to the state space. - *cfrc_ext:* This is the center of mass based external force on the body. It has shape - 14 * 6 (*nbody * 6*) and hence adds to another 84 elements in the state space. + 13 * 6 (*nbody * 6*) and hence adds to another 78 elements in the state space. where *nbody* stands for the number of bodies in the robot and *nv* stands for the number of degrees of freedom (*= dim(qvel)*) The body parts are: - | id (for `v2`,`v3`,`v4`) | body part | - | --- | ------------ | - | 0 | worldBody (note: all values are constant 0) | - | 1 | torso | - | 2 | lwaist | - | 3 | pelvis | - | 4 | right_thigh | - | 5 | right_sin | - | 6 | right_foot | - | 7 | left_thigh | - | 8 | left_sin | - | 9 | left_foot | - | 10 | right_upper_arm | - | 11 | right_lower_arm | - | 12 | left_upper_arm | - | 13 | left_lower_arm | + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | + | ---| --- | ------------ | + | 0 |excluded| worldbody (note: all values are constant 0) | + | 1 | 0 | torso | + | 2 | 1 | lwaist | + | 3 | 2 | pelvis | + | 4 | 3 | right_thigh | + | 5 | 4 | right_sin | + | 6 | 5 | right_foot | + | 7 | 6 | left_thigh | + | 8 | 7 | left_sin | + | 9 | 8 | left_foot | + | 10 | 9 | right_upper_arm | + | 11 | 10 | right_lower_arm | + | 12 | 11 | left_upper_arm | + | 13 | 12 | left_lower_arm | The joints are: - | id (for `v2`,`v3`,`v4`) | joint | - | --- | ------------ | - | 0 | root | - | 1 | root | - | 2 | root | - | 3 | root | - | 4 | root | - | 5 | root | - | 6 | abdomen_z | - | 7 | abdomen_y | - | 8 | abdomen_x | - | 9 | right_hip_x | - | 10 | right_hip_z | - | 11 | right_hip_y | - | 12 | right_knee | - | 13 | left_hip_x | - | 14 | left_hiz_z | - | 15 | left_hip_y | - | 16 | left_knee | - | 17 | right_shoulder1 | - | 18 | right_shoulder2 | - | 19 | right_elbow| - | 20 | left_shoulder1 | - | 21 | left_shoulder2 | - | 22 | left_elfbow | + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | joint | + | ---| --- | ------------ | + | 0 |excluded| root (note: all values are constant 0) | + | 1 |excluded| root (note: all values are constant 0) | + | 2 |excluded| root (note: all values are constant 0) | + | 3 |excluded| root (note: all values are constant 0) | + | 4 |excluded| root (note: all values are constant 0) | + | 5 |excluded| root (note: all values are constant 0) | + | 6 | 0 | abdomen_z | + | 7 | 1 | abdomen_y | + | 8 | 2 | abdomen_x | + | 9 | 3 | right_hip_x | + | 10 | 4 | right_hip_z | + | 11 | 5 | right_hip_y | + | 12 | 6 | right_knee | + | 13 | 7 | left_hip_x | + | 14 | 8 | left_hiz_z | + | 15 | 9 | left_hip_y | + | 16 | 10 | left_knee | + | 17 | 11 | right_shoulder1 | + | 18 | 12 | right_shoulder2 | + | 19 | 13 | right_elbow| + | 20 | 14 | left_shoulder1 | + | 21 | 15 | left_shoulder2 | + | 22 | 16 | left_elfbow | The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the @@ -204,7 +204,10 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): contact force is too large. It is calculated by clipping *`contact_cost_weight` * sum(external contact force2)* to the interval specified by `contact_cost_range`. - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost* and `info` will also contain the individual reward terms + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost* + and `info` will also contain the individual reward terms + + Note: in `v4` the total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost* ## Starting State All observations start in state @@ -320,12 +323,12 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 45 + obs_shape = 47 + obs_shape -= 2 * self._exclude_current_positions_from_observation obs_shape += 130 * self._include_cinert_in_observation obs_shape += 78 * self._include_cvel_in_observation obs_shape += 17 * self._include_qfrc_actuator_in_observation obs_shape += 78 * self._include_cfrc_ext_in_observation - obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index b3719899..9656acc2 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -16,7 +16,6 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is based on the environment introduced by Tassa, Erez and Todorov in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a @@ -58,12 +57,12 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): By default, observations do not include the x- and y-coordinates of the torso. These may be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (378,), float64)` where the first two observations + In that case, the observation space will be a `Box(-Inf, Inf, (350,), float64)` where the first two observations represent the x- and y-coordinates of the torso. Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - However, by default, the observation is a `Box(-Inf, Inf, (376,), float64)`. The elements correspond to the following: + However, by default, the observation is a `Box(-Inf, Inf, (348,), float64)`. The elements correspond to the following: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | @@ -118,73 +117,74 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): Additionally, after all the positional and velocity based values in the table, the observation contains (in order): - *cinert:* Mass and inertia of a single rigid body relative to the center of mass - (this is an intermediate result of transition). It has shape 14*10 (*nbody * 10*) - and hence adds to another 140 elements in the state space. - - *cvel:* Center of mass based velocity. It has shape 14 * 6 (*nbody * 6*) and hence - adds another 84 elements in the state space + (this is an intermediate result of transition). It has shape 13*10 (*nbody * 10*) + and hence adds to another 130 elements in the state space. + - *cvel:* Center of mass based velocity. It has shape 13 * 6 (*nbody * 6*) and hence + adds another 78 elements in the state space - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape - `(23,)` *(nv * 1)* and hence adds another 23 elements to the state space. + `(17,)` *(nv * 1)* and hence adds another 17 elements to the state space. - *cfrc_ext:* This is the center of mass based external force on the body. It has shape - 14 * 6 (*nbody * 6*) and hence adds to another 84 elements in the state space. + 13 * 6 (*nbody * 6*) and hence adds to another 78 elements in the state space. where *nbody* stands for the number of bodies in the robot and *nv* stands for the number of degrees of freedom (*= dim(qvel)*) The body parts are: - | id (for `v2`,`v3`,`v4`) | body part | - | --- | ------------ | - | 0 | worldBody (note: all values are constant 0) | - | 1 | torso | - | 2 | lwaist | - | 3 | pelvis | - | 4 | right_thigh | - | 5 | right_sin | - | 6 | right_foot | - | 7 | left_thigh | - | 8 | left_sin | - | 9 | left_foot | - | 10 | right_upper_arm | - | 11 | right_lower_arm | - | 12 | left_upper_arm | - | 13 | left_lower_arm | + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | + | ---| --- | ------------ | + | 0 |excluded| worldbody (note: all values are constant 0) | + | 1 | 0 | torso | + | 2 | 1 | lwaist | + | 3 | 2 | pelvis | + | 4 | 3 | right_thigh | + | 5 | 4 | right_sin | + | 6 | 5 | right_foot | + | 7 | 6 | left_thigh | + | 8 | 7 | left_sin | + | 9 | 8 | left_foot | + | 10 | 9 | right_upper_arm | + | 11 | 10 | right_lower_arm | + | 12 | 11 | left_upper_arm | + | 13 | 12 | left_lower_arm | The joints are: - | id (for `v2`,`v3`,`v4`) | joint | - | --- | ------------ | - | 0 | root | - | 1 | root | - | 2 | root | - | 3 | root | - | 4 | root | - | 5 | root | - | 6 | abdomen_z | - | 7 | abdomen_y | - | 8 | abdomen_x | - | 9 | right_hip_x | - | 10 | right_hip_z | - | 11 | right_hip_y | - | 12 | right_knee | - | 13 | left_hip_x | - | 14 | left_hiz_z | - | 15 | left_hip_y | - | 16 | left_knee | - | 17 | right_shoulder1 | - | 18 | right_shoulder2 | - | 19 | right_elbow| - | 20 | left_shoulder1 | - | 21 | left_shoulder2 | - | 22 | left_elfbow | + | id (for `v2`, `v3`, `v4)` | id (for `v5`) | joint | + | ---| --- | ------------ | + | 0 |excluded| root (note: all values are constant 0) | + | 1 |excluded| root (note: all values are constant 0) | + | 2 |excluded| root (note: all values are constant 0) | + | 3 |excluded| root (note: all values are constant 0) | + | 4 |excluded| root (note: all values are constant 0) | + | 5 |excluded| root (note: all values are constant 0) | + | 6 | 0 | abdomen_z | + | 7 | 1 | abdomen_y | + | 8 | 2 | abdomen_x | + | 9 | 3 | right_hip_x | + | 10 | 4 | right_hip_z | + | 11 | 5 | right_hip_y | + | 12 | 6 | right_knee | + | 13 | 7 | left_hip_x | + | 14 | 8 | left_hiz_z | + | 15 | 9 | left_hip_y | + | 16 | 10 | left_knee | + | 17 | 11 | right_shoulder1 | + | 18 | 12 | right_shoulder2 | + | 19 | 13 | right_elbow| + | 20 | 14 | left_shoulder1 | + | 21 | 15 | left_shoulder2 | + | 22 | 16 | left_elfbow | The (x,y,z) coordinates are translational DOFs while the orientations are rotational DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). - **Note:** HumanoidStandup-v4 environment no longer has the following contact forces issue. - If using previous HumanoidStandup versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 results - in the contact forces always being 0. As such we recommend to use a Mujoco-Py version < 2.0 - when using the Humanoid environment if you would like to report results with contact forces - (if contact forces are not used in your experiments, you can use version > 2.0). + **Note:** Humanoid-v4 environment no longer has the following contact forces issue. + If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 + results in the contact forces always being 0. As such we recommend to use a Mujoco-Py + version < 2.0 when using the Humanoid environment if you would like to report results + with contact forces (if contact forces are not used in your experiments, you can use + version > 2.0). ## Rewards The reward consists of three parts: @@ -219,24 +219,28 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): 2. Termination: Any of the state space values is no longer finite ## Arguments - - No additional arguments are currently supported. + `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('HumanoidStandup-v4') + env = gym.make('HumanoidStandup-v5', impact_cost_weight=0.5e-6, ....) ``` - There is no v3 for HumanoidStandup, unlike the robot environments where a v3 and - beyond take gymnasium.make kwargs such as xml_file, ctrl_cost_weight, reset_noise_scale etc. - - ```python - import gymnasium as gym - env = gym.make('HumanoidStandup-v2') - ``` + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | `xml_file` | **str** | `"humanoidstandup.xml"` | Path to a MuJoCo model | + | `uph_cost_weight` | **float** | `1` | Weight for _uph_cost_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `0.1` | Weight for _quad_ctrl_cost_ term (see section on reward) | + | `impact_cost_weight` | **float** | `0.5e-6` | Weight for _contact_cost_ term (see section on reward) | + | `impact_cost_range` | **float** | `(-np.inf, 10.0) | | + | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| + | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | + | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | + | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. * v3: This environment does not have a v3 release. * v2: All continuous control environments now use mujoco-py >= 1.50. @@ -293,12 +297,12 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 45 + obs_shape = 47 + obs_shape -= 2 * self._exclude_current_positions_from_observation obs_shape += 130 * self._include_cinert_in_observation obs_shape += 78 * self._include_cvel_in_observation obs_shape += 17 * self._include_qfrc_actuator_in_observation obs_shape += 78 * self._include_cfrc_ext_in_observation - obs_shape += 2 * (not self._exclude_current_positions_from_observation) observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index d57ba790..9baf520d 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -98,18 +98,11 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): 3.Termination: The y_coordinate of the tip of the second pole *is less than or equal* to 1. The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other). ## Arguments - No additional arguments are currently supported. + `gymnasium.make` takes additional arguments such as `xml_file`, `healthy_reward`, `reset_noise_scale`, etc. ```python import gymnasium as gym - env = gym.make('InvertedDoublePendulum-v4') - ``` - There is no v3 for InvertedPendulum, unlike the robot environments where a v3 and - beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('InvertedDoublePendulum-v2') + env = gym.make('InvertedDoublePendulum-v5', healthy_reward=10, ...) ``` | Parameter | Type | Default |Description | diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 9ef327e1..1614067c 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -14,7 +14,6 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is the cartpole environment based on the work done by Barto, Sutton, and Anderson in ["Neuronlike adaptive elements that can solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), @@ -36,7 +35,6 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): | 0 | Force applied on the cart | -3 | 3 | slider | slide | Force (N) | ## Observation Space - The state space consists of positional values of different body parts of the pendulum system, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. @@ -56,6 +54,8 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): as long as possible - as such a reward of +1 is awarded for each timestep that the pole is upright. + and `info` will also contain the reward. + ## Starting State All observations start in state (0.0, 0.0, 0.0, 0.0) with a uniform noise in the range @@ -73,13 +73,7 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): ```python import gymnasium as gym - env = gym.make('InvertedPendulum-v4') - ``` - There is no v3 for InvertedPendulum, unlike the robot environments where a - v3 and beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - ```python - import gymnasium as gym - env = gym.make('InvertedPendulum-v2') + env = gym.make('InvertedPendulum-v5') ``` ## Version History diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index ad229d40..ae6a3f6a 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -111,22 +111,16 @@ class PusherEnv(MujocoEnv, utils.EzPickle): 2. Termination: Any of the state space values is no longer finite. ## Arguments - No additional arguments are currently supported (in v2 and lower), - but modifications can be made to the XML file in the assets folder - (or by changing the path to a modified XML file in another folder).. + `gymnasium.make` takes additional arguments such as `xml_file`. ```python import gymnasium as gym - env = gym.make('Pusher-v4') + env = gym.make('Pusher-v5', xml_file=...) ``` - There is no v3 for Pusher, unlike the robot environments where a v3 and - beyond take `gymnasmium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Pusher-v2') - ``` + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|-------------------------------| + | `xml_file` | **str** |`"pusher.xml"`| Path to a MuJoCo model | ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. "reward_near" is added to the `info`. diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 081b6667..4e2f4f3c 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -98,17 +98,16 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): 2. Termination: Any of the state space values is no longer finite. ## Arguments - No additional arguments are currently supported (in v2 and lower), - but modifications can be made to the XML file in the assets folder - (or by changing the path to a modified XML file in another folder).. + `gymnasium.make` takes additional arguments such as `xml_file`. ```python import gymnasium as gym - env = gym.make('Reacher-v4') + env = gym.make('Reacher-v5', xml_file=...) ``` - There is no v3 for Reacher, unlike the robot environments where a v3 and - beyond take `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + | Parameter | Type | Default |Description | + |-------------------------|------------|-------------- |-------------------------------| + | `xml_file` | **str** |`"reacher.xml"`| Path to a MuJoCo model | ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. Remove "z - position_fingertip" from the observation space. diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 2e7f7760..8e862112 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -91,20 +91,16 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): The episode truncates when the episode length is greater than 1000. ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. + `gymnasium.make` takes additional arguments such as `xml_file`. ```python import gymnasium as gym - env = gym.make('Swimmer-v5', ctrl_cost_weight=0.1, ....) + env = gym.make('Swimmer-v5', xml_file=...) ``` - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"swimmer.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `1e-4` | Weight for _ctrl_cost_ term (see section on reward) | - | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + | Parameter | Type | Default |Description | + |-------------------------|------------|-------------- |-------------------------------| + | `xml_file` | **str** |`"swimmer.xml"`| Path to a MuJoCo model | ## Version History diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 6f2a4924..3ceff733 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -113,20 +113,20 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): ```python import gymnasium as gym - env = gym.make('Walker2d-v5', ctrl_cost_weight=0.1, ....) + env = gym.make('Walker2d-v5', ctrl_cost_weight=1e-3, ...) ``` - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | Parameter | Type | Default | Description | + | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xml_file` | **str** |`"walker2d_v5.xml"`| Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | - | `healthy_reward` | **float** | `1.0` | Constant reward given if the ant is "healthy" after timestep | - | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | - | `healthy_z_range` | **tuple** | `(0.8, 2)` | The z-coordinate of the torso of the walker must be in this range to be considered healthy | - | `healthy_angle_range` | **tuple** | `(-1, 1)` | The angle must be in this range to be considered healthy | - | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | + | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | + | `healthy_reward` | **float** | `1.0` | Constant reward given if the ant is "healthy" after timestep | + | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | + | `healthy_z_range` | **tuple** | `(0.8, 2)` | The z-coordinate of the torso of the walker must be in this range to be considered healthy | + | `healthy_angle_range` | **tuple** | `(-1, 1)` | The angle must be in this range to be considered healthy | + | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | ## Version History From 15259d5faee4b3a68a908cddb6fa396b3a0f5bc3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 20:09:20 +0300 Subject: [PATCH 069/160] update obs creation --- gymnasium_robotics/envs/mujoco/ant_v5.py | 8 ++++---- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 12 ++++-------- gymnasium_robotics/envs/mujoco/hopper_v5.py | 12 ++++-------- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 14 +++++++------- .../envs/mujoco/humanoidstandup_v5.py | 14 +++++++------- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 13 +++++-------- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 12 ++++-------- 7 files changed, 35 insertions(+), 50 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 9a8b8b44..dd0102fa 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -239,12 +239,12 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 29 - obs_shape -= 2 * self._exclude_current_positions_from_observation - obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_size = 29 + obs_size -= 2 * self._exclude_current_positions_from_observation + obs_size += 78 * self._include_cfrc_ext_in_observation observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata = { diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 2d070fd7..a9ec4158 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -156,14 +156,10 @@ def __init__( exclude_current_positions_from_observation ) - if exclude_current_positions_from_observation: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(17,), dtype=np.float64 - ) - else: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 - ) + obs_size = 18 - exclude_current_positions_from_observation + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) self.metadata = { "render_modes": [ diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index d8b2dfd1..ced41fc7 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -181,14 +181,10 @@ def __init__( exclude_current_positions_from_observation ) - if exclude_current_positions_from_observation: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(11,), dtype=np.float64 - ) - else: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(12,), dtype=np.float64 - ) + obs_size = 12 - exclude_current_positions_from_observation + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) self.metadata = { "render_modes": [ diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 209f2e9a..385a8dfa 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -323,15 +323,15 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 47 - obs_shape -= 2 * self._exclude_current_positions_from_observation - obs_shape += 130 * self._include_cinert_in_observation - obs_shape += 78 * self._include_cvel_in_observation - obs_shape += 17 * self._include_qfrc_actuator_in_observation - obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_size = 47 + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += 130 * include_cinert_in_observation + obs_size += 78 * include_cvel_in_observation + obs_size += 17 * include_qfrc_actuator_in_observation + obs_size += 78 * include_cfrc_ext_in_observation observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata = { diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 9656acc2..15eea550 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -297,15 +297,15 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_shape = 47 - obs_shape -= 2 * self._exclude_current_positions_from_observation - obs_shape += 130 * self._include_cinert_in_observation - obs_shape += 78 * self._include_cvel_in_observation - obs_shape += 17 * self._include_qfrc_actuator_in_observation - obs_shape += 78 * self._include_cfrc_ext_in_observation + obs_size = 47 + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += 130 * include_cinert_in_observation + obs_size += 78 * include_cvel_in_observation + obs_size += 17 * include_qfrc_actuator_in_observation + obs_size += 78 * include_cfrc_ext_in_observation observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_shape,), dtype=np.float64 + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata = { diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 8e862112..2bf18838 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -141,14 +141,11 @@ def __init__( self._exclude_current_positions_from_observation = ( exclude_current_positions_from_observation ) - if exclude_current_positions_from_observation: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(8,), dtype=np.float64 - ) - else: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64 - ) + + obs_size = 10 - 2 * exclude_current_positions_from_observation + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) self.metadata = { "render_modes": [ diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 3ceff733..82ffb942 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -182,14 +182,10 @@ def __init__( exclude_current_positions_from_observation ) - if exclude_current_positions_from_observation: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(17,), dtype=np.float64 - ) - else: - observation_space = Box( - low=-np.inf, high=np.inf, shape=(18,), dtype=np.float64 - ) + obs_size = 18 - exclude_current_positions_from_observation + observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) self.metadata = { "render_modes": [ From fb96314442ac2c35eeca9435c58017f9edf14c0b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 20:13:03 +0300 Subject: [PATCH 070/160] typo fix --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 385a8dfa..89372e38 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -453,7 +453,7 @@ def step(self, action): "x_position": xy_position_after[0], "y_position": xy_position_after[1], "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_l, + "tendon_velocity": self.data.ten_velocity, "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, From b8aaf6d32e8b4ae86ad6889fa0a8401e2f93f68f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 20:31:45 +0300 Subject: [PATCH 071/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index dd0102fa..6d62e064 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -240,8 +240,8 @@ def __init__( self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation obs_size = 29 - obs_size -= 2 * self._exclude_current_positions_from_observation - obs_size += 78 * self._include_cfrc_ext_in_observation + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += 78 * include_cfrc_ext_in_observation observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 From e2ce2606c8fbec27e98de4ca2503653e82b60827 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 22:57:57 +0300 Subject: [PATCH 072/160] fixed humanoid arguments docs --- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 10 +++++----- .../envs/mujoco/humanoidstandup_v5.py | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 89372e38..4e944904 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -243,16 +243,16 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | `forward_reward_weight` | **float** | `1.25` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ term (see section on reward) | | `contact_cost_weight` | **float** | `5e-7` | Weight for _contact_cost_ term (see section on reward) | - | `contact_cost_range` | **float** | `(-np.inf, 10.0) | | + | `contact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _contact_cost_ | | `healthy_reward` | **float** | `5.0` | Constant reward given if the humanoid is "healthy" after timestep | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| - | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | - | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | - | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | + | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations. | + | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | + | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | + | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 15eea550..12237744 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -228,17 +228,17 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"humanoidstandup.xml"` | Path to a MuJoCo model | - | `uph_cost_weight` | **float** | `1` | Weight for _uph_cost_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `0.1` | Weight for _quad_ctrl_cost_ term (see section on reward) | - | `impact_cost_weight` | **float** | `0.5e-6` | Weight for _contact_cost_ term (see section on reward) | - | `impact_cost_range` | **float** | `(-np.inf, 10.0) | | + | `xml_file` | **str** | `"humanoidstandup.xml"` | Path to a MuJoCo model | + | `uph_cost_weight` | **float** | `1` | Weight for _uph_cost_ term (see section on reward) | + | `ctrl_cost_weight` | **float** | `0.1` | Weight for _quad_ctrl_cost_ term (see section on reward) | + | `impact_cost_weight` | **float** | `0.5e-6` | Weight for _impact_cost_ term (see section on reward) | + | `impact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _impact_cost_ | | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations.| - | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | - | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | - | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | + | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations. | + | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | + | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | + | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. From 2674f97c86b7d78517795fb13fc09fcdab38cadd Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 26 May 2023 23:14:09 +0300 Subject: [PATCH 073/160] add dynamic obs_size calculation --- gymnasium_robotics/envs/mujoco/ant_v5.py | 18 ++++++------- .../envs/mujoco/half_cheetah_v5.py | 12 ++++----- gymnasium_robotics/envs/mujoco/hopper_v5.py | 12 ++++----- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 26 +++++++++---------- .../envs/mujoco/humanoidstandup_v5.py | 17 ++++++++---- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 12 ++++----- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 12 ++++----- 7 files changed, 58 insertions(+), 51 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 6d62e064..fbb6e062 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -239,14 +239,6 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_size = 29 - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += 78 * include_cfrc_ext_in_observation - - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -260,11 +252,19 @@ def __init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, # needs to be defined after default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs ) + obs_size = self.data.qpos.size + self.data.qvel.size + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation + + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index a9ec4158..d1ca1e41 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -156,11 +156,6 @@ def __init__( exclude_current_positions_from_observation ) - obs_size = 18 - exclude_current_positions_from_observation - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -174,11 +169,16 @@ def __init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, ) + obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) return control_cost diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index ced41fc7..1c78de05 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -181,11 +181,6 @@ def __init__( exclude_current_positions_from_observation ) - obs_size = 12 - exclude_current_positions_from_observation - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -199,11 +194,16 @@ def __init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, ) + obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 4e944904..df0e3c41 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -323,35 +323,35 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - obs_size = 47 - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += 130 * include_cinert_in_observation - obs_size += 78 * include_cvel_in_observation - obs_size += 17 * include_qfrc_actuator_in_observation - obs_size += 78 * include_cfrc_ext_in_observation - - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", "rgb_array", "depth_array", ], - "render_fps": 335 / frame_skip, + # "render_fps": 335 / frame_skip, } MujocoEnv.__init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, ) + obs_size = self.data.qpos.size + self.data.qvel.size + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += self.data.cinert[1:].size * include_cinert_in_observation + obs_size += self.data.cvel[1:].size * include_cvel_in_observation + obs_size += (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation + obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation + + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 12237744..9236bcc3 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -304,10 +304,6 @@ def __init__( obs_size += 17 * include_qfrc_actuator_in_observation obs_size += 78 * include_cfrc_ext_in_observation - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -321,11 +317,22 @@ def __init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, ) + obs_size = self.data.qpos.size + self.data.qvel.size + obs_size -= 2 * exclude_current_positions_from_observation + obs_size += self.data.cinert[1:].size * include_cinert_in_observation + obs_size += self.data.cvel[1:].size * include_cvel_in_observation + obs_size += (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation + obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation + + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + def _get_obs(self): position = self.data.qpos.flat.copy() velocity = self.data.qvel.flat.copy() diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 2bf18838..70be08ba 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -142,11 +142,6 @@ def __init__( exclude_current_positions_from_observation ) - obs_size = 10 - 2 * exclude_current_positions_from_observation - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -157,7 +152,12 @@ def __init__( } MujocoEnv.__init__( - self, xml_file, frame_skip, observation_space=observation_space, **kwargs + self, xml_file, frame_skip, observation_space=None, **kwargs + ) + + obs_size = self.data.qpos.size + self.data.qvel.size - 2 * exclude_current_positions_from_observation + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) def control_cost(self, action): diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 82ffb942..10878ba9 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -182,11 +182,6 @@ def __init__( exclude_current_positions_from_observation ) - obs_size = 18 - exclude_current_positions_from_observation - observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - self.metadata = { "render_modes": [ "human", @@ -200,11 +195,16 @@ def __init__( self, xml_file, frame_skip, - observation_space=observation_space, + observation_space=None, default_camera_config=DEFAULT_CAMERA_CONFIG, **kwargs, ) + obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + @property def healthy_reward(self): return ( From 5a025b603194b5f60faac90dc6f0645d80b7d883 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 27 May 2023 15:42:16 +0300 Subject: [PATCH 074/160] add `metadata["observation_structure"]` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/hopper_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/humanoid_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/swimmer_v5.py | 10 ++++++++++ gymnasium_robotics/envs/mujoco/walker2d_v5.py | 10 ++++++++++ 7 files changed, 70 insertions(+) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index fbb6e062..e0456c7d 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -265,6 +265,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": 0, + "cvel": 0, + "qfrc_actuator": 0, + "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, + } + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index d1ca1e41..6e8d173b 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -179,6 +179,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": 0, + "cvel": 0, + "qfrc_actuator": 0, + "cfrc_ext": 0, + } + def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) return control_cost diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 1c78de05..5d6654a3 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -204,6 +204,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": 0, + "cvel": 0, + "qfrc_actuator": 0, + "cfrc_ext": 0, + } + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index df0e3c41..196b3411 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -352,6 +352,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": self.data.cinert[1:].size * include_cinert_in_observation, + "cvel": self.data.cvel[1:].size * include_cvel_in_observation, + "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, + "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, + } + @property def healthy_reward(self): return ( diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 9236bcc3..95741ed2 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -333,6 +333,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": self.data.cinert[1:].size * include_cinert_in_observation, + "cvel": self.data.cvel[1:].size * include_cvel_in_observation, + "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, + "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, + } + def _get_obs(self): position = self.data.qpos.flat.copy() velocity = self.data.qvel.flat.copy() diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 70be08ba..5d67aba4 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -160,6 +160,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": 0, + "cvel": 0, + "qfrc_actuator": 0, + "cfrc_ext": 0, + } + def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) return control_cost diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 10878ba9..1d588fae 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -205,6 +205,16 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) + self.metadata["observation_structure"] = { + "skipped_qpos": 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qvel": self.data.qvel.size, + "cinert": 0, + "cvel": 0, + "qfrc_actuator": 0, + "cfrc_ext": 0, + } + @property def healthy_reward(self): return ( From 191e7abfd2e11bdef6b18e3d19f3f725ba7f36f8 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 27 May 2023 18:03:35 +0300 Subject: [PATCH 075/160] cartpole add args --- .../envs/mujoco/inverted_pendulum_v5.py | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 1614067c..8bc88b6a 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -69,26 +69,39 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): 3. Termination: The absolute value of the vertical angle between the pole and the cart is greater than 0.2 radian. ## Arguments - No additional arguments are currently supported. + `gymnasium.make` takes additional arguments such as `reset_noise_scale`. ```python import gymnasium as gym - env = gym.make('InvertedPendulum-v5') + env = gym.make('InvertedPendulum-v5', reset_noise_scale=0.1) ``` + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|-------------------------------| + | `xml_file` | **str** | `"inverted_double_pendulum.xml"` | Path to a MuJoCo model | + | `reset_noise_scale` | **float** | `0.01` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward survive" being 1 on every step (even on terminal steps) and added "reward_survive" to `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.. - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen. + * v3: This environment does not have a v3 release. * v2: All continuous control environments now use mujoco-py >= 1.5. * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum). * v0: Initial versions release (1.0.0) """ - def __init__(self, frame_skip=2, **kwargs): - utils.EzPickle.__init__(self, frame_skip, **kwargs) + def __init__( + self, + xml_file="inverted_pendulum.xml", + frame_skip=2, + reset_noise_scale=0.01, + **kwargs, + ): + utils.EzPickle.__init__(self, xml_file, frame_skip, reset_noise_scale, **kwargs) observation_space = Box(low=-np.inf, high=np.inf, shape=(4,), dtype=np.float64) + self._reset_noise_scale = reset_noise_scale + self.metadata = { "render_modes": [ "human", @@ -100,7 +113,7 @@ def __init__(self, frame_skip=2, **kwargs): MujocoEnv.__init__( self, - "inverted_pendulum.xml", + xml_file, frame_skip, observation_space=observation_space, default_camera_config=DEFAULT_CAMERA_CONFIG, @@ -123,11 +136,14 @@ def step(self, a): return ob, reward, terminated, False, info def reset_model(self): + noise_low = -self._reset_noise_scale + noise_high = self._reset_noise_scale + qpos = self.init_qpos + self.np_random.uniform( - size=self.model.nq, low=-0.01, high=0.01 + size=self.model.nq, low=noise_low, high=noise_high ) qvel = self.init_qvel + self.np_random.uniform( - size=self.model.nv, low=-0.01, high=0.01 + size=self.model.nv, low=noise_low, high=noise_high ) self.set_state(qpos, qvel) return self._get_obs() From 344550a3dd7c951ef7ac93dd2bfdea60ffce5dfe Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 27 May 2023 20:07:03 +0300 Subject: [PATCH 076/160] typo fix --- gymnasium_robotics/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index aec91f40..d9240154 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1149,7 +1149,7 @@ def _merge(a, b): register( id="Pusher-v5", - entry_point="gymnasium_robotics.robotics.envs.mujoco.pusher_v5:PusherEnv", + entry_point="gymnasium_robotics.envs.mujoco.pusher_v5:PusherEnv", max_episode_steps=100, reward_threshold=0.0, ) From a265c477da9399fcead86747133a147ae9f362e6 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 27 May 2023 20:55:36 +0300 Subject: [PATCH 077/160] added `reacher`&`pusher` reward weights --- gymnasium_robotics/envs/mujoco/pusher_v5.py | 28 +++++++++++++------- gymnasium_robotics/envs/mujoco/reacher_v5.py | 22 ++++++++++----- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index ae6a3f6a..ad14cd8a 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -118,9 +118,12 @@ class PusherEnv(MujocoEnv, utils.EzPickle): env = gym.make('Pusher-v5', xml_file=...) ``` - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|-------------------------------| - | `xml_file` | **str** |`"pusher.xml"`| Path to a MuJoCo model | + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|----------------------------------------------------------| + | `xml_file` | **str** |`"pusher.xml"`| Path to a MuJoCo model | + | `reward_near_weight` | **float** | `0.5` | Weight for *reward_near* term (see section on reward) | + | `reward_dist_weight` | **float** | `1` | Weight for *reward_dist* term (see section on reward) | + | `reward_control_weight` | **float** | `0.1` | Weight for *reward_control* term (see section on reward) | ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. "reward_near" is added to the `info`. @@ -143,9 +146,16 @@ def __init__( self, xml_file="pusher.xml", frame_skip=5, + reward_near_weight=0.5, + reward_dist_weight=1, + reward_control_weight=0.1, **kwargs, ): - utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) + utils.EzPickle.__init__(self, xml_file, frame_skip, reward_near_weight, reward_dist_weight, reward_control_weight, **kwargs) + self._reward_near_weight = reward_near_weight + self._reward_dist_weight = reward_dist_weight + self._reward_control_weight = reward_control_weight + observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) self.metadata = { @@ -154,7 +164,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, + #"render_fps": 100 / frame_skip, } MujocoEnv.__init__( @@ -170,10 +180,10 @@ def step(self, a): vec_1 = self.get_body_com("object") - self.get_body_com("tips_arm") vec_2 = self.get_body_com("object") - self.get_body_com("goal") - reward_near = -np.linalg.norm(vec_1) - reward_dist = -np.linalg.norm(vec_2) - reward_ctrl = -np.square(a).sum() - reward = reward_dist + 0.1 * reward_ctrl + 0.5 * reward_near + reward_near = -np.linalg.norm(vec_1) * self._reward_near_weight + reward_dist = -np.linalg.norm(vec_2) * self._reward_dist_weight + reward_ctrl = -np.square(a).sum() * self._reward_control_weight + reward = reward_dist + reward_ctrl + reward_near self.do_simulation(a, self.frame_skip) if self.render_mode == "human": diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 4e2f4f3c..3c657a0b 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -105,9 +105,11 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): env = gym.make('Reacher-v5', xml_file=...) ``` - | Parameter | Type | Default |Description | - |-------------------------|------------|-------------- |-------------------------------| - | `xml_file` | **str** |`"reacher.xml"`| Path to a MuJoCo model | + | Parameter | Type | Default |Description | + |-------------------------|------------|--------------|----------------------------------------------------------| + | `xml_file` | **str** |`"reacher.xml"`| Path to a MuJoCo model | + | `reward_dist_weight` | **float** | `1` | Weight for *reward_dist* term (see section on reward) | + | `reward_control_weight` | **float** | `0.1` | Weight for *reward_control* term (see section on reward) | ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. Remove "z - position_fingertip" from the observation space. @@ -121,9 +123,15 @@ def __init__( self, xml_file="reacher.xml", frame_skip=2, + reward_dist_weight=1, + reward_control_weight=1, **kwargs, ): - utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) + utils.EzPickle.__init__(self, xml_file, frame_skip, reward_dist_weight, reward_control_weight, **kwargs) + + self._reward_dist_weight = reward_dist_weight + self._reward_control_weight = reward_control_weight + observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) self.metadata = { @@ -132,7 +140,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, + #"render_fps": 100 / frame_skip, } MujocoEnv.__init__( @@ -146,8 +154,8 @@ def __init__( def step(self, a): vec = self.get_body_com("fingertip") - self.get_body_com("target") - reward_dist = -np.linalg.norm(vec) - reward_ctrl = -np.square(a).sum() + reward_dist = -np.linalg.norm(vec) * self._reward_dist_weight + reward_ctrl = -np.square(a).sum() * self._reward_control_weight reward = reward_dist + reward_ctrl self.do_simulation(a, self.frame_skip) From 802e22d4cd7de0e2a8bfef20e22721c9bcd4e05d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 27 May 2023 20:57:34 +0300 Subject: [PATCH 078/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 5 ++--- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 6 +++--- .../envs/mujoco/inverted_double_pendulum_v5.py | 2 +- gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py | 6 +++--- gymnasium_robotics/envs/mujoco/reacher_v5.py | 6 +++--- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index e0456c7d..35ecca8e 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -245,7 +245,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv + #"render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 6e8d173b..fa1d8ecd 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -162,7 +162,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, + #"render_fps": 100 / frame_skip, } MujocoEnv.__init__( @@ -205,7 +205,6 @@ def step(self, action): observation = self._get_obs() reward = forward_reward - ctrl_cost - terminated = False info = { "x_position": x_position_after, "x_velocity": x_velocity, @@ -215,7 +214,7 @@ def step(self, action): if self.render_mode == "human": self.render() - return observation, reward, terminated, False, info + return observation, reward, False, False, info def _get_obs(self): position = self.data.qpos.flat.copy() diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 5d6654a3..956384b7 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -187,7 +187,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 500 / frame_skip, + #"render_fps": 500 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 95741ed2..8327e7f0 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -310,7 +310,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 335 / frame_skip, + #"render_fps": 335 / frame_skip, } MujocoEnv.__init__( @@ -385,8 +385,8 @@ def _get_obs(self): ) ) - def step(self, a): - self.do_simulation(a, self.frame_skip) + def step(self, action): + self.do_simulation(action, self.frame_skip) pos_after = self.data.qpos[2] uph_cost = (pos_after - 0) / self.model.opt.timestep diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 9baf520d..7d32a151 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -141,7 +141,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, + #"render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 8bc88b6a..dc6d5a8d 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -108,7 +108,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 50 / frame_skip, + #"render_fps": 50 / frame_skip, } MujocoEnv.__init__( @@ -120,8 +120,8 @@ def __init__( **kwargs, ) - def step(self, a): - self.do_simulation(a, self.frame_skip) + def step(self, action): + self.do_simulation(action, self.frame_skip) ob = self._get_obs() diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 3c657a0b..83a1edea 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -152,13 +152,13 @@ def __init__( **kwargs, ) - def step(self, a): + def step(self, action): vec = self.get_body_com("fingertip") - self.get_body_com("target") reward_dist = -np.linalg.norm(vec) * self._reward_dist_weight - reward_ctrl = -np.square(a).sum() * self._reward_control_weight + reward_ctrl = -np.square(action).sum() * self._reward_control_weight reward = reward_dist + reward_ctrl - self.do_simulation(a, self.frame_skip) + self.do_simulation(action, self.frame_skip) if self.render_mode == "human": self.render() diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 5d67aba4..3ffe4a5e 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -148,7 +148,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 100 / frame_skip, + #"render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 1d588fae..3d81d588 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -188,7 +188,7 @@ def __init__( "rgb_array", "depth_array", ], - "render_fps": 500 / frame_skip, + #"render_fps": 500 / frame_skip, } MujocoEnv.__init__( From f60b453f71e984666fc126121bd6839f2451f71e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 29 May 2023 11:17:55 +0300 Subject: [PATCH 079/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 2 +- .../envs/mujoco/inverted_double_pendulum_v5.py | 4 ++-- gymnasium_robotics/envs/mujoco/pusher_v5.py | 9 +++++---- gymnasium_robotics/envs/mujoco/reacher_v5.py | 3 ++- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 35ecca8e..a89657f2 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -245,7 +245,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv + # "render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index fa1d8ecd..018c2bfd 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -162,7 +162,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, + # "render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 8327e7f0..227bb052 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -310,7 +310,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 335 / frame_skip, + # "render_fps": 335 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 7d32a151..2729141c 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -114,7 +114,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward_survive" being 10 on every step (even on terminal steps). Removed "constraint force" of the hinges from the observation space. Added `info` "reward_survive", "distance_penalty", "velocity_penalty". * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) + * v3: This environment does not have a v3 release. * v2: All continuous control environments now use mujoco-py >= 1.50 * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum) * v0: Initial versions release (1.0.0) @@ -141,7 +141,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, + # "render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index ad14cd8a..3556ae2a 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -128,6 +128,7 @@ class PusherEnv(MujocoEnv, utils.EzPickle): ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. "reward_near" is added to the `info`. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. + * v3: This environment does not have a v3 release. * v2: All continuous control environments now use mujoco-py >= 1.50. * v1: max_time_steps raised to 1000 for robot based tasks (not including pusher, which has a max_time_steps of 100). Added reward_threshold to environments. * v0: Initial versions release (1.0.0). @@ -164,7 +165,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, + # "render_fps": 100 / frame_skip, } MujocoEnv.__init__( @@ -176,16 +177,16 @@ def __init__( **kwargs, ) - def step(self, a): + def step(self, action): vec_1 = self.get_body_com("object") - self.get_body_com("tips_arm") vec_2 = self.get_body_com("object") - self.get_body_com("goal") reward_near = -np.linalg.norm(vec_1) * self._reward_near_weight reward_dist = -np.linalg.norm(vec_2) * self._reward_dist_weight - reward_ctrl = -np.square(a).sum() * self._reward_control_weight + reward_ctrl = -np.square(action).sum() * self._reward_control_weight reward = reward_dist + reward_ctrl + reward_near - self.do_simulation(a, self.frame_skip) + self.do_simulation(action, self.frame_skip) if self.render_mode == "human": self.render() diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 83a1edea..c8e09345 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -114,6 +114,7 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. Remove "z - position_fingertip" from the observation space. * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 + * v3: This environment does not have a v3 release. * v2: All continuous control environments now use mujoco-py >= 1.50 * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. * v0: Initial versions release (1.0.0) @@ -140,7 +141,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, + # "render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 3ffe4a5e..d1b36aa8 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -148,7 +148,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 100 / frame_skip, + # "render_fps": 100 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 3d81d588..205cc681 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -188,7 +188,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 500 / frame_skip, + # "render_fps": 500 / frame_skip, } MujocoEnv.__init__( From e8b045f93910e221da1293f95b12bbdee55712d3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 29 May 2023 11:18:01 +0300 Subject: [PATCH 080/160] cleanup --- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 956384b7..38a365e5 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -187,7 +187,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 500 / frame_skip, + # "render_fps": 500 / frame_skip, } MujocoEnv.__init__( From a7c883d687f18d89d4a2b0464ab4e30437fa64ff Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 29 May 2023 18:08:43 +0300 Subject: [PATCH 081/160] `pre-commit` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 3 ++- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 9 +++++++-- gymnasium_robotics/envs/mujoco/hopper_v5.py | 9 +++++++-- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 6 ++++-- .../envs/mujoco/humanoidstandup_v5.py | 6 ++++-- .../envs/mujoco/inverted_pendulum_v5.py | 2 +- gymnasium_robotics/envs/mujoco/pusher_v5.py | 10 +++++++++- gymnasium_robotics/envs/mujoco/reacher_v5.py | 9 ++++++++- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 13 ++++++++----- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 9 +++++++-- 10 files changed, 57 insertions(+), 19 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index a89657f2..42b06fbb 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -267,7 +267,8 @@ def __init__( self.metadata["observation_structure"] = { "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": 0, "cvel": 0, diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 018c2bfd..120862e5 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -174,14 +174,19 @@ def __init__( **kwargs, ) - obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + obs_size = ( + self.data.qpos.size + + self.data.qvel.size + - exclude_current_positions_from_observation + ) self.observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata["observation_structure"] = { "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": 0, "cvel": 0, diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 38a365e5..29715a6b 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -199,14 +199,19 @@ def __init__( **kwargs, ) - obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + obs_size = ( + self.data.qpos.size + + self.data.qvel.size + - exclude_current_positions_from_observation + ) self.observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata["observation_structure"] = { "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": 0, "cvel": 0, diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 196b3411..67061ff6 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -354,11 +354,13 @@ def __init__( self.metadata["observation_structure"] = { "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": self.data.cinert[1:].size * include_cinert_in_observation, "cvel": self.data.cvel[1:].size * include_cvel_in_observation, - "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, + "qfrc_actuator": (self.data.qvel.size - 6) + * include_qfrc_actuator_in_observation, "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, } diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 227bb052..45e4ba08 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -335,11 +335,13 @@ def __init__( self.metadata["observation_structure"] = { "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": self.data.cinert[1:].size * include_cinert_in_observation, "cvel": self.data.cvel[1:].size * include_cvel_in_observation, - "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, + "qfrc_actuator": (self.data.qvel.size - 6) + * include_qfrc_actuator_in_observation, "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, } diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index dc6d5a8d..8e64ae9d 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -108,7 +108,7 @@ def __init__( "rgb_array", "depth_array", ], - #"render_fps": 50 / frame_skip, + # "render_fps": 50 / frame_skip, } MujocoEnv.__init__( diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 3556ae2a..9111602b 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -152,7 +152,15 @@ def __init__( reward_control_weight=0.1, **kwargs, ): - utils.EzPickle.__init__(self, xml_file, frame_skip, reward_near_weight, reward_dist_weight, reward_control_weight, **kwargs) + utils.EzPickle.__init__( + self, + xml_file, + frame_skip, + reward_near_weight, + reward_dist_weight, + reward_control_weight, + **kwargs, + ) self._reward_near_weight = reward_near_weight self._reward_dist_weight = reward_dist_weight self._reward_control_weight = reward_control_weight diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index c8e09345..fcc4d7f8 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -128,7 +128,14 @@ def __init__( reward_control_weight=1, **kwargs, ): - utils.EzPickle.__init__(self, xml_file, frame_skip, reward_dist_weight, reward_control_weight, **kwargs) + utils.EzPickle.__init__( + self, + xml_file, + frame_skip, + reward_dist_weight, + reward_control_weight, + **kwargs, + ) self._reward_dist_weight = reward_dist_weight self._reward_control_weight = reward_control_weight diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index d1b36aa8..2bc92220 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -151,18 +151,21 @@ def __init__( # "render_fps": 100 / frame_skip, } - MujocoEnv.__init__( - self, xml_file, frame_skip, observation_space=None, **kwargs - ) + MujocoEnv.__init__(self, xml_file, frame_skip, observation_space=None, **kwargs) - obs_size = self.data.qpos.size + self.data.qvel.size - 2 * exclude_current_positions_from_observation + obs_size = ( + self.data.qpos.size + + self.data.qvel.size + - 2 * exclude_current_positions_from_observation + ) self.observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata["observation_structure"] = { "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": 0, "cvel": 0, diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 205cc681..cab70e8f 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -200,14 +200,19 @@ def __init__( **kwargs, ) - obs_size = self.data.qpos.size + self.data.qvel.size - exclude_current_positions_from_observation + obs_size = ( + self.data.qpos.size + + self.data.qvel.size + - exclude_current_positions_from_observation + ) self.observation_space = Box( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) self.metadata["observation_structure"] = { "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, + "qpos": self.data.qpos.size + - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, "cinert": 0, "cvel": 0, From 9c3c9e0246657fd87a32790027b94aa8da785ad3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 29 May 2023 20:31:16 +0300 Subject: [PATCH 082/160] add `default_camera_config` argunment --- gymnasium_robotics/envs/mujoco/ant_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/hopper_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 3 ++- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/pusher_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/reacher_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 4 +++- 8 files changed, 22 insertions(+), 7 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 42b06fbb..21b50658 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -196,6 +196,7 @@ def __init__( self, xml_file="ant.xml", frame_skip=5, + default_camera_config=DEFAULT_CAMERA_CONFIG, ctrl_cost_weight=0.5, contact_cost_weight=5e-4, healthy_reward=1.0, @@ -211,6 +212,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, ctrl_cost_weight, contact_cost_weight, healthy_reward, @@ -253,7 +255,7 @@ def __init__( xml_file, frame_skip, observation_space=None, # needs to be defined after - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs ) diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 120862e5..ed618fb6 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -129,6 +129,7 @@ def __init__( self, xml_file="half_cheetah.xml", frame_skip=5, + default_camera_config=DEFAULT_CAMERA_CONFIG, forward_reward_weight=1.0, ctrl_cost_weight=0.1, reset_noise_scale=0.1, @@ -139,6 +140,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, forward_reward_weight, ctrl_cost_weight, reset_noise_scale, diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 29715a6b..3d7b7a76 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -137,6 +137,7 @@ def __init__( self, xml_file="hopper_v5.xml", frame_skip=4, + default_camera_config=DEFAULT_CAMERA_CONFIG, forward_reward_weight=1.0, ctrl_cost_weight=1e-3, healthy_reward=1.0, @@ -152,6 +153,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, forward_reward_weight, ctrl_cost_weight, healthy_reward, @@ -195,7 +197,7 @@ def __init__( xml_file, frame_skip, observation_space=None, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 67061ff6..277e6012 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -267,6 +267,7 @@ def __init__( self, xml_file="humanoid.xml", frame_skip=5, + default_camera_config=DEFAULT_CAMERA_CONFIG, forward_reward_weight=1.25, ctrl_cost_weight=0.1, contact_cost_weight=5e-7, @@ -337,7 +338,7 @@ def __init__( xml_file, frame_skip, observation_space=None, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 45e4ba08..cc72c9e6 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -252,6 +252,7 @@ def __init__( self, xml_file="humanoidstandup.xml", frame_skip=5, + default_camera_config=DEFAULT_CAMERA_CONFIG, uph_cost_weight=1, ctrl_cost_weight=0.1, impact_cost_weight=0.5e-6, @@ -268,6 +269,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, uph_cost_weight, ctrl_cost_weight, impact_cost_weight, @@ -318,7 +320,7 @@ def __init__( xml_file, frame_skip, observation_space=None, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 9111602b..15231db0 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -147,6 +147,7 @@ def __init__( self, xml_file="pusher.xml", frame_skip=5, + default_camera_config=DEFAULT_CAMERA_CONFIG, reward_near_weight=0.5, reward_dist_weight=1, reward_control_weight=0.1, @@ -156,6 +157,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, reward_near_weight, reward_dist_weight, reward_control_weight, @@ -181,7 +183,7 @@ def __init__( xml_file, frame_skip, observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index fcc4d7f8..122a5f23 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -124,6 +124,7 @@ def __init__( self, xml_file="reacher.xml", frame_skip=2, + default_camera_config=DEFAULT_CAMERA_CONFIG, reward_dist_weight=1, reward_control_weight=1, **kwargs, @@ -132,6 +133,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, reward_dist_weight, reward_control_weight, **kwargs, @@ -156,7 +158,7 @@ def __init__( xml_file, frame_skip, observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index cab70e8f..d64b0f3a 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -142,6 +142,7 @@ def __init__( self, xml_file="walker2d_v5.xml", frame_skip=4, + default_camera_config=DEFAULT_CAMERA_CONFIG, forward_reward_weight=1.0, ctrl_cost_weight=1e-3, healthy_reward=1.0, @@ -156,6 +157,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, forward_reward_weight, ctrl_cost_weight, healthy_reward, @@ -196,7 +198,7 @@ def __init__( xml_file, frame_skip, observation_space=None, - default_camera_config=DEFAULT_CAMERA_CONFIG, + default_camera_config=default_camera_config, **kwargs, ) From 938b4e95dc9ff2d75573f03d98640dc2c35e06bb Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 1 Jun 2023 12:02:16 +0300 Subject: [PATCH 083/160] fix `ant/humanoid` `info["x/y_position"]` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 4 ++-- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 21b50658..2b07490c 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -339,8 +339,8 @@ def step(self, action): "reward_ctrl": -ctrl_cost, "reward_contact": -contact_cost, "reward_survive": healthy_reward, - "x_position": xy_position_after[0], - "y_position": xy_position_after[1], + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 277e6012..513caaad 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -463,8 +463,8 @@ def step(self, action): "reward_forward": forward_reward, "reward_ctrl": -ctrl_cost, "reward_contact": -contact_cost, - "x_position": xy_position_after[0], - "y_position": xy_position_after[1], + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], "tendon_lenght": self.data.ten_length, "tendon_velocity": self.data.ten_velocity, "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), From 8800d5f05e0219cbafdebf76a069268867b57e93 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 1 Jun 2023 16:24:09 +0300 Subject: [PATCH 084/160] `ant` add `forward_reward_weight` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 2b07490c..943b4100 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -197,6 +197,7 @@ def __init__( xml_file="ant.xml", frame_skip=5, default_camera_config=DEFAULT_CAMERA_CONFIG, + forward_reward_weight=1, ctrl_cost_weight=0.5, contact_cost_weight=5e-4, healthy_reward=1.0, @@ -213,6 +214,7 @@ def __init__( xml_file, frame_skip, default_camera_config, + forward_reward_weight, ctrl_cost_weight, contact_cost_weight, healthy_reward, @@ -225,6 +227,7 @@ def __init__( **kwargs ) + self._forward_reward_weight = forward_reward_weight self._ctrl_cost_weight = ctrl_cost_weight self._contact_cost_weight = contact_cost_weight @@ -323,7 +326,7 @@ def step(self, action): xy_velocity = (xy_position_after - xy_position_before) / self.dt x_velocity, y_velocity = xy_velocity - forward_reward = x_velocity + forward_reward = x_velocity * self._forward_reward_weight healthy_reward = self.healthy_reward rewards = forward_reward + healthy_reward From 3c568662aa0330041ec806aa08fd598a80a02a22 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 1 Jun 2023 16:25:09 +0300 Subject: [PATCH 085/160] ant doc --- gymnasium_robotics/envs/mujoco/ant_v5.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 943b4100..c2acf160 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -173,8 +173,8 @@ class AntEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default |Description | |--------------------------------------------|------------|--------------|-------------------------------| |`xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | + |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | |`ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | - |`use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | |`contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | |`healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | @@ -183,6 +183,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): |`reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | |`exclude_current_positions_from_observation`| **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | |`include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | + |`use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | ## Version History * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3, also removed contact forces from the default observation space (new variable `use_contact_forces=True` can restore them) From f67639e2c899b9156851e28dba6b71911c6bfb8c Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 1 Jun 2023 16:35:46 +0300 Subject: [PATCH 086/160] `ant` cleaned up xy pos aquasition --- gymnasium_robotics/envs/mujoco/ant_v5.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index c2acf160..0a840719 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -320,9 +320,13 @@ def terminated(self): return terminated def step(self, action): - xy_position_before = self.get_body_com("torso")[:2].copy() + xy_position_before = self.data.body("torso").xpos[:2] + # TODO remove after validation + assert (xy_position_before == self.get_body_com("torso")[:2].copy()).all() self.do_simulation(action, self.frame_skip) - xy_position_after = self.get_body_com("torso")[:2].copy() + xy_position_after = self.data.body("torso").xpos[:2] + # TODO remove after validation + assert (xy_position_after == self.get_body_com("torso")[:2].copy()).all() xy_velocity = (xy_position_after - xy_position_before) / self.dt x_velocity, y_velocity = xy_velocity From ebc96c08bc893684518d0b43ee0461f364b8644b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 1 Jun 2023 17:27:48 +0300 Subject: [PATCH 087/160] added `main_body` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 0a840719..f088debb 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -121,8 +121,9 @@ class AntEnv(MujocoEnv, utils.EzPickle): The reward consists of three parts: - *healthy_reward*: Every timestep that the ant is healthy (see definition in section "Episode Termination"), it gets a reward of fixed value `healthy_reward` - *forward_reward*: A reward of moving forward which is measured as - *(x-coordinate before action - x-coordinate after action)/dt*. *dt* is the time - between actions and is dependent on the `frame_skip` parameter (default is 5), + $\frac{dx}{dt} \times$ `forward_reward_weight`. + $dx$ is the displacement of the `main_body` (x-coordinate before action - x-coordinate after action)*. + $dt$ is the time between actions and is dependent on the `frame_skip` parameter (default is 5), where the frametime is 0.01 - making the default *dt = 5 * 0.01 = 0.05*. This reward would be positive if the ant moves forward (in positive x direction). - *ctrl_cost*: A negative reward for penalising the ant if it takes actions @@ -173,10 +174,11 @@ class AntEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default |Description | |--------------------------------------------|------------|--------------|-------------------------------| |`xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | - |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | - |`ctrl_cost_weight` | **float** | `0.5` | Weight for *ctrl_cost* term (see section on reward) | - |`contact_cost_weight` | **float** | `5e-4` | Weight for *contact_cost* term (see section on reward) | + |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward)| + |`ctrl_cost_weight` | **float** | `0.5` | Weight for _ctrl_cost_ term (see section on reward) | + |`contact_cost_weight` | **float** | `5e-4` | Weight for _contact_cost_ term (see section on reward) | |`healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + |`main_body` |**str|int** | `1`("torso") | Name or ID of the body, whose diplacement is used to calculate the *dx*/_forward_reward_ (usefull for custom MuJoCo models)| |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | |`healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | |`contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | @@ -202,6 +204,7 @@ def __init__( ctrl_cost_weight=0.5, contact_cost_weight=5e-4, healthy_reward=1.0, + main_body="torso", terminate_when_unhealthy=True, healthy_z_range=(0.2, 1.0), contact_force_range=(-1.0, 1.0), @@ -219,6 +222,7 @@ def __init__( ctrl_cost_weight, contact_cost_weight, healthy_reward, + main_body, terminate_when_unhealthy, healthy_z_range, contact_force_range, @@ -238,6 +242,8 @@ def __init__( self._contact_force_range = contact_force_range + self._main_body = main_body + self._reset_noise_scale = reset_noise_scale self._exclude_current_positions_from_observation = ( @@ -320,11 +326,11 @@ def terminated(self): return terminated def step(self, action): - xy_position_before = self.data.body("torso").xpos[:2] + xy_position_before = self.data.body(self._main_body).xpos[:2] # TODO remove after validation assert (xy_position_before == self.get_body_com("torso")[:2].copy()).all() self.do_simulation(action, self.frame_skip) - xy_position_after = self.data.body("torso").xpos[:2] + xy_position_after = self.data.body(self._main_body).xpos[:2] # TODO remove after validation assert (xy_position_after == self.get_body_com("torso")[:2].copy()).all() From 5263fe6f8cad652c0d189cab024b89f8bf147e60 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 2 Jun 2023 12:03:10 +0300 Subject: [PATCH 088/160] fix `healthy_reward` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 9 ++++----- gymnasium_robotics/envs/mujoco/hopper_v5.py | 9 ++++----- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 10 +++++----- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 9 ++++----- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index f088debb..4e56c826 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -290,10 +290,7 @@ def __init__( @property def healthy_reward(self): - return ( - float(self.is_healthy or self._terminate_when_unhealthy) - * self._healthy_reward - ) + return self.is_healthy * self._healthy_reward def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) @@ -322,7 +319,9 @@ def is_healthy(self): @property def terminated(self): - terminated = not self.is_healthy if self._terminate_when_unhealthy else False + terminated = (not self.is_healthy) and self._terminate_when_unhealthy + # TODO remove after validation + assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) return terminated def step(self, action): diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 3d7b7a76..e326c213 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -223,10 +223,7 @@ def __init__( @property def healthy_reward(self): - return ( - float(self.is_healthy or self._terminate_when_unhealthy) - * self._healthy_reward - ) + return self.is_healthy * self._healthy_reward def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) @@ -251,7 +248,9 @@ def is_healthy(self): @property def terminated(self): - terminated = not self.is_healthy if self._terminate_when_unhealthy else False + terminated = (not self.is_healthy) and self._terminate_when_unhealthy + # TODO remove after validation + assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) return terminated def _get_obs(self): diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 513caaad..d653c3b2 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -287,6 +287,7 @@ def __init__( self, xml_file, frame_skip, + default_camera_config, forward_reward_weight, ctrl_cost_weight, contact_cost_weight, @@ -367,10 +368,7 @@ def __init__( @property def healthy_reward(self): - return ( - float(self.is_healthy or self._terminate_when_unhealthy) - * self._healthy_reward - ) + return self.is_healthy * self._healthy_reward def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(self.data.ctrl)) @@ -393,7 +391,9 @@ def is_healthy(self): @property def terminated(self): - terminated = (not self.is_healthy) if self._terminate_when_unhealthy else False + terminated = (not self.is_healthy) and self._terminate_when_unhealthy + # TODO remove after validation + assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) return terminated def _get_obs(self): diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index d64b0f3a..df50b751 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -224,10 +224,7 @@ def __init__( @property def healthy_reward(self): - return ( - float(self.is_healthy or self._terminate_when_unhealthy) - * self._healthy_reward - ) + return self.is_healthy * self._healthy_reward def control_cost(self, action): control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) @@ -248,7 +245,9 @@ def is_healthy(self): @property def terminated(self): - terminated = not self.is_healthy if self._terminate_when_unhealthy else False + terminated = (not self.is_healthy) and self._terminate_when_unhealthy + # TODO remove after validation + assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) return terminated def _get_obs(self): From 89efd657a345753cac02df3daf49a91f95801b00 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 2 Jun 2023 12:48:24 +0300 Subject: [PATCH 089/160] pre-commit --- gymnasium_robotics/envs/mujoco/ant_v5.py | 6 ++++-- gymnasium_robotics/envs/mujoco/hopper_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 4 +++- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 4e56c826..560de7ab 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -178,7 +178,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): |`ctrl_cost_weight` | **float** | `0.5` | Weight for _ctrl_cost_ term (see section on reward) | |`contact_cost_weight` | **float** | `5e-4` | Weight for _contact_cost_ term (see section on reward) | |`healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | - |`main_body` |**str|int** | `1`("torso") | Name or ID of the body, whose diplacement is used to calculate the *dx*/_forward_reward_ (usefull for custom MuJoCo models)| + |`main_body` |**str|int** | `1`("torso") | Name or ID of the body, whose diplacement is used to calculate the *dx*/_forward_reward_ (useful for custom MuJoCo models)| |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | |`healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | |`contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | @@ -321,7 +321,9 @@ def is_healthy(self): def terminated(self): terminated = (not self.is_healthy) and self._terminate_when_unhealthy # TODO remove after validation - assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) + assert terminated == ( + not self.is_healthy if self._terminate_when_unhealthy else False + ) return terminated def step(self, action): diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index e326c213..2caa1ecc 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -250,7 +250,9 @@ def is_healthy(self): def terminated(self): terminated = (not self.is_healthy) and self._terminate_when_unhealthy # TODO remove after validation - assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) + assert terminated == ( + not self.is_healthy if self._terminate_when_unhealthy else False + ) return terminated def _get_obs(self): diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index d653c3b2..a530a099 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -393,7 +393,9 @@ def is_healthy(self): def terminated(self): terminated = (not self.is_healthy) and self._terminate_when_unhealthy # TODO remove after validation - assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) + assert terminated == ( + not self.is_healthy if self._terminate_when_unhealthy else False + ) return terminated def _get_obs(self): diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index df50b751..a49bf4f5 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -247,7 +247,9 @@ def is_healthy(self): def terminated(self): terminated = (not self.is_healthy) and self._terminate_when_unhealthy # TODO remove after validation - assert terminated == (not self.is_healthy if self._terminate_when_unhealthy else False) + assert terminated == ( + not self.is_healthy if self._terminate_when_unhealthy else False + ) return terminated def _get_obs(self): From ca360f9a71c4db99614825a961ec780bb629fbaa Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 2 Jun 2023 13:13:28 +0300 Subject: [PATCH 090/160] fix ant velocity --- gymnasium_robotics/envs/mujoco/ant_v5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 560de7ab..ebc728c1 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -327,11 +327,11 @@ def terminated(self): return terminated def step(self, action): - xy_position_before = self.data.body(self._main_body).xpos[:2] + xy_position_before = self.data.body(self._main_body).xpos[:2].copy() # TODO remove after validation assert (xy_position_before == self.get_body_com("torso")[:2].copy()).all() self.do_simulation(action, self.frame_skip) - xy_position_after = self.data.body(self._main_body).xpos[:2] + xy_position_after = self.data.body(self._main_body).xpos[:2].copy() # TODO remove after validation assert (xy_position_after == self.get_body_com("torso")[:2].copy()).all() From 3e6fc80970bb90d7ee43e28715bcb921f966ccc4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 2 Jun 2023 14:57:15 +0300 Subject: [PATCH 091/160] dict --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 5 ++--- .../envs/mujoco/inverted_double_pendulum_v5.py | 10 +++++----- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index ebc728c1..87b3d49b 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -177,7 +177,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward)| |`ctrl_cost_weight` | **float** | `0.5` | Weight for _ctrl_cost_ term (see section on reward) | |`contact_cost_weight` | **float** | `5e-4` | Weight for _contact_cost_ term (see section on reward) | - |`healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + |`healthy_reward` | **float** | `1` | Weight for _healthy_reward_ term (see section on reward) | |`main_body` |**str|int** | `1`("torso") | Name or ID of the body, whose diplacement is used to calculate the *dx*/_forward_reward_ (useful for custom MuJoCo models)| |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | |`healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 2caa1ecc..86050993 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -116,7 +116,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): | `xml_file` | **str** | `"hopper_v5.xml"` | Path to a MuJoCo model | | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctrl_cost_ reward (see section on reward) | - | `healthy_reward` | **float** | `1` | Constant reward given if the ant is "healthy" after timestep | + | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the hopper is no longer healthy | | `healthy_state_range` | **tuple** | `(-100, 100)` | The elements of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) must be in this range for the hopper to be considered healthy | | `healthy_z_range` | **tuple** | `(0.7, float("inf"))` | The z-coordinate must be in this range for the hopper to be considered healthy | diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index a530a099..401e2e00 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -243,8 +243,8 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | `forward_reward_weight` | **float** | `1.25` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ term (see section on reward) | | `contact_cost_weight` | **float** | `5e-7` | Weight for _contact_cost_ term (see section on reward) | - | `contact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _contact_cost_ | - | `healthy_reward` | **float** | `5.0` | Constant reward given if the humanoid is "healthy" after timestep | + | `contact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _contact_cost_ term (see section on reward) | + | `healthy_reward` | **float** | `5.0` | Weight for _healthy_reward_ term (see section on reward) | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | @@ -255,7 +255,6 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) * v2: All continuous control environments now use mujoco-py >= 1.50 diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 2729141c..946184c5 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -105,11 +105,11 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): env = gym.make('InvertedDoublePendulum-v5', healthy_reward=10, ...) ``` - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|-------------------------------| - | `xml_file` | **str** | `"inverted_double_pendulum.xml"` | Path to a MuJoCo model | - | `healthy_reward` | **float** | `10 | Constant reward given if the pendulum is "healthy" (upright) | - | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + | Parameter | Type | Default | Description | + |-------------------------|------------|-------------- |-------------------------------| + | `xml_file` | **str** |`"inverted_double_pendulum.xml"`| Path to a MuJoCo model | + | `healthy_reward` | **float** | `10 | Constant reward given if the pendulum is "healthy" (upright) | + | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | ## Version History * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward_survive" being 10 on every step (even on terminal steps). Removed "constraint force" of the hinges from the observation space. Added `info` "reward_survive", "distance_penalty", "velocity_penalty". diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index a49bf4f5..23ef6225 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -121,7 +121,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): | `xml_file` | **str** |`"walker2d_v5.xml"`| Path to a MuJoCo model | | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | - | `healthy_reward` | **float** | `1.0` | Constant reward given if the ant is "healthy" after timestep | + | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | | `healthy_z_range` | **tuple** | `(0.8, 2)` | The z-coordinate of the torso of the walker must be in this range to be considered healthy | | `healthy_angle_range` | **tuple** | `(-1, 1)` | The angle must be in this range to be considered healthy | From 8921dcd1194feb53ad5ed6b3d2a01b2e1891edc6 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 3 Jun 2023 17:56:03 +0300 Subject: [PATCH 092/160] update renderer --- gymnasium_robotics/envs/mujoco/ant_v5.py | 56 +++++++++++-------- .../envs/mujoco/half_cheetah_v5.py | 27 +++++---- gymnasium_robotics/envs/mujoco/hopper_v5.py | 26 ++++++--- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 26 ++++++--- .../envs/mujoco/humanoidstandup_v5.py | 26 ++++++--- .../mujoco/inverted_double_pendulum_v5.py | 26 ++++++--- .../envs/mujoco/inverted_pendulum_v5.py | 26 ++++++--- gymnasium_robotics/envs/mujoco/pusher_v5.py | 19 +++---- gymnasium_robotics/envs/mujoco/reacher_v5.py | 26 ++++++--- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 14 ++++- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 26 ++++++--- 11 files changed, 189 insertions(+), 109 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 87b3d49b..a35be93b 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -1,5 +1,7 @@ __credits__ = ["Kallinteris-Andreas"] +from typing import Dict, Tuple + import numpy as np from gymnasium import utils from gymnasium.envs.mujoco import MujocoEnv @@ -195,22 +197,30 @@ class AntEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, - xml_file="ant.xml", - frame_skip=5, - default_camera_config=DEFAULT_CAMERA_CONFIG, - forward_reward_weight=1, - ctrl_cost_weight=0.5, - contact_cost_weight=5e-4, - healthy_reward=1.0, - main_body="torso", - terminate_when_unhealthy=True, - healthy_z_range=(0.2, 1.0), - contact_force_range=(-1.0, 1.0), - reset_noise_scale=0.1, - exclude_current_positions_from_observation=True, - include_cfrc_ext_in_observation=True, + xml_file: str = "ant.xml", + frame_skip: int = 5, + default_camera_config: Dict = DEFAULT_CAMERA_CONFIG, + forward_reward_weight: float = 1, + ctrl_cost_weight: float = 0.5, + contact_cost_weight: float = 5e-4, + healthy_reward: float = 1.0, + main_body: str = "torso", + terminate_when_unhealthy: bool = True, + healthy_z_range: Tuple[float, float] = (0.2, 1.0), + contact_force_range: Tuple[float, float] = (-1.0, 1.0), + reset_noise_scale: float = 0.1, + exclude_current_positions_from_observation: bool = True, + include_cfrc_ext_in_observation: bool = True, **kwargs ): utils.EzPickle.__init__( @@ -251,15 +261,6 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 100 / frame_skip, # TODO compute `render_fps` in MujocoEnv - } - MujocoEnv.__init__( self, xml_file, @@ -269,6 +270,15 @@ def __init__( **kwargs ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = self.data.qpos.size + self.data.qvel.size obs_size -= 2 * exclude_current_positions_from_observation obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index ed618fb6..da6a6b61 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -125,6 +125,14 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="half_cheetah.xml", @@ -149,7 +157,6 @@ def __init__( ) self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight self._reset_noise_scale = reset_noise_scale @@ -158,15 +165,6 @@ def __init__( exclude_current_positions_from_observation ) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 100 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -176,6 +174,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = ( self.data.qpos.size + self.data.qvel.size diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 86050993..2328411d 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -133,6 +133,14 @@ class HopperEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0). """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="hopper_v5.xml", @@ -183,15 +191,6 @@ def __init__( exclude_current_positions_from_observation ) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 500 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -201,6 +200,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = ( self.data.qpos.size + self.data.qvel.size diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 401e2e00..d413b0d2 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -262,6 +262,14 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="humanoid.xml", @@ -324,15 +332,6 @@ def __init__( ) self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 335 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -342,6 +341,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = self.data.qpos.size + self.data.qvel.size obs_size -= 2 * exclude_current_positions_from_observation obs_size += self.data.cinert[1:].size * include_cinert_in_observation diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index cc72c9e6..e465bd44 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -248,6 +248,14 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0). """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="humanoidstandup.xml", @@ -306,15 +314,6 @@ def __init__( obs_size += 17 * include_qfrc_actuator_in_observation obs_size += 78 * include_cfrc_ext_in_observation - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 335 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -324,6 +323,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = self.data.qpos.size + self.data.qvel.size obs_size -= 2 * exclude_current_positions_from_observation obs_size += self.data.cinert[1:].size * include_cinert_in_observation diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 946184c5..04d00f3d 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -120,6 +120,14 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="inverted_double_pendulum.xml", @@ -135,15 +143,6 @@ def __init__( observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 100 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -153,6 +152,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + def step(self, action): self.do_simulation(action, self.frame_skip) diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 8e64ae9d..1cadc6ef 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -90,6 +90,14 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="inverted_pendulum.xml", @@ -102,15 +110,6 @@ def __init__( self._reset_noise_scale = reset_noise_scale - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 50 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -120,6 +119,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + def step(self, action): self.do_simulation(action, self.frame_skip) diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index 15231db0..a79f585a 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -140,7 +140,6 @@ class PusherEnv(MujocoEnv, utils.EzPickle): "rgb_array", "depth_array", ], - "render_fps": 20, } def __init__( @@ -169,15 +168,6 @@ def __init__( observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 100 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -187,6 +177,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + def step(self, action): vec_1 = self.get_body_com("object") - self.get_body_com("tips_arm") vec_2 = self.get_body_com("object") - self.get_body_com("goal") diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 122a5f23..869b5ec8 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -120,6 +120,14 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="reacher.xml", @@ -144,15 +152,6 @@ def __init__( observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 100 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -162,6 +161,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + def step(self, action): vec = self.get_body_com("fingertip") - self.get_body_com("target") reward_dist = -np.linalg.norm(vec) * self._reward_dist_weight diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 2bc92220..bf61a5bb 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -112,6 +112,14 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="swimmer.xml", @@ -142,17 +150,17 @@ def __init__( exclude_current_positions_from_observation ) + MujocoEnv.__init__(self, xml_file, frame_skip, observation_space=None, **kwargs) + self.metadata = { "render_modes": [ "human", "rgb_array", "depth_array", ], - # "render_fps": 100 / frame_skip, + "render_fps": int(np.round(1.0 / self.dt)), } - MujocoEnv.__init__(self, xml_file, frame_skip, observation_space=None, **kwargs) - obs_size = ( self.data.qpos.size + self.data.qvel.size diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 23ef6225..274055a0 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -138,6 +138,14 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): * v0: Initial versions release (1.0.0) """ + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + def __init__( self, xml_file="walker2d_v5.xml", @@ -184,15 +192,6 @@ def __init__( exclude_current_positions_from_observation ) - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - # "render_fps": 500 / frame_skip, - } - MujocoEnv.__init__( self, xml_file, @@ -202,6 +201,15 @@ def __init__( **kwargs, ) + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + obs_size = ( self.data.qpos.size + self.data.qvel.size From 360b6dcdc643e55342a793886e6fa194023ff3f9 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 5 Jun 2023 20:22:07 +0300 Subject: [PATCH 093/160] add `walker2d` `info[z_distance_from_origon]` --- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 274055a0..ac732d29 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -292,6 +292,7 @@ def step(self, action): "reward_ctrl": -ctrl_cost, "reward_survive": healthy_reward, "x_position": x_position_after, + "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], "x_velocity": x_velocity, } From f96d8883a30399c1e996020e704b8ecc3a8fcc04 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 5 Jun 2023 21:37:08 +0300 Subject: [PATCH 094/160] add `reset_info` --- gymnasium_robotics/envs/mujoco/ant_v5.py | 9 +- .../envs/mujoco/half_cheetah_v5.py | 5 + gymnasium_robotics/envs/mujoco/hopper_v5.py | 6 + gymnasium_robotics/envs/mujoco/humanoid_v5.py | 9 ++ .../envs/mujoco/humanoidstandup_v5.py | 10 ++ .../envs/mujoco/point_mujoco.py | 110 ++++++++++++++++++ gymnasium_robotics/envs/mujoco/swimmer_v5.py | 7 ++ gymnasium_robotics/envs/mujoco/walker2d_v5.py | 6 + 8 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 gymnasium_robotics/envs/mujoco/point_mujoco.py diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index a35be93b..419fa95b 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -113,7 +113,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). - **Note:** Ant-v4 environment no longer has the following contact forces issue. + **Note:** Ant-v4+ environment no longer has the following contact forces issue. If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 results in the contact forces always being 0. As such we recommend to use a Mujoco-Py version < 2.0 when using the Ant environment if you would like to report results with contact forces (if @@ -410,3 +410,10 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], + "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), + } diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index da6a6b61..e4d4a279 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -256,3 +256,8 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + } diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 2328411d..67461f4e 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -326,3 +326,9 @@ def viewer_setup(self): getattr(self.viewer.cam, key)[:] = value else: setattr(self.viewer.cam, key, value) + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], + } diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index d413b0d2..7daa4fce 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -499,3 +499,12 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], + "tendon_lenght": self.data.ten_length, + "tendon_velocity": self.data.ten_velocity, + "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), + } diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index e465bd44..642eef88 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -419,6 +419,7 @@ def step(self, action): "reward_impact": -quad_impact_cost, "x_position": self.data.qpos[0], "y_position": self.data.qpos[1], + "z_distance_from_origin": self.data.qpos[2] - self.init_qpos[2], "tendon_lenght": self.data.ten_length, "tendon_velocity": self.data.ten_velocity, } @@ -441,3 +442,12 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], + "z_distance_from_origin": self.data.qpos[2] - self.init_qpos[2], + "tendon_lenght": self.data.ten_length, + "tendon_velocity": self.data.ten_velocity, + } diff --git a/gymnasium_robotics/envs/mujoco/point_mujoco.py b/gymnasium_robotics/envs/mujoco/point_mujoco.py new file mode 100644 index 00000000..9a389b81 --- /dev/null +++ b/gymnasium_robotics/envs/mujoco/point_mujoco.py @@ -0,0 +1,110 @@ +__credits__ = ["Kallinteris-Andreas"] + +import warnings + +import numpy as np +import pytest + +import gymnasium as gym +from gymnasium import utils +from gymnasium.envs.mujoco import MujocoEnv +from gymnasium.error import Error +from gymnasium.spaces import Box + + +class PointEnv(MujocoEnv, utils.EzPickle): + """ + A simple mujuco env to test third party mujoco env, using the `Gymansium.MujocoEnv` environment API. + """ + + metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + } + + def __init__(self, xml_file="point.xml", frame_skip=1, **kwargs): + utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) + + MujocoEnv.__init__( + self, + xml_file, + frame_skip=frame_skip, + observation_space=None, # needs to be defined after + default_camera_config={}, + **kwargs, + ) + + self.metadata = { + "render_modes": [ + "human", + "rgb_array", + "depth_array", + ], + "render_fps": int(np.round(1.0 / self.dt)), + } + + obs_size = self.data.qpos.size + self.data.qvel.size + + self.observation_space = Box( + low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 + ) + + def step(self, action): + x_position_before = self.data.qpos[0] + self.do_simulation(action, self.frame_skip) + x_position_after = self.data.qpos[0] + + observation = self._get_obs() + reward = x_position_after - x_position_before + info = {} + + if self.render_mode == "human": + self.render() + return observation, reward, False, False, info + + def _get_obs(self): + position = self.data.qpos.flat.copy() + velocity = self.data.qvel.flat.copy() + return np.concatenate((position, velocity)) + + def reset_model(self): + qpos = self.init_qpos + qvel = self.init_qvel + self.set_state(qpos, qvel) + + observation = self._get_obs() + + return observation + + +CHECK_ENV_IGNORE_WARNINGS = [ + f"\x1b[33mWARN: {message}\x1b[0m" + for message in [ + "A Box observation space minimum value is -infinity. This is probably too low.", + "A Box observation space maximum value is -infinity. This is probably too high.", + "For Box action spaces, we recommend using a symmetric and normalized space (range=[-1, 1] or [0, 1]). See https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html for more information.", + ] +] + + +@pytest.mark.parametrize("frame_skip", [1, 2, 3, 4, 5]) +def test_frame_skip(frame_skip): + """verify that custom envs work with different `frame_skip` values""" + env = PointEnv(frame_skip=frame_skip) + + # Test if env adheres to Gym API + with warnings.catch_warnings(record=True) as w: + gym.utils.env_checker.check_env(env.unwrapped, skip_render_check=True) + env.close() + for warning in w: + if warning.message.args[0] not in CHECK_ENV_IGNORE_WARNINGS: + raise Error(f"Unexpected warning: {warning.message}") + + +def test_xml_file(): + """Verify that the loading of a custom XML file works """ + env = PointEnv(xml_file="./assets/walker2d_v5_uneven_feet") + assert env.unwrapped.data.qpos.size == 9 diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index bf61a5bb..ea7ff355 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -239,3 +239,10 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "y_position": self.data.qpos[1], + "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), + } diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index ac732d29..a21afa0a 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -316,3 +316,9 @@ def reset_model(self): observation = self._get_obs() return observation + + def _get_reset_info(self): + return { + "x_position": self.data.qpos[0], + "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], + } From 9f910fb6a5a8487bc22f68d66f54c69bbede3384 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 5 Jun 2023 23:11:44 +0300 Subject: [PATCH 095/160] refactored observation structures to a member variable --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 2 +- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 2 +- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 419fa95b..835cd01f 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -287,7 +287,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 2 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index e4d4a279..0ac510e8 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -192,7 +192,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 1 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 67461f4e..7eda8485 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -218,7 +218,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 1 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 7daa4fce..bf886baa 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -361,7 +361,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 2 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 642eef88..bfca2ce5 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -343,7 +343,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 2 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index ea7ff355..01ce888e 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -170,7 +170,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 2 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index a21afa0a..4bb2a42e 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -219,7 +219,7 @@ def __init__( low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 ) - self.metadata["observation_structure"] = { + self.observation_structure = { "skipped_qpos": 1 * exclude_current_positions_from_observation, "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, From 85e9edad8e7e1d7123f0df6bb59d865e57d43f7b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 5 Jun 2023 23:47:25 +0300 Subject: [PATCH 096/160] cleanup observation_structure --- gymnasium_robotics/envs/mujoco/ant_v5.py | 3 --- gymnasium_robotics/envs/mujoco/half_cheetah_v5.py | 4 ---- gymnasium_robotics/envs/mujoco/hopper_v5.py | 4 ---- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py | 2 ++ gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py | 5 +++++ gymnasium_robotics/envs/mujoco/swimmer_v5.py | 4 ---- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 4 ---- 8 files changed, 9 insertions(+), 19 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 835cd01f..4c52af67 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -292,9 +292,6 @@ def __init__( "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, - "cinert": 0, - "cvel": 0, - "qfrc_actuator": 0, "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, } diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 0ac510e8..856d3746 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -197,10 +197,6 @@ def __init__( "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, - "cinert": 0, - "cvel": 0, - "qfrc_actuator": 0, - "cfrc_ext": 0, } def control_cost(self, action): diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 7eda8485..8f614843 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -223,10 +223,6 @@ def __init__( "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, - "cinert": 0, - "cvel": 0, - "qfrc_actuator": 0, - "cfrc_ext": 0, } @property diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index bf886baa..8bfc69ca 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -371,6 +371,8 @@ def __init__( "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, + "ten_lenght": 0, + "ten_velocity": 0, } @property diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index bfca2ce5..3e1a11ed 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -353,6 +353,8 @@ def __init__( "qfrc_actuator": (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation, "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, + "ten_lenght": 0, + "ten_velocity": 0, } def _get_obs(self): diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index 1cadc6ef..bca5b298 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -128,6 +128,11 @@ def __init__( "render_fps": int(np.round(1.0 / self.dt)), } + self.observation_structure = { + "qpos": self.data.qpos.size, + "qvel": self.data.qvel.size, + } + def step(self, action): self.do_simulation(action, self.frame_skip) diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 01ce888e..9995d77b 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -175,10 +175,6 @@ def __init__( "qpos": self.data.qpos.size - 2 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, - "cinert": 0, - "cvel": 0, - "qfrc_actuator": 0, - "cfrc_ext": 0, } def control_cost(self, action): diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 4bb2a42e..17b1f947 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -224,10 +224,6 @@ def __init__( "qpos": self.data.qpos.size - 1 * exclude_current_positions_from_observation, "qvel": self.data.qvel.size, - "cinert": 0, - "cvel": 0, - "qfrc_actuator": 0, - "cfrc_ext": 0, } @property From aa1b9e22ec03c9d735598615300bb28f58738cfd Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Sat, 10 Jun 2023 14:11:23 +0300 Subject: [PATCH 097/160] Final? documention update --- gymnasium_robotics/envs/mujoco/ant_v5.py | 55 +++++---- .../envs/mujoco/half_cheetah_v5.py | 41 ++++--- gymnasium_robotics/envs/mujoco/hopper_v5.py | 51 ++++---- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 56 +++++---- .../envs/mujoco/humanoidstandup_v5.py | 50 +++++--- .../mujoco/inverted_double_pendulum_v5.py | 56 +++++---- .../envs/mujoco/inverted_pendulum_v5.py | 28 +++-- .../envs/mujoco/point_mujoco.py | 110 ------------------ gymnasium_robotics/envs/mujoco/pusher_v5.py | 56 ++++----- gymnasium_robotics/envs/mujoco/reacher_v5.py | 60 +++++----- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 50 +++++--- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 49 ++++---- 12 files changed, 327 insertions(+), 335 deletions(-) delete mode 100644 gymnasium_robotics/envs/mujoco/point_mujoco.py diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 4c52af67..2bbc305b 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -1,6 +1,6 @@ __credits__ = ["Kallinteris-Andreas"] -from typing import Dict, Tuple +from typing import Dict, Tuple, Union import numpy as np from gymnasium import utils @@ -24,6 +24,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): torques on the eight hinges connecting the two body parts of each leg and the torso (nine body parts and eight hinges). + ## Action Space The action space is a `Box(-1, 1, (8,), float32)`. An action represents the torques applied at the hinge joints. @@ -38,6 +39,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | + ## Observation Space Observations consist of positional values of different body parts of the ant, followed by the velocities of those individual parts (their derivatives) with all @@ -119,21 +121,30 @@ class AntEnv(MujocoEnv, utils.EzPickle): when using the Ant environment if you would like to report results with contact forces (if contact forces are not used in your experiments, you can use version > 2.0). + ## Rewards The reward consists of three parts: - - *healthy_reward*: Every timestep that the ant is healthy (see definition in section "Episode Termination"), it gets a reward of fixed value `healthy_reward` - - *forward_reward*: A reward of moving forward which is measured as - $\frac{dx}{dt} \times$ `forward_reward_weight`. - $dx$ is the displacement of the `main_body` (x-coordinate before action - x-coordinate after action)*. - $dt$ is the time between actions and is dependent on the `frame_skip` parameter (default is 5), - where the frametime is 0.01 - making the default *dt = 5 * 0.01 = 0.05*. - This reward would be positive if the ant moves forward (in positive x direction). - - *ctrl_cost*: A negative reward for penalising the ant if it takes actions - that are too large. It is measured as *`ctrl_cost_weight` * sum(action2)* - where *`ctr_cost_weight`* is a parameter set for the control and has a default value of 0.5. - - *contact_cost*: A negative reward for penalising the ant if the external contact - force is too large. It is calculated *`contact_cost_weight` * sum(clip(external contact - force to `contact_force_range`)2)*. + - *healthy_reward*: + Every timestep that the ant is healthy (see definition in section "Episode Termination"), + it gets a reward of fixed value `healthy_reward`. + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Ant moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the `main_body` ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), + and `frametime` which is 0.01 - making the default $dt = 5 \times 0.01 = 0.05$, + $w_{forward}$ is the `forward_reward_weight` (default is $1$). + - *ctrl_cost*: + A negative reward for penalizing the Ant if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $0.5$). + - *contact_cost*: + A negative reward for penalizing the Ant if the external contact forces are too large. + $w_{contact} \times \\|F_{contact}\\|_2^2$, where + $w_{contact}$ is `contact_cost_weight` (default is $5\times10^{-4}$), + $F_{contact}$ are the external contact forces clipped by `contact_force_range` (see `cfrc_ext` section on observation). + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost*. @@ -142,6 +153,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): In either case `info` will also contain the individual reward terms. + ## Starting State All observations start in state (0.0, 0.0, 0.75, 1.0, 0.0 ... 0.0) with a uniform noise in the range @@ -151,19 +163,18 @@ class AntEnv(MujocoEnv, utils.EzPickle): to be slightly high, thereby indicating a standing up ant. The initial orientation is designed to make it face forward as well. + ## Episode End - The ant is said to be unhealthy if any of the following happens: + #### Termination + If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Ant is unhealthy. + the Ant is unhealthy if any of the following happens: 1. Any of the state space values is no longer finite 2. The z-coordinate of the torso is **not** in the closed interval given by `healthy_z_range` (defaults to [0.2, 1.0]) - If `terminate_when_unhealthy=True` is passed during construction (which is the default), - the episode ends when any of the following happens: - - 1. Truncation: The episode duration reaches a 1000 timesteps - 2. Termination: The ant is unhealthy + #### truncation + The maximum duration of an episode is 1000 timesteps. - If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. @@ -214,7 +225,7 @@ def __init__( ctrl_cost_weight: float = 0.5, contact_cost_weight: float = 5e-4, healthy_reward: float = 1.0, - main_body: str = "torso", + main_body: Union[int, str] = 1, terminate_when_unhealthy: bool = True, healthy_z_range: Tuple[float, float] = (0.2, 1.0), contact_force_range: Tuple[float, float] = (-1.0, 1.0), diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 856d3746..0c06e167 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -13,7 +13,6 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is based on the work by P. Wawrzyński in ["A Cat-Like Robot Real-Time Learning to Run"](http://staff.elka.pw.edu.pl/~pwawrzyn/pub-s/0812_LSCLRR.pdf). The HalfCheetah is a 2-dimensional robot consisting of 9 body parts and 8 @@ -25,6 +24,7 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): over the front and back thighs (connecting to the torso), shins (connecting to the thighs) and feet (connecting to the shins). + ## Action Space The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. @@ -72,20 +72,25 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): | 16 | angular velocity of second rotor | -Inf | Inf | ffoot | hinge | angular velocity (rad/s) | | excluded | x-coordinate of the front tip | -Inf | Inf | rootx | slide | position (m) | + ## Rewards The reward consists of two parts: - - *forward_reward*: A reward of moving forward which is measured - as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is - the time between actions and is dependent on the frame_skip parameter - (fixed to 5), where the frametime is 0.01 - making the - default *dt = 5 * 0.01 = 0.05*. This reward would be positive if the cheetah - runs forward (right). - - *ctrl_cost*: A cost for penalising the cheetah if it takes - actions that are too large. It is measured as *`ctrl_cost_weight` * - sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the - control and has a default value of 0.1 - - The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Half Cheetah moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the "tip" ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), + and `frametime` which is 0.01 - making the default $dt = 5 \times 0.01 = 0.05$, + $w_{forward}$ is the `forward_reward_weight` (default is $1$). + - *ctrl_cost*: + A negative reward for penalizing the Half Cheetah if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $0.1$). + + The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost*, + and `info` will also contain the individual reward terms. + ## Starting State All observations start in state (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, @@ -96,8 +101,14 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): normal noise with a mean of 0 and standard deviation of `reset_noise_scale` is added to the initial velocity values of all zeros. + ## Episode End - The episode truncates when the episode length is greater than 1000. + #### Termination + The Half Cheetah never terminates. + + #### Truncation + The maximum duration of an episode is 1000 timesteps. + ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. @@ -110,7 +121,7 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xml_file` | **str** | `"half_cheetah.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ weight (see section on reward) | | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 8f614843..76db9f06 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -16,7 +16,6 @@ class HopperEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is based on the work done by Erez, Tassa, and Todorov in ["Infinite Horizon Model Predictive Control for Nonlinear Periodic Tasks"](http://www.roboticsproceedings.org/rss07/p10.pdf). The environment aims to increase the number of independent state and control variables as compared to @@ -27,6 +26,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): forward (right) direction by applying torques on the three hinges connecting the four body parts. + ## Action Space The action space is a `Box(-1, 1, (3,), float32)`. An action represents the torques applied at the hinge joints. @@ -36,6 +36,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | + ## Observation Space Observations consist of positional values of different body parts of the hopper, followed by the velocities of those individual parts @@ -69,39 +70,43 @@ class HopperEnv(MujocoEnv, utils.EzPickle): ## Rewards The reward consists of three parts: - - *healthy_reward*: Every timestep that the hopper is healthy (see definition in section "Episode Termination"), it gets a reward of fixed value `healthy_reward`. - - *forward_reward*: A reward of hopping forward which is measured - as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is - the time between actions and is dependent on the frame_skip parameter - (fixed to 4), where the frametime is 0.002 - making the - default *dt = 4 * 0.002 = 0.008*. This reward would be positive if the hopper - hops forward (positive x direction). - - *ctrl_cost*: A cost for penalising the hopper if it takes - actions that are too large. It is measured as *`ctrl_cost_weight` * - sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the - control and has a default value of 0.001 - - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. + - *healthy_reward*: + Every timestep that the Hopper is healthy (see definition in section "Episode Termination"), + it gets a reward of fixed value `healthy_reward`. + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Hopper moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the "torso" ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), + and `frametime` which is 0.002 - making the default $dt = 4 \times 0.002 = 0.008$, + $w_{forward}$ is the `forward_reward_weight` (default is $1$). + - *ctrl_cost*: + A negative reward for penalizing the Hopper if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-3}$). + + The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*, + and `info` will also contain the individual reward terms. ## Starting State All observations start in state (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. + ## Episode End - The hopper is said to be unhealthy if any of the following happens: + #### Termination + If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Hopper is unhealthy. + The Hopper is unhealthy if any of the following happens: 1. An element of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) is no longer contained in the closed interval specified by the argument `healthy_state_range` 2. The height of the hopper (`observation[0]` if `exclude_current_positions_from_observation=True`, else `observation[1]`) is no longer contained in the closed interval specified by the argument `healthy_z_range` (usually meaning that it has fallen) - 3. The angle (`observation[1]` if `exclude_current_positions_from_observation=True`, else `observation[2]`) is no longer contained in the closed interval specified by the argument `healthy_angle_range` - - If `terminate_when_unhealthy=True` is passed during construction (which is the default), - the episode ends when any of the following happens: + 3. The angle of the torso (`observation[1]` if `exclude_current_positions_from_observation=True`, else `observation[2]`) is no longer contained in the closed interval specified by the argument `healthy_angle_range` - 1. Truncation: The episode duration reaches a 1000 timesteps - 2. Termination: The hopper is unhealthy + #### Truncation + The maximum duration of an episode is 1000 timesteps. - If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. @@ -114,7 +119,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xml_file` | **str** | `"hopper_v5.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctrl_cost_ reward (see section on reward) | | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the hopper is no longer healthy | diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 8bfc69ca..1981148a 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -22,13 +22,13 @@ def mass_center(model, data): class HumanoidEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment is based on the environment introduced by Tassa, Erez and Todorov in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a pair of legs and arms. The legs each consist of three body parts, and the arms 2 body parts (representing the knees and elbows respectively). The goal of the environment is to walk forward as fast as possible without falling over. + ## Action Space The action space is a `Box(-1, 1, (17,), float32)`. An action represents the torques applied at the hinge joints. @@ -52,6 +52,7 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | + ## Observation Space Observations consist of positional values of different body parts of the Humanoid, followed by the velocities of those individual parts (their derivatives) with all the @@ -188,27 +189,38 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): with contact forces (if contact forces are not used in your experiments, you can use version > 2.0). + ## Rewards The reward consists of three parts: - - *healthy_reward*: Every timestep that the humanoid is alive (see section Episode Termination for definition), it gets a reward of fixed value `healthy_reward` - - *forward_reward*: A reward of walking forward which is measured as *`forward_reward_weight` * - (average center of mass before action - average center of mass after action)/dt*. - *dt* is the time between actions and is dependent on the frame_skip parameter - (default is 5), where the frametime is 0.003 - making the default *dt = 5 * 0.003 = 0.015*. - This reward would be positive if the humanoid walks forward (in positive x-direction). The calculation - for the center of mass is defined in the `.py` file for the Humanoid. - - *ctrl_cost*: A negative reward for penalising the humanoid if it has too - large of a control force. If there are *nu* actuators/controls, then the control has - shape `nu x 1`. It is measured as *`ctrl_cost_weight` * sum(control2)*. - - *contact_cost*: A negative reward for penalising the humanoid if the external - contact force is too large. It is calculated by clipping - *`contact_cost_weight` * sum(external contact force2)* to the interval specified by `contact_cost_range`. + - *healthy_reward*: + Every timestep that the Humanoid is alive (see section Episode Termination for definition), + it gets a reward of fixed value `healthy_reward`. + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Humanoid moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the center of mass ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), + and `frametime` which is 0.001 - making the default $dt = 5 \times 0.003 = 0.015$, + $w_{forward}$ is the `forward_reward_weight` (default is $1.25$). + The calculation for the center of mass is defined in the `.py` file for the Humanoid. + - *ctrl_cost*: + A negative reward for penalizing the Humanoid if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $0.1$). + If there are *nu* actuators/controls, then the control has shape `nu x 1`. + - *contact_cost*: + A negative reward for penalizing the Humanoid if the external contact forces are too large. + $w_{contact} \times clamp(contact\\_cost\\_range, \\|F_{contact}\\|_2^2)$, where + $w_{contact}$ is `contact_cost_weight` (default is $5\times10^{-7}$), + $F_{contact}$ are the external contact forces (see `cfrc_ext` section on observation). The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost* - and `info` will also contain the individual reward terms + and `info` will also contain the individual reward terms. Note: in `v4` the total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost* + ## Starting State All observations start in state (0.0, 0.0, 1.4, 1.0, 0.0 ... 0.0) with a uniform noise in the range @@ -217,17 +229,17 @@ class HumanoidEnv(MujocoEnv, utils.EzPickle): selected to be high, thereby indicating a standing up humanoid. The initial orientation is designed to make it face forward as well. + ## Episode End - The humanoid is said to be unhealthy if the z-position of the torso is no longer contained in the - closed interval specified by the argument `healthy_z_range`. + #### Termination + If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Humanoid is unhealthy. + The Humanoid is said to be unhealthy if any of the following happens: - If `terminate_when_unhealthy=True` is passed during construction (which is the default), - the episode ends when any of the following happens: + 1. The z-position of the torso (the height) is no longer contained in `healthy_z_range`. - 1. Truncation: The episode duration reaches a 1000 timesteps - 3. Termination: The humanoid is unhealthy + #### Truncation + The maximum duration of an episode is 1000 timesteps. - If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 3e1a11ed..4c851860 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -24,6 +24,7 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): and then the goal of the environment is to make the humanoid standup and then keep it standing by applying torques on the various hinges. + ## Action Space The agent take a 17-element vector for actions. @@ -50,6 +51,7 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | + ## Observation Space Observations consist of positional values of different body parts of the Humanoid, followed by the velocities of those individual parts (their derivatives) with all the @@ -186,23 +188,32 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): with contact forces (if contact forces are not used in your experiments, you can use version > 2.0). + ## Rewards The reward consists of three parts: - - *uph_cost*: A reward for moving upward (in an attempt to stand up). This is not a relative - reward which measures how much upward it has moved from the last timestep, but it is an - absolute reward which measures how much upward the Humanoid has moved overall. It is - measured as *(z coordinate after action - 0)/(atomic timestep)*, where *z coordinate after - action* is index 0 in the state/index 2 in the table, and *atomic timestep* is the time for - one frame of movement even though the simulation has a framerate of 5 (done in order to inflate - rewards a little for faster learning). - - *quad_ctrl_cost*: A negative reward for penalising the humanoid if it has too large of - a control force. If there are *nu* actuators/controls, then the control has shape `nu x 1`. - It is measured as *0.1 **x** sum(control2)*. - - *quad_impact_cost*: A negative reward for penalising the humanoid if the external - contact force is too large. It is calculated as *min(0.5 * 0.000001 * sum(external - contact force2), 10)*. - - The total reward returned is ***reward*** *=* *uph_cost + 1 - quad_ctrl_cost - quad_impact_cost* + - *uph_cost*: + A reward for moving upward (in an attempt to stand up). + This is not a relative reward which measures how much upward it has moved from the last timestep, + but it is an absolute reward which measures how much upward the Humanoid has moved overall. + It is measured as $weight_{uph} \times (z_{after action} - 0)/dt$, + where $z_{after action}$ is the z coordinate of the torso after taking an action, + and *dt* is the time between actions and is dependent on the `frame_skip` parameter + (default is 5), where the frametime is 0.003 - making the default *dt = 5 * 0.003 = 0.015*. + and $weight_{uph}$ is `uph_cost_weight`. + - *quad_ctrl_cost*: + A negative reward for penalizing the Humanoid if it takes actions that are too large. + $w_{quad_control} \times \\|action\\|_2^2$, + where $w_{quad_control}$ is `ctrl_cost_weight` (default is $0.1$). + If there are *nu* actuators/controls, then the control has shape `nu x 1`. + - *impact_cost*: + A negative reward for penalizing the Humanoid if the external contact forces are too large. + $w_{impact} \times clamp(impact\\_cost\\_range, \\|F_{contact}\\|_2^2)$, where + $w_{impact}$ is `impact_cost_weight` (default is $5\times10^{-7}$), + $F_{contact}$ are the external contact forces (see `cfrc_ext` section on observation). + + The total reward returned is ***reward*** *=* *uph_cost + 1 - quad_ctrl_cost - quad_impact_cost*, + and `info` will also contain the individual reward terms. + ## Starting State All observations start in state @@ -212,11 +223,14 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): to be low, thereby indicating a laying down humanoid. The initial orientation is designed to make it face forward as well. + ## Episode End - The episode ends when any of the following happens: + #### Termination + The Humanoid never terminates. + + #### Truncation + The maximum duration of an episode is 1000 timesteps. - 1. Truncation: The episode duration reaches a 1000 timesteps - 2. Termination: Any of the state space values is no longer finite ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 04d00f3d..0a17750f 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -13,7 +13,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment originates from control theory and builds on the cartpole environment based on the work done by Barto, Sutton, and Anderson in @@ -25,6 +25,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): and the goal is to balance the second pole on top of the first pole, which is in turn on top of the cart, by applying continuous forces on the cart. + ## Action Space The agent take a 1-element vector for actions. The action space is a continuous `(action)` in `[-1, 1]`, where `action` represents the @@ -35,6 +36,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| | 0 | Force applied on the cart | -1 | 1 | slider | slide | Force (N) | + ## Observation Space The state space consists of positional values of different body parts of the pendulum system, followed by the velocities of those individual parts (their derivatives) with all the @@ -52,9 +54,9 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): | 5 | velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | | 6 | angular velocity of the angle between the cart and the first pole | -Inf | Inf | hinge | hinge | angular velocity (rad/s) | | 7 | angular velocity of the angle between the two poles | -Inf | Inf | hinge2 | hinge | angular velocity (rad/s) | - | 8 | constraint force | -Inf | Inf | slider | slide | Force (N) | - | excluded | constraint force | -Inf | Inf | hinge | slide | Force (N) | - | excluded | constraint force | -Inf | Inf | hinge2 | slide | Force (N) | + | 8 | constraint force - x | -Inf | Inf | slider | slide | Force (N) | + | excluded | constraint force - y | -Inf | Inf | hinge | slide | Force (N) | + | excluded | constraint force - z | -Inf | Inf | hinge2 | slide | Force (N) | There is physical contact between the robots and their environment - and Mujoco @@ -62,40 +64,48 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): dynamics by aiming for physical accuracy and computational efficiency. There is one constraint force for contacts for each degree of freedom (3). - The approach and handling of constraints by Mujoco is unique to the simulator - and is based on their research. Once can find more information in their - [*documentation*](https://mujoco.readthedocs.io/en/latest/computation.html) - or in their paper - ["Analytically-invertible dynamics with contacts and constraints: Theory and implementation in MuJoCo"](https://homes.cs.washington.edu/~todorov/papers/TodorovICRA14.pdf). + The approach and handling of constraints by Mujoco is unique to the simulator and is based on their research. + Once can find more information in their [*documentation*](https://mujoco.readthedocs.io/en/latest/computation.html) + or in their paper ["Analytically-invertible dynamics with contacts and constraints: Theory and implementation in MuJoCo"](https://homes.cs.washington.edu/~todorov/papers/TodorovICRA14.pdf). ## Rewards The reward consists of two parts: - - *alive_bonus*: The goal is to make the second inverted pendulum stand upright - (within a certain angle limit) as long as possible - as such a reward of `` is awarded + - *alive_bonus*: + The goal is to make the second inverted pendulum stand upright + (within a certain angle limit) as long as possible - as such a reward of `healthy_reward` is awarded for each timestep that the second pole is upright. - - *distance_penalty*: This reward is a measure of how far the *tip* of the second pendulum - (the only free end) moves, and it is calculated as - *0.01 * x2 + (y - 2)2*, where *x* is the x-coordinate of the tip - and *y* is the y-coordinate of the tip of the second pole. - - *velocity_penalty*: A negative reward for penalising the agent if it moves too - fast *0.001 * v12 + 0.005 * v2 2* + - *distance_penalty*: + This reward is a measure of how far the *tip* of the second pendulum (the only free end) moves, + and it is calculated as $0.01 x_{pole2-tip}^2 + (y_{pole2-tip}-2)^2$, + where $x_{pole2-tip}, y_{pole2-tip}$ are the xy-coordinatesof the tip of the second pole. + - *velocity_penalty*: + A negative reward for penalising the agent if it moves too fast. + $10^{-3} \omega_1 + 5 10^{-3} \omega_2$, + where $\omega_1, \omega_2$ are the angular velocities of the hinges. The total reward returned is ***reward*** *=* *alive_bonus - distance_penalty - velocity_penalty* and `info` will also contain the individual reward terms. + ## Starting State All observations start in state (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise in the range - of [-0.1, 0.1] added to the positional values (cart position and pole angles) and standard - normal force with a standard deviation of 0.1 added to the velocity values for stochasticity. + of `[-reset_noise_scale, reset_noise_scale]` added to the positional values (cart position and pole angles) and standard + normal force with a standard deviation of `reset_noise_scale` added to the velocity values for stochasticity. + ## Episode End - The episode ends when any of the following happens: + #### Termination + The environment terminates when the Inverted Double Pendulum is unhealthy. + The Inverted Double Pendulum is unhealthy if any of the following happens: + + 1.Termination: The y_coordinate of the tip of the second pole $\leq 1$. + The maximum standing height of the system is 1.2 m when all the parts are perpendicularly vertical on top of each other). + + #### Truncation + The maximum duration of an episode is 1000 timesteps. - 1.Truncation: The episode duration reaches 1000 timesteps. - 2.Termination: Any of the state space values is no longer finite. - 3.Termination: The y_coordinate of the tip of the second pole *is less than or equal* to 1. The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other). ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `healthy_reward`, `reset_noise_scale`, etc. diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index bca5b298..f910e1ca 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -23,6 +23,7 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): at one end and having another end free. The cart can be pushed left or right, and the goal is to balance the pole on the top of the cart by applying forces on the cart. + ## Action Space The agent take a 1-element vector for actions. @@ -34,6 +35,7 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| | 0 | Force applied on the cart | -3 | 3 | slider | slide | Force (N) | + ## Observation Space The state space consists of positional values of different body parts of the pendulum system, followed by the velocities of those individual parts (their derivatives) @@ -54,19 +56,29 @@ class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): as long as possible - as such a reward of +1 is awarded for each timestep that the pole is upright. + The pole is considered upright if: + $|angle| < 0.2$ + and `info` will also contain the reward. + ## Starting State All observations start in state (0.0, 0.0, 0.0, 0.0) with a uniform noise in the range - of [-0.01, 0.01] added to the values for stochasticity. + of `[-reset_noise_scale, reset_noise_scale]` added to the values for stochasticity. + ## Episode End - The episode ends when any of the following happens: + #### Termination + The environment terminates when the Inverted Pendulum is unhealthy. + The Inverted Pendulum is unhealthy if any of the following happens: + + 1. Any of the state space values is no longer finite. + 2. The absolute value of the vertical angle between the pole and the cart is greater than 0.2 radian. + + #### Truncation + The maximum duration of an episode is 1000 timesteps. - 1. Truncation: The episode duration reaches 1000 timesteps. - 2. Termination: Any of the state space values is no longer finite. - 3. Termination: The absolute value of the vertical angle between the pole and the cart is greater than 0.2 radian. ## Arguments `gymnasium.make` takes additional arguments such as `reset_noise_scale`. @@ -136,9 +148,9 @@ def __init__( def step(self, action): self.do_simulation(action, self.frame_skip) - ob = self._get_obs() + observation = self._get_obs() - terminated = bool(not np.isfinite(ob).all() or (np.abs(ob[1]) > 0.2)) + terminated = bool(not np.isfinite(observation).all() or (np.abs(observation[1]) > 0.2)) reward = int(not terminated) @@ -146,7 +158,7 @@ def step(self, action): if self.render_mode == "human": self.render() - return ob, reward, terminated, False, info + return observation, reward, terminated, False, info def reset_model(self): noise_low = -self._reset_noise_scale diff --git a/gymnasium_robotics/envs/mujoco/point_mujoco.py b/gymnasium_robotics/envs/mujoco/point_mujoco.py deleted file mode 100644 index 9a389b81..00000000 --- a/gymnasium_robotics/envs/mujoco/point_mujoco.py +++ /dev/null @@ -1,110 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import warnings - -import numpy as np -import pytest - -import gymnasium as gym -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.error import Error -from gymnasium.spaces import Box - - -class PointEnv(MujocoEnv, utils.EzPickle): - """ - A simple mujuco env to test third party mujoco env, using the `Gymansium.MujocoEnv` environment API. - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__(self, xml_file="point.xml", frame_skip=1, **kwargs): - utils.EzPickle.__init__(self, xml_file, frame_skip, **kwargs) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip=frame_skip, - observation_space=None, # needs to be defined after - default_camera_config={}, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = self.data.qpos.size + self.data.qvel.size - - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - def step(self, action): - x_position_before = self.data.qpos[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.data.qpos[0] - - observation = self._get_obs() - reward = x_position_after - x_position_before - info = {} - - if self.render_mode == "human": - self.render() - return observation, reward, False, False, info - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - return np.concatenate((position, velocity)) - - def reset_model(self): - qpos = self.init_qpos - qvel = self.init_qvel - self.set_state(qpos, qvel) - - observation = self._get_obs() - - return observation - - -CHECK_ENV_IGNORE_WARNINGS = [ - f"\x1b[33mWARN: {message}\x1b[0m" - for message in [ - "A Box observation space minimum value is -infinity. This is probably too low.", - "A Box observation space maximum value is -infinity. This is probably too high.", - "For Box action spaces, we recommend using a symmetric and normalized space (range=[-1, 1] or [0, 1]). See https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html for more information.", - ] -] - - -@pytest.mark.parametrize("frame_skip", [1, 2, 3, 4, 5]) -def test_frame_skip(frame_skip): - """verify that custom envs work with different `frame_skip` values""" - env = PointEnv(frame_skip=frame_skip) - - # Test if env adheres to Gym API - with warnings.catch_warnings(record=True) as w: - gym.utils.env_checker.check_env(env.unwrapped, skip_render_check=True) - env.close() - for warning in w: - if warning.message.args[0] not in CHECK_ENV_IGNORE_WARNINGS: - raise Error(f"Unexpected warning: {warning.message}") - - -def test_xml_file(): - """Verify that the loading of a custom XML file works """ - env = PointEnv(xml_file="./assets/walker2d_v5_uneven_feet") - assert env.unwrapped.data.qpos.size == 9 diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py index a79f585a..be9ed636 100644 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ b/gymnasium_robotics/envs/mujoco/pusher_v5.py @@ -12,12 +12,13 @@ class PusherEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description "Pusher" is a multi-jointed robot arm which is very similar to that of a human. The goal is to move a target cylinder (called *object*) to a goal position using the robot's end effector (called *fingertip*). The robot consists of shoulder, elbow, forearm, and wrist joints. + ## Action Space The action space is a `Box(-2, 2, (7,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. @@ -31,6 +32,7 @@ class PusherEnv(MujocoEnv, utils.EzPickle): | 5 | Rotation of flexing the wrist | -2 | 2 | r_wrist_flex_joint | hinge | torque (N m) | | 6 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | + ## Observation Space Observations consist of @@ -73,24 +75,23 @@ class PusherEnv(MujocoEnv, utils.EzPickle): ## Rewards The reward consists of two parts: - - *reward_near *: This reward is a measure of how far the *fingertip* - of the pusher (the unattached end) is from the object, with a more negative - value assigned for when the pusher's *fingertip* is further away from the - target. It is calculated as the negative vector norm of (position of - the fingertip - position of target), or *-norm("fingertip" - "target")*. - - *reward_dist *: This reward is a measure of how far the object is from - the target goal position, with a more negative value assigned for object is - further away from the target. It is calculated as the negative vector norm of - (position of the object - position of goal), or *-norm("object" - "target")*. - - *reward_control*: A negative reward for penalising the pusher if - it takes actions that are too large. It is measured as the negative squared - Euclidean norm of the action, i.e. as *- sum(action2)*. - - The total reward returned is ***reward*** *=* *reward_dist + 0.1 * reward_ctrl + 0.5 * reward_near* - - Unlike other environments, Pusher does not allow you to specify weights for the individual reward terms. - However, `info` does contain the keys *reward_dist* and *reward_ctrl*. Thus, if you'd like to weight the terms, - you should create a wrapper that computes the weighted reward from `info`. + - *reward_near*: + This reward is a measure of how far the *fingertip* of the pusher (the unattached end) is from the object, + with a more negative value assigned for when the pusher's *fingertip* is further away from the target. + It is $-w_{near} \|(P_{fingertip} - P_{target})\|_2$. + where $w_{near}$ is `reward_near_weight`. + - *reward_dist*: + This reward is a measure of how far the object is from the target goal position, + with a more negative value assigned for object that is further away from the target. + It is $-w_{dist} \|(P_{object} - P_{target})\|_2$. + where $w_{dist}$ is `reward_dist_weight`. + - *reward_control*: + A negative reward for penalising the pusher if it takes actions that are too large. + It is measured as the negative squared Euclidean norm of the action, i.e. as $-w_{control} \|action\|_2^2$. + where $w_{control}$ is `reward_control_weight`. + + The total reward returned is ***reward*** *=* *reward_dist + reward_ctrl + reward_near*, + `info` will also contain the individual reward terms. ## Starting State @@ -104,11 +105,14 @@ class PusherEnv(MujocoEnv, utils.EzPickle): The default framerate is 5 with each frame lasting for 0.01, giving rise to a *dt = 5 * 0.01 = 0.05* + ## Episode End - The episode ends when any of the following happens: + #### Termination + The Pusher never terminates. + + #### Truncation + The maximum duration of an episode is 100 timesteps. - 1. Truncation: The episode duration reaches a 100 timesteps. - 2. Termination: Any of the state space values is no longer finite. ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`. @@ -196,16 +200,16 @@ def step(self, action): reward = reward_dist + reward_ctrl + reward_near self.do_simulation(action, self.frame_skip) - if self.render_mode == "human": - self.render() - ob = self._get_obs() + observation = self._get_obs() info = { "reward_dist": reward_dist, "reward_ctrl": reward_ctrl, "reward_near": reward_near, } - return (ob, reward, False, False, info) + if self.render_mode == "human": + self.render() + return observation, reward, False, False, info def reset_model(self): qpos = self.init_qpos diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py index 869b5ec8..92faea81 100644 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ b/gymnasium_robotics/envs/mujoco/reacher_v5.py @@ -9,11 +9,12 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description "Reacher" is a two-jointed robot arm. The goal is to move the robot's end effector (called *fingertip*) close to a target that is spawned at a random position. + ## Action Space The action space is a `Box(-1, 1, (2,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. @@ -22,6 +23,7 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): | 0 | Torque applied at the first hinge (connecting the link to the point of fixture) | -1 | 1 | joint0 | hinge | torque (N m) | | 1 | Torque applied at the second hinge (connecting the two links) | -1 | 1 | joint1 | hinge | torque (N m) | + ## Observation Space Observations consist of @@ -45,14 +47,13 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): | 7 | angular velocity of the second arm | -Inf | Inf | joint1 | hinge | angular velocity (rad/s) | | 8 | x-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | | 9 | y-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | - | excluded | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d and z is same for both) | -Inf | Inf | NA | slide | position (m) | - + | excluded | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d) | -Inf | Inf | NA | slide | position (m) | Most Gym environments just return the positions and velocity of the joints in the `.xml` file as the state of the environment. However, in reacher the state is created by combining only certain elements of the position and velocity, and performing some function transformations on them. - If one is to read the `.xml` for reacher then they will find 4 joints: + If one is to read the `reacher.xml` then they will find 4 joints: | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | |-----|-----------------------------|----------|----------|----------------------------------|-------|--------------------| @@ -64,21 +65,18 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): ## Rewards The reward consists of two parts: - - *reward_distance*: This reward is a measure of how far the *fingertip* - of the reacher (the unattached end) is from the target, with a more negative - value assigned for when the reacher's *fingertip* is further away from the - target. It is calculated as the negative vector norm of (position of - the fingertip - position of target), or *-norm("fingertip" - "target")*. - - *reward_control*: A negative reward for penalising the walker if - it takes actions that are too large. It is measured as the negative squared - Euclidean norm of the action, i.e. as *- sum(action2)*. - - The total reward returned is ***reward*** *=* *reward_distance + reward_control* - - Unlike other environments, Reacher does not allow you to specify weights for the individual reward terms. - However, `info` does contain the keys *reward_dist* and *reward_ctrl*. Thus, if you'd like to weight the terms, - you should create a wrapper that computes the weighted reward from `info`. - + - *reward_distance*: + This reward is a measure of how far the *fingertip* of the reacher (the unattached end) is from the target, + with a more negative value assigned for when the reacher's *fingertip* is further away from the target. + It is $-w_{near} \|(P_{fingertip} - P_{target})\|_2$. + where $w_{near}$ is `reward_near_weight`. + - *reward_control*: + A negative reward for penalising the walker if it takes actions that are too large. + It is measured as the negative squared Euclidean norm of the action, i.e. as $-w_{control} \|action\|_2^2$. + where $w_{control}$ is `reward_control_weight`. + + The total reward returned is ***reward*** *=* *reward_distance + reward_control*. + `info` will also contain the individual reward terms. ## Starting State All observations start in state @@ -92,10 +90,12 @@ class ReacherEnv(MujocoEnv, utils.EzPickle): is set. The default setting has a framerate of 2 and a *dt = 2 * 0.01 = 0.02* ## Episode End - The episode ends when any of the following happens: + #### Termination + The Reacher never terminates. + + #### Truncation + The maximum duration of an episode is 50 timesteps. - 1. Truncation: The episode duration reaches a 50 timesteps (with a new random target popping up if the reacher's fingertip reaches it before 50 timesteps) - 2. Termination: Any of the state space values is no longer finite. ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`. @@ -177,17 +177,15 @@ def step(self, action): reward = reward_dist + reward_ctrl self.do_simulation(action, self.frame_skip) + + observation = self._get_obs() + info = { + "reward_dist": reward_dist, + "reward_ctrl": reward_ctrl, + } if self.render_mode == "human": self.render() - - ob = self._get_obs() - return ( - ob, - reward, - False, - False, - dict(reward_dist=reward_dist, reward_ctrl=reward_ctrl), - ) + return observation, reward, False, False, info def reset_model(self): qpos = ( diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 9995d77b..09e23b1a 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -9,7 +9,6 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment corresponds to the Swimmer environment described in Rémi Coulom's PhD thesis ["Reinforcement Learning Using Neural Networks, with Applications to Motor Control"](https://tel.archives-ouvertes.fr/tel-00003985/document). The environment aims to increase the number of independent state and control @@ -34,6 +33,7 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): and *k* = 0.1. It is possible to pass a custom MuJoCo XML file during construction to increase the number of links, or to tweak any of the parameters. + ## Action Space The action space is a `Box(-1, 1, (2,), float32)`. An action represents the torques applied between *links* @@ -42,6 +42,7 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): | 0 | Torque applied on the first rotor | -1 | 1 | motor1_rot | hinge | torque (N m) | | 1 | Torque applied on the second rotor | -1 | 1 | motor2_rot | hinge | torque (N m) | + ## Observation Space By default, observations consists of: * θi: angle of part *i* with respect to the *x* axis @@ -69,26 +70,37 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): | excluded | position of the tip along the x-axis | -Inf | Inf | slider1 | slide | position (m) | | excluded | position of the tip along the y-axis | -Inf | Inf | slider2 | slide | position (m) | + ## Rewards The reward consists of two parts: - - *forward_reward*: A reward of moving forward which is measured - as *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. *dt* is - the time between actions and is dependent on the frame_skip parameter - (default is 4), where the frametime is 0.01 - making the - default *dt = 4 * 0.01 = 0.04*. This reward would be positive if the swimmer - swims right as desired. - - *ctrl_cost*: A cost for penalising the swimmer if it takes - actions that are too large. It is measured as *`ctrl_cost_weight` * - sum(action2)* where *`ctrl_cost_weight`* is a parameter set for the - control and has a default value of 1e-4 - - The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost* and `info` will also contain the individual reward terms + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Swimmer moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the (front) "tip" ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), + and `frametime` which is 0.01 - making the default $dt = 4 \times 0.01 = 0.04$, + $w_{forward}$ is the `forward_reward_weight` (default is $1$). + - *ctrl_cost*: + A negative reward for penalizing the Swimmer if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-4}$). + + The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost*, + and `info` will also contain the individual reward terms + ## Starting State All observations start in state (0,0,0,0,0,0,0,0) with a Uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] is added to the initial state for stochasticity. + ## Episode End - The episode truncates when the episode length is greater than 1000. + #### Termination + The Swimmer never terminates. + + #### truncation + The maximum duration of an episode is 1000 timesteps. + ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`. @@ -98,9 +110,13 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): env = gym.make('Swimmer-v5', xml_file=...) ``` - | Parameter | Type | Default |Description | - |-------------------------|------------|-------------- |-------------------------------| - | `xml_file` | **str** |`"swimmer.xml"`| Path to a MuJoCo model | + | Parameter | Type | Default |Description | + |--------------------------------------------| --------- |-------------- |-------------------------------| + |`xml_file` | **str** |`"swimmer.xml"`| Path to a MuJoCo model | + |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward)| + |`ctrl_cost_weight` | **float** | `1e-4` | Weight for _ctrl_cost_ term (see section on reward) | + |`reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | + |`exclude_current_positions_from_observation`| **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | ## Version History diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 17b1f947..6301ecad 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -16,7 +16,6 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): """ ## Description - This environment builds on the [hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) environment by adding another set of legs making it possible for the robot to walk forward instead of hop. Like other Mujoco environments, this environment aims to increase the number of independent state @@ -27,6 +26,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): The goal is to walk in the in the forward (right) direction by applying torques on the six hinges connecting the seven body parts. + ## Action Space The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. @@ -39,6 +39,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): | 4 | Torque applied on the left leg rotor | -1 | 1 | leg_left_joint | hinge | torque (N m) | | 5 | Torque applied on the left foot rotor | -1 | 1 | foot_left_joint | hinge | torque (N m) | + ## Observation Space Observations consist of positional values of different body parts of the walker, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. @@ -73,41 +74,49 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): | 15 | angular velocity of the leg hinge | -Inf | Inf | leg_left_joint | hinge | angular velocity (rad/s) | | 16 | angular velocity of the foot hinge | -Inf | Inf | foot_left_joint | hinge | angular velocity (rad/s) | + ## Rewards The reward consists of three parts: - - *healthy_reward*: Every timestep that the walker is alive, it receives a fixed reward of value `healthy_reward`, - - *forward_reward*: A reward of walking forward which is measured as - *`forward_reward_weight` * (x-coordinate before action - x-coordinate after action)/dt*. - *dt* is the time between actions and is dependeent on the frame_skip parameter - (default is 4), where the frametime is 0.002 - making the default - *dt = 4 * 0.002 = 0.008*. This reward would be positive if the walker walks forward (positive x direction). - - *ctrl_cost*: A cost for penalising the walker if it - takes actions that are too large. It is measured as - *`ctrl_cost_weight` * sum(action2)* where *`ctrl_cost_weight`* is - a parameter set for the control and has a default value of 0.001 - - The total reward returned is ***reward*** *=* *healthy_reward bonus + forward_reward - ctrl_cost* and `info` will also contain the individual reward terms. + - *healthy_reward*: + Every timestep that the Walker2d is alive, it receives a fixed reward of value `healthy_reward`, + - *forward_reward*: + A reward of moving forward, + this reward would be positive if the Swimmer moves forward (in the positive $x$ direction / in the right direction). + $w_{forward} \times \frac{dx}{dt}$, where + $dx$ is the displacement of the (front) "tip" ($x_{after-action} - x_{before-action}$), + $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), + and `frametime` which is 0.002 - making the default $dt = 4 \times 0.002 = 0.008$, + $w_{forward}$ is the `forward_reward_weight` (default is $1$). + - *ctrl_cost*: + A negative reward for penalizing the Walker2d if it takes actions that are too large. + $w_{control} \times \\|action\\|_2^2$, + where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-3}$). + + The total reward returned is ***reward*** *=* *healthy_reward bonus + forward_reward - ctrl_cost* + and `info` will also contain the individual reward terms. + ## Starting State All observations start in state (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. + ## Episode End - The walker is said to be unhealthy if any of the following happens: + #### termination + If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Walker2d is unhealthy. + The Walker2d is unhealthy if any of the following happens: 1. Any of the state space values is no longer finite 2. The height of the walker is ***not*** in the closed interval specified by `healthy_z_range` 3. The absolute value of the angle (`observation[1]` if `exclude_current_positions_from_observation=False`, else `observation[2]`) is ***not*** in the closed interval specified by `healthy_angle_range` - If `terminate_when_unhealthy=True` is passed during construction (which is the default), - the episode ends when any of the following happens: - - 1. Truncation: The episode duration reaches a 1000 timesteps - 2. Termination: The walker is unhealthy + #### truncation + the maximum duration of an episode is 1000 timesteps. If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. + ## Arguments `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. @@ -119,7 +128,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): | Parameter | Type | Default | Description | | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xml_file` | **str** |`"walker2d_v5.xml"`| Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.0` | Weight for _forward_reward_ term (see section on reward) | + | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | From 577f21a535df44796b56339e909e66508828597f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 16 Jun 2023 16:33:02 +0300 Subject: [PATCH 098/160] cleanup --- .../envs/mujoco/inverted_double_pendulum_v5.py | 8 ++++---- gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 0a17750f..35e2fe4c 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -13,7 +13,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): - r""" + """ ## Description This environment originates from control theory and builds on the cartpole environment based on the work done by Barto, Sutton, and Anderson in @@ -174,7 +174,7 @@ def __init__( def step(self, action): self.do_simulation(action, self.frame_skip) - ob = self._get_obs() + observation = self._get_obs() x, _, y = self.data.site_xpos[0] v1, v2 = self.data.qvel[1:3] @@ -184,7 +184,7 @@ def step(self, action): dist_penalty = 0.01 * x**2 + (y - 2) ** 2 vel_penalty = 1e-3 * v1**2 + 5e-3 * v2**2 alive_bonus = self._healthy_reward * int(not terminated) - r = alive_bonus - dist_penalty - vel_penalty + reward = alive_bonus - dist_penalty - vel_penalty info = { "reward_survive": alive_bonus, @@ -194,7 +194,7 @@ def step(self, action): if self.render_mode == "human": self.render() - return ob, r, terminated, False, info + return observation, reward, terminated, False, info def _get_obs(self): assert self.data.qfrc_constraint[2] == 0 # TODO remove after validation diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py index f910e1ca..6bb231b4 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py @@ -150,7 +150,9 @@ def step(self, action): observation = self._get_obs() - terminated = bool(not np.isfinite(observation).all() or (np.abs(observation[1]) > 0.2)) + terminated = bool( + not np.isfinite(observation).all() or (np.abs(observation[1]) > 0.2) + ) reward = int(not terminated) From c4064187c3455ee9db3c3b287181799ced0710fd Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 16 Jun 2023 21:19:18 +0300 Subject: [PATCH 099/160] fix distance_from_origin info --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index 2bbc305b..e77e3f70 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -374,7 +374,7 @@ def step(self, action): "reward_survive": healthy_reward, "x_position": self.data.qpos[0], "y_position": self.data.qpos[1], - "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), + "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, } diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 1981148a..988fddeb 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -490,7 +490,7 @@ def step(self, action): "y_position": self.data.qpos[1], "tendon_lenght": self.data.ten_length, "tendon_velocity": self.data.ten_velocity, - "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), + "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), "x_velocity": x_velocity, "y_velocity": y_velocity, } From faea55c0d16709bcb593c21d3c48096a39353579 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 16 Jun 2023 21:28:48 +0300 Subject: [PATCH 100/160] pre-commit --- gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py index 35e2fe4c..765be745 100644 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py @@ -13,7 +13,7 @@ class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment originates from control theory and builds on the cartpole environment based on the work done by Barto, Sutton, and Anderson in From 358f55d818f102c882f989d1f27779092c22220f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas <30759571+Kallinteris-Andreas@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:45:38 +0300 Subject: [PATCH 101/160] Update maze_v4.py --- gymnasium_robotics/envs/maze/maze_v4.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/maze/maze_v4.py b/gymnasium_robotics/envs/maze/maze_v4.py index 3ec03694..36d4da56 100644 --- a/gymnasium_robotics/envs/maze/maze_v4.py +++ b/gymnasium_robotics/envs/maze/maze_v4.py @@ -355,11 +355,11 @@ def add_xy_position_noise(self, xy_pos: np.ndarray) -> np.ndarray: def compute_reward( self, achieved_goal: np.ndarray, desired_goal: np.ndarray, info ) -> float: - d = np.linalg.norm(achieved_goal - desired_goal, axis=-1) + distance = np.linalg.norm(achieved_goal - desired_goal, axis=-1) if self.reward_type == "dense": - return np.exp(-d) + return np.exp(-distance) elif self.reward_type == "sparse": - return -(d > 0.45).astype(np.float32) + return (distance <= 0.45).astype(np.float64) def compute_terminated( self, achieved_goal: np.ndarray, desired_goal: np.ndarray, info From f94a2c3448fb144f5489ce3d22feb25dd5d9209d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 27 Jun 2023 20:41:44 +0300 Subject: [PATCH 102/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 2 +- .../envs/mujoco/half_cheetah_v5.py | 2 +- gymnasium_robotics/envs/mujoco/hopper_v5.py | 2 +- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 2 +- .../envs/mujoco/humanoidstandup_v5.py | 2 +- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 2 +- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 2 +- tests/envs/mujoco/test_mujoco_v5.py | 471 ++++++++++++++++++ 8 files changed, 478 insertions(+), 7 deletions(-) create mode 100644 tests/envs/mujoco/test_mujoco_v5.py diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py index e77e3f70..dcf2d388 100644 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ b/gymnasium_robotics/envs/mujoco/ant_v5.py @@ -13,7 +13,7 @@ class AntEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment is based on the environment introduced by Schulman, Moritz, Levine, Jordan and Abbeel in ["High-Dimensional Continuous Control diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py index 0c06e167..3b5c5fb5 100644 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py @@ -11,7 +11,7 @@ class HalfCheetahEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment is based on the work by P. Wawrzyński in ["A Cat-Like Robot Real-Time Learning to Run"](http://staff.elka.pw.edu.pl/~pwawrzyn/pub-s/0812_LSCLRR.pdf). diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py index 76db9f06..b3d2b912 100644 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ b/gymnasium_robotics/envs/mujoco/hopper_v5.py @@ -14,7 +14,7 @@ class HopperEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment is based on the work done by Erez, Tassa, and Todorov in ["Infinite Horizon Model Predictive Control for Nonlinear Periodic Tasks"](http://www.roboticsproceedings.org/rss07/p10.pdf). The environment aims to diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py index 988fddeb..b45afc80 100644 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoid_v5.py @@ -20,7 +20,7 @@ def mass_center(model, data): class HumanoidEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment is based on the environment introduced by Tassa, Erez and Todorov in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py index 4c851860..ba13b9f1 100644 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py @@ -14,7 +14,7 @@ class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment is based on the environment introduced by Tassa, Erez and Todorov in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py index 09e23b1a..b156ac5d 100644 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ b/gymnasium_robotics/envs/mujoco/swimmer_v5.py @@ -7,7 +7,7 @@ class SwimmerEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment corresponds to the Swimmer environment described in Rémi Coulom's PhD thesis ["Reinforcement Learning Using Neural Networks, with Applications to Motor Control"](https://tel.archives-ouvertes.fr/tel-00003985/document). diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py index 6301ecad..c09217bd 100644 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ b/gymnasium_robotics/envs/mujoco/walker2d_v5.py @@ -14,7 +14,7 @@ class Walker2dEnv(MujocoEnv, utils.EzPickle): - """ + r""" ## Description This environment builds on the [hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) environment by adding another set of legs making it possible for the robot to walk forward instead of diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py new file mode 100644 index 00000000..ab53a647 --- /dev/null +++ b/tests/envs/mujoco/test_mujoco_v5.py @@ -0,0 +1,471 @@ +import gymnasium as gym +import pytest +import numpy as np +from gymnasium.utils.env_checker import check_env +import mujoco +from gymnasium.error import Error +import warnings + +ALL_MUJOCO_ENVS = ['Ant', 'HalfCheetah', 'Hopper', 'Humanoid', 'HumanoidStandup', 'InvertedDoublePendulum', 'InvertedPendulum', "Pusher", "Reacher", "Swimmer", 'Walker2d'] + + +# Note: "HumnanoidStandup-v4" does not have `info` +# Note: "Humnanoid-v4/3" & "Ant-v4/3" fail this test +@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'HalfCheetah-v4', 'HalfCheetah-v3', 'Hopper-v5', 'Hopper-v4', 'Hopper-v3', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3', 'Walker2d-v5', 'Walker2d-v4', 'Walker2d-v3']) +def test_verify_info_x_position(env_id): + """Asserts that the environment has position[0] == info['x_position']""" + env = gym.make(env_id, exclude_current_positions_from_observation=False) + + _, _ = env.reset() + obs, _, _, _, info = env.step(env.action_space.sample()) + + assert obs[0] == info['x_position'] + + +# Note: "HumnanoidStandup-v4" does not have `info` +# Note: "Humnanoid-v4/3" & "Ant-v4/3" fail this test +@pytest.mark.parametrize("env_id", ['Ant-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5']) +def test_verify_info_y_position(env_id): + """Asserts that the environment has position[1] == info['y_position']""" + env = gym.make(env_id, exclude_current_positions_from_observation=False) + + _, _ = env.reset() + obs, _, _, _, info = env.step(env.action_space.sample()) + + assert obs[1] == info['y_position'] + + +# Note: "HumnanoidStandup-v4" does not have `info` +@pytest.mark.parametrize("env", ['HalfCheetah', 'Hopper', 'Swimmer', 'Walker2d']) +@pytest.mark.parametrize("version", ['v5', 'v4', 'v3']) +def test_verify_info_x_velocity(env, version): + """Asserts that the environment `info['x_velocity']` is properly assigned""" + env = gym.make(f"{env}-{version}") + env.reset() + + old_x = env.unwrapped.data.qpos[0] + _, _, _, _, info = env.step(env.action_space.sample()) + new_x = env.unwrapped.data.qpos[0] + + dx = new_x - old_x + vel_x = dx / env.dt + assert vel_x == info['x_velocity'] + + +# Note: "HumnanoidStandup-v4" does not have `info` +@pytest.mark.parametrize("env_id", ['Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3']) +def test_verify_info_y_velocity(env_id): + """Asserts that the environment `info['y_velocity']` is properly assigned""" + env = gym.make(env_id) + env.reset() + + old_y = env.unwrapped.data.qpos[1] + _, _, _, _, info = env.step(env.action_space.sample()) + new_y = env.unwrapped.data.qpos[1] + + dy = new_y - old_y + vel_y = dy / env.dt + assert vel_y == info['y_velocity'] + + +@pytest.mark.parametrize("env_id", ['Ant-v5', 'Ant-v4', 'Ant-v3']) +def test_verify_info_xy_velocity_xpos(env_id): + """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) + env.reset() + + old_xy = env.get_body_com("torso")[:2].copy() + _, _, _, _, info = env.step(env.action_space.sample()) + new_xy = env.get_body_com("torso")[:2].copy() + + dxy = new_xy - old_xy + vel_x, vel_y = dxy / env.dt + assert vel_x == info['x_velocity'] + assert vel_y == info['y_velocity'] + + +@pytest.mark.parametrize("env_id", ['Humanoid-v5', 'Humanoid-v4', 'Humanoid-v3']) +def test_verify_info_xy_velocity_com(env_id): + """Asserts that the environment `info['x/y_velocity']` is properly assigned, for the humanoid environment which uses kinmatics of Center Of Mass for the velocity""" + + def mass_center(model, data): + mass = np.expand_dims(model.body_mass, axis=1) + xpos = data.xipos + return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() + + env = gym.make(env_id) + env.reset() + + old_xy = mass_center(env.unwrapped.model, env.unwrapped.data) + _, _, _, _, info = env.step(env.action_space.sample()) + new_xy = mass_center(env.unwrapped.model, env.unwrapped.data) + + dxy = new_xy - old_xy + vel_x, vel_y = dxy / env.dt + assert vel_x == info['x_velocity'] + assert vel_y == info['y_velocity'] + + +# Note: Hopper-v4/3/2 does not have `info['reward_survive']`, but it is still affected +# Note: Walker2d-v4/3/2 does not have `info['reward_survive']`, but it is still affected +# Note: Inverted(Double)Pendulum-v4/2 does not have `info['reward_survive']`, but it is still affected +# Note: all `v4/v3/v2` environments with a heathly reward are fail this test +@pytest.mark.parametrize("env_id", ['Ant-v5', 'Hopper-v5', 'Humanoid-v5', 'InvertedDoublePendulum-v5', 'InvertedPendulum-v5', 'Walker2d-v5']) +def test_verify_reward_survive(env_id): + """Assert that `reward_survive` is 0 on `terminal` states and not 0 on non-`terminal` states""" + env = gym.make(env_id, reset_noise_scale=0) + env.reset(seed=0) + env.action_space.seed(0) + + for step in range(175): + obs, rew, terminal, truncated, info = env.step(env.action_space.sample()) + + if terminal: + assert info['reward_survive'] == 0 + break + + assert info['reward_survive'] != 0 + + assert terminal, "The environment, should have terminated, if not the test is not valid." + + +CHECK_ENV_IGNORE_WARNINGS = [ + f"\x1b[33mWARN: {message}\x1b[0m" + for message in [ + "A Box observation space minimum value is -infinity. This is probably too low.", + "A Box observation space maximum value is -infinity. This is probably too high.", + "For Box action spaces, we recommend using a symmetric and normalized space (range=[-1, 1] or [0, 1]). See https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html for more information.", + ] +] + + +@pytest.mark.parametrize("env", ALL_MUJOCO_ENVS) +@pytest.mark.parametrize("version", ['v5']) +@pytest.mark.parametrize("frame_skip", [1, 2, 3, 4, 5]) +def test_frame_skip(env, version, frame_skip): + """Verify that all `mujoco` envs work with different `frame_skip` values""" + env_id = f"{env}-{version}" + env = gym.make(env_id, frame_skip=frame_skip) + + # Test if env adheres to Gym API + with warnings.catch_warnings(record=True) as w: + gym.utils.env_checker.check_env(env.unwrapped, skip_render_check=True) + env.close() + for warning in w: + if warning.message.args[0] not in CHECK_ENV_IGNORE_WARNINGS: + raise Error(f"Unexpected warning: {warning.message}") + + +# Dev Note: This can be version env parametrized because each env has it's own reward function +@pytest.mark.parametrize("version", ['v5']) +def test_reward_sum(version): + """Assert that the total reward equals the sum of the individual reward terms.""" + env = gym.make(f'Ant-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_contact"] - info["reward_survive"] < 1e-14 + + env = gym.make(f'HalfCheetah-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] < 1e-14 + + env = gym.make(f'Hopper-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_survive"] < 1e-14 + + env = gym.make(f'Humanoid-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_contact"] - info["reward_survive"] < 1e-14 + + env = gym.make(f'HumanoidStandup-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_linup"] - info["reward_quadctrl"] - info["reward_impact"] - 1 < 1e-14 + + env = gym.make(f'InvertedDoublePendulum-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_survive"] - info["distance_penalty"] - info["velocity_penalty"] < 1e-14 + + env = gym.make(f'InvertedPendulum-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward == info["reward_survive"] + + env = gym.make(f'Pusher-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_dist"] - info["reward_ctrl"] - info["reward_near"] < 1e-14 + + env = gym.make(f'Reacher-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_dist"] - info["reward_ctrl"] < 1e-14 + + env = gym.make(f'Swimmer-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] < 1e-14 + + env = gym.make(f'Walker2d-{version}') + env.reset() + _, reward, _, _, info = env.step(env.action_space.sample()) + assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_survive"] < 1e-14 + + +# Note: the environtments that are not present, is because they do not have identical behaviour +@pytest.mark.parametrize("env", ['HalfCheetah', 'HumanoidStandup', 'Pusher', 'Reacher', 'Swimmer']) +def test_identical_behaviour_v45(env): + """Verify that v4 -> v5 transition. does not change the behaviour of the environments in way way""" + env_v4 = gym.make(env + '-v4') + env_v5 = gym.make(env + '-v5') + env_v4.reset(seed=1234) + env_v5.reset(seed=1234) + action = env_v4.action_space.sample() + obs_v4, rew_v4, terminal_v4, truncated_v4, info_v4 = env_v4.step(action) + obs_v5, rew_v5, terminal_v5, truncated_v5, info_v5 = env_v5.step(action) + assert obs_v4.shape[0] != 1 + assert obs_v5.shape[0] != 1 + assert (env_v4.unwrapped.data.qpos == env_v5.unwrapped.data.qpos).all() + assert (env_v4.unwrapped.data.qvel == env_v5.unwrapped.data.qvel).all() + if env not in ['HumanoidStandup', 'Reacher']: # they have different obs + assert (obs_v4 == obs_v5).all() + assert rew_v4 == rew_v5 + assert terminal_v4 == terminal_v5 and truncated_v4 == truncated_v5 + + +@pytest.mark.parametrize("version", ['v5', 'v4']) +def test_ant_com(version): + """Verify the kinmatic behaviour of the ant""" + env = gym.make(f'Ant-{version}') # `env` contains `data : MjData` and `model : MjModel` + env.reset() # randomly initlizies the `data.qpos` and `data.qvel`, calls mujoco.mj_forward(env.model, env.data) + + x_position_before = env.unwrapped.data.qpos[0] + x_position_before_com = env.unwrapped.data.body("torso").xpos[0] + assert x_position_before == x_position_before_com, "before failed" # This succeeds + + random_control = env.action_space.sample() + _, _, _, _, info = env.step(random_control) # This calls mujoco.mj_step(env.model, env.data, nstep=env.frame_skip) + mujoco.mj_kinematics(env.unwrapped.model, env.unwrapped.data) + + x_position_after = env.unwrapped.data.qpos[0] + x_position_after_com = env.unwrapped.data.body("torso").xpos[0] + assert x_position_after == x_position_after_com, "after failed" # This succeeds + + +@pytest.mark.parametrize("version", ['v5', 'v4', 'v3', 'v2']) +def test_set_state(version): + """Simple Test to verify that `mujocoEnv.set_state()` works correctly""" + env = gym.make(f'Hopper-{version}') + env.reset() + new_qpos = np.array([0.00136962, 1.24769787, -0.00459026, -0.00483472, 0.0031327 , 0.00412756]) + new_qvel = np.array([0.00106636, 0.00229497, 0.00043625, 0.00435072, 0.00315854, -0.00497261]) + env.set_state(new_qpos, new_qvel) + assert (env.unwrapped.data.qpos == new_qpos).all() + assert (env.unwrapped.data.qvel == new_qvel).all() + + +# Note: HumanoidStandup-v4/v3 does not have `info` +# Note: Ant-v4/v3 fails this test +# Note: Humanoid-v4/v3 fails this test +# Note: v2 does not have `info` +@pytest.mark.parametrize("env_id", ['Ant-v5', 'Humanoid-v5', 'Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3']) +def test_distance_from_origin_info(env_id): + """Verify that `info"distance_from_origin"` is correct""" + env = gym.make(env_id) + env.reset() + _, _, _, _, info = env.step(env.action_space.sample()) + assert info["distance_from_origin"] == np.linalg.norm(env.unwrapped.data.qpos[0:2] - env.init_qpos[0:2]) + + +@pytest.mark.parametrize("env_id", ['Hopper-v5', 'HumanoidStandup-v5', 'Walker2d-v5']) +def test_z_distance_from_origin_info(env_id): + """Verify that `info"z_distance_from_origin"` is correct""" + env = gym.make(env_id) + env.reset() + _, _, _, _, info = env.step(env.action_space.sample()) + mujoco.mj_kinematics(env.unwrapped.model, env.unwrapped.data) + z_index = env.observation_structure["skipped_qpos"] + assert info["z_distance_from_origin"] == env.unwrapped.data.qpos[z_index] - env.init_qpos[z_index] + + +@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'Hopper-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'InvertedPendulum-v5', 'Swimmer-v5', 'Walker2d-v5']) +def test_observation_structure(env_id): + """Verify that the `env.observation_structure` is properly defined.""" + env = gym.make(env_id) + if hasattr(env, "observation_structure"): + return + + obs_struct = env.observation_structure + + assert env.unwrapped.model.nq == obs_struct.get("skipped_qpos", 0) + obs_struct["qpos"] + assert env.unwrapped.model.nv == obs_struct["qvel"] + if obs_struct.get("cinert", 0): + assert (env.unwrapped.model.nbody - 1) * 10 == obs_struct["cinert"] + if obs_struct.get("cvel", 0): + assert (env.unwrapped.model.nbody - 1) * 6 == obs_struct["cvel"] + if obs_struct.get("qfrc_actuator", 0): + assert env.unwrapped.model.nv - 6 == obs_struct["qfrc_actuator"] + if obs_struct.get("cfrc_ext", 0): + assert (env.unwrapped.model.nbody - 1) * 6 == obs_struct["cfrc_ext"] + if obs_struct.get("ten_lenght", 0): + assert env.unwrapped.model.ntendon == obs_struct["ten_lenght"] + if obs_struct.get("ten_velocity", 0): + assert env.unwrapped.model.ntendon == obs_struct["ten_velocity"] + + +@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'Hopper-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5', 'Walker2d-v5']) +def test_reset_info(env_id): + """Verify that the environmet returns info at `reset()`""" + env = gym.make(env_id) + _, reset_info = env.reset() + assert reset_info.get('x_position') + + +""" +[Bug Report] [Documentation] Inverted Double Pendulum max Height is wrong + +The Documentation States: +```md +The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other) +``` +but the height of each pole is 0.6 (0.6+0.6==1.2) +https://github.com/Farama-Foundation/Gymnasium/blob/deb50802facfd827abd4d1f0cf1069afb12a726b/gymnasium/envs/mujoco/assets/inverted_double_pendulum.xml#L33-L39 +""" + + +# Note: the max height used to be wrong in the documention. +@pytest.mark.parametrize("version", ['v5']) +def test_inverted_double_pendulum_max_height(version): + """Verify the max height of Inverted Double Pendulum""" + env = gym.make(f"InvertedDoublePendulum-{version}", reset_noise_scale=0) + env.reset() + y = env.unwrapped.data.site_xpos[0][2] + assert y == 1.2 + + +@pytest.mark.parametrize("version", ['v4']) +def test_inverted_double_pendulum_max_height_old(version): + """Verify the max height of Inverted Double Pendulum (v4 does not have `reset_noise_scale` argument)""" + env = gym.make(f"InvertedDoublePendulum-{version}") + env.set_state(env.init_qpos, env.init_qvel) + y = env.unwrapped.data.site_xpos[0][2] + assert y == 1.2 + + +# note: fails with `brax==0.9.0` +@pytest.mark.parametrize("version", ['v5', 'v4']) +def test_model_object_count(version): + """Verify that all the objects of the model are loaded, mostly usefull for using non-mujoco simulator.""" + env = gym.make(f'Ant-{version}') + assert env.unwrapped.model.nq == 15 + assert env.unwrapped.model.nv == 14 + assert env.unwrapped.model.nu == 8 + assert env.unwrapped.model.nbody == 14 + assert env.unwrapped.model.nbvh == 14 + assert env.unwrapped.model.njnt == 9 + assert env.unwrapped.model.ngeom == 14 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'HalfCheetah-{version}') + assert env.unwrapped.model.nq == 9 + assert env.unwrapped.model.nv == 9 + assert env.unwrapped.model.nu == 6 + assert env.unwrapped.model.nbody == 8 + assert env.unwrapped.model.nbvh == 10 + assert env.unwrapped.model.njnt == 9 + assert env.unwrapped.model.ngeom == 9 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Hopper-{version}') + assert env.unwrapped.model.nq == 6 + assert env.unwrapped.model.nv == 6 + assert env.unwrapped.model.nu == 3 + assert env.unwrapped.model.nbody == 5 + assert env.unwrapped.model.nbvh == 5 + assert env.unwrapped.model.njnt == 6 + assert env.unwrapped.model.ngeom == 5 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Humanoid-{version}') + assert env.unwrapped.model.nq == 24 + assert env.unwrapped.model.nv == 23 + assert env.unwrapped.model.nu == 17 + assert env.unwrapped.model.nbody == 14 + assert env.unwrapped.model.nbvh == 22 + assert env.unwrapped.model.njnt == 18 + assert env.unwrapped.model.ngeom == 18 + assert env.unwrapped.model.ntendon == 2 + + env = gym.make(f'HumanoidStandup-{version}') + assert env.unwrapped.model.nq == 24 + assert env.unwrapped.model.nv == 23 + assert env.unwrapped.model.nu == 17 + assert env.unwrapped.model.nbody == 14 + assert env.unwrapped.model.nbvh == 22 + assert env.unwrapped.model.njnt == 18 + assert env.unwrapped.model.ngeom == 18 + assert env.unwrapped.model.ntendon == 2 + + env = gym.make(f'InvertedDoublePendulum-{version}') + assert env.unwrapped.model.nq == 3 + assert env.unwrapped.model.nv == 3 + assert env.unwrapped.model.nu == 1 + assert env.unwrapped.model.nbody == 4 + assert env.unwrapped.model.nbvh == 6 + assert env.unwrapped.model.njnt == 3 + assert env.unwrapped.model.ngeom == 5 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'InvertedPendulum-{version}') + assert env.unwrapped.model.nq == 2 + assert env.unwrapped.model.nv == 2 + assert env.unwrapped.model.nu == 1 + assert env.unwrapped.model.nbody == 3 + assert env.unwrapped.model.nbvh == 3 + assert env.unwrapped.model.njnt == 2 + assert env.unwrapped.model.ngeom == 3 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Pusher-{version}') + assert env.unwrapped.model.nq == 11 + assert env.unwrapped.model.nv == 11 + assert env.unwrapped.model.nu == 7 + assert env.unwrapped.model.nbody == 13 + assert env.unwrapped.model.nbvh == 18 + assert env.unwrapped.model.njnt == 11 + assert env.unwrapped.model.ngeom == 21 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Reacher-{version}') + assert env.unwrapped.model.nq == 4 + assert env.unwrapped.model.nv == 4 + assert env.unwrapped.model.nu == 2 + assert env.unwrapped.model.nbody == 5 + assert env.unwrapped.model.nbvh == 5 + assert env.unwrapped.model.njnt == 4 + assert env.unwrapped.model.ngeom == 10 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Swimmer-{version}') + assert env.unwrapped.model.nq == 5 + assert env.unwrapped.model.nv == 5 + assert env.unwrapped.model.nu == 2 + assert env.unwrapped.model.nbody == 4 + assert env.unwrapped.model.nbvh == 4 + assert env.unwrapped.model.njnt == 5 + assert env.unwrapped.model.ngeom == 4 + assert env.unwrapped.model.ntendon == 0 + + env = gym.make(f'Walker2d-{version}') + assert env.unwrapped.model.nq == 9 + assert env.unwrapped.model.nv == 9 + assert env.unwrapped.model.nu == 6 + assert env.unwrapped.model.nbody == 8 + assert env.unwrapped.model.nbvh == 8 + assert env.unwrapped.model.njnt == 9 + assert env.unwrapped.model.ngeom == 8 + assert env.unwrapped.model.ntendon == 0 From eb4f21b1fd541bef4000756ec48fb44b44ce24c5 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 17 Jul 2023 23:06:48 +0300 Subject: [PATCH 103/160] cleanup --- gymnasium_robotics/envs/mujoco/ant_v5.py | 427 -------------- .../envs/mujoco/assets/hopper_v5.xml | 53 -- .../envs/mujoco/assets/walker2d_v5.xml | 68 --- .../envs/mujoco/assets/walker2d_v5_old.xml | 68 --- .../envs/mujoco/half_cheetah_v5.py | 270 --------- gymnasium_robotics/envs/mujoco/hopper_v5.py | 335 ----------- gymnasium_robotics/envs/mujoco/humanoid_v5.py | 524 ------------------ .../envs/mujoco/humanoidstandup_v5.py | 469 ---------------- .../mujoco/inverted_double_pendulum_v5.py | 224 -------- .../envs/mujoco/inverted_pendulum_v5.py | 179 ------ gymnasium_robotics/envs/mujoco/pusher_v5.py | 246 -------- gymnasium_robotics/envs/mujoco/reacher_v5.py | 220 -------- gymnasium_robotics/envs/mujoco/swimmer_v5.py | 260 --------- gymnasium_robotics/envs/mujoco/walker2d_v5.py | 329 ----------- 14 files changed, 3672 deletions(-) delete mode 100644 gymnasium_robotics/envs/mujoco/ant_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml delete mode 100644 gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml delete mode 100644 gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml delete mode 100644 gymnasium_robotics/envs/mujoco/half_cheetah_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/hopper_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/humanoid_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/pusher_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/reacher_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/swimmer_v5.py delete mode 100644 gymnasium_robotics/envs/mujoco/walker2d_v5.py diff --git a/gymnasium_robotics/envs/mujoco/ant_v5.py b/gymnasium_robotics/envs/mujoco/ant_v5.py deleted file mode 100644 index dcf2d388..00000000 --- a/gymnasium_robotics/envs/mujoco/ant_v5.py +++ /dev/null @@ -1,427 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -from typing import Dict, Tuple, Union - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "distance": 4.0, -} - - -class AntEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment is based on the environment introduced by Schulman, - Moritz, Levine, Jordan and Abbeel in ["High-Dimensional Continuous Control - Using Generalized Advantage Estimation"](https://arxiv.org/abs/1506.02438). - The ant is a 3D robot consisting of one torso (free rotational body) with - four legs attached to it with each leg having two body parts. The goal is to - coordinate the four legs to move in the forward (right) direction by applying - torques on the eight hinges connecting the two body parts of each leg and the torso - (nine body parts and eight hinges). - - - ## Action Space - The action space is a `Box(-1, 1, (8,), float32)`. An action represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ----------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | - | 0 | Torque applied on the rotor between the torso and back right hip | -1 | 1 | hip_4 (right_back_leg) | hinge | torque (N m) | - | 1 | Torque applied on the rotor between the back right two links | -1 | 1 | angle_4 (right_back_leg) | hinge | torque (N m) | - | 2 | Torque applied on the rotor between the torso and front left hip | -1 | 1 | hip_1 (front_left_leg) | hinge | torque (N m) | - | 3 | Torque applied on the rotor between the front left two links | -1 | 1 | angle_1 (front_left_leg) | hinge | torque (N m) | - | 4 | Torque applied on the rotor between the torso and front right hip | -1 | 1 | hip_2 (front_right_leg) | hinge | torque (N m) | - | 5 | Torque applied on the rotor between the front right two links | -1 | 1 | angle_2 (front_right_leg) | hinge | torque (N m) | - | 6 | Torque applied on the rotor between the torso and back left hip | -1 | 1 | hip_3 (back_leg) | hinge | torque (N m) | - | 7 | Torque applied on the rotor between the back left two links | -1 | 1 | angle_3 (back_leg) | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the ant, - followed by the velocities of those individual parts (their derivatives) with all - the positions ordered before all the velocities. - - By default, observations do not include the x- and y-coordinates of the ant's torso. These may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (107,), float64)` where the first two observations - represent the x- and y- coordinates of the ant's torso. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates - of the torso will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - - However, by default, observation Space is a `Box(-Inf, Inf, (105,), float64)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - |-----|--------------------------------------------------------------|--------|--------|----------------------------------------|-------|--------------------------| - | 0 | z-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | - | 1 | x-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | - | 2 | y-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | - | 3 | z-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | - | 4 | w-orientation of the torso (centre) | -Inf | Inf | torso | free | angle (rad) | - | 5 | angle between torso and first link on front left | -Inf | Inf | hip_1 (front_left_leg) | hinge | angle (rad) | - | 6 | angle between the two links on the front left | -Inf | Inf | ankle_1 (front_left_leg) | hinge | angle (rad) | - | 7 | angle between torso and first link on front right | -Inf | Inf | hip_2 (front_right_leg) | hinge | angle (rad) | - | 8 | angle between the two links on the front right | -Inf | Inf | ankle_2 (front_right_leg) | hinge | angle (rad) | - | 9 | angle between torso and first link on back left | -Inf | Inf | hip_3 (back_leg) | hinge | angle (rad) | - | 10 | angle between the two links on the back left | -Inf | Inf | ankle_3 (back_leg) | hinge | angle (rad) | - | 11 | angle between torso and first link on back right | -Inf | Inf | hip_4 (right_back_leg) | hinge | angle (rad) | - | 12 | angle between the two links on the back right | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | - | 13 | x-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | - | 14 | y-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | - | 15 | z-coordinate velocity of the torso | -Inf | Inf | torso | free | velocity (m/s) | - | 16 | x-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | - | 17 | y-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | - | 18 | z-coordinate angular velocity of the torso | -Inf | Inf | torso | free | angular velocity (rad/s) | - | 19 | angular velocity of angle between torso and front left link | -Inf | Inf | hip_1 (front_left_leg) | hinge | angle (rad) | - | 20 | angular velocity of the angle between front left links | -Inf | Inf | ankle_1 (front_left_leg) | hinge | angle (rad) | - | 21 | angular velocity of angle between torso and front right link | -Inf | Inf | hip_2 (front_right_leg) | hinge | angle (rad) | - | 22 | angular velocity of the angle between front right links | -Inf | Inf | ankle_2 (front_right_leg) | hinge | angle (rad) | - | 23 | angular velocity of angle between torso and back left link | -Inf | Inf | hip_3 (back_leg) | hinge | angle (rad) | - | 24 | angular velocity of the angle between back left links | -Inf | Inf | ankle_3 (back_leg) | hinge | angle (rad) | - | 25 | angular velocity of angle between torso and back right link | -Inf | Inf | hip_4 (right_back_leg) | hinge | angle (rad) | - | 26 | angular velocity of the angle between back right links | -Inf | Inf | ankle_4 (right_back_leg) | hinge | angle (rad) | - | excluded | x-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | - | excluded | y-coordinate of the torso (centre) | -Inf | Inf | torso | free | position (m) | - - - Additionally, after all the positional and velocity based values in the table, the observation contains: - - *cfrc_ext:* 13*6 = 78 elements, which are contact forces - (external forces - force x, y, z and torque x, y, z) applied to the - center of mass of each of the body parts. - - The body parts are: - - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | - | ---| --- | ------------ | - | 0 |excluded| worldbody (note: all values are constant 0) | - | 1 |0 | torso | - | 2 |1 | front_left_leg | - | 3 |2 | aux_1 (front left leg) | - | 4 |3 | ankle_1 (front left leg) | - | 5 |4 | front_right_leg | - | 6 |5 | aux_2 (front right leg) | - | 7 |6 | ankle_2 (front right leg) | - | 8 |7 | back_leg (back left leg) | - | 9 |8 | aux_3 (back left leg) | - | 10 |9 | ankle_3 (back left leg) | - | 11 |10 | right_back_leg | - | 12 |11 | aux_4 (back right leg) | - | 13 |12 | ankle_4 (back right leg) | - - The (x,y,z) coordinates are translational DOFs while the orientations are rotational - DOFs expressed as quaternions. One can read more about free joints on the [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). - - - **Note:** Ant-v4+ environment no longer has the following contact forces issue. - If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 results - in the contact forces always being 0. As such we recommend to use a Mujoco-Py version < 2.0 - when using the Ant environment if you would like to report results with contact forces (if - contact forces are not used in your experiments, you can use version > 2.0). - - - ## Rewards - The reward consists of three parts: - - *healthy_reward*: - Every timestep that the ant is healthy (see definition in section "Episode Termination"), - it gets a reward of fixed value `healthy_reward`. - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Ant moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the `main_body` ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), - and `frametime` which is 0.01 - making the default $dt = 5 \times 0.01 = 0.05$, - $w_{forward}$ is the `forward_reward_weight` (default is $1$). - - *ctrl_cost*: - A negative reward for penalizing the Ant if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $0.5$). - - *contact_cost*: - A negative reward for penalizing the Ant if the external contact forces are too large. - $w_{contact} \times \\|F_{contact}\\|_2^2$, where - $w_{contact}$ is `contact_cost_weight` (default is $5\times10^{-4}$), - $F_{contact}$ are the external contact forces clipped by `contact_force_range` (see `cfrc_ext` section on observation). - - - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost*. - - But if `use_contact_forces=false` on `v4` - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*. - - In either case `info` will also contain the individual reward terms. - - - ## Starting State - All observations start in state - (0.0, 0.0, 0.75, 1.0, 0.0 ... 0.0) with a uniform noise in the range - of [-`reset_noise_scale`, `reset_noise_scale`] added to the positional values and standard normal noise - with mean 0 and standard deviation `reset_noise_scale` added to the velocity values for - stochasticity. Note that the initial z coordinate is intentionally selected - to be slightly high, thereby indicating a standing up ant. The initial orientation - is designed to make it face forward as well. - - - ## Episode End - #### Termination - If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Ant is unhealthy. - the Ant is unhealthy if any of the following happens: - - 1. Any of the state space values is no longer finite - 2. The z-coordinate of the torso is **not** in the closed interval given by `healthy_z_range` (defaults to [0.2, 1.0]) - - #### truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Ant-v5', ctrl_cost_weight=0.5, ...) - ``` - - | Parameter | Type | Default |Description | - |--------------------------------------------|------------|--------------|-------------------------------| - |`xml_file` | **str** | `"ant.xml"` | Path to a MuJoCo model | - |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward)| - |`ctrl_cost_weight` | **float** | `0.5` | Weight for _ctrl_cost_ term (see section on reward) | - |`contact_cost_weight` | **float** | `5e-4` | Weight for _contact_cost_ term (see section on reward) | - |`healthy_reward` | **float** | `1` | Weight for _healthy_reward_ term (see section on reward) | - |`main_body` |**str|int** | `1`("torso") | Name or ID of the body, whose diplacement is used to calculate the *dx*/_forward_reward_ (useful for custom MuJoCo models)| - |`terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | - |`healthy_z_range` | **tuple** | `(0.2, 1)` | The ant is considered healthy if the z-coordinate of the torso is in this range | - |`contact_force_range` | **tuple** | `(-1, 1)` | Contact forces are clipped to this range in the computation of *contact_cost* | - |`reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - |`exclude_current_positions_from_observation`| **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - |`include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | - |`use_contact_forces` (`v4` only) | **bool** | `False` | If true, it extends the observation space by adding contact forces (see `Observation Space` section) and includes contact_cost to the reward function (see `Rewards` section) | - - ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3, also removed contact forces from the default observation space (new variable `use_contact_forces=True` can restore them) - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) - * v2: All continuous control environments now use mujoco-py >= 1.50 - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file: str = "ant.xml", - frame_skip: int = 5, - default_camera_config: Dict = DEFAULT_CAMERA_CONFIG, - forward_reward_weight: float = 1, - ctrl_cost_weight: float = 0.5, - contact_cost_weight: float = 5e-4, - healthy_reward: float = 1.0, - main_body: Union[int, str] = 1, - terminate_when_unhealthy: bool = True, - healthy_z_range: Tuple[float, float] = (0.2, 1.0), - contact_force_range: Tuple[float, float] = (-1.0, 1.0), - reset_noise_scale: float = 0.1, - exclude_current_positions_from_observation: bool = True, - include_cfrc_ext_in_observation: bool = True, - **kwargs - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - forward_reward_weight, - ctrl_cost_weight, - contact_cost_weight, - healthy_reward, - main_body, - terminate_when_unhealthy, - healthy_z_range, - contact_force_range, - reset_noise_scale, - exclude_current_positions_from_observation, - include_cfrc_ext_in_observation, - **kwargs - ) - - self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight - self._contact_cost_weight = contact_cost_weight - - self._healthy_reward = healthy_reward - self._terminate_when_unhealthy = terminate_when_unhealthy - self._healthy_z_range = healthy_z_range - - self._contact_force_range = contact_force_range - - self._main_body = main_body - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, # needs to be defined after - default_camera_config=default_camera_config, - **kwargs - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = self.data.qpos.size + self.data.qvel.size - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation - - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 2 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, - } - - @property - def healthy_reward(self): - return self.is_healthy * self._healthy_reward - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) - return control_cost - - @property - def contact_forces(self): - raw_contact_forces = self.data.cfrc_ext - min_value, max_value = self._contact_force_range - contact_forces = np.clip(raw_contact_forces, min_value, max_value) - return contact_forces - - @property - def contact_cost(self): - contact_cost = self._contact_cost_weight * np.sum( - np.square(self.contact_forces) - ) - return contact_cost - - @property - def is_healthy(self): - state = self.state_vector() - min_z, max_z = self._healthy_z_range - is_healthy = np.isfinite(state).all() and min_z <= state[2] <= max_z - return is_healthy - - @property - def terminated(self): - terminated = (not self.is_healthy) and self._terminate_when_unhealthy - # TODO remove after validation - assert terminated == ( - not self.is_healthy if self._terminate_when_unhealthy else False - ) - return terminated - - def step(self, action): - xy_position_before = self.data.body(self._main_body).xpos[:2].copy() - # TODO remove after validation - assert (xy_position_before == self.get_body_com("torso")[:2].copy()).all() - self.do_simulation(action, self.frame_skip) - xy_position_after = self.data.body(self._main_body).xpos[:2].copy() - # TODO remove after validation - assert (xy_position_after == self.get_body_com("torso")[:2].copy()).all() - - xy_velocity = (xy_position_after - xy_position_before) / self.dt - x_velocity, y_velocity = xy_velocity - - forward_reward = x_velocity * self._forward_reward_weight - healthy_reward = self.healthy_reward - - rewards = forward_reward + healthy_reward - - costs = ctrl_cost = self.control_cost(action) - contact_cost = self.contact_cost - costs += contact_cost - - terminated = self.terminated - observation = self._get_obs() - info = { - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "reward_contact": -contact_cost, - "reward_survive": healthy_reward, - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), - "x_velocity": x_velocity, - "y_velocity": y_velocity, - } - - reward = rewards - costs - - if self.render_mode == "human": - self.render() - return observation, reward, terminated, False, info - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - - if self._exclude_current_positions_from_observation: - position = position[2:] - - if self._include_cfrc_ext_in_observation: - assert ( - self.contact_forces[0].flat.copy() == 0 - ).all() # TODO remove after validation - contact_force = self.contact_forces[1:].flat.copy() - assert len(contact_force) == 78 - return np.concatenate((position, velocity, contact_force)) - else: - return np.concatenate((position, velocity)) - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = ( - self.init_qvel - + self._reset_noise_scale * self.np_random.standard_normal(self.model.nv) - ) - self.set_state(qpos, qvel) - - observation = self._get_obs() - - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), - } diff --git a/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml b/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml deleted file mode 100644 index cc3fbc84..00000000 --- a/gymnasium_robotics/envs/mujoco/assets/hopper_v5.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml deleted file mode 100644 index 12baef14..00000000 --- a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - diff --git a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml b/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml deleted file mode 100644 index 180bfaec..00000000 --- a/gymnasium_robotics/envs/mujoco/assets/walker2d_v5_old.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - diff --git a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py b/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py deleted file mode 100644 index 3b5c5fb5..00000000 --- a/gymnasium_robotics/envs/mujoco/half_cheetah_v5.py +++ /dev/null @@ -1,270 +0,0 @@ -__credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "distance": 4.0, -} - - -class HalfCheetahEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment is based on the work by P. Wawrzyński in - ["A Cat-Like Robot Real-Time Learning to Run"](http://staff.elka.pw.edu.pl/~pwawrzyn/pub-s/0812_LSCLRR.pdf). - The HalfCheetah is a 2-dimensional robot consisting of 9 body parts and 8 - joints connecting them (including two paws). The goal is to apply a torque - on the joints to make the cheetah run forward (right) as fast as possible, - with a positive reward allocated based on the distance moved forward and a - negative reward allocated for moving backward. The torso and head of the - cheetah are fixed, and the torque can only be applied on the other 6 joints - over the front and back thighs (connecting to the torso), shins - (connecting to the thighs) and feet (connecting to the shins). - - - ## Action Space - The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - | --- | --------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | - | 0 | Torque applied on the back thigh rotor | -1 | 1 | bthigh | hinge | torque (N m) | - | 1 | Torque applied on the back shin rotor | -1 | 1 | bshin | hinge | torque (N m) | - | 2 | Torque applied on the back foot rotor | -1 | 1 | bfoot | hinge | torque (N m) | - | 3 | Torque applied on the front thigh rotor | -1 | 1 | fthigh | hinge | torque (N m) | - | 4 | Torque applied on the front shin rotor | -1 | 1 | fshin | hinge | torque (N m) | - | 5 | Torque applied on the front foot rotor | -1 | 1 | ffoot | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the - cheetah, followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. - - By default, observations do not include the cheetah's `rootx`. It may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (18,), float64)` where the first element - represents the `rootx`. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the - will be returned in `info` with key `"x_position"`. - - However, by default, the observation is a `Box(-Inf, Inf, (17,), float64)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ------------------------------------ | ---- | --- | -------------------------------- | ----- | ------------------------ | - | 0 | z-coordinate of the front tip | -Inf | Inf | rootz | slide | position (m) | - | 1 | angle of the front tip | -Inf | Inf | rooty | hinge | angle (rad) | - | 2 | angle of the second rotor | -Inf | Inf | bthigh | hinge | angle (rad) | - | 3 | angle of the second rotor | -Inf | Inf | bshin | hinge | angle (rad) | - | 4 | velocity of the tip along the x-axis | -Inf | Inf | bfoot | hinge | angle (rad) | - | 5 | velocity of the tip along the y-axis | -Inf | Inf | fthigh | hinge | angle (rad) | - | 6 | angular velocity of front tip | -Inf | Inf | fshin | hinge | angle (rad) | - | 7 | angular velocity of second rotor | -Inf | Inf | ffoot | hinge | angle (rad) | - | 8 | x-coordinate of the front tip | -Inf | Inf | rootx | slide | velocity (m/s) | - | 9 | y-coordinate of the front tip | -Inf | Inf | rootz | slide | velocity (m/s) | - | 10 | angle of the front tip | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | - | 11 | angle of the second rotor | -Inf | Inf | bthigh | hinge | angular velocity (rad/s) | - | 12 | angle of the second rotor | -Inf | Inf | bshin | hinge | angular velocity (rad/s) | - | 13 | velocity of the tip along the x-axis | -Inf | Inf | bfoot | hinge | angular velocity (rad/s) | - | 14 | velocity of the tip along the y-axis | -Inf | Inf | fthigh | hinge | angular velocity (rad/s) | - | 15 | angular velocity of front tip | -Inf | Inf | fshin | hinge | angular velocity (rad/s) | - | 16 | angular velocity of second rotor | -Inf | Inf | ffoot | hinge | angular velocity (rad/s) | - | excluded | x-coordinate of the front tip | -Inf | Inf | rootx | slide | position (m) | - - - ## Rewards - The reward consists of two parts: - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Half Cheetah moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the "tip" ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), - and `frametime` which is 0.01 - making the default $dt = 5 \times 0.01 = 0.05$, - $w_{forward}$ is the `forward_reward_weight` (default is $1$). - - *ctrl_cost*: - A negative reward for penalizing the Half Cheetah if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $0.1$). - - The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost*, - and `info` will also contain the individual reward terms. - - - ## Starting State - All observations start in state (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,) with a noise added to the - initial state for stochasticity. As seen before, the first 8 values in the - state are positional and the last 9 values are velocity. A uniform noise in - the range of [-`reset_noise_scale`, `reset_noise_scale`] is added to the positional values while a standard - normal noise with a mean of 0 and standard deviation of `reset_noise_scale` is added to the - initial velocity values of all zeros. - - - ## Episode End - #### Termination - The Half Cheetah never terminates. - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('HalfCheetah-v5', ctrl_cost_weight=0.1, ....) - ``` - - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"half_cheetah.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ weight (see section on reward) | - | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - - ## Version History - - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. The `xml_file` argument is re-added, renamed `info`'s `reward_run` to `reward_forward`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen). - * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="half_cheetah.xml", - frame_skip=5, - default_camera_config=DEFAULT_CAMERA_CONFIG, - forward_reward_weight=1.0, - ctrl_cost_weight=0.1, - reset_noise_scale=0.1, - exclude_current_positions_from_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - forward_reward_weight, - ctrl_cost_weight, - reset_noise_scale, - exclude_current_positions_from_observation, - **kwargs, - ) - - self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, - default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = ( - self.data.qpos.size - + self.data.qvel.size - - exclude_current_positions_from_observation - ) - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 1 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - } - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) - return control_cost - - def step(self, action): - x_position_before = self.data.qpos[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.data.qpos[0] - x_velocity = (x_position_after - x_position_before) / self.dt - - ctrl_cost = self.control_cost(action) - - forward_reward = self._forward_reward_weight * x_velocity - - observation = self._get_obs() - reward = forward_reward - ctrl_cost - info = { - "x_position": x_position_after, - "x_velocity": x_velocity, - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - } - - if self.render_mode == "human": - self.render() - return observation, reward, False, False, info - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - - if self._exclude_current_positions_from_observation: - position = position[1:] - - observation = np.concatenate((position, velocity)).ravel() - return observation - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = ( - self.init_qvel - + self._reset_noise_scale * self.np_random.standard_normal(self.model.nv) - ) - - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - } diff --git a/gymnasium_robotics/envs/mujoco/hopper_v5.py b/gymnasium_robotics/envs/mujoco/hopper_v5.py deleted file mode 100644 index b3d2b912..00000000 --- a/gymnasium_robotics/envs/mujoco/hopper_v5.py +++ /dev/null @@ -1,335 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 2, - "distance": 3.0, - "lookat": np.array((0.0, 0.0, 1.15)), - "elevation": -20.0, -} - - -class HopperEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment is based on the work done by Erez, Tassa, and Todorov in - ["Infinite Horizon Model Predictive Control for Nonlinear Periodic Tasks"](http://www.roboticsproceedings.org/rss07/p10.pdf). The environment aims to - increase the number of independent state and control variables as compared to - the classic control environments. The hopper is a two-dimensional - one-legged figure that consist of four main body parts - the torso at the - top, the thigh in the middle, the leg in the bottom, and a single foot on - which the entire body rests. The goal is to make hops that move in the - forward (right) direction by applying torques on the three hinges - connecting the four body parts. - - - ## Action Space - The action space is a `Box(-1, 1, (3,), float32)`. An action represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| - | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | - | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | - | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the - hopper, followed by the velocities of those individual parts - (their derivatives) with all the positions ordered before all the velocities. - - By default, observations do not include the x-coordinate of the hopper. It may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be `Box(-Inf, Inf, (12,), float64)` where the first observation - represents the x-coordinate of the hopper. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x-coordinate - will be returned in `info` with key `"x_position"`. - - However, by default, the observation is a `Box(-Inf, Inf, (11,), float64)` where the elements - correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | -------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | - | 0 | z-coordinate of the torso (height of hopper) | -Inf | Inf | rootz | slide | position (m) | - | 1 | angle of the torso | -Inf | Inf | rooty | hinge | angle (rad) | - | 2 | angle of the thigh joint | -Inf | Inf | thigh_joint | hinge | angle (rad) | - | 3 | angle of the leg joint | -Inf | Inf | leg_joint | hinge | angle (rad) | - | 4 | angle of the foot joint | -Inf | Inf | foot_joint | hinge | angle (rad) | - | 5 | velocity of the x-coordinate of the torso | -Inf | Inf | rootx | slide | velocity (m/s) | - | 6 | velocity of the z-coordinate (height) of the torso | -Inf | Inf | rootz | slide | velocity (m/s) | - | 7 | angular velocity of the angle of the torso | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | - | 8 | angular velocity of the thigh hinge | -Inf | Inf | thigh_joint | hinge | angular velocity (rad/s) | - | 9 | angular velocity of the leg hinge | -Inf | Inf | leg_joint | hinge | angular velocity (rad/s) | - | 10 | angular velocity of the foot hinge | -Inf | Inf | foot_joint | hinge | angular velocity (rad/s) | - | excluded | x-coordinate of the torso | -Inf | Inf | rootx | slide | position (m) | - - - ## Rewards - The reward consists of three parts: - - *healthy_reward*: - Every timestep that the Hopper is healthy (see definition in section "Episode Termination"), - it gets a reward of fixed value `healthy_reward`. - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Hopper moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the "torso" ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), - and `frametime` which is 0.002 - making the default $dt = 4 \times 0.002 = 0.008$, - $w_{forward}$ is the `forward_reward_weight` (default is $1$). - - *ctrl_cost*: - A negative reward for penalizing the Hopper if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-3}$). - - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost*, - and `info` will also contain the individual reward terms. - - ## Starting State - All observations start in state - (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise - in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. - - - ## Episode End - #### Termination - If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Hopper is unhealthy. - The Hopper is unhealthy if any of the following happens: - - 1. An element of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) is no longer contained in the closed interval specified by the argument `healthy_state_range` - 2. The height of the hopper (`observation[0]` if `exclude_current_positions_from_observation=True`, else `observation[1]`) is no longer contained in the closed interval specified by the argument `healthy_z_range` (usually meaning that it has fallen) - 3. The angle of the torso (`observation[1]` if `exclude_current_positions_from_observation=True`, else `observation[2]`) is no longer contained in the closed interval specified by the argument `healthy_angle_range` - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Hopper-v5', ctrl_cost_weight=1e-3, ....) - ``` - - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"hopper_v5.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctrl_cost_ reward (see section on reward) | - | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | - | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the hopper is no longer healthy | - | `healthy_state_range` | **tuple** | `(-100, 100)` | The elements of `observation[1:]` (if `exclude_current_positions_from_observation=True`, else `observation[2:]`) must be in this range for the hopper to be considered healthy | - | `healthy_z_range` | **tuple** | `(0.7, float("inf"))` | The z-coordinate must be in this range for the hopper to be considered healthy | - | `healthy_angle_range` | **tuple** | `(-0.2, 0.2)` | The angle given by `observation[1]` (if `exclude_current_positions_from_observation=True`, else `observation[2]`) must be in this range for the hopper to be considered healthy | - | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Hopper gets a new model, the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) - * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0). - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="hopper_v5.xml", - frame_skip=4, - default_camera_config=DEFAULT_CAMERA_CONFIG, - forward_reward_weight=1.0, - ctrl_cost_weight=1e-3, - healthy_reward=1.0, - terminate_when_unhealthy=True, - healthy_state_range=(-100.0, 100.0), - healthy_z_range=(0.7, float("inf")), - healthy_angle_range=(-0.2, 0.2), - reset_noise_scale=5e-3, - exclude_current_positions_from_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - forward_reward_weight, - ctrl_cost_weight, - healthy_reward, - terminate_when_unhealthy, - healthy_state_range, - healthy_z_range, - healthy_angle_range, - reset_noise_scale, - exclude_current_positions_from_observation, - **kwargs, - ) - - self._forward_reward_weight = forward_reward_weight - - self._ctrl_cost_weight = ctrl_cost_weight - - self._healthy_reward = healthy_reward - self._terminate_when_unhealthy = terminate_when_unhealthy - - self._healthy_state_range = healthy_state_range - self._healthy_z_range = healthy_z_range - self._healthy_angle_range = healthy_angle_range - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = ( - self.data.qpos.size - + self.data.qvel.size - - exclude_current_positions_from_observation - ) - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 1 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - } - - @property - def healthy_reward(self): - return self.is_healthy * self._healthy_reward - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) - return control_cost - - @property - def is_healthy(self): - z, angle = self.data.qpos[1:3] - state = self.state_vector()[2:] - - min_state, max_state = self._healthy_state_range - min_z, max_z = self._healthy_z_range - min_angle, max_angle = self._healthy_angle_range - - healthy_state = np.all(np.logical_and(min_state < state, state < max_state)) - healthy_z = min_z < z < max_z - healthy_angle = min_angle < angle < max_angle - - is_healthy = all((healthy_state, healthy_z, healthy_angle)) - - return is_healthy - - @property - def terminated(self): - terminated = (not self.is_healthy) and self._terminate_when_unhealthy - # TODO remove after validation - assert terminated == ( - not self.is_healthy if self._terminate_when_unhealthy else False - ) - return terminated - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = np.clip(self.data.qvel.flat.copy(), -10, 10) - - if self._exclude_current_positions_from_observation: - position = position[1:] - - observation = np.concatenate((position, velocity)).ravel() - return observation - - def step(self, action): - x_position_before = self.data.qpos[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.data.qpos[0] - x_velocity = (x_position_after - x_position_before) / self.dt - - ctrl_cost = self.control_cost(action) - - forward_reward = self._forward_reward_weight * x_velocity - healthy_reward = self.healthy_reward - - rewards = forward_reward + healthy_reward - costs = ctrl_cost - - observation = self._get_obs() - reward = rewards - costs - terminated = self.terminated - info = { - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "reward_survive": healthy_reward, - "x_position": x_position_after, - "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], - "x_velocity": x_velocity, - } - - if self.render_mode == "human": - self.render() - return observation, reward, terminated, False, info - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = self.init_qvel + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nv - ) - - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def viewer_setup(self): - assert self.viewer is not None - for key, value in DEFAULT_CAMERA_CONFIG.items(): - if isinstance(value, np.ndarray): - getattr(self.viewer.cam, key)[:] = value - else: - setattr(self.viewer.cam, key, value) - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], - } diff --git a/gymnasium_robotics/envs/mujoco/humanoid_v5.py b/gymnasium_robotics/envs/mujoco/humanoid_v5.py deleted file mode 100644 index b45afc80..00000000 --- a/gymnasium_robotics/envs/mujoco/humanoid_v5.py +++ /dev/null @@ -1,524 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 1, - "distance": 4.0, - "lookat": np.array((0.0, 0.0, 2.0)), - "elevation": -20.0, -} - - -def mass_center(model, data): - mass = np.expand_dims(model.body_mass, axis=1) - xpos = data.xipos - return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() - - -class HumanoidEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment is based on the environment introduced by Tassa, Erez and Todorov - in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). - The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a pair of - legs and arms. The legs each consist of three body parts, and the arms 2 body parts (representing the knees and - elbows respectively). The goal of the environment is to walk forward as fast as possible without falling over. - - - ## Action Space - The action space is a `Box(-1, 1, (17,), float32)`. An action represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|----------------------|---------------|----------------|---------------------------------------|-------|------| - | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | - | 1 | Torque applied on the hinge in the z-coordinate of the abdomen | -0.4 | 0.4 | abdomen_z | hinge | torque (N m) | - | 2 | Torque applied on the hinge in the x-coordinate of the abdomen | -0.4 | 0.4 | abdomen_x | hinge | torque (N m) | - | 3 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | - | 4 | Torque applied on the rotor between torso/abdomen and the right hip (z-coordinate) | -0.4 | 0.4 | right_hip_z (right_thigh) | hinge | torque (N m) | - | 5 | Torque applied on the rotor between torso/abdomen and the right hip (y-coordinate) | -0.4 | 0.4 | right_hip_y (right_thigh) | hinge | torque (N m) | - | 6 | Torque applied on the rotor between the right hip/thigh and the right shin | -0.4 | 0.4 | right_knee | hinge | torque (N m) | - | 7 | Torque applied on the rotor between torso/abdomen and the left hip (x-coordinate) | -0.4 | 0.4 | left_hip_x (left_thigh) | hinge | torque (N m) | - | 8 | Torque applied on the rotor between torso/abdomen and the left hip (z-coordinate) | -0.4 | 0.4 | left_hip_z (left_thigh) | hinge | torque (N m) | - | 9 | Torque applied on the rotor between torso/abdomen and the left hip (y-coordinate) | -0.4 | 0.4 | left_hip_y (left_thigh) | hinge | torque (N m) | - | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | - | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | - | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | - | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | - | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | - | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | - | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the Humanoid, - followed by the velocities of those individual parts (their derivatives) with all the - positions ordered before all the velocities. - - By default, observations do not include the x- and y-coordinates of the torso. These may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (350,), float64)` where the first two observations - represent the x- and y-coordinates of the torso. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates - will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - - However, by default, the observation is a `Box(-Inf, Inf, (348,), float64)`. The elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | - | 0 | z-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - | 1 | x-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 2 | y-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 3 | z-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 4 | w-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 5 | z-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | angle (rad) | - | 6 | y-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | angle (rad) | - | 7 | x-angle of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | angle (rad) | - | 8 | x-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | angle (rad) | - | 9 | z-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | angle (rad) | - | 10 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | - | 11 | angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | angle (rad) | - | 12 | x-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | angle (rad) | - | 13 | z-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | angle (rad) | - | 14 | y-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | angle (rad) | - | 15 | angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | angle (rad) | - | 16 | coordinate-1 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | angle (rad) | - | 17 | coordinate-2 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | angle (rad) | - | 18 | angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | angle (rad) | - | 19 | coordinate-1 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | angle (rad) | - | 20 | coordinate-2 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | angle (rad) | - | 21 | angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | angle (rad) | - | 22 | x-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 23 | y-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 24 | z-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 25 | x-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 26 | y-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 27 | z-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 28 | z-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | anglular velocity (rad/s) | - | 29 | y-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | anglular velocity (rad/s) | - | 30 | x-coordinate of angular velocity of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | aanglular velocity (rad/s) | - | 31 | x-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | anglular velocity (rad/s) | - | 32 | z-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | anglular velocity (rad/s) | - | 33 | y-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | anglular velocity (rad/s) | - | 34 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | - | 35 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | - | 36 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | - | 37 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | - | 38 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | - | 39 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | - | 40 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | - | 41 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | - | 42 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | - | 43 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | - | 44 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | - | excluded | x-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - | excluded | y-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - - Additionally, after all the positional and velocity based values in the table, - the observation contains (in order): - - *cinert:* Mass and inertia of a single rigid body relative to the center of mass - (this is an intermediate result of transition). It has shape 13*10 (*nbody * 10*) - and hence adds to another 130 elements in the state space. - - *cvel:* Center of mass based velocity. It has shape 13 * 6 (*nbody * 6*) and hence - adds another 78 elements in the state space - - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape - `(17,)` *(nv * 1)* and hence adds another 17 elements to the state space. - - *cfrc_ext:* This is the center of mass based external force on the body. It has shape - 13 * 6 (*nbody * 6*) and hence adds to another 78 elements in the state space. - where *nbody* stands for the number of bodies in the robot and *nv* stands for the - number of degrees of freedom (*= dim(qvel)*) - - The body parts are: - - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | - | ---| --- | ------------ | - | 0 |excluded| worldbody (note: all values are constant 0) | - | 1 | 0 | torso | - | 2 | 1 | lwaist | - | 3 | 2 | pelvis | - | 4 | 3 | right_thigh | - | 5 | 4 | right_sin | - | 6 | 5 | right_foot | - | 7 | 6 | left_thigh | - | 8 | 7 | left_sin | - | 9 | 8 | left_foot | - | 10 | 9 | right_upper_arm | - | 11 | 10 | right_lower_arm | - | 12 | 11 | left_upper_arm | - | 13 | 12 | left_lower_arm | - - The joints are: - - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | joint | - | ---| --- | ------------ | - | 0 |excluded| root (note: all values are constant 0) | - | 1 |excluded| root (note: all values are constant 0) | - | 2 |excluded| root (note: all values are constant 0) | - | 3 |excluded| root (note: all values are constant 0) | - | 4 |excluded| root (note: all values are constant 0) | - | 5 |excluded| root (note: all values are constant 0) | - | 6 | 0 | abdomen_z | - | 7 | 1 | abdomen_y | - | 8 | 2 | abdomen_x | - | 9 | 3 | right_hip_x | - | 10 | 4 | right_hip_z | - | 11 | 5 | right_hip_y | - | 12 | 6 | right_knee | - | 13 | 7 | left_hip_x | - | 14 | 8 | left_hiz_z | - | 15 | 9 | left_hip_y | - | 16 | 10 | left_knee | - | 17 | 11 | right_shoulder1 | - | 18 | 12 | right_shoulder2 | - | 19 | 13 | right_elbow| - | 20 | 14 | left_shoulder1 | - | 21 | 15 | left_shoulder2 | - | 22 | 16 | left_elfbow | - - The (x,y,z) coordinates are translational DOFs while the orientations are rotational - DOFs expressed as quaternions. One can read more about free joints on the - [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). - - **Note:** Humanoid-v4 environment no longer has the following contact forces issue. - If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 - results in the contact forces always being 0. As such we recommend to use a Mujoco-Py - version < 2.0 when using the Humanoid environment if you would like to report results - with contact forces (if contact forces are not used in your experiments, you can use - version > 2.0). - - - ## Rewards - The reward consists of three parts: - - *healthy_reward*: - Every timestep that the Humanoid is alive (see section Episode Termination for definition), - it gets a reward of fixed value `healthy_reward`. - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Humanoid moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the center of mass ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 5), - and `frametime` which is 0.001 - making the default $dt = 5 \times 0.003 = 0.015$, - $w_{forward}$ is the `forward_reward_weight` (default is $1.25$). - The calculation for the center of mass is defined in the `.py` file for the Humanoid. - - *ctrl_cost*: - A negative reward for penalizing the Humanoid if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $0.1$). - If there are *nu* actuators/controls, then the control has shape `nu x 1`. - - *contact_cost*: - A negative reward for penalizing the Humanoid if the external contact forces are too large. - $w_{contact} \times clamp(contact\\_cost\\_range, \\|F_{contact}\\|_2^2)$, where - $w_{contact}$ is `contact_cost_weight` (default is $5\times10^{-7}$), - $F_{contact}$ are the external contact forces (see `cfrc_ext` section on observation). - - The total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost - contact_cost* - and `info` will also contain the individual reward terms. - - Note: in `v4` the total reward returned is ***reward*** *=* *healthy_reward + forward_reward - ctrl_cost* - - - ## Starting State - All observations start in state - (0.0, 0.0, 1.4, 1.0, 0.0 ... 0.0) with a uniform noise in the range - of [-`reset_noise_scale`, `reset_noise_scale`] added to the positional and velocity values (values in the table) - for stochasticity. Note that the initial z coordinate is intentionally - selected to be high, thereby indicating a standing up humanoid. The initial - orientation is designed to make it face forward as well. - - - ## Episode End - #### Termination - If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Humanoid is unhealthy. - The Humanoid is said to be unhealthy if any of the following happens: - - 1. The z-position of the torso (the height) is no longer contained in `healthy_z_range`. - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Humanoid-v5', ctrl_cost_weight=0.1, ....) - ``` - - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"humanoid.xml"` | Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1.25` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `0.1` | Weight for _ctrl_cost_ term (see section on reward) | - | `contact_cost_weight` | **float** | `5e-7` | Weight for _contact_cost_ term (see section on reward) | - | `contact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _contact_cost_ term (see section on reward) | - | `healthy_reward` | **float** | `5.0` | Weight for _healthy_reward_ term (see section on reward) | - | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the torso is no longer in the `healthy_z_range` | - | `healthy_z_range` | **tuple** | `(1.0, 2.0)` | The humanoid is considered healthy if the z-coordinate of the torso is in this range | - | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations. | - | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | - | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | - | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | - - ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) - * v2: All continuous control environments now use mujoco-py >= 1.50 - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="humanoid.xml", - frame_skip=5, - default_camera_config=DEFAULT_CAMERA_CONFIG, - forward_reward_weight=1.25, - ctrl_cost_weight=0.1, - contact_cost_weight=5e-7, - contact_cost_range=(-np.inf, 10.0), - healthy_reward=5.0, - terminate_when_unhealthy=True, - healthy_z_range=(1.0, 2.0), - reset_noise_scale=1e-2, - exclude_current_positions_from_observation=True, - include_cinert_in_observation=True, - include_cvel_in_observation=True, - include_qfrc_actuator_in_observation=True, - include_cfrc_ext_in_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - forward_reward_weight, - ctrl_cost_weight, - contact_cost_weight, - contact_cost_range, - healthy_reward, - terminate_when_unhealthy, - healthy_z_range, - reset_noise_scale, - exclude_current_positions_from_observation, - include_cinert_in_observation, - include_cvel_in_observation, - include_qfrc_actuator_in_observation, - include_cfrc_ext_in_observation, - **kwargs, - ) - - self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight - self._contact_cost_weight = contact_cost_weight - self._contact_cost_range = contact_cost_range - self._healthy_reward = healthy_reward - self._terminate_when_unhealthy = terminate_when_unhealthy - self._healthy_z_range = healthy_z_range - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - self._include_cinert_in_observation = include_cinert_in_observation - self._include_cvel_in_observation = include_cvel_in_observation - self._include_qfrc_actuator_in_observation = ( - include_qfrc_actuator_in_observation - ) - self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = self.data.qpos.size + self.data.qvel.size - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += self.data.cinert[1:].size * include_cinert_in_observation - obs_size += self.data.cvel[1:].size * include_cvel_in_observation - obs_size += (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation - obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation - - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 2 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - "cinert": self.data.cinert[1:].size * include_cinert_in_observation, - "cvel": self.data.cvel[1:].size * include_cvel_in_observation, - "qfrc_actuator": (self.data.qvel.size - 6) - * include_qfrc_actuator_in_observation, - "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, - "ten_lenght": 0, - "ten_velocity": 0, - } - - @property - def healthy_reward(self): - return self.is_healthy * self._healthy_reward - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(self.data.ctrl)) - return control_cost - - @property - def contact_cost(self): - contact_forces = self.data.cfrc_ext - contact_cost = self._contact_cost_weight * np.sum(np.square(contact_forces)) - min_cost, max_cost = self._contact_cost_range - contact_cost = np.clip(contact_cost, min_cost, max_cost) - return contact_cost - - @property - def is_healthy(self): - min_z, max_z = self._healthy_z_range - is_healthy = min_z < self.data.qpos[2] < max_z - - return is_healthy - - @property - def terminated(self): - terminated = (not self.is_healthy) and self._terminate_when_unhealthy - # TODO remove after validation - assert terminated == ( - not self.is_healthy if self._terminate_when_unhealthy else False - ) - return terminated - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - - if self._include_cinert_in_observation is True: - com_inertia = self.data.cinert[1:].flat.copy() - else: - com_inertia = np.array([]) - if self._include_cvel_in_observation is True: - com_velocity = self.data.cvel[1:].flat.copy() - else: - com_velocity = np.array([]) - - if self._include_qfrc_actuator_in_observation is True: - actuator_forces = self.data.qfrc_actuator[6:].flat.copy() - else: - actuator_forces = np.array([]) - if self._include_cfrc_ext_in_observation is True: - external_contact_forces = self.data.cfrc_ext[1:].flat.copy() - else: - external_contact_forces = np.array([]) - - # TODO remove after validation - assert (self.data.cinert[0].flat.copy() == 0).all() - assert (self.data.cvel[0].flat.copy() == 0).all() - assert (self.data.qfrc_actuator[:6].flat.copy() == 0).all() - assert (self.data.cfrc_ext[0].flat.copy() == 0).all() - - if self._exclude_current_positions_from_observation: - position = position[2:] - - return np.concatenate( - ( - position, - velocity, - com_inertia, - com_velocity, - actuator_forces, - external_contact_forces, - ) - ) - - def step(self, action): - xy_position_before = mass_center(self.model, self.data) - self.do_simulation(action, self.frame_skip) - xy_position_after = mass_center(self.model, self.data) - - xy_velocity = (xy_position_after - xy_position_before) / self.dt - x_velocity, y_velocity = xy_velocity - - ctrl_cost = self.control_cost(action) - contact_cost = self.contact_cost - costs = ctrl_cost + contact_cost - - forward_reward = self._forward_reward_weight * x_velocity - healthy_reward = self.healthy_reward - - rewards = forward_reward + healthy_reward - - observation = self._get_obs() - reward = rewards - costs - terminated = self.terminated - info = { - "reward_survive": healthy_reward, - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "reward_contact": -contact_cost, - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_velocity, - "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), - "x_velocity": x_velocity, - "y_velocity": y_velocity, - } - - if self.render_mode == "human": - self.render() - return observation, reward, terminated, False, info - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = self.init_qvel + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nv - ) - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_velocity, - "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), - } diff --git a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py b/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py deleted file mode 100644 index ba13b9f1..00000000 --- a/gymnasium_robotics/envs/mujoco/humanoidstandup_v5.py +++ /dev/null @@ -1,469 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 1, - "distance": 4.0, - "lookat": np.array((0.0, 0.0, 0.8925)), - "elevation": -20.0, -} - - -class HumanoidStandupEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment is based on the environment introduced by Tassa, Erez and Todorov - in ["Synthesis and stabilization of complex behaviors through online trajectory optimization"](https://ieeexplore.ieee.org/document/6386025). - The 3D bipedal robot is designed to simulate a human. It has a torso (abdomen) with a - pair of legs and arms. The legs each consist of two links, and so the arms (representing the - knees and elbows respectively). The environment starts with the humanoid laying on the ground, - and then the goal of the environment is to make the humanoid standup and then keep it standing - by applying torques on the various hinges. - - - ## Action Space - The agent take a 17-element vector for actions. - - The action space is a continuous `(action, ...)` all in `[-1, 1]`, where `action` - represents the numerical torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ---------------------------------------------------------------------------------- | ----------- | ----------- | -------------------------------- | ----- | ------------ | - | 0 | Torque applied on the hinge in the y-coordinate of the abdomen | -0.4 | 0.4 | abdomen_y | hinge | torque (N m) | - | 1 | Torque applied on the hinge in the z-coordinate of the abdomen | -0.4 | 0.4 | abdomen_z | hinge | torque (N m) | - | 2 | Torque applied on the hinge in the x-coordinate of the abdomen | -0.4 | 0.4 | abdomen_x | hinge | torque (N m) | - | 3 | Torque applied on the rotor between torso/abdomen and the right hip (x-coordinate) | -0.4 | 0.4 | right_hip_x (right_thigh) | hinge | torque (N m) | - | 4 | Torque applied on the rotor between torso/abdomen and the right hip (z-coordinate) | -0.4 | 0.4 | right_hip_z (right_thigh) | hinge | torque (N m) | - | 5 | Torque applied on the rotor between torso/abdomen and the right hip (y-coordinate) | -0.4 | 0.4 | right_hip_y (right_thigh) | hinge | torque (N m) | - | 6 | Torque applied on the rotor between the right hip/thigh and the right shin | -0.4 | 0.4 | right_knee | hinge | torque (N m) | - | 7 | Torque applied on the rotor between torso/abdomen and the left hip (x-coordinate) | -0.4 | 0.4 | left_hip_x (left_thigh) | hinge | torque (N m) | - | 8 | Torque applied on the rotor between torso/abdomen and the left hip (z-coordinate) | -0.4 | 0.4 | left_hip_z (left_thigh) | hinge | torque (N m) | - | 9 | Torque applied on the rotor between torso/abdomen and the left hip (y-coordinate) | -0.4 | 0.4 | left_hip_y (left_thigh) | hinge | torque (N m) | - | 10 | Torque applied on the rotor between the left hip/thigh and the left shin | -0.4 | 0.4 | left_knee | hinge | torque (N m) | - | 11 | Torque applied on the rotor between the torso and right upper arm (coordinate -1) | -0.4 | 0.4 | right_shoulder1 | hinge | torque (N m) | - | 12 | Torque applied on the rotor between the torso and right upper arm (coordinate -2) | -0.4 | 0.4 | right_shoulder2 | hinge | torque (N m) | - | 13 | Torque applied on the rotor between the right upper arm and right lower arm | -0.4 | 0.4 | right_elbow | hinge | torque (N m) | - | 14 | Torque applied on the rotor between the torso and left upper arm (coordinate -1) | -0.4 | 0.4 | left_shoulder1 | hinge | torque (N m) | - | 15 | Torque applied on the rotor between the torso and left upper arm (coordinate -2) | -0.4 | 0.4 | left_shoulder2 | hinge | torque (N m) | - | 16 | Torque applied on the rotor between the left upper arm and left lower arm | -0.4 | 0.4 | left_elbow | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the Humanoid, - followed by the velocities of those individual parts (their derivatives) with all the - positions ordered before all the velocities. - - By default, observations do not include the x- and y-coordinates of the torso. These may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be a `Box(-Inf, Inf, (350,), float64)` where the first two observations - represent the x- and y-coordinates of the torso. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates - will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - - However, by default, the observation is a `Box(-Inf, Inf, (348,), float64)`. The elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | --------------------------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | -------------------------- | - | 0 | z-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - | 1 | x-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 2 | y-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 3 | z-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 4 | w-orientation of the torso (centre) | -Inf | Inf | root | free | angle (rad) | - | 5 | z-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | angle (rad) | - | 6 | y-angle of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | angle (rad) | - | 7 | x-angle of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | angle (rad) | - | 8 | x-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | angle (rad) | - | 9 | z-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | angle (rad) | - | 10 | y-coordinate of angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | angle (rad) | - | 11 | angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | angle (rad) | - | 12 | x-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | angle (rad) | - | 13 | z-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | angle (rad) | - | 14 | y-coordinate of angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | angle (rad) | - | 15 | angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | angle (rad) | - | 16 | coordinate-1 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | angle (rad) | - | 17 | coordinate-2 (multi-axis) angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | angle (rad) | - | 18 | angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | angle (rad) | - | 19 | coordinate-1 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | angle (rad) | - | 20 | coordinate-2 (multi-axis) angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | angle (rad) | - | 21 | angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | angle (rad) | - | 22 | x-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 23 | y-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 24 | z-coordinate velocity of the torso (centre) | -Inf | Inf | root | free | velocity (m/s) | - | 25 | x-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 26 | y-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 27 | z-coordinate angular velocity of the torso (centre) | -Inf | Inf | root | free | anglular velocity (rad/s) | - | 28 | z-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_z | hinge | anglular velocity (rad/s) | - | 29 | y-coordinate of angular velocity of the abdomen (in lower_waist) | -Inf | Inf | abdomen_y | hinge | anglular velocity (rad/s) | - | 30 | x-coordinate of angular velocity of the abdomen (in pelvis) | -Inf | Inf | abdomen_x | hinge | aanglular velocity (rad/s) | - | 31 | x-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_x | hinge | anglular velocity (rad/s) | - | 32 | z-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_z | hinge | anglular velocity (rad/s) | - | 33 | y-coordinate of the angular velocity of the angle between pelvis and right hip (in right_thigh) | -Inf | Inf | right_hip_y | hinge | anglular velocity (rad/s) | - | 34 | angular velocity of the angle between right hip and the right shin (in right_knee) | -Inf | Inf | right_knee | hinge | anglular velocity (rad/s) | - | 35 | x-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_x | hinge | anglular velocity (rad/s) | - | 36 | z-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_z | hinge | anglular velocity (rad/s) | - | 37 | y-coordinate of the angular velocity of the angle between pelvis and left hip (in left_thigh) | -Inf | Inf | left_hip_y | hinge | anglular velocity (rad/s) | - | 38 | angular velocity of the angle between left hip and the left shin (in left_knee) | -Inf | Inf | left_knee | hinge | anglular velocity (rad/s) | - | 39 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder1 | hinge | anglular velocity (rad/s) | - | 40 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and right arm (in right_upper_arm) | -Inf | Inf | right_shoulder2 | hinge | anglular velocity (rad/s) | - | 41 | angular velocity of the angle between right upper arm and right_lower_arm | -Inf | Inf | right_elbow | hinge | anglular velocity (rad/s) | - | 42 | coordinate-1 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder1 | hinge | anglular velocity (rad/s) | - | 43 | coordinate-2 (multi-axis) of the angular velocity of the angle between torso and left arm (in left_upper_arm) | -Inf | Inf | left_shoulder2 | hinge | anglular velocity (rad/s) | - | 44 | angular velocity of the angle between left upper arm and left_lower_arm | -Inf | Inf | left_elbow | hinge | anglular velocity (rad/s) | - | excluded | x-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - | excluded | y-coordinate of the torso (centre) | -Inf | Inf | root | free | position (m) | - - Additionally, after all the positional and velocity based values in the table, - the observation contains (in order): - - *cinert:* Mass and inertia of a single rigid body relative to the center of mass - (this is an intermediate result of transition). It has shape 13*10 (*nbody * 10*) - and hence adds to another 130 elements in the state space. - - *cvel:* Center of mass based velocity. It has shape 13 * 6 (*nbody * 6*) and hence - adds another 78 elements in the state space - - *qfrc_actuator:* Constraint force generated as the actuator force. This has shape - `(17,)` *(nv * 1)* and hence adds another 17 elements to the state space. - - *cfrc_ext:* This is the center of mass based external force on the body. It has shape - 13 * 6 (*nbody * 6*) and hence adds to another 78 elements in the state space. - where *nbody* stands for the number of bodies in the robot and *nv* stands for the - number of degrees of freedom (*= dim(qvel)*) - - The body parts are: - - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | body part | - | ---| --- | ------------ | - | 0 |excluded| worldbody (note: all values are constant 0) | - | 1 | 0 | torso | - | 2 | 1 | lwaist | - | 3 | 2 | pelvis | - | 4 | 3 | right_thigh | - | 5 | 4 | right_sin | - | 6 | 5 | right_foot | - | 7 | 6 | left_thigh | - | 8 | 7 | left_sin | - | 9 | 8 | left_foot | - | 10 | 9 | right_upper_arm | - | 11 | 10 | right_lower_arm | - | 12 | 11 | left_upper_arm | - | 13 | 12 | left_lower_arm | - - The joints are: - - | id (for `v2`, `v3`, `v4)` | id (for `v5`) | joint | - | ---| --- | ------------ | - | 0 |excluded| root (note: all values are constant 0) | - | 1 |excluded| root (note: all values are constant 0) | - | 2 |excluded| root (note: all values are constant 0) | - | 3 |excluded| root (note: all values are constant 0) | - | 4 |excluded| root (note: all values are constant 0) | - | 5 |excluded| root (note: all values are constant 0) | - | 6 | 0 | abdomen_z | - | 7 | 1 | abdomen_y | - | 8 | 2 | abdomen_x | - | 9 | 3 | right_hip_x | - | 10 | 4 | right_hip_z | - | 11 | 5 | right_hip_y | - | 12 | 6 | right_knee | - | 13 | 7 | left_hip_x | - | 14 | 8 | left_hiz_z | - | 15 | 9 | left_hip_y | - | 16 | 10 | left_knee | - | 17 | 11 | right_shoulder1 | - | 18 | 12 | right_shoulder2 | - | 19 | 13 | right_elbow| - | 20 | 14 | left_shoulder1 | - | 21 | 15 | left_shoulder2 | - | 22 | 16 | left_elfbow | - - The (x,y,z) coordinates are translational DOFs while the orientations are rotational - DOFs expressed as quaternions. One can read more about free joints on the - [Mujoco Documentation](https://mujoco.readthedocs.io/en/latest/XMLreference.html). - - **Note:** Humanoid-v4 environment no longer has the following contact forces issue. - If using previous Humanoid versions from v4, there have been reported issues that using a Mujoco-Py version > 2.0 - results in the contact forces always being 0. As such we recommend to use a Mujoco-Py - version < 2.0 when using the Humanoid environment if you would like to report results - with contact forces (if contact forces are not used in your experiments, you can use - version > 2.0). - - - ## Rewards - The reward consists of three parts: - - *uph_cost*: - A reward for moving upward (in an attempt to stand up). - This is not a relative reward which measures how much upward it has moved from the last timestep, - but it is an absolute reward which measures how much upward the Humanoid has moved overall. - It is measured as $weight_{uph} \times (z_{after action} - 0)/dt$, - where $z_{after action}$ is the z coordinate of the torso after taking an action, - and *dt* is the time between actions and is dependent on the `frame_skip` parameter - (default is 5), where the frametime is 0.003 - making the default *dt = 5 * 0.003 = 0.015*. - and $weight_{uph}$ is `uph_cost_weight`. - - *quad_ctrl_cost*: - A negative reward for penalizing the Humanoid if it takes actions that are too large. - $w_{quad_control} \times \\|action\\|_2^2$, - where $w_{quad_control}$ is `ctrl_cost_weight` (default is $0.1$). - If there are *nu* actuators/controls, then the control has shape `nu x 1`. - - *impact_cost*: - A negative reward for penalizing the Humanoid if the external contact forces are too large. - $w_{impact} \times clamp(impact\\_cost\\_range, \\|F_{contact}\\|_2^2)$, where - $w_{impact}$ is `impact_cost_weight` (default is $5\times10^{-7}$), - $F_{contact}$ are the external contact forces (see `cfrc_ext` section on observation). - - The total reward returned is ***reward*** *=* *uph_cost + 1 - quad_ctrl_cost - quad_impact_cost*, - and `info` will also contain the individual reward terms. - - - ## Starting State - All observations start in state - (0.0, 0.0, 0.105, 1.0, 0.0 ... 0.0) with a uniform noise in the range of - [-0.01, 0.01] added to the positional and velocity values (values in the table) - for stochasticity. Note that the initial z coordinate is intentionally selected - to be low, thereby indicating a laying down humanoid. The initial orientation is - designed to make it face forward as well. - - - ## Episode End - #### Termination - The Humanoid never terminates. - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('HumanoidStandup-v5', impact_cost_weight=0.5e-6, ....) - ``` - - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** | `"humanoidstandup.xml"` | Path to a MuJoCo model | - | `uph_cost_weight` | **float** | `1` | Weight for _uph_cost_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `0.1` | Weight for _quad_ctrl_cost_ term (see section on reward) | - | `impact_cost_weight` | **float** | `0.5e-6` | Weight for _impact_cost_ term (see section on reward) | - | `impact_cost_range` | **float** | `(-np.inf, 10.0) | Clamps the _impact_cost_ | - | `reset_noise_scale` | **float** | `1e-2` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - | `include_cinert_in_observation` | **bool** | `True` | Whether to include *cinert* elements in the observations. | - | `include_cvel_in_observation` | **bool** | `True` | Whether to include *cvel* elements in the observations. | - | `include_qfrc_actuator_in_observation` | **bool** | `True` | Whether to include *qfrc_actuator* elements in the observations. | - | `include_cfrc_ext_in_observation` | **bool** | `True` | Whether to include *cfrc_ext* elements in the observations. | - - ## Version History - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. - * v3: This environment does not have a v3 release. - * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0). - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="humanoidstandup.xml", - frame_skip=5, - default_camera_config=DEFAULT_CAMERA_CONFIG, - uph_cost_weight=1, - ctrl_cost_weight=0.1, - impact_cost_weight=0.5e-6, - impact_cost_range=(-np.inf, 10.0), - reset_noise_scale=1e-2, - exclude_current_positions_from_observation=True, - include_cinert_in_observation=True, - include_cvel_in_observation=True, - include_qfrc_actuator_in_observation=True, - include_cfrc_ext_in_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - uph_cost_weight, - ctrl_cost_weight, - impact_cost_weight, - impact_cost_range, - reset_noise_scale, - exclude_current_positions_from_observation, - include_cinert_in_observation, - include_cvel_in_observation, - include_qfrc_actuator_in_observation, - include_cfrc_ext_in_observation, - **kwargs, - ) - - self._uph_cost_weight = uph_cost_weight - self._ctrl_cost_weight = ctrl_cost_weight - self._impact_cost_weight = impact_cost_weight - self._impact_cost_range = impact_cost_range - self._reset_noise_scale = reset_noise_scale - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - self._include_cinert_in_observation = include_cinert_in_observation - self._include_cvel_in_observation = include_cvel_in_observation - self._include_qfrc_actuator_in_observation = ( - include_qfrc_actuator_in_observation - ) - self._include_cfrc_ext_in_observation = include_cfrc_ext_in_observation - - obs_size = 47 - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += 130 * include_cinert_in_observation - obs_size += 78 * include_cvel_in_observation - obs_size += 17 * include_qfrc_actuator_in_observation - obs_size += 78 * include_cfrc_ext_in_observation - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = self.data.qpos.size + self.data.qvel.size - obs_size -= 2 * exclude_current_positions_from_observation - obs_size += self.data.cinert[1:].size * include_cinert_in_observation - obs_size += self.data.cvel[1:].size * include_cvel_in_observation - obs_size += (self.data.qvel.size - 6) * include_qfrc_actuator_in_observation - obs_size += self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation - - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 2 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - "cinert": self.data.cinert[1:].size * include_cinert_in_observation, - "cvel": self.data.cvel[1:].size * include_cvel_in_observation, - "qfrc_actuator": (self.data.qvel.size - 6) - * include_qfrc_actuator_in_observation, - "cfrc_ext": self.data.cfrc_ext[1:].size * include_cfrc_ext_in_observation, - "ten_lenght": 0, - "ten_velocity": 0, - } - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - - if self._include_cinert_in_observation is True: - com_inertia = self.data.cinert[1:].flat.copy() - else: - com_inertia = np.array([]) - if self._include_cvel_in_observation is True: - com_velocity = self.data.cvel[1:].flat.copy() - else: - com_velocity = np.array([]) - - if self._include_qfrc_actuator_in_observation is True: - actuator_forces = self.data.qfrc_actuator[6:].flat.copy() - else: - actuator_forces = np.array([]) - if self._include_cfrc_ext_in_observation is True: - external_contact_forces = self.data.cfrc_ext[1:].flat.copy() - else: - external_contact_forces = np.array([]) - - # TODO remove after validation - assert (self.data.cinert[0].flat.copy() == 0).all() - assert (self.data.cvel[0].flat.copy() == 0).all() - assert (self.data.qfrc_actuator[:6].flat.copy() == 0).all() - assert (self.data.cfrc_ext[0].flat.copy() == 0).all() - - if self._exclude_current_positions_from_observation: - position = position[2:] - - return np.concatenate( - ( - position, - velocity, - com_inertia, - com_velocity, - actuator_forces, - external_contact_forces, - ) - ) - - def step(self, action): - self.do_simulation(action, self.frame_skip) - pos_after = self.data.qpos[2] - - uph_cost = (pos_after - 0) / self.model.opt.timestep - - quad_ctrl_cost = self._ctrl_cost_weight * np.square(self.data.ctrl).sum() - - quad_impact_cost = ( - self._impact_cost_weight * np.square(self.data.cfrc_ext).sum() - ) - min_impact_cost, max_impact_cost = self._impact_cost_range - quad_impact_cost = np.clip(quad_impact_cost, min_impact_cost, max_impact_cost) - - reward = uph_cost - quad_ctrl_cost - quad_impact_cost + 1 - - info = { - "reward_linup": uph_cost, - "reward_quadctrl": -quad_ctrl_cost, - "reward_impact": -quad_impact_cost, - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "z_distance_from_origin": self.data.qpos[2] - self.init_qpos[2], - "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_velocity, - } - - if self.render_mode == "human": - self.render() - return self._get_obs(), reward, False, False, info - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = self.init_qvel + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nv - ) - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "z_distance_from_origin": self.data.qpos[2] - self.init_qpos[2], - "tendon_lenght": self.data.ten_length, - "tendon_velocity": self.data.ten_velocity, - } diff --git a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py deleted file mode 100644 index 765be745..00000000 --- a/gymnasium_robotics/envs/mujoco/inverted_double_pendulum_v5.py +++ /dev/null @@ -1,224 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 0, - "distance": 4.1225, - "lookat": np.array((0.0, 0.0, 0.12250000000000005)), -} - - -class InvertedDoublePendulumEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment originates from control theory and builds on the cartpole - environment based on the work done by Barto, Sutton, and Anderson in - ["Neuronlike adaptive elements that can solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), - powered by the Mujoco physics simulator - allowing for more complex experiments - (such as varying the effects of gravity or constraints). This environment involves a cart that can - moved linearly, with a pole fixed on it and a second pole fixed on the other end of the first one - (leaving the second pole as the only one with one free end). The cart can be pushed left or right, - and the goal is to balance the second pole on top of the first pole, which is in turn on top of the - cart, by applying continuous forces on the cart. - - - ## Action Space - The agent take a 1-element vector for actions. - The action space is a continuous `(action)` in `[-1, 1]`, where `action` represents the - numerical force applied to the cart (with magnitude representing the amount of force and - sign representing the direction) - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| - | 0 | Force applied on the cart | -1 | 1 | slider | slide | Force (N) | - - - ## Observation Space - The state space consists of positional values of different body parts of the pendulum system, - followed by the velocities of those individual parts (their derivatives) with all the - positions ordered before all the velocities. - - The observation is a `ndarray` with shape `(9,)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ----------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | - | 0 | position of the cart along the linear surface | -Inf | Inf | slider | slide | position (m) | - | 1 | sine of the angle between the cart and the first pole | -Inf | Inf | sin(hinge) | hinge | unitless | - | 2 | sine of the angle between the two poles | -Inf | Inf | sin(hinge2) | hinge | unitless | - | 3 | cosine of the angle between the cart and the first pole | -Inf | Inf | cos(hinge) | hinge | unitless | - | 4 | cosine of the angle between the two poles | -Inf | Inf | cos(hinge2) | hinge | unitless | - | 5 | velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | - | 6 | angular velocity of the angle between the cart and the first pole | -Inf | Inf | hinge | hinge | angular velocity (rad/s) | - | 7 | angular velocity of the angle between the two poles | -Inf | Inf | hinge2 | hinge | angular velocity (rad/s) | - | 8 | constraint force - x | -Inf | Inf | slider | slide | Force (N) | - | excluded | constraint force - y | -Inf | Inf | hinge | slide | Force (N) | - | excluded | constraint force - z | -Inf | Inf | hinge2 | slide | Force (N) | - - - There is physical contact between the robots and their environment - and Mujoco - attempts at getting realistic physics simulations for the possible physical contact - dynamics by aiming for physical accuracy and computational efficiency. - - There is one constraint force for contacts for each degree of freedom (3). - The approach and handling of constraints by Mujoco is unique to the simulator and is based on their research. - Once can find more information in their [*documentation*](https://mujoco.readthedocs.io/en/latest/computation.html) - or in their paper ["Analytically-invertible dynamics with contacts and constraints: Theory and implementation in MuJoCo"](https://homes.cs.washington.edu/~todorov/papers/TodorovICRA14.pdf). - - - ## Rewards - The reward consists of two parts: - - *alive_bonus*: - The goal is to make the second inverted pendulum stand upright - (within a certain angle limit) as long as possible - as such a reward of `healthy_reward` is awarded - for each timestep that the second pole is upright. - - *distance_penalty*: - This reward is a measure of how far the *tip* of the second pendulum (the only free end) moves, - and it is calculated as $0.01 x_{pole2-tip}^2 + (y_{pole2-tip}-2)^2$, - where $x_{pole2-tip}, y_{pole2-tip}$ are the xy-coordinatesof the tip of the second pole. - - *velocity_penalty*: - A negative reward for penalising the agent if it moves too fast. - $10^{-3} \omega_1 + 5 10^{-3} \omega_2$, - where $\omega_1, \omega_2$ are the angular velocities of the hinges. - - The total reward returned is ***reward*** *=* *alive_bonus - distance_penalty - velocity_penalty* - and `info` will also contain the individual reward terms. - - - ## Starting State - All observations start in state - (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) with a uniform noise in the range - of `[-reset_noise_scale, reset_noise_scale]` added to the positional values (cart position and pole angles) and standard - normal force with a standard deviation of `reset_noise_scale` added to the velocity values for stochasticity. - - - ## Episode End - #### Termination - The environment terminates when the Inverted Double Pendulum is unhealthy. - The Inverted Double Pendulum is unhealthy if any of the following happens: - - 1.Termination: The y_coordinate of the tip of the second pole $\leq 1$. - The maximum standing height of the system is 1.2 m when all the parts are perpendicularly vertical on top of each other). - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `healthy_reward`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('InvertedDoublePendulum-v5', healthy_reward=10, ...) - ``` - - | Parameter | Type | Default | Description | - |-------------------------|------------|-------------- |-------------------------------| - | `xml_file` | **str** |`"inverted_double_pendulum.xml"`| Path to a MuJoCo model | - | `healthy_reward` | **float** | `10 | Constant reward given if the pendulum is "healthy" (upright) | - | `reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward_survive" being 10 on every step (even on terminal steps). Removed "constraint force" of the hinges from the observation space. Added `info` "reward_survive", "distance_penalty", "velocity_penalty". - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: This environment does not have a v3 release. - * v2: All continuous control environments now use mujoco-py >= 1.50 - * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum) - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="inverted_double_pendulum.xml", - frame_skip=5, - healthy_reward=10.0, - reset_noise_scale=0.1, - **kwargs, - ): - utils.EzPickle.__init__(self, xml_file, frame_skip, reset_noise_scale, **kwargs) - - self._healthy_reward = healthy_reward - self._reset_noise_scale = reset_noise_scale - - observation_space = Box(low=-np.inf, high=np.inf, shape=(9,), dtype=np.float64) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - def step(self, action): - self.do_simulation(action, self.frame_skip) - - observation = self._get_obs() - - x, _, y = self.data.site_xpos[0] - v1, v2 = self.data.qvel[1:3] - - terminated = bool(y <= 1) - - dist_penalty = 0.01 * x**2 + (y - 2) ** 2 - vel_penalty = 1e-3 * v1**2 + 5e-3 * v2**2 - alive_bonus = self._healthy_reward * int(not terminated) - reward = alive_bonus - dist_penalty - vel_penalty - - info = { - "reward_survive": alive_bonus, - "distance_penalty": -dist_penalty, - "velocity_penalty": -vel_penalty, - } - - if self.render_mode == "human": - self.render() - return observation, reward, terminated, False, info - - def _get_obs(self): - assert self.data.qfrc_constraint[2] == 0 # TODO remove after validation - assert self.data.qfrc_constraint[1] == 0 # TODO remove after validation - return np.concatenate( - [ - self.data.qpos[:1], # cart x pos - np.sin(self.data.qpos[1:]), # link angles - np.cos(self.data.qpos[1:]), - np.clip(self.data.qvel, -10, 10), - np.clip(self.data.qfrc_constraint, -10, 10)[:1], - ] - ).ravel() - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - self.set_state( - self.init_qpos - + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ), - self.init_qvel - + self.np_random.standard_normal(self.model.nv) * self._reset_noise_scale, - ) - return self._get_obs() diff --git a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py b/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py deleted file mode 100644 index 6bb231b4..00000000 --- a/gymnasium_robotics/envs/mujoco/inverted_pendulum_v5.py +++ /dev/null @@ -1,179 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 0, - "distance": 2.04, -} - - -class InvertedPendulumEnv(MujocoEnv, utils.EzPickle): - """ - ## Description - This environment is the cartpole environment based on the work done by - Barto, Sutton, and Anderson in ["Neuronlike adaptive elements that can - solve difficult learning control problems"](https://ieeexplore.ieee.org/document/6313077), - just like in the classic environments but now powered by the Mujoco physics simulator - - allowing for more complex experiments (such as varying the effects of gravity). - This environment involves a cart that can moved linearly, with a pole fixed on it - at one end and having another end free. The cart can be pushed left or right, and the - goal is to balance the pole on the top of the cart by applying forces on the cart. - - - ## Action Space - The agent take a 1-element vector for actions. - - The action space is a continuous `(action)` in `[-3, 3]`, where `action` represents - the numerical force applied to the cart (with magnitude representing the amount of - force and sign representing the direction) - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|---------------------------|-------------|-------------|----------------------------------|-------|-----------| - | 0 | Force applied on the cart | -3 | 3 | slider | slide | Force (N) | - - - ## Observation Space - The state space consists of positional values of different body parts of - the pendulum system, followed by the velocities of those individual parts (their derivatives) - with all the positions ordered before all the velocities. - - The observation is a `ndarray` with shape `(4,)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | --------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------- | - | 0 | position of the cart along the linear surface | -Inf | Inf | slider | slide | position (m) | - | 1 | vertical angle of the pole on the cart | -Inf | Inf | hinge | hinge | angle (rad) | - | 2 | linear velocity of the cart | -Inf | Inf | slider | slide | velocity (m/s) | - | 3 | angular velocity of the pole on the cart | -Inf | Inf | hinge | hinge | anglular velocity (rad/s) | - - - ## Rewards - The goal is to make the inverted pendulum stand upright (within a certain angle limit) - as long as possible - as such a reward of +1 is awarded for each timestep that - the pole is upright. - - The pole is considered upright if: - $|angle| < 0.2$ - - and `info` will also contain the reward. - - - ## Starting State - All observations start in state - (0.0, 0.0, 0.0, 0.0) with a uniform noise in the range - of `[-reset_noise_scale, reset_noise_scale]` added to the values for stochasticity. - - - ## Episode End - #### Termination - The environment terminates when the Inverted Pendulum is unhealthy. - The Inverted Pendulum is unhealthy if any of the following happens: - - 1. Any of the state space values is no longer finite. - 2. The absolute value of the vertical angle between the pole and the cart is greater than 0.2 radian. - - #### Truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `reset_noise_scale`. - - ```python - import gymnasium as gym - env = gym.make('InvertedPendulum-v5', reset_noise_scale=0.1) - ``` - - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|-------------------------------| - | `xml_file` | **str** | `"inverted_double_pendulum.xml"` | Path to a MuJoCo model | - | `reset_noise_scale` | **float** | `0.01` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Fixed "reward survive" being 1 on every step (even on terminal steps) and added "reward_survive" to `info`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.. - * v3: This environment does not have a v3 release. - * v2: All continuous control environments now use mujoco-py >= 1.5. - * v1: max_time_steps raised to 1000 for robot based tasks (including inverted pendulum). - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="inverted_pendulum.xml", - frame_skip=2, - reset_noise_scale=0.01, - **kwargs, - ): - utils.EzPickle.__init__(self, xml_file, frame_skip, reset_noise_scale, **kwargs) - observation_space = Box(low=-np.inf, high=np.inf, shape=(4,), dtype=np.float64) - - self._reset_noise_scale = reset_noise_scale - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - self.observation_structure = { - "qpos": self.data.qpos.size, - "qvel": self.data.qvel.size, - } - - def step(self, action): - self.do_simulation(action, self.frame_skip) - - observation = self._get_obs() - - terminated = bool( - not np.isfinite(observation).all() or (np.abs(observation[1]) > 0.2) - ) - - reward = int(not terminated) - - info = {"reward_survive": reward} - - if self.render_mode == "human": - self.render() - return observation, reward, terminated, False, info - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - size=self.model.nq, low=noise_low, high=noise_high - ) - qvel = self.init_qvel + self.np_random.uniform( - size=self.model.nv, low=noise_low, high=noise_high - ) - self.set_state(qpos, qvel) - return self._get_obs() - - def _get_obs(self): - return np.concatenate([self.data.qpos, self.data.qvel]).ravel() diff --git a/gymnasium_robotics/envs/mujoco/pusher_v5.py b/gymnasium_robotics/envs/mujoco/pusher_v5.py deleted file mode 100644 index be9ed636..00000000 --- a/gymnasium_robotics/envs/mujoco/pusher_v5.py +++ /dev/null @@ -1,246 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": -1, - "distance": 4.0, -} - - -class PusherEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - "Pusher" is a multi-jointed robot arm which is very similar to that of a human. - The goal is to move a target cylinder (called *object*) to a goal position using the robot's end effector (called *fingertip*). - The robot consists of shoulder, elbow, forearm, and wrist joints. - - - ## Action Space - The action space is a `Box(-2, 2, (7,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|--------------------------------------------------------------------|-------------|-------------|----------------------------------|-------|--------------| - | 0 | Rotation of the panning the shoulder | -2 | 2 | r_shoulder_pan_joint | hinge | torque (N m) | - | 1 | Rotation of the shoulder lifting joint | -2 | 2 | r_shoulder_lift_joint | hinge | torque (N m) | - | 2 | Rotation of the shoulder rolling joint | -2 | 2 | r_upper_arm_roll_joint | hinge | torque (N m) | - | 3 | Rotation of hinge joint that flexed the elbow | -2 | 2 | r_elbow_flex_joint | hinge | torque (N m) | - | 4 | Rotation of hinge that rolls the forearm | -2 | 2 | r_forearm_roll_joint | hinge | torque (N m) | - | 5 | Rotation of flexing the wrist | -2 | 2 | r_wrist_flex_joint | hinge | torque (N m) | - | 6 | Rotation of rolling the wrist | -2 | 2 | r_wrist_roll_joint | hinge | torque (N m) | - - - ## Observation Space - Observations consist of - - - Angle of rotational joints on the pusher - - Angular velocities of rotational joints on the pusher - - The coordinates of the fingertip of the pusher - - The coordinates of the object to be moved - - The coordinates of the goal position - - The observation is a `Box(-Inf, Inf, (23,), float64)` where the elements correspond to the table below. - An analogy can be drawn to a human arm in order to help understand the state space, with the words flex and roll meaning the - same as human joints. - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | -------------------------------------------------------- | ---- | --- | -------------------------------- | -------- | ------------------------ | - | 0 | Rotation of the panning the shoulder | -Inf | Inf | r_shoulder_pan_joint | hinge | angle (rad) | - | 1 | Rotation of the shoulder lifting joint | -Inf | Inf | r_shoulder_lift_joint | hinge | angle (rad) | - | 2 | Rotation of the shoulder rolling joint | -Inf | Inf | r_upper_arm_roll_joint | hinge | angle (rad) | - | 3 | Rotation of hinge joint that flexed the elbow | -Inf | Inf | r_elbow_flex_joint | hinge | angle (rad) | - | 4 | Rotation of hinge that rolls the forearm | -Inf | Inf | r_forearm_roll_joint | hinge | angle (rad) | - | 5 | Rotation of flexing the wrist | -Inf | Inf | r_wrist_flex_joint | hinge | angle (rad) | - | 6 | Rotation of rolling the wrist | -Inf | Inf | r_wrist_roll_joint | hinge | angle (rad) | - | 7 | Rotational velocity of the panning the shoulder | -Inf | Inf | r_shoulder_pan_joint | hinge | angular velocity (rad/s) | - | 8 | Rotational velocity of the shoulder lifting joint | -Inf | Inf | r_shoulder_lift_joint | hinge | angular velocity (rad/s) | - | 9 | Rotational velocity of the shoulder rolling joint | -Inf | Inf | r_upper_arm_roll_joint | hinge | angular velocity (rad/s) | - | 10 | Rotational velocity of hinge joint that flexed the elbow | -Inf | Inf | r_elbow_flex_joint | hinge | angular velocity (rad/s) | - | 11 | Rotational velocity of hinge that rolls the forearm | -Inf | Inf | r_forearm_roll_joint | hinge | angular velocity (rad/s) | - | 12 | Rotational velocity of flexing the wrist | -Inf | Inf | r_wrist_flex_joint | hinge | angular velocity (rad/s) | - | 13 | Rotational velocity of rolling the wrist | -Inf | Inf | r_wrist_roll_joint | hinge | angular velocity (rad/s) | - | 14 | x-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | - | 15 | y-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | - | 16 | z-coordinate of the fingertip of the pusher | -Inf | Inf | tips_arm | slide | position (m) | - | 17 | x-coordinate of the object to be moved | -Inf | Inf | object (obj_slidex) | slide | position (m) | - | 18 | y-coordinate of the object to be moved | -Inf | Inf | object (obj_slidey) | slide | position (m) | - | 19 | z-coordinate of the object to be moved | -Inf | Inf | object | cylinder | position (m) | - | 20 | x-coordinate of the goal position of the object | -Inf | Inf | goal (goal_slidex) | slide | position (m) | - | 21 | y-coordinate of the goal position of the object | -Inf | Inf | goal (goal_slidey) | slide | position (m) | - | 22 | z-coordinate of the goal position of the object | -Inf | Inf | goal | sphere | position (m) | - - - ## Rewards - The reward consists of two parts: - - *reward_near*: - This reward is a measure of how far the *fingertip* of the pusher (the unattached end) is from the object, - with a more negative value assigned for when the pusher's *fingertip* is further away from the target. - It is $-w_{near} \|(P_{fingertip} - P_{target})\|_2$. - where $w_{near}$ is `reward_near_weight`. - - *reward_dist*: - This reward is a measure of how far the object is from the target goal position, - with a more negative value assigned for object that is further away from the target. - It is $-w_{dist} \|(P_{object} - P_{target})\|_2$. - where $w_{dist}$ is `reward_dist_weight`. - - *reward_control*: - A negative reward for penalising the pusher if it takes actions that are too large. - It is measured as the negative squared Euclidean norm of the action, i.e. as $-w_{control} \|action\|_2^2$. - where $w_{control}$ is `reward_control_weight`. - - The total reward returned is ***reward*** *=* *reward_dist + reward_ctrl + reward_near*, - `info` will also contain the individual reward terms. - - - ## Starting State - All pusher (not including object and goal) states start in - (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0). A uniform noise in the range - [-0.005, 0.005] is added to the velocity attributes only. The velocities of - the object and goal are permanently set to 0. The object's x-position is selected uniformly - between [-0.3, 0] while the y-position is selected uniformly between [-0.2, 0.2], and this - process is repeated until the vector norm between the object's (x,y) position and origin is not greater - than 0.17. The goal always have the same position of (0.45, -0.05, -0.323). - - The default framerate is 5 with each frame lasting for 0.01, giving rise to a *dt = 5 * 0.01 = 0.05* - - - ## Episode End - #### Termination - The Pusher never terminates. - - #### Truncation - The maximum duration of an episode is 100 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`. - - ```python - import gymnasium as gym - env = gym.make('Pusher-v5', xml_file=...) - ``` - - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|----------------------------------------------------------| - | `xml_file` | **str** |`"pusher.xml"`| Path to a MuJoCo model | - | `reward_near_weight` | **float** | `0.5` | Weight for *reward_near* term (see section on reward) | - | `reward_dist_weight` | **float** | `1` | Weight for *reward_dist* term (see section on reward) | - | `reward_control_weight` | **float** | `0.1` | Weight for *reward_control* term (see section on reward) | - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. "reward_near" is added to the `info`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. - * v3: This environment does not have a v3 release. - * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks (not including pusher, which has a max_time_steps of 100). Added reward_threshold to environments. - * v0: Initial versions release (1.0.0). - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="pusher.xml", - frame_skip=5, - default_camera_config=DEFAULT_CAMERA_CONFIG, - reward_near_weight=0.5, - reward_dist_weight=1, - reward_control_weight=0.1, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - reward_near_weight, - reward_dist_weight, - reward_control_weight, - **kwargs, - ) - self._reward_near_weight = reward_near_weight - self._reward_dist_weight = reward_dist_weight - self._reward_control_weight = reward_control_weight - - observation_space = Box(low=-np.inf, high=np.inf, shape=(23,), dtype=np.float64) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=observation_space, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - def step(self, action): - vec_1 = self.get_body_com("object") - self.get_body_com("tips_arm") - vec_2 = self.get_body_com("object") - self.get_body_com("goal") - - reward_near = -np.linalg.norm(vec_1) * self._reward_near_weight - reward_dist = -np.linalg.norm(vec_2) * self._reward_dist_weight - reward_ctrl = -np.square(action).sum() * self._reward_control_weight - reward = reward_dist + reward_ctrl + reward_near - - self.do_simulation(action, self.frame_skip) - - observation = self._get_obs() - info = { - "reward_dist": reward_dist, - "reward_ctrl": reward_ctrl, - "reward_near": reward_near, - } - if self.render_mode == "human": - self.render() - return observation, reward, False, False, info - - def reset_model(self): - qpos = self.init_qpos - - self.goal_pos = np.asarray([0, 0]) - while True: - self.cylinder_pos = np.concatenate( - [ - self.np_random.uniform(low=-0.3, high=0, size=1), - self.np_random.uniform(low=-0.2, high=0.2, size=1), - ] - ) - if np.linalg.norm(self.cylinder_pos - self.goal_pos) > 0.17: - break - - qpos[-4:-2] = self.cylinder_pos - qpos[-2:] = self.goal_pos - qvel = self.init_qvel + self.np_random.uniform( - low=-0.005, high=0.005, size=self.model.nv - ) - qvel[-4:] = 0 - self.set_state(qpos, qvel) - return self._get_obs() - - def _get_obs(self): - return np.concatenate( - [ - self.data.qpos.flat[:7], - self.data.qvel.flat[:7], - self.get_body_com("tips_arm"), - self.get_body_com("object"), - self.get_body_com("goal"), - ] - ) diff --git a/gymnasium_robotics/envs/mujoco/reacher_v5.py b/gymnasium_robotics/envs/mujoco/reacher_v5.py deleted file mode 100644 index 92faea81..00000000 --- a/gymnasium_robotics/envs/mujoco/reacher_v5.py +++ /dev/null @@ -1,220 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = {"trackbodyid": 0} - - -class ReacherEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - "Reacher" is a two-jointed robot arm. The goal is to move the robot's end effector (called *fingertip*) close to a - target that is spawned at a random position. - - - ## Action Space - The action space is a `Box(-1, 1, (2,), float32)`. An action `(a, b)` represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|---------------------------------------------------------------------------------|-------------|-------------|--------------------------|-------|------| - | 0 | Torque applied at the first hinge (connecting the link to the point of fixture) | -1 | 1 | joint0 | hinge | torque (N m) | - | 1 | Torque applied at the second hinge (connecting the two links) | -1 | 1 | joint1 | hinge | torque (N m) | - - - ## Observation Space - Observations consist of - - - The cosine of the angles of the two arms - - The sine of the angles of the two arms - - The coordinates of the target - - The angular velocities of the arms - - The vector between the target and the reacher's fingertip (3 dimensional with the last element being 0) - - The observation is a `Box(-Inf, Inf, (10,), float64)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ---------------------------------------------------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | - | 0 | cosine of the angle of the first arm | -Inf | Inf | cos(joint0) | hinge | unitless | - | 1 | cosine of the angle of the second arm | -Inf | Inf | cos(joint1) | hinge | unitless | - | 2 | sine of the angle of the first arm | -Inf | Inf | sin(joint0) | hinge | unitless | - | 3 | sine of the angle of the second arm | -Inf | Inf | sin(joint1) | hinge | unitless | - | 4 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | - | 5 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | - | 6 | angular velocity of the first arm | -Inf | Inf | joint0 | hinge | angular velocity (rad/s) | - | 7 | angular velocity of the second arm | -Inf | Inf | joint1 | hinge | angular velocity (rad/s) | - | 8 | x-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | - | 9 | y-value of position_fingertip - position_target | -Inf | Inf | NA | slide | position (m) | - | excluded | z-value of position_fingertip - position_target (constantly 0 since reacher is 2d) | -Inf | Inf | NA | slide | position (m) | - - Most Gym environments just return the positions and velocity of the - joints in the `.xml` file as the state of the environment. However, in - reacher the state is created by combining only certain elements of the - position and velocity, and performing some function transformations on them. - If one is to read the `reacher.xml` then they will find 4 joints: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - |-----|-----------------------------|----------|----------|----------------------------------|-------|--------------------| - | 0 | angle of the first arm | -Inf | Inf | joint0 | hinge | angle (rad) | - | 1 | angle of the second arm | -Inf | Inf | joint1 | hinge | angle (rad) | - | 2 | x-coordinate of the target | -Inf | Inf | target_x | slide | position (m) | - | 3 | y-coordinate of the target | -Inf | Inf | target_y | slide | position (m) | - - - ## Rewards - The reward consists of two parts: - - *reward_distance*: - This reward is a measure of how far the *fingertip* of the reacher (the unattached end) is from the target, - with a more negative value assigned for when the reacher's *fingertip* is further away from the target. - It is $-w_{near} \|(P_{fingertip} - P_{target})\|_2$. - where $w_{near}$ is `reward_near_weight`. - - *reward_control*: - A negative reward for penalising the walker if it takes actions that are too large. - It is measured as the negative squared Euclidean norm of the action, i.e. as $-w_{control} \|action\|_2^2$. - where $w_{control}$ is `reward_control_weight`. - - The total reward returned is ***reward*** *=* *reward_distance + reward_control*. - `info` will also contain the individual reward terms. - - ## Starting State - All observations start in state - (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) - with a noise added for stochasticity. A uniform noise in the range - [-0.1, 0.1] is added to the positional attributes, while the target position - is selected uniformly at random in a disk of radius 0.2 around the origin. - Independent, uniform noise in the - range of [-0.005, 0.005] is added to the velocities, and the last - element ("fingertip" - "target") is calculated at the end once everything - is set. The default setting has a framerate of 2 and a *dt = 2 * 0.01 = 0.02* - - ## Episode End - #### Termination - The Reacher never terminates. - - #### Truncation - The maximum duration of an episode is 50 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`. - - ```python - import gymnasium as gym - env = gym.make('Reacher-v5', xml_file=...) - ``` - - | Parameter | Type | Default |Description | - |-------------------------|------------|--------------|----------------------------------------------------------| - | `xml_file` | **str** |`"reacher.xml"`| Path to a MuJoCo model | - | `reward_dist_weight` | **float** | `1` | Weight for *reward_dist* term (see section on reward) | - | `reward_control_weight` | **float** | `0.1` | Weight for *reward_control* term (see section on reward) | - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Added `xml_file` argument. Remove "z - position_fingertip" from the observation space. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: This environment does not have a v3 release. - * v2: All continuous control environments now use mujoco-py >= 1.50 - * v1: max_time_steps raised to 1000 for robot based tasks (not including reacher, which has a max_time_steps of 50). Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="reacher.xml", - frame_skip=2, - default_camera_config=DEFAULT_CAMERA_CONFIG, - reward_dist_weight=1, - reward_control_weight=1, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - reward_dist_weight, - reward_control_weight, - **kwargs, - ) - - self._reward_dist_weight = reward_dist_weight - self._reward_control_weight = reward_control_weight - - observation_space = Box(low=-np.inf, high=np.inf, shape=(10,), dtype=np.float64) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=observation_space, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - def step(self, action): - vec = self.get_body_com("fingertip") - self.get_body_com("target") - reward_dist = -np.linalg.norm(vec) * self._reward_dist_weight - reward_ctrl = -np.square(action).sum() * self._reward_control_weight - reward = reward_dist + reward_ctrl - - self.do_simulation(action, self.frame_skip) - - observation = self._get_obs() - info = { - "reward_dist": reward_dist, - "reward_ctrl": reward_ctrl, - } - if self.render_mode == "human": - self.render() - return observation, reward, False, False, info - - def reset_model(self): - qpos = ( - self.np_random.uniform(low=-0.1, high=0.1, size=self.model.nq) - + self.init_qpos - ) - while True: - self.goal = self.np_random.uniform(low=-0.2, high=0.2, size=2) - if np.linalg.norm(self.goal) < 0.2: - break - qpos[-2:] = self.goal - qvel = self.init_qvel + self.np_random.uniform( - low=-0.005, high=0.005, size=self.model.nv - ) - qvel[-2:] = 0 - self.set_state(qpos, qvel) - return self._get_obs() - - def _get_obs(self): - theta = self.data.qpos.flat[:2] - assert (self.get_body_com("fingertip") - self.get_body_com("target"))[ - 2 - ] == 0 # TODO remove after validation - return np.concatenate( - [ - np.cos(theta), - np.sin(theta), - self.data.qpos.flat[2:], - self.data.qvel.flat[:2], - (self.get_body_com("fingertip") - self.get_body_com("target"))[:2], - ] - ) diff --git a/gymnasium_robotics/envs/mujoco/swimmer_v5.py b/gymnasium_robotics/envs/mujoco/swimmer_v5.py deleted file mode 100644 index b156ac5d..00000000 --- a/gymnasium_robotics/envs/mujoco/swimmer_v5.py +++ /dev/null @@ -1,260 +0,0 @@ -__credits__ = ["Kallinteris-Andreas", "Rushiv Arora"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - - -class SwimmerEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment corresponds to the Swimmer environment described in Rémi Coulom's PhD thesis - ["Reinforcement Learning Using Neural Networks, with Applications to Motor Control"](https://tel.archives-ouvertes.fr/tel-00003985/document). - The environment aims to increase the number of independent state and control - variables as compared to the classic control environments. The swimmers - consist of three or more segments ('***links***') and one less articulation - joints ('***rotors***') - one rotor joint connecting exactly two links to - form a linear chain. The swimmer is suspended in a two dimensional pool and - always starts in the same position (subject to some deviation drawn from an - uniform distribution), and the goal is to move as fast as possible towards - the right by applying torque on the rotors and using the fluids friction. - - ## Notes - - The problem parameters are: - Problem parameters: - * *n*: number of body parts - * *mi*: mass of part *i* (*i* ∈ {1...n}) - * *li*: length of part *i* (*i* ∈ {1...n}) - * *k*: viscous-friction coefficient - - While the default environment has *n* = 3, *li* = 0.1, - and *k* = 0.1. It is possible to pass a custom MuJoCo XML file during construction to increase the - number of links, or to tweak any of the parameters. - - - ## Action Space - The action space is a `Box(-1, 1, (2,), float32)`. An action represents the torques applied between *links* - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|------------------------------------|-------------|-------------|----------------------------------|-------|--------------| - | 0 | Torque applied on the first rotor | -1 | 1 | motor1_rot | hinge | torque (N m) | - | 1 | Torque applied on the second rotor | -1 | 1 | motor2_rot | hinge | torque (N m) | - - - ## Observation Space - By default, observations consists of: - * θi: angle of part *i* with respect to the *x* axis - * θi': its derivative with respect to time (angular velocity) - - In the default case, observations do not include the x- and y-coordinates of the front tip. These may - be included by passing `exclude_current_positions_from_observation=False` during construction. - Then, the observation space will be `Box(-Inf, Inf, (10,), float64)` where the first two observations - represent the x- and y-coordinates of the front tip. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x- and y-coordinates - will be returned in `info` with keys `"x_position"` and `"y_position"`, respectively. - - By default, the observation is a `Box(-Inf, Inf, (8,), float64)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | ------------------------------------ | ---- | --- | -------------------------------- | ----- | ------------------------ | - | 0 | angle of the front tip | -Inf | Inf | free_body_rot | hinge | angle (rad) | - | 1 | angle of the first rotor | -Inf | Inf | motor1_rot | hinge | angle (rad) | - | 2 | angle of the second rotor | -Inf | Inf | motor2_rot | hinge | angle (rad) | - | 3 | velocity of the tip along the x-axis | -Inf | Inf | slider1 | slide | velocity (m/s) | - | 4 | velocity of the tip along the y-axis | -Inf | Inf | slider2 | slide | velocity (m/s) | - | 5 | angular velocity of front tip | -Inf | Inf | free_body_rot | hinge | angular velocity (rad/s) | - | 6 | angular velocity of first rotor | -Inf | Inf | motor1_rot | hinge | angular velocity (rad/s) | - | 7 | angular velocity of second rotor | -Inf | Inf | motor2_rot | hinge | angular velocity (rad/s) | - | excluded | position of the tip along the x-axis | -Inf | Inf | slider1 | slide | position (m) | - | excluded | position of the tip along the y-axis | -Inf | Inf | slider2 | slide | position (m) | - - - ## Rewards - The reward consists of two parts: - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Swimmer moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the (front) "tip" ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), - and `frametime` which is 0.01 - making the default $dt = 4 \times 0.01 = 0.04$, - $w_{forward}$ is the `forward_reward_weight` (default is $1$). - - *ctrl_cost*: - A negative reward for penalizing the Swimmer if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-4}$). - - The total reward returned is ***reward*** *=* *forward_reward - ctrl_cost*, - and `info` will also contain the individual reward terms - - - ## Starting State - All observations start in state (0,0,0,0,0,0,0,0) with a Uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] is added to the initial state for stochasticity. - - - ## Episode End - #### Termination - The Swimmer never terminates. - - #### truncation - The maximum duration of an episode is 1000 timesteps. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`. - - ```python - import gymnasium as gym - env = gym.make('Swimmer-v5', xml_file=...) - ``` - - | Parameter | Type | Default |Description | - |--------------------------------------------| --------- |-------------- |-------------------------------| - |`xml_file` | **str** |`"swimmer.xml"`| Path to a MuJoCo model | - |`forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward)| - |`ctrl_cost_weight` | **float** | `1e-4` | Weight for _ctrl_cost_ term (see section on reward) | - |`reset_noise_scale` | **float** | `0.1` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - |`exclude_current_positions_from_observation`| **bool** | `True` | Whether or not to omit the x- and y-coordinates from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. The `xml_file` argument is re-added, replaced "reward_fwd"/ "forward_reward" with "reward_forward" in `info`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3. - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen). - * v2: All continuous control environments now use mujoco-py >= 1.50. - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="swimmer.xml", - frame_skip=4, - forward_reward_weight=1.0, - ctrl_cost_weight=1e-4, - reset_noise_scale=0.1, - exclude_current_positions_from_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - forward_reward_weight, - ctrl_cost_weight, - reset_noise_scale, - exclude_current_positions_from_observation, - **kwargs, - ) - - self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - MujocoEnv.__init__(self, xml_file, frame_skip, observation_space=None, **kwargs) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = ( - self.data.qpos.size - + self.data.qvel.size - - 2 * exclude_current_positions_from_observation - ) - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 2 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 2 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - } - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) - return control_cost - - def step(self, action): - xy_position_before = self.data.qpos[0:2].copy() - self.do_simulation(action, self.frame_skip) - xy_position_after = self.data.qpos[0:2].copy() - - xy_velocity = (xy_position_after - xy_position_before) / self.dt - x_velocity, y_velocity = xy_velocity - - forward_reward = self._forward_reward_weight * x_velocity - - ctrl_cost = self.control_cost(action) - - observation = self._get_obs() - reward = forward_reward - ctrl_cost - info = { - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "x_position": xy_position_after[0], - "y_position": xy_position_after[1], - "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), - "x_velocity": x_velocity, - "y_velocity": y_velocity, - } - - if self.render_mode == "human": - self.render() - - return observation, reward, False, False, info - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = self.data.qvel.flat.copy() - - if self._exclude_current_positions_from_observation: - position = position[2:] - - observation = np.concatenate([position, velocity]).ravel() - return observation - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = self.init_qvel + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nv - ) - - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "y_position": self.data.qpos[1], - "distance_from_origin": np.linalg.norm(self.data.qpos[0:2], ord=2), - } diff --git a/gymnasium_robotics/envs/mujoco/walker2d_v5.py b/gymnasium_robotics/envs/mujoco/walker2d_v5.py deleted file mode 100644 index c09217bd..00000000 --- a/gymnasium_robotics/envs/mujoco/walker2d_v5.py +++ /dev/null @@ -1,329 +0,0 @@ -__credits__ = ["Kallinteris-Andreas"] - -import numpy as np -from gymnasium import utils -from gymnasium.envs.mujoco import MujocoEnv -from gymnasium.spaces import Box - -DEFAULT_CAMERA_CONFIG = { - "trackbodyid": 2, - "distance": 4.0, - "lookat": np.array((0.0, 0.0, 1.15)), - "elevation": -20.0, -} - - -class Walker2dEnv(MujocoEnv, utils.EzPickle): - r""" - ## Description - This environment builds on the [hopper](https://gymnasium.farama.org/environments/mujoco/hopper/) environment - by adding another set of legs making it possible for the robot to walk forward instead of - hop. Like other Mujoco environments, this environment aims to increase the number of independent state - and control variables as compared to the classic control environments. The walker is a - two-dimensional two-legged figure that consist of seven main body parts - a single torso at the top - (with the two legs splitting after the torso), two thighs in the middle below the torso, two legs - in the bottom below the thighs, and two feet attached to the legs on which the entire body rests. - The goal is to walk in the in the forward (right) - direction by applying torques on the six hinges connecting the seven body parts. - - - ## Action Space - The action space is a `Box(-1, 1, (6,), float32)`. An action represents the torques applied at the hinge joints. - - | Num | Action | Control Min | Control Max | Name (in corresponding XML file) | Joint | Unit | - |-----|----------------------------------------|-------------|-------------|----------------------------------|-------|--------------| - | 0 | Torque applied on the thigh rotor | -1 | 1 | thigh_joint | hinge | torque (N m) | - | 1 | Torque applied on the leg rotor | -1 | 1 | leg_joint | hinge | torque (N m) | - | 2 | Torque applied on the foot rotor | -1 | 1 | foot_joint | hinge | torque (N m) | - | 3 | Torque applied on the left thigh rotor | -1 | 1 | thigh_left_joint | hinge | torque (N m) | - | 4 | Torque applied on the left leg rotor | -1 | 1 | leg_left_joint | hinge | torque (N m) | - | 5 | Torque applied on the left foot rotor | -1 | 1 | foot_left_joint | hinge | torque (N m) | - - - ## Observation Space - Observations consist of positional values of different body parts of the walker, - followed by the velocities of those individual parts (their derivatives) with all the positions ordered before all the velocities. - - By default, observations do not include the x-coordinate of the torso. It may - be included by passing `exclude_current_positions_from_observation=False` during construction. - In that case, the observation space will be `Box(-Inf, Inf, (18,), float64)` where the first observation - represent the x-coordinates of the torso of the walker. - Regardless of whether `exclude_current_positions_from_observation` was set to true or false, the x-coordinate - of the torso will be returned in `info` with key `"x_position"`. - - By default, observation is a `Box(-Inf, Inf, (17,), float64)` where the elements correspond to the following: - - | Num | Observation | Min | Max | Name (in corresponding XML file) | Joint | Unit | - | --- | -------------------------------------------------- | ---- | --- | -------------------------------- | ----- | ------------------------ | - | excluded | x-coordinate of the torso | -Inf | Inf | rootx | slide | position (m) | - | 0 | z-coordinate of the torso (height of Walker2d) | -Inf | Inf | rootz | slide | position (m) | - | 1 | angle of the torso | -Inf | Inf | rooty | hinge | angle (rad) | - | 2 | angle of the thigh joint | -Inf | Inf | thigh_joint | hinge | angle (rad) | - | 3 | angle of the leg joint | -Inf | Inf | leg_joint | hinge | angle (rad) | - | 4 | angle of the foot joint | -Inf | Inf | foot_joint | hinge | angle (rad) | - | 5 | angle of the left thigh joint | -Inf | Inf | thigh_left_joint | hinge | angle (rad) | - | 6 | angle of the left leg joint | -Inf | Inf | leg_left_joint | hinge | angle (rad) | - | 7 | angle of the left foot joint | -Inf | Inf | foot_left_joint | hinge | angle (rad) | - | 8 | velocity of the x-coordinate of the torso | -Inf | Inf | rootx | slide | velocity (m/s) | - | 9 | velocity of the z-coordinate (height) of the torso | -Inf | Inf | rootz | slide | velocity (m/s) | - | 10 | angular velocity of the angle of the torso | -Inf | Inf | rooty | hinge | angular velocity (rad/s) | - | 11 | angular velocity of the thigh hinge | -Inf | Inf | thigh_joint | hinge | angular velocity (rad/s) | - | 12 | angular velocity of the leg hinge | -Inf | Inf | leg_joint | hinge | angular velocity (rad/s) | - | 13 | angular velocity of the foot hinge | -Inf | Inf | foot_joint | hinge | angular velocity (rad/s) | - | 14 | angular velocity of the thigh hinge | -Inf | Inf | thigh_left_joint | hinge | angular velocity (rad/s) | - | 15 | angular velocity of the leg hinge | -Inf | Inf | leg_left_joint | hinge | angular velocity (rad/s) | - | 16 | angular velocity of the foot hinge | -Inf | Inf | foot_left_joint | hinge | angular velocity (rad/s) | - - - ## Rewards - The reward consists of three parts: - - *healthy_reward*: - Every timestep that the Walker2d is alive, it receives a fixed reward of value `healthy_reward`, - - *forward_reward*: - A reward of moving forward, - this reward would be positive if the Swimmer moves forward (in the positive $x$ direction / in the right direction). - $w_{forward} \times \frac{dx}{dt}$, where - $dx$ is the displacement of the (front) "tip" ($x_{after-action} - x_{before-action}$), - $dt$ is the time between actions which is dependent on the `frame_skip` parameter (default is 4), - and `frametime` which is 0.002 - making the default $dt = 4 \times 0.002 = 0.008$, - $w_{forward}$ is the `forward_reward_weight` (default is $1$). - - *ctrl_cost*: - A negative reward for penalizing the Walker2d if it takes actions that are too large. - $w_{control} \times \\|action\\|_2^2$, - where $w_{control}$ is `ctrl_cost_weight` (default is $10^{-3}$). - - The total reward returned is ***reward*** *=* *healthy_reward bonus + forward_reward - ctrl_cost* - and `info` will also contain the individual reward terms. - - - ## Starting State - All observations start in state - (0.0, 1.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) - with a uniform noise in the range of [-`reset_noise_scale`, `reset_noise_scale`] added to the values for stochasticity. - - - ## Episode End - #### termination - If `terminate_when_unhealthy is True` (which is the default), the environment terminates when the Walker2d is unhealthy. - The Walker2d is unhealthy if any of the following happens: - - 1. Any of the state space values is no longer finite - 2. The height of the walker is ***not*** in the closed interval specified by `healthy_z_range` - 3. The absolute value of the angle (`observation[1]` if `exclude_current_positions_from_observation=False`, else `observation[2]`) is ***not*** in the closed interval specified by `healthy_angle_range` - - #### truncation - the maximum duration of an episode is 1000 timesteps. - - If `terminate_when_unhealthy=False` is passed, the episode is ended only when 1000 timesteps are exceeded. - - - ## Arguments - `gymnasium.make` takes additional arguments such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. - - ```python - import gymnasium as gym - env = gym.make('Walker2d-v5', ctrl_cost_weight=1e-3, ...) - ``` - - | Parameter | Type | Default | Description | - | -------------------------------------------- | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | `xml_file` | **str** |`"walker2d_v5.xml"`| Path to a MuJoCo model | - | `forward_reward_weight` | **float** | `1` | Weight for _forward_reward_ term (see section on reward) | - | `ctrl_cost_weight` | **float** | `1e-3` | Weight for _ctr_cost_ term (see section on reward) | - | `healthy_reward` | **float** | `1` | Weight for _healthy_reward_ reward (see section on reward) | - | `terminate_when_unhealthy` | **bool** | `True` | If true, issue a done signal if the z-coordinate of the walker is no longer healthy | - | `healthy_z_range` | **tuple** | `(0.8, 2)` | The z-coordinate of the torso of the walker must be in this range to be considered healthy | - | `healthy_angle_range` | **tuple** | `(-1, 1)` | The angle must be in this range to be considered healthy | - | `reset_noise_scale` | **float** | `5e-3` | Scale of random perturbations of initial position and velocity (see section on Starting State) | - | `exclude_current_positions_from_observation` | **bool** | `True` | Whether or not to omit the x-coordinate from observations. Excluding the position can serve as an inductive bias to induce position-agnostic behavior in policies | - - - ## Version History - * v5: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.3.3. Walker2d gets a new model (feet friction set to 1.9), the `xml_file` argument is re-added, added "reward_forward", "reward_ctrl", "reward_survive" to `info`. - * v4: All MuJoCo environments now use the MuJoCo bindings in mujoco >= 2.1.3 - * v3: Support for `gymnasium.make` kwargs such as `xml_file`, `ctrl_cost_weight`, `reset_noise_scale`, etc. rgb rendering comes from tracking camera (so agent does not run away from screen) - * v2: All continuous control environments now use mujoco-py >= 1.50 - * v1: max_time_steps raised to 1000 for robot based tasks. Added reward_threshold to environments. - * v0: Initial versions release (1.0.0) - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - } - - def __init__( - self, - xml_file="walker2d_v5.xml", - frame_skip=4, - default_camera_config=DEFAULT_CAMERA_CONFIG, - forward_reward_weight=1.0, - ctrl_cost_weight=1e-3, - healthy_reward=1.0, - terminate_when_unhealthy=True, - healthy_z_range=(0.8, 2.0), - healthy_angle_range=(-1.0, 1.0), - reset_noise_scale=5e-3, - exclude_current_positions_from_observation=True, - **kwargs, - ): - utils.EzPickle.__init__( - self, - xml_file, - frame_skip, - default_camera_config, - forward_reward_weight, - ctrl_cost_weight, - healthy_reward, - terminate_when_unhealthy, - healthy_z_range, - healthy_angle_range, - reset_noise_scale, - exclude_current_positions_from_observation, - **kwargs, - ) - - self._forward_reward_weight = forward_reward_weight - self._ctrl_cost_weight = ctrl_cost_weight - - self._healthy_reward = healthy_reward - self._terminate_when_unhealthy = terminate_when_unhealthy - - self._healthy_z_range = healthy_z_range - self._healthy_angle_range = healthy_angle_range - - self._reset_noise_scale = reset_noise_scale - - self._exclude_current_positions_from_observation = ( - exclude_current_positions_from_observation - ) - - MujocoEnv.__init__( - self, - xml_file, - frame_skip, - observation_space=None, - default_camera_config=default_camera_config, - **kwargs, - ) - - self.metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": int(np.round(1.0 / self.dt)), - } - - obs_size = ( - self.data.qpos.size - + self.data.qvel.size - - exclude_current_positions_from_observation - ) - self.observation_space = Box( - low=-np.inf, high=np.inf, shape=(obs_size,), dtype=np.float64 - ) - - self.observation_structure = { - "skipped_qpos": 1 * exclude_current_positions_from_observation, - "qpos": self.data.qpos.size - - 1 * exclude_current_positions_from_observation, - "qvel": self.data.qvel.size, - } - - @property - def healthy_reward(self): - return self.is_healthy * self._healthy_reward - - def control_cost(self, action): - control_cost = self._ctrl_cost_weight * np.sum(np.square(action)) - return control_cost - - @property - def is_healthy(self): - z, angle = self.data.qpos[1:3] - - min_z, max_z = self._healthy_z_range - min_angle, max_angle = self._healthy_angle_range - - healthy_z = min_z < z < max_z - healthy_angle = min_angle < angle < max_angle - is_healthy = healthy_z and healthy_angle - - return is_healthy - - @property - def terminated(self): - terminated = (not self.is_healthy) and self._terminate_when_unhealthy - # TODO remove after validation - assert terminated == ( - not self.is_healthy if self._terminate_when_unhealthy else False - ) - return terminated - - def _get_obs(self): - position = self.data.qpos.flat.copy() - velocity = np.clip(self.data.qvel.flat.copy(), -10, 10) - - if self._exclude_current_positions_from_observation: - position = position[1:] - - observation = np.concatenate((position, velocity)).ravel() - return observation - - def step(self, action): - x_position_before = self.data.qpos[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.data.qpos[0] - x_velocity = (x_position_after - x_position_before) / self.dt - - ctrl_cost = self.control_cost(action) - - forward_reward = self._forward_reward_weight * x_velocity - healthy_reward = self.healthy_reward - - rewards = forward_reward + healthy_reward - costs = ctrl_cost - - observation = self._get_obs() - reward = rewards - costs - terminated = self.terminated - info = { - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "reward_survive": healthy_reward, - "x_position": x_position_after, - "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], - "x_velocity": x_velocity, - } - - if self.render_mode == "human": - self.render() - - return observation, reward, terminated, False, info - - def reset_model(self): - noise_low = -self._reset_noise_scale - noise_high = self._reset_noise_scale - - qpos = self.init_qpos + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nq - ) - qvel = self.init_qvel + self.np_random.uniform( - low=noise_low, high=noise_high, size=self.model.nv - ) - - self.set_state(qpos, qvel) - - observation = self._get_obs() - return observation - - def _get_reset_info(self): - return { - "x_position": self.data.qpos[0], - "z_distance_from_origin": self.data.qpos[1] - self.init_qpos[1], - } From e188458f5031e722d298273b28a7ef17c7d2130e Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 17 Jul 2023 23:36:40 +0300 Subject: [PATCH 104/160] clean --- gymnasium_robotics/__init__.py | 293 ----------------- tests/envs/mujoco/test_mujoco_v5.py | 471 ---------------------------- 2 files changed, 764 deletions(-) delete mode 100644 tests/envs/mujoco/test_mujoco_v5.py diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index ff272bca..7f2817a6 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -1224,299 +1224,6 @@ def _merge(a, b): max_episode_steps=280, ) - # Mujoco - # ---------------------------------------- - - # Manipulation - - """ - register( - id="Reacher-v2", - entry_point="gymnasium_robotics.envs.mujoco:ReacherEnv", - max_episode_steps=50, - reward_threshold=-3.75, - ) - - register( - id="Reacher-v4", - entry_point="gymnasium_robotics.envs.mujoco.reacher_v4:ReacherEnv", - max_episode_steps=50, - reward_threshold=-3.75, - ) - """ - - register( - id="Reacher-v5", - entry_point="gymnasium_robotics.envs.mujoco.reacher_v5:ReacherEnv", - max_episode_steps=50, - reward_threshold=-3.75, - ) - - """ - register( - id="Pusher-v2", - entry_point="gymnasium_robotics.envs.mujoco:PusherEnv", - max_episode_steps=100, - reward_threshold=0.0, - ) - - register( - id="Pusher-v4", - entry_point="gymnasium_robotics.envs.mujoco.pusher_v4:PusherEnv", - max_episode_steps=100, - reward_threshold=0.0, - ) - """ - - register( - id="Pusher-v5", - entry_point="gymnasium_robotics.envs.mujoco.pusher_v5:PusherEnv", - max_episode_steps=100, - reward_threshold=0.0, - ) - - # Balance - - """ - register( - id="InvertedPendulum-v2", - entry_point="gymnasium_robotics.envs.mujoco:InvertedPendulumEnv", - max_episode_steps=1000, - reward_threshold=950.0, - ) - - register( - id="InvertedPendulum-v4", - entry_point="gymnasium_robotics.envs.mujoco.inverted_pendulum_v4:InvertedPendulumEnv", - max_episode_steps=1000, - reward_threshold=950.0, - ) - """ - - register( - id="InvertedPendulum-v5", - entry_point="gymnasium_robotics.envs.mujoco.inverted_pendulum_v5:InvertedPendulumEnv", - max_episode_steps=1000, - reward_threshold=950.0, - ) - - """ - register( - id="InvertedDoublePendulum-v2", - entry_point="gymnasium_robotics.envs.mujoco:InvertedDoublePendulumEnv", - max_episode_steps=1000, - reward_threshold=9100.0, - ) - - register( - id="InvertedDoublePendulum-v4", - entry_point="gymnasium_robotics.envs.mujoco.inverted_double_pendulum_v4:InvertedDoublePendulumEnv", - max_episode_steps=1000, - reward_threshold=9100.0, - ) - """ - - register( - id="InvertedDoublePendulum-v5", - entry_point="gymnasium_robotics.envs.mujoco.inverted_double_pendulum_v5:InvertedDoublePendulumEnv", - max_episode_steps=1000, - reward_threshold=9100.0, - ) - - # Running - - """ - register( - id="HalfCheetah-v2", - entry_point="gymnasium_robotics.envs.mujoco:HalfCheetahEnv", - max_episode_steps=1000, - reward_threshold=4800.0, - ) - - register( - id="HalfCheetah-v3", - entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v3:HalfCheetahEnv", - max_episode_steps=1000, - reward_threshold=4800.0, - ) - - register( - id="HalfCheetah-v4", - entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v4:HalfCheetahEnv", - max_episode_steps=1000, - reward_threshold=4800.0, - ) - """ - - register( - id="HalfCheetah-v5", - entry_point="gymnasium_robotics.envs.mujoco.half_cheetah_v5:HalfCheetahEnv", - max_episode_steps=1000, - reward_threshold=4800.0, - ) - - """ - register( - id="Hopper-v2", - entry_point="gymnasium_robotics.envs.mujoco:HopperEnv", - max_episode_steps=1000, - reward_threshold=3800.0, - ) - - register( - id="Hopper-v3", - entry_point="gymnasium_robotics.envs.mujoco.hopper_v3:HopperEnv", - max_episode_steps=1000, - reward_threshold=3800.0, - ) - - register( - id="Hopper-v4", - entry_point="gymnasium_robotics.envs.mujoco.hopper_v4:HopperEnv", - max_episode_steps=1000, - reward_threshold=3800.0, - ) - """ - - register( - id="Hopper-v5", - entry_point="gymnasium_robotics.envs.mujoco.hopper_v5:HopperEnv", - max_episode_steps=1000, - reward_threshold=3800.0, - ) - - """ - register( - id="Swimmer-v2", - entry_point="gymnasium_robotics.envs.mujoco:SwimmerEnv", - max_episode_steps=1000, - reward_threshold=360.0, - ) - - register( - id="Swimmer-v3", - entry_point="gymnasium_robotics.envs.mujoco.swimmer_v3:SwimmerEnv", - max_episode_steps=1000, - reward_threshold=360.0, - ) - - register( - id="Swimmer-v4", - entry_point="gymnasium_robotics.envs.mujoco.swimmer_v4:SwimmerEnv", - max_episode_steps=1000, - reward_threshold=360.0, - ) - """ - - register( - id="Swimmer-v5", - entry_point="gymnasium_robotics.envs.mujoco.swimmer_v5:SwimmerEnv", - max_episode_steps=1000, - reward_threshold=360.0, - ) - - """ - register( - id="Walker2d-v2", - max_episode_steps=1000, - entry_point="gymnasium_robotics.envs.mujoco:Walker2dEnv", - ) - - register( - id="Walker2d-v3", - max_episode_steps=1000, - entry_point="gymnasium_robotics.envs.mujoco.walker2d_v3:Walker2dEnv", - ) - - register( - id="Walker2d-v4", - max_episode_steps=1000, - entry_point="gymnasium_robotics.envs.mujoco.walker2d_v4:Walker2dEnv", - ) - """ - - register( - id="Walker2d-v5", - max_episode_steps=1000, - entry_point="gymnasium_robotics.envs.mujoco.walker2d_v5:Walker2dEnv", - ) - - """ - register( - id="Ant-v2", - entry_point="gymnasium_robotics.envs.mujoco:AntEnv", - max_episode_steps=1000, - reward_threshold=6000.0, - ) - - register( - id="Ant-v3", - entry_point="gymnasium_robotics.envs.mujoco.ant_v3:AntEnv", - max_episode_steps=1000, - reward_threshold=6000.0, - ) - - register( - id="Ant-v4", - entry_point="gymnasium_robotics.envs.mujoco.ant_v4:AntEnv", - max_episode_steps=1000, - reward_threshold=6000.0, - ) - """ - - register( - id="Ant-v5", - entry_point="gymnasium_robotics.envs.mujoco.ant_v5:AntEnv", - max_episode_steps=1000, - reward_threshold=6000.0, - ) - - """ - register( - id="Humanoid-v2", - entry_point="gymnasium_robotics.envs.mujoco:HumanoidEnv", - max_episode_steps=1000, - ) - - register( - id="Humanoid-v3", - entry_point="gymnasium_robotics.envs.mujoco.humanoid_v3:HumanoidEnv", - max_episode_steps=1000, - ) - - register( - id="Humanoid-v4", - entry_point="gymnasium_robotics.envs.mujoco.humanoid_v4:HumanoidEnv", - max_episode_steps=1000, - ) - """ - - register( - id="Humanoid-v5", - entry_point="gymnasium_robotics.envs.mujoco.humanoid_v5:HumanoidEnv", - max_episode_steps=1000, - ) - - """ - register( - id="HumanoidStandup-v2", - entry_point="gymnasium_robotics.envs.mujoco:HumanoidStandupEnv", - max_episode_steps=1000, - ) - - register( - id="HumanoidStandup-v4", - entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v4:HumanoidStandupEnv", - max_episode_steps=1000, - ) - """ - - register( - id="HumanoidStandup-v5", - entry_point="gymnasium_robotics.envs.mujoco.humanoidstandup_v5:HumanoidStandupEnv", - max_episode_steps=1000, - ) - __version__ = "1.2.2" diff --git a/tests/envs/mujoco/test_mujoco_v5.py b/tests/envs/mujoco/test_mujoco_v5.py deleted file mode 100644 index ab53a647..00000000 --- a/tests/envs/mujoco/test_mujoco_v5.py +++ /dev/null @@ -1,471 +0,0 @@ -import gymnasium as gym -import pytest -import numpy as np -from gymnasium.utils.env_checker import check_env -import mujoco -from gymnasium.error import Error -import warnings - -ALL_MUJOCO_ENVS = ['Ant', 'HalfCheetah', 'Hopper', 'Humanoid', 'HumanoidStandup', 'InvertedDoublePendulum', 'InvertedPendulum', "Pusher", "Reacher", "Swimmer", 'Walker2d'] - - -# Note: "HumnanoidStandup-v4" does not have `info` -# Note: "Humnanoid-v4/3" & "Ant-v4/3" fail this test -@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'HalfCheetah-v4', 'HalfCheetah-v3', 'Hopper-v5', 'Hopper-v4', 'Hopper-v3', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3', 'Walker2d-v5', 'Walker2d-v4', 'Walker2d-v3']) -def test_verify_info_x_position(env_id): - """Asserts that the environment has position[0] == info['x_position']""" - env = gym.make(env_id, exclude_current_positions_from_observation=False) - - _, _ = env.reset() - obs, _, _, _, info = env.step(env.action_space.sample()) - - assert obs[0] == info['x_position'] - - -# Note: "HumnanoidStandup-v4" does not have `info` -# Note: "Humnanoid-v4/3" & "Ant-v4/3" fail this test -@pytest.mark.parametrize("env_id", ['Ant-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5']) -def test_verify_info_y_position(env_id): - """Asserts that the environment has position[1] == info['y_position']""" - env = gym.make(env_id, exclude_current_positions_from_observation=False) - - _, _ = env.reset() - obs, _, _, _, info = env.step(env.action_space.sample()) - - assert obs[1] == info['y_position'] - - -# Note: "HumnanoidStandup-v4" does not have `info` -@pytest.mark.parametrize("env", ['HalfCheetah', 'Hopper', 'Swimmer', 'Walker2d']) -@pytest.mark.parametrize("version", ['v5', 'v4', 'v3']) -def test_verify_info_x_velocity(env, version): - """Asserts that the environment `info['x_velocity']` is properly assigned""" - env = gym.make(f"{env}-{version}") - env.reset() - - old_x = env.unwrapped.data.qpos[0] - _, _, _, _, info = env.step(env.action_space.sample()) - new_x = env.unwrapped.data.qpos[0] - - dx = new_x - old_x - vel_x = dx / env.dt - assert vel_x == info['x_velocity'] - - -# Note: "HumnanoidStandup-v4" does not have `info` -@pytest.mark.parametrize("env_id", ['Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3']) -def test_verify_info_y_velocity(env_id): - """Asserts that the environment `info['y_velocity']` is properly assigned""" - env = gym.make(env_id) - env.reset() - - old_y = env.unwrapped.data.qpos[1] - _, _, _, _, info = env.step(env.action_space.sample()) - new_y = env.unwrapped.data.qpos[1] - - dy = new_y - old_y - vel_y = dy / env.dt - assert vel_y == info['y_velocity'] - - -@pytest.mark.parametrize("env_id", ['Ant-v5', 'Ant-v4', 'Ant-v3']) -def test_verify_info_xy_velocity_xpos(env_id): - """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) - env.reset() - - old_xy = env.get_body_com("torso")[:2].copy() - _, _, _, _, info = env.step(env.action_space.sample()) - new_xy = env.get_body_com("torso")[:2].copy() - - dxy = new_xy - old_xy - vel_x, vel_y = dxy / env.dt - assert vel_x == info['x_velocity'] - assert vel_y == info['y_velocity'] - - -@pytest.mark.parametrize("env_id", ['Humanoid-v5', 'Humanoid-v4', 'Humanoid-v3']) -def test_verify_info_xy_velocity_com(env_id): - """Asserts that the environment `info['x/y_velocity']` is properly assigned, for the humanoid environment which uses kinmatics of Center Of Mass for the velocity""" - - def mass_center(model, data): - mass = np.expand_dims(model.body_mass, axis=1) - xpos = data.xipos - return (np.sum(mass * xpos, axis=0) / np.sum(mass))[0:2].copy() - - env = gym.make(env_id) - env.reset() - - old_xy = mass_center(env.unwrapped.model, env.unwrapped.data) - _, _, _, _, info = env.step(env.action_space.sample()) - new_xy = mass_center(env.unwrapped.model, env.unwrapped.data) - - dxy = new_xy - old_xy - vel_x, vel_y = dxy / env.dt - assert vel_x == info['x_velocity'] - assert vel_y == info['y_velocity'] - - -# Note: Hopper-v4/3/2 does not have `info['reward_survive']`, but it is still affected -# Note: Walker2d-v4/3/2 does not have `info['reward_survive']`, but it is still affected -# Note: Inverted(Double)Pendulum-v4/2 does not have `info['reward_survive']`, but it is still affected -# Note: all `v4/v3/v2` environments with a heathly reward are fail this test -@pytest.mark.parametrize("env_id", ['Ant-v5', 'Hopper-v5', 'Humanoid-v5', 'InvertedDoublePendulum-v5', 'InvertedPendulum-v5', 'Walker2d-v5']) -def test_verify_reward_survive(env_id): - """Assert that `reward_survive` is 0 on `terminal` states and not 0 on non-`terminal` states""" - env = gym.make(env_id, reset_noise_scale=0) - env.reset(seed=0) - env.action_space.seed(0) - - for step in range(175): - obs, rew, terminal, truncated, info = env.step(env.action_space.sample()) - - if terminal: - assert info['reward_survive'] == 0 - break - - assert info['reward_survive'] != 0 - - assert terminal, "The environment, should have terminated, if not the test is not valid." - - -CHECK_ENV_IGNORE_WARNINGS = [ - f"\x1b[33mWARN: {message}\x1b[0m" - for message in [ - "A Box observation space minimum value is -infinity. This is probably too low.", - "A Box observation space maximum value is -infinity. This is probably too high.", - "For Box action spaces, we recommend using a symmetric and normalized space (range=[-1, 1] or [0, 1]). See https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html for more information.", - ] -] - - -@pytest.mark.parametrize("env", ALL_MUJOCO_ENVS) -@pytest.mark.parametrize("version", ['v5']) -@pytest.mark.parametrize("frame_skip", [1, 2, 3, 4, 5]) -def test_frame_skip(env, version, frame_skip): - """Verify that all `mujoco` envs work with different `frame_skip` values""" - env_id = f"{env}-{version}" - env = gym.make(env_id, frame_skip=frame_skip) - - # Test if env adheres to Gym API - with warnings.catch_warnings(record=True) as w: - gym.utils.env_checker.check_env(env.unwrapped, skip_render_check=True) - env.close() - for warning in w: - if warning.message.args[0] not in CHECK_ENV_IGNORE_WARNINGS: - raise Error(f"Unexpected warning: {warning.message}") - - -# Dev Note: This can be version env parametrized because each env has it's own reward function -@pytest.mark.parametrize("version", ['v5']) -def test_reward_sum(version): - """Assert that the total reward equals the sum of the individual reward terms.""" - env = gym.make(f'Ant-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_contact"] - info["reward_survive"] < 1e-14 - - env = gym.make(f'HalfCheetah-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] < 1e-14 - - env = gym.make(f'Hopper-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_survive"] < 1e-14 - - env = gym.make(f'Humanoid-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_contact"] - info["reward_survive"] < 1e-14 - - env = gym.make(f'HumanoidStandup-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_linup"] - info["reward_quadctrl"] - info["reward_impact"] - 1 < 1e-14 - - env = gym.make(f'InvertedDoublePendulum-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_survive"] - info["distance_penalty"] - info["velocity_penalty"] < 1e-14 - - env = gym.make(f'InvertedPendulum-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward == info["reward_survive"] - - env = gym.make(f'Pusher-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_dist"] - info["reward_ctrl"] - info["reward_near"] < 1e-14 - - env = gym.make(f'Reacher-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_dist"] - info["reward_ctrl"] < 1e-14 - - env = gym.make(f'Swimmer-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] < 1e-14 - - env = gym.make(f'Walker2d-{version}') - env.reset() - _, reward, _, _, info = env.step(env.action_space.sample()) - assert reward - info["reward_forward"] - info["reward_ctrl"] - info["reward_survive"] < 1e-14 - - -# Note: the environtments that are not present, is because they do not have identical behaviour -@pytest.mark.parametrize("env", ['HalfCheetah', 'HumanoidStandup', 'Pusher', 'Reacher', 'Swimmer']) -def test_identical_behaviour_v45(env): - """Verify that v4 -> v5 transition. does not change the behaviour of the environments in way way""" - env_v4 = gym.make(env + '-v4') - env_v5 = gym.make(env + '-v5') - env_v4.reset(seed=1234) - env_v5.reset(seed=1234) - action = env_v4.action_space.sample() - obs_v4, rew_v4, terminal_v4, truncated_v4, info_v4 = env_v4.step(action) - obs_v5, rew_v5, terminal_v5, truncated_v5, info_v5 = env_v5.step(action) - assert obs_v4.shape[0] != 1 - assert obs_v5.shape[0] != 1 - assert (env_v4.unwrapped.data.qpos == env_v5.unwrapped.data.qpos).all() - assert (env_v4.unwrapped.data.qvel == env_v5.unwrapped.data.qvel).all() - if env not in ['HumanoidStandup', 'Reacher']: # they have different obs - assert (obs_v4 == obs_v5).all() - assert rew_v4 == rew_v5 - assert terminal_v4 == terminal_v5 and truncated_v4 == truncated_v5 - - -@pytest.mark.parametrize("version", ['v5', 'v4']) -def test_ant_com(version): - """Verify the kinmatic behaviour of the ant""" - env = gym.make(f'Ant-{version}') # `env` contains `data : MjData` and `model : MjModel` - env.reset() # randomly initlizies the `data.qpos` and `data.qvel`, calls mujoco.mj_forward(env.model, env.data) - - x_position_before = env.unwrapped.data.qpos[0] - x_position_before_com = env.unwrapped.data.body("torso").xpos[0] - assert x_position_before == x_position_before_com, "before failed" # This succeeds - - random_control = env.action_space.sample() - _, _, _, _, info = env.step(random_control) # This calls mujoco.mj_step(env.model, env.data, nstep=env.frame_skip) - mujoco.mj_kinematics(env.unwrapped.model, env.unwrapped.data) - - x_position_after = env.unwrapped.data.qpos[0] - x_position_after_com = env.unwrapped.data.body("torso").xpos[0] - assert x_position_after == x_position_after_com, "after failed" # This succeeds - - -@pytest.mark.parametrize("version", ['v5', 'v4', 'v3', 'v2']) -def test_set_state(version): - """Simple Test to verify that `mujocoEnv.set_state()` works correctly""" - env = gym.make(f'Hopper-{version}') - env.reset() - new_qpos = np.array([0.00136962, 1.24769787, -0.00459026, -0.00483472, 0.0031327 , 0.00412756]) - new_qvel = np.array([0.00106636, 0.00229497, 0.00043625, 0.00435072, 0.00315854, -0.00497261]) - env.set_state(new_qpos, new_qvel) - assert (env.unwrapped.data.qpos == new_qpos).all() - assert (env.unwrapped.data.qvel == new_qvel).all() - - -# Note: HumanoidStandup-v4/v3 does not have `info` -# Note: Ant-v4/v3 fails this test -# Note: Humanoid-v4/v3 fails this test -# Note: v2 does not have `info` -@pytest.mark.parametrize("env_id", ['Ant-v5', 'Humanoid-v5', 'Swimmer-v5', 'Swimmer-v4', 'Swimmer-v3']) -def test_distance_from_origin_info(env_id): - """Verify that `info"distance_from_origin"` is correct""" - env = gym.make(env_id) - env.reset() - _, _, _, _, info = env.step(env.action_space.sample()) - assert info["distance_from_origin"] == np.linalg.norm(env.unwrapped.data.qpos[0:2] - env.init_qpos[0:2]) - - -@pytest.mark.parametrize("env_id", ['Hopper-v5', 'HumanoidStandup-v5', 'Walker2d-v5']) -def test_z_distance_from_origin_info(env_id): - """Verify that `info"z_distance_from_origin"` is correct""" - env = gym.make(env_id) - env.reset() - _, _, _, _, info = env.step(env.action_space.sample()) - mujoco.mj_kinematics(env.unwrapped.model, env.unwrapped.data) - z_index = env.observation_structure["skipped_qpos"] - assert info["z_distance_from_origin"] == env.unwrapped.data.qpos[z_index] - env.init_qpos[z_index] - - -@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'Hopper-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'InvertedPendulum-v5', 'Swimmer-v5', 'Walker2d-v5']) -def test_observation_structure(env_id): - """Verify that the `env.observation_structure` is properly defined.""" - env = gym.make(env_id) - if hasattr(env, "observation_structure"): - return - - obs_struct = env.observation_structure - - assert env.unwrapped.model.nq == obs_struct.get("skipped_qpos", 0) + obs_struct["qpos"] - assert env.unwrapped.model.nv == obs_struct["qvel"] - if obs_struct.get("cinert", 0): - assert (env.unwrapped.model.nbody - 1) * 10 == obs_struct["cinert"] - if obs_struct.get("cvel", 0): - assert (env.unwrapped.model.nbody - 1) * 6 == obs_struct["cvel"] - if obs_struct.get("qfrc_actuator", 0): - assert env.unwrapped.model.nv - 6 == obs_struct["qfrc_actuator"] - if obs_struct.get("cfrc_ext", 0): - assert (env.unwrapped.model.nbody - 1) * 6 == obs_struct["cfrc_ext"] - if obs_struct.get("ten_lenght", 0): - assert env.unwrapped.model.ntendon == obs_struct["ten_lenght"] - if obs_struct.get("ten_velocity", 0): - assert env.unwrapped.model.ntendon == obs_struct["ten_velocity"] - - -@pytest.mark.parametrize("env_id", ['Ant-v5', 'HalfCheetah-v5', 'Hopper-v5', 'Humanoid-v5', 'HumanoidStandup-v5', 'Swimmer-v5', 'Walker2d-v5']) -def test_reset_info(env_id): - """Verify that the environmet returns info at `reset()`""" - env = gym.make(env_id) - _, reset_info = env.reset() - assert reset_info.get('x_position') - - -""" -[Bug Report] [Documentation] Inverted Double Pendulum max Height is wrong - -The Documentation States: -```md -The maximum standing height of the system is 1.196 m when all the parts are perpendicularly vertical on top of each other) -``` -but the height of each pole is 0.6 (0.6+0.6==1.2) -https://github.com/Farama-Foundation/Gymnasium/blob/deb50802facfd827abd4d1f0cf1069afb12a726b/gymnasium/envs/mujoco/assets/inverted_double_pendulum.xml#L33-L39 -""" - - -# Note: the max height used to be wrong in the documention. -@pytest.mark.parametrize("version", ['v5']) -def test_inverted_double_pendulum_max_height(version): - """Verify the max height of Inverted Double Pendulum""" - env = gym.make(f"InvertedDoublePendulum-{version}", reset_noise_scale=0) - env.reset() - y = env.unwrapped.data.site_xpos[0][2] - assert y == 1.2 - - -@pytest.mark.parametrize("version", ['v4']) -def test_inverted_double_pendulum_max_height_old(version): - """Verify the max height of Inverted Double Pendulum (v4 does not have `reset_noise_scale` argument)""" - env = gym.make(f"InvertedDoublePendulum-{version}") - env.set_state(env.init_qpos, env.init_qvel) - y = env.unwrapped.data.site_xpos[0][2] - assert y == 1.2 - - -# note: fails with `brax==0.9.0` -@pytest.mark.parametrize("version", ['v5', 'v4']) -def test_model_object_count(version): - """Verify that all the objects of the model are loaded, mostly usefull for using non-mujoco simulator.""" - env = gym.make(f'Ant-{version}') - assert env.unwrapped.model.nq == 15 - assert env.unwrapped.model.nv == 14 - assert env.unwrapped.model.nu == 8 - assert env.unwrapped.model.nbody == 14 - assert env.unwrapped.model.nbvh == 14 - assert env.unwrapped.model.njnt == 9 - assert env.unwrapped.model.ngeom == 14 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'HalfCheetah-{version}') - assert env.unwrapped.model.nq == 9 - assert env.unwrapped.model.nv == 9 - assert env.unwrapped.model.nu == 6 - assert env.unwrapped.model.nbody == 8 - assert env.unwrapped.model.nbvh == 10 - assert env.unwrapped.model.njnt == 9 - assert env.unwrapped.model.ngeom == 9 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Hopper-{version}') - assert env.unwrapped.model.nq == 6 - assert env.unwrapped.model.nv == 6 - assert env.unwrapped.model.nu == 3 - assert env.unwrapped.model.nbody == 5 - assert env.unwrapped.model.nbvh == 5 - assert env.unwrapped.model.njnt == 6 - assert env.unwrapped.model.ngeom == 5 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Humanoid-{version}') - assert env.unwrapped.model.nq == 24 - assert env.unwrapped.model.nv == 23 - assert env.unwrapped.model.nu == 17 - assert env.unwrapped.model.nbody == 14 - assert env.unwrapped.model.nbvh == 22 - assert env.unwrapped.model.njnt == 18 - assert env.unwrapped.model.ngeom == 18 - assert env.unwrapped.model.ntendon == 2 - - env = gym.make(f'HumanoidStandup-{version}') - assert env.unwrapped.model.nq == 24 - assert env.unwrapped.model.nv == 23 - assert env.unwrapped.model.nu == 17 - assert env.unwrapped.model.nbody == 14 - assert env.unwrapped.model.nbvh == 22 - assert env.unwrapped.model.njnt == 18 - assert env.unwrapped.model.ngeom == 18 - assert env.unwrapped.model.ntendon == 2 - - env = gym.make(f'InvertedDoublePendulum-{version}') - assert env.unwrapped.model.nq == 3 - assert env.unwrapped.model.nv == 3 - assert env.unwrapped.model.nu == 1 - assert env.unwrapped.model.nbody == 4 - assert env.unwrapped.model.nbvh == 6 - assert env.unwrapped.model.njnt == 3 - assert env.unwrapped.model.ngeom == 5 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'InvertedPendulum-{version}') - assert env.unwrapped.model.nq == 2 - assert env.unwrapped.model.nv == 2 - assert env.unwrapped.model.nu == 1 - assert env.unwrapped.model.nbody == 3 - assert env.unwrapped.model.nbvh == 3 - assert env.unwrapped.model.njnt == 2 - assert env.unwrapped.model.ngeom == 3 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Pusher-{version}') - assert env.unwrapped.model.nq == 11 - assert env.unwrapped.model.nv == 11 - assert env.unwrapped.model.nu == 7 - assert env.unwrapped.model.nbody == 13 - assert env.unwrapped.model.nbvh == 18 - assert env.unwrapped.model.njnt == 11 - assert env.unwrapped.model.ngeom == 21 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Reacher-{version}') - assert env.unwrapped.model.nq == 4 - assert env.unwrapped.model.nv == 4 - assert env.unwrapped.model.nu == 2 - assert env.unwrapped.model.nbody == 5 - assert env.unwrapped.model.nbvh == 5 - assert env.unwrapped.model.njnt == 4 - assert env.unwrapped.model.ngeom == 10 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Swimmer-{version}') - assert env.unwrapped.model.nq == 5 - assert env.unwrapped.model.nv == 5 - assert env.unwrapped.model.nu == 2 - assert env.unwrapped.model.nbody == 4 - assert env.unwrapped.model.nbvh == 4 - assert env.unwrapped.model.njnt == 5 - assert env.unwrapped.model.ngeom == 4 - assert env.unwrapped.model.ntendon == 0 - - env = gym.make(f'Walker2d-{version}') - assert env.unwrapped.model.nq == 9 - assert env.unwrapped.model.nv == 9 - assert env.unwrapped.model.nu == 6 - assert env.unwrapped.model.nbody == 8 - assert env.unwrapped.model.nbvh == 8 - assert env.unwrapped.model.njnt == 9 - assert env.unwrapped.model.ngeom == 8 - assert env.unwrapped.model.ntendon == 0 From cf7ebe39224772778773f4f846140ffcffe29fa7 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 3 Oct 2023 17:50:31 +0300 Subject: [PATCH 105/160] cleaup local/global cat generation --- .../envs/multiagent_mujoco/mujoco_multi.py | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 76d99a4e..9a4595ed 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -503,25 +503,18 @@ def _generate_local_categories(self, scenario: str) -> list[list[str]]: if self.agent_obsk is None: return [[]] - if scenario in ["Ant-v4", "ManySegmentAnt"]: - # k_split = ["qpos,qvel,cfrc_ext", "qpos"] # Gymansium.MuJoCo.Ant-v4 has disabled cfrc_ext by default - k_split = ["qpos,qvel", "qpos"] - elif scenario in ["Humanoid-v4", "HumanoidStandup-v4"]: - k_split = [ - "qpos,qvel,cinert,cvel,qfrc_actuator,cfrc_ext", - "qpos", - ] - elif scenario in ["CoupledHalfCheetah-v4"]: - k_split = ["qpos,qvel,ten_J,ten_length,ten_velocity", "qpos"] - elif scenario in ["Reacher-v4"]: - k_split = ["qpos,qvel,fingertip_dist", "qpos"] + if scenario in ["Ant-v5", "ManySegmentAnt"]: + k_categories = [['qpos', 'qvel', 'cfrc_ext'], ['qpos']] + elif scenario in ["Humanoid-v5", "HumanoidStandup-v5"]: + k_categories = [['qpos', 'qvel', 'cinert', 'cvel', 'qfrc_actuator', 'cfrc_ext'], ['qpos']] + elif scenario in ["CoupledHalfCheetah-v5"]: + k_categories = [['qpos', 'qvel', 'ten_J', 'ten_length', 'ten_velocity'], ['qpos']] + elif scenario in ["Reacher-v5"]: + k_categories = [['qpos', 'qvel', 'fingertip_dist'], ['qpos']] else: - k_split = ["qpos,qvel", "qpos"] + k_categories = [['qpos', 'qvel'], ['qpos']] - categories = [ - k_split[k if k < len(k_split) else -1].split(",") - for k in range(self.agent_obsk + 1) - ] + categories = [k_categories[k if k < len(k_categories) else -1] for k in range(self.agent_obsk + 1)] return categories def _generate_global_categories(self, scenario: str) -> tuple[str, ...]: @@ -536,14 +529,7 @@ def _generate_global_categories(self, scenario: str) -> tuple[str, ...]: if self.agent_obsk is None: return () - if scenario in ["Ant-v4", "ManySegmentAnt"]: - return ("qpos", "qvel") - elif scenario in ["Humanoid-v4", "HumanoidStandup-v4"]: - return ("qpos", "qvel", "cinert", "cvel", "qfrc_actuator", "cfrc_ext") - elif scenario in ["CoupledHalfCheetah-v4"]: - return ("qpos", "qvel") - else: - return ("qpos", "qvel") + return ("qpos", "qvel") # These are the export functions (for `PettingZoo` style exportations) From 160b783534ad47c7f3322ff79e751052eb599842 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 3 Oct 2023 17:55:03 +0300 Subject: [PATCH 106/160] remove `osbk._observation_structure` --- .../envs/multiagent_mujoco/obsk.py | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 35389e4c..69a27083 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -1062,74 +1062,3 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals else: raise Exception(f"UNKNOWN label environment: {label}") - - -def _observation_structure(scenario: str) -> dict[str, int]: - """Get the types of observations for each Gymnasium.MuJoCo environment. - - Args: - scenario: the mujoco scenartio - - Returns: - a dictionary keyied by observation type with values indicating the number of observations for that type - """ - ret = { - "skipped_qpos": 0, # Position data what is excluded/skip - "qpos": 0, # Position - "qvel": 0, # Velocity - "cinert": 0, # com inertia - "cvel": 0, # com velocity - "qfrc_actuator": 0, # Actuator Forces - "cfrc_ext": 0, # Contact Forces - } - - if scenario == "Ant-v4": - ret["skipped_qpos"] = 2 - ret["qpos"] = 13 - ret["qvel"] = 14 - # ret["cfrc_ext"] = 84 - elif scenario == "HalfCheetah-v4": - ret["skipped_qpos"] = 1 - ret["qpos"] = 8 - ret["qvel"] = 9 - elif scenario == "Hopper-v4": - ret["skipped_qpos"] = 1 - ret["qpos"] = 5 - ret["qvel"] = 6 - elif scenario == "HumanoidStandup-v4" or scenario == "Humanoid-v4": - ret["skipped_qpos"] = 2 - ret["qpos"] = 22 - ret["qvel"] = 23 - ret["cinert"] = 140 - ret["cvel"] = 84 - ret["qfrc_actuator"] = 23 - ret["cfrc_ext"] = 84 - elif scenario == "InvertedDoublePendulum-v4": - assert False, scenario + "can not be factorized" - ret["qpos"] = 3 - ret["qvel"] = 3 - # qfrc_constraint = 3 - elif scenario == "InvertedPendulum-v4": - assert False, scenario + "can not be factorized" - ret["qpos"] = 2 - ret["qvel"] = 2 - elif scenario == "Pusher-v4": - assert False, scenario + "is not supported" - ret["qpos"] = 7 - ret["qvel"] = 7 - # 9 body_com - elif scenario == "Reacher-v4": - assert False, scenario + "can not be factorized" - ret["qpos"] = 6 - ret["qvel"] = 2 - # 3 body_com - elif scenario == "Swimmer-v4": - ret["skipped_qpos"] = 2 - ret["qpos"] = 3 - ret["qvel"] = 5 - elif scenario == "Walker2d-v4": - ret["skipped_qpos"] = 1 - ret["qpos"] = 8 - ret["qvel"] = 9 - - return ret From eab7f08098f2b6ebbfd1f954ea720f7de52f4875 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Oct 2023 11:51:40 +0300 Subject: [PATCH 107/160] type hint adition --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 9a4595ed..6079cde8 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -75,7 +75,7 @@ def __init__( scenario: str, agent_conf: str | None, agent_obsk: int | None = 1, - agent_factorization: dict | None = None, + agent_factorization: dict[str, any] | None = None, local_categories: list[list[str]] | None = None, global_categories: tuple[str, ...] | None = None, render_mode: str | None = None, From 651e398ebcbc570e44a714ad0389959d49fc9332 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Oct 2023 11:52:51 +0300 Subject: [PATCH 108/160] doc typo fix --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 6079cde8..0e34de44 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -108,7 +108,7 @@ def __init__( kwargs: Additional arguments passed to the [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) environment, Note: arguments that change the observation space will not work. - Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values) + Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values). """ scenario += "-v4" From 8f2bc1d6b58677aa6c485c73f97ebbfb7ed57ee7 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Oct 2023 11:57:30 +0300 Subject: [PATCH 109/160] more to `gymnasium/mujoco-v5` --- .../envs/multiagent_mujoco/mujoco_multi.py | 28 +++++++++---------- .../envs/multiagent_mujoco/obsk.py | 25 ++++++++--------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 0e34de44..5602b77b 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -42,17 +42,17 @@ # support other Gymnasium-Robotics MuJoCo environments _MUJOCO_GYM_ENVIROMENTS = [ - "Ant-v4", - "HalfCheetah-v4", - "Hopper-v4", - "HumanoidStandup-v4", - "Humanoid-v4", - "Reacher-v4", - "Swimmer-v4", - "Pusher-v4", - "Walker2d-v4", - "InvertedPendulum-v4", - "InvertedDoublePendulum-v4", + "Ant-v5", + "HalfCheetah-v5", + "Hopper-v5", + "HumanoidStandup-v5", + "Humanoid-v5", + "Reacher-v5", + "Swimmer-v5", + "Pusher-v5", + "Walker2d-v5", + "InvertedPendulum-v5", + "InvertedDoublePendulum-v5", ] @@ -110,14 +110,14 @@ def __init__( Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values). """ - scenario += "-v4" + scenario += "-v5" # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` if scenario in _MUJOCO_GYM_ENVIROMENTS: self.single_agent_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv = ( gymnasium.make(scenario, **kwargs, render_mode=render_mode) ) - elif scenario in ["ManySegmentAnt-v4"]: + elif scenario in ["ManySegmentAnt-v5"]: assert isinstance(agent_conf, str) try: n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) @@ -137,7 +137,7 @@ def __init__( self.single_agent_env = TimeLimit( ManySegmentSwimmerEnv(n_segs, render_mode), max_episode_steps=1000 ) - elif scenario in ["CoupledHalfCheetah-v4"]: + elif scenario in ["CoupledHalfCheetah-v5"]: self.single_agent_env = TimeLimit( CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000 ) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 69a27083..13f02680 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -240,8 +240,7 @@ def get_parts_and_edges( # noqa: C901 Returns: the partition of the mujoco graph nodes, the graph edges, and global nodes """ - if label in ["HalfCheetah-v4"]: - + if label in ["HalfCheetah-v5"]: # define Mujoco graph bthigh = Node("bthigh", -6, -6, 0) bshin = Node("bshin", -5, -5, 1) @@ -276,8 +275,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Ant-v4"]: - + elif label in ["Ant-v5"]: # define Mujoco graph torso = 1 front_left_leg = 2 @@ -392,8 +390,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Hopper-v4"]: - + elif label in ["Hopper-v5"]: # define Mujoco-Graph thigh_joint = Node( "thigh_joint", @@ -467,7 +464,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Humanoid-v4", "HumanoidStandup-v4"]: + elif label in ["Humanoid-v5", "HumanoidStandup-v5"]: # bodies # worldbody = 0 torso = 1 @@ -608,7 +605,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Reacher-v4"]: + elif label in ["Reacher-v5"]: # define Mujoco-Graph # worldbody = 0 body0 = 1 @@ -679,7 +676,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Pusher-v4"]: + elif label in ["Pusher-v5"]: # define Mujoco-Graph r_shoulder_pan_joint = Node("r_wrist_roll_joint", 0, 0, 0) r_shoulder_lift_joint = Node("r_wrist_roll_joint", 1, 1, 1) @@ -759,7 +756,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Swimmer-v4"]: + elif label in ["Swimmer-v5"]: # define Mujoco-Graph joint0 = Node( "rot2", @@ -794,7 +791,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Walker2d-v4"]: + elif label in ["Walker2d-v5"]: # define Mujoco-Graph thigh_joint = Node("thigh_joint", -6, -6, 0) leg_joint = Node("leg_joint", -5, -5, 1) @@ -844,7 +841,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["CoupledHalfCheetah-v4"]: + elif label in ["CoupledHalfCheetah-v5"]: # define Mujoco graph tendon = 0 @@ -940,7 +937,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["ManySegmentSwimmer-v4"]: + elif label in ["ManySegmentSwimmer-v5"]: assert partitioning is not None, "Partitioning, required with " + label try: @@ -965,7 +962,7 @@ def get_parts_and_edges( # noqa: C901 ] return parts, edges, globals - elif label in ["ManySegmentAnt-v4"]: + elif label in ["ManySegmentAnt-v5"]: assert partitioning is not None, "Partitioning, required with " + label try: From ed9f05f8066e569ebc9e5f86290b1e18ab5108f4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Oct 2023 12:27:47 +0300 Subject: [PATCH 110/160] disable const 0 observation (`Ant`&`Humanoid`) --- gymnasium_robotics/envs/multiagent_mujoco/obsk.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 13f02680..1ad6aac8 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -364,18 +364,17 @@ def get_parts_and_edges( # noqa: C901 HyperEdge(hip4, hip1, hip2, hip3), ] - torso = Node( - "torso", + root = Node( + "root", 0, 0, None, extra_obs={ "qpos": lambda data: data.qpos[2:7], "qvel": lambda data: data.qvel[:6], - "cfrc_ext": lambda data: np.clip(data.cfrc_ext[0:1], -1, 1), }, ) - globals = [torso] + globals = [root] if partitioning is None: parts = [(hip4, ankle4, hip1, ankle1, hip2, ankle2, hip3, ankle3)] @@ -546,8 +545,6 @@ def get_parts_and_edges( # noqa: C901 extra_obs={ "qpos": lambda data: data.qpos[2:7], "qvel": lambda data: data.qvel[:6], - "qfrc_actuator": lambda data: data.qfrc_actuator[:6], - # "cfrc_ext": lambda data: np.clip(data.cfrc_ext[0:1], -1, 1), }, ) globals = [root] From 73938be8095b3a68ecb3b206d8d6e4c3d6506a9a Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 10 Oct 2023 12:31:47 +0300 Subject: [PATCH 111/160] port to v5 (missed one) --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 5602b77b..69559f70 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -127,7 +127,7 @@ def __init__( self.single_agent_env = TimeLimit( ManySegmentAntEnv(n_segs, render_mode), max_episode_steps=1000 ) - elif scenario in ["ManySegmentSwimmer-v4"]: + elif scenario in ["ManySegmentSwimmer-v5"]: assert isinstance(agent_conf, str) try: n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) From 0a5be3a9e69dd4fb8f9d5f10ba8a49f3240b0df4 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Oct 2023 10:25:12 +0300 Subject: [PATCH 112/160] `dummy_node`, when `factorizatoion=None` now includes action_id --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 69559f70..a8e4863b 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -162,11 +162,9 @@ def __init__( mujoco_edges = agent_factorization["edges"] self.mujoco_globals = agent_factorization["globals"] else: - assert self.single_agent_env.action_space.shape is not None - dummy_node = Node("dummy_node", None, None, None) self.agent_action_partitions = [ tuple( - dummy_node + Node("dummy_node", None, None, i) for i in range(self.single_agent_env.action_space.shape[0]) ) ] From 7f7e83cbfb6f8432b858bcf9e88fbc203442cd27 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Oct 2023 10:29:19 +0300 Subject: [PATCH 113/160] update `map_global_state_to_local_observations` for `v5` --- .../envs/multiagent_mujoco/mujoco_multi.py | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index a8e4863b..494c1f8a 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -31,7 +31,6 @@ ) from gymnasium_robotics.envs.multiagent_mujoco.obsk import ( Node, - _observation_structure, build_obs, get_joints_at_kdist, get_parts_and_edges, @@ -320,8 +319,8 @@ def map_global_action_to_local_actions( return local_actions def map_global_state_to_local_observations( - self, global_state: np.ndarray - ) -> dict[str, np.ndarray]: + self, global_state: np.ndarray[np.float64] + ) -> dict[str, np.ndarray[np.float64]]: """Maps single agent observation into multi agent observation spaces. Args: @@ -333,6 +332,10 @@ def map_global_state_to_local_observations( """ if self.agent_obsk is None: return {self.possible_agents[0]: global_state} + if not hasattr(self.single_agent_env, "observation_structure"): + assert ( + False + ), "to map states the MuJoCo environment must have `observation_structure` member variable" class data_struct: def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): @@ -343,43 +346,50 @@ def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): self.qfrc_actuator = qfrc_actuator self.cfrc_ext = cfrc_ext - obs_struct = _observation_structure(self.single_agent_env.spec.id) + obs_struct = self.single_agent_env.unwrapped.observation_structure qpos_end_index = obs_struct["qpos"] qvel_end_index = qpos_end_index + obs_struct["qvel"] - cinert_end_index = qvel_end_index + obs_struct["cinert"] - cvel_end_index = cinert_end_index + obs_struct["cvel"] - qfrc_actuator_end_index = cvel_end_index + obs_struct["qfrc_actuator"] - cfrc_ext_end_index = qfrc_actuator_end_index + obs_struct["cfrc_ext"] + cinert_end_index = qvel_end_index + obs_struct.get("cinert", 0) + cvel_end_index = cinert_end_index + obs_struct.get("cvel", 0) + qfrc_actuator_end_index = cvel_end_index + obs_struct.get("qfrc_actuator", 0) + cfrc_ext_end_index = qfrc_actuator_end_index + obs_struct.get("cfrc_ext", 0) - assert len(global_state) == cfrc_ext_end_index + assert len(global_state) == cfrc_ext_end_index, "wrong indexing" mujoco_data = data_struct( qpos=np.concatenate( - ( + [ np.zeros(obs_struct["skipped_qpos"]), global_state[0:qpos_end_index], - ) + ] ), qvel=np.array(global_state[qpos_end_index:qvel_end_index]), - cinert=np.array(global_state[qvel_end_index:cinert_end_index]), - cvel=np.array(global_state[cinert_end_index:cvel_end_index]), - qfrc_actuator=np.array( - global_state[cvel_end_index:qfrc_actuator_end_index] + cinert=np.concatenate( + [np.zeros(10), global_state[qvel_end_index:cinert_end_index]] + ), + cvel=np.concatenate( + [np.zeros(6), global_state[cinert_end_index:cvel_end_index]] + ), + qfrc_actuator=np.concatenate( + [np.zeros(6), global_state[cvel_end_index:qfrc_actuator_end_index]] + ), + cfrc_ext=np.concatenate( + [np.zeros(6), global_state[qfrc_actuator_end_index:cfrc_ext_end_index]] ), - cfrc_ext=np.array(global_state[qfrc_actuator_end_index:cfrc_ext_end_index]), ) - if len(mujoco_data.cinert) != 0: + if len(mujoco_data.cinert) > 10: mujoco_data.cinert = np.reshape( - mujoco_data.cinert, self.single_agent_env.data.cinert.shape + mujoco_data.cinert, self.single_agent_env.unwrapped.data.cinert.shape ) - if len(mujoco_data.cvel) != 0: + if len(mujoco_data.cvel) > 6: mujoco_data.cvel = np.reshape( - mujoco_data.cvel, self.single_agent_env.data.cvel.shape + mujoco_data.cvel, self.single_agent_env.unwrapped.data.cvel.shape ) - if len(mujoco_data.cfrc_ext) != 0: + if len(mujoco_data.cfrc_ext) > 6: mujoco_data.cfrc_ext = np.reshape( - mujoco_data.cfrc_ext, self.single_agent_env.data.cfrc_ext.shape + mujoco_data.cfrc_ext, + self.single_agent_env.unwrapped.data.cfrc_ext.shape, ) assert len(self.single_agent_env.unwrapped.data.qpos.flat) == len( @@ -445,7 +455,7 @@ def _get_obs_agent(self, agent_id: int, data=None) -> np.ndarray: if self.agent_obsk is None: return self.single_agent_env.unwrapped._get_obs() if data is None: - data = self.single_agent_env.data + data = self.single_agent_env.unwrapped.data return build_obs( data, From cf23584538e4a0904554b301d6d53b262ddbaa99 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 12 Oct 2023 13:42:18 +0300 Subject: [PATCH 114/160] fix `Walker2d` globals from `[root_x, root_x, root_z]` to `[root_x, root_z, root_y]` --- gymnasium_robotics/envs/multiagent_mujoco/obsk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 1ad6aac8..59c975b4 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -809,7 +809,7 @@ def get_parts_and_edges( # noqa: C901 ) root_z = Node("root_z", 1, 1, None) root_y = Node("root_y", 2, 2, None) - globals = [root_x, root_x, root_z] + globals = [root_x, root_z, root_y] if partitioning is None: parts = [ From 3f58f72040b6678d66cebb95512924a41d0ee37d Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 16 Oct 2023 11:54:39 +0300 Subject: [PATCH 115/160] add `ignore_body_fn` in `build_obs` --- gymnasium_robotics/envs/multiagent_mujoco/obsk.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 59c975b4..193602b1 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -160,6 +160,7 @@ def build_obs( k_categories: list[list[str]], global_nodes: list[Node], global_categories: tuple[str, ...], + ignore_body_fn : bool = False, ) -> np.ndarray: """Given a k_dict from get_joints_at_kdist, extract observation vector. @@ -169,6 +170,7 @@ def build_obs( k_categories: the categories at every depth level global_nodes: The MuJoCo global godes global_categories: The observation Categories for the global MuJoCo nodes + ingore_body_fn: If `True` it ignores the nodes's `body_fn` membeer variable Returns: observation for the agent (indicated by K_dict) @@ -195,7 +197,7 @@ def build_obs( body_set_dict[category] = set() if body not in body_set_dict[category]: items = getattr(data, category)[body].tolist() - if node.body_fn is not None: + if node.body_fn is not None and not ignore_body_fn: items = node.body_fn(body, items) obs_lst.extend( items if isinstance(items, list) else [items] @@ -220,7 +222,7 @@ def build_obs( body_set_dict[category] = set() if body not in body_set_dict[category]: items = getattr(data, category)[body].tolist() - if joint.body_fn is not None: + if joint.body_fn is not None and not ignore_body_fn: items = joint.body_fn(body, items) obs_lst.extend(items if isinstance(items, list) else [items]) body_set_dict[category].add(body) From 1f7ca8a3f80540de8014ae05b985b97c1230c1ac Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 16 Oct 2023 12:01:52 +0300 Subject: [PATCH 116/160] enchance observation mapping --- .../envs/multiagent_mujoco/mujoco_multi.py | 109 ++++++++++++------ tests/envs/MaMuJoCo/test_MaMuJoCo.py | 9 ++ 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 494c1f8a..c632dbad 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -197,6 +197,8 @@ def __init__( for agent_id in range(self.num_agents) ] + self.observation_factorization = self.create_observation_mapping() + # Create observation and action spaces self.observation_spaces, self.action_spaces = {}, {} for agent_id, partition in enumerate(self.agent_action_partitions): @@ -330,12 +332,66 @@ def map_global_state_to_local_observations( Returns: A dictionary of states that would be observed by each agent given the 'global_state' """ + assert ( + self.observation_factorization is not None + ), "to map states the MuJoCo environment must have `observation_structure` member variable" + + local_observation = {} + for agent, partition in self.observation_factorization.items(): + local_observation[agent] = global_state[partition] + + # assert sizes + assert len(local_observation) == len(self.action_spaces) + for agent in self.possible_agents: + assert len(local_observation[agent]) == self.observation_spaces[agent].shape[0] + + return local_observation + + def map_local_observations_to_global_state( + self, local_observation: np.ndarray[np.float64] + ) -> np.ndarray[np.float64]: + """Maps multi agent observations into single agent observation space. + + Args: + local_obserations: + the local observation of each agents (generated from MaMuJoCo.step()) + + Returns: + the global observations that correspond to a single agent (what you would get with MaMuJoCo.state()) + """ + assert ( + self.observation_factorization is not None + ), "to map states the MuJoCo environment must have `observation_structure` member variable" + + global_observation = ( + np.zeros((self.single_agent_env.observation_space.shape[0],)) + np.nan + ) + + for agent, partition in self.observation_factorization.items(): + for local_idx, global_idx in enumerate(partition): + assert np.isnan(global_observation[global_idx]) or global_observation[global_idx] == local_observation[agent][local_idx], "FATAL: At least one gym_env observation is doubly defined!" + global_observation[global_idx] = local_observation[agent][local_idx] + + if np.isnan(global_observation).any(): + breakpoint() + assert not np.isnan( + global_observation + ).any(), "FATAL: At least one gym_env observation is undefined!" + return global_observation + + return None + + def create_observation_mapping(self) -> dict[str, np.ndarray[np.float64]]: + """Creates a cache of the observation factorization. + The cache is intented to be used with `map_global_state_to_local_observations` & `map_local_observations_to_global_state`. + + Returns: + A cache that indexes global osbervations to local. + """ if self.agent_obsk is None: - return {self.possible_agents[0]: global_state} + return {self.possible_agents[0] : np.arange(self.single_agent_env.observation_space.shape[0])} if not hasattr(self.single_agent_env, "observation_structure"): - assert ( - False - ), "to map states the MuJoCo environment must have `observation_structure` member variable" + return None # TODO class data_struct: def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): @@ -354,27 +410,28 @@ def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): qfrc_actuator_end_index = cvel_end_index + obs_struct.get("qfrc_actuator", 0) cfrc_ext_end_index = qfrc_actuator_end_index + obs_struct.get("cfrc_ext", 0) - assert len(global_state) == cfrc_ext_end_index, "wrong indexing" + global_index = np.arange(self.single_agent_env.observation_space.shape[0]) + assert len(global_index) == cfrc_ext_end_index, "wrong indexing" mujoco_data = data_struct( qpos=np.concatenate( [ - np.zeros(obs_struct["skipped_qpos"]), - global_state[0:qpos_end_index], + np.zeros(obs_struct["skipped_qpos"], dtype=np.int64), + global_index[0:qpos_end_index], ] ), - qvel=np.array(global_state[qpos_end_index:qvel_end_index]), + qvel=np.array(global_index[qpos_end_index:qvel_end_index]), cinert=np.concatenate( - [np.zeros(10), global_state[qvel_end_index:cinert_end_index]] + [np.zeros(10, dtype=np.int64), global_index[qvel_end_index:cinert_end_index]] ), cvel=np.concatenate( - [np.zeros(6), global_state[cinert_end_index:cvel_end_index]] + [np.zeros(6, dtype=np.int64), global_index[cinert_end_index:cvel_end_index]] ), qfrc_actuator=np.concatenate( - [np.zeros(6), global_state[cvel_end_index:qfrc_actuator_end_index]] + [np.zeros(6, dtype=np.int64), global_index[cvel_end_index:qfrc_actuator_end_index]] ), cfrc_ext=np.concatenate( - [np.zeros(6), global_state[qfrc_actuator_end_index:cfrc_ext_end_index]] + [np.zeros(6, dtype=np.int64), global_index[qfrc_actuator_end_index:cfrc_ext_end_index]] ), ) @@ -399,28 +456,10 @@ def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): mujoco_data.qvel ) - observations = {} + local_index = {} for agent_id, agent in enumerate(self.possible_agents): - observations[agent] = self._get_obs_agent(agent_id, mujoco_data) - return observations - - def map_local_observation_to_global_state( - self, local_observations: dict[str, np.ndarray] - ) -> np.ndarray: - """Maps multi agent observations into single agent observation space. - - NOT IMPLEMENTED, try using MaMuJoCo.state() instead - - Args: - local_obserations: - the local observation of each agents (generated from MaMuJoCo.step()) - - Returns: - the global observations that correspond to a single agent (what you would get with MaMuJoCo.state()) - """ - # Dev notes for anyone who attempts to implement it: - # - Depending on the factorization the local observations may not observe the total global observable space, you will need to handle that - raise NotImplementedError + local_index[agent] = self._get_obs_agent(agent_id, mujoco_data) + return local_index def observation_space(self, agent: str) -> gymnasium.spaces.Box: """See [pettingzoo.utils.env.ParallelEnv.observation_space](https://pettingzoo.farama.org/api/parallel/#pettingzoo.utils.env.ParallelEnv.observation_space).""" @@ -454,8 +493,11 @@ def _get_obs_agent(self, agent_id: int, data=None) -> np.ndarray: """ if self.agent_obsk is None: return self.single_agent_env.unwrapped._get_obs() + + index_only = True if data is None: data = self.single_agent_env.unwrapped.data + index_only = False return build_obs( data, @@ -463,6 +505,7 @@ def _get_obs_agent(self, agent_id: int, data=None) -> np.ndarray: self.k_categories, self.mujoco_globals, self.global_categories, + index_only ) def reset(self, seed: int | None = None, options=None): diff --git a/tests/envs/MaMuJoCo/test_MaMuJoCo.py b/tests/envs/MaMuJoCo/test_MaMuJoCo.py index 1570b682..5d326e02 100644 --- a/tests/envs/MaMuJoCo/test_MaMuJoCo.py +++ b/tests/envs/MaMuJoCo/test_MaMuJoCo.py @@ -90,6 +90,15 @@ def test_action_and_observation_mapping(observation_depth, task): local_observations, ) + data_equivalence( + test_env.map_local_observations_to_global_state(local_observations), + global_observations, + ) + + # sanity check making sure the observation factorizations are sane + for agent_obs_factor in test_env.observation_factorization.values(): + len(agent_obs_factor) != len(set(agent_obs_factor)), "an agent observes the same state value multiple times" + @pytest.mark.parametrize("observation_depth", observation_depths) @pytest.mark.parametrize("task", sample_configurations) From 80e8ce29733eacb6e58865cb61eae8cda3429b5c Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 16 Oct 2023 17:05:12 +0300 Subject: [PATCH 117/160] fix map_obs_local2global error msg --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index c632dbad..63860bb8 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -372,11 +372,9 @@ def map_local_observations_to_global_state( assert np.isnan(global_observation[global_idx]) or global_observation[global_idx] == local_observation[agent][local_idx], "FATAL: At least one gym_env observation is doubly defined!" global_observation[global_idx] = local_observation[agent][local_idx] - if np.isnan(global_observation).any(): - breakpoint() assert not np.isnan( global_observation - ).any(), "FATAL: At least one gym_env observation is undefined!" + ).any(), "FATAL: At least one gym_env observation is undefined, observations can not be mapped." return global_observation return None From ff7e64754fae585f6cc368547713118eff224da2 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 16 Oct 2023 18:03:10 +0300 Subject: [PATCH 118/160] remove `ManySegment` classes --- .../envs/multiagent_mujoco/__init__.py | 6 - .../multiagent_mujoco/many_segment_ant.py | 226 ++++-------------- .../multiagent_mujoco/many_segment_swimmer.py | 132 +--------- .../envs/multiagent_mujoco/mujoco_multi.py | 25 +- tests/envs/MaMuJoCo/test_MaMuJoCo.py | 31 +-- 5 files changed, 80 insertions(+), 340 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/__init__.py b/gymnasium_robotics/envs/multiagent_mujoco/__init__.py index cf745d64..07eb2b5d 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/__init__.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/__init__.py @@ -3,12 +3,6 @@ from gymnasium_robotics.envs.multiagent_mujoco.coupled_half_cheetah import ( # noqa: F401 CoupledHalfCheetahEnv, ) -from gymnasium_robotics.envs.multiagent_mujoco.many_segment_ant import ( # noqa: F401 - ManySegmentAntEnv, -) -from gymnasium_robotics.envs.multiagent_mujoco.many_segment_swimmer import ( # noqa: F401 - ManySegmentSwimmerEnv, -) from gymnasium_robotics.envs.multiagent_mujoco.mujoco_multi import ( # noqa: F401 MultiAgentMujocoEnv, ) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py index be394f35..2dab8054 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py @@ -11,190 +11,60 @@ """ import os -import typing - -import gymnasium -import numpy as np -from gymnasium.envs.mujoco import mujoco_env -from gymnasium.utils.ezpickle import EzPickle from jinja2 import Template -DEFAULT_CAMERA_CONFIG = { - "distance": 4.0, -} - - -class ManySegmentAntEnv(mujoco_env.MujocoEnv, EzPickle): - """Is a vartion of the Ant environment, but with ants coupled together (each segment has a torso + 4 legs). - - This environment was first introduced ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709). - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 50, - } - def __init__(self, n_segs: int, render_mode: typing.Optional[str] = None): - """Init. - - Args: - n_segs: the number of segments of the ant (1 segment is the same as Gymansium's ant) - render_mode: see [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) - """ - self.healthy_reward = 1 - self._ctrl_cost_weight = 0.5 - self._contact_cost_weight = 5e-4 - self._reset_noise_scale = 0.1 - - # Check whether asset file exists already, otherwise create it - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_ant_{n_segs}_segments.auto.xml", - ) - self._generate_asset(n_segs=n_segs, asset_path=asset_path) - - observation_space = gymnasium.spaces.Box( - low=-np.inf, - high=np.inf, - shape=(n_segs * 50 + 17,), - dtype=np.float64, - ) - mujoco_env.MujocoEnv.__init__( - self, - asset_path, - 4, - observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, - render_mode=render_mode, - ) - EzPickle.__init__(self, n_segs=n_segs, render_mode=render_mode) - os.remove(asset_path) - - def _generate_asset(self, n_segs: int, asset_path: str) -> None: - template_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - "many_segment_ant.xml.template", - ) - with open(template_path) as file: - template = Template(file.read()) - body_str_template = """ - - - - - - - - - - - - - +def gen_asset(n_segs: int, asset_path: str) -> None: + template_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + "many_segment_ant.xml.template", + ) + with open(template_path) as file: + template = Template(file.read()) + body_str_template = """ + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - """ - - body_close_str_template = "\n" - actuator_str_template = """\t - - - \n""" - - body_str = "" - for i in range(1, n_segs): - body_str += body_str_template.format(*([i] * 16)) - body_str += body_close_str_template * (n_segs - 1) - - actuator_str = "" - for i in range(n_segs): - actuator_str += actuator_str_template.format(*([i] * 8)) - - rt = template.render(body=body_str, actuators=actuator_str) - with open(asset_path, "w") as file: - file.write(rt) - - def step(self, action: np.ndarray): - """Performs a single step given the `action`. - - Reward has same structure as ant - Does terminate when the front torso is unhealthy (in the same way as the ant) - """ - x_position_before = self.get_body_com("torso_0")[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.get_body_com("torso_0")[0] - - x_velocity = (x_position_after - x_position_before) / self.dt - - forward_reward = x_velocity - healthy_reward = self.healthy_reward - - ctrl_cost = self._ctrl_cost_weight * np.square(action).sum() - contact_cost = self._contact_cost_weight * np.sum( - np.square(np.clip(self.data.cfrc_ext, -1, 1)) - ) - contact_cost = 0 # In Gymnasium.MuJoCo-v4 contanct costs are ignored - - state = self.state_vector() - notdone = np.isfinite(state).all() and state[2] >= 0.2 and state[2] <= 1.0 - - observation = self._get_obs() - reward = forward_reward - ctrl_cost - contact_cost + healthy_reward - terminated = not notdone - truncated = False + + + """ - info = { - "reward_forward": forward_reward, - "reward_ctrl": -ctrl_cost, - "reward_survive": healthy_reward, - "x_position": x_position_after, - # "y_position": xy_position_after[1], - # "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), - "x_velocity": x_velocity, - # "y_velocity": y_velocity, - "forward_reward": forward_reward, - } + body_close_str_template = "\n" + actuator_str_template = """\t + + + \n""" - if self.render_mode == "human": - self.render() - return (observation, reward, terminated, truncated, info) + body_str = "" + for i in range(1, n_segs): + body_str += body_str_template.format(*([i] * 16)) + body_str += body_close_str_template * (n_segs - 1) - def _get_obs(self) -> np.ndarray: - return np.concatenate( - [ - self.data.qpos.flat[2:], - self.data.qvel.flat, - np.clip(self.data.cfrc_ext, -1, 1).flat, - ] - ) + actuator_str = "" + for i in range(n_segs): + actuator_str += actuator_str_template.format(*([i] * 8)) - def reset_model(self) -> np.ndarray: - """Resets the model in same way as the Swimmer.""" - qpos = self.init_qpos + self.np_random.uniform( - size=self.model.nq, - low=-self._reset_noise_scale, - high=self._reset_noise_scale, - ) - qvel = ( - self.init_qvel - + self.np_random.standard_normal(self.model.nv) * self._reset_noise_scale - ) - self.set_state(qpos, qvel) - return self._get_obs() + rt = template.render(body=body_str, actuators=actuator_str) + with open(asset_path, "w") as file: + file.write(rt) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py index b435d166..bb714b64 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py @@ -12,75 +12,17 @@ import os -import typing - -import gymnasium -import numpy as np -from gymnasium.envs.mujoco import mujoco_env -from gymnasium.utils.ezpickle import EzPickle from jinja2 import Template -DEFAULT_CAMERA_CONFIG = { - "distance": 4.0, -} - - -class ManySegmentSwimmerEnv(mujoco_env.MujocoEnv, EzPickle): - """Is a vartion of the Swimmer environment, but with many segments. - - This environment was first introduced ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709). - """ - - metadata = { - "render_modes": [ - "human", - "rgb_array", - "depth_array", - ], - "render_fps": 50, - } - - def __init__(self, n_segs: int, render_mode: typing.Optional[str] = None): - """Init. - - Args: - n_segs: the number of segments of the swimmer (3 segments is the same as Gymansium's swimmer) - render_mode: see [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) - """ - self._forward_reward_weight = 1.0 - self._ctrl_cost_weight = 1e-4 - self._reset_noise_scale = 0.1 - - # Check whether asset file exists already, otherwise create it - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_swimmer_{n_segs}_segments.auto.xml", - ) - self._generate_asset(n_segs=n_segs, asset_path=asset_path) - - observation_space = gymnasium.spaces.Box( - low=-np.inf, high=np.inf, shape=(n_segs * 2 + 4,), dtype=np.float64 - ) - mujoco_env.MujocoEnv.__init__( - self, - asset_path, - 4, - observation_space=observation_space, - default_camera_config=DEFAULT_CAMERA_CONFIG, - render_mode=render_mode, - ) - EzPickle.__init__(self, n_segs=n_segs, render_mode=render_mode) - os.remove(asset_path) - def _generate_asset(self, n_segs: int, asset_path: str) -> None: - template_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - "many_segment_swimmer.xml.template", - ) - with open(template_path) as file: - template = Template(file.read()) +def gen_asset(n_segs: int, asset_path: str) -> None: + template_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + "many_segment_swimmer.xml.template", + ) + with open(template_path) as file: + template = Template(file.read()) body_str_template = """ @@ -110,61 +52,3 @@ def _generate_asset(self, n_segs: int, asset_path: str) -> None: rt = template.render(body=body_str, actuators=actuator_str) with open(asset_path, "w") as file: file.write(rt) - - def step(self, action: np.ndarray): - """Performs a single step given the `action`. - - Reward has same structure as Swimmer - Does never terminate (like Swimmer) - """ - x_position_before = self.data.qpos[0] - self.do_simulation(action, self.frame_skip) - x_position_after = self.data.qpos[0] - - x_velocity = (x_position_after - x_position_before) / self.dt - - forward_reward = self._forward_reward_weight * x_velocity - - ctrl_cost = self._ctrl_cost_weight * np.square(action).sum() - - observation = self._get_obs() - reward = forward_reward - ctrl_cost - terminal = False - truncated = False - info = { - "reward_fwd": forward_reward, - "reward_ctrl": -ctrl_cost, - "x_position": x_position_after, - # "y_position": xy_position_after[1], - # "distance_from_origin": np.linalg.norm(xy_position_after, ord=2), - "x_velocity": x_velocity, - # "y_velocity": y_velocity, - "forward_reward": forward_reward, - } - - if self.render_mode == "human": - self.render() - return observation, reward, terminal, truncated, info - - def _get_obs(self) -> np.ndarray: - qpos = self.data.qpos - qvel = self.data.qvel - return np.concatenate([qpos.flat[2:], qvel.flat]) - - def reset_model(self) -> np.ndarray: - """Resets the model in same way as the Swimmer.""" - self.set_state( - self.init_qpos - + self.np_random.uniform( - low=-self._reset_noise_scale, - high=self._reset_noise_scale, - size=self.model.nq, - ), - self.init_qvel - + self.np_random.uniform( - low=-self._reset_noise_scale, - high=self._reset_noise_scale, - size=self.model.nv, - ), - ) - return self._get_obs() diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 63860bb8..03b7aa7f 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -16,6 +16,7 @@ from __future__ import annotations +import os import gymnasium import numpy as np @@ -25,10 +26,8 @@ from gymnasium_robotics.envs.multiagent_mujoco.coupled_half_cheetah import ( CoupledHalfCheetahEnv, ) -from gymnasium_robotics.envs.multiagent_mujoco.many_segment_ant import ManySegmentAntEnv -from gymnasium_robotics.envs.multiagent_mujoco.many_segment_swimmer import ( - ManySegmentSwimmerEnv, -) +import gymnasium_robotics.envs.multiagent_mujoco.many_segment_swimmer as many_segment_swimmer +import gymnasium_robotics.envs.multiagent_mujoco.many_segment_ant as many_segment_ant from gymnasium_robotics.envs.multiagent_mujoco.obsk import ( Node, build_obs, @@ -123,9 +122,14 @@ def __init__( except Exception: raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - self.single_agent_env = TimeLimit( - ManySegmentAntEnv(n_segs, render_mode), max_episode_steps=1000 + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_ant_{n_segs}_segments.auto.xml", ) + many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) + self.single_agent_env = gymnasium.make("Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) + os.remove(asset_path) elif scenario in ["ManySegmentSwimmer-v5"]: assert isinstance(agent_conf, str) try: @@ -133,9 +137,14 @@ def __init__( except Exception: raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - self.single_agent_env = TimeLimit( - ManySegmentSwimmerEnv(n_segs, render_mode), max_episode_steps=1000 + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_swimmer_{n_segs}_segments.auto.xml", ) + many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) + self.single_agent_env = gymnasium.make("Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) + os.remove(asset_path) elif scenario in ["CoupledHalfCheetah-v5"]: self.single_agent_env = TimeLimit( CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000 diff --git a/tests/envs/MaMuJoCo/test_MaMuJoCo.py b/tests/envs/MaMuJoCo/test_MaMuJoCo.py index 5d326e02..27a20486 100644 --- a/tests/envs/MaMuJoCo/test_MaMuJoCo.py +++ b/tests/envs/MaMuJoCo/test_MaMuJoCo.py @@ -33,11 +33,8 @@ scenario_conf("Pusher", None), scenario_conf("Walker2d", "2x3"), scenario_conf("Walker2d", None), -] - -sample_configurations = [ scenario_conf("ManySegmentSwimmer", "10x2"), - # scenario_conf("ManySegmentSwimmer", "5x4"), + scenario_conf("ManySegmentSwimmer", "5x4"), scenario_conf("ManySegmentSwimmer", "6x1"), scenario_conf("ManySegmentAnt", "2x3"), scenario_conf("ManySegmentAnt", "3x1"), @@ -49,7 +46,7 @@ @pytest.mark.parametrize("observation_depth", observation_depths) -@pytest.mark.parametrize("task", pre_defined_factorizations + sample_configurations) +@pytest.mark.parametrize("task", pre_defined_factorizations) def test_general(observation_depth, task) -> None: """Asserts that the environments are compliant with `pettingzoo.utils.env.ParallelEnv` API.""" parallel_api_test( @@ -77,8 +74,8 @@ def test_action_and_observation_mapping(observation_depth, task): ) ).all() - if task.scenario in ["Reacher", "Pusher"] and task.conf is not None: - return # observation mapping not implemented on "Reacher" and "Pusher" Environment + if task.scenario in ["Reacher", "Pusher", "CoupledHalfCheetah"] and task.conf is not None: + return # observation mapping not implemented on those environments # assert observation mapping test_env.reset() @@ -90,6 +87,9 @@ def test_action_and_observation_mapping(observation_depth, task): local_observations, ) + if task.scenario in ["ManySegmentSwimmer", "ManySegmentAnt"] and task.conf is not None: + return # mapping local to global observation is not supported on these environments since the local observation do not observe the full environment + data_equivalence( test_env.map_local_observations_to_global_state(local_observations), global_observations, @@ -100,23 +100,6 @@ def test_action_and_observation_mapping(observation_depth, task): len(agent_obs_factor) != len(set(agent_obs_factor)), "an agent observes the same state value multiple times" -@pytest.mark.parametrize("observation_depth", observation_depths) -@pytest.mark.parametrize("task", sample_configurations) -def test_action_mapping(observation_depth, task): - # observation mapping not implemented non-Gymansium mujoco environments - """Assert that converting local <-> global <-> local actions results in the same actions.""" - test_env = mamujoco_v0.parallel_env( - task.scenario, task.conf, agent_obsk=observation_depth - ) - global_action = test_env.single_agent_env.action_space.sample() - assert ( - global_action - == test_env.map_local_actions_to_global_action( - test_env.map_global_action_to_local_actions(global_action) - ) - ).all() - - # The black formatter was disabled because it results in `k_dicts_tasks` being an unreadable mess # fmt: off pre_computed_k_dict = collections.namedtuple("pre_computed_k_dict", "scenario, conf, list_k_dicts") From ab86e78ca4e2b217c83acf66b46d790a7eccfdb3 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 16 Oct 2023 18:29:27 +0300 Subject: [PATCH 119/160] rename to `v1` --- gymnasium_robotics/__init__.py | 2 +- .../multiagent_mujoco/{mamujoco_v0.py => mamujoco_v1.py} | 0 tests/envs/MaMuJoCo/test_MaMuJoCo.py | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename gymnasium_robotics/envs/multiagent_mujoco/{mamujoco_v0.py => mamujoco_v1.py} (100%) diff --git a/gymnasium_robotics/__init__.py b/gymnasium_robotics/__init__.py index 7f2817a6..aafb3fe6 100644 --- a/gymnasium_robotics/__init__.py +++ b/gymnasium_robotics/__init__.py @@ -3,7 +3,7 @@ from gymnasium_robotics.core import GoalEnv from gymnasium_robotics.envs.maze import maps -from gymnasium_robotics.envs.multiagent_mujoco import mamujoco_v0 +from gymnasium_robotics.envs.multiagent_mujoco import mamujoco_v1 def register_robotics_envs(): diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mamujoco_v0.py b/gymnasium_robotics/envs/multiagent_mujoco/mamujoco_v1.py similarity index 100% rename from gymnasium_robotics/envs/multiagent_mujoco/mamujoco_v0.py rename to gymnasium_robotics/envs/multiagent_mujoco/mamujoco_v1.py diff --git a/tests/envs/MaMuJoCo/test_MaMuJoCo.py b/tests/envs/MaMuJoCo/test_MaMuJoCo.py index 27a20486..56b1a903 100644 --- a/tests/envs/MaMuJoCo/test_MaMuJoCo.py +++ b/tests/envs/MaMuJoCo/test_MaMuJoCo.py @@ -6,7 +6,7 @@ from gymnasium.utils.env_checker import data_equivalence from pettingzoo.test import parallel_api_test -from gymnasium_robotics import mamujoco_v0 +from gymnasium_robotics import mamujoco_v1 scenario_conf = collections.namedtuple("scenario_conf", "scenario, conf") @@ -51,7 +51,7 @@ def test_general(observation_depth, task) -> None: """Asserts that the environments are compliant with `pettingzoo.utils.env.ParallelEnv` API.""" parallel_api_test( # MultiAgentMujocoEnv(task.scenario, task.conf, agent_obsk=observation_depth), - mamujoco_v0.parallel_env( + mamujoco_v1.parallel_env( task.scenario, task.conf, agent_obsk=observation_depth ), num_cycles=1_000_000, @@ -62,7 +62,7 @@ def test_general(observation_depth, task) -> None: @pytest.mark.parametrize("task", pre_defined_factorizations) def test_action_and_observation_mapping(observation_depth, task): """Assert that converting local <-> global <-> local obervations/actions results in the same observation/actions.""" - test_env = mamujoco_v0.parallel_env( + test_env = mamujoco_v1.parallel_env( task.scenario, task.conf, agent_obsk=observation_depth ) # assert action mapping @@ -133,7 +133,7 @@ def test_k_dict(task): If this test fails it means either the factorization in `obsk.get_parts_and_edges()` is wrong or that `obsk.get_joints_at_kdist()` generates wrong k_dict """ for k, k_dict in enumerate(task.list_k_dicts): - test_env = mamujoco_v0.parallel_env( + test_env = mamujoco_v1.parallel_env( scenario=task.scenario, agent_conf=task.conf, agent_obsk=k ) assert str(test_env.k_dicts) == k_dict, str(test_env.k_dicts) From 8480ce42c0ecc3e1fa7a202bed5341d8c2e06025 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 17 Oct 2023 12:53:15 +0300 Subject: [PATCH 120/160] update docstrings --- .../envs/multiagent_mujoco/coupled_half_cheetah.py | 2 +- gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py | 4 ++++ .../envs/multiagent_mujoco/many_segment_swimmer.py | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py b/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py index 07b27c25..d5badbb1 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/coupled_half_cheetah.py @@ -38,7 +38,7 @@ class CoupledHalfCheetahEnv(mujoco_env.MujocoEnv, EzPickle): | 0 | Torque applied on the back thigh rotor of the first cheetah | -1 | 1 | bthigh0 | hinge | torque (N m) | | 1 | Torque applied on the back shin rotor of the first cheetah | -1 | 1 | bshin0 | hinge | torque (N m) | | 2 | Torque applied on the back foot rotor of the first cheetah | -1 | 1 | bfoot0 | hinge | torque (N m) | - | 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | + | 3 | Torque applied on the front thigh rotor of the first cheetah | -1 | 1 | fthigh0 | hinge | torque (N m) | | 4 | Torque applied on the front shin rotor of the first cheetah | -1 | 1 | fshin0 | hinge | torque (N m) | | 5 | Torque applied on the front foot rotor of the first cheetah | -1 | 1 | ffoot0 | hinge | torque (N m) | | 6 | Torque applied on the back thigh rotor of the second cheetah | -1 | 1 | bthigh1 | hinge | torque (N m) | diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py index 2dab8054..6633d69d 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py @@ -15,6 +15,10 @@ def gen_asset(n_segs: int, asset_path: str) -> None: + """Generates a vartion of the Ant environment, but with ants coupled together (each segment has a torso + 4 legs). + + This environment was first introduced ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709). + """ template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets", diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py index bb714b64..2cce2f82 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py @@ -16,6 +16,10 @@ def gen_asset(n_segs: int, asset_path: str) -> None: + """Generates a vartion of the Swimmer environment, but with many segments. + + This environment was first introduced ["FACMAC: Factored Multi-Agent Centralised Policy Gradients"](https://arxiv.org/abs/2003.06709). + """ template_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets", From 5c83c9ba87e6a6dfb56c3a60c8b1d5a2bfe2aa6b Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 17 Oct 2023 15:31:37 +0300 Subject: [PATCH 121/160] cleanup --- .../envs/multiagent_mujoco/mujoco_multi.py | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 03b7aa7f..78a55fc3 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -191,7 +191,7 @@ def __init__( else: self.k_categories = local_categories if global_categories is None: - self.global_categories = self._generate_global_categories(scenario) + self.global_categories = ("qpos", "qvel") else: self.global_categories = global_categories @@ -545,10 +545,6 @@ def close(self): """See [pettingzoo.utils.env.ParallelEnv.close](https://pettingzoo.farama.org/api/parallel/#pettingzoo.utils.env.ParallelEnv.close).""" self.single_agent_env.close() - def seed(self, seed: int | None = None): - """Not implemented use env.reset(seed=...) instead.""" - raise NotImplementedError("use env.reset(seed=...) instead") - def _generate_local_categories(self, scenario: str) -> list[list[str]]: """Generated the default observation categories for each observation depth. @@ -575,20 +571,6 @@ def _generate_local_categories(self, scenario: str) -> list[list[str]]: categories = [k_categories[k if k < len(k_categories) else -1] for k in range(self.agent_obsk + 1)] return categories - def _generate_global_categories(self, scenario: str) -> tuple[str, ...]: - """Generates the default global categories of observations. - - Args: - scenario: The name of the MuJoCo Task - - Returns: - The default Global Categories for the scenario (a list of all observable types for that domain) - """ - if self.agent_obsk is None: - return () - - return ("qpos", "qvel") - # These are the export functions (for `PettingZoo` style exportations) env = pettingzoo.utils.conversions.aec_wrapper_fn(MultiAgentMujocoEnv) From 53a5d2345bc953331af090e64e22e539490f9a68 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 19 Oct 2023 16:25:55 +0300 Subject: [PATCH 122/160] add support for loading custom envs --- .../envs/multiagent_mujoco/mujoco_multi.py | 87 ++++++++++--------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 78a55fc3..0b5b1b31 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -77,6 +77,7 @@ def __init__( local_categories: list[list[str]] | None = None, global_categories: tuple[str, ...] | None = None, render_mode: str | None = None, + gym_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv | None = None, **kwargs, ): """Init. @@ -103,54 +104,58 @@ def __init__( The default is: Check each environment's page on the "observation space" section. render_mode: see [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/), valid values: 'human', 'rgb_array', 'depth_array' + gym_env: A custom `MujocoEnv` envinronment. kwargs: Additional arguments passed to the [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) environment, Note: arguments that change the observation space will not work. Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values). """ - scenario += "-v5" + if gym_env is None: + scenario += "-v5" - # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` - if scenario in _MUJOCO_GYM_ENVIROMENTS: - self.single_agent_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv = ( - gymnasium.make(scenario, **kwargs, render_mode=render_mode) - ) - elif scenario in ["ManySegmentAnt-v5"]: - assert isinstance(agent_conf, str) - try: - n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) - except Exception: - raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_ant_{n_segs}_segments.auto.xml", - ) - many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) - self.single_agent_env = gymnasium.make("Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) - os.remove(asset_path) - elif scenario in ["ManySegmentSwimmer-v5"]: - assert isinstance(agent_conf, str) - try: - n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) - except Exception: - raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_swimmer_{n_segs}_segments.auto.xml", - ) - many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) - self.single_agent_env = gymnasium.make("Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) - os.remove(asset_path) - elif scenario in ["CoupledHalfCheetah-v5"]: - self.single_agent_env = TimeLimit( - CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000 - ) + # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` + if scenario in _MUJOCO_GYM_ENVIROMENTS: + self.single_agent_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv = ( + gymnasium.make(scenario, **kwargs, render_mode=render_mode) + ) + elif scenario in ["ManySegmentAnt-v5"]: + assert isinstance(agent_conf, str) + try: + n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) + except Exception: + raise Exception(f"UNKNOWN partitioning config: {agent_conf}") + + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_ant_{n_segs}_segments.auto.xml", + ) + many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) + self.single_agent_env = gymnasium.make("Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) + os.remove(asset_path) + elif scenario in ["ManySegmentSwimmer-v5"]: + assert isinstance(agent_conf, str) + try: + n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) + except Exception: + raise Exception(f"UNKNOWN partitioning config: {agent_conf}") + + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_swimmer_{n_segs}_segments.auto.xml", + ) + many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) + self.single_agent_env = gymnasium.make("Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) + os.remove(asset_path) + elif scenario in ["CoupledHalfCheetah-v5"]: + self.single_agent_env = TimeLimit( + CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000 + ) + else: + raise NotImplementedError("Custom env not implemented!") else: - raise NotImplementedError("Custom env not implemented!") + self.single_agent_env = gym_env if agent_conf is None: self.agent_obsk = None From 502bf90c4b89bebd8304dba7f4a8f8a6170b6a15 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Fri, 20 Oct 2023 16:47:56 +0300 Subject: [PATCH 123/160] cleanup --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 0b5b1b31..dbfa8cea 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -402,8 +402,8 @@ def create_observation_mapping(self) -> dict[str, np.ndarray[np.float64]]: """ if self.agent_obsk is None: return {self.possible_agents[0] : np.arange(self.single_agent_env.observation_space.shape[0])} - if not hasattr(self.single_agent_env, "observation_structure"): - return None # TODO + if not hasattr(self.unwrapped.single_agent_env, "observation_structure"): + return None class data_struct: def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): From 02c8c329e30bb0ef66fc75720832e6ada5eb720f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 23 Oct 2023 13:29:12 +0300 Subject: [PATCH 124/160] cleanup --- .../multiagent_mujoco/many_segment_ant.py | 1 + .../multiagent_mujoco/many_segment_swimmer.py | 1 + .../envs/multiagent_mujoco/mujoco_multi.py | 195 +++++++++++------- .../envs/multiagent_mujoco/obsk.py | 24 +-- tests/envs/MaMuJoCo/test_MaMuJoCo.py | 14 +- 5 files changed, 144 insertions(+), 91 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py index 6633d69d..cd6f9cb6 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_ant.py @@ -11,6 +11,7 @@ """ import os + from jinja2 import Template diff --git a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py index 2cce2f82..d10fdd88 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/many_segment_swimmer.py @@ -12,6 +12,7 @@ import os + from jinja2 import Template diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index dbfa8cea..c44e82f9 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -16,6 +16,7 @@ from __future__ import annotations + import os import gymnasium @@ -23,11 +24,11 @@ import pettingzoo from gymnasium.wrappers.time_limit import TimeLimit +import gymnasium_robotics.envs.multiagent_mujoco.many_segment_ant as many_segment_ant +import gymnasium_robotics.envs.multiagent_mujoco.many_segment_swimmer as many_segment_swimmer from gymnasium_robotics.envs.multiagent_mujoco.coupled_half_cheetah import ( CoupledHalfCheetahEnv, ) -import gymnasium_robotics.envs.multiagent_mujoco.many_segment_swimmer as many_segment_swimmer -import gymnasium_robotics.envs.multiagent_mujoco.many_segment_ant as many_segment_ant from gymnasium_robotics.envs.multiagent_mujoco.obsk import ( Node, build_obs, @@ -40,17 +41,17 @@ # support other Gymnasium-Robotics MuJoCo environments _MUJOCO_GYM_ENVIROMENTS = [ - "Ant-v5", - "HalfCheetah-v5", - "Hopper-v5", - "HumanoidStandup-v5", - "Humanoid-v5", - "Reacher-v5", - "Swimmer-v5", - "Pusher-v5", - "Walker2d-v5", - "InvertedPendulum-v5", - "InvertedDoublePendulum-v5", + "Ant", + "HalfCheetah", + "Hopper", + "HumanoidStandup", + "Humanoid", + "Reacher", + "Swimmer", + "Pusher", + "Walker2d", + "InvertedPendulum", + "InvertedDoublePendulum", ] @@ -104,56 +105,17 @@ def __init__( The default is: Check each environment's page on the "observation space" section. render_mode: see [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/), valid values: 'human', 'rgb_array', 'depth_array' - gym_env: A custom `MujocoEnv` envinronment. + gym_env: A custom `MujocoEnv` envinronment, overrides generation of environment by `MaMuJoCo`. kwargs: Additional arguments passed to the [Gymansium/MuJoCo](https://gymnasium.farama.org/environments/mujoco/) environment, Note: arguments that change the observation space will not work. Raises: NotImplementedError: When the scenario is not supported (not part of of the valid values). """ + # Create underlying single agent environment if gym_env is None: - scenario += "-v5" - - # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` - if scenario in _MUJOCO_GYM_ENVIROMENTS: - self.single_agent_env: gymnasium.envs.mujoco.mujoco_env.MujocoEnv = ( - gymnasium.make(scenario, **kwargs, render_mode=render_mode) - ) - elif scenario in ["ManySegmentAnt-v5"]: - assert isinstance(agent_conf, str) - try: - n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) - except Exception: - raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_ant_{n_segs}_segments.auto.xml", - ) - many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) - self.single_agent_env = gymnasium.make("Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) - os.remove(asset_path) - elif scenario in ["ManySegmentSwimmer-v5"]: - assert isinstance(agent_conf, str) - try: - n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) - except Exception: - raise Exception(f"UNKNOWN partitioning config: {agent_conf}") - - asset_path = os.path.join( - os.path.dirname(os.path.abspath(__file__)), - "assets", - f"many_segment_swimmer_{n_segs}_segments.auto.xml", - ) - many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) - self.single_agent_env = gymnasium.make("Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode) - os.remove(asset_path) - elif scenario in ["CoupledHalfCheetah-v5"]: - self.single_agent_env = TimeLimit( - CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000 - ) - else: - raise NotImplementedError("Custom env not implemented!") + self.single_agent_env = self._create_base_gym_env( + scenario, agent_conf, render_mode, **kwargs + ) else: self.single_agent_env = gym_env @@ -231,6 +193,54 @@ def __init__( dtype=self.single_agent_env.observation_space.dtype, ) + def _create_base_gym_env( + self, scenario: str, agent_conf: str, render_mode: str, **kwargs + ) -> gymnasium.envs.mujoco.mujoco_env.MujocoEnv: + """Creates the single agent environments that is to be factorized.""" + # scenario += "-v5" + + # load the underlying single agent Gymansium MuJoCo Environment in `self.single_agent_env` + if scenario in _MUJOCO_GYM_ENVIROMENTS: + return gymnasium.make(f"{scenario}-v5", **kwargs, render_mode=render_mode) + elif scenario in ["ManySegmentAnt"]: + try: + n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) + except Exception: + raise Exception(f"UNKNOWN partitioning config: {agent_conf}") + + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_ant_{n_segs}_segments.auto.xml", + ) + many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) + return gymnasium.make( + "Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode + ) + os.remove(asset_path) + breakpoint() + elif scenario in ["ManySegmentSwimmer"]: + try: + n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) + except Exception: + raise Exception(f"UNKNOWN partitioning config: {agent_conf}") + + asset_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "assets", + f"many_segment_swimmer_{n_segs}_segments.auto.xml", + ) + many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) + return gymnasium.make( + "Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode + ) + os.remove(asset_path) + elif scenario in ["CoupledHalfCheetah"]: + return TimeLimit(CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000) + else: + breakpoint() + raise NotImplementedError("Custom env not implemented!") + def step( self, actions: dict[str, np.ndarray] ) -> tuple[ @@ -349,6 +359,7 @@ def map_global_state_to_local_observations( assert ( self.observation_factorization is not None ), "to map states the MuJoCo environment must have `observation_structure` member variable" + global_state = np.array(global_state) local_observation = {} for agent, partition in self.observation_factorization.items(): @@ -357,7 +368,9 @@ def map_global_state_to_local_observations( # assert sizes assert len(local_observation) == len(self.action_spaces) for agent in self.possible_agents: - assert len(local_observation[agent]) == self.observation_spaces[agent].shape[0] + assert ( + len(local_observation[agent]) == self.observation_spaces[agent].shape[0] + ) return local_observation @@ -383,7 +396,11 @@ def map_local_observations_to_global_state( for agent, partition in self.observation_factorization.items(): for local_idx, global_idx in enumerate(partition): - assert np.isnan(global_observation[global_idx]) or global_observation[global_idx] == local_observation[agent][local_idx], "FATAL: At least one gym_env observation is doubly defined!" + assert ( + np.isnan(global_observation[global_idx]) + or global_observation[global_idx] + == local_observation[agent][local_idx] + ), "FATAL: At least one gym_env observation is doubly defined!" global_observation[global_idx] = local_observation[agent][local_idx] assert not np.isnan( @@ -395,13 +412,18 @@ def map_local_observations_to_global_state( def create_observation_mapping(self) -> dict[str, np.ndarray[np.float64]]: """Creates a cache of the observation factorization. - The cache is intented to be used with `map_global_state_to_local_observations` & `map_local_observations_to_global_state`. + + The cache is intended to be used with `map_global_state_to_local_observations` & `map_local_observations_to_global_state`. Returns: A cache that indexes global osbervations to local. """ if self.agent_obsk is None: - return {self.possible_agents[0] : np.arange(self.single_agent_env.observation_space.shape[0])} + return { + self.possible_agents[0]: np.arange( + self.single_agent_env.observation_space.shape[0] + ) + } if not hasattr(self.unwrapped.single_agent_env, "observation_structure"): return None @@ -434,16 +456,28 @@ def __init__(self, qpos, qvel, cinert, cvel, qfrc_actuator, cfrc_ext): ), qvel=np.array(global_index[qpos_end_index:qvel_end_index]), cinert=np.concatenate( - [np.zeros(10, dtype=np.int64), global_index[qvel_end_index:cinert_end_index]] + [ + np.zeros(10, dtype=np.int64), + global_index[qvel_end_index:cinert_end_index], + ] ), cvel=np.concatenate( - [np.zeros(6, dtype=np.int64), global_index[cinert_end_index:cvel_end_index]] + [ + np.zeros(6, dtype=np.int64), + global_index[cinert_end_index:cvel_end_index], + ] ), qfrc_actuator=np.concatenate( - [np.zeros(6, dtype=np.int64), global_index[cvel_end_index:qfrc_actuator_end_index]] + [ + np.zeros(6, dtype=np.int64), + global_index[cvel_end_index:qfrc_actuator_end_index], + ] ), cfrc_ext=np.concatenate( - [np.zeros(6, dtype=np.int64), global_index[qfrc_actuator_end_index:cfrc_ext_end_index]] + [ + np.zeros(6, dtype=np.int64), + global_index[qfrc_actuator_end_index:cfrc_ext_end_index], + ] ), ) @@ -517,7 +551,7 @@ def _get_obs_agent(self, agent_id: int, data=None) -> np.ndarray: self.k_categories, self.mujoco_globals, self.global_categories, - index_only + index_only, ) def reset(self, seed: int | None = None, options=None): @@ -562,18 +596,27 @@ def _generate_local_categories(self, scenario: str) -> list[list[str]]: if self.agent_obsk is None: return [[]] - if scenario in ["Ant-v5", "ManySegmentAnt"]: - k_categories = [['qpos', 'qvel', 'cfrc_ext'], ['qpos']] - elif scenario in ["Humanoid-v5", "HumanoidStandup-v5"]: - k_categories = [['qpos', 'qvel', 'cinert', 'cvel', 'qfrc_actuator', 'cfrc_ext'], ['qpos']] - elif scenario in ["CoupledHalfCheetah-v5"]: - k_categories = [['qpos', 'qvel', 'ten_J', 'ten_length', 'ten_velocity'], ['qpos']] - elif scenario in ["Reacher-v5"]: - k_categories = [['qpos', 'qvel', 'fingertip_dist'], ['qpos']] + if scenario in ["Ant", "ManySegmentAnt"]: + k_categories = [["qpos", "qvel", "cfrc_ext"], ["qpos"]] + elif scenario in ["Humanoid", "HumanoidStandup"]: + k_categories = [ + ["qpos", "qvel", "cinert", "cvel", "qfrc_actuator", "cfrc_ext"], + ["qpos"], + ] + elif scenario in ["CoupledHalfCheetah"]: + k_categories = [ + ["qpos", "qvel", "ten_J", "ten_length", "ten_velocity"], + ["qpos"], + ] + elif scenario in ["Reacher"]: + k_categories = [["qpos", "qvel", "fingertip_dist"], ["qpos"]] else: - k_categories = [['qpos', 'qvel'], ['qpos']] + k_categories = [["qpos", "qvel"], ["qpos"]] - categories = [k_categories[k if k < len(k_categories) else -1] for k in range(self.agent_obsk + 1)] + categories = [ + k_categories[k if k < len(k_categories) else -1] + for k in range(self.agent_obsk + 1) + ] return categories diff --git a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py index 193602b1..ffe99aa8 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/obsk.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/obsk.py @@ -160,7 +160,7 @@ def build_obs( k_categories: list[list[str]], global_nodes: list[Node], global_categories: tuple[str, ...], - ignore_body_fn : bool = False, + ignore_body_fn: bool = False, ) -> np.ndarray: """Given a k_dict from get_joints_at_kdist, extract observation vector. @@ -242,7 +242,7 @@ def get_parts_and_edges( # noqa: C901 Returns: the partition of the mujoco graph nodes, the graph edges, and global nodes """ - if label in ["HalfCheetah-v5"]: + if label in ["HalfCheetah"]: # define Mujoco graph bthigh = Node("bthigh", -6, -6, 0) bshin = Node("bshin", -5, -5, 1) @@ -277,7 +277,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Ant-v5"]: + elif label in ["Ant"]: # define Mujoco graph torso = 1 front_left_leg = 2 @@ -391,7 +391,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Hopper-v5"]: + elif label in ["Hopper"]: # define Mujoco-Graph thigh_joint = Node( "thigh_joint", @@ -465,7 +465,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Humanoid-v5", "HumanoidStandup-v5"]: + elif label in ["Humanoid", "HumanoidStandup"]: # bodies # worldbody = 0 torso = 1 @@ -604,7 +604,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Reacher-v5"]: + elif label in ["Reacher"]: # define Mujoco-Graph # worldbody = 0 body0 = 1 @@ -675,7 +675,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Pusher-v5"]: + elif label in ["Pusher"]: # define Mujoco-Graph r_shoulder_pan_joint = Node("r_wrist_roll_joint", 0, 0, 0) r_shoulder_lift_joint = Node("r_wrist_roll_joint", 1, 1, 1) @@ -755,7 +755,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Swimmer-v5"]: + elif label in ["Swimmer"]: # define Mujoco-Graph joint0 = Node( "rot2", @@ -790,7 +790,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["Walker2d-v5"]: + elif label in ["Walker2d"]: # define Mujoco-Graph thigh_joint = Node("thigh_joint", -6, -6, 0) leg_joint = Node("leg_joint", -5, -5, 1) @@ -840,7 +840,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["CoupledHalfCheetah-v5"]: + elif label in ["CoupledHalfCheetah"]: # define Mujoco graph tendon = 0 @@ -936,7 +936,7 @@ def get_parts_and_edges( # noqa: C901 return parts, edges, globals - elif label in ["ManySegmentSwimmer-v5"]: + elif label in ["ManySegmentSwimmer"]: assert partitioning is not None, "Partitioning, required with " + label try: @@ -961,7 +961,7 @@ def get_parts_and_edges( # noqa: C901 ] return parts, edges, globals - elif label in ["ManySegmentAnt-v5"]: + elif label in ["ManySegmentAnt"]: assert partitioning is not None, "Partitioning, required with " + label try: diff --git a/tests/envs/MaMuJoCo/test_MaMuJoCo.py b/tests/envs/MaMuJoCo/test_MaMuJoCo.py index 56b1a903..17af031c 100644 --- a/tests/envs/MaMuJoCo/test_MaMuJoCo.py +++ b/tests/envs/MaMuJoCo/test_MaMuJoCo.py @@ -74,7 +74,10 @@ def test_action_and_observation_mapping(observation_depth, task): ) ).all() - if task.scenario in ["Reacher", "Pusher", "CoupledHalfCheetah"] and task.conf is not None: + if ( + task.scenario in ["Reacher", "Pusher", "CoupledHalfCheetah"] + and task.conf is not None + ): return # observation mapping not implemented on those environments # assert observation mapping @@ -87,7 +90,10 @@ def test_action_and_observation_mapping(observation_depth, task): local_observations, ) - if task.scenario in ["ManySegmentSwimmer", "ManySegmentAnt"] and task.conf is not None: + if ( + task.scenario in ["ManySegmentSwimmer", "ManySegmentAnt"] + and task.conf is not None + ): return # mapping local to global observation is not supported on these environments since the local observation do not observe the full environment data_equivalence( @@ -97,7 +103,9 @@ def test_action_and_observation_mapping(observation_depth, task): # sanity check making sure the observation factorizations are sane for agent_obs_factor in test_env.observation_factorization.values(): - len(agent_obs_factor) != len(set(agent_obs_factor)), "an agent observes the same state value multiple times" + len(agent_obs_factor) != len( + set(agent_obs_factor) + ), "an agent observes the same state value multiple times" # The black formatter was disabled because it results in `k_dicts_tasks` being an unreadable mess From fa7be3f32edf40339f952622a4a84c665c077ec1 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 23 Oct 2023 18:27:24 +0300 Subject: [PATCH 125/160] fixed temp assets not being removed --- gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index c44e82f9..3d8365f0 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -214,11 +214,10 @@ def _create_base_gym_env( f"many_segment_ant_{n_segs}_segments.auto.xml", ) many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) + os.remove(asset_path) return gymnasium.make( "Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode ) - os.remove(asset_path) - breakpoint() elif scenario in ["ManySegmentSwimmer"]: try: n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) @@ -231,10 +230,10 @@ def _create_base_gym_env( f"many_segment_swimmer_{n_segs}_segments.auto.xml", ) many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) + os.remove(asset_path) return gymnasium.make( "Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode ) - os.remove(asset_path) elif scenario in ["CoupledHalfCheetah"]: return TimeLimit(CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000) else: From 610e5c11ed8f37b4ff4d57d9b7b9e3b02c126a8f Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Mon, 23 Oct 2023 18:31:47 +0300 Subject: [PATCH 126/160] fix stupid bug --- .../envs/multiagent_mujoco/mujoco_multi.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py index 3d8365f0..2f3155fc 100755 --- a/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py +++ b/gymnasium_robotics/envs/multiagent_mujoco/mujoco_multi.py @@ -214,10 +214,11 @@ def _create_base_gym_env( f"many_segment_ant_{n_segs}_segments.auto.xml", ) many_segment_ant.gen_asset(n_segs=n_segs, asset_path=asset_path) - os.remove(asset_path) - return gymnasium.make( + single_agent_env = gymnasium.make( "Ant-v5", xml_file=asset_path, **kwargs, render_mode=render_mode ) + os.remove(asset_path) + return single_agent_env elif scenario in ["ManySegmentSwimmer"]: try: n_segs = int(agent_conf.split("x")[0]) * int(agent_conf.split("x")[1]) @@ -230,10 +231,11 @@ def _create_base_gym_env( f"many_segment_swimmer_{n_segs}_segments.auto.xml", ) many_segment_swimmer.gen_asset(n_segs=n_segs, asset_path=asset_path) - os.remove(asset_path) - return gymnasium.make( + single_agent_env = gymnasium.make( "Swimmer-v5", xml_file=asset_path, **kwargs, render_mode=render_mode ) + os.remove(asset_path) + return single_agent_env elif scenario in ["CoupledHalfCheetah"]: return TimeLimit(CoupledHalfCheetahEnv(render_mode), max_episode_steps=1000) else: From c7435600916897c654f9df46fd29782b99e39d28 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Tue, 24 Oct 2023 12:13:30 +0300 Subject: [PATCH 127/160] update manysegswimmer timestep to match swimmer --- .../multiagent_mujoco/assets/many_segment_swimmer.xml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gymnasium_robotics/envs/multiagent_mujoco/assets/many_segment_swimmer.xml.template b/gymnasium_robotics/envs/multiagent_mujoco/assets/many_segment_swimmer.xml.template index 9fb49a95..97ecd160 100644 --- a/gymnasium_robotics/envs/multiagent_mujoco/assets/many_segment_swimmer.xml.template +++ b/gymnasium_robotics/envs/multiagent_mujoco/assets/many_segment_swimmer.xml.template @@ -1,6 +1,6 @@ -

    c&$HPeI@49?J@RP{6h}dK^rH*G@zF3%en)u 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 z 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 vnVVW%l9*cn(qpV^V5n Date: Wed, 11 Jan 2023 22:26:20 +0200 Subject: [PATCH 006/160] typo fix --- docs/envs/MaMuJoCo/ma_walker2d.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/envs/MaMuJoCo/ma_walker2d.md b/docs/envs/MaMuJoCo/ma_walker2d.md index c890dd53..6fa6fbea 100644 --- a/docs/envs/MaMuJoCo/ma_walker2d.md +++ b/docs/envs/MaMuJoCo/ma_walker2d.md @@ -42,8 +42,8 @@ If partitioning, is `None` then the environment contains a single agent with the ### elif partitioning == "2x3": # isolate right and left foot -```{figure} figures/walker_2x3.png - :name: walker_2x3 +```{figure} figures/walker2d_2x3.png + :name: walker2d_2x3 ``` | Instantiate | `env = mamujoco_v0.parallel_env("Walker2d", "2x3")`| From 53736bafe8b1b92222df1588fad06373ca372281 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Jan 2023 22:30:02 +0200 Subject: [PATCH 007/160] typo fixes --- docs/envs/MaMuJoCo/ma_single.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/envs/MaMuJoCo/ma_single.md b/docs/envs/MaMuJoCo/ma_single.md index 119f6c1c..95d3bc79 100644 --- a/docs/envs/MaMuJoCo/ma_single.md +++ b/docs/envs/MaMuJoCo/ma_single.md @@ -8,7 +8,9 @@ lastpage: MaMuJoCo also support single action [Gymansium/MuJoCo/](https://gymnasium.farama.org/environments/mujoco/) environment such as [Gymnasium/Mujoco/InvertedPendulum](https://gymnasium.farama.org/environments/mujoco/inverted_pendulum/) and [Gymnasium/Mujoco/InvertedDoublePendulum](https://gymnasium.farama.org/environments/mujoco/inverted_double_pendulum/) And can be instantiated (without a partition) + `env = mamujoco_v0.parallel_env("InvertedDoublePendulum", None)` + `env = mamujoco_v0.parallel_env("InvertedPendulum", None)` In which case, they simply are the same environments with a single agent using the `PettingZoo` APIs. @@ -18,7 +20,7 @@ In which case, they simply are the same environments with a single agent using t The action spaces is depended on the partitioning ## Observation Space -The agent receive the same [Gymnasium's Walker2D](https://gymnasium.farama.org/environments/mujoco/walker2d/#observation-space) reward. +The agent receives the same observations as the single agent Gymnasium environment. From 59772fb77205a945b108068479782ac722e85ef7 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Wed, 11 Jan 2023 22:31:25 +0200 Subject: [PATCH 008/160] fix `shinx` warning --- docs/envs/MaMuJoCo/figures/{credits.md => credits} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/envs/MaMuJoCo/figures/{credits.md => credits} (100%) diff --git a/docs/envs/MaMuJoCo/figures/credits.md b/docs/envs/MaMuJoCo/figures/credits similarity index 100% rename from docs/envs/MaMuJoCo/figures/credits.md rename to docs/envs/MaMuJoCo/figures/credits From 22e11105177c1f0c008f6c13facd6404d363ee18 Mon Sep 17 00:00:00 2001 From: Kallinteris Andreas Date: Thu, 12 Jan 2023 06:05:38 +0200 Subject: [PATCH 009/160] `MaMuJoCo` DOC update --- docs/envs/MaMuJoCo/Ant_2x4.png | Bin 31399 -> 31399 bytes docs/envs/MaMuJoCo/figures/ant.png | Bin 0 -> 30776 bytes docs/envs/MaMuJoCo/figures/ant_2x4.png | Bin 31399 -> 31399 bytes docs/envs/MaMuJoCo/figures/ant_2x4d.png | Bin 29862 -> 29862 bytes docs/envs/MaMuJoCo/figures/ant_4x2.png | Bin 30836 -> 30836 bytes .../MaMuJoCo/figures/coupled_half_cheetah.png | Bin 0 -> 46726 bytes .../figures/coupled_half_cheetah_1p1.png | Bin 47477 -> 47477 bytes docs/envs/MaMuJoCo/figures/half_cheetah.png | Bin 0 -> 28167 bytes .../MaMuJoCo/figures/half_cheetah_2x3.png | Bin 28132 -> 28132 bytes .../MaMuJoCo/figures/half_cheetah_6x1.png | Bin 29245 -> 29245 bytes docs/envs/MaMuJoCo/figures/hopper.png | Bin 0 -> 20167 bytes docs/envs/MaMuJoCo/figures/hopper_3x1.png | Bin 20156 -> 20156 bytes docs/envs/MaMuJoCo/figures/humanoid.png | Bin 0 -> 42808 bytes docs/envs/MaMuJoCo/figures/humanoid_9|8.png | Bin 42163 -> 42163 bytes docs/envs/MaMuJoCo/figures/mamujoco.png | Bin 306724 -> 306724 bytes .../MaMuJoCo/figures/many_segment_ant.png | Bin 26048 -> 26048 bytes .../MaMuJoCo/figures/many_segment_swimmer.png | Bin 18654 -> 18654 bytes docs/envs/MaMuJoCo/figures/pusher.png | Bin 0 -> 18746 bytes docs/envs/MaMuJoCo/figures/pusher_3p.png | Bin 18761 -> 18761 bytes docs/envs/MaMuJoCo/figures/reacher.png | Bin 0 -> 16528 bytes docs/envs/MaMuJoCo/figures/reacher_2x1.png | Bin 16538 -> 16538 bytes docs/envs/MaMuJoCo/figures/swimmer.png | Bin 0 -> 17823 bytes docs/envs/MaMuJoCo/figures/swimmer_2x1.png | Bin 17790 -> 17790 bytes docs/envs/MaMuJoCo/figures/walker2d.png | Bin 0 -> 26438 bytes docs/envs/MaMuJoCo/figures/walker2d_2x3.png | Bin 26200 -> 26200 bytes docs/envs/MaMuJoCo/index.md | 30 ++++++------- docs/envs/MaMuJoCo/ma_ant.md | 40 +++++++----------- docs/envs/MaMuJoCo/ma_coupled_half_cheetah.md | 15 +++---- docs/envs/MaMuJoCo/ma_half_cheetah.md | 15 ++++--- docs/envs/MaMuJoCo/ma_hopper.md | 10 +++-- docs/envs/MaMuJoCo/ma_humanoid.md | 19 +++++---- docs/envs/MaMuJoCo/ma_humanoid_standup.md | 19 +++++---- docs/envs/MaMuJoCo/ma_pusher.md | 23 +++++----- docs/envs/MaMuJoCo/ma_reacher.md | 9 ++-- docs/envs/MaMuJoCo/ma_single.md | 9 ++-- docs/envs/MaMuJoCo/ma_swimmer.md | 12 ++++-- docs/envs/MaMuJoCo/ma_walker2d.md | 16 +++---- 37 files changed, 112 insertions(+), 105 deletions(-) create mode 100644 docs/envs/MaMuJoCo/figures/ant.png create mode 100644 docs/envs/MaMuJoCo/figures/coupled_half_cheetah.png create mode 100644 docs/envs/MaMuJoCo/figures/half_cheetah.png create mode 100644 docs/envs/MaMuJoCo/figures/hopper.png create mode 100644 docs/envs/MaMuJoCo/figures/humanoid.png create mode 100644 docs/envs/MaMuJoCo/figures/pusher.png create mode 100644 docs/envs/MaMuJoCo/figures/reacher.png create mode 100644 docs/envs/MaMuJoCo/figures/swimmer.png create mode 100644 docs/envs/MaMuJoCo/figures/walker2d.png diff --git a/docs/envs/MaMuJoCo/Ant_2x4.png b/docs/envs/MaMuJoCo/Ant_2x4.png index be51c6af5b5c50bc3fe234cc30d51469f35e9d1f..68bf0c5c074288d8e843d1b9f20908e7a8245558 100644 GIT binary patch delta 130 zcmZ4fm2vr3#tB&*JS@ygp6P2dHx{k0bTc#xF)+3=HMBA^(Kax!GB7AN)fHo4U{Eb_ zjVMV;EJ?M>%}>cpt7I@TFf!IPFx17+z?Ly})#UUlc}0+ni51W^W2jM6+0LB-8pYu0 L>gTe~DWM4fvcDuq delta 130 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 literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/ant_2x4.png b/docs/envs/MaMuJoCo/figures/ant_2x4.png index 333ea71cf2c4595ac5e43f5fcd89124144bd57f5..68bf0c5c074288d8e843d1b9f20908e7a8245558 100644 GIT binary patch delta 128 zcmZ4fm2vr3#tB&*JS@ygp6P2dHx_NEbTtYwFt#!^v@$Z$HZZUgTe~DWM4fbBZJ8 delta 128 zcmZ4fm2vr3#tB&*+#=#ECZ;JI8;dqnx*CQU8CV&bS{WH=8yHv_82GCk%VA((P%Uwd zC`m~yNwvz&PsvQHWH2%?GS)RP)HOucU{_gpVRCwvf&y5=$k@uj1Y(l+uPk+-NerH@ KelF{r5}E){AS0Oo diff --git a/docs/envs/MaMuJoCo/figures/ant_2x4d.png b/docs/envs/MaMuJoCo/figures/ant_2x4d.png index 40902c4dc9ee4ea20e7bb74134f15fa701a529f4..70ff562e88f46197070f5dd985844d46e0ee4c50 100644 GIT binary patch delta 128 zcmZ4Xl5yEf#tB&*JS@ygp6P2dHx{igb~OqyFt#!^v@$Z)HZZUmdKI;Vst0Ir=RdH?_b delta 128 zcmZ4Xl5yEf#tB&*+#=#ECZ;JI8;jN#yBdZV8CV&bS{WH=8yHv_82GCk%VA((P%Uwd zC`m~yNwvz&PsvQHWH2%?GS)RP)HOucU{_gpVRBlDf&y5=$k@uj1Y(l+uPk+-NerH@ KelF{r5}E)$bR%&9 diff --git a/docs/envs/MaMuJoCo/figures/ant_4x2.png b/docs/envs/MaMuJoCo/figures/ant_4x2.png index ccda5a1a1f87aae4b2c6702f3f03f8ab5df6bb03..3bbaa3ce9045d9d5469c0f4693bd935001fb6391 100644 GIT binary patch delta 128 zcmezJf$_@+#tB&*JS@ygp6P2dHx|{FyBdWU7+aYdS{a#Y8yHv_7+iExTg1S?pjzS@ zQIe8al4_NkpOTqY$zWt)WUOmosB4I>VS)TSxydFK3JO386Dy!$#wHMxrm~$o12l=j M)78&qol`;+0O)!o$N&HU delta 128 zcmezJf$_@+#tB&*+#=#ECZ;JI8;feoT@6Ew46F=It&9w`4GgRd4E$A&c8S5Gt>KdYJu&b=QFxjL+K>;jbWNc+%0x`+^SC%@^BnD4c KKbLh*2~7ZaQ6s+q diff --git a/docs/envs/MaMuJoCo/figures/coupled_half_cheetah.png b/docs/envs/MaMuJoCo/figures/coupled_half_cheetah.png new file mode 100644 index 0000000000000000000000000000000000000000..bbddcf0e11aef1586c3cd3dd6d501e9f0916e7a4 GIT binary patch literal 46726 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 z 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 xIFATuK=c0{!NtexJYE0* literal 0 HcmV?d00001 diff --git a/docs/envs/MaMuJoCo/figures/coupled_half_cheetah_1p1.png b/docs/envs/MaMuJoCo/figures/coupled_half_cheetah_1p1.png index 056cbc456b4e0b7c9e21a3dbaef74628f02088ce..f64b3734a9db19fb03dd3ec84d8f9c8017b4b410 100644 GIT binary patch delta 128 zcmezRiRtSnrU_XbJS@ygp6P2dHx||Ha5V}sFt#!^v@$Z)HZZU%n#y+W4A3M7 MPgg&ebxsLQ01;IsQ~&?~ delta 128 zcmezRiRtSnrU_Xb+#=$v&PE~gHWt!LG9I!erB(3JPEeBV#KA6NpLPzp~VUCNX%r L`njxgN@xNA;fN&O diff --git a/docs/envs/MaMuJoCo/figures/half_cheetah.png b/docs/envs/MaMuJoCo/figures/half_cheetah.png new file mode 100644 index 0000000000000000000000000000000000000000..6d9150ac6e29571fff058d90757fe2df1fadeb5b GIT binary patch literal 28167 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 MPgg&ebxsLQ0R3JiNB{r; delta 128 zcmaEIoAJqQ#tB&*+#=$v&PE~gHWt0jaWxDvGO#i!LG9I!sI2n3JPEeBV#KAQ;11#dlM%EO=9qL L^>bP0l+XkK)^a6l diff --git a/docs/envs/MaMuJoCo/figures/half_cheetah_6x1.png b/docs/envs/MaMuJoCo/figures/half_cheetah_6x1.png index ae9016cca25fa695daab70f7e348d64fe426e130..9762c589b1b6b70837b52c9a00c9b9a53c6486c7 100644 GIT binary patch delta 104 zcmdn{gmLc^#tB&*JS@!0b_sL;Z!B^v6fp`hFt#!^v@$Z)HZZUxu`Hi77v%69Gy0}yz+`njxgN@xNA#sD8r delta 104 zcmdn{gmLc^#tB&*+#=$v&PE~gHWs-RiWr6%8CV&bS{WH?8yHv_7`zNVdt>sMLRk!n l8GNNFlm8YfD1dbs8Cw~cLUg?CO`OaC1fH&bF6*2UngGG#AGH7g diff --git a/docs/envs/MaMuJoCo/figures/hopper.png b/docs/envs/MaMuJoCo/figures/hopper.png new file mode 100644 index 0000000000000000000000000000000000000000..919d707129d4135d373b285e5fb654a66673b8d3 GIT binary patch literal 20167 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 diff --git a/docs/envs/MaMuJoCo/figures/humanoid.png b/docs/envs/MaMuJoCo/figures/humanoid.png new file mode 100644 index 0000000000000000000000000000000000000000..d03e62ebfd21d747b1849f2639cb9a09abbcd430 GIT binary patch literal 42808 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