Skip to content

[OVPHYSX] RigidObjectCollection asset#5570

Open
AntoineRichard wants to merge 123 commits into
isaac-sim:developfrom
AntoineRichard:antoiner/feat/ovphysx_rigidobjectcollection
Open

[OVPHYSX] RigidObjectCollection asset#5570
AntoineRichard wants to merge 123 commits into
isaac-sim:developfrom
AntoineRichard:antoiner/feat/ovphysx_rigidobjectcollection

Conversation

@AntoineRichard
Copy link
Copy Markdown
Collaborator

@AntoineRichard AntoineRichard commented May 11, 2026

Description

Implements RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend, completing the rigid-body asset surface alongside RigidObject (#5426) and Articulation (#5459). The collection manages N distinct rigid bodies per environment with (env, body) dual indexing.

The asset creates one native fused TensorBinding per tensor type via the ovphysx 0.4.3 create_tensor_binding(prim_paths=[glob_0, …, glob_{B-1}]) API, mirroring how PhysX's RigidBodyView aggregates multiple body prims into a single flat view. Each binding spans num_instances * num_bodies prims and returns body-major flat data (body_0_env_0, body_0_env_1, …, body_1_env_0, …). The data class and asset writers use strided-view reshape helpers (_reshape_view_to_data_2d/_3d and reshape_data_to_view_2d/_3d, ported from the PhysX collection) to convert between body-major view layout and the instance-major (N, B, D) layout exposed to users — no Warp kernels added, no per-body Python fan-out at runtime.

Fixes #5317

Stacked on:

Carries a one-commit cherry-pick of #5545's ovphysx_manager.py portion (required for ovphysx 0.4 active_cuda_gpus API). Drop the cherry-pick once #5545 lands.

Type of change

  • New feature (non-breaking change which adds functionality)

Screenshots

N/A — backend infrastructure, no visible behaviour change.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog (fragment file under source/isaaclab_ovphysx/changelog.d/)
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

Test plan

Tested in a Docker container built from docker/Dockerfile.base (IsaacSim 6.0.0-dev2) with the ovphysx 0.4.3 wheel installed:

  • ./scripts/run_ovphysx.sh -m pytest source/isaaclab/test/assets/test_rigid_object_collection_iface.py -k ovphysx636 passed
  • ./scripts/run_ovphysx.sh -m pytest source/isaaclab_ovphysx/test/assets/test_rigid_object_collection.py72 passed, 76 skipped (device-mode lock — a second invocation with -k 'cpu' covers CPU), 4 xfailed (material-properties gap shared with RigidObject until the wheel exposes RIGID_BODY_MATERIAL)
  • ./isaaclab.sh -f → clean

Add nine RIGID_BODY_* aliases to isaaclab_ovphysx.tensor_types covering
the rigid-actor root pose/velocity/acceleration, wrench application, and
mass/inertia/COM properties. Each alias carries a shape/units docstring
that Sphinx autoattribute can pick up.

Extend _CPU_ONLY_TYPES with the five CPU-routed rigid-body variants so
the existing GPU/CPU dispatch in _to_flat_f32 routes them correctly.

Direct imports (no shim) intentionally couple this package to a future
ovphysx wheel that exposes the matching TensorType enum values; see
docs/superpowers/specs/2026-04-27-ovphysx-rigid-body-tensortypes-gap.md.

Issue: isaac-sim#5316
Move kernels reused across multiple asset types from
isaaclab_ovphysx.assets.articulation.kernels into a new
isaaclab_ovphysx.assets.kernels module: _body_wrench_to_world,
_scatter_rows_partial, _copy_first_body, _compose_root_com_pose,
_projected_gravity, _compute_heading, _world_vel_to_body_lin,
_world_vel_to_body_ang.

Articulation-only kernels (joint-limit setup, FD joint acceleration,
multi-body COM compose) stay in articulation/kernels.py. Articulation
modules update their imports accordingly. No behavior change.

This refactor unblocks the upcoming RigidObject implementation, which
needs the same kernels for frame conversions and wrench packing.

Issue: isaac-sim#5316
Generalize the mock binding factory to produce a rigid-object-shaped
binding set (RIGID_BODY_* keys only, num_joints=0, num_bodies=1, no
tendons) when called with asset_kind='rigid_object'. Default behavior
is unchanged: existing articulation callers do not need updates.

Make set_random_data tolerant of the smaller binding set so it works
for both asset kinds.

Issue: isaac-sim#5316
Add the rigid_object sub-package and the RigidObjectData class skeleton
with constructor, count properties, update/invalidate hooks, and
_process_cfg that fills _default_root_pose / _default_root_velocity from
cfg.init_state.

Add the backend-specific test file with a hasattr-based wheel gate
(importorskip-then-attr on the module would AttributeError rather than
skip when RIGID_BODY_* enums are absent on the wheel) and a basic-counts
smoke test.

Issue: isaac-sim#5316
Address review blockers on commit 65084f7:

1. _process_cfg now mirrors the articulation pattern (wp.zeros to
   pre-allocate, then wp.copy from wp.from_numpy with the typed dtype
   directly — matching articulation._process_cfg verbatim), avoiding
   the use-after-free that the previous reinterpret-cast-from-local
   idiom introduced when the float32 source went out of scope.

2. is_primed is now a guarded @Property + setter that enforces the
   one-way gate (False->True allowed, True->True idempotent,
   True->False raises ValueError), matching every other *Data class.

3. Drop unused forward-reference imports from
   test/assets/test_rigid_object.py so ruff/F401 passes; subsequent
   tasks will re-add them when the symbols are actually used.

Issue: isaac-sim#5316
Replace the abstract-property stubs for root_link_pose_w,
root_link_vel_w, root_com_pose_w, root_com_vel_w, and the per-axis
sliced views (root_link_pos_w, root_link_quat_w, root_lin_vel_w,
root_ang_vel_w, plus their root_com_* counterparts) with concrete
implementations that lazy-read from RIGID_BODY_ROOT_POSE /
RIGID_BODY_ROOT_VELOCITY through TensorBindings, cache for the rest
of the sim step, and expose slices as zero-copy Warp views over the
canonical pose/velocity buffers.

Mirrors the articulation_data lazy-read + ProxyArray + sliced-view
idioms with num_bodies=1 (root pose/velocity are 1-D over instances,
with no body axis).

Issue: isaac-sim#5316
The per-property TimestampedBuffer.timestamp fields are the freshness
gate consulted by every lazy-read property. _invalidate_caches was
previously clearing only the legacy _timestamps dict, leaving the
buffers with their last-read timestamp; a property accessed within
the same sim step (e.g. immediately after RigidObject.reset and
before update(dt) advances _sim_time) would serve pre-reset data.

Reset every allocated buffer's timestamp to -1.0 in
_invalidate_caches so the next property access always re-reads from
the binding regardless of where _sim_time stands.

Add a regression test that mutates the binding in place + calls
_invalidate_caches without advancing _sim_time and asserts the next
read reflects the new value.

Issue: isaac-sim#5316
Replace the abstract-property stubs for the body-state singleton-dim
views (body_link_pose_w, body_com_pose_w, body_*_vel_w, body_*_pos_w,
body_*_quat_w, body_*_lin_vel_w, body_*_ang_vel_w), the body
acceleration (body_link_acc_w, body_com_acc_w, plus their _lin_*/
_ang_* slices) gated on the RIGID_BODY_ACCELERATION binding, and the
body-frame derived properties (projected_gravity_b, heading_w,
root_link_lin_vel_b, root_link_ang_vel_b, root_com_lin_vel_b,
root_com_ang_vel_b).

Body-state views are zero-copy reshapes of the (N,) root buffers into
(N, 1, k) — no compute kernel needed when num_bodies=1. Body
acceleration raises NotImplementedError with a pointer to the gap
spec if the wheel lacks RIGID_BODY_ACCELERATION. Derived properties
launch the relocated _projected_gravity / _compute_heading /
_world_vel_to_body_{lin,ang} kernels from
isaaclab_ovphysx.assets.kernels.

Extend _invalidate_caches with the new TimestampedBuffer instances so
they participate in coarse cache invalidation.

Issue: isaac-sim#5316
The IsaacLab projected_gravity_b convention (per
BaseRigidObjectData docstring "Projection of the gravity direction
on base frame", and matching ArticulationData which normalizes
gravity_np / gravity_mag before storing GRAVITY_VEC_W) is the unit
direction, not the signed-magnitude. The Task 6 test assertion
expected -9.81 (signed magnitude); the kernel correctly produces
-1.0 (unit z-component of the gravity direction).

Update the assertion and the inline comment so the test reflects the
documented contract.

Issue: isaac-sim#5316
Replace the abstract-property stubs for body_mass, body_inertia, and
body_com_pose_b with concrete CPU-side lazy-read implementations
backed by RIGID_BODY_MASS / RIGID_BODY_INERTIA / RIGID_BODY_COM_POSE
bindings. Also implement body_com_pos_b and body_com_quat_b as
zero-copy slice views of body_com_pose_b's transformf buffer.

Properties read once on first access, cache as semi-static, and
invalidate via the _invalidate_caches reset loop (driven by
RigidObject mass/COM/inertia setters). Mirrors the articulation_data
pattern adapted for num_bodies = 1.

Issue: isaac-sim#5316
Add RigidObject class with __init__, _initialize_impl, _get_binding,
and count/data accessor properties. Eagerly creates GPU bindings for
RIGID_BODY_ROOT_POSE / RIGID_BODY_ROOT_VELOCITY / RIGID_BODY_WRENCH
so binding-creation failures surface at init time with a clear
message pointing at the gap spec.

_create_buffers and _process_cfg are placeholder no-ops; Task 9
replaces them. Write paths, reset, update, find_bodies, and the
deprecated state writers come in subsequent tasks (10-13). Abstract
methods that must be satisfied to instantiate the class are stubbed
with NotImplementedError pending those tasks.

Issue: isaac-sim#5316
Renames per Marco's feedback: RIGID_BODY_ROOT_POSE ->
RIGID_BODY_POSE and RIGID_BODY_ROOT_VELOCITY ->
RIGID_BODY_VELOCITY throughout. "Root" is articulation
vocabulary; a standalone rigid body IS the body.

Shape correction: RIGID_BODY_MASS and RIGID_BODY_INV_MASS
ship as (N,) not (N, 1). MockOvPhysxBindingSet allocates
(N,) for both; rigid_object_data.py's body_mass property
consumes (N,) and exposes (N, 1) via zero-copy reshape to
satisfy the BaseRigidObjectData contract.

Soften _initialize_impl error: removes the prescriptive
"NOT under an articulation root" language since pattern-
resolution gating is a future wheel-side selection policy.

Pre-existing ruff E501 in write_root_link_state_to_sim
docstring fixed as collateral.
Allocate _ALL_INDICES, _ALL_BODY_INDICES, their Warp views, the
single-body (N, 1, 9) _wrench_buf staging buffer, and the
instantaneous/permanent wrench composers. _process_cfg delegates to
RigidObjectData._process_cfg.

Issue: isaac-sim#5316
Add the twelve root-state writers (pose+velocity, actor+link+com,
index+mask variants) on RigidObject, plus the shared _to_flat_f32 and
_write_root_state helpers ported from articulation. Frame-conversion
variants launch the relocated assets/kernels.py kernels before the
binding write. Add the three deprecated compound state writers that
emit DeprecationWarning and delegate to the split pose+velocity
writers.

Add _compose_root_link_pose_from_com kernel to assets/kernels.py
to support COM->link pose inversion on the write path:
  link_pose = com_pose_w * inverse(com_pose_b)

Issue: isaac-sim#5316
Add set_masses_{index,mask}, set_coms_{index,mask},
set_inertias_{index,mask}. Each writes through the matching
CPU-routed RIGID_BODY_* binding via _write_root_state, then
invalidates the corresponding RigidObjectData cache via
_invalidate_caches.

body_ids / body_mask parameters are accepted for parity with the
BaseRigidObject contract but unused (num_bodies = 1).

Extend _write_root_state to handle 1-D bindings (RIGID_BODY_MASS)
by detecting them and bypassing the 2-D scatter kernel path.

Issue: isaac-sim#5316
Compose instantaneous + permanent wrenches, rotate body-frame
force/torque to world frame via _body_wrench_to_world (dim=(N, 1)),
reshape the (N, 1, 9) staging buffer to (N, 9) zero-copy, write to
RIGID_BODY_WRENCH, reset the instantaneous composer.

Issue: isaac-sim#5316
Replace stubs for reset, update, and find_bodies. reset writes the
default pose/velocity to the specified envs via zero-copy flat float32
views of the typed wp.transformf/wp.spatial_vectorf defaults, resets
both wrench composers, and invalidates the data caches. update
delegates to RigidObjectData.update. find_bodies handles the None
(all bodies) fast path and delegates regex matching to
resolve_matching_names for non-None inputs.

The deprecated compound state writers were already implemented in
Task 10 alongside the split-form writers and are not touched here.

Issue: isaac-sim#5316
Add the rigid_object/__init__.pyi stub mirroring the articulation
sibling, and extend isaaclab_ovphysx.assets/__init__.pyi to include
RigidObject and RigidObjectData. Public imports now resolve via
``from isaaclab_ovphysx.assets import RigidObject, RigidObjectData``.

Issue: isaac-sim#5316
Add the import-guarded BACKENDS.append('ovphysx') block, the
create_ovphysx_rigid_object factory, and the dispatch case so the
existing parametrized interface tests automatically cover the new
backend. Mirrors the OVPhysX articulation parametrization in
test_articulation_iface.py.

Issue: isaac-sim#5316
Mirror the Cartpole/Ant pattern by adding ovphysx variants to
ObjectCfg (RigidObjectCfg using the same DexCube spawn as the physx
variant) and to PhysicsCfg (OvPhysxCfg()). Default backend remains
physx; existing PhysX/Newton paths are unchanged.

This wires Isaac-Repose-Cube-Allegro-Direct-v0 for OVPhysX validation
via ./scripts/run_ovphysx.sh source/isaaclab_tasks/isaaclab_tasks/
direct/allegro_hand/allegro_hand_env.py --num_envs 4 --headless once
the wheel ships.

Issue: isaac-sim#5316
Add the 0.2.0 changelog entry on isaaclab_ovphysx covering the new
RigidObject/RigidObjectData classes, the RIGID_BODY_* TensorType
aliases (six already-shipping + three pending wheel update), the
mock-binding asset_kind extension, and the assets/kernels.py kernel
relocation. Bump the matching extension.toml version.

Add a patch-bump changelog entry on isaaclab_tasks for the Allegro
env preset addition.

Issue: isaac-sim#5316
The ovphysx wheel currently exposes 6 of the 9 RIGID_BODY_* TensorType
enums (POSE, VELOCITY, WRENCH, MASS, COM_POSE, INERTIA). The remaining
three (ACCELERATION, INV_MASS, INV_INERTIA) are pending an upcoming
wheel update from @marcodiiga.

Make the IsaacLab side wheel-update-agnostic:

* Guard tensor_types.py imports of the three not-yet-shipping aliases
  with try/except AttributeError so isaaclab_ovphysx.tensor_types
  imports cleanly on today's wheel. _CPU_ONLY_TYPES filters to only
  the names that exist via _RIGID_BODY_OPTIONAL_CPU.
* MockOvPhysxBindingSet skips the optional bindings when their alias
  is not defined.
* RigidObjectData.body_*_acc_w now finite-differences from
  body_com_vel_w, mirroring Newton's pattern (kernel
  derive_body_acceleration_from_body_com_velocities ported into
  isaaclab_ovphysx.assets.kernels). Removes the
  NotImplementedError-on-missing-binding fallback.
