From e59b3c69e6979cb240ea2e8206f03fc077e8ca6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kripner?= Date: Thu, 2 Apr 2026 15:51:57 +0200 Subject: [PATCH 1/3] Skip material randomization on physics backends that don't support it randomize_rigid_body_material uses PhysX-specific APIs (link_paths, max_shapes, get/set_material_properties) that don't exist on Newton's ArticulationView. This causes an AttributeError when running tasks like Isaac-Open-Drawer-Franka-v0 with env.sim=newton. Detect backend support via hasattr check on get_material_properties and skip the term with a warning when unsupported. Also use the asset's num_shapes_per_body property when available instead of the PhysX-specific link_paths workaround. --- source/isaaclab/isaaclab/envs/mdp/events.py | 47 +++++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/source/isaaclab/isaaclab/envs/mdp/events.py b/source/isaaclab/isaaclab/envs/mdp/events.py index 18c9b5eff63a..5b8063e9a2d2 100644 --- a/source/isaaclab/isaaclab/envs/mdp/events.py +++ b/source/isaaclab/isaaclab/envs/mdp/events.py @@ -203,22 +203,37 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv): f" with type: '{type(self.asset)}'." ) + # check if the physics backend supports material property randomization + self._supported = hasattr(self.asset.root_view, "get_material_properties") + if not self._supported: + import logging + + logging.getLogger(__name__).warning( + "randomize_rigid_body_material: skipping because the current physics backend" + " does not support material property randomization." + ) + self.num_shapes_per_body = None + return + # obtain number of shapes per body (needed for indexing the material properties correctly) - # note: this is a workaround since the Articulation does not provide a direct way to obtain the number of shapes - # per body. We use the physics simulation view to obtain the number of shapes per body. if isinstance(self.asset, BaseArticulation) and self.asset_cfg.body_ids != slice(None): - self.num_shapes_per_body = [] - for link_path in self.asset.root_view.link_paths[0]: - link_physx_view = self.asset._physics_sim_view.create_rigid_body_view(link_path) # type: ignore - self.num_shapes_per_body.append(link_physx_view.max_shapes) - # ensure the parsing is correct - num_shapes = sum(self.num_shapes_per_body) - expected_shapes = self.asset.root_view.max_shapes - if num_shapes != expected_shapes: - raise ValueError( - "Randomization term 'randomize_rigid_body_material' failed to parse the number of shapes per body." - f" Expected total shapes: {expected_shapes}, but got: {num_shapes}." - ) + # check if the asset already provides num_shapes_per_body (e.g. Newton backend) + if hasattr(self.asset, "num_shapes_per_body"): + self.num_shapes_per_body = self.asset.num_shapes_per_body + else: + # PhysX workaround: use the simulation view to obtain the number of shapes per body + self.num_shapes_per_body = [] + for link_path in self.asset.root_view.link_paths[0]: + link_physx_view = self.asset._physics_sim_view.create_rigid_body_view(link_path) # type: ignore + self.num_shapes_per_body.append(link_physx_view.max_shapes) + # ensure the parsing is correct + num_shapes = sum(self.num_shapes_per_body) + expected_shapes = self.asset.root_view.max_shapes + if num_shapes != expected_shapes: + raise ValueError( + "Randomization term 'randomize_rigid_body_material' failed to parse the number of shapes" + f" per body. Expected total shapes: {expected_shapes}, but got: {num_shapes}." + ) else: # in this case, we don't need to do special indexing self.num_shapes_per_body = None @@ -252,6 +267,10 @@ def __call__( asset_cfg: SceneEntityCfg, make_consistent: bool = False, ): + # skip if backend doesn't support material randomization + if not self._supported: + return + # resolve environment ids if env_ids is None: env_ids = torch.arange(env.scene.num_envs, device="cpu", dtype=torch.int32) From d272931bf2d6b790fcbdfb8e6fb76548a4d1b314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kripner?= Date: Thu, 2 Apr 2026 16:06:36 +0200 Subject: [PATCH 2/3] Update source/isaaclab/isaaclab/envs/mdp/events.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unnecessary local import. Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: Matěj Kripner --- source/isaaclab/isaaclab/envs/mdp/events.py | 1 - 1 file changed, 1 deletion(-) diff --git a/source/isaaclab/isaaclab/envs/mdp/events.py b/source/isaaclab/isaaclab/envs/mdp/events.py index 5b8063e9a2d2..8c91b065da02 100644 --- a/source/isaaclab/isaaclab/envs/mdp/events.py +++ b/source/isaaclab/isaaclab/envs/mdp/events.py @@ -206,7 +206,6 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv): # check if the physics backend supports material property randomization self._supported = hasattr(self.asset.root_view, "get_material_properties") if not self._supported: - import logging logging.getLogger(__name__).warning( "randomize_rigid_body_material: skipping because the current physics backend" From 96659d14dca69ca7ecb1dc01be11aa9274cda34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kripner?= Date: Thu, 2 Apr 2026 16:06:55 +0200 Subject: [PATCH 3/3] Update source/isaaclab/isaaclab/envs/mdp/events.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clarify comment. Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Signed-off-by: Matěj Kripner --- source/isaaclab/isaaclab/envs/mdp/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/isaaclab/isaaclab/envs/mdp/events.py b/source/isaaclab/isaaclab/envs/mdp/events.py index 8c91b065da02..48a1adcc2fe8 100644 --- a/source/isaaclab/isaaclab/envs/mdp/events.py +++ b/source/isaaclab/isaaclab/envs/mdp/events.py @@ -216,7 +216,7 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv): # obtain number of shapes per body (needed for indexing the material properties correctly) if isinstance(self.asset, BaseArticulation) and self.asset_cfg.body_ids != slice(None): - # check if the asset already provides num_shapes_per_body (e.g. Newton backend) + # check if the asset directly provides num_shapes_per_body (e.g. future backends that expose it) if hasattr(self.asset, "num_shapes_per_body"): self.num_shapes_per_body = self.asset.num_shapes_per_body else: