Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9afd8fc
Add RIGID_BODY_* TensorType aliases
AntoineRichard Apr 28, 2026
4b3503b
Relocate shared Warp kernels to isaaclab_ovphysx.assets.kernels
AntoineRichard Apr 28, 2026
5f8c4c6
Add asset_kind='rigid_object' mode to MockOvPhysxBindingSet
AntoineRichard Apr 28, 2026
65084f7
Scaffold OVPhysX RigidObjectData skeleton
AntoineRichard Apr 28, 2026
0a79c66
Fix RigidObjectData defaults and is_primed gating
AntoineRichard Apr 28, 2026
3cb5de1
Implement RigidObjectData root-state properties
AntoineRichard Apr 28, 2026
b2afe59
Reset buffer timestamps in _invalidate_caches
AntoineRichard Apr 28, 2026
95dc4fa
Implement body-state and body-frame derived properties
AntoineRichard Apr 28, 2026
1e9e9aa
Fix projected_gravity_b test to use unit-vector gravity
AntoineRichard Apr 28, 2026
5963cfe
Implement RigidObjectData body mass/inertia/COM properties
AntoineRichard Apr 28, 2026
9a69088
Scaffold OVPhysX RigidObject skeleton
AntoineRichard Apr 28, 2026
af2e8a9
Apply Marco's RIGID_BODY_* contract corrections
AntoineRichard Apr 28, 2026
0ec4611
Add RigidObject _create_buffers and _process_cfg
AntoineRichard Apr 28, 2026
af529eb
Implement RigidObject root pose and velocity writers
AntoineRichard Apr 28, 2026
fed5d23
Implement RigidObject mass/COM/inertia setters
AntoineRichard Apr 28, 2026
6cb0c36
Implement RigidObject.write_data_to_sim wrench application
AntoineRichard Apr 28, 2026
f10f59c
Implement RigidObject lifecycle methods
AntoineRichard Apr 28, 2026
bbad12b
Export RigidObject and RigidObjectData publicly
AntoineRichard Apr 28, 2026
5c07601
Add OVPhysX backend to test_rigid_object_iface
AntoineRichard Apr 28, 2026
0a950bd
Add OVPhysX preset to Allegro hand env
AntoineRichard Apr 28, 2026
6b16fe9
Bump isaaclab_ovphysx to 0.2.0 for RigidObject
AntoineRichard Apr 28, 2026
db27e50
Drop RIGID_BODY_ACCELERATION dependency, FD acc from velocity
AntoineRichard Apr 28, 2026
a07c425
Fix stale-buffer bug in body_com_pose_b lazy read
AntoineRichard Apr 28, 2026
6e81211
Add kitless guard to test_rigid_object_iface
AntoineRichard Apr 28, 2026
cd38c46
Fix shape mismatch in 1-D body-property setter writes
AntoineRichard Apr 28, 2026
b3b93b3
Replace mock test_rigid_object with PhysX-copy kitless adapter
AntoineRichard Apr 28, 2026
b9eb426
Add kitless real-OvPhysxManager warmup + load tests
AntoineRichard Apr 28, 2026
e320fe1
Rewrite test_rigid_object to use real SimulationContext + Nucleus assets
AntoineRichard Apr 28, 2026
45557cd
Fix RigidObject._initialize_impl swallows on body_names and device
AntoineRichard Apr 28, 2026
aa0ee90
Match Newton's pattern in external_force_on_single_body
AntoineRichard Apr 28, 2026
7153229
Address W1 audit fixes: is_primed, naming, body_names, set_coms reshape
AntoineRichard Apr 29, 2026
e762ab5
Revert RigidObject.reset auto-write to match PhysX/Newton
AntoineRichard Apr 29, 2026
1e3dcf8
Port test_external_force_at_position to PhysX/Newton pattern
AntoineRichard Apr 29, 2026
b1a841e
Derive root_link_vel_w from root_com_vel_w via lever-arm kernel
AntoineRichard Apr 29, 2026
231e9be
Implement deprecated state-concat properties on RigidObjectData
AntoineRichard Apr 29, 2026
482e7af
Address remaining audit investigates: docstring, demotions, removal
AntoineRichard Apr 29, 2026
5e09119
Reorganize RigidObjectData to match PhysX/Newton structure
AntoineRichard Apr 29, 2026
b0532f2
Polish RigidObject docstrings + rename _write_root_state
AntoineRichard Apr 29, 2026
ed30c2c
Add docstrings to every kernel in isaaclab_ovphysx.assets.kernels
AntoineRichard Apr 29, 2026
7adf3b6
Polish test_rigid_object.py: drop gates, GPU coverage, docstring cleanup
AntoineRichard Apr 29, 2026
66df142
Unify _configure_physx_scene_prim across CPU and GPU
AntoineRichard Apr 29, 2026
2bd2752
Align test_rigid_object.py 1-to-1 with isaaclab_physx
AntoineRichard Apr 29, 2026
f06e64b
Use session-scoped sim fixture for rigid-object tests
AntoineRichard Apr 29, 2026
0887d2d
Mirror PhysX/Newton patterns in OVPhysX RigidObject
AntoineRichard Apr 29, 2026
8b180fb
Update RigidObject tests for new contract; document two-pass CI
AntoineRichard Apr 29, 2026
e2b2f55
Compact 0.2.15 changelog entry
AntoineRichard Apr 30, 2026
588c1c8
Squash 0.2.x changelog entries into a single 0.2.15 release
AntoineRichard Apr 30, 2026
d74e904
Drop full_data and dead helpers from OVPhysX RigidObject
AntoineRichard Apr 30, 2026
5cdc178
Cache flat wrench-buffer view in _create_buffers
AntoineRichard Apr 30, 2026
abbeda4
Trim kitless module stubs in iface test guards
AntoineRichard Apr 30, 2026
b340a55
Fix OVPhysX iface fixture to mirror PhysX/Newton setup
AntoineRichard Apr 30, 2026
08180d3
Move ovphysx lifecycle workaround into OvPhysxManager
AntoineRichard May 5, 2026
8bc3a4a
Address PR #5426 review feedback
AntoineRichard May 5, 2026
26a442b
Merge branch 'develop' into antoiner/feat/ovphysx_rigidobject
AntoineRichard May 5, 2026
c453822
Switch isaaclab_ovphysx PR changelog to fragments
AntoineRichard May 5, 2026
658e73c
Make OVPhysX RigidObject._get_binding strict
AntoineRichard May 5, 2026
cf1adf0
Address PR review on RigidObjectData
AntoineRichard May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions scripts/run_ovphysx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
# Use when ovphysx is installed into Kit's Python.
#
# Usage: ./scripts/run_ovphysx.sh [your_script.py or -m pytest ...]
#
# CI note: the OVPhysX wheel's device mode is a process-global C++/Carbonite
# static (gap G5 in docs/superpowers/specs/2026-04-28-ovphysx-wheel-gaps-for-marco.md).
# To exercise both CPU and GPU coverage, invoke this script TWICE in separate
# processes -- e.g.
# ./scripts/run_ovphysx.sh -m pytest <path> -k 'cpu'
# ./scripts/run_ovphysx.sh -m pytest <path> -k 'cuda:0'
# A single invocation locks to whichever device is requested first.
set -e

