Skip to content

Commit 25e8f3c

Browse files
Wire OvPhysX preset into AnymalD Flat + multi-env sensor count
* Add 'ovphysx' field to PhysicsCfg in AnymalD flat_env_cfg.py * Add 'ovphysx' field to VelocityEnvContactSensorCfg in velocity_env_cfg.py * events.py randomize_rigid_body_material: explicit OvPhysxManager branch (no-op + warning; PhysX impl assumes root_view.link_paths which OvPhysx's per-tensor-type bindings dict does not satisfy) * ContactSensor._initialize_impl: override _num_envs from binding sensor_count (clone_usd=False on OvPhysX leaves env_1..N out of USD, so the parent class's USD walk reports num_envs=1 even with N=4096 cloned physics envs)
1 parent ca5bdaf commit 25e8f3c

4 files changed

Lines changed: 57 additions & 11 deletions

File tree

source/isaaclab/isaaclab/envs/mdp/events.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,35 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv):
385385
f" with type: '{type(self.asset)}'."
386386
)
387387

388-
# detect physics backend and instantiate the appropriate implementation
389-
manager_name = env.sim.physics_manager.__name__.lower()
390-
if "newton" in manager_name:
388+
# detect physics backend and instantiate the appropriate implementation.
389+
# Exact name matches: ``"physx" in name.lower()`` would also catch
390+
# ``OvPhysxManager`` and route it to the PhysX impl, which assumes a
391+
# ``root_view`` with ``.link_paths`` — OVPhysX's per-tensor-type
392+
# bindings dict does not satisfy that contract.
393+
manager_name = env.sim.physics_manager.__name__
394+
if manager_name == "NewtonManager":
391395
self._impl = _RandomizeRigidBodyMaterialNewton(cfg, env, self.asset, self.asset_cfg)
392-
else:
396+
elif manager_name == "PhysxManager":
393397
self._impl = _RandomizeRigidBodyMaterialPhysx(cfg, env, self.asset, self.asset_cfg)
398+
elif manager_name == "OvPhysxManager":
399+
# No OVPhysX implementation yet — wheel-side
400+
# ``RIGID_BODY_MATERIAL`` tensor binding is missing; randomization
401+
# would require per-body view creation that ovphysx does not yet
402+
# expose. Run with material randomization disabled (warns once).
403+
import logging # noqa: PLC0415
404+
405+
logging.getLogger(__name__).warning(
406+
"randomize_rigid_body_material is a no-op on the OVPhysX backend "
407+
"(wheel-side gap — see docs/superpowers/specs/2026-04-27-ovphysx-contact-api-gaps.md)."
408+
)
409+
410+
class _Noop:
411+
def __call__(self, *args, **kwargs):
412+
pass
413+
414+
self._impl = _Noop()
415+
else:
416+
raise ValueError(f"Unsupported physics manager for randomize_rigid_body_material: {manager_name!r}")
394417