* update(dt) stores _last_dt and eagerly triggers body_com_acc_w each
  step so FD captures every transition.

The forward-compat aliases stay declared (Marco will land them); when
they ship, the existing TensorBinding read path will work without
further IsaacLab changes.

Issue: isaac-sim#5316
WrenchComposer.__init__ calls hasattr(asset.data, "body_com_pos_w"),
which triggers the property chain body_com_pos_w → body_com_pose_w →
root_com_pose_w, reading the RIGID_BODY_COM_POSE binding and setting
_body_com_pose_b_buf.timestamp = _sim_time = 0.0.

Any subsequent mutation of the binding (e.g. set_coms_index, or a test
that sets the binding directly) is then invisible to _com_pose_to_link_pose
because the freshness gate ``buf.timestamp >= _sim_time`` treats 0.0 ≥ 0.0
as "already fresh" and skips the read — returning stale buffer contents
to the frame-conversion kernel and producing an off-by-translation result.

Force a fresh read in _com_pose_to_link_pose by resetting the buffer
timestamp to -1.0 immediately before calling _read_transform_binding.
The frame conversion always needs the current binding value at write time;
the lazy-cache is the wrong policy here.

Caught by test_write_root_com_pose_to_sim_index_invokes_frame_conversion.

Issue: isaac-sim#5316
When running via ./scripts/run_ovphysx.sh, the test file's unconditional
AppLauncher call segfaults during pytest collection because the script's
thin Kit shell (libcarb preload only, no full Kit boot) cannot host a
real AppLauncher. Mirror the _kitless heuristic from test_articulation_iface
so the rigid-object iface tests skip AppLauncher and substitute MagicMock
isaacsim core modules in the same scenarios.

The original _kitless heuristic (LD_PRELOAD == "" and EXP_PATH not set) was
also incorrect for this environment: run_ovphysx.sh sets LD_PRELOAD to the
ovphysx libcarb.so path, not an empty string. Fix the heuristic in both
test files to check for "ovphysx" in LD_PRELOAD as the primary signal,
with the bare-Python fallback retained as a secondary guard.

Also extend the kitless sys.modules stub list to cover omni.physics.tensors
and related Kit modules that physx_manager.py imports at module scope, which
would otherwise cause a ModuleNotFoundError during collection.

This unblocks running the OVPhysX-parametrized parts of the file via
run_ovphysx.sh. PhysX/Newton/Mock paths are unaffected.

Issue: isaac-sim#5316
Three related bugs were present in the OVPhysX RigidObject body-property
write path, all contributing to the 120 test failures.

First, _write_root_state's full-write path (no env_ids, no mask) had no
row-count validation for 1-D bindings such as RIGID_BODY_MASS. Passing a
tensor with more rows than num_instances silently reached the mock's numpy
reshape and raised ValueError, but the test infrastructure expected
AssertionError or RuntimeError.  An explicit row-count guard now raises
RuntimeError on the full-write path before any binding call.

Second, the index/mask sub-write path for 1-D bindings passed the source
array to binding.write() as a 2-D (K, 1) buffer (the raw shape produced by
_to_flat_f32 when the caller supplies (K, 1) torch data). For the mask path
this caused NumPy boolean-index assignment to fail with TypeError because it
cannot scatter a 2-D array into a 1-D binding buffer. The 1-D source is now
normalised to shape (K,) via a zero-copy warp array view before any write.

