Unverified Commit 79fec2ff authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Fixes tutorials on rigid objects and articulations (#280)

# Description

This MR simplifies the current tutorials on rigid objects and
articulations. It introduces the rigid object first as that is a simpler
example. The articulation builds on top of that tutorial to explain the
joints-related details.

## Type of change

- This change requires a documentation update

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./orbit.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
parent dc2281a7
.. _actuators-guide:
.. _feature-actuators:
Actuators
=========
......
.. _how-to-create-articulation-config:
Creating an Articulation
========================
......@@ -127,7 +129,7 @@ Defining the Cartpole's Actuators
The cartpole articulation has two actuators, one corresponding to each joint
``cart_to_pole`` and ``slider_to_cart``. for more details on actuators, see
:ref:`actuators-guide`.
:ref:`feature-actuators`.
.. literalinclude:: ../../../../source/extensions/omni.isaac.orbit/omni/isaac/orbit/assets/config/cartpole.py
:language: python
......
Create an empty scene
=====================
Creating an empty scene
=======================
.. currentmodule:: omni.isaac.orbit
......
.. _how_to_spawn_objects_label:
.. _tutorial-spawn-prims:
Spawn prims into the scene
==========================
Spawning prims into the scene
=============================
.. currentmodule:: omni.isaac.orbit
......@@ -16,11 +17,12 @@ The Code
The tutorial corresponds to the ``prims.py`` script in the ``orbit/source/standalone/tutorials/00_sim`` directory.
Let's take a look at the Python script:
.. dropdown:: :fa:`eye,mr-1` Code for `prims.py`
.. dropdown:: Code for prims.py
:icon: code
.. literalinclude:: ../../../../source/standalone/tutorials/00_sim/prims.py
:language: python
:emphasize-lines: 43-79
:emphasize-lines: 43-82, 95
:linenos:
......@@ -135,7 +137,7 @@ default to the default values set by USD Physics.
.. literalinclude:: ../../../../source/standalone/tutorials/00_sim/prims.py
:language: python
:lines: 67-76
:lines: 67-78
:linenos:
:lineno-start: 67
......@@ -149,9 +151,9 @@ All of this information is stored in its USD file.
.. literalinclude:: ../../../../source/standalone/tutorials/00_sim/prims.py
:language: python
:lines: 78-80
:lines: 80-82
:linenos:
:lineno-start: 78
:lineno-start: 80
The table above is added as a reference to the scene. In layman terms, this means that the table is not actually added
to the scene, but a `pointer` to the table asset is added. This allows us to modify the table asset and have the changes
......
.. _how_to_articulation_label:
.. _tutorial-interact-articulation:
Interacting with an articulation
================================
In the previous tutorial, we explained the essential workings of the standalone script and how to
play the simulator. This tutorial shows how to add a robotic arm to the stage and control the
arm by providing random joint commands.
.. currentmodule:: omni.isaac.orbit
The tutorial will cover how to use the robot classes provided in Orbit. This includes spawning,
initializing, resetting, and controlling the robot.
This tutorial shows how to interact with an articulated robot in the simulation. It is a continuation of the
:ref:`tutorial-interact-rigid-object` tutorial, where we learned how to interact with a rigid object.
On top of setting the root state, we will see how to set the joint state and apply commands to the articulated
robot.
The Code
~~~~~~~~
The tutorial corresponds to the ``arms.py`` script in the ``orbit/source/standalone/demos`` directory.
The tutorial corresponds to the ``articulation.py`` script in the ``orbit/source/standalone/tutorials/01_assets``
directory.
.. dropdown:: :fa:`eye,mr-1` Code for `arms.py`
.. dropdown:: Code for articulation.py
:icon: code
.. literalinclude:: ../../../../source/standalone/demos/arms.py
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/articulation.py
:language: python
:emphasize-lines: 74-85,92-95,105-120,121-125,126-135,146-147
:emphasize-lines: 58-69, 92-103, 103-105, 111, 125-127, 133
:linenos:
The Code Explained
~~~~~~~~~~~~~~~~~~
The scene is designed similarly to the previous tutorial.
Here, we add two articulated robots to the scene and apply some actions to them. You can choose from franka_panda or ur10 robots.
In the following, we will detail the ``add_robots`` function, which is responsible for adding the robots to the scene, and the ``run_simulator`` function, which steps the simulator, applies some actions to the robot, and handles their reset.
Adding the robots
-----------------
We create an instance of the single-arm robot class using a pre-defined configuration object in `Orbit`. This object contains information on the associated USD file, default initial state, actuator models for different joints, and other meta information about the robot's kinematics.
All pre-defined config files can be found in `orbit/source/extensions/omni.isaac.orbit/omni/isaac/orbit/assets/config`.
Single arm manipulators refer to robotic arms with a fixed base. These robots are at the ground height, i.e., `z=0`. Similar to previous objects, a spawn function is defined in each configuration, which is used to place the robot in the scene. Again, we provide the prim path, the spawn configuration, and the translation to the spawn function.
Designing the scene
-------------------
.. literalinclude:: ../../../../source/standalone/demos/arms.py
:language: python
:lines: 74-83
:linenos:
:lineno-start: 74
As we want to articulate the robot and enable it to move, we need to model it as a combination of fixed and articulated joints. While the articulated joints are defined in the configuration, we want to initialize their physics handles. In `Orbit`, we provide this behavior in the form of an :class:`Articulation` class that allows us to set actions to the articulated joints and retrieve the robot's current state.
Here, multiple prims can be grouped by specifying their paths as regex patterns.
Similar to the previous tutorial, we populate the scene with a ground plane and a distant light. Instead of
spawning rigid objects, we now spawn a cart-pole articulation from its USD file. The cart-pole is a simple robot
consisting of a cart and a pole attached to it. The cart is free to move along the x-axis, and the pole is free to
rotate about the cart. The USD file for the cart-pole contains the robot's geometry, joints, and other physical
properties.
.. literalinclude:: ../../../../source/standalone/demos/arms.py
:language: python
:lines: 84-85
:linenos:
:lineno-start: 84
For the cart-pole, we use its pre-defined configuration object, which is an instance of the
:class:`assets.ArticulationCfg` class. This class contains information about the articulation's spawning strategy,
default initial state, actuator models for different joints, and other meta-information. A deeper-dive into how to
create this configuration object is provided in the :ref:`how-to-create-articulation-config` tutorial.
Please note that the physics handles are initialized when the simulation is played. Thus, we must call ``sim.reset()`` before accessing the physics handles.
As seen in the previous tutorial, we can spawn the articulation into the scene in a similar fashion by creating
an instance of the :class:`assets.Articulation` class by passing the configuration object to its constructor.
.. literalinclude:: ../../../../source/standalone/demos/arms.py
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/articulation.py
:language: python
:lines: 146-147
:lines: 58-69
:linenos:
:lineno-start: 146
:lineno-start: 58
Running the simulation loop
---------------------------
In this tutorial, we step the simulation, apply some actions to the robot, and reset the robot at regular intervals.
Continuing from the previous tutorial, we reset the simulation at regular intervals, set commands to the articulation,
step the simulation, and update the articulation's internal buffers.
At first, we generate a joint position target that should be achieved. Every articulation class contains a :class:`ArticulationData` object that contains the current state of the robot. This object can be used to retrieve the current state of the robot as well as some default values. Here, we use it to get the default joint positions and add a slight random random offset to get a target position.
Resetting the simulation
""""""""""""""""""""""""
.. literalinclude:: ../../../../source/standalone/demos/arms.py
Similar to a rigid object, an articulation also has a root state. This state corresponds to the root body in the
articulation tree. On top of the root state, an articulation also has joint states. These states correspond to the
joint positions and velocities.
To reset the articulation, we first set the root state by calling the :meth:`Articulation.write_root_state_to_sim`
method. Similarly, we set the joint states by calling the :meth:`Articulation.write_joint_state_to_sim` method.
Finally, we call the :meth:`Articulation.reset` method to reset any internal buffers and caches.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/articulation.py
:language: python
:lines: 92-95
:lines: 92-103
:linenos:
:lineno-start: 92
As long as the simulation runs, we reset the robot regularly.
We first acquire the default joint position and velocity from the data buffer to perform the reset. By calling the :meth:`Articulation.write_joint_state_to_sim` method, we directly write these values into the PhysX buffer. Then, we call :meth:`Articulation.reset` to reset the robot to its default state.
Following the reset, a new target position of the robot and, if present, the gripper is generated.
Stepping the simulation
"""""""""""""""""""""""
.. literalinclude:: ../../../../source/standalone/demos/arms.py
:language: python
:lines: 105-120
:linenos:
:lineno-start: 105
Applying commands to the articulation involves two steps:
1. *Setting the joint targets*: This sets the desired joint position, velocity, or effort targets for the articulation.
2. *Writing the data to the simulation*: Based on the articulation's configuration, this step handles any
:ref:`actuation conversions <feature-actuators>` and writes the converted values to the PhysX buffer.
If a gripper is present, we toggle the command regularly to simulate a grasp and release action.
In this tutorial, we control the articulation using joint effort commands. For this to work, we need to set the
articulation's stiffness and damping parameters to zero. This is done a-priori inside the cart-pole's pre-defined
configuration object.
.. literalinclude:: ../../../../source/standalone/demos/arms.py
At every step, we randomly sample joint efforts and set them to the articulation by calling the
:meth:`Articulation.set_joint_effort_target` method. After setting the targets, we call the
:meth:`Articulation.write_data_to_sim` method to write the data to the PhysX buffer. Finally, we step
the simulation.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/articulation.py
:language: python
:lines: 121-125
:lines: 108-112
:linenos:
:lineno-start: 121
:lineno-start: 108
Updating the state
""""""""""""""""""
The joint position target is set to the Articulation object by calling the :meth:`Articulation.set_joint_target` method. Similar methods exist to set velocity and effort targets depending on the use case. Afterward, the values are again written into the PhysX buffer before the simulation is stepped. Finally, we update the articulation object's internal buffers to reflect the robot's new state.
Every articulation class contains a :class:`assets.ArticulationData` object. This stores the state of the
articulation. To update the state inside the buffer, we call the :meth:`assets.Articulation.update` method.
.. literalinclude:: ../../../../source/standalone/demos/arms.py
:language: python
:lines: 126-135
:lines: 116-117
:linenos:
:lineno-start: 126
:lineno-start: 116
The Code Execution
~~~~~~~~~~~~~~~~~~
Now that we have gone through the code let's run the script and see the result:
To run the code and see the results, let's run the script from the terminal:
.. code-block:: bash
./orbit.sh -p source/standalone/demos/arms.py --robot franka_panda
./orbit.sh -p source/standalone/tutorials/01_assets/articulation.py
This command should open a stage with a ground plane, lights, and robots.
The simulation should play with the robot arms going to random joint configurations. The
gripper, if present, should be opening or closing at regular intervals. To stop the simulation,
you can either close the window, press the ``STOP`` button in the UI, or press ``Ctrl+C``
in the terminal
This command should open a stage with a ground plane, lights, and two cart-poles that are moving around randomly.
To stop the simulation, you can either close the window, press the ``STOP`` button in the UI, or press ``Ctrl+C``
in the terminal.
In addition to the demo script for playing single-arm manipulators, we also provide a script
for spawning a few quadrupeds:
In this tutorial, we learned how to create and interact with a simple articulation. We saw how to set the state
of an articulation (its root and joint state) and how to apply commands to it. We also saw how to update its
buffers to read the latest state from the simulation.
In addition to this tutorial, we also provide a few other scripts that spawn different robots.These are included
in the ``orbit/source/standalone/demos`` directory. You can run these scripts as:
.. code-block:: bash
# Quadruped -- Spawns ANYmal C, ANYmal B, Unitree A1 on one stage
./orbit.sh -p source/standalone/demos/quadrupeds.py
# Spawn many different single-arm manipulators
./orbit.sh -p source/standalone/demos/arms.py
In this tutorial, we saw how to spawn a robot multiple times and wrap it in an Articulation class to initialize all physics handles, and that lets us control the robot. We also saw how to reset the robot and set joint targets.
# Spawn many different quadrupeds
./orbit.sh -p source/standalone/demos/quadrupeds.py
.. _how_to_rigid_objects_label:
.. _tutorial-interact-rigid-object:
Interacting with a rigid object
===============================
In the previous tutorial, we explained the essential workings of the standalone script, how to
play the simulator and add the first robot to the scene.
This tutorial demonstrates how to wrap objects in the :class:`RigidObject` class and how to control their position and velocity by directly interacting with the physx buffers.
.. currentmodule:: omni.isaac.orbit
In the previous tutorials, we learned the essential workings of the standalone script and how to
spawn different objects (or *prims*) into the simulation. This tutorial shows how to create and interact
with a rigid object. For this, we will use the :class:`assets.RigidObject` class provided in Orbit.
The Code
~~~~~~~~
The tutorial corresponds to the ``rigid_object.py`` script in the ``orbit/source/standalone/tutorials/01_assets`` directory.
.. dropdown:: :fa:`eye,mr-1` Code for `rigid_object.py`
.. dropdown:: Code for rigid_object.py
:icon: code
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:emphasize-lines: 64-74, 76-79, 130-131, 92-110, 111-119
:emphasize-lines: 59-78, 80-82, 102-112, 115-118, 122-123, 136-138, 143-144
:linenos:
The Code Explained
~~~~~~~~~~~~~~~~~~
The scene is designed similarly to the previous tutorial. In the following, we will discuss in detail the ``add_rigid_objects`` function, which is responsible for adding the objects to the scene, and the ``run_simulator`` function, which steps the simulator and changes their position and orientation.
In this script, we split the ``main`` function into two separate functions, which highlight the two main
steps of setting up any simulation in the simulator:
Adding the rigid_objects
------------------------
1. **Design scene**: As the name suggests, this part is responsible for adding all the prims to the scene.
2. **Run simulation**: This part is responsible for stepping the simulator, interacting with the prims
in the scene, e.g., changing their poses, and applying any commands to them.
The objects are included in the Nucleus Server of Omniverse. At first, we define their path and then spawn them into the scene. This procedure is discussed in the :ref:`Spawn Objects <_how_to_spawn_objects_label>` how-to guide.
A distinction between these two steps is necessary because the second step only happens after the first step
is complete and the simulator is reset. Once the simulator is reset (which automatically plays the simulation),
no new (physics-enabled) prims should be added to the scene as it may lead to unexpected behaviors. However,
the prims can be interacted with through their respective handles.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 64-74
:linenos:
:lineno-start: 64
In addition, we now wrap the spawned objects as objects of the :class:`RigidObject` class. This class is a wrapper around the PhysX rigid-body view and allows us to access the physics handles of the object directly. The class also provides methods to set the object's pose and velocity and retrieve the current state.
Designing the scene
-------------------
Similar to the previous tutorial, we populate the scene with a ground plane and a light source. In addition,
we add a rigid object to the scene using the :class:`assets.RigidObject` class. This class is responsible for
spawning the prims at the input path and initializes their corresponding rigid body physics handles.
In this tutorial, we create a conical rigid object using the spawn configuration similar to the rigid cone
in the :ref:`Spawn Objects <tutorial-spawn-prims>` tutorial. The only difference is that now we wrap
the spawning configuration into the :class:`assets.RigidObjectCfg` class. This class contains information about
the asset's spawning strategy, default initial state, and other meta-information. When this class is passed to
the :class:`assets.RigidObject` class, it spawns the object and initializes the corresponding physics handles
when the simulation is played.
As an example on spawning the rigid object prim multiple times, we create its parent Xform prims,
``/World/Origin{i}``, that correspond to different spawn locations. When the regex expression
``/World/Origin*/Cone`` is passed to the :class:`assets.RigidObject` class, it spawns the rigid object prim at
each of the ``/World/Origin{i}`` locations. For instance, if ``/World/Origin1`` and ``/World/Origin2`` are
present in the scene, the rigid object prims are spawned at the locations ``/World/Origin1/Cone`` and
``/World/Origin2/Cone`` respectively.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 76-79
:lines: 59-78
:linenos:
:lineno-start: 76
:lineno-start: 59
Please note that the physics handles are initialized when the simulation is played. Thus, we must call ``sim.reset()`` before accessing the physics handles.
Since we want to interact with the rigid object, we pass this entity back to the main function. This entity
is then used to interact with the rigid object in the simulation loop. In later tutorials, we will see a more
convenient way to handle multiple scene entities using the :class:`scene.InteractiveScene` class.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 130-131
:lines: 80-82
:linenos:
:lineno-start: 130
:lineno-start: 80
Running the simulation loop
---------------------------
In this tutorial, we step the simulation and change the translation and orientation of the objects at regular intervals. The translation is sampled uniformly from a cylinder surface while the orientation is random. By calling the :meth:`RigidObject.write_root_state_to_sim` method, we directly write these values into the PhysX buffer. Then, we call :meth:`RigidObject.reset` the internal buffers of the objects are reset.
We modify the simulation loop to interact with the rigid object to include three steps -- resetting the
simulation state at fixed intervals, stepping the simulation, and updating the internal buffers of the
rigid object. For the convenience of this tutorial, we extract the rigid object's entity from the scene
dictionary and store it in a variable.
Resetting the simulation state
""""""""""""""""""""""""""""""
To reset the simulation state of the spawned rigid object prims, we need to set their pose and velocity.
Together they define the root state of the spawned rigid objects. It is important to note that this state
is defined in the **simulation world frame**, and not of their parent Xform prim. This is because the physics
engine only understands the world frame and not the parent Xform prim's frame. Thus, we need to transform
desired state of the rigid object prim into the world frame before setting it.
We use the :attr:`assets.RigidObject.data.default_root_state` attribute to get the default root state of the
spawned rigid object prims. This default state can be configured from the :attr:`assets.RigidObjectCfg.init_state`
attribute, which we left as identity in this tutorial. We then randomize the translation of the root state and
set the desired state of the rigid object prim using the :meth:`assets.RigidObject.write_root_state_to_sim` method.
As the name suggests, this method writes the root state of the rigid object prim into the simulation buffer.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 92-110
:lines: 102-112
:linenos:
:lineno-start: 92
:lineno-start: 102
Stepping the simulation
"""""""""""""""""""""""
Before stepping the simulation, we perform the :meth:`assets.RigidObject.write_data_to_sim` method. This method
writes other data, such as external forces, into the simulation buffer. In this tutorial, we do not apply any
external forces to the rigid object, so this method is not necessary. However, it is included for completeness.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 115-116
:linenos:
:lineno-start: 115
Updating the state
""""""""""""""""""
The method :meth:`RigidObject.write_data_to_sim` is included for completeness. It is only necessary when external torques or wrenches are applied to the objects and have to be written into the PhysX buffer. Afterward, we step the simulator and update the articulation object's internal buffers to reflect the robot's new state.
After stepping the simulation, we update the internal buffers of the rigid object prims to reflect their new state
inside the :class:`assets.RigidObject.data` attribute. This is done using the :meth:`assets.RigidObject.update` method.
.. literalinclude:: ../../../../source/standalone/tutorials/01_assets/rigid_object.py
:language: python
:lines: 111-119
:lines: 122-123
:linenos:
:lineno-start: 111
:lineno-start: 122
The Code Execution
~~~~~~~~~~~~~~~~~~
Now that we have gone through the code let's run the script and see the result:
Now that we have gone through the code, let's run the script and see the result:
.. code-block:: bash
./orbit.sh -p source/standalone/tutorials/01_assets/rigid_object.py
This should open a stage with a ground plane, lights, and objects. To stop the simulation,
you can either close the window, or press the ``STOP`` button in the UI, or press ``Ctrl+C``
in the terminal
This should open a stage with a ground plane, lights, and several green cones. The cones must be dropping from
a random height and settling on to the ground. To stop the simulation, you can either close the window, or press
the ``STOP`` button in the UI, or press ``Ctrl+C`` in the terminal
In this how-to guide, we saw how to spawn rigid objects and wrap them in a :class:`RigidObject` class to initialize all physics handles and that lets us control their position and velocity.
This tutorial showed how to spawn rigid objects and wrap them in a :class:`RigidObject` class to initialize their
physics handles which allows setting and obtaining their state. In the next tutorial, we will see how to interact
with an articulated object which is a collection of rigid objects connected by joints.
......@@ -86,7 +86,7 @@ def design_scene() -> tuple[dict, list[list[float]]]:
def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
"""Runs the simulator by applying actions to the robot at every time-step"""
"""Runs the simulation loop."""
# Define simulation stepping
sim_dt = sim.get_physics_dt()
sim_time = 0.0
......
......@@ -93,8 +93,7 @@ def spawn_markers():
def main():
"""Spawns lights in the stage and sets the camera view."""
"""Main function."""
# Load kit helper
sim = SimulationContext(sim_utils.SimulationCfg(dt=0.01, substeps=1))
# Set main camera
......
......@@ -102,7 +102,7 @@ def design_scene() -> tuple[dict, list[list[float]]]:
def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
"""Runs the simulator by applying actions to the robot at every time-step"""
"""Runs the simulation loop."""
# Define simulation stepping
sim_dt = sim.get_physics_dt()
sim_time = 0.0
......
......@@ -3,7 +3,13 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""This script demonstrates how to define and spawn a Cartpole Articulation
"""This script demonstrates how to spawn a cart-pole and interact with it.
.. code-block:: bash
# Usage
./orbit.sh -p source/standalone/tutorials/01_assets/articulation.py
"""
from __future__ import annotations
......@@ -16,9 +22,7 @@ import argparse
from omni.isaac.orbit.app import AppLauncher
# add argparse arguments
parser = argparse.ArgumentParser(
description="This script demonstrates how to define and spawn a Cartpole Articulation."
)
parser = argparse.ArgumentParser(description="This script demonstrates how to spawn and interact with an articulation.")
# append AppLauncher cli args
AppLauncher.add_app_launcher_args(parser)
# parse the arguments
......@@ -28,6 +32,8 @@ args_cli = parser.parse_args()
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app
"""Rest everything follows."""
import torch
import traceback
......@@ -46,17 +52,21 @@ def design_scene() -> tuple[dict, list[list[float]]]:
cfg = sim_utils.GroundPlaneCfg()
cfg.func("/World/defaultGroundPlane", cfg)
# Lights
cfg = sim_utils.DistantLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
cfg.func("/World/Light", cfg)
# Create separate groups called "Origin1", "Origin2", "Origin3"
# Each group will have a mount and a robot on top of it
origins = [[0.0, 1.0, 0.0]]
# Origin 1 with Cartpole
prim_utils.create_prim("/World/Origin", "Xform", translation=origins[0])
# -- Articulation
cartpole = Articulation(cfg=CARTPOLE_CFG.replace(prim_path="/World/Robot"))
# Each group will have a robot in it
origins = [[0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]]
# Origin 1
prim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0])
# Origin 2
prim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1])
# Articulation
cartpole_cfg = CARTPOLE_CFG.copy()
cartpole_cfg.prim_path = "/World/Origin.*/Robot"
cartpole = Articulation(cfg=cartpole_cfg)
# return the scene information
scene_entities = {"cartpole": cartpole}
......@@ -64,7 +74,11 @@ def design_scene() -> tuple[dict, list[list[float]]]:
def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor):
"""Runs the simulator by applying actions to the articulation at every time-step"""
"""Runs the simulation loop."""
# Extract scene entities
# note: we only do this here for readability. In general, it is better to access the entities directly from
# the dictionary. This dictionary is replaced by the InteractiveScene class in the next tutorial.
robot = entities["cartpole"]
# Define simulation stepping
sim_dt = sim.get_physics_dt()
count = 0
......@@ -75,43 +89,41 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
# reset counter
count = 0
# reset the scene entities
for index, robot in enumerate(entities.values()):
# root state
# we offset the root state by the origin since the states are written in simulation world frame
# if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
root_state = robot.data.default_root_state.clone()
root_state[:, :3] += origins[index]
robot.write_root_state_to_sim(root_state)
# set joint positions with some noise
joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
joint_pos += torch.rand_like(joint_pos) * 0.1
robot.write_joint_state_to_sim(joint_pos, joint_vel)
# clear internal buffers
robot.reset()
# root state
# we offset the root state by the origin since the states are written in simulation world frame
# if this is not done, then the robots will be spawned at the (0, 0, 0) of the simulation world
root_state = robot.data.default_root_state.clone()
root_state[:, :3] += origins
robot.write_root_state_to_sim(root_state)
# set joint positions with some noise
joint_pos, joint_vel = robot.data.default_joint_pos.clone(), robot.data.default_joint_vel.clone()
joint_pos += torch.rand_like(joint_pos) * 0.1
robot.write_joint_state_to_sim(joint_pos, joint_vel)
# clear internal buffers
robot.reset()
print("[INFO]: Resetting robot state...")
# Apply random action
for robot in entities.values():
# generate random joint efforts
efforts = torch.randn_like(robot.data.joint_pos) * 5.0
# apply action to the robot
robot.set_joint_effort_target(efforts)
# write data to sim
robot.write_data_to_sim()
# -- generate random joint efforts
efforts = torch.randn_like(robot.data.joint_pos) * 5.0
# -- apply action to the robot
robot.set_joint_effort_target(efforts)
# -- write data to sim
robot.write_data_to_sim()
# Perform step
sim.step()
# Increment counter
count += 1
# Update buffers
for robot in entities.values():
robot.update(sim_dt)
robot.update(sim_dt)
def main():
"""Main function."""
# Load kit helper
sim_cfg = sim_utils.SimulationCfg(device="cpu", use_gpu_pipeline=False)
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view([2.5, 1.0, 4.0], [0.0, 1.0, 2.0])
sim.set_camera_view([2.5, 0.0, 4.0], [0.0, 0.0, 2.0])
# Design scene
scene_entities, scene_origins = design_scene()
scene_origins = torch.tensor(scene_origins, device=sim.device)
......
......@@ -39,12 +39,12 @@ import torch
import traceback
import carb
import omni.isaac.core.utils.prims as prim_utils
import omni.isaac.orbit.sim as sim_utils
import omni.isaac.orbit.utils.math as math_utils
from omni.isaac.orbit.assets import RigidObject, RigidObjectCfg
from omni.isaac.orbit.sim import SimulationContext
from omni.isaac.orbit.utils.assets import ISAAC_NUCLEUS_DIR
from omni.isaac.orbit.utils.math import quat_mul, random_yaw_orientation, sample_cylinder
def design_scene():
......@@ -53,35 +53,41 @@ def design_scene():
cfg = sim_utils.GroundPlaneCfg()
cfg.func("/World/defaultGroundPlane", cfg)
# Lights
cfg = sim_utils.DistantLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
cfg = sim_utils.DomeLightCfg(intensity=2000.0, color=(0.8, 0.8, 0.8))
cfg.func("/World/Light", cfg)
# add rigid objects and return them
return add_rigid_objects()
def add_rigid_objects():
"""Adds rigid objects to the scene."""
# add YCB objects
ycb_usd_paths = {
"crackerBox": f"{ISAAC_NUCLEUS_DIR}/Props/YCB/Axis_Aligned_Physics/003_cracker_box.usd",
"sugarBox": f"{ISAAC_NUCLEUS_DIR}/Props/YCB/Axis_Aligned_Physics/004_sugar_box.usd",
"tomatoSoupCan": f"{ISAAC_NUCLEUS_DIR}/Props/YCB/Axis_Aligned_Physics/005_tomato_soup_can.usd",
"mustardBottle": f"{ISAAC_NUCLEUS_DIR}/Props/YCB/Axis_Aligned_Physics/006_mustard_bottle.usd",
}
for key, usd_path in ycb_usd_paths.items():
translation = torch.rand(3).tolist()
cfg = sim_utils.UsdFileCfg(usd_path=usd_path)
cfg.func(f"/World/Objects/{key}", cfg, translation=translation)
# Setup rigid object
cfg = RigidObjectCfg(prim_path="/World/Objects/.*")
# Create rigid object handler
rigid_object = RigidObject(cfg)
return rigid_object
def run_simulator(sim: sim_utils.SimulationContext, rigid_object: RigidObject):
# Create separate groups called "Origin1", "Origin2", "Origin3"
# Each group will have a robot in it
origins = [[0.25, 0.25, 0.0], [-0.25, 0.25, 0.0], [0.25, -0.25, 0.0], [-0.25, -0.25, 0.0]]
for i, origin in enumerate(origins):
prim_utils.create_prim(f"/World/Origin{i}", "Xform", translation=origin)
# Rigid Object
cone_cfg = RigidObjectCfg(
prim_path="/World/Origin.*/Cone",
spawn=sim_utils.ConeCfg(
radius=0.1,
height=0.2,
rigid_props=sim_utils.RigidBodyPropertiesCfg(),
mass_props=sim_utils.MassPropertiesCfg(mass=1.0),
collision_props=sim_utils.CollisionPropertiesCfg(),
visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2),
),
init_state=RigidObjectCfg.InitialStateCfg(),
)
cone_object = RigidObject(cfg=cone_cfg)
# return the scene information
scene_entities = {"cone": cone_object}
return scene_entities, origins
def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, RigidObject], origins: torch.Tensor):
"""Runs the simulation loop."""
# Extract scene entities
# note: we only do this here for readability. In general, it is better to access the entities directly from
# the dictionary. This dictionary is replaced by the InteractiveScene class in the next tutorial.
cone_object = entities["cone"]
# Define simulation stepping
sim_dt = sim.get_physics_dt()
sim_time = 0.0
......@@ -94,45 +100,48 @@ def run_simulator(sim: sim_utils.SimulationContext, rigid_object: RigidObject):
sim_time = 0.0
count = 0
# reset root state
root_state = rigid_object.data.default_root_state.clone()
# -- position
root_state[:, :3] = sample_cylinder(
radius=0.5, h_range=(0.15, 0.25), size=rigid_object.num_instances, device=rigid_object.device
)
# -- orientation: apply yaw rotation
root_state[:, 3:7] = quat_mul(
random_yaw_orientation(rigid_object.num_instances, rigid_object.device), root_state[:, 3:7]
root_state = cone_object.data.default_root_state.clone()
# sample a random position on a cylinder around the origins
root_state[:, :3] += origins
root_state[:, :3] += math_utils.sample_cylinder(
radius=0.1, h_range=(0.25, 0.5), size=cone_object.num_instances, device=cone_object.device
)
# -- set root state
rigid_object.write_root_state_to_sim(root_state)
# write root state to simulation
cone_object.write_root_state_to_sim(root_state)
# reset buffers
rigid_object.reset()
print(">>>>>>>> Reset!")
cone_object.reset()
print("----------------------------------------")
print("[INFO]: Resetting object state...")
# apply sim data
rigid_object.write_data_to_sim()
cone_object.write_data_to_sim()
# perform step
sim.step()
# update sim-time
sim_time += sim_dt
count += 1
# update buffers
rigid_object.update(sim_dt)
cone_object.update(sim_dt)
# print the root position
if count % 50 == 0:
print(f"Root position (in world): {cone_object.data.root_state_w[:, :3]}")
def main():
"""Main function."""
# Load kit helper
sim = SimulationContext(sim_utils.SimulationCfg())
sim_cfg = sim_utils.SimulationCfg()
sim = SimulationContext(sim_cfg)
# Set main camera
sim.set_camera_view(eye=[1.5, 1.5, 1.5], target=[0.0, 0.0, 0.0])
# design scene
rigid_object = design_scene()
sim.set_camera_view(eye=[1.5, 0.0, 1.0], target=[0.0, 0.0, 0.0])
# Design scene
scene_entities, scene_origins = design_scene()
scene_origins = torch.tensor(scene_origins, device=sim.device)
# Play the simulator
sim.reset()
# Now we are ready!
print("[INFO]: Setup complete...")
# Run the simulator
run_simulator(sim, rigid_object)
run_simulator(sim, scene_entities, scene_origins)
if __name__ == "__main__":
......
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