Unverified Commit c2b32665 authored by lgulich's avatar lgulich Committed by GitHub

Adds check that no articulation root API is applied on rigid bodies (#1358)

# Description

If a USD contains an articulation root but we load it with a
`RigidObjectCfg` this would also load the articulation. This is
confusing as a user only expects a rigid body.

This PR adds a fix to ignore the articulation if a rigid body is
spawned.

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link: https://isaac-sim.github.io/IsaacLab/source/refs/contributing.html
-->

Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context.
List any dependencies that are required for this change.

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

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

- Bug fix (non-breaking change which fixes an issue)

## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] 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
-->

---------
Signed-off-by: 's avatarlgulich <22480644+lgulich@users.noreply.github.com>
Signed-off-by: 's avatarDavid Hoeller <dhoeller@nvidia.com>
Signed-off-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarDavid Hoeller <dhoeller@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
parent 5a3ade2b
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.27.21" version = "0.27.22"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.27.22 (2024-12-06)
~~~~~~~~~~~~~~~~~~~~
Changed
^^^^^^^
* If a USD that contains an articulation root is loaded using a
:attr:`omni.isaac_lab.assets.RigidBody` we now fail unless the articulation root is explicitly
disabled. Using an articulation root for rigid bodies is not needed and decreases overall performance.
0.27.21 (2024-12-06) 0.27.21 (2024-12-06)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
......
...@@ -291,6 +291,18 @@ class RigidObject(AssetBase): ...@@ -291,6 +291,18 @@ class RigidObject(AssetBase):
" Please ensure that there is only one rigid body in the prim path tree." " Please ensure that there is only one rigid body in the prim path tree."
) )
articulation_prims = sim_utils.get_all_matching_child_prims(
template_prim_path, predicate=lambda prim: prim.HasAPI(UsdPhysics.ArticulationRootAPI)
)
if len(articulation_prims) != 0:
if articulation_prims[0].GetAttribute("physxArticulation:articulationEnabled").Get():
raise RuntimeError(
f"Found an articulation root when resolving '{self.cfg.prim_path}' for rigid objects. These are"
f" located at: '{articulation_prims}' under '{template_prim_path}'. Please disable the articulation"
" root in the USD or from code by setting the parameter"
" 'ArticulationRootPropertiesCfg.articulation_enabled' to False in the spawn configuration."
)
# resolve root prim back into regex expression # resolve root prim back into regex expression
root_prim_path = root_prims[0].GetPath().pathString root_prim_path = root_prims[0].GetPath().pathString
root_prim_path_expr = self.cfg.prim_path + root_prim_path[len(template_prim_path) :] root_prim_path_expr = self.cfg.prim_path + root_prim_path[len(template_prim_path) :]
......
...@@ -24,6 +24,7 @@ simulation_app = app_launcher.app ...@@ -24,6 +24,7 @@ simulation_app = app_launcher.app
import ctypes import ctypes
import torch import torch
import unittest import unittest
from typing import Literal
import omni.isaac.core.utils.prims as prim_utils import omni.isaac.core.utils.prims as prim_utils
...@@ -31,19 +32,23 @@ import omni.isaac.lab.sim as sim_utils ...@@ -31,19 +32,23 @@ import omni.isaac.lab.sim as sim_utils
from omni.isaac.lab.assets import RigidObject, RigidObjectCfg from omni.isaac.lab.assets import RigidObject, RigidObjectCfg
from omni.isaac.lab.sim import build_simulation_context from omni.isaac.lab.sim import build_simulation_context
from omni.isaac.lab.sim.spawners import materials from omni.isaac.lab.sim.spawners import materials
from omni.isaac.lab.utils.assets import ISAAC_NUCLEUS_DIR from omni.isaac.lab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR
from omni.isaac.lab.utils.math import default_orientation, random_orientation from omni.isaac.lab.utils.math import default_orientation, random_orientation
def generate_cubes_scene( def generate_cubes_scene(
num_cubes: int = 1, height=1.0, has_api: bool = True, kinematic_enabled: bool = False, device: str = "cuda:0" num_cubes: int = 1,
height=1.0,
api: Literal["none", "rigid_body", "articulation_root"] = "rigid_body",
kinematic_enabled: bool = False,
device: str = "cuda:0",
) -> tuple[RigidObject, torch.Tensor]: ) -> tuple[RigidObject, torch.Tensor]:
"""Generate a scene with the provided number of cubes. """Generate a scene with the provided number of cubes.
Args: Args:
num_cubes: Number of cubes to generate. num_cubes: Number of cubes to generate.
height: Height of the cubes. height: Height of the cubes.
has_api: Whether the cubes have a rigid body API on them. api: The type of API that the cubes should have.
kinematic_enabled: Whether the cubes are kinematic. kinematic_enabled: Whether the cubes are kinematic.
device: Device to use for the simulation. device: Device to use for the simulation.
...@@ -57,17 +62,25 @@ def generate_cubes_scene( ...@@ -57,17 +62,25 @@ def generate_cubes_scene(
prim_utils.create_prim(f"/World/Table_{i}", "Xform", translation=origin) prim_utils.create_prim(f"/World/Table_{i}", "Xform", translation=origin)
# Resolve spawn configuration # Resolve spawn configuration
if has_api: if api == "none":
spawn_cfg = sim_utils.UsdFileCfg(
usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
rigid_props=sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=kinematic_enabled),
)
else:
# since no rigid body properties defined, this is just a static collider # since no rigid body properties defined, this is just a static collider
spawn_cfg = sim_utils.CuboidCfg( spawn_cfg = sim_utils.CuboidCfg(
size=(0.1, 0.1, 0.1), size=(0.1, 0.1, 0.1),
collision_props=sim_utils.CollisionPropertiesCfg(), collision_props=sim_utils.CollisionPropertiesCfg(),
) )
elif api == "rigid_body":
spawn_cfg = sim_utils.UsdFileCfg(
usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
rigid_props=sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=kinematic_enabled),
)
elif api == "articulation_root":
spawn_cfg = sim_utils.UsdFileCfg(
usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Tests/RigidObject/Cube/dex_cube_instanceable_with_articulation_root.usd",
rigid_props=sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=kinematic_enabled),
)
else:
raise ValueError(f"Unknown api: {api}")
# Create rigid object # Create rigid object
cube_object_cfg = RigidObjectCfg( cube_object_cfg = RigidObjectCfg(
prim_path="/World/Table_.*/Object", prim_path="/World/Table_.*/Object",
...@@ -161,7 +174,27 @@ class TestRigidObject(unittest.TestCase): ...@@ -161,7 +174,27 @@ class TestRigidObject(unittest.TestCase):
with self.subTest(num_cubes=num_cubes, device=device): with self.subTest(num_cubes=num_cubes, device=device):
with build_simulation_context(device=device, auto_add_lighting=True) as sim: with build_simulation_context(device=device, auto_add_lighting=True) as sim:
# Generate cubes scene # Generate cubes scene
cube_object, _ = generate_cubes_scene(num_cubes=num_cubes, has_api=False, device=device) cube_object, _ = generate_cubes_scene(num_cubes=num_cubes, api="none", device=device)
# Check that boundedness of rigid object is correct
self.assertEqual(ctypes.c_long.from_address(id(cube_object)).value, 1)
# Play sim
sim.reset()
# Check if object is initialized
self.assertFalse(cube_object.is_initialized)
def test_initialization_with_articulation_root(self):
"""Test that initialization fails when an articulation root is found at the provided prim path."""
for num_cubes in (1, 2):
for device in ("cuda:0", "cpu"):
with self.subTest(num_cubes=num_cubes, device=device):
with build_simulation_context(device=device, auto_add_lighting=True) as sim:
# Generate cubes scene
cube_object, _ = generate_cubes_scene(
num_cubes=num_cubes, api="articulation_root", device=device
)
# Check that boundedness of rigid object is correct # Check that boundedness of rigid object is correct
self.assertEqual(ctypes.c_long.from_address(id(cube_object)).value, 1) self.assertEqual(ctypes.c_long.from_address(id(cube_object)).value, 1)
......
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