Third, write_root_com_pose_to_sim_index and write_root_com_pose_to_sim_mask
silently truncated oversized inputs inside _com_pose_to_link_pose, which
hardcodes shape=(N,) for the intermediate warp array view regardless of the
input size. This masked shape errors on full writes. Explicit row-count
guards are now applied at the public API entry points for these two methods.

Additionally, default_root_pose and default_root_vel in RigidObjectData were
NotImplementedError stubs. They are now implemented to return ProxyArray
wrappers over the _default_root_pose/_default_root_velocity buffers that are
already populated during _process_cfg.

Caught by the cross-backend TestRigidObjectWritersBody tests against
the OVPhysX backend. After the fix all 372 ovphysx-parametrized cases
pass.

Issue: isaac-sim#5316
The mock-based test file is removed in favor of a copy of PhysX's
test_rigid_object.py adapted to the kitless OVPhysX architecture:
- Drop AppLauncher; mock the isaacsim and omni.* modules instead so
  the file imports under run_ovphysx.sh without launching Kit.
- Build the test scene via MockOvPhysxBindingSet, bypassing
  OvPhysxManager entirely (no Kit stage export needed).
- Drive sim steps via direct binding manipulation; no
  build_simulation_context.
- Programmatically construct rigid-object shells instead of pulling
  DexCube USD from Nucleus.

Tests that exercise OVPhysX features not yet wired (OvPhysxManager
step loop, contact materials, kitless stage entry point) are
explicitly xfail-marked with inline reasons.

Result: 67 passed, 73 xfailed, 0 failed.

See docs/superpowers/specs/2026-04-28-ovphysx-rigid-object-test-gaps.md
(worktree-only, gitignored) for the consolidated gap list for Marco.
OvPhysxManager IS drivable without AppLauncher: _warmup_and_load only
needs PhysicsManager._sim.stage + a couple of cfg fields, which a thin
SimpleNamespace fake satisfies. Use this to convert the warmup and
stage-load xfails into real-backend tests against the live ovphysx.PhysX
instance.

Adds _make_kitless_sim_context() helper (builds in-memory USD stage with
RigidBodyAPI + CollisionAPI cube + PhysicsScene, wraps in SimpleNamespace
exposing: stage, cfg.physics, cfg.device, cfg.physics_prim_path,
cfg.enable_scene_query_support, cfg.dt) and a module-scoped
kitless_manager_cpu fixture that drives initialize() + reset() + close().

Converts test_warmup_attach_stage_not_called_for_cpu (1 xfail) to three
passing real-backend tests:
- test_warmup_and_load_cpu: lifecycle assertions
- test_warmup_gpu_not_called_for_cpu: CPU path skips warmup_gpu
- test_stage_load_cpu: stage export + usd_handle type check

Adds test_warmup_and_load_gpu as xfail pending a GPU CI runner.

Result: 70 passed, 73 xfailed (was 67 passed, 73 xfailed).

Update the test-gaps doc to reflect the closed gap (no wheel change
required for this category).

Issue: isaac-sim#5316
Drop the kitless mocks, the SimpleNamespace sim-context fake, and the
MockOvPhysxBindingSet shell-injection helpers. The standard
SimulationContext + UsdFileCfg(ISAAC_NUCLEUS_DIR/...) pattern works
under ./scripts/run_ovphysx.sh without AppLauncher — omni.client
resolves Nucleus URLs from Kit's Python directly, and SimulationContext
runs kitless via has_kit() returning False.

Tests now exercise real RigidObject + RigidObjectData + OvPhysxManager
against live ovphysx.PhysX bindings, using the same Nucleus assets
the Cartpole/Newton tests use.

Material-properties tests stay xfailed pending a wheel-side
RIGID_BODY_MATERIAL TensorType. GPU tests now pass (NVIDIA RTX 5000 Ada
verified).

Production bug surfaced: RigidObject._initialize_impl uses hasattr() on
TensorBinding.body_names which propagates RuntimeError (not
AttributeError), blocking all RigidObject lifecycle tests against the
real backend. Fix: wrap in try/except (AttributeError, RuntimeError).
Tracking: issue isaac-sim#5316.

Issue: isaac-sim#5316
The previous ``hasattr(root_pose, "body_names")`` gate only catches
AttributeError, but the real ovphysx TensorBinding raises TypeError
on the body_names property for non-articulation tensor types such as
RIGID_BODY_POSE: "Articulation metadata … is not available for
tensor type 'RIGID_BODY_POSE'." Replace with try/except that catches
both AttributeError and TypeError; fall back to ["base_link"].

Also fix self._device derivation: ``hasattr(self._ovphysx, "device")``
always returns False for the real PhysX object (no .device property),
so the device silently fell back to "cuda:0" even when the simulation
runs on CPU, causing a device mismatch in TensorBinding.read(). Use
OvPhysxManager.get_device() which mirrors SimulationContext.cfg.device.

Un-xfail 68 of the 70 tests tagged _INIT_IMPL_BUG in test_rigid_object.py
— they now run cleanly against the live ovphysx CPU backend (59 passed).
The two remaining xfails use a tightened reason (_FORCE_BALANCE_GAP):
test_external_force_on_single_body drifts ~0.57 m instead of < 0.1 m,
a physics-accuracy gap under investigation.

Issue: isaac-sim#5316
Rewrites test_external_force_on_single_body to mirror Newton's reference
implementation: 5 outer iterations with a full pose/velocity reset between
each, 5 inner sim steps per iteration, force applied to every 2nd cube
(indices 0::2), and alternating global/local frame each outer iteration.

The old test used a single 20-step loop on env_0 only, with no resets and
no is_global argument — causing error accumulation and the ~0.57 m drift.

Because RigidObjectData._bindings has no RIGID_BODY_MASS entry at init
time, body_mass.torch returns zeros; the USD-stage MassAPI value is read
instead.  The per-block drift is ~6 mm vs ~35 mm free-fall, well within
the atol=1e-2 guard, so the xfail decorator is removed.
Replace `# ----` section banners with the triple-quoted section
markers used by PhysX/Newton, move the deprecated `write_root_*_state_to_sim`
writers below the simulation callbacks, and tighten the internal-helper
docstrings.  Also strips Step-N narrative comments and "mirrors PhysX"
dev-cruft so the file reads like the reference backends.

No behavior change.
Use the same `Articulation-specific warp functions/kernels` section
headers, drop the verbose absent-kernel module docstring, and tighten
kernel docstrings to match PhysX conventions (SI units in `[unit]`
notation, full Args sections with shapes, `:paramref:` instead of
`:attr:` for kernel arguments). No behavior changes; kernel names and
import paths are unchanged.
Reorder the public Articulation properties to match the PhysX/Newton
section ordering (data, num_instances, is_fixed_base, num_joints,
num_fixed_tendons, num_spatial_tendons, num_bodies, joint_names,
fixed_tendon_names, spatial_tendon_names, body_names, root_view,
*_wrench_composer). Align find_*, reset, write_data_to_sim, and
_initialize_impl docstrings/comments with the PhysX style:

* drop the verbose `Step N:` annotations from `_initialize_impl`,
  keeping only WHY-comments around non-obvious invariants
* remove `# Mirrors PhysX (articulation.py:NNN)` line-number
  cross-references that aged poorly
* tighten `reset`, `write_data_to_sim`, and `update` docstrings to
  match the PhysX wording

