From da34688b10ba6c70c33be8faaa68583eb8ffde69 Mon Sep 17 00:00:00 2001 From: Mayank Mittal <12863862+Mayankm96@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:34:03 +0200 Subject: [PATCH] Removes the use of body view inside the asset classes (#643) # Description The deprecation notice has been out for a while. The MR removes the body PhysX view from the rigid object and articulation classes. This is no longer needed as their respective root views expose all the data needed for them. This is a breaking change, and we lose compatibility with Isaac Sim 2023.1.1. Fixes #237 ## Type of change - Bug fix (non-breaking change which fixes an issue) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - This change requires a documentation update ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [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 --- docs/source/setup/installation/index.rst | 2 +- .../omni.isaac.lab/config/extension.toml | 2 +- .../omni.isaac.lab/docs/CHANGELOG.rst | 19 +++++ .../lab/assets/articulation/articulation.py | 81 +++---------------- .../lab/assets/rigid_object/rigid_object.py | 46 +++-------- .../test/assets/test_articulation.py | 20 ----- .../test/assets/test_rigid_object.py | 12 ++- 7 files changed, 52 insertions(+), 130 deletions(-) diff --git a/docs/source/setup/installation/index.rst b/docs/source/setup/installation/index.rst index 074fe38598..afe8aebfb4 100644 --- a/docs/source/setup/installation/index.rst +++ b/docs/source/setup/installation/index.rst @@ -19,7 +19,7 @@ Installation Guide .. caution:: - We have dropped support for Isaac Sim versions 2023.1.0 and below. We recommend using the latest + We have dropped support for Isaac Sim versions 2023.1.1 and below. We recommend using the latest Isaac Sim 4.0 release to benefit from the latest features and improvements. For more information, please refer to the diff --git a/source/extensions/omni.isaac.lab/config/extension.toml b/source/extensions/omni.isaac.lab/config/extension.toml index 1b42771082..58d6fd0466 100644 --- a/source/extensions/omni.isaac.lab/config/extension.toml +++ b/source/extensions/omni.isaac.lab/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.18.6" +version = "0.19.0" # Description title = "Isaac Lab framework for Robot Learning" diff --git a/source/extensions/omni.isaac.lab/docs/CHANGELOG.rst b/source/extensions/omni.isaac.lab/docs/CHANGELOG.rst index 767ef45496..68abf65f4d 100644 --- a/source/extensions/omni.isaac.lab/docs/CHANGELOG.rst +++ b/source/extensions/omni.isaac.lab/docs/CHANGELOG.rst @@ -1,6 +1,25 @@ Changelog --------- +0.19.0 (2024-07-04) +~~~~~~~~~~~~~~~~~~~ + +Fixed +^^^^^ + +* Fixed parsing of articulations with nested rigid links while using the :class:`omni.isaac.lab.assets.Articulation` + class. Earlier, the class initialization failed when the articulation had nested rigid links since the rigid + links were not being parsed correctly by the PhysX view. + +Removed +^^^^^^^ + +* Removed the attribute :attr:`body_physx_view` from the :class:`omni.isaac.lab.assets.Articulation` and + :class:`omni.isaac.lab.assets.RigidObject` classes. These were causing confusions when used with articulation + view since the body names were not following the same ordering. +* Dropped support for Isaac Sim 2023.1.1. The minimum supported version is now Isaac Sim 4.0.0. + + 0.18.6 (2024-07-01) ~~~~~~~~~~~~~~~~~~~ diff --git a/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/articulation/articulation.py b/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/articulation/articulation.py index e2198f217f..c6e64ac489 100644 --- a/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/articulation/articulation.py +++ b/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/articulation/articulation.py @@ -9,7 +9,6 @@ from __future__ import annotations import torch -import warnings from collections.abc import Sequence from prettytable import PrettyTable from typing import TYPE_CHECKING @@ -156,25 +155,6 @@ def root_physx_view(self) -> physx.ArticulationView: """ return self._root_physx_view - @property - def body_physx_view(self) -> physx.RigidBodyView: - """Rigid body view for the asset (PhysX). - - .. deprecated:: v0.3.0 - - In previous versions, this attribute returned the rigid body view over all the links of the articulation. - However, this led to confusion with the link ordering as they were not ordered in the same way as the - articulation view. - - Therefore, this attribute will be removed in v0.4.0. Please use the :attr:`root_physx_view` attribute - instead. - - """ - dep_msg = "The attribute 'body_physx_view' will be removed in v0.4.0. Please use 'root_physx_view' instead." - warnings.warn(dep_msg, DeprecationWarning) - carb.log_error(dep_msg) - return self._body_physx_view - """ Operations. """ @@ -194,16 +174,8 @@ def write_data_to_sim(self): If any explicit actuators are present, then the actuator models are used to compute the joint commands. Otherwise, the joint commands are directly set into the simulation. """ - # write external wrench - if self.has_external_wrench: - # apply external forces and torques - self._body_physx_view.apply_forces_and_torques_at_position( - force_data=self._external_force_body_view_b.view(-1, 3), - torque_data=self._external_torque_body_view_b.view(-1, 3), - position_data=None, - indices=self._ALL_BODY_INDICES, - is_global=False, - ) + # apply external forces and torques + super().write_data_to_sim() # apply actuator models self._apply_actuator_model() @@ -260,24 +232,6 @@ def find_fixed_tendons( # find tendons return string_utils.resolve_matching_names(name_keys, tendon_subsets, preserve_order) - """ - Operations - Setters. - """ - - def set_external_force_and_torque( - self, - forces: torch.Tensor, - torques: torch.Tensor, - body_ids: Sequence[int] | slice | None = None, - env_ids: Sequence[int] | None = None, - ): - # call parent to set the external forces and torques into buffers - super().set_external_force_and_torque(forces, torques, body_ids, env_ids) - # reordering of the external forces and torques to match the body view ordering - if self.has_external_wrench: - self._external_force_body_view_b = self._external_force_b[:, self._body_view_ordering] - self._external_torque_body_view_b = self._external_torque_b[:, self._body_view_ordering] - """ Operations - Writers. """ @@ -332,6 +286,7 @@ def write_joint_state_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -364,6 +319,7 @@ def write_joint_stiffness_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -394,6 +350,7 @@ def write_joint_damping_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -422,6 +379,7 @@ def write_joint_effort_limit_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # move tensor to cpu if needed @@ -453,6 +411,7 @@ def write_joint_armature_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -480,6 +439,7 @@ def write_joint_friction_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -508,6 +468,7 @@ def write_joint_limits_to_sim( physx_env_ids = self._ALL_INDICES if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set into internal buffers @@ -538,6 +499,7 @@ def set_joint_position_target( env_ids = slice(None) if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set targets @@ -562,6 +524,7 @@ def set_joint_velocity_target( env_ids = slice(None) if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set targets @@ -586,6 +549,7 @@ def set_joint_effort_target( env_ids = slice(None) if joint_ids is None: joint_ids = slice(None) + # broadcast env_ids if needed to allow double indexing if env_ids != slice(None) and joint_ids != slice(None): env_ids = env_ids[:, None] # set targets @@ -821,24 +785,6 @@ def _initialize_impl(self): root_prim_path_expr = self.cfg.prim_path + root_prim_path[len(template_prim_path) :] # -- articulation self._root_physx_view = self._physics_sim_view.create_articulation_view(root_prim_path_expr.replace(".*", "*")) - # -- link views - # note: we use the root view to get the body names, but we use the body view to get the - # actual data. This is mainly needed to apply external forces to the bodies. - physx_body_names = self.root_physx_view.shared_metatype.link_names - body_names_regex = r"(" + "|".join(physx_body_names) + r")" - body_names_regex = f"{self.cfg.prim_path}/{body_names_regex}" - self._body_physx_view = self._physics_sim_view.create_rigid_body_view(body_names_regex.replace(".*", "*")) - - # create ordering from articulation view to body view for body names - # note: we need to do this since the body view is not ordered in the same way as the articulation view - # -- root view - root_view_body_names = self.body_names - # -- body view - prim_paths = self._body_physx_view.prim_paths[: self.num_bodies] - body_view_body_names = [path.split("/")[-1] for path in prim_paths] - # -- mapping from articulation view to body view - self._body_view_ordering = [root_view_body_names.index(name) for name in body_view_body_names] - self._body_view_ordering = torch.tensor(self._body_view_ordering, dtype=torch.long, device=self.device) # log information about the articulation carb.log_info(f"Articulation initialized at: {self.cfg.prim_path} with root '{root_prim_path_expr}'.") @@ -848,9 +794,6 @@ def _initialize_impl(self): carb.log_info(f"Number of joints: {self.num_joints}") carb.log_info(f"Joint names: {self.joint_names}") carb.log_info(f"Number of fixed tendons: {self.num_fixed_tendons}") - # -- assert that parsing was successful - if set(physx_body_names) != set(self.body_names): - raise RuntimeError("Failed to parse all bodies properly in the articulation.") # container for data access self._data = ArticulationData(self.root_physx_view, self.device) diff --git a/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/rigid_object/rigid_object.py b/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/rigid_object/rigid_object.py index 13a87117eb..80a7067503 100644 --- a/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/rigid_object/rigid_object.py +++ b/source/extensions/omni.isaac.lab/omni/isaac/lab/assets/rigid_object/rigid_object.py @@ -6,7 +6,6 @@ from __future__ import annotations import torch -import warnings from collections.abc import Sequence from typing import TYPE_CHECKING @@ -89,20 +88,6 @@ def root_physx_view(self) -> physx.RigidBodyView: """ return self._root_physx_view - @property - def body_physx_view(self) -> physx.RigidBodyView: - """Rigid body view for the asset (PhysX). - - .. deprecated:: v0.3.0 - - The attribute 'body_physx_view' will be removed in v0.4.0. Please use :attr:`root_physx_view` instead. - - """ - dep_msg = "The attribute 'body_physx_view' will be removed in v0.4.0. Please use 'root_physx_view' instead." - warnings.warn(dep_msg, DeprecationWarning) - carb.log_error(dep_msg) - return self.root_physx_view - """ Operations. """ @@ -128,7 +113,7 @@ def write_data_to_sim(self): force_data=self._external_force_b.view(-1, 3), torque_data=self._external_torque_b.view(-1, 3), position_data=None, - indices=self._ALL_BODY_INDICES, + indices=self._ALL_INDICES, is_global=False, ) @@ -253,26 +238,17 @@ def set_external_force_and_torque( # resolve all indices # -- env_ids if env_ids is None: - env_ids = self._ALL_INDICES - elif not isinstance(env_ids, torch.Tensor): - env_ids = torch.tensor(env_ids, dtype=torch.long, device=self.device) + env_ids = slice(None) # -- body_ids if body_ids is None: - body_ids = torch.arange(self.num_bodies, dtype=torch.long, device=self.device) - elif isinstance(body_ids, slice): - body_ids = torch.arange(self.num_bodies, dtype=torch.long, device=self.device)[body_ids] - elif not isinstance(body_ids, torch.Tensor): - body_ids = torch.tensor(body_ids, dtype=torch.long, device=self.device) - - # note: we need to do this complicated indexing since torch doesn't support multi-indexing - # create global body indices from env_ids and env_body_ids - # (env_id * total_bodies_per_env) + body_id - indices = body_ids.repeat(len(env_ids), 1) + env_ids.unsqueeze(1) * self.num_bodies - indices = indices.view(-1) + body_ids = slice(None) + # broadcast env_ids if needed to allow double indexing + if env_ids != slice(None) and body_ids != slice(None): + env_ids = env_ids[:, None] + # set into internal buffers - # note: these are applied in the write_to_sim function - self._external_force_b.flatten(0, 1)[indices] = forces.flatten(0, 1) - self._external_torque_b.flatten(0, 1)[indices] = torques.flatten(0, 1) + self._external_force_b[env_ids, body_ids] = forces + self._external_torque_b[env_ids, body_ids] = torques else: self.has_external_wrench = False @@ -332,9 +308,7 @@ def _create_buffers(self): """Create buffers for storing data.""" # constants self._ALL_INDICES = torch.arange(self.num_instances, dtype=torch.long, device=self.device) - self._ALL_BODY_INDICES = torch.arange( - self.root_physx_view.count * self.num_bodies, dtype=torch.long, device=self.device - ) + # external forces and torques self.has_external_wrench = False self._external_force_b = torch.zeros((self.num_instances, self.num_bodies, 3), device=self.device) diff --git a/source/extensions/omni.isaac.lab/test/assets/test_articulation.py b/source/extensions/omni.isaac.lab/test/assets/test_articulation.py index d03027593e..f96dfc4882 100644 --- a/source/extensions/omni.isaac.lab/test/assets/test_articulation.py +++ b/source/extensions/omni.isaac.lab/test/assets/test_articulation.py @@ -98,10 +98,6 @@ def test_initialization_floating_base_non_root(self): prim_path_body_names = [path.split("/")[-1] for path in robot.root_physx_view.link_paths[0]] self.assertListEqual(prim_path_body_names, robot.body_names) - # Check that the body_physx_view is deprecated - with self.assertWarns(DeprecationWarning): - robot.body_physx_view - # Simulate physics for _ in range(10): # perform rendering @@ -141,10 +137,6 @@ def test_initialization_floating_base(self): prim_path_body_names = [path.split("/")[-1] for path in robot.root_physx_view.link_paths[0]] self.assertListEqual(prim_path_body_names, robot.body_names) - # Check that the body_physx_view is deprecated - with self.assertWarns(DeprecationWarning): - robot.body_physx_view - # Simulate physics for _ in range(10): # perform rendering @@ -184,10 +176,6 @@ def test_initialization_fixed_base(self): prim_path_body_names = [path.split("/")[-1] for path in robot.root_physx_view.link_paths[0]] self.assertListEqual(prim_path_body_names, robot.body_names) - # Check that the body_physx_view is deprecated - with self.assertWarns(DeprecationWarning): - robot.body_physx_view - # Simulate physics for _ in range(10): # perform rendering @@ -242,10 +230,6 @@ def test_initialization_fixed_base_single_joint(self): prim_path_body_names = [path.split("/")[-1] for path in robot.root_physx_view.link_paths[0]] self.assertListEqual(prim_path_body_names, robot.body_names) - # Check that the body_physx_view is deprecated - with self.assertWarns(DeprecationWarning): - robot.body_physx_view - # Simulate physics for _ in range(10): # perform rendering @@ -324,10 +308,6 @@ def test_initialization_floating_base_made_fixed_base(self): prim_path_body_names = [path.split("/")[-1] for path in robot.root_physx_view.link_paths[0]] self.assertListEqual(prim_path_body_names, robot.body_names) - # Check that the body_physx_view is deprecated - with self.assertWarns(DeprecationWarning): - robot.body_physx_view - # Root state should be at the default state robot.write_root_state_to_sim(robot.data.default_root_state.clone()) # Simulate physics diff --git a/source/extensions/omni.isaac.lab/test/assets/test_rigid_object.py b/source/extensions/omni.isaac.lab/test/assets/test_rigid_object.py index 3cd18f03ca..3d079f3e02 100644 --- a/source/extensions/omni.isaac.lab/test/assets/test_rigid_object.py +++ b/source/extensions/omni.isaac.lab/test/assets/test_rigid_object.py @@ -48,8 +48,7 @@ def generate_cubes_scene( device: Device to use for the simulation. Returns: - RigidObject: The rigid object representing the cubes. - origins: The origins of the cubes. + A tuple containing the rigid object representing the cubes and the origins of the cubes. """ origins = torch.tensor([(i * 1.0, 0, height) for i in range(num_cubes)]).to(device) @@ -93,6 +92,7 @@ def test_initialization(self): 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, device=device) # Check that boundedness of rigid object is correct @@ -122,6 +122,7 @@ def test_initialization_with_kinematic_enabled(self): 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, origins = generate_cubes_scene( num_cubes=num_cubes, kinematic_enabled=True, device=device ) @@ -157,6 +158,7 @@ def test_initialization_with_no_rigid_body(self): 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, has_api=False, device=device) # Check that boundedness of rigid object is correct @@ -178,6 +180,7 @@ def test_external_force_on_single_body(self): for num_cubes in (2, 4): for device in ("cuda:0", "cpu"): with self.subTest(num_cubes=num_cubes, device=device): + # Generate cubes scene with build_simulation_context(device=device, add_ground_plane=True, auto_add_lighting=True) as sim: cube_object, origins = generate_cubes_scene(num_cubes=num_cubes, device=device) @@ -239,6 +242,7 @@ def test_set_rigid_object_state(self): # Turn off gravity for this test as we don't want any external forces acting on the object # to ensure state remains static with build_simulation_context(device=device, gravity_enabled=False, auto_add_lighting=True) as sim: + # Generate cubes scene cube_object, _ = generate_cubes_scene(num_cubes=num_cubes, device=device) # Play the simulator @@ -297,6 +301,7 @@ def test_reset_rigid_object(self): for device in ("cuda:0", "cpu"): with self.subTest(num_cubes=num_cubes, device=device): with build_simulation_context(device=device, gravity_enabled=True, auto_add_lighting=True) as sim: + # Generate cubes scene cube_object, _ = generate_cubes_scene(num_cubes=num_cubes, device=device) # Play the simulator @@ -334,7 +339,7 @@ def test_rigid_body_set_material_properties(self): with build_simulation_context( device=device, gravity_enabled=True, add_ground_plane=True, auto_add_lighting=True ) as sim: - # Create rigid object(s) + # Generate cubes scene cube_object, _ = generate_cubes_scene(num_cubes=num_cubes, device=device) # Play sim @@ -369,6 +374,7 @@ def test_rigid_body_no_friction(self): 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, height=0.0, device=device) # Create ground plane with no friction