Unverified Commit 71608f94 authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Adds RSL-RL symmetry example for cartpole and ANYmal locomotion (#3057)

# Description

This MR introduces the following:

* An `agent` argument to all scripts to allow selecting different entry
points (each then get resolved to their respective settings file).
* Symmetry function for ANYmal robot for the locomotion task and
cartpole balancing task
* Documentation on how to configure RL training agent using gym
resgistry

Fixes #2835

## Type of change

- New feature (non-breaking change which adds functionality)
- This change requires a documentation update

## Screenshots

### Cartpole

```bash
# without symmetry
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-v0 --headless --agent rsl_rl_with_symmetry_cfg_entry_point --run_name ppo_with_no_symmetry agent.algorithm.symmetry_cfg.use_data_augmentation=false

# with symmetry
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-v0--headless --agent rsl_rl_with_symmetry_cfg_entry_point --run_name ppo_with_symmetry_data_augmentation  agent.algorithm.symmetry_cfg.use_data_augmentation=true
```

| Isaac-Cartpole-v0 (pink w/o symmetry, blue w/ symmetry) |
| ------ |
| <img width="823" height="421" alt="image"
src="https://github.com/user-attachments/assets/9c33db99-0d79-4c1d-b437-e01275d613b5"
/> |

### Locomotion

```bash

# without symmetry
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Anymal-D-v0 --headless --agent rsl_rl_with_symmetry_cfg_entry_point --run_name ppo_with_no_symmetry agent.algorithm.symmetry_cfg.use_data_augmentation=false

# with symmetry
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Velocity-Rough-Anymal-D-v0 --headless --agent rsl_rl_with_symmetry_cfg_entry_point --run_name ppo_with_symmetry_data_augmentation  agent.algorithm.symmetry_cfg.use_data_augmentation=true
```

| Isaac-Velocity-Rough-Anymal-D-v0 (green w/o symmetry, purple w/
symmetry) |
| ------ |
| <img width="1241" height="414" alt="image"
src="https://github.com/user-attachments/assets/625c125d-db9f-4006-9a62-0d55701a9407"
/> |

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarooctipus <zhengyuz@nvidia.com>
parent 5fdd8b9f
isaaclab_rl
.. _api-isaaclab-rl:
isaaclab_rl
===========
.. automodule:: isaaclab_rl
......
......@@ -68,7 +68,7 @@ Isaac Lab, you will first need to make a wrapper for the library, as explained i
The following steps can be followed to integrate a new library with Isaac Lab:
1. Add your library as an extra-dependency in the ``setup.py`` for the extension ``isaaclab_tasks``.
1. Add your library as an extra-dependency in the ``setup.py`` for the extension ``isaaclab_rl``.
This will ensure that the library is installed when you install Isaac Lab or it will complain if the library is not
installed or available.
2. Install your library in the Python environment used by Isaac Lab. You can do this by following the steps mentioned
......@@ -86,6 +86,15 @@ works as expected and can guide users on how to use the wrapper.
* Add some tests to ensure that the wrapper works as expected and remains compatible with the library.
These tests can be added to the ``source/isaaclab_rl/test`` directory.
* Add some documentation for the wrapper. You can add the API documentation to the
``docs/source/api/lab_tasks/isaaclab_rl.rst`` file.
:ref:`API documentation<api-isaaclab-rl>` for the ``isaaclab_rl`` module.
Configuring an RL Agent
-----------------------
Once you have integrated a new library with Isaac Lab, you can configure the example environment to use the new library.
You can check the :ref:`tutorial-configure-rl-training` for an example of how to configure the training process to use a
different library.
.. _rsl-rl: https://github.com/leggedrobotics/rsl_rl
......@@ -154,7 +154,7 @@ Improvements
------------
Core API
^^^^^^^^
~~~~~~~~
* **Actuator Interfaces**
* Fixes implicit actuator limits configs for assets by @ooctipus
......@@ -198,7 +198,7 @@ Core API
* Allows slicing from list values in dicts by @LinghengMeng @kellyguo11
Tasks API
^^^^^^^^^
~~~~~~~~~
* Adds support for ``module:task`` and gymnasium >=1.0 by @kellyguo11
* Adds RL library error hints by @Toni-SM
......@@ -212,7 +212,7 @@ Tasks API
* Pre-processes SB3 env image obs-space for CNN pipeline by @ooctipus
Infrastructure
^^^^^^^^^^^^^^^
~~~~~~~~~~~~~~
* **Dependencies**
* Updates torch to 2.7.0 with CUDA 12.8 by @kellyguo11
......@@ -239,7 +239,7 @@ Bug Fixes
---------
Core API
^^^^^^^^
~~~~~~~~
* **Actuator Interfaces**
* Fixes DCMotor clipping for negative power by @jtigue-bdai
......@@ -267,12 +267,12 @@ Core API
* Fixes ``quat_inv()`` implementation by @ozhanozen
Tasks API
^^^^^^^^^
~~~~~~~~~
* Fixes LSTM to ONNX export by @jtigue-bdai
Example Tasks
^^^^^^^^^^^^^
~~~~~~~~~~~~~
* Removes contact termination redundancy by @louislelay
* Fixes memory leak in SDF by @leondavi
......
......@@ -69,7 +69,7 @@ used as the default output directories for tasks run by this project.
Project Structure
------------------------------
-----------------
There are four nested structures you need to be aware of when working in the direct workflow with an Isaac Lab template
project: the **Project**, the **Extension**, the **Modules**, and the **Task**.
......
.. _tutorial-configure-rl-training:
Configuring an RL Agent
=======================
.. currentmodule:: isaaclab
In the previous tutorial, we saw how to train an RL agent to solve the cartpole balancing task
using the `Stable-Baselines3`_ library. In this tutorial, we will see how to configure the
training process to use different RL libraries and different training algorithms.
In the directory ``scripts/reinforcement_learning``, you will find the scripts for
different RL libraries. These are organized into subdirectories named after the library name.
Each subdirectory contains the training and playing scripts for the library.
To configure a learning library with a specific task, you need to create a configuration file
for the learning agent. This configuration file is used to create an instance of the learning agent
and is used to configure the training process. Similar to the environment registration shown in
the :ref:`tutorial-register-rl-env-gym` tutorial, you can register the learning agent with the
``gymnasium.register`` method.
The Code
--------
As an example, we will look at the configuration included for the task ``Isaac-Cartpole-v0``
in the ``isaaclab_tasks`` package. This is the same task that we used in the
:ref:`tutorial-run-rl-training` tutorial.
.. literalinclude:: ../../../../source/isaaclab_tasks/isaaclab_tasks/manager_based/classic/cartpole/__init__.py
:language: python
:lines: 18-29
The Code Explained
------------------
Under the attribute ``kwargs``, we can see the configuration for the different learning libraries.
The key is the name of the library and the value is the path to the configuration instance.
This configuration instance can be a string, a class, or an instance of the class.
For example, the value of the key ``"rl_games_cfg_entry_point"`` is a string that points to the
configuration YAML file for the RL-Games library. Meanwhile, the value of the key
``"rsl_rl_cfg_entry_point"`` points to the configuration class for the RSL-RL library.
The pattern used for specifying an agent configuration class follows closely to that used for
specifying the environment configuration entry point. This means that while the following
are equivalent:
.. dropdown:: Specifying the configuration entry point as a string
:icon: code
.. code-block:: python
from . import agents
gym.register(
id="Isaac-Cartpole-v0",
entry_point="isaaclab.envs:ManagerBasedRLEnv",
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.cartpole_env_cfg:CartpoleEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:CartpolePPORunnerCfg",
},
)
.. dropdown:: Specifying the configuration entry point as a class
:icon: code
.. code-block:: python
from . import agents
gym.register(
id="Isaac-Cartpole-v0",
entry_point="isaaclab.envs:ManagerBasedRLEnv",
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.cartpole_env_cfg:CartpoleEnvCfg",
"rsl_rl_cfg_entry_point": agents.rsl_rl_ppo_cfg.CartpolePPORunnerCfg,
},
)
The first code block is the preferred way to specify the configuration entry point.
The second code block is equivalent to the first one, but it leads to import of the configuration
class which slows down the import time. This is why we recommend using strings for the configuration
entry point.
All the scripts in the ``scripts/reinforcement_learning`` directory are configured by default to read the
``<library_name>_cfg_entry_point`` from the ``kwargs`` dictionary to retrieve the configuration instance.
For instance, the following code block shows how the ``train.py`` script reads the configuration
instance for the Stable-Baselines3 library:
.. dropdown:: Code for train.py with SB3
:icon: code
.. literalinclude:: ../../../../scripts/reinforcement_learning/sb3/train.py
:language: python
:emphasize-lines: 26-28, 102-103
:linenos:
The argument ``--agent`` is used to specify the learning library to use. This is used to
retrieve the configuration instance from the ``kwargs`` dictionary. You can manually specify
alternate configuration instances by passing the ``--agent`` argument.
The Code Execution
------------------
Since for the cartpole balancing task, RSL-RL library offers two configuration instances,
we can use the ``--agent`` argument to specify the configuration instance to use.
* Training with the standard PPO configuration:
.. code-block:: bash
# standard PPO training
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-v0 --headless \
--run_name ppo
* Training with the PPO configuration with symmetry augmentation:
.. code-block:: bash
# PPO training with symmetry augmentation
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-v0 --headless \
--agent rsl_rl_with_symmetry_cfg_entry_point \
--run_name ppo_with_symmetry_data_augmentation
# you can use hydra to disable symmetry augmentation but enable mirror loss computation
./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-v0 --headless \
--agent rsl_rl_with_symmetry_cfg_entry_point \
--run_name ppo_without_symmetry_data_augmentation \
agent.algorithm.symmetry_cfg.use_data_augmentation=false
The ``--run_name`` argument is used to specify the name of the run. This is used to
create a directory for the run in the ``logs/rsl_rl/cartpole`` directory.
.. _Stable-Baselines3: https://stable-baselines3.readthedocs.io/en/master/
.. _RL-Games: https://github.com/Denys88/rl_games
.. _RSL-RL: https://github.com/leggedrobotics/rsl_rl
.. _SKRL: https://skrl.readthedocs.io
......@@ -79,6 +79,7 @@ different aspects of the framework to create a simulation environment for agent
03_envs/create_direct_rl_env
03_envs/register_rl_env_gym
03_envs/run_rl_training
03_envs/configuring_rl_training
03_envs/modify_direct_rl_env
03_envs/policy_inference_in_usd
......
......@@ -21,6 +21,9 @@ parser.add_argument(
)
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="rl_games_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--checkpoint", type=str, default=None, help="Path to model checkpoint.")
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
......@@ -82,7 +85,7 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
@hydra_task_config(args_cli.task, "rl_games_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: dict):
"""Play with RL-Games agent."""
# grab task name for checkpoint path
......
......@@ -20,6 +20,9 @@ parser.add_argument("--video_length", type=int, default=200, help="Length of the
parser.add_argument("--video_interval", type=int, default=2000, help="Interval between video recordings (in steps).")
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="rl_games_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
"--distributed", action="store_true", default=False, help="Run training with multiple GPUs or nodes."
......@@ -84,7 +87,7 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
@hydra_task_config(args_cli.task, "rl_games_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: dict):
"""Train with RL-Games agent."""
# override configurations with non-hydra CLI arguments
......
......@@ -24,6 +24,9 @@ parser.add_argument(
)
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="rsl_rl_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
"--use_pretrained_checkpoint",
......@@ -77,7 +80,7 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
"""Play with RSL-RL agent."""
# grab task name for checkpoint path
......
......@@ -23,6 +23,9 @@ parser.add_argument("--video_length", type=int, default=200, help="Length of the
parser.add_argument("--video_interval", type=int, default=2000, help="Interval between video recordings (in steps).")
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="rsl_rl_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument("--max_iterations", type=int, default=None, help="RL Policy training iterations.")
parser.add_argument(
......@@ -100,7 +103,7 @@ torch.backends.cudnn.deterministic = False
torch.backends.cudnn.benchmark = False
@hydra_task_config(args_cli.task, "rsl_rl_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: RslRlOnPolicyRunnerCfg):
"""Train with RSL-RL agent."""
# override configurations with non-hydra CLI arguments
......
......@@ -22,6 +22,9 @@ parser.add_argument(
)
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="sb3_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--checkpoint", type=str, default=None, help="Path to model checkpoint.")
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
......@@ -86,7 +89,7 @@ from isaaclab_tasks.utils.parse_cfg import get_checkpoint_path
# PLACEHOLDER: Extension template (do not remove this comment)
@hydra_task_config(args_cli.task, "sb3_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: dict):
"""Play with stable-baselines agent."""
# grab task name for checkpoint path
......
......@@ -23,6 +23,9 @@ parser.add_argument("--video_length", type=int, default=200, help="Length of the
parser.add_argument("--video_interval", type=int, default=2000, help="Interval between video recordings (in steps).")
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent", type=str, default="sb3_cfg_entry_point", help="Name of the RL agent configuration entry point."
)
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument("--log_interval", type=int, default=100_000, help="Log data every n timesteps.")
parser.add_argument("--checkpoint", type=str, default=None, help="Continue the training from checkpoint.")
......@@ -96,7 +99,7 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
@hydra_task_config(args_cli.task, "sb3_cfg_entry_point")
@hydra_task_config(args_cli.task, args_cli.agent)
def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agent_cfg: dict):
"""Train with stable-baselines agent."""
# randomly sample a seed if seed = -1
......
......@@ -26,6 +26,15 @@ parser.add_argument(
)
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent",
type=str,
default=None,
help=(
"Name of the RL agent configuration entry point. Defaults to None, in which case the argument "
"--algorithm is used to determine the default agent configuration entry point."
),
)
parser.add_argument("--checkpoint", type=str, default=None, help="Path to model checkpoint.")
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
......@@ -107,8 +116,11 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
# config shortcuts
algorithm = args_cli.algorithm.lower()
agent_cfg_entry_point = "skrl_cfg_entry_point" if algorithm in ["ppo"] else f"skrl_{algorithm}_cfg_entry_point"
if args_cli.agent is None:
algorithm = args_cli.algorithm.lower()
agent_cfg_entry_point = "skrl_cfg_entry_point" if algorithm in ["ppo"] else f"skrl_{algorithm}_cfg_entry_point"
else:
agent_cfg_entry_point = args_cli.agent
@hydra_task_config(args_cli.task, agent_cfg_entry_point)
......
......@@ -24,6 +24,15 @@ parser.add_argument("--video_length", type=int, default=200, help="Length of the
parser.add_argument("--video_interval", type=int, default=2000, help="Interval between video recordings (in steps).")
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
parser.add_argument("--task", type=str, default=None, help="Name of the task.")
parser.add_argument(
"--agent",
type=str,
default=None,
help=(
"Name of the RL agent configuration entry point. Defaults to None, in which case the argument "
"--algorithm is used to determine the default agent configuration entry point."
),
)
parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment")
parser.add_argument(
"--distributed", action="store_true", default=False, help="Run training with multiple GPUs or nodes."
......@@ -103,8 +112,11 @@ from isaaclab_tasks.utils.hydra import hydra_task_config
# PLACEHOLDER: Extension template (do not remove this comment)
# config shortcuts
algorithm = args_cli.algorithm.lower()
agent_cfg_entry_point = "skrl_cfg_entry_point" if algorithm in ["ppo"] else f"skrl_{algorithm}_cfg_entry_point"
if args_cli.agent is None:
algorithm = args_cli.algorithm.lower()
agent_cfg_entry_point = "skrl_cfg_entry_point" if algorithm in ["ppo"] else f"skrl_{algorithm}_cfg_entry_point"
else:
agent_cfg_entry_point = args_cli.agent
@hydra_task_config(args_cli.task, agent_cfg_entry_point)
......
[package]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.10.45"
version = "0.10.46"
# Description
title = "Isaac Lab Environments"
......
Changelog
---------
0.10.46 (2025-08-16)
~~~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added symmetry data augmentation example with RSL-RL for cartpole and anymal locomotion environments.
* Added :attr:`--agent` to RL workflow scripts to allow switching between different configurations.
0.10.45 (2025-07-16)
~~~~~~~~~~~~~~~~~~~~
......
......@@ -23,6 +23,7 @@ gym.register(
"env_cfg_entry_point": f"{__name__}.cartpole_env_cfg:CartpoleEnvCfg",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_ppo_cfg.yaml",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:CartpolePPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:CartpolePPORunnerWithSymmetryCfg",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_ppo_cfg.yaml",
"sb3_cfg_entry_point": f"{agents.__name__}:sb3_ppo_cfg.yaml",
},
......
......@@ -5,7 +5,9 @@
from isaaclab.utils import configclass
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg, RslRlSymmetryCfg
import isaaclab_tasks.manager_based.classic.cartpole.mdp.symmetry as symmetry
@configclass
......@@ -35,3 +37,27 @@ class CartpolePPORunnerCfg(RslRlOnPolicyRunnerCfg):
desired_kl=0.01,
max_grad_norm=1.0,
)
@configclass
class CartpolePPORunnerWithSymmetryCfg(CartpolePPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=symmetry.compute_symmetric_states
),
)
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Functions to specify the symmetry in the observation and action space for cartpole."""
from __future__ import annotations
import torch
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from omni.isaac.lab.envs import ManagerBasedRLEnv
# specify the functions that are available for import
__all__ = ["compute_symmetric_states"]
@torch.no_grad()
def compute_symmetric_states(
env: ManagerBasedRLEnv,
obs: torch.Tensor | None = None,
actions: torch.Tensor | None = None,
obs_type: str = "policy",
):
"""Augments the given observations and actions by applying symmetry transformations.
This function creates augmented versions of the provided observations and actions by applying
two symmetrical transformations: original, left-right. The symmetry
transformations are beneficial for reinforcement learning tasks by providing additional
diverse data without requiring additional data collection.
Args:
env: The environment instance.
obs: The original observation tensor. Defaults to None.
actions: The original actions tensor. Defaults to None.
obs_type: The type of observation to augment. Defaults to "policy".
Returns:
Augmented observations and actions tensors, or None if the respective input was None.
"""
# observations
if obs is not None:
num_envs = obs.shape[0]
# since we have 2 different symmetries, we need to augment the batch size by 2
obs_aug = torch.zeros(num_envs * 2, obs.shape[1], device=obs.device)
# -- original
obs_aug[:num_envs] = obs[:]
# -- left-right
obs_aug[num_envs : 2 * num_envs] = -obs
else:
obs_aug = None
# actions
if actions is not None:
num_envs = actions.shape[0]
# since we have 4 different symmetries, we need to augment the batch size by 4
actions_aug = torch.zeros(num_envs * 2, actions.shape[1], device=actions.device)
# -- original
actions_aug[:num_envs] = actions[:]
# -- left-right
actions_aug[num_envs : 2 * num_envs] = -actions
else:
actions_aug = None
return obs_aug, actions_aug
......@@ -17,6 +17,7 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalBFlatEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBFlatPPORunnerWithSymmetryCfg",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
)
......@@ -28,6 +29,7 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalBFlatEnvCfg_PLAY",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBFlatPPORunnerWithSymmetryCfg",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
)
......@@ -39,6 +41,9 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalBRoughEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBRoughPPORunnerWithSymmetryCfg"
),
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -50,6 +55,9 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalBRoughEnvCfg_PLAY",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalBRoughPPORunnerWithSymmetryCfg"
),
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -5,7 +5,9 @@
from isaaclab.utils import configclass
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg, RslRlSymmetryCfg
from isaaclab_tasks.manager_based.locomotion.velocity.mdp.symmetry import anymal
@configclass
......@@ -46,3 +48,51 @@ class AnymalBFlatPPORunnerCfg(AnymalBRoughPPORunnerCfg):
self.experiment_name = "anymal_b_flat"
self.policy.actor_hidden_dims = [128, 128, 128]
self.policy.critic_hidden_dims = [128, 128, 128]
@configclass
class AnymalBFlatPPORunnerWithSymmetryCfg(AnymalBFlatPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
@configclass
class AnymalBRoughPPORunnerWithSymmetryCfg(AnymalBRoughPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
......@@ -18,6 +18,7 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalCFlatEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCFlatPPORunnerWithSymmetryCfg",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_flat_ppo_cfg.yaml",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
......@@ -29,8 +30,9 @@ gym.register(
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalCFlatEnvCfg_PLAY",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_flat_ppo_cfg.yaml",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCFlatPPORunnerWithSymmetryCfg",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_flat_ppo_cfg.yaml",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
)
......@@ -41,8 +43,11 @@ gym.register(
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalCRoughEnvCfg",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_rough_ppo_cfg.yaml",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCRoughPPORunnerWithSymmetryCfg"
),
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_rough_ppo_cfg.yaml",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -53,8 +58,11 @@ gym.register(
disable_env_checker=True,
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalCRoughEnvCfg_PLAY",
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_rough_ppo_cfg.yaml",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalCRoughPPORunnerWithSymmetryCfg"
),
"rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_rough_ppo_cfg.yaml",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -5,7 +5,9 @@
from isaaclab.utils import configclass
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg, RslRlSymmetryCfg
from isaaclab_tasks.manager_based.locomotion.velocity.mdp.symmetry import anymal
@configclass
......@@ -46,3 +48,51 @@ class AnymalCFlatPPORunnerCfg(AnymalCRoughPPORunnerCfg):
self.experiment_name = "anymal_c_flat"
self.policy.actor_hidden_dims = [128, 128, 128]
self.policy.critic_hidden_dims = [128, 128, 128]
@configclass
class AnymalCFlatPPORunnerWithSymmetryCfg(AnymalCFlatPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
@configclass
class AnymalCRoughPPORunnerWithSymmetryCfg(AnymalCRoughPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
......@@ -18,6 +18,7 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalDFlatEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDFlatPPORunnerWithSymmetryCfg",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
)
......@@ -29,6 +30,7 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.flat_env_cfg:AnymalDFlatEnvCfg_PLAY",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDFlatPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDFlatPPORunnerWithSymmetryCfg",
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_flat_ppo_cfg.yaml",
},
)
......@@ -40,6 +42,9 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalDRoughEnvCfg",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDRoughPPORunnerWithSymmetryCfg"
),
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -51,6 +56,9 @@ gym.register(
kwargs={
"env_cfg_entry_point": f"{__name__}.rough_env_cfg:AnymalDRoughEnvCfg_PLAY",
"rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDRoughPPORunnerCfg",
"rsl_rl_with_symmetry_cfg_entry_point": (
f"{agents.__name__}.rsl_rl_ppo_cfg:AnymalDRoughPPORunnerWithSymmetryCfg"
),
"skrl_cfg_entry_point": f"{agents.__name__}:skrl_rough_ppo_cfg.yaml",
},
)
......@@ -5,7 +5,9 @@
from isaaclab.utils import configclass
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg
from isaaclab_rl.rsl_rl import RslRlOnPolicyRunnerCfg, RslRlPpoActorCriticCfg, RslRlPpoAlgorithmCfg, RslRlSymmetryCfg
from isaaclab_tasks.manager_based.locomotion.velocity.mdp.symmetry import anymal
@configclass
......@@ -46,3 +48,50 @@ class AnymalDFlatPPORunnerCfg(AnymalDRoughPPORunnerCfg):
self.experiment_name = "anymal_d_flat"
self.policy.actor_hidden_dims = [128, 128, 128]
self.policy.critic_hidden_dims = [128, 128, 128]
@configclass
class AnymalDFlatPPORunnerWithSymmetryCfg(AnymalDFlatPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
@configclass
class AnymalDRoughPPORunnerWithSymmetryCfg(AnymalDRoughPPORunnerCfg):
"""Configuration for the PPO agent with symmetry augmentation."""
# all the other settings are inherited from the parent class
algorithm = RslRlPpoAlgorithmCfg(
value_loss_coef=1.0,
use_clipped_value_loss=True,
clip_param=0.2,
entropy_coef=0.005,
num_learning_epochs=5,
num_mini_batches=4,
learning_rate=1.0e-3,
schedule="adaptive",
gamma=0.99,
lam=0.95,
desired_kl=0.01,
max_grad_norm=1.0,
symmetry_cfg=RslRlSymmetryCfg(
use_data_augmentation=True, data_augmentation_func=anymal.compute_symmetric_states
),
)
# Copyright (c) 2022-2025, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Symmetry functions for the velocity tasks.
These functions are used to augment the observations and actions of the environment.
They are specific to the velocity task and the choice of the robot.
"""
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment