From 47a80035ea4cf89a397714da73e6b4f52895cfca Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Wed, 23 Sep 2026 19:45:07 -0700 Subject: [PATCH 1/2] Move external wrench frame selection into WrenchComposer (#7536) Backport the merged change unchanged to release/3.0.0. (cherry picked from commit 726f045a7dd2052311bf64099b096391468f0292) --- AGENTS.md | 5 + .../changelog.d/wrench-fast-paths.minor.rst | 9 + .../isaaclab/utils/wrench_composer.py | 67 ++++- .../assets/_articulation_iface_test_utils.py | 8 +- ...igid_object_collection_iface_test_utils.py | 8 +- .../assets/_rigid_object_iface_test_utils.py | 12 +- .../test_articulation_ordering_iface.py | 65 +++-- .../test/assets/test_rigid_object_iface.py | 44 ++++ .../test/utils/test_wrench_composer.py | 239 +++++++++++------- .../changelog.d/wrench-fast-paths.rst | 6 + .../assets/articulation/articulation.py | 10 +- .../assets/rigid_object/rigid_object.py | 6 +- .../rigid_object_collection.py | 6 +- .../changelog.d/pr7536-test-reset.skip | 1 + .../changelog.d/wrench-fast-paths.rst | 7 + .../assets/articulation/articulation.py | 20 +- .../isaaclab_ov/isaaclab_ov/assets/kernels.py | 48 ++-- .../assets/rigid_object/rigid_object.py | 13 +- .../rigid_object_collection.py | 13 +- .../test/assets/test_articulation.py | 8 +- .../changelog.d/wrench-fast-paths.rst | 7 + .../assets/articulation/articulation.py | 15 +- .../assets/rigid_object/rigid_object.py | 49 +--- .../rigid_object_collection.py | 14 +- .../benchmark/assets/runtime.py | 4 +- 25 files changed, 441 insertions(+), 243 deletions(-) create mode 100644 source/isaaclab/changelog.d/wrench-fast-paths.minor.rst create mode 100644 source/isaaclab_newton/changelog.d/wrench-fast-paths.rst create mode 100644 source/isaaclab_ov/changelog.d/pr7536-test-reset.skip create mode 100644 source/isaaclab_ov/changelog.d/wrench-fast-paths.rst create mode 100644 source/isaaclab_physx/changelog.d/wrench-fast-paths.rst diff --git a/AGENTS.md b/AGENTS.md index be46a9f6edf..3400069a1f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,11 @@ - Use modern Python type hints, including `X | None` instead of `Optional[X]`. - Use `snake_case` for methods, functions, and CLI arguments. - Keep related public symbols discoverable through consistent prefixes. +- For external wrenches, follow the asset API's `is_global` boolean and `_b`/`_w` buffer naming. Keep + frame conversion decisions in `WrenchComposer` and track pending contributions with plain booleans; + do not introduce frame enums, content bitmasks, or a classification layer. +- Name wrench reads `get_forces_and_torques`, matching the existing add/set methods; avoid a separate + "submission" API or compatibility alias for the unreleased `resolve_submission` method. - Use concrete types for public interfaces where practical. - Use Google-style docstrings for public APIs. - Document SI units for public physical quantities in docstrings using inline `[unit]` notation (e.g. `Particle positions [m], shape [N, 3]`); use `[m or rad, depending on joint type]` where applicable, and skip non-physical fields (indices, counts, flags). diff --git a/source/isaaclab/changelog.d/wrench-fast-paths.minor.rst b/source/isaaclab/changelog.d/wrench-fast-paths.minor.rst new file mode 100644 index 00000000000..7cfcbb3a3b4 --- /dev/null +++ b/source/isaaclab/changelog.d/wrench-fast-paths.minor.rst @@ -0,0 +1,9 @@ +Added +^^^^^ + +* Added :meth:`~isaaclab.utils.wrench_composer.WrenchComposer.get_forces_and_torques`, which returns the + cheapest representation of the buffered external wrench a consumer can accept, along with an + ``is_global`` boolean matching the asset API. Consumers that can apply a world-frame wrench at the + center of mass opt in with the new ``supports_world_at_com`` + constructor argument. Wrenches that are already local-frame, or already global-frame at the center of + mass, are now submitted without composing them through the body poses. diff --git a/source/isaaclab/isaaclab/utils/wrench_composer.py b/source/isaaclab/isaaclab/utils/wrench_composer.py index 83c884c8f3b..1ccfd35e90b 100644 --- a/source/isaaclab/isaaclab/utils/wrench_composer.py +++ b/source/isaaclab/isaaclab/utils/wrench_composer.py @@ -30,7 +30,12 @@ class WrenchComposer: - def __init__(self, asset: BaseArticulation | BaseRigidObject | BaseRigidObjectCollection) -> None: + def __init__( + self, + asset: BaseArticulation | BaseRigidObject | BaseRigidObjectCollection, + *, + supports_world_at_com: bool = False, + ) -> None: """Wrench composer with dual-buffer architecture. This class composes forces and torques applied to rigid bodies. Forces and torques can be @@ -55,6 +60,9 @@ def __init__(self, asset: BaseArticulation | BaseRigidObject | BaseRigidObjectCo Args: asset: Asset to use. + supports_world_at_com: Whether the consumer can apply a world-frame wrench at the body's + center of mass. When False, :meth:`get_forces_and_torques` always returns a body-frame + wrench. Defaults to False. """ self.num_envs = asset.num_instances # Avoid isinstance to prevent circular import issues; check by attribute presence instead. @@ -66,6 +74,11 @@ def __init__(self, asset: BaseArticulation | BaseRigidObject | BaseRigidObjectCo self._asset = asset self._active = False self._dirty = False + self._supports_world_at_com = supports_world_at_com + # Conservative until a full reset; partial resets do not scan the remaining buffers. + self._has_local_wrench = False + self._has_global_wrench = False + self._has_global_positions = False if hasattr(self._asset.data, "body_com_pos_w"): self._get_com_pos_fn = lambda a=self._asset: a.data.body_com_pos_w.warp else: @@ -265,6 +278,11 @@ def add_forces_and_torques_index( self._active = True self._dirty = True + if is_global: + self._has_global_wrench = True + self._has_global_positions = self._has_global_positions or (forces is not None and positions is not None) + else: + self._has_local_wrench = True wp.launch( add_forces_to_dual_buffers_index_kernel(env_ids, body_ids), @@ -328,6 +346,11 @@ def set_forces_and_torques_index( self._active = True self._dirty = True + if is_global: + self._has_global_wrench = True + self._has_global_positions = self._has_global_positions or (forces is not None and positions is not None) + else: + self._has_local_wrench = True wp.launch( set_forces_to_dual_buffers_index_kernel(env_ids, body_ids), @@ -388,6 +411,11 @@ def add_forces_and_torques_mask( self._active = True self._dirty = True + if is_global: + self._has_global_wrench = True + self._has_global_positions = self._has_global_positions or (forces is not None and positions is not None) + else: + self._has_local_wrench = True wp.launch( add_forces_to_dual_buffers_mask, @@ -453,6 +481,11 @@ def set_forces_and_torques_mask( self._active = True self._dirty = True + if is_global: + self._has_global_wrench = True + self._has_global_positions = self._has_global_positions or (forces is not None and positions is not None) + else: + self._has_local_wrench = True wp.launch( set_forces_to_dual_buffers_mask, @@ -493,6 +526,9 @@ def add_raw_buffers_from(self, other: WrenchComposer): self._active = True self._dirty = True + self._has_local_wrench = self._has_local_wrench or other._has_local_wrench + self._has_global_wrench = self._has_global_wrench or other._has_global_wrench + self._has_global_positions = self._has_global_positions or other._has_global_positions wp.launch( add_raw_wrench_buffers, @@ -542,6 +578,32 @@ def compose_to_body_frame(self): ) self._dirty = False + def get_forces_and_torques(self) -> tuple[wp.array, wp.array, bool]: + """Get the buffered forces and torques in a frame the consumer accepts. + + Composition into the body frame reads the body poses, which on some backends forces a + kinematics update. That work is unnecessary when the buffered wrench is already in a frame + the consumer accepts: an all-local wrench is its own body-frame composition, and an + all-global-at-CoM wrench can be used directly by a consumer that accepts a world frame. + + The eligibility state is conservative after a partial :meth:`reset`: it may keep composing + even when the selected reset removed every contribution that made composition necessary. A + full :meth:`reset` restores it. This never changes the resulting wrench, only the cost. + + Returns: + Force [N], torque [N·m], and ``is_global``: True for world-frame vectors, False for + body-frame vectors. Both representations apply the force at the body's CoM. Shapes are + ``(num_envs, num_bodies)`` with dtype ``wp.vec3f``. The arrays are owned by the composer + and stay valid until the next mutating call. + """ + if not self._has_global_wrench: + return self._local_force_b, self._local_torque_b, False + if self._supports_world_at_com and not self._has_local_wrench and not self._has_global_positions: + return self._global_force_at_com_w, self._global_torque_w, True + # The fallback depends on the live body pose, even when the input buffers are unchanged. + self.compose_to_body_frame() + return self._out_force_b, self._out_torque_b, False + def reset( self, env_ids: wp.array | torch.Tensor | Sequence[int] | slice | None = None, @@ -571,6 +633,9 @@ def reset( self._out_torque_b.zero_() self._active = False self._dirty = False + self._has_local_wrench = False + self._has_global_wrench = False + self._has_global_positions = False elif env_mask is not None: wp.launch( reset_wrench_composer_mask, diff --git a/source/isaaclab/test/assets/_articulation_iface_test_utils.py b/source/isaaclab/test/assets/_articulation_iface_test_utils.py index 61ac035bd2b..a3896243ce9 100644 --- a/source/isaaclab/test/assets/_articulation_iface_test_utils.py +++ b/source/isaaclab/test/assets/_articulation_iface_test_utils.py @@ -125,8 +125,8 @@ def create_physx_articulation( data.spatial_tendon_names = spatial_tendon_names # Create wrench composers (pass articulation which has num_instances, num_bodies, device properties) - mock_inst_wrench = WrenchComposer(articulation) - mock_perm_wrench = WrenchComposer(articulation) + mock_inst_wrench = WrenchComposer(articulation, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(articulation, supports_world_at_com=True) object.__setattr__(articulation, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(articulation, "_permanent_wrench_composer", mock_perm_wrench) @@ -281,8 +281,8 @@ def create_ovphysx_articulation( articulation._create_buffers() # Wrench composers - mock_inst_wrench = WrenchComposer(articulation) - mock_perm_wrench = WrenchComposer(articulation) + mock_inst_wrench = WrenchComposer(articulation, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(articulation, supports_world_at_com=True) object.__setattr__(articulation, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(articulation, "_permanent_wrench_composer", mock_perm_wrench) # Prevent __del__ / _clear_callbacks from raising diff --git a/source/isaaclab/test/assets/_rigid_object_collection_iface_test_utils.py b/source/isaaclab/test/assets/_rigid_object_collection_iface_test_utils.py index 602e4949729..92bda8ac518 100644 --- a/source/isaaclab/test/assets/_rigid_object_collection_iface_test_utils.py +++ b/source/isaaclab/test/assets/_rigid_object_collection_iface_test_utils.py @@ -101,8 +101,8 @@ def create_physx_rigid_object_collection( data.body_names = [f"object_{i}" for i in range(num_bodies)] # Create wrench composers - mock_inst_wrench = WrenchComposer(collection) - mock_perm_wrench = WrenchComposer(collection) + mock_inst_wrench = WrenchComposer(collection, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(collection, supports_world_at_com=True) object.__setattr__(collection, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(collection, "_permanent_wrench_composer", mock_perm_wrench) @@ -260,8 +260,8 @@ def create_ovphysx_rigid_object_collection( collection._create_buffers() # Use production wrench composers for interface coverage. - mock_inst_wrench = WrenchComposer(collection) - mock_perm_wrench = WrenchComposer(collection) + mock_inst_wrench = WrenchComposer(collection, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(collection, supports_world_at_com=True) object.__setattr__(collection, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(collection, "_permanent_wrench_composer", mock_perm_wrench) diff --git a/source/isaaclab/test/assets/_rigid_object_iface_test_utils.py b/source/isaaclab/test/assets/_rigid_object_iface_test_utils.py index 94b198da64b..3c6d11de4f2 100644 --- a/source/isaaclab/test/assets/_rigid_object_iface_test_utils.py +++ b/source/isaaclab/test/assets/_rigid_object_iface_test_utils.py @@ -86,8 +86,8 @@ def create_physx_rigid_object( data.body_names = body_names # Create wrench composers - mock_inst_wrench = WrenchComposer(rigid_object) - mock_perm_wrench = WrenchComposer(rigid_object) + mock_inst_wrench = WrenchComposer(rigid_object, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(rigid_object, supports_world_at_com=True) object.__setattr__(rigid_object, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(rigid_object, "_permanent_wrench_composer", mock_perm_wrench) @@ -104,10 +104,6 @@ def create_physx_rigid_object( # Cached .view(wp.float32) wrappers object.__setattr__(rigid_object, "_root_link_pose_w_f32", None) object.__setattr__(rigid_object, "_root_com_vel_w_f32", None) - object.__setattr__(rigid_object, "_inst_wrench_force_f32", None) - object.__setattr__(rigid_object, "_inst_wrench_torque_f32", None) - object.__setattr__(rigid_object, "_perm_wrench_force_f32", None) - object.__setattr__(rigid_object, "_perm_wrench_torque_f32", None) # Pre-allocated pinned CPU buffers for PhysX TensorAPI writes N, B = num_instances, 1 # rigid object has 1 body @@ -247,8 +243,8 @@ def create_ovphysx_rigid_object( obj._create_buffers() # Use production wrench composers for interface coverage. - mock_inst_wrench = WrenchComposer(obj) - mock_perm_wrench = WrenchComposer(obj) + mock_inst_wrench = WrenchComposer(obj, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(obj, supports_world_at_com=True) object.__setattr__(obj, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(obj, "_permanent_wrench_composer", mock_perm_wrench) diff --git a/source/isaaclab/test/assets/test_articulation_ordering_iface.py b/source/isaaclab/test/assets/test_articulation_ordering_iface.py index 85aef24c6d7..0e27ed25d38 100644 --- a/source/isaaclab/test/assets/test_articulation_ordering_iface.py +++ b/source/isaaclab/test/assets/test_articulation_ordering_iface.py @@ -7,7 +7,7 @@ """Mocked cross-backend articulation ordering interface tests.""" -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import numpy as np import pytest @@ -17,7 +17,6 @@ from _pytest.mark.structures import ParameterSet from isaaclab.utils.buffers import TimestampedBufferWarp -from isaaclab.utils.wrench_composer import WrenchComposer def _make_body_ordering_backend_data(num_instances: int, num_bodies: int) -> tuple[np.ndarray, ...]: @@ -679,24 +678,21 @@ def _set_body_ordering_backend_data( raise AssertionError(f"Unsupported backend for body-ordering test: {backend}") -def _set_identity_body_poses(backend: str, art, raw_backend) -> None: - """Give wrench transforms deterministic identity rotations.""" +def _set_rotated_body_poses(backend: str, art, raw_backend) -> None: + """Use a known 90-degree Z rotation and nonzero positions for wrench packing.""" + poses = np.zeros((art.num_instances, art.num_bodies, 7), dtype=np.float32) + poses[..., 0] = np.arange(art.num_bodies) + 10.0 + poses[..., 5:7] = 2.0**-0.5 if backend == "newton": - poses = np.zeros((art.num_instances, art.num_bodies, 7), dtype=np.float32) - poses[..., 6] = 1.0 poses_wp = wp.array(poses[:, None], dtype=wp.transformf, device=art.device) raw_backend.set_mock_link_transforms(poses_wp) art.data._sim_bind_body_link_pose_w.assign(poses_wp[:, 0]) art.data._refresh_user_order_body_state() - return - if backend != "ovphysx": - return - from isaaclab_ov import tensor_types as TT + elif backend == "ovphysx": + from isaaclab_ov import tensor_types as TT - poses = np.zeros((art.num_instances, art.num_bodies, 7), dtype=np.float32) - poses[..., 6] = 1.0 - raw_backend.bindings[TT.LINK_POSE]._data = poses - art.data._reset_pose() + raw_backend.bindings[TT.LINK_POSE]._data = poses + art.data._reset_pose() def _read_backend_wrench(backend: str, art, raw_backend, captured: dict) -> tuple[np.ndarray, np.ndarray]: @@ -2011,13 +2007,13 @@ def test_external_wrenches_are_written_in_backend_body_order(self, backend, orde is_fixed_base=is_fixed_base, body_ordering=body_ordering, ) - _set_identity_body_poses(backend, art, raw_backend) - object.__setattr__(art, "_instantaneous_wrench_composer", WrenchComposer(art)) - object.__setattr__(art, "_permanent_wrench_composer", WrenchComposer(art)) + _set_rotated_body_poses(backend, art, raw_backend) captured = {} if backend == "physx": def capture_wrench(*, force_data, torque_data, position_data, indices, is_global): + captured["is_global"] = is_global + assert position_data is None captured["force"] = force_data.numpy().reshape(num_instances, num_bodies, 3).copy() captured["torque"] = torque_data.numpy().reshape(num_instances, num_bodies, 3).copy() @@ -2025,17 +2021,32 @@ def capture_wrench(*, force_data, torque_data, position_data, indices, is_global forces = np.arange(num_instances * num_bodies * 3, dtype=np.float32).reshape(num_instances, num_bodies, 3) torques = forces + 100.0 - art.instantaneous_wrench_composer.set_forces_and_torques_index( - forces=wp.array(forces, dtype=wp.vec3f, device=device), - torques=wp.array(torques, dtype=wp.vec3f, device=device), - ) - - art.write_data_to_sim() - backend_to_user = _expected_backend_to_user(body_ordering, backend_body_names) - backend_force, backend_torque = _read_backend_wrench(backend, art, raw_backend, captured) - np.testing.assert_allclose(backend_force, forces[:, backend_to_user]) - np.testing.assert_allclose(backend_torque, torques[:, backend_to_user]) + composer = art.instantaneous_wrench_composer + for is_global in (False, True): + composer.set_forces_and_torques_index( + forces=wp.array(forces, dtype=wp.vec3f, device=device), + torques=wp.array(torques, dtype=wp.vec3f, device=device), + is_global=is_global, + ) + with patch.object(composer, "compose_to_body_frame", wraps=composer.compose_to_body_frame) as compose: + art.write_data_to_sim() + assert compose.call_count == int(is_global and backend == "newton") + + expected_force, expected_torque = forces, torques + if backend == "physx": + assert captured["is_global"] is is_global + elif not is_global: + expected_force = np.stack((-forces[..., 1], forces[..., 0], forces[..., 2]), axis=-1) + expected_torque = np.stack((-torques[..., 1], torques[..., 0], torques[..., 2]), axis=-1) + backend_force, backend_torque = _read_backend_wrench(backend, art, raw_backend, captured) + np.testing.assert_allclose(backend_force, expected_force[:, backend_to_user], atol=1e-4) + np.testing.assert_allclose(backend_torque, expected_torque[:, backend_to_user], atol=1e-4) + if backend == "ovphysx": + from isaaclab_ov import tensor_types as TT + + packed_positions = raw_backend.bindings[TT.LINK_WRENCH]._data[..., 6:9] + np.testing.assert_array_equal(packed_positions, raw_backend.bindings[TT.LINK_POSE]._data[..., :3]) @_requires_ovphysx def test_ovphysx_configured_defaults_use_public_joint_names(self): diff --git a/source/isaaclab/test/assets/test_rigid_object_iface.py b/source/isaaclab/test/assets/test_rigid_object_iface.py index e8a6147b7b4..f3dba59bd84 100644 --- a/source/isaaclab/test/assets/test_rigid_object_iface.py +++ b/source/isaaclab/test/assets/test_rigid_object_iface.py @@ -13,12 +13,16 @@ The setup is a bit convoluted so that we can run these tests without requiring Isaac Sim or GPU simulation. """ +from unittest.mock import MagicMock, patch + import numpy as np import pytest import torch import warp as wp from _rigid_object_iface_test_utils import BACKENDS, get_rigid_object +from isaaclab.utils.math import quat_apply + pytestmark = pytest.mark.integration @@ -954,6 +958,46 @@ def test_write_root_velocity_to_sim_mask(self, backend, num_instances, device, r class TestRigidObjectWritersBody: """Test body property writers/setters with all input combinations.""" + @_production_backends + @_default_devices + def test_external_wrench_frames(self, backend, device): + """Forward local and world wrenches through the real writer in each backend's frame.""" + obj, raw_backend = get_rigid_object(backend, num_instances=2, device=device) + composer = obj.permanent_wrench_composer + forces = torch.arange(1.0, 7.0, device=device).reshape(2, 1, 3) + torques = forces + 10.0 + if backend == "physx": + raw_backend.apply_forces_and_torques_at_position = MagicMock() + + for is_global in (False, True): + composer.reset() + composer.set_forces_and_torques_index(forces=forces, torques=torques, is_global=is_global) + with patch.object(composer, "compose_to_body_frame", wraps=composer.compose_to_body_frame) as compose: + obj.write_data_to_sim() + assert compose.call_count == int(is_global and backend == "newton") + + expected_force, expected_torque = forces, torques + if backend == "physx": + call = raw_backend.apply_forces_and_torques_at_position.call_args.kwargs + assert call["is_global"] is is_global + assert call["position_data"] is None + actual_force = call["force_data"].numpy().reshape(2, 1, 3) + actual_torque = call["torque_data"].numpy().reshape(2, 1, 3) + else: + if not is_global: + quat = obj.data.body_link_quat_w.torch + expected_force, expected_torque = quat_apply(quat, forces), quat_apply(quat, torques) + if backend == "newton": + packed = obj.data._sim_bind_body_external_wrench.numpy() + else: + from isaaclab_ov import tensor_types as TT + + packed = raw_backend.bindings[TT.RIGID_BODY_WRENCH]._data.reshape(2, 1, 9) + np.testing.assert_allclose(packed[..., 6:9], obj.data.body_link_pos_w.warp.numpy()) + actual_force, actual_torque = packed[..., :3], packed[..., 3:6] + np.testing.assert_allclose(actual_force, expected_force.cpu().numpy(), atol=1e-5, rtol=1e-5) + np.testing.assert_allclose(actual_torque, expected_torque.cpu().numpy(), atol=1e-5, rtol=1e-5) + @_backends @_default_dims @_default_devices diff --git a/source/isaaclab/test/utils/test_wrench_composer.py b/source/isaaclab/test/utils/test_wrench_composer.py index b0a346eaa6c..17b64287baf 100644 --- a/source/isaaclab/test/utils/test_wrench_composer.py +++ b/source/isaaclab/test/utils/test_wrench_composer.py @@ -3,7 +3,11 @@ # # SPDX-License-Identifier: BSD-3-Clause +import ast +import inspect from types import SimpleNamespace +from typing import get_type_hints +from unittest.mock import patch import numpy as np import pytest @@ -17,6 +21,28 @@ pytestmark = pytest.mark.unit +def test_wrench_composer_uses_asset_frame_conventions(): + """Keep frame selection as an is_global boolean, with no enum or content-classification layer.""" + tree = ast.parse(inspect.getsource(WrenchComposer)) + assert [node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)] == ["WrenchComposer"] + assert not any( + isinstance(node, ast.Attribute) and node.attr in {"_content", "_classify"} for node in ast.walk(tree) + ) + module_tree = ast.parse(inspect.getsource(inspect.getmodule(WrenchComposer))) + assert not any(isinstance(node, ast.ImportFrom) and node.module == "enum" for node in ast.walk(module_tree)) + assert not hasattr(WrenchComposer, "resolve_submission") + assert get_type_hints(WrenchComposer.get_forces_and_torques)["return"] == tuple[wp.array, wp.array, bool] + + +def _get_wrench_without_pose_reads(composer: WrenchComposer) -> tuple[wp.array, wp.array, bool]: + """Read a fast-path wrench while rejecting the expensive body-pose queries.""" + with ( + patch.object(composer, "_get_com_pos_fn", side_effect=AssertionError("unexpected CoM read")), + patch.object(composer, "_get_link_quat_fn", side_effect=AssertionError("unexpected quaternion read")), + ): + return composer.get_forces_and_torques() + + class _WrenchAssetDataFixture: """Minimal asset data required by :class:`WrenchComposer`.""" @@ -415,27 +441,35 @@ def test_global_forces_with_rotation(device: str, num_envs: int, num_bodies: int """Test that global forces are correctly rotated to the local frame.""" rng = np.random.default_rng(seed=10) - for _ in range(5): + for method in ( + "add_forces_and_torques_index", + "add_forces_and_torques_mask", + "set_forces_and_torques_index", + "set_forces_and_torques_mask", + ): # Create random link quaternions link_quat_np = random_unit_quaternion_np(rng, (num_envs, num_bodies)) link_quat_torch = torch.from_numpy(link_quat_np) # Create mock asset with custom quaternions mock_asset = create_mock_asset(num_envs, num_bodies, device, link_quat=link_quat_torch) - wrench_composer = WrenchComposer(mock_asset) + wrench_composer = WrenchComposer(mock_asset, supports_world_at_com=True) # Generate random global forces for all envs and bodies forces_global_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) forces_global = wp.from_numpy(forces_global_np, dtype=wp.vec3f, device=device) # Apply global forces - wrench_composer.add_forces_and_torques_index(forces=forces_global, is_global=True) + getattr(wrench_composer, method)(forces=forces_global, is_global=True) # Compute expected local forces by rotating global forces by inverse quaternion expected_forces_local = quat_rotate_inv_np(link_quat_np, forces_global_np) # Check raw global buffer has the global forces - global_force_np = wrench_composer.global_force_at_com_w.numpy() + force, torque, is_global = _get_wrench_without_pose_reads(wrench_composer) + assert is_global is True + np.testing.assert_array_equal(torque.numpy(), np.zeros_like(forces_global_np)) + global_force_np = force.numpy() assert np.allclose(global_force_np, forces_global_np, atol=1e-4, rtol=1e-5), ( f"Global force buffer mismatch.\nExpected:\n{forces_global_np}\nGot:\n{global_force_np}" ) @@ -457,27 +491,35 @@ def test_global_torques_with_rotation(device: str, num_envs: int, num_bodies: in """Test that global torques are correctly rotated to the local frame.""" rng = np.random.default_rng(seed=11) - for _ in range(5): + for method in ( + "add_forces_and_torques_index", + "add_forces_and_torques_mask", + "set_forces_and_torques_index", + "set_forces_and_torques_mask", + ): # Create random link quaternions link_quat_np = random_unit_quaternion_np(rng, (num_envs, num_bodies)) link_quat_torch = torch.from_numpy(link_quat_np) # Create mock asset with custom quaternions mock_asset = create_mock_asset(num_envs, num_bodies, device, link_quat=link_quat_torch) - wrench_composer = WrenchComposer(mock_asset) + wrench_composer = WrenchComposer(mock_asset, supports_world_at_com=True) # Generate random global torques torques_global_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) torques_global = wp.from_numpy(torques_global_np, dtype=wp.vec3f, device=device) - # Apply global torques - wrench_composer.add_forces_and_torques_index(torques=torques_global, is_global=True) + # Positions are unused when only torques are supplied. + getattr(wrench_composer, method)(torques=torques_global, positions=torques_global, is_global=True) # Compute expected local torques expected_torques_local = quat_rotate_inv_np(link_quat_np, torques_global_np) # Check raw global buffer has the global torques - global_torque_np = wrench_composer.global_torque_w.numpy() + force, torque, is_global = _get_wrench_without_pose_reads(wrench_composer) + assert is_global is True + np.testing.assert_array_equal(force.numpy(), np.zeros_like(torques_global_np)) + global_torque_np = torque.numpy() assert np.allclose(global_torque_np, torques_global_np, atol=1e-4, rtol=1e-5), ( f"Global torque buffer mismatch.\nExpected:\n{torques_global_np}\nGot:\n{global_torque_np}" ) @@ -499,7 +541,12 @@ def test_global_forces_at_global_position(device: str, num_envs: int, num_bodies """Test global forces at global positions with full coordinate transformation.""" rng = np.random.default_rng(seed=12) - for _ in range(5): + for method in ( + "add_forces_and_torques_index", + "add_forces_and_torques_mask", + "set_forces_and_torques_index", + "set_forces_and_torques_mask", + ): # Create random link poses link_pos_np = rng.uniform(-10.0, 10.0, (num_envs, num_bodies, 3)).astype(np.float32) link_quat_np = random_unit_quaternion_np(rng, (num_envs, num_bodies)) @@ -508,7 +555,7 @@ def test_global_forces_at_global_position(device: str, num_envs: int, num_bodies # Create mock asset mock_asset = create_mock_asset(num_envs, num_bodies, device, link_pos=link_pos_torch, link_quat=link_quat_torch) - wrench_composer = WrenchComposer(mock_asset) + wrench_composer = WrenchComposer(mock_asset, supports_world_at_com=True) # Generate random global forces and positions forces_global_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) @@ -517,7 +564,7 @@ def test_global_forces_at_global_position(device: str, num_envs: int, num_bodies positions_global = wp.from_numpy(positions_global_np, dtype=wp.vec3f, device=device) # Apply global forces at global positions - wrench_composer.add_forces_and_torques_index(forces=forces_global, positions=positions_global, is_global=True) + getattr(wrench_composer, method)(forces=forces_global, positions=positions_global, is_global=True) # Compute expected results: # 1. Force in local frame = quat_rotate_inv(link_quat, global_force) @@ -540,17 +587,17 @@ def test_global_forces_at_global_position(device: str, num_envs: int, num_bodies f"Global force buffer mismatch.\nExpected:\n{forces_global_np}\nGot:\n{global_force_np}" ) - # Compose to body frame before checking output - wrench_composer.compose_to_body_frame() + force, torque, is_global = wrench_composer.get_forces_and_torques() + assert is_global is False # Verify forces - composed_force_np = wrench_composer.out_force_b.warp.numpy() + composed_force_np = force.numpy() assert np.allclose(composed_force_np, expected_forces_local, atol=1e-3, rtol=1e-4), ( f"Global force at position failed.\nExpected forces:\n{expected_forces_local}\nGot:\n{composed_force_np}" ) # Verify torques - composed_torque_np = wrench_composer.out_torque_b.warp.numpy() + composed_torque_np = torque.numpy() assert np.allclose(composed_torque_np, expected_torques_local, atol=1e-3, rtol=1e-4), ( f"Global force at position failed.\nExpected torques:\n{expected_torques_local}\nGot:\n{composed_torque_np}" ) @@ -622,14 +669,21 @@ def test_90_degree_rotation_global_force(device: str): # Actually, inverse rotation of +90° around Z applied to (1,0,0) gives (0,-1,0) expected_force_local = np.array([[[0.0, -1.0, 0.0]]], dtype=np.float32) - # Compose to body frame before checking output - wrench_composer.compose_to_body_frame() + force, _, is_global = wrench_composer.get_forces_and_torques() + assert is_global is False - composed_force_np = wrench_composer.out_force_b.warp.numpy() + composed_force_np = force.numpy() assert np.allclose(composed_force_np, expected_force_local, atol=1e-5), ( f"90-degree rotation test failed.\nExpected:\n{expected_force_local}\nGot:\n{composed_force_np}" ) + # A second read must use the live pose even though the wrench buffers have not changed. + quat_180_z = torch.tensor([[[0.0, 0.0, 1.0, 0.0]]], device=device) + mock_asset.data.body_link_quat_w = ProxyArray(wp.from_torch(quat_180_z, dtype=wp.quatf)) + force, _, is_global = wrench_composer.get_forces_and_torques() + assert is_global is False + np.testing.assert_allclose(force.numpy(), [[[-1.0, 0.0, 0.0]]], atol=1e-6) + @pytest.mark.parametrize("device", test_devices()) def test_composition_mixed_local_and_global(device: str): @@ -642,7 +696,7 @@ def test_composition_mixed_local_and_global(device: str): link_quat_torch = torch.from_numpy(link_quat_np) mock_asset = create_mock_asset(num_envs, num_bodies, device, link_quat=link_quat_torch) - wrench_composer = WrenchComposer(mock_asset) + wrench_composer = WrenchComposer(mock_asset, supports_world_at_com=True) # Generate random local and global forces forces_local_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) @@ -667,10 +721,10 @@ def test_composition_mixed_local_and_global(device: str): global_force_at_com_np = wrench_composer.global_force_at_com_w.numpy() assert np.allclose(global_force_at_com_np, forces_global_np, atol=1e-4, rtol=1e-5) - # Compose to body frame before checking output - wrench_composer.compose_to_body_frame() + force, _, is_global = wrench_composer.get_forces_and_torques() + assert is_global is False - composed_force_np = wrench_composer.out_force_b.warp.numpy() + composed_force_np = force.numpy() assert np.allclose(composed_force_np, expected_total, atol=1e-4, rtol=1e-5), ( f"Mixed local/global composition failed.\nExpected:\n{expected_total}\nGot:\n{composed_force_np}" ) @@ -683,7 +737,12 @@ def test_local_forces_at_local_position(device: str, num_envs: int, num_bodies: """Test local forces at local positions (offset from link frame).""" rng = np.random.default_rng(seed=15) - for _ in range(5): + for method in ( + "add_forces_and_torques_index", + "add_forces_and_torques_mask", + "set_forces_and_torques_index", + "set_forces_and_torques_mask", + ): # Create random link poses (shouldn't affect local frame calculations) link_pos_np = rng.uniform(-10.0, 10.0, (num_envs, num_bodies, 3)).astype(np.float32) link_quat_np = random_unit_quaternion_np(rng, (num_envs, num_bodies)) @@ -691,7 +750,7 @@ def test_local_forces_at_local_position(device: str, num_envs: int, num_bodies: link_quat_torch = torch.from_numpy(link_quat_np) mock_asset = create_mock_asset(num_envs, num_bodies, device, link_pos=link_pos_torch, link_quat=link_quat_torch) - wrench_composer = WrenchComposer(mock_asset) + wrench_composer = WrenchComposer(mock_asset, supports_world_at_com=True) # Generate random local forces and local positions (offsets) forces_local_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) @@ -700,7 +759,7 @@ def test_local_forces_at_local_position(device: str, num_envs: int, num_bodies: positions_local = wp.from_numpy(positions_local_np, dtype=wp.vec3f, device=device) # Apply local forces at local positions - wrench_composer.add_forces_and_torques_index(forces=forces_local, positions=positions_local, is_global=False) + getattr(wrench_composer, method)(forces=forces_local, positions=positions_local, is_global=False) # Expected: forces stay as-is, torque = cross(position, force) expected_forces = forces_local_np @@ -710,12 +769,12 @@ def test_local_forces_at_local_position(device: str, num_envs: int, num_bodies: local_force_np = wrench_composer.local_force_b.numpy() assert np.allclose(local_force_np, expected_forces, atol=1e-4, rtol=1e-5) - # Compose to body frame before checking output - wrench_composer.compose_to_body_frame() + force, torque, is_global = _get_wrench_without_pose_reads(wrench_composer) + assert is_global is False # Verify - composed_force_np = wrench_composer.out_force_b.warp.numpy() - composed_torque_np = wrench_composer.out_torque_b.warp.numpy() + composed_force_np = force.numpy() + composed_torque_np = torque.numpy() assert np.allclose(composed_force_np, expected_forces, atol=1e-4, rtol=1e-5) assert np.allclose(composed_torque_np, expected_torques, atol=1e-4, rtol=1e-5) @@ -785,7 +844,7 @@ def test_add_raw_buffers_from(device: str, num_envs: int, num_bodies: int): mock_a = create_mock_asset(num_envs, num_bodies, device, link_pos=link_pos_torch, link_quat=link_quat_torch) mock_b = create_mock_asset(num_envs, num_bodies, device, link_pos=link_pos_torch, link_quat=link_quat_torch) - composer_a = WrenchComposer(mock_a) + composer_a = WrenchComposer(mock_a, supports_world_at_com=True) composer_b = WrenchComposer(mock_b) # Populate composer_a with local forces at positions @@ -824,15 +883,16 @@ def test_add_raw_buffers_from(device: str, num_envs: int, num_bodies: int): ) # Compose both and compare - composer_a.compose_to_body_frame() + force, torque, is_global = composer_a.get_forces_and_torques() + assert is_global is False composer_ref.compose_to_body_frame() - assert np.allclose( - composer_a.out_force_b.warp.numpy(), composer_ref.out_force_b.warp.numpy(), atol=1e-4, rtol=1e-5 - ), "add_raw_buffers_from force mismatch vs direct accumulation" - assert np.allclose( - composer_a.out_torque_b.warp.numpy(), composer_ref.out_torque_b.warp.numpy(), atol=1e-4, rtol=1e-5 - ), "add_raw_buffers_from torque mismatch vs direct accumulation" + assert np.allclose(force.numpy(), composer_ref.out_force_b.warp.numpy(), atol=1e-4, rtol=1e-5), ( + "add_raw_buffers_from force mismatch vs direct accumulation" + ) + assert np.allclose(torque.numpy(), composer_ref.out_torque_b.warp.numpy(), atol=1e-4, rtol=1e-5), ( + "add_raw_buffers_from torque mismatch vs direct accumulation" + ) @pytest.mark.parametrize("device", test_devices()) @@ -1042,12 +1102,11 @@ def test_set_forces_overwrites_previous_add(device: str): forces=wp.from_numpy(forces_b_np, dtype=wp.vec3f, device=device), ) - composer.compose_to_body_frame() + force, _, is_global = _get_wrench_without_pose_reads(composer) + assert is_global is False # Output should match forces_b only (forces_a should be gone) - assert np.allclose(composer.out_force_b.warp.numpy(), forces_b_np, atol=1e-4, rtol=1e-5), ( - "set_forces did not clear previous add" - ) + assert np.allclose(force.numpy(), forces_b_np, atol=1e-4, rtol=1e-5), "set_forces did not clear previous add" @pytest.mark.parametrize("device", test_devices()) @@ -1122,55 +1181,45 @@ def test_set_forces_clears_targeted_envs_only(device: str): @pytest.mark.parametrize("device", test_devices()) -def test_partial_reset_zeros_only_specified_envs(device: str): - """Test that partial reset zeros only the specified environments and leaves others intact.""" +@pytest.mark.parametrize("source_is_global", [False, True]) +def test_partial_reset_zeros_only_specified_envs(device: str, source_is_global: bool): + """Partial resets preserve the remaining merged wrench and its required composition.""" num_envs, num_bodies = 8, 3 rng = np.random.default_rng(seed=50) - - mock_asset = create_mock_asset(num_envs, num_bodies, device) - composer = WrenchComposer(mock_asset) - - # Populate all envs with local forces - forces_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) - composer.add_forces_and_torques_index( - forces=wp.from_numpy(forces_np, dtype=wp.vec3f, device=device), + asset = create_mock_asset(num_envs, num_bodies, device) + composer = WrenchComposer(asset, supports_world_at_com=True) + source = WrenchComposer(asset) + forces = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) + source_forces = rng.uniform(-100.0, 100.0, forces.shape).astype(np.float32) + positions = rng.uniform(-5.0, 5.0, forces.shape).astype(np.float32) + composer.add_forces_and_torques_index(forces=wp.array(forces, dtype=wp.vec3f, device=device), is_global=True) + assert _get_wrench_without_pose_reads(composer)[2] is True + source.add_forces_and_torques_index( + forces=wp.array(source_forces, dtype=wp.vec3f, device=device), + positions=wp.array(positions, dtype=wp.vec3f, device=device), + is_global=source_is_global, ) + composer.add_raw_buffers_from(source) - # Also add global forces to populate more buffers - forces_global_np = rng.uniform(-100.0, 100.0, (num_envs, num_bodies, 3)).astype(np.float32) - composer.add_forces_and_torques_index( - forces=wp.from_numpy(forces_global_np, dtype=wp.vec3f, device=device), - is_global=True, - ) - - # Partial reset: only envs [1, 3, 5] - reset_env_ids = np.array([1, 3, 5], dtype=np.int32) - kept_env_ids = np.array([0, 2, 4, 6, 7], dtype=np.int32) - composer.reset(env_ids=wp.from_numpy(reset_env_ids, dtype=wp.int32, device=device)) - - # Reset envs should be zeroed across all input buffers - zeros = np.zeros((num_bodies, 3), dtype=np.float32) - local_force = composer.local_force_b.numpy() - global_force_at_com = composer.global_force_at_com_w.numpy() - for eid in reset_env_ids: - assert np.allclose(local_force[eid], zeros, atol=1e-7), f"local_force_b not zeroed for env {eid}" - assert np.allclose(global_force_at_com[eid], zeros, atol=1e-7), ( - f"global_force_at_com_w not zeroed for env {eid}" - ) - - # Kept envs should retain their values - for eid in kept_env_ids: - assert np.allclose(local_force[eid], forces_np[eid], atol=1e-4, rtol=1e-5), ( - f"local_force_b changed for non-reset env {eid}" - ) - assert np.allclose(global_force_at_com[eid], forces_global_np[eid], atol=1e-4, rtol=1e-5), ( - f"global_force_at_com_w changed for non-reset env {eid}" - ) - - # Flags: _active should still be True, _dirty should be True + reset_env_ids = [1, 3, 5] + if source_is_global: + mask = np.zeros(num_envs, dtype=bool) + mask[reset_env_ids] = True + composer.reset(env_mask=wp.array(mask, dtype=wp.bool, device=device)) + else: + composer.reset(env_ids=reset_env_ids) assert composer.active assert composer._dirty + force, torque, is_global = composer.get_forces_and_torques() + assert is_global is False + expected_force = forces + source_forces + expected_torque = np.cross(positions, source_forces) + expected_force[reset_env_ids] = 0.0 + expected_torque[reset_env_ids] = 0.0 + np.testing.assert_allclose(force.numpy(), expected_force, atol=1e-4, rtol=1e-5) + np.testing.assert_allclose(torque.numpy(), expected_torque, atol=1e-4, rtol=1e-5) + @pytest.mark.parametrize("device", test_devices()) @pytest.mark.parametrize("env_ids", [None, slice(None)], ids=["none", "full_slice"]) @@ -1179,18 +1228,33 @@ def test_full_reset_clears_active_flag(device: str, env_ids: slice | None): num_envs, num_bodies = 4, 2 mock_asset = create_mock_asset(num_envs, num_bodies, device) - composer = WrenchComposer(mock_asset) + composer = WrenchComposer(mock_asset, supports_world_at_com=True) forces_np = np.ones((num_envs, num_bodies, 3), dtype=np.float32) composer.add_forces_and_torques_index( forces=wp.from_numpy(forces_np, dtype=wp.vec3f, device=device), ) + composer.add_forces_and_torques_index( + forces=wp.from_numpy(forces_np, dtype=wp.vec3f, device=device), + positions=wp.from_numpy(forces_np, dtype=wp.vec3f, device=device), + is_global=True, + ) + composer.get_forces_and_torques() assert composer.active composer.reset(env_ids=env_ids) assert not composer.active assert not composer._dirty + force, torque, is_global = _get_wrench_without_pose_reads(composer) + assert is_global is False + np.testing.assert_array_equal(force.numpy(), np.zeros_like(forces_np)) + np.testing.assert_array_equal(torque.numpy(), np.zeros_like(forces_np)) + composer.add_forces_and_torques_index( + forces=wp.from_numpy(forces_np, dtype=wp.vec3f, device=device), is_global=True + ) + assert _get_wrench_without_pose_reads(composer)[2] is True + # ============================================================================ # Deprecated API Backward-Compatibility Tests @@ -1282,10 +1346,11 @@ def test_set_forces_mask_overwrites_previous_add(device: str): forces=wp.from_numpy(forces_b_np, dtype=wp.vec3f, device=device), ) - composer.compose_to_body_frame() + force, _, is_global = _get_wrench_without_pose_reads(composer) + assert is_global is False # Output should match forces_b only (forces_a should be gone) - assert np.allclose(composer.out_force_b.warp.numpy(), forces_b_np, atol=1e-4, rtol=1e-5), ( + assert np.allclose(force.numpy(), forces_b_np, atol=1e-4, rtol=1e-5), ( "set_forces_and_torques_mask did not clear previous add" ) diff --git a/source/isaaclab_newton/changelog.d/wrench-fast-paths.rst b/source/isaaclab_newton/changelog.d/wrench-fast-paths.rst new file mode 100644 index 00000000000..7e994873559 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/wrench-fast-paths.rst @@ -0,0 +1,6 @@ +Changed +^^^^^^^ + +* Changed the external-wrench writers to submit through + :meth:`~isaaclab.utils.wrench_composer.WrenchComposer.get_forces_and_torques`, so an all-local-frame + wrench no longer reads the body transforms before being written to the solver. diff --git a/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py b/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py index 8621d57897d..5784e07a5d5 100644 --- a/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py +++ b/source/isaaclab_newton/isaaclab_newton/assets/articulation/articulation.py @@ -397,7 +397,7 @@ def write_data_to_sim(self): composer.add_raw_buffers_from(self._permanent_wrench_composer) else: composer = self._permanent_wrench_composer - composer.compose_to_body_frame() + force_b, torque_b, _ = composer.get_forces_and_torques() # Kept separate from the joint-target gather below: this scatter runs # over bodies while the target gather runs over joints (mismatched # item axes), and it must precede the actuator compute/submit below, @@ -409,8 +409,8 @@ def write_data_to_sim(self): dim=(self.num_instances, self.num_bodies), device=self.device, inputs=[ - composer.out_force_b.warp, - composer.out_torque_b.warp, + force_b, + torque_b, self._data.body_link_pose_w.warp, self._body_user_to_backend_map(), self._data._sim_bind_body_external_wrench, @@ -424,8 +424,8 @@ def write_data_to_sim(self): dim=(self.num_instances, self.num_bodies), device=self.device, inputs=[ - composer.out_force_b, - composer.out_torque_b, + force_b, + torque_b, self._data.body_link_pose_w.warp, self._data._sim_bind_body_external_wrench, self._ALL_ENV_MASK, diff --git a/source/isaaclab_newton/isaaclab_newton/assets/rigid_object/rigid_object.py b/source/isaaclab_newton/isaaclab_newton/assets/rigid_object/rigid_object.py index 39251a42760..eb4cf3ad5b2 100644 --- a/source/isaaclab_newton/isaaclab_newton/assets/rigid_object/rigid_object.py +++ b/source/isaaclab_newton/isaaclab_newton/assets/rigid_object/rigid_object.py @@ -148,14 +148,14 @@ def write_data_to_sim(self) -> None: composer.add_raw_buffers_from(self._permanent_wrench_composer) else: composer = self._permanent_wrench_composer - composer.compose_to_body_frame() + force_b, torque_b, _ = composer.get_forces_and_torques() wp.launch( shared_kernels.update_wrench_array_with_force_and_torque, dim=(self.num_instances, self.num_bodies), device=self.device, inputs=[ - composer.out_force_b, - composer.out_torque_b, + force_b, + torque_b, self._data.body_link_pose_w.warp, self._data._sim_bind_body_external_wrench, self._ALL_ENV_MASK, diff --git a/source/isaaclab_newton/isaaclab_newton/assets/rigid_object_collection/rigid_object_collection.py b/source/isaaclab_newton/isaaclab_newton/assets/rigid_object_collection/rigid_object_collection.py index b4e4edb4e95..c52ae05bc56 100644 --- a/source/isaaclab_newton/isaaclab_newton/assets/rigid_object_collection/rigid_object_collection.py +++ b/source/isaaclab_newton/isaaclab_newton/assets/rigid_object_collection/rigid_object_collection.py @@ -194,14 +194,14 @@ def write_data_to_sim(self) -> None: composer.add_raw_buffers_from(self._permanent_wrench_composer) else: composer = self._permanent_wrench_composer - composer.compose_to_body_frame() + force_b, torque_b, _ = composer.get_forces_and_torques() wp.launch( shared_kernels.update_wrench_array_with_force_and_torque, dim=(self.num_instances, self.num_bodies), device=self.device, inputs=[ - composer.out_force_b, - composer.out_torque_b, + force_b, + torque_b, self._data.body_link_pose_w.warp, self._wrench_buffer, self._ALL_ENV_MASK, diff --git a/source/isaaclab_ov/changelog.d/pr7536-test-reset.skip b/source/isaaclab_ov/changelog.d/pr7536-test-reset.skip new file mode 100644 index 00000000000..09ec8ebf815 --- /dev/null +++ b/source/isaaclab_ov/changelog.d/pr7536-test-reset.skip @@ -0,0 +1 @@ +Test-only fix: preserve environment origins when resetting the external-wrench test articulations. diff --git a/source/isaaclab_ov/changelog.d/wrench-fast-paths.rst b/source/isaaclab_ov/changelog.d/wrench-fast-paths.rst new file mode 100644 index 00000000000..a3e008ddf5d --- /dev/null +++ b/source/isaaclab_ov/changelog.d/wrench-fast-paths.rst @@ -0,0 +1,7 @@ +Changed +^^^^^^^ + +* Changed the external-wrench writers to submit through + :meth:`~isaaclab.utils.wrench_composer.WrenchComposer.get_forces_and_torques`. A wrench that is already + global-frame at the center of mass is now packed without rotating it into the body frame and back, + and an all-local-frame wrench no longer reads the body transforms before packing. diff --git a/source/isaaclab_ov/isaaclab_ov/assets/articulation/articulation.py b/source/isaaclab_ov/isaaclab_ov/assets/articulation/articulation.py index 98efca43379..2df2dade3ae 100644 --- a/source/isaaclab_ov/isaaclab_ov/assets/articulation/articulation.py +++ b/source/isaaclab_ov/isaaclab_ov/assets/articulation/articulation.py @@ -244,11 +244,10 @@ def write_data_to_sim(self) -> None: if inst.active: if perm.active: inst.add_raw_buffers_from(perm) - force_b = inst.out_force_b.warp - torque_b = inst.out_torque_b.warp + composer = inst else: - force_b = perm.out_force_b.warp - torque_b = perm.out_torque_b.warp + composer = perm + force_in, torque_in, is_global = composer.get_forces_and_torques() # rotate body-frame wrenches into the world frame expected by ``LINK_WRENCH``. # Read the link poses directly from the backend-order ``LINK_POSE`` buffer: the @@ -259,7 +258,14 @@ def write_data_to_sim(self) -> None: wp.launch( shared_kernels._body_wrench_to_world_ordered, dim=(self._num_instances, self._num_bodies), - inputs=[force_b, torque_b, poses, self._body_user_to_backend_map(), has_body_ordering], + inputs=[ + force_in, + torque_in, + poses, + self._body_user_to_backend_map(), + has_body_ordering, + is_global, + ], outputs=[self._wrench_buf], device=self._device, ) @@ -4064,8 +4070,8 @@ def _create_buffers(self) -> None: self._wrench_buf = wp.zeros((N, B, 9), dtype=wp.float32, device=device) # Wrench composers. - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # Pinned-host CPU staging for env ids/masks (PR #5329 pattern). self._cpu_env_ids_all = wp.zeros(N, dtype=wp.int32, device="cpu", pinned=True) diff --git a/source/isaaclab_ov/isaaclab_ov/assets/kernels.py b/source/isaaclab_ov/isaaclab_ov/assets/kernels.py index fc557f68f3c..14ff26a0fcb 100644 --- a/source/isaaclab_ov/isaaclab_ov/assets/kernels.py +++ b/source/isaaclab_ov/isaaclab_ov/assets/kernels.py @@ -1164,12 +1164,13 @@ def derive_body_acceleration_from_body_com_velocities( @wp.kernel def _body_wrench_to_world( - force_b: wp.array(dtype=wp.vec3f, ndim=2), - torque_b: wp.array(dtype=wp.vec3f, ndim=2), + force_in: wp.array(dtype=wp.vec3f, ndim=2), + torque_in: wp.array(dtype=wp.vec3f, ndim=2), poses: wp.array(dtype=wp.transformf, ndim=2), + is_global: bool, wrench_out: wp.array(dtype=wp.float32, ndim=3), ): - """Rotate body-frame force/torque to world frame and pack into a flat output array. + """Rotate a force/torque to world frame and pack into a flat output array. Output layout per ``(i, j)`` slice (9 floats total): @@ -1178,15 +1179,21 @@ def _body_wrench_to_world( * ``[6:9]`` -- world-frame link position ``[m]`` Args: - force_b: Body-frame applied forces ``[N]``. Shape is ``(N, L)``. - torque_b: Body-frame applied torques ``[N*m]``. Shape is ``(N, L)``. + force_in: Applied forces ``[N]`` in the frame selected by ``is_global``. Shape is ``(N, L)``. + torque_in: Applied torques ``[N*m]`` in the frame selected by ``is_global``. Shape is ``(N, L)``. poses: Link poses in world frame. Shape is ``(N, L)``. + is_global: Whether the input wrench is already in the world frame, in which case no + rotation is applied. wrench_out: Output packed wrench array. Shape is ``(N, L, 9)``. """ i, j = wp.tid() - q = wp.transform_get_rotation(poses[i, j]) - f_w = wp.quat_rotate(q, force_b[i, j]) - t_w = wp.quat_rotate(q, torque_b[i, j]) + if is_global: + f_w = force_in[i, j] + t_w = torque_in[i, j] + else: + q = wp.transform_get_rotation(poses[i, j]) + f_w = wp.quat_rotate(q, force_in[i, j]) + t_w = wp.quat_rotate(q, torque_in[i, j]) wrench_out[i, j, 0] = f_w[0] wrench_out[i, j, 1] = f_w[1] wrench_out[i, j, 2] = f_w[2] @@ -1201,16 +1208,17 @@ def _body_wrench_to_world( @wp.kernel def _body_wrench_to_world_ordered( - force_b: wp.array(dtype=wp.vec3f, ndim=2), - torque_b: wp.array(dtype=wp.vec3f, ndim=2), + force_in: wp.array(dtype=wp.vec3f, ndim=2), + torque_in: wp.array(dtype=wp.vec3f, ndim=2), poses: wp.array(dtype=wp.transformf, ndim=2), user_to_backend: wp.array(dtype=wp.int32), has_ordering: bool, + is_global: bool, wrench_out: wp.array(dtype=wp.float32, ndim=3), ): """Rotate public-order body wrenches to world frame and write them in backend order. - The wrench (``force_b`` / ``torque_b``) is indexed in public body order while the + The wrench (``force_in`` / ``torque_in``) is indexed in public body order while the link ``poses`` are read directly from the backend-order ``LINK_POSE`` buffer. The public body ``user_body_id`` and the backend body ``backend_body_id`` address the same physical body, so its world-frame orientation and position are identical in @@ -1218,12 +1226,16 @@ def _body_wrench_to_world_ordered( pose shadow (no per-substep reorder launch). Args: - force_b: Body-frame applied forces ``[N]`` in public body order. Shape is ``(N, L)``. - torque_b: Body-frame applied torques ``[N*m]`` in public body order. Shape is ``(N, L)``. + force_in: Applied forces ``[N]`` in public body order, in the frame selected by + ``is_global``. Shape is ``(N, L)``. + torque_in: Applied torques ``[N*m]`` in public body order, in the frame selected by + ``is_global``. Shape is ``(N, L)``. poses: Link poses in world frame in backend body order (identity when ``has_ordering`` is False). Shape is ``(N, L)``. user_to_backend: Map from public body index to backend body index. Shape is ``(L,)``. has_ordering: Whether the public-to-backend body map is nonidentity. + is_global: Whether the input wrench is already in the world frame, in which case no + rotation is applied. wrench_out: Output packed wrench array in backend body order. Shape is ``(N, L, 9)`` with ``[0:3]`` world force ``[N]``, ``[3:6]`` world torque ``[N*m]``, ``[6:9]`` world link position ``[m]``. @@ -1232,9 +1244,13 @@ def _body_wrench_to_world_ordered( backend_body_id = user_body_id if has_ordering: backend_body_id = user_to_backend[user_body_id] - q = wp.transform_get_rotation(poses[i, backend_body_id]) - f_w = wp.quat_rotate(q, force_b[i, user_body_id]) - t_w = wp.quat_rotate(q, torque_b[i, user_body_id]) + if is_global: + f_w = force_in[i, user_body_id] + t_w = torque_in[i, user_body_id] + else: + q = wp.transform_get_rotation(poses[i, backend_body_id]) + f_w = wp.quat_rotate(q, force_in[i, user_body_id]) + t_w = wp.quat_rotate(q, torque_in[i, user_body_id]) wrench_out[i, backend_body_id, 0] = f_w[0] wrench_out[i, backend_body_id, 1] = f_w[1] wrench_out[i, backend_body_id, 2] = f_w[2] diff --git a/source/isaaclab_ov/isaaclab_ov/assets/rigid_object/rigid_object.py b/source/isaaclab_ov/isaaclab_ov/assets/rigid_object/rigid_object.py index e38a59b0c35..262c2f57c21 100644 --- a/source/isaaclab_ov/isaaclab_ov/assets/rigid_object/rigid_object.py +++ b/source/isaaclab_ov/isaaclab_ov/assets/rigid_object/rigid_object.py @@ -166,17 +166,16 @@ def write_data_to_sim(self) -> None: if inst.active: if perm.active: inst.add_raw_buffers_from(perm) - force_b = inst.out_force_b.warp - torque_b = inst.out_torque_b.warp + composer = inst else: - force_b = perm.out_force_b.warp - torque_b = perm.out_torque_b.warp + composer = perm + force_in, torque_in, is_global = composer.get_forces_and_torques() poses = self._data.body_link_pose_w.warp # (N, 1) wp.transformf wp.launch( _body_wrench_to_world, dim=(self._num_instances, 1), - inputs=[force_b, torque_b, poses], + inputs=[force_in, torque_in, poses, is_global], outputs=[self._wrench_buf], device=self._device, ) @@ -1038,8 +1037,8 @@ def _create_buffers(self) -> None: device=device, copy=False, ) - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # set information about rigid body into data self._data.body_names = self._body_names diff --git a/source/isaaclab_ov/isaaclab_ov/assets/rigid_object_collection/rigid_object_collection.py b/source/isaaclab_ov/isaaclab_ov/assets/rigid_object_collection/rigid_object_collection.py index a3ca1621da9..e57ffedfc18 100644 --- a/source/isaaclab_ov/isaaclab_ov/assets/rigid_object_collection/rigid_object_collection.py +++ b/source/isaaclab_ov/isaaclab_ov/assets/rigid_object_collection/rigid_object_collection.py @@ -195,17 +195,16 @@ def write_data_to_sim(self) -> None: if inst.active: if perm.active: inst.add_raw_buffers_from(perm) - force_b = inst.out_force_b.warp - torque_b = inst.out_torque_b.warp + composer = inst else: - force_b = perm.out_force_b.warp - torque_b = perm.out_torque_b.warp + composer = perm + force_in, torque_in, is_global = composer.get_forces_and_torques() poses = self._data.body_link_pose_w.warp # (N, B) wp.transformf wp.launch( _body_wrench_to_world, dim=(self._num_instances, self._num_bodies), - inputs=[force_b, torque_b, poses], + inputs=[force_in, torque_in, poses, is_global], outputs=[self._wrench_buf], device=self._device, ) @@ -1193,8 +1192,8 @@ def _create_buffers(self) -> None: # The fused LINK_WRENCH binding writes from a single (N, B, 9) buffer. self._wrench_buf = wp.zeros((N, B, 9), dtype=wp.float32, device=self._device) - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # set information about rigid body into data self._data.body_names = self._body_names_list diff --git a/source/isaaclab_ov/test/assets/test_articulation.py b/source/isaaclab_ov/test/assets/test_articulation.py index 0652fad2f06..e5bdb089079 100644 --- a/source/isaaclab_ov/test/assets/test_articulation.py +++ b/source/isaaclab_ov/test/assets/test_articulation.py @@ -2479,7 +2479,7 @@ def test_external_force_on_multiple_bodies_at_position(sim, num_articulations, d num_articulations: Number of articulations to test """ articulation_cfg = generate_articulation_cfg(articulation_type="anymal") - articulation, _ = generate_articulation(articulation_cfg, num_articulations, device=sim.device) + articulation, translations = generate_articulation(articulation_cfg, num_articulations, device=sim.device) # Play the simulator sim.reset() @@ -2499,8 +2499,10 @@ def test_external_force_on_multiple_bodies_at_position(sim, num_articulations, d # Now we are ready! for i in range(5): - # reset root state - articulation.write_root_pose_to_sim_index(root_pose=articulation.data.default_root_pose.torch.clone()) + # Preserve environment separation when converting the default root pose to world coordinates. + root_pose = articulation.data.default_root_pose.torch.clone() + root_pose[:, :3] += translations + articulation.write_root_pose_to_sim_index(root_pose=root_pose) articulation.write_root_velocity_to_sim_index(root_velocity=articulation.data.default_root_vel.torch.clone()) # reset dof state joint_pos, joint_vel = ( diff --git a/source/isaaclab_physx/changelog.d/wrench-fast-paths.rst b/source/isaaclab_physx/changelog.d/wrench-fast-paths.rst new file mode 100644 index 00000000000..97d67a3b7f6 --- /dev/null +++ b/source/isaaclab_physx/changelog.d/wrench-fast-paths.rst @@ -0,0 +1,7 @@ +Changed +^^^^^^^ + +* Changed the external-wrench writers to submit through + :meth:`~isaaclab.utils.wrench_composer.WrenchComposer.get_forces_and_torques`, so a wrench that is + already local-frame, or already global-frame at the center of mass, is sent to PhysX without + reading the body transforms. diff --git a/source/isaaclab_physx/isaaclab_physx/assets/articulation/articulation.py b/source/isaaclab_physx/isaaclab_physx/assets/articulation/articulation.py index eac8ca94050..e1950a8ab51 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/articulation/articulation.py +++ b/source/isaaclab_physx/isaaclab_physx/assets/articulation/articulation.py @@ -251,7 +251,7 @@ def write_data_to_sim(self): composer.add_raw_buffers_from(self._permanent_wrench_composer) else: composer = self._permanent_wrench_composer - composer.compose_to_body_frame() + force_user, torque_user, is_global = composer.get_forces_and_torques() if self.data.has_body_ordering: force_backend = self._body_wrench_force_backend torque_backend = self._body_wrench_torque_backend @@ -259,8 +259,8 @@ def write_data_to_sim(self): ordering_kernels.reorder_body_wrench_user_to_backend, dim=(self.num_instances, self.num_bodies), inputs=[ - composer.out_force_b.warp, - composer.out_torque_b.warp, + force_user, + torque_user, self.data.body_ordering.backend_to_user, ], outputs=[force_backend, torque_backend], @@ -269,14 +269,13 @@ def write_data_to_sim(self): force_data = force_backend torque_data = torque_backend else: - force_data = composer.out_force_b.warp - torque_data = composer.out_torque_b.warp + force_data, torque_data = force_user, torque_user self.root_view.apply_forces_and_torques_at_position( force_data=force_data.flatten().view(wp.float32), torque_data=torque_data.flatten().view(wp.float32), position_data=None, indices=self._ALL_INDICES, - is_global=False, + is_global=is_global, ) if self._instantaneous_wrench_composer.active: self._instantaneous_wrench_composer.reset() @@ -4003,8 +4002,8 @@ def _create_buffers(self): self._cpu_env_ids_views: dict[int, wp.array] = {} # external wrench composer - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # asset named data self._joint_pos_target_backend: wp.array | None = None diff --git a/source/isaaclab_physx/isaaclab_physx/assets/rigid_object/rigid_object.py b/source/isaaclab_physx/isaaclab_physx/assets/rigid_object/rigid_object.py index 23951536c64..c190b516cac 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/rigid_object/rigid_object.py +++ b/source/isaaclab_physx/isaaclab_physx/assets/rigid_object/rigid_object.py @@ -153,19 +153,15 @@ def write_data_to_sim(self) -> None: if self._instantaneous_wrench_composer.active: composer = self._instantaneous_wrench_composer composer.add_raw_buffers_from(self._permanent_wrench_composer) - get_force_data = self._get_inst_wrench_force_f32 - get_torque_data = self._get_inst_wrench_torque_f32 else: composer = self._permanent_wrench_composer - get_force_data = self._get_perm_wrench_force_f32 - get_torque_data = self._get_perm_wrench_torque_f32 - composer.compose_to_body_frame() + force_user, torque_user, is_global = composer.get_forces_and_torques() self.root_view.apply_forces_and_torques_at_position( - force_data=get_force_data(), - torque_data=get_torque_data(), + force_data=force_user.flatten().view(wp.float32), + torque_data=torque_user.flatten().view(wp.float32), position_data=None, indices=self._ALL_INDICES, - is_global=False, + is_global=is_global, ) self._instantaneous_wrench_composer.reset() @@ -1029,8 +1025,8 @@ def _create_buffers(self): self._ALL_BODY_INDICES = wp.array(np.arange(self.num_bodies, dtype=np.int32), device=self.device) # external wrench composer - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # set information about rigid body into data self._data.body_names = self.body_names @@ -1040,11 +1036,6 @@ def _create_buffers(self): # Reset to None each time _create_buffers runs (during initialization). self._root_link_pose_w_f32: wp.array | None = None self._root_com_vel_w_f32: wp.array | None = None - # Cached wrench views for write_data_to_sim - self._inst_wrench_force_f32: wp.array | None = None - self._inst_wrench_torque_f32: wp.array | None = None - self._perm_wrench_force_f32: wp.array | None = None - self._perm_wrench_torque_f32: wp.array | None = None # Pre-allocated pinned CPU buffers for PhysX TensorAPI writes. # PhysX requires CPU arrays for "model" property updates (masses, coms, inertias). @@ -1156,34 +1147,6 @@ def _get_root_com_vel_w_f32(self) -> wp.array: self._root_com_vel_w_f32 = self.data._root_com_vel_w.data.view(wp.float32) return self._root_com_vel_w_f32 - def _get_inst_wrench_force_f32(self) -> wp.array: - """Get a cached flattened float32 view of instantaneous wrench force. Invalidated in ``_create_buffers``.""" - if self._inst_wrench_force_f32 is None: - self._inst_wrench_force_f32 = self._instantaneous_wrench_composer.out_force_b.warp.flatten().view( - wp.float32 - ) - return self._inst_wrench_force_f32 - - def _get_inst_wrench_torque_f32(self) -> wp.array: - """Get a cached flattened float32 view of instantaneous wrench torque. Invalidated in ``_create_buffers``.""" - if self._inst_wrench_torque_f32 is None: - self._inst_wrench_torque_f32 = self._instantaneous_wrench_composer.out_torque_b.warp.flatten().view( - wp.float32 - ) - return self._inst_wrench_torque_f32 - - def _get_perm_wrench_force_f32(self) -> wp.array: - """Get a cached flattened float32 view of permanent wrench force. Invalidated in ``_create_buffers``.""" - if self._perm_wrench_force_f32 is None: - self._perm_wrench_force_f32 = self._permanent_wrench_composer.out_force_b.warp.flatten().view(wp.float32) - return self._perm_wrench_force_f32 - - def _get_perm_wrench_torque_f32(self) -> wp.array: - """Get a cached flattened float32 view of permanent wrench torque. Invalidated in ``_create_buffers``.""" - if self._perm_wrench_torque_f32 is None: - self._perm_wrench_torque_f32 = self._permanent_wrench_composer.out_torque_b.warp.flatten().view(wp.float32) - return self._perm_wrench_torque_f32 - def _sim_env_ids_view(self, count: int) -> wp.array: """Return a cached prefix of the simulator-index scratch buffer.""" if count not in self._sim_env_ids_views: diff --git a/source/isaaclab_physx/isaaclab_physx/assets/rigid_object_collection/rigid_object_collection.py b/source/isaaclab_physx/isaaclab_physx/assets/rigid_object_collection/rigid_object_collection.py index 714a2bd17ad..3f8afeb8aef 100644 --- a/source/isaaclab_physx/isaaclab_physx/assets/rigid_object_collection/rigid_object_collection.py +++ b/source/isaaclab_physx/isaaclab_physx/assets/rigid_object_collection/rigid_object_collection.py @@ -191,17 +191,15 @@ def write_data_to_sim(self) -> None: composer.add_raw_buffers_from(self._permanent_wrench_composer) else: composer = self._permanent_wrench_composer - composer.compose_to_body_frame() + force_user, torque_user, is_global = composer.get_forces_and_torques() self.root_view.apply_forces_and_torques_at_position( - force_data=self.reshape_data_to_view_2d(composer.out_force_b.warp, device=self.device).view(wp.float32), - torque_data=self.reshape_data_to_view_2d(composer.out_torque_b.warp, device=self.device).view( - wp.float32 - ), + force_data=self.reshape_data_to_view_2d(force_user, device=self.device).view(wp.float32), + torque_data=self.reshape_data_to_view_2d(torque_user, device=self.device).view(wp.float32), position_data=None, indices=self._env_body_ids_to_view_ids( self._ALL_ENV_INDICES, self._ALL_BODY_INDICES, device=self.device ), - is_global=False, + is_global=is_global, ) self._instantaneous_wrench_composer.reset() @@ -1394,8 +1392,8 @@ def _create_buffers(self): self._cpu_view_ids_views: dict[int, wp.array] = {} # external wrench composer - self._instantaneous_wrench_composer = WrenchComposer(self) - self._permanent_wrench_composer = WrenchComposer(self) + self._instantaneous_wrench_composer = WrenchComposer(self, supports_world_at_com=True) + self._permanent_wrench_composer = WrenchComposer(self, supports_world_at_com=True) # set information about rigid body into data self._data.body_names = self.body_names diff --git a/source/isaaclab_physx/isaaclab_physx/benchmark/assets/runtime.py b/source/isaaclab_physx/isaaclab_physx/benchmark/assets/runtime.py index afb7e7e8637..6e7577734cb 100644 --- a/source/isaaclab_physx/isaaclab_physx/benchmark/assets/runtime.py +++ b/source/isaaclab_physx/isaaclab_physx/benchmark/assets/runtime.py @@ -122,8 +122,8 @@ def create_test_articulation( object.__setattr__(articulation, "_data", data) # Create mock wrench composers (pass articulation which has num_instances, num_bodies, device properties) - mock_inst_wrench = WrenchComposer(articulation) - mock_perm_wrench = WrenchComposer(articulation) + mock_inst_wrench = WrenchComposer(articulation, supports_world_at_com=True) + mock_perm_wrench = WrenchComposer(articulation, supports_world_at_com=True) object.__setattr__(articulation, "_instantaneous_wrench_composer", mock_inst_wrench) object.__setattr__(articulation, "_permanent_wrench_composer", mock_perm_wrench) From 0fcc8f74b730666049ae350e415abdeca133c946 Mon Sep 17 00:00:00 2001 From: vidurv-nvidia Date: Wed, 23 Sep 2026 20:02:07 -0700 Subject: [PATCH 2/2] Migrate remaining legacy schema cfg call sites to fragments (#7971) Backport the merged change unchanged to release/3.0.0. (cherry picked from commit 1370bb6f7ce1ab57b8939dd9e9b8bfde6f425000) --- .../benchmarks/benchmark_newton_raycast.py | 4 +- .../benchmarks/benchmark_view_comparison.py | 18 ++--- .../benchmarks/benchmark_xform_prim_view.py | 6 +- scripts/demos/bin_packing.py | 32 +++++---- scripts/demos/haply_teleoperation.py | 6 +- scripts/demos/mpm/newton_mpm_granular.py | 9 +-- .../demos/mpm/newton_mpm_twoway_coupling.py | 9 +-- scripts/demos/mpm/snowball_smash.py | 10 +-- scripts/demos/mpm/teapot_fill.py | 68 +++++++++++-------- scripts/demos/multi_asset.py | 22 +++--- scripts/demos/newton_viewer_dominoes.py | 14 ++-- scripts/demos/pick_and_place.py | 6 +- scripts/demos/sensors/contact_sensor.py | 6 +- .../demos/sensors/frame_transformer_sensor.py | 6 +- scripts/demos/sensors/multi_mesh_raycaster.py | 11 ++- .../sensors/multi_mesh_raycaster_camera.py | 9 ++- .../sensors/newton_raycast_heightfield.py | 4 +- .../sensors/newton_raycast_moving_geometry.py | 14 ++-- scripts/demos/sensors/ppisp_camera.py | 6 +- scripts/demos/sensors/ppisp_camera_ovrtx.py | 6 +- scripts/demos/sensors/tacsl_sensor.py | 38 +++++------ scripts/tools/convert_instanceable.py | 6 +- scripts/tools/convert_mesh.py | 32 +++++---- scripts/tutorials/01_assets/add_new_robot.py | 13 ++-- .../tutorials/01_assets/run_rigid_object.py | 6 +- .../tutorials/03_envs/create_cube_base_env.py | 5 +- .../tutorials/04_sensors/run_ray_caster.py | 6 +- .../tutorials/04_sensors/run_usd_camera.py | 6 +- scripts/tutorials/05_controllers/run_osc.py | 4 +- ...idurv-migrate-legacy-source-remaining.skip | 1 + .../test_camera_pose_write_across_backends.py | 6 +- .../test_first_frame_textured_rendering.py | 7 +- ...idurv-migrate-legacy-source-remaining.skip | 1 + .../test/assets/test_cable_object.py | 6 +- .../test/assets/test_mpm_object.py | 13 ++-- .../test_camera_opencv_distortion_newton.py | 6 +- ...idurv-migrate-legacy-source-remaining.skip | 1 + .../test/physics/test_ovphysx_gravity.py | 4 +- ...vidurv-migrate-legacy-source-remaining.rst | 12 ++++ .../contrib/nist/factory_assets_cfg.py | 4 +- .../ur10_particle_push_env_cfg.py | 36 +++++----- .../core/reach/reach_env_cfg.py | 2 +- 42 files changed, 266 insertions(+), 215 deletions(-) create mode 100644 source/isaaclab/changelog.d/vidurv-migrate-legacy-source-remaining.skip create mode 100644 source/isaaclab_newton/changelog.d/vidurv-migrate-legacy-source-remaining.skip create mode 100644 source/isaaclab_ov/changelog.d/vidurv-migrate-legacy-source-remaining.skip create mode 100644 source/isaaclab_tasks/changelog.d/vidurv-migrate-legacy-source-remaining.rst diff --git a/scripts/benchmarks/benchmark_newton_raycast.py b/scripts/benchmarks/benchmark_newton_raycast.py index a96cd2365cc..cd24441d4ea 100644 --- a/scripts/benchmarks/benchmark_newton_raycast.py +++ b/scripts/benchmarks/benchmark_newton_raycast.py @@ -81,8 +81,8 @@ class RaycastBenchSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/SensorBody", spawn=sim_utils.CuboidCfg( size=(0.1, 0.1, 0.1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)), ) diff --git a/scripts/benchmarks/benchmark_view_comparison.py b/scripts/benchmarks/benchmark_view_comparison.py index 868f19115e3..e40e7af8c33 100644 --- a/scripts/benchmarks/benchmark_view_comparison.py +++ b/scripts/benchmarks/benchmark_view_comparison.py @@ -101,9 +101,9 @@ def benchmark_usd_or_fabric(view_type: str, num_iterations: int) -> dict[str, fl object_cfg = sim_utils.ConeCfg( radius=0.15, height=0.5, - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), ) for i in range(args_cli.num_envs): @@ -151,9 +151,9 @@ class _SceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(0.2, 0.2, 0.2), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)), ) @@ -207,9 +207,9 @@ def benchmark_physx(num_iterations: int) -> dict[str, float]: object_cfg = sim_utils.ConeCfg( radius=0.15, height=0.5, - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), ) for i in range(args_cli.num_envs): diff --git a/scripts/benchmarks/benchmark_xform_prim_view.py b/scripts/benchmarks/benchmark_xform_prim_view.py index 13fd08530c4..1f2006ed84a 100644 --- a/scripts/benchmarks/benchmark_xform_prim_view.py +++ b/scripts/benchmarks/benchmark_xform_prim_view.py @@ -63,9 +63,9 @@ class _NewtonSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Object", spawn=sim_utils.CuboidCfg( size=(0.2, 0.2, 0.2), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.0)), ) diff --git a/scripts/demos/bin_packing.py b/scripts/demos/bin_packing.py index 3f8cf063e2a..c03dbfc8c5f 100644 --- a/scripts/demos/bin_packing.py +++ b/scripts/demos/bin_packing.py @@ -55,6 +55,7 @@ import torch import warp as wp +from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg import isaaclab.sim as sim_utils import isaaclab.utils.math as math_utils @@ -148,10 +149,10 @@ def spawn_grocery( ) -> Usd.Prim: """Spawn a visual YCB model and author the physics schemas it does not ship with. - These models carry no physics schemas, so the ``rigid_props`` and ``collision_props`` of a - spawner configuration would find nothing to modify. This spawner defines each schema - instead: the rigid body on the asset root and a convex-hull collider on the mesh, matching - how the physics-ready YCB models are authored. + These models carry no physics schemas, so the ``collision_props`` of a spawner configuration + would author the collider on the asset root rather than on its meshes. This spawner defines + each schema instead: the rigid body on the asset root and a convex-hull collider on the mesh, + matching how the physics-ready YCB models are authored. Args: prim_path: Prim path to spawn the asset at. @@ -167,12 +168,16 @@ def spawn_grocery( root_path = prim.GetPath().pathString for mesh in sim_utils.get_all_matching_child_prims(root_path, lambda child: child.GetTypeName() == "Mesh"): mesh_path = mesh.GetPath().pathString - schemas.define_collision_properties(mesh_path, schemas.CollisionBaseCfg(collision_enabled=True)) - schemas.define_mesh_collision_properties( - mesh_path, schemas.MeshCollisionBaseCfg(mesh_approximation_name="convexHull") + schemas.apply_collision_properties( + mesh_path, [schemas.UsdPhysicsCollisionCfg(collision_enabled=True)], create_if_missing=True ) - schemas.define_rigid_body_properties(root_path, sim_utils.RigidBodyPropertiesCfg(solver_position_iteration_count=4)) - schemas.define_mass_properties(root_path, schemas.MassPropertiesCfg(mass=GROCERY_MASS)) + schemas.apply_mesh_collision_properties( + mesh_path, [schemas.UsdPhysicsMeshCollisionCfg(mesh_approximation_name="convexHull")] + ) + schemas.apply_rigid_body_properties( + root_path, [PhysxRigidBodyCfg(solver_position_iteration_count=4)], create_if_missing=True + ) + schemas.apply_mass_properties(root_path, [schemas.MassCfg(mass=GROCERY_MASS)], create_if_missing=True) return prim @@ -240,10 +245,11 @@ class BinPackingSceneCfg(InteractiveSceneCfg): spawn=sim_utils.UsdFileCfg( usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/KLT_Bin/small_KLT.usd", scale=(2.0, 2.0, 2.0), - rigid_props=sim_utils.RigidBodyPropertiesCfg( - solver_position_iteration_count=4, solver_velocity_iteration_count=0, kinematic_enabled=True - ), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=[ + sim_utils.UsdPhysicsRigidBodyCfg(kinematic_enabled=True), + PhysxRigidBodyCfg(solver_position_iteration_count=4, solver_velocity_iteration_count=0), + ], + mass_props=sim_utils.MassCfg(mass=1.0), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 0.15)), ) diff --git a/scripts/demos/haply_teleoperation.py b/scripts/demos/haply_teleoperation.py index b9c6bdc668b..eb88f728333 100644 --- a/scripts/demos/haply_teleoperation.py +++ b/scripts/demos/haply_teleoperation.py @@ -175,9 +175,9 @@ class FrankaHaplySceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(0.06, 0.06, 0.06), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=0.5), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=0.5), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=0.5, dynamic_friction=0.5), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.8, 0.2), metallic=0.2), ), diff --git a/scripts/demos/mpm/newton_mpm_granular.py b/scripts/demos/mpm/newton_mpm_granular.py index a7c009b5e51..844f49f2444 100644 --- a/scripts/demos/mpm/newton_mpm_granular.py +++ b/scripts/demos/mpm/newton_mpm_granular.py @@ -115,6 +115,7 @@ def create_sim_cfg(): def create_scene_cfg(): """Create an Isaac Lab scene config using declarative assets.""" from isaaclab_newton.assets import MPMObjectCfg + from isaaclab_newton.sim.schemas import NewtonCollisionCfg from isaaclab_newton.sim.spawners.mpm import MPMGridCfg import isaaclab.sim as sim_utils @@ -127,10 +128,10 @@ def collider_cfg(prim_path: str, center, half_extents, orientation, friction: fl prim_path=prim_path, spawn=sim_utils.CuboidCfg( size=(2.0 * half_extents[0], 2.0 * half_extents[1], 2.0 * half_extents[2]), - collision_props=sim_utils.NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=COLLIDER_MARGIN, - ), + collision_props=[ + sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=COLLIDER_MARGIN), + ], physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=friction, dynamic_friction=friction, diff --git a/scripts/demos/mpm/newton_mpm_twoway_coupling.py b/scripts/demos/mpm/newton_mpm_twoway_coupling.py index af648f19828..d841b424de3 100644 --- a/scripts/demos/mpm/newton_mpm_twoway_coupling.py +++ b/scripts/demos/mpm/newton_mpm_twoway_coupling.py @@ -161,6 +161,7 @@ def create_sim_cfg(): def create_scene_cfg(): """Create the declarative rigid-sphere and granular-bath scene.""" from isaaclab_newton.assets.mpm_object import MPMObjectCfg + from isaaclab_newton.sim.schemas import NewtonCollisionCfg from isaaclab_newton.sim.spawners.mpm import MPMGridCfg, MPMParticleMaterialCfg from isaaclab.assets import AssetBaseCfg, RigidObjectCfg, RigidObjectCollectionCfg @@ -183,7 +184,7 @@ def bath_collider( else sim_utils.spawn_cuboid ), size=size, - collision_props=sim_utils.NewtonCollisionPropertiesCfg(contact_margin=COLLIDER_MARGIN), + collision_props=NewtonCollisionCfg(contact_margin=COLLIDER_MARGIN), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=0.6, dynamic_friction=0.6, @@ -203,9 +204,9 @@ def bath_collider( color=SPHERE_COLORS[index], ), radius=SPHERE_RADIUS, - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=SPHERE_MASS), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=SPHERE_MASS), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=0.5, dynamic_friction=0.5, diff --git a/scripts/demos/mpm/snowball_smash.py b/scripts/demos/mpm/snowball_smash.py index 0be3833d4ae..e908ab22ba8 100644 --- a/scripts/demos/mpm/snowball_smash.py +++ b/scripts/demos/mpm/snowball_smash.py @@ -231,9 +231,9 @@ def crate_cfg(index: int) -> RigidObjectCfg: prim_path=f"{{ENV_REGEX_NS}}/Crate_{index}", spawn=sim_utils.CuboidCfg( size=(CRATE_SIZE, CRATE_SIZE, CRATE_SIZE), - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg(rigid_body_enabled=True), - mass_props=sim_utils.MassPropertiesCfg(mass=args_cli.crate_mass), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(collision_enabled=True), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), + mass_props=sim_utils.MassCfg(mass=args_cli.crate_mass), + collision_props=sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=CRATE_FRICTION, dynamic_friction=CRATE_FRICTION, @@ -289,11 +289,11 @@ class SnowballSmashSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/MPMGround", spawn=sim_utils.CuboidCfg( size=(12.0, 12.0, 0.10), - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg( + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg( rigid_body_enabled=True, kinematic_enabled=True, ), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(collision_enabled=True), + collision_props=sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=CRATE_FRICTION, dynamic_friction=CRATE_FRICTION, diff --git a/scripts/demos/mpm/teapot_fill.py b/scripts/demos/mpm/teapot_fill.py index a3055a6b6a8..4ac192676dd 100644 --- a/scripts/demos/mpm/teapot_fill.py +++ b/scripts/demos/mpm/teapot_fill.py @@ -433,12 +433,20 @@ def spawn_demo_mesh( if not cfg.visible: UsdGeom.Imageable(stage.GetPrimAtPath(mesh_prim_path)).MakeInvisible() + def as_fragments(value) -> list: + # the schema slots accept a bare fragment or a list of fragments + return list(value) if isinstance(value, (list, tuple)) else [value] + if cfg.collision_props is not None: - schemas.define_collision_properties(mesh_prim_path, cfg.collision_props, stage=stage) + schemas.apply_collision_properties( + mesh_prim_path, as_fragments(cfg.collision_props), create_if_missing=True, stage=stage + ) if cfg.mesh_collision_props is not None: - schemas.define_mesh_collision_properties(mesh_prim_path, cfg.mesh_collision_props, stage=stage) + schemas.apply_mesh_collision_properties(mesh_prim_path, as_fragments(cfg.mesh_collision_props), stage=stage) if cfg.rigid_props is not None: - schemas.define_rigid_body_properties(prim_path, cfg.rigid_props, stage=stage) + schemas.apply_rigid_body_properties( + prim_path, as_fragments(cfg.rigid_props), create_if_missing=True, stage=stage + ) if cfg.visual_material is not None: material_path = cfg.visual_material_path @@ -585,7 +593,9 @@ def create_sim_cfg(): def create_scene_cfg(container_usd: str, island_usd: str | None, bowl_usd: str | None): """Create the teapot-fill scene using declarative Isaac Lab assets.""" from isaaclab_newton.assets import MPMObjectCfg + from isaaclab_newton.sim.schemas import NewtonCollisionCfg from isaaclab_newton.sim.spawners.mpm import MPMParticleMaterialCfg, MPMPointsCfg + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg import isaaclab.sim as sim_utils from isaaclab.assets import AssetBaseCfg, RigidObjectCfg @@ -605,7 +615,12 @@ class DemoMeshCfg(sim_utils.MeshCfg): func: Callable | str = clone(spawn_demo_mesh) vertices: list[list[float]] = MISSING faces: list[list[int]] = MISSING - mesh_collision_props: sim_utils.NewtonMeshCollisionPropertiesCfg | None = None + mesh_collision_props: sim_utils.UsdPhysicsMeshCollisionCfg | None = None + + # The visual assets only disable rigid bodies and colliders they already carry; the explicit + # target mappings keep an asset without physics from gaining a body on its spawn prim. + disable_asset_rigid_bodies = {"(/.*)?": [sim_utils.UsdPhysicsRigidBodyCfg(rigid_body_enabled=False)]} + disable_asset_colliders = {"(/.*)?": [sim_utils.UsdPhysicsCollisionCfg(collision_enabled=False)]} island_cfg = None if island_usd is not None: @@ -615,8 +630,8 @@ class DemoMeshCfg(sim_utils.MeshCfg): usd_path=island_usd, variants={"Physics": "none"}, make_uninstanceable=True, - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg(rigid_body_enabled=False), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(collision_enabled=False), + rigid_props=disable_asset_rigid_bodies, + collision_props=disable_asset_colliders, ), init_state=AssetBaseCfg.InitialStateCfg(rot=TABLE_ORIENTATION), ) @@ -630,8 +645,8 @@ class DemoMeshCfg(sim_utils.MeshCfg): scale=(BOWL_SCALE,) * 3, variants={"Physics": "none"}, make_uninstanceable=True, - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg(rigid_body_enabled=False), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(collision_enabled=False), + rigid_props=disable_asset_rigid_bodies, + collision_props=disable_asset_colliders, ), init_state=AssetBaseCfg.InitialStateCfg(pos=BOWL_BASE_POS), ) @@ -646,10 +661,10 @@ class TeapotFillSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/TabletopCollider", spawn=sim_utils.CuboidCfg( size=(2.0 * TABLE_HALF_EXTENTS[0], 2.0 * TABLE_HALF_EXTENTS[1], 2.0 * TABLE_HALF_EXTENTS[2]), - collision_props=sim_utils.NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=COLLIDER_MARGIN, - ), + collision_props=[ + sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=COLLIDER_MARGIN), + ], physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=TABLE_FRICTION, dynamic_friction=TABLE_FRICTION, @@ -674,11 +689,11 @@ class TeapotFillSceneCfg(InteractiveSceneCfg): spawn=DemoMeshCfg( vertices=(BOWL_SCALE * bowl_vertices).tolist(), faces=bowl_faces.tolist(), - collision_props=sim_utils.NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=COLLIDER_MARGIN, - ), - mesh_collision_props=sim_utils.NewtonMeshCollisionPropertiesCfg(mesh_approximation_name="none"), + collision_props=[ + sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=COLLIDER_MARGIN), + ], + mesh_collision_props=sim_utils.UsdPhysicsMeshCollisionCfg(mesh_approximation_name="none"), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=BOWL_FRICTION, dynamic_friction=BOWL_FRICTION, @@ -701,16 +716,15 @@ class TeapotFillSceneCfg(InteractiveSceneCfg): spawn=DemoMeshCfg( vertices=container_vertices.tolist(), faces=container_faces.tolist(), - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg( - rigid_body_enabled=True, - kinematic_enabled=True, - disable_gravity=True, - ), - collision_props=sim_utils.NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=COLLIDER_MARGIN, - ), - mesh_collision_props=sim_utils.NewtonMeshCollisionPropertiesCfg(mesh_approximation_name="none"), + rigid_props=[ + sim_utils.UsdPhysicsRigidBodyCfg(rigid_body_enabled=True, kinematic_enabled=True), + PhysxRigidBodyCfg(disable_gravity=True), + ], + collision_props=[ + sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=COLLIDER_MARGIN), + ], + mesh_collision_props=sim_utils.UsdPhysicsMeshCollisionCfg(mesh_approximation_name="none"), physics_material=sim_utils.NewtonMaterialPropertiesCfg( static_friction=CONTAINER_FRICTION, dynamic_friction=CONTAINER_FRICTION, diff --git a/scripts/demos/multi_asset.py b/scripts/demos/multi_asset.py index 98e01ce578c..d7ee5ab2a6c 100644 --- a/scripts/demos/multi_asset.py +++ b/scripts/demos/multi_asset.py @@ -45,6 +45,9 @@ # parse the arguments args_cli = parser.parse_args() +from isaaclab_newton.sim.schemas import NewtonArticulationCfg +from isaaclab_physx.sim.schemas import PhysxArticulationCfg, PhysxRigidBodyCfg + import isaaclab.sim as sim_utils ## @@ -71,11 +74,9 @@ GOLD_MATERIAL = {"visual_material": sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.75, 0.0), metallic=0.2)} PURPLE_MATERIAL = {"visual_material": sim_utils.PreviewSurfaceCfg(diffuse_color=(0.5, 0.0, 1.0), metallic=0.2)} OBJECT_PHYSICS = { - "rigid_props": sim_utils.RigidBodyPropertiesCfg( - solver_position_iteration_count=4, solver_velocity_iteration_count=0 - ), - "mass_props": sim_utils.MassPropertiesCfg(mass=1.0), - "collision_props": sim_utils.CollisionPropertiesCfg(), + "rigid_props": PhysxRigidBodyCfg(solver_position_iteration_count=4, solver_velocity_iteration_count=0), + "mass_props": sim_utils.MassCfg(mass=1.0), + "collision_props": sim_utils.UsdPhysicsCollisionCfg(), } ## @@ -146,7 +147,7 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-D/anymal_d.usd", ], random_choice=False, - rigid_props=sim_utils.RigidBodyPropertiesCfg( + rigid_props=PhysxRigidBodyCfg( disable_gravity=False, retain_accelerations=False, linear_damping=0.0, @@ -155,9 +156,12 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): max_angular_velocity=1000.0, max_depenetration_velocity=1.0, ), - articulation_props=sim_utils.ArticulationRootPropertiesCfg( - enabled_self_collisions=True, solver_position_iteration_count=4, solver_velocity_iteration_count=0 - ), + articulation_props=[ + PhysxArticulationCfg( + enabled_self_collisions=True, solver_position_iteration_count=4, solver_velocity_iteration_count=0 + ), + NewtonArticulationCfg(self_collision_enabled=True), + ], activate_contact_sensors=True, ), init_state=ArticulationCfg.InitialStateCfg( diff --git a/scripts/demos/newton_viewer_dominoes.py b/scripts/demos/newton_viewer_dominoes.py index d0c4275dc25..92adf888df6 100644 --- a/scripts/demos/newton_viewer_dominoes.py +++ b/scripts/demos/newton_viewer_dominoes.py @@ -56,9 +56,9 @@ def _domino_cfg(position: tuple[float, float, float], orientation: tuple[float, prim_path="", spawn=sim_utils.CuboidCfg( size=DOMINO_SIZE, - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(density=580.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(density=580.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg( static_friction=1.0, dynamic_friction=1.0, @@ -76,9 +76,9 @@ def _trigger_cfg() -> RigidObjectCfg: prim_path="/World/Dominoes/Trigger", spawn=sim_utils.CuboidCfg( size=(DOMINO_SIZE[1], LOGO_FOOTPRINT[1], DOMINO_SIZE[2]), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(density=20.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(density=20.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg( static_friction=1.0, dynamic_friction=1.0, @@ -97,7 +97,7 @@ class DominoSceneCfg(InteractiveSceneCfg): prim_path="/World/Floor", spawn=sim_utils.CuboidCfg( size=(LOGO_FOOTPRINT[0] + 4.0, LOGO_FOOTPRINT[1] + 4.0, 0.10), - collision_props=sim_utils.CollisionPropertiesCfg(), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg( static_friction=1.0, dynamic_friction=1.0, diff --git a/scripts/demos/pick_and_place.py b/scripts/demos/pick_and_place.py index e240a00faba..919894a4c76 100644 --- a/scripts/demos/pick_and_place.py +++ b/scripts/demos/pick_and_place.py @@ -59,9 +59,9 @@ class PickAndPlaceSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Robot/Cube", spawn=sim_utils.CuboidCfg( size=(0.4, 0.4, 0.4), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.8, 0.0, 0.8)), ), init_state=RigidObjectCfg.InitialStateCfg(), diff --git a/scripts/demos/sensors/contact_sensor.py b/scripts/demos/sensors/contact_sensor.py index 23e0a64711c..5e80727c0b5 100644 --- a/scripts/demos/sensors/contact_sensor.py +++ b/scripts/demos/sensors/contact_sensor.py @@ -66,9 +66,9 @@ class ContactSensorSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(0.5, 0.5, 0.1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=100.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=100.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=1.0), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), ), diff --git a/scripts/demos/sensors/frame_transformer_sensor.py b/scripts/demos/sensors/frame_transformer_sensor.py index 6d9f6d6fb16..2c085ff61df 100644 --- a/scripts/demos/sensors/frame_transformer_sensor.py +++ b/scripts/demos/sensors/frame_transformer_sensor.py @@ -63,9 +63,9 @@ class FrameTransformerSensorSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(1, 1, 1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=100.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=100.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg(static_friction=1.0), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), ), diff --git a/scripts/demos/sensors/multi_mesh_raycaster.py b/scripts/demos/sensors/multi_mesh_raycaster.py index 9b416dd6807..c6fbd83c32f 100644 --- a/scripts/demos/sensors/multi_mesh_raycaster.py +++ b/scripts/demos/sensors/multi_mesh_raycaster.py @@ -63,6 +63,7 @@ import random import torch +from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg import isaaclab.sim as sim_utils from isaaclab.assets import AssetBaseCfg, RigidObjectCfg @@ -86,7 +87,7 @@ if args_cli.flat_ground: ground_spawn_cfg = sim_utils.MeshCuboidCfg( size=(20.0, 20.0, 0.1), - collision_props=sim_utils.CollisionPropertiesCfg(), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ) ground_init_state = AssetBaseCfg.InitialStateCfg(pos=(0.0, 0.0, -0.05)) else: @@ -184,11 +185,9 @@ spawn=sim_utils.MultiAssetSpawnerCfg( assets_cfg=object_assets_cfg, random_choice=True, - rigid_props=sim_utils.RigidBodyPropertiesCfg( - solver_position_iteration_count=4, solver_velocity_iteration_count=0 - ), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=PhysxRigidBodyCfg(solver_position_iteration_count=4, solver_velocity_iteration_count=0), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 2.0)), ) diff --git a/scripts/demos/sensors/multi_mesh_raycaster_camera.py b/scripts/demos/sensors/multi_mesh_raycaster_camera.py index 259c56c6df9..73b80be2f42 100644 --- a/scripts/demos/sensors/multi_mesh_raycaster_camera.py +++ b/scripts/demos/sensors/multi_mesh_raycaster_camera.py @@ -55,6 +55,7 @@ import random import torch +from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg from pxr import Gf, Sdf @@ -165,11 +166,9 @@ ), ], random_choice=True, - rigid_props=sim_utils.RigidBodyPropertiesCfg( - solver_position_iteration_count=4, solver_velocity_iteration_count=0 - ), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=PhysxRigidBodyCfg(solver_position_iteration_count=4, solver_velocity_iteration_count=0), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 2.0)), ) diff --git a/scripts/demos/sensors/newton_raycast_heightfield.py b/scripts/demos/sensors/newton_raycast_heightfield.py index f37ff0d87ef..553f844f82a 100644 --- a/scripts/demos/sensors/newton_raycast_heightfield.py +++ b/scripts/demos/sensors/newton_raycast_heightfield.py @@ -70,8 +70,8 @@ class HeightfieldSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/SensorBody", spawn=sim_utils.CuboidCfg( size=(0.4, 0.25, 0.1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.9, 0.6, 0.1)), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 1.5)), diff --git a/scripts/demos/sensors/newton_raycast_moving_geometry.py b/scripts/demos/sensors/newton_raycast_moving_geometry.py index 271e765fdbd..7940c1200b6 100644 --- a/scripts/demos/sensors/newton_raycast_moving_geometry.py +++ b/scripts/demos/sensors/newton_raycast_moving_geometry.py @@ -55,9 +55,9 @@ def _falling_box_cfg(index: int) -> RigidObjectCfg: prim_path=f"{{ENV_REGEX_NS}}/Box_{index}", spawn=sim_utils.CuboidCfg( size=(0.5, 0.5, 0.5), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.2, 0.4 + 0.2 * index, 0.9 - 0.3 * index)), ), init_state=RigidObjectCfg.InitialStateCfg(pos=BOX_DROP_POSITIONS[index]), @@ -78,8 +78,8 @@ class MovingGeometrySceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Bar", spawn=sim_utils.CuboidCfg( size=(3.5, 0.3, 0.3), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.9, 0.2, 0.5)), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 0.8)), @@ -89,8 +89,8 @@ class MovingGeometrySceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/SensorBody", spawn=sim_utils.CuboidCfg( size=(0.3, 0.3, 0.1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.9, 0.6, 0.1)), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 2.5)), diff --git a/scripts/demos/sensors/ppisp_camera.py b/scripts/demos/sensors/ppisp_camera.py index ed39ae2b1d4..83c31bd03ac 100644 --- a/scripts/demos/sensors/ppisp_camera.py +++ b/scripts/demos/sensors/ppisp_camera.py @@ -157,9 +157,9 @@ class PpispCameraSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Anchor", spawn=sim_utils.CuboidCfg( size=(0.01, 0.01, 0.01), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=0.001), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=0.001), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 0.0)), ), diff --git a/scripts/demos/sensors/ppisp_camera_ovrtx.py b/scripts/demos/sensors/ppisp_camera_ovrtx.py index 11f7422fee7..597aed18eed 100644 --- a/scripts/demos/sensors/ppisp_camera_ovrtx.py +++ b/scripts/demos/sensors/ppisp_camera_ovrtx.py @@ -143,9 +143,9 @@ class PpispCameraOvrtxSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Anchor", spawn=sim_utils.CuboidCfg( size=(0.01, 0.01, 0.01), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=0.001), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=0.001), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=sim_utils.RigidBodyMaterialCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 0.0)), ), diff --git a/scripts/demos/sensors/tacsl_sensor.py b/scripts/demos/sensors/tacsl_sensor.py index 1271daff025..4699ef54db5 100644 --- a/scripts/demos/sensors/tacsl_sensor.py +++ b/scripts/demos/sensors/tacsl_sensor.py @@ -83,11 +83,8 @@ """Rest everything follows.""" -from isaaclab_physx.sim.schemas import ( - PhysxArticulationRootPropertiesCfg, - PhysxCollisionPropertiesCfg, - PhysxRigidBodyPropertiesCfg, -) +from isaaclab_newton.sim.schemas import NewtonArticulationCfg +from isaaclab_physx.sim.schemas import PhysxArticulationCfg, PhysxCollisionCfg, PhysxRigidBodyCfg from isaaclab_physx.sim.spawners.materials import PhysxRigidBodyMaterialCfg import isaaclab.sim as sim_utils @@ -121,19 +118,22 @@ class TactileSensorsSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Robot", spawn=sim_utils.UsdFileWithCompliantContactCfg( usd_path=f"{ISAACLAB_NUCLEUS_DIR}/TacSL/gelsight_r15_finger/gelsight_r15_finger.usd", - rigid_props=PhysxRigidBodyPropertiesCfg( + rigid_props=PhysxRigidBodyCfg( disable_gravity=True, max_depenetration_velocity=5.0, ), compliant_contact_stiffness=args_cli.tactile_compliance_stiffness, compliant_contact_damping=args_cli.tactile_compliant_damping, physics_material_prim_path="elastomer", - articulation_props=PhysxArticulationRootPropertiesCfg( - enabled_self_collisions=False, - solver_position_iteration_count=12, - solver_velocity_iteration_count=1, - ), - collision_props=PhysxCollisionPropertiesCfg(contact_offset=0.001, rest_offset=-0.0005), + articulation_props=[ + PhysxArticulationCfg( + enabled_self_collisions=False, + solver_position_iteration_count=12, + solver_velocity_iteration_count=1, + ), + NewtonArticulationCfg(self_collision_enabled=False), + ], + collision_props=PhysxCollisionCfg(contact_offset=0.001, rest_offset=-0.0005), ), init_state=ArticulationCfg.InitialStateCfg( pos=(0.0, 0.0, 0.5), @@ -188,9 +188,9 @@ class CubeTactileSceneCfg(TactileSensorsSceneCfg): prim_path="{ENV_REGEX_NS}/contact_object", spawn=sim_utils.CuboidCfg( size=(0.01, 0.01, 0.01), - rigid_props=PhysxRigidBodyPropertiesCfg(disable_gravity=True), - mass_props=sim_utils.MassPropertiesCfg(mass=0.00327211), - collision_props=PhysxCollisionPropertiesCfg(), + rigid_props=PhysxRigidBodyCfg(disable_gravity=True), + mass_props=sim_utils.MassCfg(mass=0.00327211), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=PhysxRigidBodyMaterialCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.1, 0.1)), ), @@ -207,15 +207,15 @@ class NutTactileSceneCfg(TactileSensorsSceneCfg): prim_path="{ENV_REGEX_NS}/contact_object", spawn=sim_utils.UsdFileCfg( usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Factory/factory_nut_m16.usd", - rigid_props=PhysxRigidBodyPropertiesCfg( + rigid_props=PhysxRigidBodyCfg( disable_gravity=True, solver_position_iteration_count=12, solver_velocity_iteration_count=1, max_angular_velocity=180.0, ), - mass_props=sim_utils.MassPropertiesCfg(mass=0.1), - collision_props=PhysxCollisionPropertiesCfg(contact_offset=0.005, rest_offset=0), - articulation_props=PhysxArticulationRootPropertiesCfg(articulation_enabled=False), + mass_props=sim_utils.MassCfg(mass=0.1), + collision_props=PhysxCollisionCfg(contact_offset=0.005, rest_offset=0), + articulation_props=PhysxArticulationCfg(articulation_enabled=False), ), init_state=RigidObjectCfg.InitialStateCfg( pos=(0.0, 0.0 + 0.06776, 0.498), diff --git a/scripts/tools/convert_instanceable.py b/scripts/tools/convert_instanceable.py index 7713bdc728f..527fcc71e54 100644 --- a/scripts/tools/convert_instanceable.py +++ b/scripts/tools/convert_instanceable.py @@ -126,14 +126,14 @@ def main(): ): # Mass properties if args_cli.mass is not None: - mass_props = schemas_cfg.MassPropertiesCfg(mass=args_cli.mass) - rigid_props = schemas_cfg.RigidBodyPropertiesCfg() + mass_props = schemas_cfg.MassCfg(mass=args_cli.mass) + rigid_props = schemas_cfg.UsdPhysicsRigidBodyCfg() else: mass_props = None rigid_props = None # Collision properties - collision_props = schemas_cfg.CollisionPropertiesCfg( + collision_props = schemas_cfg.UsdPhysicsCollisionCfg( collision_enabled=args_cli.collision_approximation != "none" ) # Mesh converter call diff --git a/scripts/tools/convert_mesh.py b/scripts/tools/convert_mesh.py index 0493bc3f2ae..fd39f5c2de9 100644 --- a/scripts/tools/convert_mesh.py +++ b/scripts/tools/convert_mesh.py @@ -98,14 +98,16 @@ from isaaclab.utils.assets import check_file_path from isaaclab.utils.dict import print_dict +# Mesh-collision approximation token authored for each collision approximation choice. +# A triangle-mesh collider uses the "none" token (the mesh itself is the collider). collision_approximation_map = { - "convexDecomposition": schemas_cfg.ConvexDecompositionPropertiesCfg, - "convexHull": schemas_cfg.ConvexHullPropertiesCfg, - "triangleMesh": schemas_cfg.TriangleMeshPropertiesCfg, - "meshSimplification": schemas_cfg.TriangleMeshSimplificationPropertiesCfg, - "sdf": schemas_cfg.SDFMeshPropertiesCfg, - "boundingCube": schemas_cfg.BoundingCubePropertiesCfg, - "boundingSphere": schemas_cfg.BoundingSpherePropertiesCfg, + "convexDecomposition": "convexDecomposition", + "convexHull": "convexHull", + "triangleMesh": "none", + "meshSimplification": "meshSimplification", + "sdf": "sdf", + "boundingCube": "boundingCube", + "boundingSphere": "boundingSphere", "none": None, } @@ -125,24 +127,28 @@ def main(): # Mass properties if args_cli.mass is not None: - mass_props = schemas_cfg.MassPropertiesCfg(mass=args_cli.mass) - rigid_props = schemas_cfg.RigidBodyPropertiesCfg() + mass_props = schemas_cfg.MassCfg(mass=args_cli.mass) + rigid_props = schemas_cfg.UsdPhysicsRigidBodyCfg() else: mass_props = None rigid_props = None # Collision properties - collision_props = schemas_cfg.CollisionPropertiesCfg(collision_enabled=args_cli.collision_approximation != "none") + collision_props = schemas_cfg.UsdPhysicsCollisionCfg(collision_enabled=args_cli.collision_approximation != "none") # Create Mesh converter config - cfg_class = collision_approximation_map.get(args_cli.collision_approximation) - if cfg_class is None and args_cli.collision_approximation != "none": + approximation_name = collision_approximation_map.get(args_cli.collision_approximation) + if approximation_name is None and args_cli.collision_approximation != "none": valid_keys = ", ".join(sorted(collision_approximation_map.keys())) raise ValueError( f"Invalid collision approximation type '{args_cli.collision_approximation}'. " f"Valid options are: {valid_keys}." ) - collision_cfg = cfg_class() if cfg_class is not None else None + collision_cfg = ( + schemas_cfg.UsdPhysicsMeshCollisionCfg(mesh_approximation_name=approximation_name) + if approximation_name is not None + else None + ) mesh_converter_cfg = MeshConverterCfg( mass_props=mass_props, diff --git a/scripts/tutorials/01_assets/add_new_robot.py b/scripts/tutorials/01_assets/add_new_robot.py index ca0c477ef58..64f078e68a9 100644 --- a/scripts/tutorials/01_assets/add_new_robot.py +++ b/scripts/tutorials/01_assets/add_new_robot.py @@ -23,6 +23,8 @@ import numpy as np import torch +from isaaclab_newton.sim.schemas import NewtonArticulationCfg +from isaaclab_physx.sim.schemas import PhysxArticulationCfg, PhysxRigidBodyCfg import isaaclab.sim as sim_utils from isaaclab.actuators import ImplicitActuatorCfg @@ -39,13 +41,16 @@ DOFBOT_CONFIG = ArticulationCfg( spawn=sim_utils.UsdFileCfg( usd_path=f"{ISAAC_NUCLEUS_DIR}/Robots/Yahboom/Dofbot/dofbot.usd", - rigid_props=sim_utils.RigidBodyPropertiesCfg( + rigid_props=PhysxRigidBodyCfg( disable_gravity=False, max_depenetration_velocity=5.0, ), - articulation_props=sim_utils.ArticulationRootPropertiesCfg( - enabled_self_collisions=True, solver_position_iteration_count=8, solver_velocity_iteration_count=0 - ), + articulation_props=[ + PhysxArticulationCfg( + enabled_self_collisions=True, solver_position_iteration_count=8, solver_velocity_iteration_count=0 + ), + NewtonArticulationCfg(self_collision_enabled=True), + ], ), init_state=ArticulationCfg.InitialStateCfg( joint_pos={ diff --git a/scripts/tutorials/01_assets/run_rigid_object.py b/scripts/tutorials/01_assets/run_rigid_object.py index 266cac7b0b1..52149192dbb 100644 --- a/scripts/tutorials/01_assets/run_rigid_object.py +++ b/scripts/tutorials/01_assets/run_rigid_object.py @@ -62,9 +62,9 @@ def design_scene(): 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(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), ), init_state=RigidObjectCfg.InitialStateCfg(), diff --git a/scripts/tutorials/03_envs/create_cube_base_env.py b/scripts/tutorials/03_envs/create_cube_base_env.py index 08d203b9e63..b9dc0b43d48 100644 --- a/scripts/tutorials/03_envs/create_cube_base_env.py +++ b/scripts/tutorials/03_envs/create_cube_base_env.py @@ -51,6 +51,7 @@ """Rest everything follows.""" import torch +from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg import isaaclab.envs.mdp as mdp import isaaclab.sim as sim_utils @@ -181,8 +182,8 @@ class MySceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/cube", spawn=sim_utils.CuboidCfg( size=(0.2, 0.2, 0.2), - rigid_props=sim_utils.RigidBodyPropertiesCfg(max_depenetration_velocity=1.0, disable_gravity=True), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), + rigid_props=PhysxRigidBodyCfg(max_depenetration_velocity=1.0, disable_gravity=True), + mass_props=sim_utils.MassCfg(mass=1.0), physics_material=sim_utils.RigidBodyMaterialCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.5, 0.0, 0.0)), ), diff --git a/scripts/tutorials/04_sensors/run_ray_caster.py b/scripts/tutorials/04_sensors/run_ray_caster.py index 7287df3afc3..d2462e0e928 100644 --- a/scripts/tutorials/04_sensors/run_ray_caster.py +++ b/scripts/tutorials/04_sensors/run_ray_caster.py @@ -75,9 +75,9 @@ def design_scene() -> dict: prim_path="/World/Origin.*/ball", spawn=sim_utils.SphereCfg( radius=0.25, - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=0.5), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=0.5), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)), ), ) diff --git a/scripts/tutorials/04_sensors/run_usd_camera.py b/scripts/tutorials/04_sensors/run_usd_camera.py index 7fa75adb7ad..cbd468b13b9 100644 --- a/scripts/tutorials/04_sensors/run_usd_camera.py +++ b/scripts/tutorials/04_sensors/run_usd_camera.py @@ -140,9 +140,9 @@ def design_scene() -> dict: # choose random prim type prim_type = random.choice(["Cube", "Cone", "Cylinder"]) common_properties = { - "rigid_props": sim_utils.RigidBodyPropertiesCfg(), - "mass_props": sim_utils.MassPropertiesCfg(mass=5.0), - "collision_props": sim_utils.CollisionPropertiesCfg(), + "rigid_props": sim_utils.UsdPhysicsRigidBodyCfg(), + "mass_props": sim_utils.MassCfg(mass=5.0), + "collision_props": sim_utils.UsdPhysicsCollisionCfg(), "visual_material": sim_utils.PreviewSurfaceCfg(diffuse_color=color, metallic=0.5), "semantic_tags": [("class", prim_type)], } diff --git a/scripts/tutorials/05_controllers/run_osc.py b/scripts/tutorials/05_controllers/run_osc.py index f3665010d7a..7a858830dd7 100644 --- a/scripts/tutorials/05_controllers/run_osc.py +++ b/scripts/tutorials/05_controllers/run_osc.py @@ -80,9 +80,9 @@ class SceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/TiltedWall", spawn=sim_utils.CuboidCfg( size=(2.0, 1.5, 0.01), - collision_props=sim_utils.CollisionPropertiesCfg(), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), opacity=0.1), - rigid_props=sim_utils.RigidBodyPropertiesCfg(kinematic_enabled=True), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(kinematic_enabled=True), activate_contact_sensors=True, ), init_state=AssetBaseCfg.InitialStateCfg( diff --git a/source/isaaclab/changelog.d/vidurv-migrate-legacy-source-remaining.skip b/source/isaaclab/changelog.d/vidurv-migrate-legacy-source-remaining.skip new file mode 100644 index 00000000000..8e368b384d8 --- /dev/null +++ b/source/isaaclab/changelog.d/vidurv-migrate-legacy-source-remaining.skip @@ -0,0 +1 @@ +Migrated test scene configurations off the deprecated legacy schema property configs; test-only change. diff --git a/source/isaaclab/test/sensors/test_camera_pose_write_across_backends.py b/source/isaaclab/test/sensors/test_camera_pose_write_across_backends.py index 05fe671ad3d..a3a3662c3e2 100644 --- a/source/isaaclab/test/sensors/test_camera_pose_write_across_backends.py +++ b/source/isaaclab/test/sensors/test_camera_pose_write_across_backends.py @@ -50,9 +50,9 @@ class _SceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(0.5, 0.5, 0.5), - rigid_props=sim_utils.RigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 0.25)), ) diff --git a/source/isaaclab/test/sensors/test_first_frame_textured_rendering.py b/source/isaaclab/test/sensors/test_first_frame_textured_rendering.py index 08d129b510d..c7a5edc3ad8 100644 --- a/source/isaaclab/test/sensors/test_first_frame_textured_rendering.py +++ b/source/isaaclab/test/sensors/test_first_frame_textured_rendering.py @@ -15,7 +15,6 @@ import pytest import torch from isaaclab_physx.physics import PhysxCfg -from isaaclab_physx.sim.schemas import PhysxCollisionPropertiesCfg, PhysxRigidBodyPropertiesCfg import omni.replicator.core as rep @@ -176,9 +175,9 @@ class CameraObservationsCfg(ObservationGroupCfg): prim_path="{ENV_REGEX_NS}/Cube", spawn=sim_utils.CuboidCfg( size=(0.3, 0.15, 0.2), - rigid_props=PhysxRigidBodyPropertiesCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=PhysxCollisionPropertiesCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(-0.35, 0.0, 0.2)), ) diff --git a/source/isaaclab_newton/changelog.d/vidurv-migrate-legacy-source-remaining.skip b/source/isaaclab_newton/changelog.d/vidurv-migrate-legacy-source-remaining.skip new file mode 100644 index 00000000000..8e368b384d8 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/vidurv-migrate-legacy-source-remaining.skip @@ -0,0 +1 @@ +Migrated test scene configurations off the deprecated legacy schema property configs; test-only change. diff --git a/source/isaaclab_newton/test/assets/test_cable_object.py b/source/isaaclab_newton/test/assets/test_cable_object.py index d4f167fb741..c2801445702 100644 --- a/source/isaaclab_newton/test/assets/test_cable_object.py +++ b/source/isaaclab_newton/test/assets/test_cable_object.py @@ -52,9 +52,9 @@ class _ProxyCableSceneCfg(_CableSceneCfg): prim_path="{ENV_REGEX_NS}/Rigid", spawn=sim_utils.CuboidCfg( size=(0.1, 0.1, 0.1), - rigid_props=sim_utils.RigidBodyBaseCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=1.0), - collision_props=sim_utils.CollisionBaseCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=1.0), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(2.0, 0.0, 1.0)), ) diff --git a/source/isaaclab_newton/test/assets/test_mpm_object.py b/source/isaaclab_newton/test/assets/test_mpm_object.py index 5b87d16728d..92332751b6d 100644 --- a/source/isaaclab_newton/test/assets/test_mpm_object.py +++ b/source/isaaclab_newton/test/assets/test_mpm_object.py @@ -78,6 +78,8 @@ class MPMSceneCfg(InteractiveSceneCfg): def test_mpm_solver_refreshes_kinematic_rigid_body_transforms(): + from isaaclab_physx.sim.schemas import PhysxRigidBodyCfg # noqa: PLC0415 + import isaaclab.sim as sim_utils # noqa: PLC0415 @configclass @@ -86,12 +88,11 @@ class MPMSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/KinematicBox", spawn=sim_utils.CuboidCfg( size=(0.1, 0.1, 0.1), - rigid_props=sim_utils.NewtonRigidBodyPropertiesCfg( - rigid_body_enabled=True, - kinematic_enabled=True, - disable_gravity=True, - ), - collision_props=sim_utils.NewtonCollisionPropertiesCfg(collision_enabled=True), + rigid_props=[ + sim_utils.UsdPhysicsRigidBodyCfg(rigid_body_enabled=True, kinematic_enabled=True), + PhysxRigidBodyCfg(disable_gravity=True), + ], + collision_props=sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 0.2)), ) diff --git a/source/isaaclab_newton/test/sensors/test_camera_opencv_distortion_newton.py b/source/isaaclab_newton/test/sensors/test_camera_opencv_distortion_newton.py index a26b9201a57..79e056feef1 100644 --- a/source/isaaclab_newton/test/sensors/test_camera_opencv_distortion_newton.py +++ b/source/isaaclab_newton/test/sensors/test_camera_opencv_distortion_newton.py @@ -60,9 +60,9 @@ class _DistortionSceneCfg(InteractiveSceneCfg): prim_path="{ENV_REGEX_NS}/Anchor", spawn=sim_utils.CuboidCfg( size=(0.01, 0.01, 0.01), - rigid_props=sim_utils.RigidBodyBaseCfg(), - mass_props=sim_utils.MassPropertiesCfg(mass=0.001), - collision_props=sim_utils.CollisionBaseCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), + mass_props=sim_utils.MassCfg(mass=0.001), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), physics_material=RigidBodyMaterialBaseCfg(), ), init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, -100.0)), diff --git a/source/isaaclab_ov/changelog.d/vidurv-migrate-legacy-source-remaining.skip b/source/isaaclab_ov/changelog.d/vidurv-migrate-legacy-source-remaining.skip new file mode 100644 index 00000000000..8e368b384d8 --- /dev/null +++ b/source/isaaclab_ov/changelog.d/vidurv-migrate-legacy-source-remaining.skip @@ -0,0 +1 @@ +Migrated test scene configurations off the deprecated legacy schema property configs; test-only change. diff --git a/source/isaaclab_ov/test/physics/test_ovphysx_gravity.py b/source/isaaclab_ov/test/physics/test_ovphysx_gravity.py index 6ac0228d9a1..e9a525813a5 100644 --- a/source/isaaclab_ov/test/physics/test_ovphysx_gravity.py +++ b/source/isaaclab_ov/test/physics/test_ovphysx_gravity.py @@ -32,9 +32,9 @@ def test_gravity_event_changes_rigid_body_motion(): init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, 0.0, 10.0)), spawn=sim_utils.CuboidCfg( size=(0.5, 0.5, 0.5), - rigid_props=sim_utils.RigidBodyBaseCfg(), + rigid_props=sim_utils.UsdPhysicsRigidBodyCfg(), mass_props=sim_utils.MassCfg(mass=1.0), - collision_props=sim_utils.CollisionBaseCfg(), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), ) ) diff --git a/source/isaaclab_tasks/changelog.d/vidurv-migrate-legacy-source-remaining.rst b/source/isaaclab_tasks/changelog.d/vidurv-migrate-legacy-source-remaining.rst new file mode 100644 index 00000000000..d3813f34549 --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/vidurv-migrate-legacy-source-remaining.rst @@ -0,0 +1,12 @@ +Changed +^^^^^^^ + +* Changed the reach table collider, the UR10 particle-push colliders, and the NIST factory Newton + Franka rigid-body properties to author their physics schemas with schema fragments + (:class:`~isaaclab.sim.schemas.UsdPhysicsCollisionCfg`, + :class:`~isaaclab_newton.sim.schemas.NewtonCollisionCfg`, and + :class:`~isaaclab_newton.sim.schemas.MujocoRigidBodyCfg`) instead of the deprecated legacy + property configs, so loading these tasks no longer emits their ``DeprecationWarning``. The + authored USD is unchanged. Configurations that tune these spawner slots in place should select + the fragment that owns the field (e.g. the :class:`~isaaclab_newton.sim.schemas.NewtonCollisionCfg` + entry of the UR10 particle-push ``collision_props`` list for ``contact_margin``). diff --git a/source/isaaclab_tasks/isaaclab_tasks/contrib/nist/factory_assets_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/contrib/nist/factory_assets_cfg.py index a1896c0f615..851751188ad 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/contrib/nist/factory_assets_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/contrib/nist/factory_assets_cfg.py @@ -3,7 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -from isaaclab_newton.sim.schemas import NewtonArticulationCfg +from isaaclab_newton.sim.schemas import MujocoRigidBodyCfg, NewtonArticulationCfg from isaaclab_newton.sim.spawners.materials import NewtonMaterialCfg from isaaclab_physx.sim.schemas import PhysxArticulationCfg, PhysxCollisionCfg, PhysxRigidBodyCfg @@ -171,7 +171,7 @@ class _PlugCollisionPropsCfg(PresetCfg): spawn=sim_utils.UsdFileCfg( usd_path=f"{ASSET_DIR}/franka_mimic.usd", activate_contact_sensors=True, - rigid_props=sim_utils.MujocoRigidBodyPropertiesCfg(gravcomp=1.0), + rigid_props=MujocoRigidBodyCfg(gravcomp=1.0), articulation_props=[ PhysxArticulationCfg(enabled_self_collisions=False), NewtonArticulationCfg(self_collision_enabled=False), diff --git a/source/isaaclab_tasks/isaaclab_tasks/contrib/ur10_particle_push/ur10_particle_push_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/contrib/ur10_particle_push/ur10_particle_push_env_cfg.py index ebc48be8b13..0ebf60617cb 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/contrib/ur10_particle_push/ur10_particle_push_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/contrib/ur10_particle_push/ur10_particle_push_env_cfg.py @@ -15,7 +15,7 @@ from isaaclab_newton.assets import MPMObjectCfg from isaaclab_newton.physics import MJWarpSolverCfg, MPMSolverCfg, NewtonCfg, NewtonCollisionPipelineCfg -from isaaclab_newton.sim.schemas import MujocoJointCfg, NewtonCollisionPropertiesCfg +from isaaclab_newton.sim.schemas import MujocoJointCfg, NewtonCollisionCfg from isaaclab_newton.sim.spawners.mpm import MPMGridCfg, MPMParticleMaterialCfg from isaaclab_visualizers.newton import NewtonGLVisualizerCfg, NewtonRTXVisualizerCfg @@ -31,7 +31,7 @@ from isaaclab.markers import VisualizationMarkersCfg from isaaclab.scene import InteractiveSceneCfg from isaaclab.sim import SimulationCfg -from isaaclab.sim.schemas import UsdPhysicsRigidBodyCfg +from isaaclab.sim.schemas import UsdPhysicsCollisionCfg, UsdPhysicsRigidBodyCfg from isaaclab.sim.spawners.materials import RigidBodyMaterialBaseCfg from isaaclab.utils import configclass from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR @@ -281,12 +281,14 @@ def _kinematic_box( rigid_body_enabled=True, kinematic_enabled=True, ), - collision_props=NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=MPM_COLLIDER_MARGIN, - # Implicit MPM consumes shape margin; gap is a rigid-contact parameter. - contact_gap=0.0, - ), + collision_props=[ + UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg( + contact_margin=MPM_COLLIDER_MARGIN, + # Implicit MPM consumes shape margin; gap is a rigid-contact parameter. + contact_gap=0.0, + ), + ], physics_material=RigidBodyMaterialBaseCfg( static_friction=0.8, dynamic_friction=0.7, @@ -312,11 +314,10 @@ def _static_collision_box( init_state=AssetBaseCfg.InitialStateCfg(pos=position), spawn=sim_utils.CuboidCfg( size=size, - collision_props=NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=0.004, - contact_gap=0.002, - ), + collision_props=[ + UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=0.004, contact_gap=0.002), + ], physics_material=RigidBodyMaterialBaseCfg( static_friction=0.8, dynamic_friction=0.7, @@ -370,11 +371,10 @@ class UR10ParticlePushSceneCfg(InteractiveSceneCfg): size=PADDLE_SIZE, rigid_props=UsdPhysicsRigidBodyCfg(rigid_body_enabled=True), mass_props=sim_utils.MassCfg(mass=PADDLE_MASS), - collision_props=NewtonCollisionPropertiesCfg( - collision_enabled=True, - contact_margin=PADDLE_CONTACT_MARGIN, - contact_gap=0.002, - ), + collision_props=[ + UsdPhysicsCollisionCfg(collision_enabled=True), + NewtonCollisionCfg(contact_margin=PADDLE_CONTACT_MARGIN, contact_gap=0.002), + ], physics_material=RigidBodyMaterialBaseCfg( static_friction=0.8, dynamic_friction=0.7, diff --git a/source/isaaclab_tasks/isaaclab_tasks/core/reach/reach_env_cfg.py b/source/isaaclab_tasks/isaaclab_tasks/core/reach/reach_env_cfg.py index e7cd5d99252..8f0b99fcebe 100644 --- a/source/isaaclab_tasks/isaaclab_tasks/core/reach/reach_env_cfg.py +++ b/source/isaaclab_tasks/isaaclab_tasks/core/reach/reach_env_cfg.py @@ -81,7 +81,7 @@ class ReachSceneCfg(InteractiveSceneCfg): init_state=AssetBaseCfg.InitialStateCfg(pos=(0.5, 0.0, -0.5)), spawn=sim_utils.CuboidCfg( size=(0.9, 1.3, 1.0), - collision_props=sim_utils.CollisionBaseCfg(), + collision_props=sim_utils.UsdPhysicsCollisionCfg(), ), )