395418
def __call__(
396419
self,

source/isaaclab_ovphysx/isaaclab_ovphysx/sensors/contact_sensor/contact_sensor.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,13 @@ def _initialize_impl(self) -> None:
209209
filter_patterns = None
210210

211211
# Create the contact binding (must happen BEFORE the next step()).
212+
# OVPhysX's ``InteractiveScene`` runs in ``clone_usd=False`` mode:
213+
# env_1..N have no USD prim — they're physics-layer clones via
214+
# ``physx.clone()``. The parent class's ``find_matching_prims`` walk
215+
# therefore sees only env_0 and sets ``self._num_envs = 1`` even when
216+
# the scene is configured for many envs. We size the
217+
# ``max_contact_data_count`` for env_0 only here; the binding's
218+
# ``sensor_count`` after creation gives us the real env count.
212219
max_count = self.cfg.max_contact_data_count_per_prim * self._num_sensors * self._num_envs
213220
self._contact_binding = physx_instance.create_contact_binding(
214221
sensor_patterns=sensor_patterns,
@@ -217,17 +224,29 @@ def _initialize_impl(self) -> None:
217224
max_contact_data_count=max_count,
218225
)
219226

220-
# Validate that ovphysx matched what we expected. sensor_count is the
221-
# global total (envs * bodies); the binding does not split per env.
222-
expected_sensors = self._num_sensors * self._num_envs
223-
if self._contact_binding.sensor_count != expected_sensors:
227+
# Validate: sensor_count must be a non-zero multiple of num_sensors.
228+
if self._contact_binding.sensor_count == 0 or self._contact_binding.sensor_count % self._num_sensors != 0:
224229
raise RuntimeError(
225230
"Failed to initialize contact binding for specified bodies."
226231
f"\n\tInput prim path : {self.cfg.prim_path}"
227-
f"\n\tExpected sensors : {expected_sensors} ({self._num_envs} envs * {self._num_sensors} bodies)"
232+
f"\n\tNum sensor bodies : {self._num_sensors}"
228233
f"\n\tBound sensors : {self._contact_binding.sensor_count}"
229234
)
230235

236+
# Override ``_num_envs`` with the binding's view if it differs (it does
237+
# for any OVPhysX scene with ``num_envs > 1`` due to ``clone_usd=False``).
238+
# Re-allocate the env-sized buffers from the parent class so they match
239+
# the real env count.
240+
binding_num_envs = self._contact_binding.sensor_count // self._num_sensors
241+
if binding_num_envs != self._num_envs:
242+
self._num_envs = binding_num_envs
243+
self._ALL_ENV_MASK = wp.ones((self._num_envs,), dtype=wp.bool, device=self._device)
244+
self._reset_mask = wp.zeros((self._num_envs,), dtype=wp.bool, device=self._device)
245+
self._reset_mask_torch = wp.to_torch(self._reset_mask)
246+
self._is_outdated = wp.ones(self._num_envs, dtype=wp.bool, device=self._device)
247+
self._timestamp = wp.zeros(self._num_envs, dtype=wp.float32, device=self._device)
248+
self._timestamp_last_update = wp.zeros_like(self._timestamp)
249+
231250
# Optional: pose tracking via a RIGID_BODY_POSE tensor binding.
232251
# ovphysx fnmatch does not brace-expand, so we cannot match multiple
233252
# body names with a single glob. Single-body sensors (the common case
@@ -247,12 +266,12 @@ def _initialize_impl(self) -> None:
247266
pattern=single_pose_pattern,
248267
tensor_type=TT.RIGID_BODY_POSE,
249268
)
250-
if self._pose_binding.count != expected_sensors:
269+
if self._pose_binding.count != self._contact_binding.sensor_count:
251270
raise RuntimeError(
252271
"RIGID_BODY_POSE binding count mismatch."
253272
f"\n\tPattern: {single_pose_pattern}"
254273
f"\n\tBound : {self._pose_binding.count}"
255-
f"\n\tExpect : {expected_sensors}"
274+
f"\n\tExpect : {self._contact_binding.sensor_count}"
256275
)
257276

258277
self._create_buffers()

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_d/flat_env_cfg.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# SPDX-License-Identifier: BSD-3-Clause
55

66
from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg
7+
from isaaclab_ovphysx.physics import OvPhysxCfg
78
from isaaclab_physx.physics import PhysxCfg
89

910
from isaaclab.sim import SimulationCfg
@@ -28,6 +29,7 @@ class PhysicsCfg(PresetCfg):
2829
debug_mode=False,
2930
)
3031
physx = default
32+
ovphysx = OvPhysxCfg()
3133

3234

3335
@configclass

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg, NewtonCollisionPipelineCfg, NewtonShapeCfg
1010
from isaaclab_newton.sensors import ContactSensorCfg as NewtonContactSensorCfg
11+
from isaaclab_ovphysx.sensors import ContactSensorCfg as OvPhysXContactSensorCfg
1112
from isaaclab_physx.physics import PhysxCfg
1213
from isaaclab_physx.sensors import ContactSensorCfg as PhysXContactSensorCfg
1314

@@ -78,6 +79,7 @@ class VelocityEnvContactSensorCfg(PresetCfg):
7879
default = PhysXContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, track_air_time=True)
7980
newton_mjwarp = NewtonContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, track_air_time=True)
8081
physx = default
82+
ovphysx = OvPhysXContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, track_air_time=True)
8183

8284

8385
@configclass

0 commit comments

Comments
 (0)