Commit 52422b6b authored by Kelly Guo's avatar Kelly Guo Committed by David Hoeller

Restructures documentation and fixes class types (#98)

This change restructures documentation to add a new Overview section
that combines previous pages around learning workflows, environment
design, and developer guides.

New pages are added for RL library comparison and training performance
benchmarking.

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- This change requires a documentation update

- [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

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->
parent e5214b89
......@@ -6,9 +6,19 @@ Overview
:alt: H1 Humanoid example using Isaac Lab
**Isaac Lab** is a unified and modular framework for robot learning that aims to simplify common workflows
in robotics research (such as RL, learning from demonstrations, and motion planning). It is built upon
in robotics research (such as reinforcement learning, learning from demonstrations, and motion planning). It is built upon
`NVIDIA Isaac Sim`_ to leverage the latest simulation capabilities for photo-realistic scenes, and fast
and efficient simulation. The core objectives of the framework are:
and efficient simulation.
Isaac Lab provides over 26 environments, and we are actively working on adding more environments to the list. Current environments include Classic tasks - Cartpole, Cartpole with camera, Humanoid and Ant, fixed-arm and dexterous manipulation tasks - UR10, Franka, Allegro, Shadow Hand, Legged locomotion tasks - Quadrupeds and Humanoids, Navigation tasks - Quadruped, and Quadcopter. It features over 16 robots:
- Classic - Cartpole, Humanoid, Ant
- Fixed-Arms and Hands - UR10, Franka, Allegro, Shadow Hand
- Quadrupeds – Anybotics Anymal-B, Anymal-C, Anymal-D, Unitree A1, Unitree Go1, Unitree Go2, Boston Dynamics Spot
- Humanoids - Unitree H1, Unitree G1
- Quadcopter - Crazyflie
The core objectives of the framework are:
- **Modularity**: Easily customize and add new environments, robots, and sensors.
- **Agility**: Adapt to the changing needs of the community.
......@@ -60,23 +70,18 @@ Table of Contents
:caption: Getting Started
source/setup/installation/index
source/setup/developer
source/setup/sample
source/setup/template
source/setup/faq
.. toctree::
:maxdepth: 2
:caption: Features
:maxdepth: 3
:caption: Overview
:titlesonly:
source/features/task_workflows
source/features/hydra
source/features/multi_gpu
source/features/tiled_rendering
source/features/environments
source/features/actuators
source/features/reproducibility
.. source/features/motion_generators
source/overview/developer-guide/index
source/overview/environment-workflows/index
source/overview/reinforcement-learning/index
source/overview/teleop_imitation
source/overview/sample_scripts
.. toctree::
:maxdepth: 1
......@@ -87,12 +92,6 @@ Table of Contents
source/how-to/index
source/deployment/index
.. toctree::
:maxdepth: 1
:caption: Source API
source/api/index
.. toctree::
:maxdepth: 1
:caption: Migration Guides
......@@ -102,6 +101,25 @@ Table of Contents
source/migration/migrating_from_omniisaacgymenvs
source/migration/migrating_from_orbit
.. toctree::
:maxdepth: 3
:caption: Features
source/features/task_workflows
source/features/hydra
source/features/multi_gpu
source/features/tiled_rendering
source/features/environments
source/features/actuators
source/features/reproducibility
.. source/features/motion_generators
.. toctree::
:maxdepth: 1
:caption: Source API
source/api/index
.. toctree::
:maxdepth: 1
:caption: References
......
......@@ -75,7 +75,8 @@ for the reach environment:
+----------------+---------------------------+-----------------------------------------------------------------------------+
| |lift-cube| | |lift-cube-link| | Pick a cube and bring it to a sampled target position with the Franka robot |
+----------------+---------------------------+-----------------------------------------------------------------------------+
| |cabi-franka| | |cabi-franka-link| | Grasp the handle of a cabinet's drawer and open it with the Franka robot |
| |cabi-franka| | | |cabi-franka-link| | Grasp the handle of a cabinet's drawer and open it with the Franka robot |
| | | |franka-direct-link| | |
+----------------+---------------------------+-----------------------------------------------------------------------------+
| |cube-allegro| | |cube-allegro-link| | In-hand reorientation of a cube using Allegro hand |
| | |allegro-direct-link| | |
......@@ -98,6 +99,7 @@ for the reach environment:
.. |lift-cube-ik-abs-link| replace:: `Isaac-Lift-Cube-Franka-IK-Abs-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/manipulation/lift/config/franka/ik_abs_env_cfg.py>`__
.. |lift-cube-ik-rel-link| replace:: `Isaac-Lift-Cube-Franka-IK-Rel-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/manipulation/lift/config/franka/ik_rel_env_cfg.py>`__
.. |cabi-franka-link| replace:: `Isaac-Open-Drawer-Franka-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/manipulation/cabinet/config/franka/joint_pos_env_cfg.py>`__
.. |franka-direct-link| replace:: `Isaac-Franka-Cabinet-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/franka_cabinet/franka_cabinet_env.py>`__
.. |cube-allegro-link| replace:: `Isaac-Repose-Cube-Allegro-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/manipulation/inhand/config/allegro_hand/allegro_env_cfg.py>`__
.. |allegro-direct-link| replace:: `Isaac-Repose-Cube-Allegro-Direct-v0 <https://github.com/isaac-sim/IsaacLab/blob/main/source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/allegro_hand/allegro_hand_env_cfg.py>`__
......
......@@ -10,7 +10,7 @@ Environment wrappers are a way to modify the behavior of an environment without
This can be used to apply functions to modify observations or rewards, record videos, enforce time limits, etc.
A detailed description of the API is available in the :class:`gymnasium.Wrapper` class.
At present, all RL environments inheriting from the :class:`~envs.ManagerBasedRLEnv` class
At present, all RL environments inheriting from the :class:`~envs.ManagerBasedRLEnv` or :class:`~envs.DirectRLEnv` classes
are compatible with :class:`gymnasium.Wrapper`, since the base class implements the :class:`gymnasium.Env` interface.
In order to wrap an environment, you need to first initialize the base environment. After that, you can
wrap it with as many wrappers as you want by calling ``env = wrapper(env, *args, **kwargs)`` repeatedly.
......@@ -127,7 +127,7 @@ Every learning framework has its own API for interacting with environments. For
`Stable-Baselines3`_ library uses the `gym.Env <https://gymnasium.farama.org/api/env/>`_
interface to interact with environments. However, libraries like `RL-Games`_, `RSL-RL`_ or `SKRL`_
use their own API for interfacing with a learning environments. Since there is no one-size-fits-all
solution, we do not base the :class:`~envs.ManagerBasedRLEnv` class on any particular learning framework's
solution, we do not base the :class:`~envs.ManagerBasedRLEnv` and :class:`~envs.DirectRLEnv` classes on any particular learning framework's
environment definition. Instead, we implement wrappers to make it compatible with the learning
framework's environment definition.
......@@ -155,6 +155,7 @@ Adding new wrappers
All new wrappers should be added to the :mod:`omni.isaac.lab_tasks.utils.wrappers` module.
They should check that the underlying environment is an instance of :class:`omni.isaac.lab.envs.ManagerBasedRLEnv`
or :class:`~envs.DirectRLEnv`
before applying the wrapper. This can be done by using the :func:`unwrapped` property.
We include a set of wrappers in this module that can be used as a reference to implement your own wrappers.
......
Developer's Guide
=================
For development, we suggest using `Microsoft Visual Studio Code
(VSCode) <https://code.visualstudio.com/>`__. This is also suggested by
NVIDIA Omniverse and there exists tutorials on how to `debug Omniverse
extensions <https://www.youtube.com/watch?v=Vr1bLtF1f4U&ab_channel=NVIDIAOmniverse>`__
using VSCode.
Setting up Visual Studio Code
-----------------------------
The following is only applicable for Isaac Sim installed via the Omniverse Launcher.
The ``Isaac Lab`` repository includes the VSCode settings to easily allow setting
up your development environment. These are included in the ``.vscode`` directory
and include the following files:
.. code-block:: bash
.vscode
├── tools
│   ├── launch.template.json
│   ├── settings.template.json
│   └── setup_vscode.py
├── extensions.json
├── launch.json # <- this is generated by setup_vscode.py
├── settings.json # <- this is generated by setup_vscode.py
└── tasks.json
To setup the IDE, please follow these instructions:
1. Open the ``Isaac Lab`` directory on Visual Studio Code IDE
2. Run VSCode `Tasks <https://code.visualstudio.com/docs/editor/tasks>`__, by
pressing ``Ctrl+Shift+P``, selecting ``Tasks: Run Task`` and running the
``setup_python_env`` in the drop down menu.
.. image:: ../_static/vscode_tasks.png
:width: 600px
:align: center
:alt: VSCode Tasks
If everything executes correctly, it should create the following files:
* ``.vscode/launch.json``: Contains the launch configurations for debugging python code.
* ``.vscode/settings.json``: Contains the settings for the python interpreter and the python environment.
For more information on VSCode support for Omniverse, please refer to the
following links:
* `Isaac Sim VSCode support <https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/manual_standalone_python.html#isaac-sim-python-vscode>`__
* `Debugging with VSCode <https://docs.omniverse.nvidia.com/isaacsim/latest/advanced_tutorials/tutorial_advanced_python_debugging.html>`__
Configuring the python interpreter
----------------------------------
In the provided configuration, we set the default python interpreter to use the
python executable provided by Omniverse. This is specified in the
``.vscode/settings.json`` file:
.. code-block:: json
{
"python.defaultInterpreterPath": "${workspaceFolder}/_isaac_sim/python.sh",
}
If you want to use a different python interpreter (for instance, from your conda environment),
you need to change the python interpreter used by selecting and activating the python interpreter
of your choice in the bottom left corner of VSCode, or opening the command palette (``Ctrl+Shift+P``)
and selecting ``Python: Select Interpreter``.
For more information on how to set python interpreter for VSCode, please
refer to the `VSCode documentation <https://code.visualstudio.com/docs/python/environments#_working-with-python-interpreters>`_.
Repository organization
-----------------------
The ``Isaac Lab`` repository is structured as follows:
.. code-block:: bash
IsaacLab
├── .vscode
├── .flake8
├── LICENSE
├── isaaclab.sh
├── pyproject.toml
├── README.md
├── docs
├── source
│   ├── extensions
│   │   ├── omni.isaac.lab
│   │   ├── omni.isaac.lab_assets
│   │   └── omni.isaac.lab_tasks
│   ├── standalone
│   │   ├── demos
│   │   ├── environments
│   │   ├── tools
│   │   ├── tutorials
│   │   └── workflows
└── VERSION
The ``source`` directory contains the source code for all ``Isaac Lab`` *extensions*
and *standalone applications*. The two are the different development workflows
supported in `Isaac Sim <https://docs.omniverse.nvidia.com/isaacsim/latest/introductory_tutorials/tutorial_intro_workflows.html>`__.
These are described in the following sections.
Extensions
~~~~~~~~~~
......
Developer's Guide
=================
For development, we suggest using `Microsoft Visual Studio Code
(VSCode) <https://code.visualstudio.com/>`__. This is also suggested by
NVIDIA Omniverse and there exists tutorials on how to `debug Omniverse
extensions <https://www.youtube.com/watch?v=Vr1bLtF1f4U&ab_channel=NVIDIAOmniverse>`__
using VSCode.
.. toctree::
:maxdepth: 1
vs_code
repo_structure
extensions
template
Repository organization
-----------------------
The ``Isaac Lab`` repository is structured as follows:
.. code-block:: bash
IsaacLab
├── .vscode
├── .flake8
├── LICENSE
├── isaaclab.sh
├── pyproject.toml
├── README.md
├── docs
├── source
│   ├── extensions
│   │   ├── omni.isaac.lab
│   │   ├── omni.isaac.lab_assets
│   │   └── omni.isaac.lab_tasks
│   ├── standalone
│   │   ├── demos
│   │   ├── environments
│   │   ├── tools
│   │   ├── tutorials
│   │   └── workflows
└── VERSION
The ``source`` directory contains the source code for all ``Isaac Lab`` *extensions*
and *standalone applications*. The two are the different development workflows
supported in `Isaac Sim <https://docs.omniverse.nvidia.com/isaacsim/latest/introductory_tutorials/tutorial_intro_workflows.html>`__.
These are described in the following sections.
Setting up Visual Studio Code
-----------------------------
The following is only applicable for Isaac Sim installed via the Omniverse Launcher.
The ``Isaac Lab`` repository includes the VSCode settings to easily allow setting
up your development environment. These are included in the ``.vscode`` directory
and include the following files:
.. code-block:: bash
.vscode
├── tools
│   ├── launch.template.json
│   ├── settings.template.json
│   └── setup_vscode.py
├── extensions.json
├── launch.json # <- this is generated by setup_vscode.py
├── settings.json # <- this is generated by setup_vscode.py
└── tasks.json
To setup the IDE, please follow these instructions:
1. Open the ``Isaac Lab`` directory on Visual Studio Code IDE
2. Run VSCode `Tasks <https://code.visualstudio.com/docs/editor/tasks>`__, by
pressing ``Ctrl+Shift+P``, selecting ``Tasks: Run Task`` and running the
``setup_python_env`` in the drop down menu.
.. image:: ../../_static/vscode_tasks.png
:width: 600px
:align: center
:alt: VSCode Tasks
If everything executes correctly, it should create the following files:
* ``.vscode/launch.json``: Contains the launch configurations for debugging python code.
* ``.vscode/settings.json``: Contains the settings for the python interpreter and the python environment.
For more information on VSCode support for Omniverse, please refer to the
following links:
* `Isaac Sim VSCode support <https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/manual_standalone_python.html#isaac-sim-python-vscode>`__
* `Debugging with VSCode <https://docs.omniverse.nvidia.com/isaacsim/latest/advanced_tutorials/tutorial_advanced_python_debugging.html>`__
Configuring the python interpreter
----------------------------------
In the provided configuration, we set the default python interpreter to use the
python executable provided by Omniverse. This is specified in the
``.vscode/settings.json`` file:
.. code-block:: json
{
"python.defaultInterpreterPath": "${workspaceFolder}/_isaac_sim/python.sh",
}
If you want to use a different python interpreter (for instance, from your conda environment),
you need to change the python interpreter used by selecting and activating the python interpreter
of your choice in the bottom left corner of VSCode, or opening the command palette (``Ctrl+Shift+P``)
and selecting ``Python: Select Interpreter``.
For more information on how to set python interpreter for VSCode, please
refer to the `VSCode documentation <https://code.visualstudio.com/docs/python/environments#_working-with-python-interpreters>`_.
Environment Workflows
=====================
Workflows
---------
With Isaac Lab, we also provide a suite of benchmark environments included
in the ``omni.isaac.lab_tasks`` extension. We use the OpenAI Gym registry
to register these environments. For each environment, we provide a default
configuration file that defines the scene, observations, rewards and action spaces.
The list of environments available registered with OpenAI Gym can be found by running:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/list_envs.py
Basic agents
~~~~~~~~~~~~
These include basic agents that output zero or random agents. They are
useful to ensure that the environments are configured correctly.
- Zero-action agent on the Cart-pole example
.. code:: bash
./isaaclab.sh -p source/standalone/environments/zero_agent.py --task Isaac-Cartpole-v0 --num_envs 32
- Random-action agent on the Cart-pole example:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/random_agent.py --task Isaac-Cartpole-v0 --num_envs 32
State machine
~~~~~~~~~~~~~
We include examples on hand-crafted state machines for the environments. These
help in understanding the environment and how to use the provided interfaces.
The state machines are written in `warp <https://github.com/NVIDIA/warp>`__ which
allows efficient execution for large number of environments using CUDA kernels.
.. code:: bash
./isaaclab.sh -p source/standalone/environments/state_machine/lift_cube_sm.py --num_envs 32
Introduction to Environments
============================
This section introduces fundamental concepts of environments and agents in Isaac Lab.
Example scripts are available to show implementations of basic agents and state machines.
In addition, we introduce the different task workflows available in Isaac Lab for
implementing new environments.
.. toctree::
:maxdepth: 1
basic_environments
task_workflows
......@@ -68,7 +68,7 @@ for each component (such as the ``ObservationCfg`` and ``RewardCfg``).
implementation, weight and additional parameters to be passed to the function. Users can define multiple
reward terms and their weights to be used in the reward function.
.. literalinclude:: ../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/classic/cartpole/cartpole_env_cfg.py
.. literalinclude:: ../../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/manager_based/classic/cartpole/cartpole_env_cfg.py
:language: python
:pyobject: RewardsCfg
......@@ -105,14 +105,14 @@ for setting up the scene, processing the actions, computing the rewards, observa
The following function is a part of the Cartpole environment class and is responsible for computing the rewards.
.. literalinclude:: ../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/cartpole/cartpole_env.py
.. literalinclude:: ../../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/cartpole/cartpole_env.py
:language: python
:pyobject: CartpoleEnv._get_rewards
:dedent: 4
It calls the :meth:`compute_rewards` function which is Torch JIT compiled for performance benefits.
.. literalinclude:: ../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/cartpole/cartpole_env.py
.. literalinclude:: ../../../../source/extensions/omni.isaac.lab_tasks/omni/isaac/lab_tasks/direct/cartpole/cartpole_env.py
:language: python
:pyobject: compute_rewards
......
Reinforcement Learning
======================
Isaac Lab supports multiple reinforcement learning frameworks.
In this section, we show existing scripts for running reinforcement learning
with supported RL libraries and provide a comparison of the supported
learning frameworks.
.. toctree::
:maxdepth: 1
rl_existing_scripts
rl_frameworks
performance_benchmarks
Performance Benchmarks
======================
Isaac Lab leverages end-to-end GPU training for reinforcement learning workflows,
allowing for fast parallel training across thousands of environments.
In this section, we provide runtime performance benchmark results for reinforcement learning
training of various example environments on different GPU setups.
Multi-GPU and multi-node training performance results are also outlined.
Benchmark Results
-----------------
All benchmarking results were performed with the RL Games library with ``--headless`` flag on Ubuntu 22.04.
``Isaac-Velocity-Rough-G1-v0`` environment benchmarks were performed with the RSL RL library.
Memory Consumption
^^^^^^^^^^^^^^^^^^
+-------------------------------------+-------------------+----------+-----------+
| Environment Name | # of Environments | RAM (GB) | VRAM (GB) |
+=====================================+===================+==========+===========+
| Isaac-Cartpole-Direct-v0 | 4096 | 3.7 | 3.3 |
+-------------------------------------+-------------------+----------+-----------+
| Isaac-Cartpole-RGB-Camera-Direct-v0 | 1024 | 7.5 | 16.7 |
+-------------------------------------+-------------------+----------+-----------+
| Isaac-Velocity-Rough-G1-v0 | 4096 | 6.5 | 6.1 |
+-------------------------------------+-------------------+----------+-----------+
| Isaac-Repose-Cube-Shadow-Direct-v0 | 8192 | 6.7 | 6.4 |
+-------------------------------------+-------------------+----------+-----------+
Single GPU - RTX 4090
^^^^^^^^^^^^^^^^^^^^^
CPU: AMD Ryzen 9 7950X 16-Core Processor
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Environment Name | # of Environments | Environment | Environment Step | Environment Step, |
| | | Step FPS | and | Inference, |
| | | | Inference FPS | and Train FPS |
+=====================================+===================+==============+===================+====================+
| Isaac-Cartpole-Direct-v0 | 4096 | 1100000 | 910000 | 510000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Cartpole-RGB-Camera-Direct-v0 | 1024 | 50000 | 45000 | 32000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Velocity-Rough-G1-v0 | 4096 | 94000 | 88000 | 82000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Repose-Cube-Shadow-Direct-v0 | 8192 | 200000 | 190000 | 170000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
Single GPU - L40
^^^^^^^^^^^^^^^^
CPU: Intel(R) Xeon(R) Platinum 8362 CPU @ 2.80GHz
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Environment Name | # of Environments | Environment | Environment Step | Environment Step, |
| | | Step FPS | and | Inference, |
| | | | Inference FPS | and Train FPS |
+=====================================+===================+==============+===================+====================+
| Isaac-Cartpole-Direct-v0 | 4096 | 620000 | 490000 | 260000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Cartpole-RGB-Camera-Direct-v0 | 1024 | 30000 | 28000 | 21000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Velocity-Rough-G1-v0 | 4096 | 72000 | 64000 | 62000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Repose-Cube-Shadow-Direct-v0 | 8192 | 170000 | 140000 | 120000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
Single-Node, 4 x L40 GPUs
^^^^^^^^^^^^^^^^^^^^^^^^^
CPU: Intel(R) Xeon(R) Platinum 8362 CPU @ 2.80GHz
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Environment Name | # of Environments | Environment | Environment Step | Environment Step, |
| | | Step FPS | and | Inference, |
| | | | Inference FPS | and Train FPS |
+=====================================+===================+==============+===================+====================+
| Isaac-Cartpole-Direct-v0 | 4096 | 2700000 | 2100000 | 950000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Cartpole-RGB-Camera-Direct-v0 | 1024 | 130000 | 120000 | 90000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Velocity-Rough-G1-v0 | 4096 | 290000 | 270000 | 250000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Repose-Cube-Shadow-Direct-v0 | 8192 | 440000 | 420000 | 390000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
4 Nodes, 4 x L40 GPUs per node
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CPU: Intel(R) Xeon(R) Platinum 8362 CPU @ 2.80GHz
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Environment Name | # of Environments | Environment | Environment Step | Environment Step, |
| | | Step FPS | and | Inference, |
| | | | Inference FPS | and Train FPS |
+=====================================+===================+==============+===================+====================+
| Isaac-Cartpole-Direct-v0 | 4096 | 10200000 | 8200000 | 3500000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Cartpole-RGB-Camera-Direct-v0 | 1024 | 530000 | 490000 | 260000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Velocity-Rough-G1-v0 | 4096 | 1200000 | 1100000 | 960000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
| Isaac-Repose-Cube-Shadow-Direct-v0 | 8192 | 2400000 | 2300000 | 1800000 |
+-------------------------------------+-------------------+--------------+-------------------+--------------------+
Benchmark Scripts
-----------------
For ease of reproducibility, we provide benchmarking scripts available at ``source/standalone/benchmarks``.
This folder contains individual benchmark scripts that resemble the ``train.py`` script for RL-Games
and RSL RL. In addition, we also provide a benchmarking script that runs only the environment implementation
without any reinforcement learning library.
Example scripts can be run similarly to training scripts:
.. code-block:: bash
# benchmark with RSL RL
python source/standalone/benchmarks/benchmark_rsl_rl.py --task=Isaac-Cartpole-v0 --headless
# benchmark with RL Games
python source/standalone/benchmarks/benchmark_rlgames.py --task=Isaac-Cartpole-v0 --headless
# benchmark without RL libraries
python source/standalone/benchmarks/benchmark_non_rl.py --task=Isaac-Cartpole-v0 --headless
Each script will generate a set of KPI files at the end of the run, which includes data on the
startup times, runtime statistics, such as the time taken for each simulation or rendering step,
as well as overall environment FPS for stepping the environment, performing inference during
rollout, as well as training.
Running Existing Scripts
========================
Showroom
--------
The main core interface extension in Isaac Lab ``omni.isaac.lab`` provides
the main modules for actuators, objects, robots and sensors. We provide
a list of demo scripts and tutorials. These showcase how to use the provided
interfaces within a code in a minimal way.
A few quick showroom scripts to run and checkout:
- Spawn different quadrupeds and make robots stand using position commands:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/quadrupeds.py
- Spawn different arms and apply random joint position commands:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/arms.py
- Spawn different hands and command them to open and close:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/hands.py
- Spawn procedurally generated terrains with different configurations:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/procedural_terrain.py
- Spawn different deformable (soft) bodies and let them fall from a height:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/deformables.py
- Spawn multiple markers that are useful for visualizations:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/markers.py
Workflows
---------
With Isaac Lab, we also provide a suite of benchmark environments included
in the ``omni.isaac.lab_tasks`` extension. We use the OpenAI Gym registry
to register these environments. For each environment, we provide a default
configuration file that defines the scene, observations, rewards and action spaces.
The list of environments available registered with OpenAI Gym can be found by running:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/list_envs.py
Basic agents
~~~~~~~~~~~~
These include basic agents that output zero or random agents. They are
useful to ensure that the environments are configured correctly.
- Zero-action agent on the Cart-pole example
.. code:: bash
./isaaclab.sh -p source/standalone/environments/zero_agent.py --task Isaac-Cartpole-v0 --num_envs 32
- Random-action agent on the Cart-pole example:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/random_agent.py --task Isaac-Cartpole-v0 --num_envs 32
State machine
~~~~~~~~~~~~~
We include examples on hand-crafted state machines for the environments. These
help in understanding the environment and how to use the provided interfaces.
The state machines are written in `warp <https://github.com/NVIDIA/warp>`__ which
allows efficient execution for large number of environments using CUDA kernels.
.. code:: bash
./isaaclab.sh -p source/standalone/environments/state_machine/lift_cube_sm.py --num_envs 32
Teleoperation
~~~~~~~~~~~~~
We provide interfaces for providing commands in SE(2) and SE(3) space
for robot control. In case of SE(2) teleoperation, the returned command
is the linear x-y velocity and yaw rate, while in SE(3), the returned
command is a 6-D vector representing the change in pose.
To play inverse kinematics (IK) control with a keyboard device:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/teleoperation/teleop_se3_agent.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --num_envs 1 --teleop_device keyboard
The script prints the teleoperation events configured. For keyboard,
these are as follows:
.. code:: text
Keyboard Controller for SE(3): Se3Keyboard
Reset all commands: L
Toggle gripper (open/close): K
Move arm along x-axis: W/S
Move arm along y-axis: A/D
Move arm along z-axis: Q/E
Rotate arm along x-axis: Z/X
Rotate arm along y-axis: T/G
Rotate arm along z-axis: C/V
Imitation Learning
~~~~~~~~~~~~~~~~~~
Using the teleoperation devices, it is also possible to collect data for
learning from demonstrations (LfD). For this, we support the learning
framework `Robomimic <https://robomimic.github.io/>`__ (Linux only) and allow saving
data in
`HDF5 <https://robomimic.github.io/docs/tutorials/dataset_contents.html#viewing-hdf5-dataset-structure>`__
format.
1. Collect demonstrations with teleoperation for the environment
``Isaac-Lift-Cube-Franka-IK-Rel-v0``:
.. code:: bash
# step a: collect data with keyboard
./isaaclab.sh -p source/standalone/workflows/robomimic/collect_demonstrations.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --num_envs 1 --num_demos 10 --teleop_device keyboard
# step b: inspect the collected dataset
./isaaclab.sh -p source/standalone/workflows/robomimic/tools/inspect_demonstrations.py logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5
2. Split the dataset into train and validation set:
.. code:: bash
# install the dependencies
sudo apt install cmake build-essential
# install python module (for robomimic)
./isaaclab.sh -i robomimic
# split data
./isaaclab.sh -p source/standalone//workflows/robomimic/tools/split_train_val.py logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5 --ratio 0.2
3. Train a BC agent for ``Isaac-Lift-Cube-Franka-IK-Rel-v0`` with
`Robomimic <https://robomimic.github.io/>`__:
.. code:: bash
./isaaclab.sh -p source/standalone/workflows/robomimic/train.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --algo bc --dataset logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5
4. Play the learned model to visualize results:
.. code:: bash
./isaaclab.sh -p source/standalone/workflows/robomimic/play.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --checkpoint /PATH/TO/model.pth
Reinforcement Learning
~~~~~~~~~~~~~~~~~~~~~~
Reinforcement Learning Wrappers
===============================
We provide wrappers to different reinforcement libraries. These wrappers convert the data
from the environments into the respective libraries function argument and return types.
Stable-Baselines3
-----------------
- Training an agent with
`Stable-Baselines3 <https://stable-baselines3.readthedocs.io/en/master/index.html>`__
on ``Isaac-Cartpole-v0``:
......@@ -189,6 +23,9 @@ from the environments into the respective libraries function argument and return
# run script for recording video of a trained agent (requires installing `ffmpeg`)
./isaaclab.sh -p source/standalone/workflows/sb3/play.py --task Isaac-Cartpole-v0 --headless --video --video_length 200
SKRL
----
- Training an agent with
`SKRL <https://skrl.readthedocs.io>`__ on ``Isaac-Reach-Franka-v0``:
......@@ -222,6 +59,9 @@ from the environments into the respective libraries function argument and return
# run script for recording video of a trained agent (requires installing `ffmpeg`)
./isaaclab.sh -p source/standalone/workflows/skrl/play.py --task Isaac-Reach-Franka-v0 --headless --ml_framework jax --video --video_length 200
RL-Games
--------
- Training an agent with
`RL-Games <https://github.com/Denys88/rl_games>`__ on ``Isaac-Ant-v0``:
......@@ -236,6 +76,10 @@ from the environments into the respective libraries function argument and return
# run script for recording video of a trained agent (requires installing `ffmpeg`)
./isaaclab.sh -p source/standalone/workflows/rl_games/play.py --task Isaac-Ant-v0 --headless --video --video_length 200
RSL-RL
------
- Training an agent with
`RSL-RL <https://github.com/leggedrobotics/rsl_rl>`__ on ``Isaac-Reach-Franka-v0``:
......
Reinforcement Learning Library Comparison
=========================================
In this section, we provide an overview of the supported reinforcement learning libraries in Isaac Lab,
along with performance benchmarks across the libraries.
The supported libraries are:
- `SKRL <https://skrl.readthedocs.io>`__
- `RSL-RL <https://github.com/leggedrobotics/rsl_rl>`__
- `RL-Games <https://github.com/Denys88/rl_games>`__
- `Stable-Baselines3 <https://stable-baselines3.readthedocs.io/en/master/index.html>`__
Feature Comparison
------------------
.. list-table::
:widths: 20 20 20 20 20
:header-rows: 1
* - Feature
- RL-Games
- RSL RL
- SKRL
- Stable Baselines3
* - Algorithms Included
- PPO, SAC, A2C
- PPO
- `Extensive List <https://skrl.readthedocs.io/en/latest/#agents>`__
- `Extensive List <https://github.com/DLR-RM/stable-baselines3?tab=readme-ov-file#implemented-algorithms>`__
* - Vectorized Training
- Yes
- Yes
- Yes
- No
* - Distributed Training
- Yes
- No
- Yes
- No
* - ML Frameworks Supported
- PyTorch
- PyTorch
- PyTorch, JAX
- PyTorch
* - Multi-Agent Support
- PPO
- PPO
- PPO + Multi-Agent algorithms
- External projects support
* - Documentation
- Low
- Low
- Moderate
- Extensive
* - Community Support
- Small Community
- Small Community
- Small Community
- Large Community
* - Available Examples in Isaac Lab
- Large
- Large
- Large
- Small
Training Performance
--------------------
We performed training with each RL library on the same ``Isaac-Humanoid-v0`` environment
with ``--headless`` on a single RTX 4090 GPU
and logged the total training time for 65.5M steps for each RL library.
+--------------------+-----------------+
| RL Library | Time in seconds |
+====================+=================+
| RL-Games | 216 |
+--------------------+-----------------+
| RSL RL | 215 |
+--------------------+-----------------+
| SKRL | 321 |
+--------------------+-----------------+
| Stable-Baselines3 | 6320 |
+--------------------+-----------------+
Showroom Demos
==============
The main core interface extension in Isaac Lab ``omni.isaac.lab`` provides
the main modules for actuators, objects, robots and sensors. We provide
a list of demo scripts and tutorials. These showcase how to use the provided
interfaces within a code in a minimal way.
A few quick showroom scripts to run and checkout:
- Spawn different quadrupeds and make robots stand using position commands:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/quadrupeds.py
- Spawn different arms and apply random joint position commands:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/arms.py
- Spawn different hands and command them to open and close:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/hands.py
- Spawn procedurally generated terrains with different configurations:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/procedural_terrain.py
- Spawn different deformable (soft) bodies and let them fall from a height:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/deformables.py
- Spawn multiple markers that are useful for visualizations:
.. code:: bash
./isaaclab.sh -p source/standalone/demos/markers.py
Teleoperation and Imitation Learning
====================================
Teleoperation
~~~~~~~~~~~~~
We provide interfaces for providing commands in SE(2) and SE(3) space
for robot control. In case of SE(2) teleoperation, the returned command
is the linear x-y velocity and yaw rate, while in SE(3), the returned
command is a 6-D vector representing the change in pose.
To play inverse kinematics (IK) control with a keyboard device:
.. code:: bash
./isaaclab.sh -p source/standalone/environments/teleoperation/teleop_se3_agent.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --num_envs 1 --teleop_device keyboard
The script prints the teleoperation events configured. For keyboard,
these are as follows:
.. code:: text
Keyboard Controller for SE(3): Se3Keyboard
Reset all commands: L
Toggle gripper (open/close): K
Move arm along x-axis: W/S
Move arm along y-axis: A/D
Move arm along z-axis: Q/E
Rotate arm along x-axis: Z/X
Rotate arm along y-axis: T/G
Rotate arm along z-axis: C/V
Imitation Learning
~~~~~~~~~~~~~~~~~~
Using the teleoperation devices, it is also possible to collect data for
learning from demonstrations (LfD). For this, we support the learning
framework `Robomimic <https://robomimic.github.io/>`__ (Linux only) and allow saving
data in
`HDF5 <https://robomimic.github.io/docs/tutorials/dataset_contents.html#viewing-hdf5-dataset-structure>`__
format.
1. Collect demonstrations with teleoperation for the environment
``Isaac-Lift-Cube-Franka-IK-Rel-v0``:
.. code:: bash
# step a: collect data with keyboard
./isaaclab.sh -p source/standalone/workflows/robomimic/collect_demonstrations.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --num_envs 1 --num_demos 10 --teleop_device keyboard
# step b: inspect the collected dataset
./isaaclab.sh -p source/standalone/workflows/robomimic/tools/inspect_demonstrations.py logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5
2. Split the dataset into train and validation set:
.. code:: bash
# install the dependencies
sudo apt install cmake build-essential
# install python module (for robomimic)
./isaaclab.sh -i robomimic
# split data
./isaaclab.sh -p source/standalone//workflows/robomimic/tools/split_train_val.py logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5 --ratio 0.2
3. Train a BC agent for ``Isaac-Lift-Cube-Franka-IK-Rel-v0`` with
`Robomimic <https://robomimic.github.io/>`__:
.. code:: bash
./isaaclab.sh -p source/standalone/workflows/robomimic/train.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --algo bc --dataset logs/robomimic/Isaac-Lift-Cube-Franka-IK-Rel-v0/hdf_dataset.hdf5
4. Play the learned model to visualize results:
.. code:: bash
./isaaclab.sh -p source/standalone/workflows/robomimic/play.py --task Isaac-Lift-Cube-Franka-IK-Rel-v0 --checkpoint /PATH/TO/model.pth
......@@ -64,7 +64,7 @@ class PhysxCfg:
"""
min_velocity_iteration_count: int = 0
"""Minimum number of solver position iterations (rigid bodies, cloth, particles etc.). Default is 0.
"""Minimum number of solver velocity iterations (rigid bodies, cloth, particles etc.). Default is 0.
.. note::
......@@ -74,7 +74,7 @@ class PhysxCfg:
"""
max_velocity_iteration_count: int = 255
"""Maximum number of solver position iterations (rigid bodies, cloth, particles etc.). Default is 255.
"""Maximum number of solver velocity iterations (rigid bodies, cloth, particles etc.). Default is 255.
.. note::
......
......@@ -3,7 +3,7 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""Wrapper to configure an :class:`ManagerBasedRLEnv` instance to RL-Games vectorized environment.
"""Wrapper to configure a :class:`ManagerBasedRLEnv` or :class:`DirectRlEnv` instance to RL-Games vectorized environment.
The following example shows how to wrap an environment for RL-Games and register the environment construction
for RL-Games :class:`Runner` class:
......@@ -60,7 +60,7 @@ class RlGamesVecEnvWrapper(IVecEnv):
observations. This dictionary contains "obs" and "states" which typically correspond
to the actor and critic observations respectively.
To use asymmetric actor-critic, the environment observations from :class:`ManagerBasedRLEnv`
To use asymmetric actor-critic, the environment observations from :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`
must have the key or group name "critic". The observation group is used to set the
:attr:`num_states` (int) and :attr:`state_space` (:obj:`gym.spaces.Box`). These are
used by the learning agent in RL-Games to allocate buffers in the trajectory memory.
......@@ -79,7 +79,7 @@ class RlGamesVecEnvWrapper(IVecEnv):
https://github.com/NVIDIA-Omniverse/IsaacGymEnvs
"""
def __init__(self, env: ManagerBasedRLEnv, rl_device: str, clip_obs: float, clip_actions: float):
def __init__(self, env: ManagerBasedRLEnv | DirectRLEnv, rl_device: str, clip_obs: float, clip_actions: float):
"""Initializes the wrapper instance.
Args:
......@@ -89,12 +89,15 @@ class RlGamesVecEnvWrapper(IVecEnv):
clip_actions: The clipping value for actions.
Raises:
ValueError: The environment is not inherited from :class:`ManagerBasedRLEnv`.
ValueError: The environment is not inherited from :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`.
ValueError: If specified, the privileged observations (critic) are not of type :obj:`gym.spaces.Box`.
"""
# check that input is valid
if not isinstance(env.unwrapped, ManagerBasedRLEnv) and not isinstance(env.unwrapped, DirectRLEnv):
raise ValueError(f"The environment must be inherited from ManagerBasedRLEnv. Environment type: {type(env)}")
raise ValueError(
"The environment must be inherited from ManagerBasedRLEnv or DirectRLEnv. Environment type:"
f" {type(env)}"
)
# initialize the wrapper
self.env = env
# store provided arguments
......@@ -168,7 +171,7 @@ class RlGamesVecEnvWrapper(IVecEnv):
return cls.__name__
@property
def unwrapped(self) -> ManagerBasedRLEnv:
def unwrapped(self) -> ManagerBasedRLEnv | DirectRLEnv:
"""Returns the base environment of the wrapper.
This will be the bare :class:`gymnasium.Env` environment, underneath all layers of wrappers.
......
......@@ -3,7 +3,7 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""Wrapper to configure an :class:`ManagerBasedRLEnv` instance to RSL-RL vectorized environment.
"""Wrapper to configure a :class:`ManagerBasedRLEnv` or :class:`DirectRlEnv` instance to RSL-RL vectorized environment.
The following example shows how to wrap an environment for RSL-RL:
......@@ -43,7 +43,7 @@ class RslRlVecEnvWrapper(VecEnv):
https://github.com/leggedrobotics/rsl_rl/blob/master/rsl_rl/env/vec_env.py
"""
def __init__(self, env: ManagerBasedRLEnv):
def __init__(self, env: ManagerBasedRLEnv | DirectRLEnv):
"""Initializes the wrapper.
Note:
......@@ -53,7 +53,7 @@ class RslRlVecEnvWrapper(VecEnv):
env: The environment to wrap around.
Raises:
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv`.
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`.
"""
# check that input is valid
if not isinstance(env.unwrapped, ManagerBasedRLEnv) and not isinstance(env.unwrapped, DirectRLEnv):
......@@ -126,7 +126,7 @@ class RslRlVecEnvWrapper(VecEnv):
return cls.__name__
@property
def unwrapped(self) -> ManagerBasedRLEnv:
def unwrapped(self) -> ManagerBasedRLEnv | DirectRLEnv:
"""Returns the base environment of the wrapper.
This will be the bare :class:`gymnasium.Env` environment, underneath all layers of wrappers.
......
......@@ -3,7 +3,7 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""Wrapper to configure an :class:`ManagerBasedRLEnv` instance to Stable-Baselines3 vectorized environment.
"""Wrapper to configure a :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv` instance to Stable-Baselines3 vectorized environment.
The following example shows how to wrap an environment for Stable-Baselines3:
......@@ -85,7 +85,7 @@ class Sb3VecEnvWrapper(VecEnv):
still considered a single environment instance, Stable Baselines tries to wrap
around it using the :class:`DummyVecEnv`. This is only done if the environment
is not inheriting from their :class:`VecEnv`. Thus, this class thinly wraps
over the environment from :class:`ManagerBasedRLEnv`.
over the environment from :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`.
Note:
While Stable-Baselines3 supports Gym 0.26+ API, their vectorized environment
......@@ -123,14 +123,14 @@ class Sb3VecEnvWrapper(VecEnv):
"""
def __init__(self, env: ManagerBasedRLEnv):
def __init__(self, env: ManagerBasedRLEnv | DirectRLEnv):
"""Initialize the wrapper.
Args:
env: The environment to wrap around.
Raises:
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv`.
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`.
"""
# check that input is valid
if not isinstance(env.unwrapped, ManagerBasedRLEnv) and not isinstance(env.unwrapped, DirectRLEnv):
......@@ -177,7 +177,7 @@ class Sb3VecEnvWrapper(VecEnv):
return cls.__name__
@property
def unwrapped(self) -> ManagerBasedRLEnv:
def unwrapped(self) -> ManagerBasedRLEnv | DirectRLEnv:
"""Returns the base environment of the wrapper.
This will be the bare :class:`gymnasium.Env` environment, underneath all layers of wrappers.
......
......@@ -3,7 +3,7 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""Wrapper to configure an :class:`ManagerBasedRLEnv` instance to skrl environment.
"""Wrapper to configure a :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv` instance to skrl environment.
The following example shows how to wrap an environment for skrl:
......@@ -100,10 +100,12 @@ Vectorized environment wrapper.
"""
def SkrlVecEnvWrapper(env: ManagerBasedRLEnv, ml_framework: Literal["torch", "jax", "jax-numpy"] = "torch"):
def SkrlVecEnvWrapper(
env: ManagerBasedRLEnv | DirectRLEnv, ml_framework: Literal["torch", "jax", "jax-numpy"] = "torch"
):
"""Wraps around Isaac Lab environment for skrl.
This function wraps around the Isaac Lab environment. Since the :class:`ManagerBasedRLEnv` environment
This function wraps around the Isaac Lab environment. Since the :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv` environment
wrapping functionality is defined within the skrl library itself, this implementation
is maintained for compatibility with the structure of the extension that contains it.
Internally it calls the :func:`wrap_env` from the skrl library API.
......@@ -113,7 +115,7 @@ def SkrlVecEnvWrapper(env: ManagerBasedRLEnv, ml_framework: Literal["torch", "ja
ml_framework: The ML framework to use for the wrapper. Defaults to "torch".
Raises:
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv`.
ValueError: When the environment is not an instance of :class:`ManagerBasedRLEnv` or :class:`DirectRLEnv`.
ValueError: If the specified ML framework is not valid.
Reference:
......
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