ISAACLAB_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
Expand Down
Empty file.
23 changes: 20 additions & 3 deletions source/isaaclab/test/assets/test_articulation_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,33 @@
from unittest.mock import MagicMock

# When running kitless (e.g., ovphysx backend via run_ovphysx.sh), AppLauncher
# will try to boot Kit and hang. Skip it entirely when LD_PRELOAD is cleared
# (the signature of run_ovphysx.sh) or when EXP_PATH is not set.
_kitless = os.environ.get("LD_PRELOAD", "") == "" and "EXP_PATH" not in os.environ
# will try to boot Kit and hang. Skip it entirely: run_ovphysx.sh sets
# LD_PRELOAD to the ovphysx libcarb.so, which is the signature of a kitless
# ovphysx run. Also guard the case where neither LD_PRELOAD nor EXP_PATH is
# set (bare Python, no Kit at all).
_kitless = "ovphysx" in os.environ.get("LD_PRELOAD", "") or (
os.environ.get("LD_PRELOAD", "") == "" and "EXP_PATH" not in os.environ
)

if not _kitless:
from isaaclab.app import AppLauncher

simulation_app = AppLauncher(headless=True).app
else:
simulation_app = None
# Stub out the Kit/Omniverse modules that are not present under
# run_ovphysx.sh (pxr, carb, omni, omni.kit[.app] are real on PYTHONPATH).
# ``omni`` is a real namespace package, so missing submodules also need
# to be installed as attributes on it -- ``sys.modules`` alone is not
# enough because attribute access on the real ``omni`` won't fall
# through to ``sys.modules``.
import omni as _omni

for _mod in ("physics", "physics.tensors", "physx", "timeline", "usd"):
_stub = MagicMock()
sys.modules[f"omni.{_mod}"] = _stub
# Bind the leaf attribute so that ``omni.<leaf>`` resolves.
setattr(_omni, _mod.split(".", 1)[0], _stub)
for _mod in ("isaacsim.core", "isaacsim.core.simulation_manager"):
sys.modules.setdefault(_mod, MagicMock())

Expand Down
107 changes: 100 additions & 7 deletions source/isaaclab/test/assets/test_rigid_object_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,42 @@
The setup is a bit convoluted so that we can run these tests without requiring Isaac Sim or GPU simulation.
"""

"""Launch Isaac Sim Simulator first."""
"""Launch Isaac Sim Simulator first (when available)."""

from isaaclab.app import AppLauncher

HEADLESS = True
import os
import sys
from unittest.mock import MagicMock

# launch omniverse app
simulation_app = AppLauncher(headless=True).app
# When running kitless (e.g., ovphysx backend via run_ovphysx.sh), AppLauncher
# will try to boot Kit and hang. Skip it entirely: run_ovphysx.sh sets
# LD_PRELOAD to the ovphysx libcarb.so, which is the signature of a kitless
# ovphysx run. Also guard the case where neither LD_PRELOAD nor EXP_PATH is
# set (bare Python, no Kit at all).
_kitless = "ovphysx" in os.environ.get("LD_PRELOAD", "") or (
os.environ.get("LD_PRELOAD", "") == "" and "EXP_PATH" not in os.environ
Comment on lines 21 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Fragile kitless-detection heuristic may skip AppLauncher in standard Kit CI

The second branch of _kitless:

os.environ.get("LD_PRELOAD", "") == "" and "EXP_PATH" not in os.environ

treats any Python process with no LD_PRELOAD and no EXP_PATH as "kitless" and skips AppLauncher. An Isaac Sim Kit environment that happens to have cleared LD_PRELOAD (or never sets EXP_PATH in some CI configurations) would also satisfy this condition, silently bypassing the full Kit initialization. The physx/newton backends in the same file still require a live Kit session, so if those backends are exercised in the same test run the session state will be inconsistent.

Consider using a dedicated opt-in env var (e.g. ISAACLAB_KITLESS=1) rather than inferring from LD_PRELOAD/EXP_PATH.

)

from unittest.mock import MagicMock
if not _kitless:
from isaaclab.app import AppLauncher

simulation_app = AppLauncher(headless=True).app
else:
simulation_app = None
# Stub out the Kit/Omniverse modules that are not present under
# run_ovphysx.sh (pxr, carb, omni, omni.kit[.app] are real on PYTHONPATH).
# ``omni`` is a real namespace package, so missing submodules also need
# to be installed as attributes on it -- ``sys.modules`` alone is not
# enough because attribute access on the real ``omni`` won't fall
# through to ``sys.modules``.
import omni as _omni

for _mod in ("physics", "physics.tensors", "physx", "timeline", "usd"):
_stub = MagicMock()
sys.modules[f"omni.{_mod}"] = _stub
# Bind the leaf attribute so that ``omni.<leaf>`` resolves.
setattr(_omni, _mod.split(".", 1)[0], _stub)
for _mod in ("isaacsim.core", "isaacsim.core.simulation_manager"):
sys.modules.setdefault(_mod, MagicMock())

import numpy as np
import pytest
Expand Down Expand Up @@ -66,6 +92,15 @@
except ImportError:
pass

try:
from isaaclab_ovphysx.assets.rigid_object.rigid_object import RigidObject as OvPhysxRigidObject
from isaaclab_ovphysx.assets.rigid_object.rigid_object_data import RigidObjectData as OvPhysxRigidObjectData
from isaaclab_ovphysx.test.mock_interfaces.views import MockOvPhysxBindingSet

BACKENDS.append("ovphysx")
except (ImportError, AttributeError):
pass


def create_physx_rigid_object(
num_instances: int = 2,
Expand Down Expand Up @@ -206,6 +241,62 @@ def create_newton_rigid_object(
return rigid_object, mock_view


def create_ovphysx_rigid_object(
num_instances: int = 2,
device: str = "cuda:0",
):
"""Create a test OvPhysX RigidObject instance with mocked tensor bindings."""
body_names = ["base_link"]

obj = object.__new__(OvPhysxRigidObject)

obj.cfg = RigidObjectCfg(prim_path="/World/object")

# Create mock binding set
mock_bindings = MockOvPhysxBindingSet(
num_instances=num_instances,
num_joints=0,
num_bodies=1,
body_names=body_names,
asset_kind="rigid_object",
)
mock_bindings.set_random_data()

object.__setattr__(obj, "_device", device)
object.__setattr__(obj, "_ovphysx", MagicMock())
object.__setattr__(obj, "_bindings", mock_bindings.bindings)
object.__setattr__(obj, "_num_instances", num_instances)
object.__setattr__(obj, "_num_bodies", 1)
object.__setattr__(obj, "_body_names", body_names)

# Create RigidObjectData
data = OvPhysxRigidObjectData(mock_bindings.bindings, device)
data.num_instances = num_instances
data.num_bodies = 1
data._is_primed = True
object.__setattr__(obj, "_data", data)

# Build the buffers RigidObject normally allocates in _initialize_impl
# (_ALL_INDICES, _ALL_*_MASK, pinned CPU staging buffers, wrench buf).
# _create_buffers also instantiates real WrenchComposers; those get
# replaced with mocks just below.
obj._create_buffers()

# Replace the real wrench composers with mocks for iface coverage.
mock_inst_wrench = MockWrenchComposer(obj)
mock_perm_wrench = MockWrenchComposer(obj)
object.__setattr__(obj, "_instantaneous_wrench_composer", mock_inst_wrench)
object.__setattr__(obj, "_permanent_wrench_composer", mock_perm_wrench)

# Prevent __del__ / _clear_callbacks from raising
object.__setattr__(obj, "_initialize_handle", None)
object.__setattr__(obj, "_invalidate_initialize_handle", None)
object.__setattr__(obj, "_prim_deletion_handle", None)
object.__setattr__(obj, "_debug_vis_handle", None)

return obj, mock_bindings


def create_mock_rigid_object(
num_instances: int = 2,
device: str = "cuda:0",
Expand All @@ -226,6 +317,8 @@ def get_rigid_object(
):
if backend == "physx":
return create_physx_rigid_object(num_instances, device)
elif backend == "ovphysx":
return create_ovphysx_rigid_object(num_instances, device)
elif backend == "newton":
return create_newton_rigid_object(num_instances, device)
elif backend.lower() == "mock":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Added
^^^^^

* Added :class:`~isaaclab_ovphysx.assets.RigidObject` and
:class:`~isaaclab_ovphysx.assets.RigidObjectData` for single-actor rigid-body
simulation against the OVPhysX backend, satisfying the
:class:`~isaaclab.assets.BaseRigidObject` and
:class:`~isaaclab.assets.BaseRigidObjectData` contracts. Public surface
matches the PhysX/Newton conventions: ``write_root_*_to_sim_index`` /
``write_root_*_to_sim_mask`` writers (link- and com-frame variants),
``set_masses_*``, ``set_coms_*``, ``set_inertias_*`` setters, and the
external-wrench composers exposed via
:meth:`~isaaclab_ovphysx.assets.RigidObject.set_external_force_and_torque`.
* Added the ``RIGID_BODY_*`` :class:`TensorType` aliases in
:mod:`isaaclab_ovphysx.tensor_types` (``POSE``, ``VELOCITY``, ``WRENCH``,
``MASS``, ``COM_POSE``, ``INERTIA``; plus ``ACCELERATION``, ``INV_MASS``,
``INV_INERTIA`` declared for forward compatibility once the wheel ships
them).
* Added :class:`~isaaclab_ovphysx.assets.kernels` as a shared Warp-kernel
module (frame conversions, state concatenation, finite-difference
acceleration, index- and mask-style scatter writers) consumed by both the
rigid-object and articulation assets.
* Added USD prim-scan validation in
:meth:`~isaaclab_ovphysx.assets.RigidObject._initialize_impl`: a clear
``RuntimeError`` is raised when ``cfg.prim_path`` resolves to no
``UsdPhysics.RigidBodyAPI`` prim, multiple rigid-body prims, or a prim with
an enabled ``UsdPhysics.ArticulationRootAPI``.

Changed
^^^^^^^

* Changed :meth:`~isaaclab_ovphysx.physics.OvPhysxManager._release_physx` to
perform a soft reset (``physx.reset()``) and keep the cached
:class:`ovphysx.PhysX` reference alive across
:class:`~isaaclab.sim.SimulationContext` lifetimes, instead of dropping the
reference and triggering the wheel's dual-Carbonite static-destructor race.
:meth:`~isaaclab_ovphysx.physics.OvPhysxManager._warmup_and_load` now reuses
the cached instance on subsequent calls.
* Changed :meth:`~isaaclab_ovphysx.physics.OvPhysxManager._warmup_and_load` to
raise a clear ``RuntimeError`` when a later
:class:`~isaaclab.sim.SimulationContext` requests a different device than
the one the process is locked to, surfacing the wheel's process-global
device-mode lock as a Python error before
:exc:`ovphysx.types.PhysXDeviceError` would fire.
* Changed :meth:`~isaaclab_ovphysx.physics.OvPhysxManager._configure_physx_scene_prim`
to apply the ``UsdPhysics.PhysxSceneAPI`` schema and
``enableSceneQuerySupport`` on both CPU and GPU; GPU-only attributes
(``enableGPUDynamics``, ``broadphaseType``, the ``gpu*`` capacity attributes
from :class:`~isaaclab_ovphysx.physics.OvPhysxCfg`) remain gated on
``device == "gpu"``.
3 changes: 3 additions & 0 deletions source/isaaclab_ovphysx/isaaclab_ovphysx/assets/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
__all__ = [
"Articulation",
"ArticulationData",
"RigidObject",
"RigidObjectData",
]

from .articulation import Articulation, ArticulationData
from .rigid_object import RigidObject, RigidObjectData
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
from isaaclab.utils.wrench_composer import WrenchComposer

from isaaclab_ovphysx import tensor_types as TT
from isaaclab_ovphysx.assets.kernels import _body_wrench_to_world, _scatter_rows_partial
from isaaclab_ovphysx.physics import OvPhysxManager

from .articulation_data import ArticulationData
from .kernels import _body_wrench_to_world, _scatter_rows_partial, update_soft_joint_pos_limits
from .kernels import update_soft_joint_pos_limits

if TYPE_CHECKING:
from isaaclab.actuators import ActuatorBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@
from isaaclab.utils.warp import ProxyArray

from isaaclab_ovphysx import tensor_types as TT

from .kernels import (
_compose_body_com_poses,
from isaaclab_ovphysx.assets.kernels import (
_compose_root_com_pose,
_compute_heading,
_copy_first_body,
_fd_joint_acc,
_projected_gravity,
_world_vel_to_body_ang,
_world_vel_to_body_lin,
)

from .kernels import _compose_body_com_poses, _fd_joint_acc


class ArticulationData(BaseArticulationData):
"""Data container for an articulation backed by ovphysx tensor bindings.
Expand Down
Loading
Loading