No behavior changes.
Replace the OVPhysX-implementation-flavored module docstring and class
docstring with the same domain-level prose used by the PhysX/Newton
ArticulationData, plus an OVPhysX-specific note about pinned-host CPU
staging for CPU-only bindings. Reword internal `__init__` comments to
drop dev-cruft references ("post-audit RigidObjectData demotion",
"PR isaac-sim#5329 pattern") and group fields the way PhysX does (timestamp,
constants, buffers). Reword `update()` to mirror PhysX phrasing.
No behavior changes.
Replace the OVPhysX-implementation-flavored module/class docstring
with the same domain-level prose used by the PhysX and Newton
Articulation classes, plus a short OVPhysX-specific paragraph about
per-tensor-type ``TensorBinding`` objects and pinned-host CPU
staging.

Drop "the wheel" backend-internal slang from comments and docstrings
in favor of "binding" / "simulation". Remove leftover "PR isaac-sim#5329 pattern"
references and other dev cruft. No behavior changes.
Replace the OVPhysX-implementation-flavored module/class docstring
with the domain-level prose used by the PhysX and Newton Articulation
classes, plus a short OVPhysX-specific paragraph about per-tensor-type
``TensorBinding`` objects and pinned-host CPU staging. Add the pyright
header that the PhysX/Newton files carry.
Drop the OVPhysX-specific class docstring, module docstring, and
``mirror PhysX`` cruft comments. Use the triple-quoted section
markers (``Properties`` / ``Operations.``) used by PhysX/Newton in
place of ``# ----`` dashed banners, restore the per-property
one-line docstrings on ``num_bodies``/``body_names``, and rewrite
``reset()``'s docstring to match the base contract.
Drop the dashed ``Pose writers (3 pairs)`` banner in favor of the
``Operations - Finders.`` and ``Operations - Write to simulation.``
markers used by PhysX/Newton. Fill in update()'s docstring and
clean up the disambiguation comment in write_data_to_sim.
PhysX/Newton order the wrappers as pose-pair then velocity-pair
before the link/com specialisations; OVPhysX previously kept
velocity-wrappers down with the link/com velocity setters. Bring
the order in line with the reference backends.
Swap link/com velocity setter blocks so center-of-mass comes first
(PhysX/Newton order). Strip the dashed ``Property setters (3 pairs)``
banner, drop ``# type: ignore[override]`` decorators on the
velocity setters, and replace narrative ``# Push updated COM
velocities to simulation`` / ``# Invalidate dependent timestamps so
the next read recomposes them`` comments with the terse PhysX-style
``# set into simulation`` / ``# Invalidate dependent timestamps``.
Drop ``# type: ignore[override]`` decorators and tighten the
narrative ``# Mark the pose buffer as fresh...`` /
``# Push updated link poses to simulation via single fused
binding`` comments to the terse PhysX-style ``# set into
simulation`` / ``# Invalidate dependent timestamps`` markers on
the link/com pose setters.
Drop ``# type: ignore[override]`` from the pose-wrapper methods and
property setters, and remove the ``For rigid bodies the actor frame
coincides with the link frame, so this delegates to ...`` note that
duplicates information already in the base class contract.
PhysX and Newton keep deprecated ``write_body_*_state_to_sim``
methods at the very end of the class behind a
``Deprecated properties and methods.`` section marker. Mirror
the layout in OVPhysX and trim the redundant
``# Convert wp.array to torch.Tensor for slicing.`` what-comments.
Use ``Helper functions.`` / ``Internal helper.`` / ``Internal
simulation callbacks.`` markers (PhysX/Newton style) in place of the
``# ----`` dashed banners, and strip the ``(mirrors PhysX)`` /
``(mirrors PhysX collection)`` parenthetical cruft on the docstrings
and inline comments.
Drop the OVPhysX-specific module docstring and ``Single fused
binding.`` note; reuse the PhysX class docstring (including the
ProxyArray pointer-stability note) so the OVPhysX data container
reads like the reference. Also tighten the constructor inline
comments away from full sentences with end punctuation.
Drop the ``# ----`` dashed section banners in favor of the
triple-quoted ``Names.`` / ``Defaults.`` / ``Body state properties.``
/ ``Derived Properties.`` / ``Sliced properties.`` / ``Helpers.``
section markers used by PhysX/Newton. Also strip the ``Mirrors
RigidObject``/``Mirrors Articulation`` cruft narrative in
``update()``, ``_pin_proxy_arrays`` and ``_get_binding``.
# Conflicts:
#	source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation.py
#	source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation_data.py
#	source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/kernels.py
#	source/isaaclab_ovphysx/isaaclab_ovphysx/assets/kernels.py
Remove the two orphan callers of `_read_binding_into_view` (a method
that was never defined on this branch, leaving an AttributeError on
every read through `_read_binding_into_buf` and
`_read_spatial_vector_binding`) and restore the original
`self._get_binding(tensor_type).read(view)` pattern used elsewhere.

