Skip to content

Commit 17136c6

Browse files
authored
Fix contact sensor activation for nested rigid bodies (isaac-sim#6259)
## Summary - use `get_all_matching_child_prims` when activating PhysX contact report schemas - continue matching rigid descendants under nested rigid-body trees instead of stopping at the first rigid body - add regression coverage for nested rigid bodies receiving `PhysxContactReportAPI` ## Testing - ./isaaclab.sh -p -m pytest source/isaaclab/test/sim/test_schemas.py -k activate_contact_sensors_nested_rigid_bodies -q - ./isaaclab.sh -p -m py_compile source/isaaclab/isaaclab/sim/schemas/schemas.py source/isaaclab/test/sim/test_schemas.py ## Note - Attempted `./isaaclab.sh -p -m pytest source/isaaclab_physx/test/sensors/test_contact_sensor.py -k contact_sensor_threshold -q`, but collection failed because `flaky` is not installed in the local environment.
1 parent e211621 commit 17136c6

3 files changed

Lines changed: 49 additions & 26 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed contact sensor activation for rigid bodies nested below other rigid bodies.

source/isaaclab/isaaclab/sim/schemas/schemas.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -933,33 +933,25 @@ def activate_contact_sensors(prim_path: str, threshold: float = 0.0, stage: Usd.
933933
# check if prim is valid
934934
if not prim.IsValid():
935935
raise ValueError(f"Prim path '{prim_path}' is not valid.")
936-
# iterate over all children
937-
num_contact_sensors = 0
938-
all_prims = [prim]
939-
while len(all_prims) > 0:
940-
# get current prim
941-
child_prim = all_prims.pop(0)
942-
# check if prim is a rigid body
943-
# nested rigid bodies are not allowed by SDK so we can safely assume that
944-
# if a prim has a rigid body API, it is a rigid body and we don't need to
945-
# check its children
946-
if child_prim.HasAPI(UsdPhysics.RigidBodyAPI):
947-
# set sleep threshold to zero
948-
child_applied = child_prim.GetAppliedSchemas()
949-
if "PhysxRigidBodyAPI" not in child_applied:
950-
child_prim.AddAppliedSchema("PhysxRigidBodyAPI")
951-
safe_set_attribute_on_usd_prim(child_prim, "physxRigidBody:sleepThreshold", 0.0, camel_case=False)
952-
# add contact report API with threshold of zero
953-
if "PhysxContactReportAPI" not in child_applied:
954-
child_prim.AddAppliedSchema("PhysxContactReportAPI")
955-
safe_set_attribute_on_usd_prim(child_prim, "physxContactReport:threshold", threshold, camel_case=False)
956-
# increment number of contact sensors
957-
num_contact_sensors += 1
958-
else:
959-
# add all children to tree
960-
all_prims += child_prim.GetChildren()
936+
# collect all rigid bodies under the prim, including nested rigid-body trees
937+
rigid_body_prims = get_all_matching_child_prims(
938+
prim_path,
939+
predicate=lambda child_prim: child_prim.HasAPI(UsdPhysics.RigidBodyAPI),
940+
stage=stage,
941+
traverse_instance_prims=False,
942+
)
943+
for child_prim in rigid_body_prims:
944+
# set sleep threshold to zero
945+
child_applied = child_prim.GetAppliedSchemas()
946+
if "PhysxRigidBodyAPI" not in child_applied:
947+
child_prim.AddAppliedSchema("PhysxRigidBodyAPI")
948+
safe_set_attribute_on_usd_prim(child_prim, "physxRigidBody:sleepThreshold", 0.0, camel_case=False)
949+
# add contact report API with threshold of zero
950+
if "PhysxContactReportAPI" not in child_applied:
951+
child_prim.AddAppliedSchema("PhysxContactReportAPI")
952+
safe_set_attribute_on_usd_prim(child_prim, "physxContactReport:threshold", threshold, camel_case=False)
961953
# check if no contact sensors were found
962-
if num_contact_sensors == 0:
954+
if not rigid_body_prims:
963955
descendant_count = 0
964956
frontier = [prim]
965957
while frontier:

source/isaaclab/test/sim/test_schemas.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,33 @@ def test_modify_properties_on_articulation_usd(setup_simulation):
752752
_validate_articulation_properties_on_prim("/World/asset", arti_cfg, True)
753753

754754

755+
@pytest.mark.isaacsim_ci
756+
def test_activate_contact_sensors_nested_rigid_bodies(setup_simulation):
757+
"""Test contact-report schemas are applied to nested rigid-body trees."""
758+
stage = sim_utils.get_current_stage()
759+
760+
rigid_body_paths = [
761+
"/World/Robot/Geometry/pelvis",
762+
"/World/Robot/Geometry/pelvis/left_hip",
763+
"/World/Robot/Geometry/pelvis/left_hip/left_knee",
764+
]
765+
sim_utils.create_prim("/World/Robot", prim_type="Xform")
766+
sim_utils.create_prim("/World/Robot/Geometry", prim_type="Xform")
767+
for prim_path in rigid_body_paths:
768+
sim_utils.create_prim(prim_path, prim_type="Xform")
769+
UsdPhysics.RigidBodyAPI.Apply(stage.GetPrimAtPath(prim_path))
770+
771+
schemas.activate_contact_sensors("/World/Robot", threshold=2.5)
772+
773+
for prim_path in rigid_body_paths:
774+
prim = stage.GetPrimAtPath(prim_path)
775+
applied_schemas = prim.GetAppliedSchemas()
776+
assert "PhysxRigidBodyAPI" in applied_schemas
777+
assert "PhysxContactReportAPI" in applied_schemas
778+
assert prim.GetAttribute("physxRigidBody:sleepThreshold").Get() == pytest.approx(0.0)
779+
assert prim.GetAttribute("physxContactReport:threshold").Get() == pytest.approx(2.5)
780+
781+
755782
@pytest.mark.isaacsim_ci
756783
def test_defining_rigid_body_properties_on_prim(setup_simulation):
757784
"""Test defining rigid body properties on a prim."""

0 commit comments

Comments
 (0)