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
......
......@@ -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,12 +89,11 @@ 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]
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()
......@@ -90,28 +103,27 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula
robot.reset()
print("[INFO]: Resetting robot state...")
# Apply random action
for robot in entities.values():
# generate random joint efforts
# -- generate random joint efforts
efforts = torch.randn_like(robot.data.joint_pos) * 5.0
# apply action to the robot
# -- apply action to the robot
robot.set_joint_effort_target(efforts)
# write data to sim
# -- 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)
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()
# 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)
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
# return the scene information
scene_entities = {"cone": cone_object}
return scene_entities, origins
def run_simulator(sim: sim_utils.SimulationContext, rigid_object: RigidObject):
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