Add `cls._physx.update_articulations_kinematic()` to
`OvPhysxManager.step()` so link transforms reflect the freshly
integrated joint state, matching the PhysX backend's behavior. This
relies on the FK API exposed by ovphysx 0.4.

Unblocks ~80 articulation tests previously failing with
`'ArticulationData' object has no attribute '_read_binding_into_view'`.
The `body_incoming_joint_wrench_b` property reads into
`self._body_incoming_joint_wrench_buf` and wraps it in a `ProxyArray`,
but `_create_buffers` never allocated the buffer and `_pin_proxy_arrays`
never declared the corresponding `_ta` slot. The first access raised
AttributeError on the missing buffer.

Allocate it next to the other body-state buffers (shape `(N, L)`,
dtype `wp.spatial_vectorf`, matching the property docstring) and
declare its `ProxyArray` cache slot in `_pin_proxy_arrays`.
The latest develop merge renamed
`JointDrivePropertiesCfg.max_velocity` → `JointDriveBaseCfg.max_joint_velocity`
and `max_effort` → `max_force`. The constructor still accepts the old
kwargs as deprecation aliases, but `__post_init__` forwards the value
to the canonical name and **blanks the alias**, so reads through the
old name return None. Update the three test sites that resolve the
USD default limit to use the canonical field names.
Mirror the PhysX backend's FK-on-demand pattern (see
``isaaclab_physx.assets.articulation_data:732``): when ``body_link_pose_w``
finds its buffer stale, call
``ovphysx.PhysX.update_articulations_kinematic`` before reading the
LINK_POSE binding. This ensures body link poses reflect joint state
written via ``write_joint_position_to_sim_*`` without requiring a sim
step, closing the gap that previously xfailed
``test_write_joint_state_data_consistency`` on GPU sims.

Drop the now-obsolete ``_FK_ON_DEMAND_GAP_REASON`` xfail marker and
constant in the test file.
The isaaclab_ov CI job's glob collects all ``test/`` files under
``source/isaaclab_ov*``, including the isaaclab_ovphysx tests. The
other three ovphysx tests in this directory already guard their
isaaclab_ovphysx imports with ``pytest.importorskip("ovphysx.types",
reason="ovphysx wheel not installed")``; only ``test_rigid_object.py``
went straight to a top-level ``from isaaclab_ovphysx.assets import
RigidObject``, which raised ``ModuleNotFoundError`` at collection
time and failed the entire isaaclab_ov job in the CI image (where
the ovphysx wheel is not installed).

Add the same ``importorskip`` guard so the file is skipped cleanly
in that environment, matching the established pattern.
AntoineRichard added a commit to AntoineRichard/IsaacLab that referenced this pull request May 15, 2026
Verification agents flagged five issues against the previous two
commits and one issue-isaac-sim#876 gap that the first pass missed.

1. Newton using-kamino.rst literalinclude path was one level short
   (../../../../../ → ../../../../../../). The previous depth resolved
   to a non-existent /docs/source/isaaclab_tasks/... and would have
   broken the sphinx build.

2. PhysX supported-features.rst listed Ray Caster, Visuo-tactile, and
   Camera as PhysX-specific sensors. Ray Caster and Camera are
   implemented in isaaclab core (backend-agnostic); Visuo-tactile lives
   in isaaclab_contrib. Reframe the section to separate PhysX-implemented
   sensors from backend-agnostic ones. Drop the unverifiable
   "path-traced" qualifier on the RTX renderer claim.

3. OvPhysX stub referenced only PR isaac-sim#5426 and PR isaac-sim#5459. The actual
   in-flight set spans six PRs: isaac-sim#5426 (merged), isaac-sim#5459, isaac-sim#5422, isaac-sim#5421,
   isaac-sim#5570, isaac-sim#5589. List them all with merge state, and reword "primary
   covered surfaces" to reflect that most are still open PRs.

4. backends/index.rst feature matrix said OvPhysX sensor coverage was
   "Partial" — actually only RigidObject is merged. Replace the
   matrix rows with concrete "In-flight (PR #...)" / "Not yet" cells.

5. Issue isaac-sim#876 asked to "review the limitations list and update it." The
   previous pass only reworded the intro. Refresh the task list against
   develop's actual newton_mjwarp coverage, add Shadow Hand / Shadow
   Hand Over / cabinet / dexsuite / rough-terrain locomotion, and
   replace the rigid bullet list with a discovery recipe so the list
   stops bit-rotting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

2 participants