Skip to content

[OVPHYSX] Articulation rewrite (data class + asset class + kernels)#10

Draft
AntoineRichard wants to merge 53 commits into
antoiner/feat/ovphysx_rigidobjectfrom
antoiner/feat/ovphysx_articulation
Draft

[OVPHYSX] Articulation rewrite (data class + asset class + kernels)#10
AntoineRichard wants to merge 53 commits into
antoiner/feat/ovphysx_rigidobjectfrom
antoiner/feat/ovphysx_articulation

Conversation

@AntoineRichard

Copy link
Copy Markdown
Owner

Description

Drastic rewrite of OVPhysX Articulation and ArticulationData so they follow the same shape as the post-refactor OVPhysX RigidObject (PR isaac-sim#5426 on isaac-sim/IsaacLab), with the API surface mirroring Newton Articulation and behavior parity with PhysX Articulation. Single-PR atomic rewrite, clean break (no deprecation aliases for OVPhysX-introduced renames; framework-inherited deprecated shims kept).

The OVPhysX articulation diverged significantly from the rest of the framework conventions. This PR brings it back in line: same docstring template, same section ordering, same naming, same internal patterns, same lifecycle.

This PR is stacked on the rigid-object refactor (antoiner/feat/ovphysx_rigidobject, isaac-sim PR isaac-sim#5426). Once isaac-sim#5426 merges to develop, the upstream PR for this work will be retargeted to develop.

Status: draft. Implementation is complete (all writers, setters, properties, lifecycle, actuator pipeline); the verbatim PhysX test mirror that already lives in tree (test_articulation.py, ~210 parametrizations) currently shows 104 failures from a single root-cause bug — _binding_read(TT.BODY_COM_POSE, ...) during _initialize_impl is not routing through pinned-host staging despite the type being in _CPU_ONLY_TYPES. Iterating on that bug + remaining triage in a fresh-context follow-up session.

Fixes # (none — internal refactor; no associated issue)

Design references

  • Spec: docs/superpowers/specs/2026-04-30-ovphysx-articulation-rewrite-design.md (gitignored, local).
  • Plan: docs/superpowers/plans/2026-04-30-ovphysx-articulation-rewrite-plan.md (gitignored, local).
  • Test contract: source/isaaclab_ovphysx/test/assets/test_articulation.py is a verbatim 1-to-1 mirror of isaaclab_physx.test.assets.test_articulation (~210 parametrizations) — landed in the rigid-object PR [OVPHYSX] RigidObject + RigidObjectData asset isaac-sim/IsaacLab#5426. The _ovphysx_session_patches and _ovphysx_skip_other_device autouse fixtures bridge the kitless invocation gap (see the rigid-object test header).

Architectural changes

OVPhysX RigidObject is the design template. It has navigated the hybrid OVPhysX surface — Newton-style mask+index dual API + PhysX-style CPU-only bindings via pinned-host staging à la isaac-sim#5329 + pull-to-refresh binding.read(target):

  • Eager TimestampedBufferWarp allocation in _create_buffers (single source of truth — no _invalidate_caches / _ensure_*_buffers / _ensure_root_buffers machinery).
  • Pinned-host CPU staging buffers for every CPU-only binding (mass, COM, inertia, all DOF properties, tendon properties).
  • _binding_read / _binding_write / _stage_to_pinned_cpu helpers route CPU-only types through pinned-host memory.
  • Every public property returns a ProxyArray (warp + torch dual view); raw wp.array for one-shot config buffers.
  • Counts and names (num_instances, num_bodies, num_joints, body_names, joint_names, ...) demoted from @property to plain instance attributes.
  • Dual mask+index API on every writer/setter (*_index accepts partial data; *_mask accepts full data with a wp.bool mask).
  • All write_* / set_* parameters are kwarg-only after *,. No positional. No full_data flag anywhere.
  • The deprecated _write_body_state plumbing layer is removed; deprecated state-writer shims (write_root_state_to_sim, etc.) call the public write_*_to_sim_index methods directly, mirroring RigidObject.

Articulation-specific surface mirrors Newton 1-to-1: joint-state writers, joint-property writers (CPU-only), body-property setters (multi-body shape), joint-command target setters, external-wrench setters via WrenchComposer, fixed/spatial tendon setters, deprecated state-writer shims, full actuator pipeline (compute, _apply_actuator_model, _process_actuators_cfg).

Files changed

  • source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation.py — full rewrite (~3863 lines, matches Newton).
  • source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation_data.py — full rewrite (~2504 lines).
  • source/isaaclab_ovphysx/isaaclab_ovphysx/assets/kernels.py — gained 6 articulation kernels migrated from the stop-gap kernels_old.py (now deleted): _compose_root_com_pose, _compute_heading, _copy_first_body, _projected_gravity, _world_vel_to_body_ang, _world_vel_to_body_lin. Plus 2 new joint-property kernels (write_joint_position_limit_to_buffer_index/mask for trailing-dim-2 limits, write_joint_friction_to_buffer_index/mask for the broadcast-coefficient pattern).
  • source/isaaclab_ovphysx/isaaclab_ovphysx/assets/kernels_old.py — deleted.

Type of change

  • Breaking change (existing functionality will not work without user modification — OVPhysX is at 0.2.x, clean break is acceptable per semver-on-0.x; no deprecation aliases for OVPhysX-introduced renames).
  • Code modernization / refactor.

Validation

Verifications happen in three layers (Tasks 16-19 of the implementation plan, deferred to fresh-context follow-up):

  1. Real-backend porttest_articulation.py (already in tree, verbatim PhysX mirror). Run on GPU and CPU separately via ./scripts/run_ovphysx.sh -m pytest <path> -k 'cuda:0' and ... -k 'cpu' (the wheel's process-global device-mode lock makes a single invocation lock to one device). Expected end state: each pass shows <X> passed, <Y> xfailed, 0 failed. Y is bounded by the wheel-gaps spec; every xfail carries a reason pointing at the spec entry.

  2. Cross-backend interfacesource/isaaclab/test/assets/test_articulation_iface.py will gain an ovphysx backend, mirroring the rigid-object iface treatment from [OVPHYSX] RigidObject + RigidObjectData asset isaac-sim/IsaacLab#5426 (commit b340a551f6c). Run on both devices.

  3. API consistency audit — per-method side-by-side checklist comparing Newton, RigidObject (post-refactor), and the rewritten Articulation; verifies method name, kwarg-only signature, parameter order, return type, docstring template (one-liner → .. note::.. tip::Args:), section-header placement.

Outstanding

  • GPU/CPU triage (Tasks 16-17 of the plan): the verbatim PhysX test mirror currently shows 104 failures on GPU. Dominant root cause: _binding_read(TT.BODY_COM_POSE, ...) during _initialize_impl does not route through pinned-host staging despite BODY_COM_POSE being in TT._CPU_ONLY_TYPES. Likely a single bug in the read path; fix unblocks ~80% of failures.
  • Iface extension (Task 18): pending real-backend green.
  • API audit (Task 19): pending real-backend green.
  • CHANGELOG + version bump (Task 20): pending all of the above; planned bump 0.2.x0.3.0.

Type of change

  • Breaking change (existing functionality will not work without user modification)
  • Code modernization

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 (the verbatim PhysX test mirror is the contract; bug-fixing in progress)
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file (Task 20)
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_articulation branch 2 times, most recently from 394ade8 to f72c5f4 Compare May 5, 2026 14:58
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_rigidobject branch from 2b8cc8d to 26a442b Compare May 5, 2026 15:17
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_articulation branch 3 times, most recently from da3653e to e3a782e Compare May 6, 2026 12:26
Add Articulation and ArticulationData for the OVPhysX backend, mirroring
the PhysX/Newton public API. Resolves PR isaac-sim#5459.

Articulation
^^^^^^^^^^^^

* index/mask split for every state writer, simulation-parameter writer,
  setter, and tendon setter; OVPhysX exposes both _index and _mask as
  first-class paths and intentionally drops the PhysX-specific
  ``full_data`` kwarg.
* Dedicated dynamic + viscous friction setters
  (write_joint_{dynamic,viscous}_friction_coefficient_to_sim_{index,mask})
  that touch only their slot of the combined (N, J, 3)
  DOF_FRICTION_PROPERTIES buffer.  The combined
  write_joint_friction_coefficient_to_sim_index/_mask still accepts all
  three components as kwargs (Coulomb static + dynamic + optional
  viscous) for source-compatible PhysX call sites.
* Deprecated non-indexed shorthand shims for friction (x3) and root /
  joint state (x4), forwarding to the index variants with a
  DeprecationWarning, matching PhysX's deprecated section.
* Wrench-composer return types tightened to non-None
  (instantaneous_wrench_composer / permanent_wrench_composer);
  composers are always set in _create_buffers, mirroring PhysX/Newton.
* Section organisation matches PhysX exactly: Properties -> Operations
  -> Operations - Finders -> Operations - State Writers -> Operations
  - Simulation Parameters Writers -> Operations - Setters -> Operations
  - Tendons -> Internal helper -> Internal simulation callbacks ->
  Internal helpers -- Actuators -> Internal helpers -- Debugging ->
  Deprecated methods.  Section delimiters use bare """Section."""
  docstring blocks (Newton/PhysX convention).

ArticulationData
^^^^^^^^^^^^^^^^

* Pull-on-demand timestamped buffers; CPU-only bindings route through
  pinned-host staging (PR isaac-sim#5329 pattern).
* Property names match PhysX exactly: joint_friction_coeff,
  joint_dynamic_friction_coeff, joint_viscous_friction_coeff (no
  *_static / *_dynamic / *_viscous renames).
* SI units annotated on every public property docstring
  ([m or rad, depending on joint type], [m/s or rad/s, ...], [N*m],
  [kg], etc.) per AGENTS.md.
* binding_getter parameter on __init__ typed as
  Callable[[int], Any] | None.
* Section organisation matches PhysX (Defaults -> Joint commands ->
  Joint properties -> Fixed tendon -> Spatial tendon -> Root state ->
  Body state -> Joint state -> Derived -> Sliced -> Internal helpers ->
  Deprecated properties).

Kernels
^^^^^^^

* Articulation-specific kernels in
  isaaclab_ovphysx/assets/articulation/kernels.py (soft-limit clamp,
  friction-data writer, finite-difference joint-acc helper, body-CoM
  pose composer); shared kernels promoted to
  isaaclab_ovphysx/assets/kernels.py.
* Per-kernel docstrings document purpose, shape/dtype/SI units, and
  divergence notes where the OVPhysX implementation differs (e.g.
  _fd_joint_acc takes inv_dt rather than dt to avoid per-element
  division).

Tests
^^^^^

* Real-backend test_articulation.py mirrors isaaclab_physx 1-to-1
  under run_ovphysx.sh; 99 tests pass on each of CPU + CUDA.
* test_articulation_helpers.py covers the kitless-only helpers
  (tendon scoping, mock binding shapes).
* Cross-backend test_articulation_iface.py runs the OVPhysX path:
  544 tests pass, 16 skipped, 0 failed on each of CPU + CUDA.  Brings
  the iface helper up to the actual ArticulationData constructor
  signature and broadcasts scalar inputs across joint / fixed-tendon
  / spatial-tendon mask setters that previously rejected them.

OVPhysX-only surface dropped
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* Articulation: set_external_force_and_torque_{index,mask} (use
  instantaneous_wrench_composer.add_forces_and_torques_*),
  set_spatial_tendon_limit_{index,mask}, and
  set_spatial_tendon_rest_length_{index,mask} (NotImplementedError
  stubs PhysX never had).
* ArticulationData: body_pose_w / body_lin_vel_w / body_ang_vel_w /
  body_acc_w / body_link_acc_w (base class provides matching
  defaults), body_inv_mass / body_inv_inertia, fixed_tendon_limit
  (PhysX exposes only fixed_tendon_pos_limits), spatial_tendon_limit /
  spatial_tendon_rest_length (no PhysX equivalent).
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_articulation branch from e3a782e to 4d05d9d Compare May 6, 2026 12:32
Adds ``ovphysx`` to both preset classes in
``allegro_hand_env_cfg.py``:

* ``ObjectCfg`` — cube uses ``RigidObjectCfg`` (same as the ``physx``
  preset, since OVPhysX ships RigidObject support).
* ``PhysicsCfg`` — ``OvPhysxCfg()`` with defaults.

Validated with RSL-RL: 300 iterations clean on cuda:0.  Exercises both
the articulation (Allegro hand, 16 DOFs) and the rigid object (cube)
backends in the same scene.
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_articulation branch from 4d05d9d to 18c90dc Compare May 6, 2026 12:37
kellyguo11 and others added 16 commits May 11, 2026 16:25
Signed-off-by: Kelly Guo <kellyg@nvidia.com>
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.
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.
## Summary

Implements `RigidObject` and `RigidObjectData` for the OVPhysX backend
(issue isaac-sim#5316), satisfying the `BaseRigidObject` and
`BaseRigidObjectData` contracts. Mirrors the PhysX `RigidObject` and the
existing OVPhysX `Articulation` patterns; runs kitless via the standard
`SimulationContext` + `UsdFileCfg(usd_path=…)` pipeline.

- New:
`source/isaaclab_ovphysx/isaaclab_ovphysx/assets/rigid_object/{rigid_object.py,
rigid_object_data.py, __init__.py, __init__.pyi}` (~1900 lines).
- New: `source/isaaclab_ovphysx/isaaclab_ovphysx/assets/kernels.py` —
shared Warp kernels relocated from `articulation/kernels.py` so both
asset types use them; new `_compose_root_link_pose_from_com` for
COM→link write conversion; ported Newton's
`derive_body_acceleration_from_body_com_velocities` to FD acceleration
locally (no wheel `RIGID_BODY_ACCELERATION` dependency).
- New `RIGID_BODY_*` `TensorType` aliases in
`isaaclab_ovphysx/tensor_types.py` — six already-shipping types as
direct imports, three forward-compat aliases (`ACCELERATION`,
`INV_MASS`, `INV_INERTIA`) gated by `try/except AttributeError` so the
module loads cleanly today.
- Allegro env hookup
(`source/isaaclab_tasks/.../allegro_hand/allegro_hand_env_cfg.py`): adds
`ovphysx` variants to `ObjectCfg` and `PhysicsCfg`, mirroring the
Cartpole/Ant pattern. Enables running
`Isaac-Repose-Cube-Allegro-Direct-v0` against OVPhysX via
`./scripts/run_ovphysx.sh`.
- Cross-backend interface tests: `BACKENDS.append(\"ovphysx\")` +
`create_ovphysx_rigid_object` factory in
`source/isaaclab/test/assets/test_rigid_object_iface.py`.
- Versioning: `isaaclab_ovphysx 0.1.2 → 0.2.0`, `isaaclab_tasks 1.5.29 →
1.5.30`.

## Test plan

### Real-backend rigid-object tests (kitless, via `run_ovphysx.sh`)

```
./scripts/run_ovphysx.sh -m pytest source/isaaclab_ovphysx/test/assets/test_rigid_object.py -v
```

Current state: **61 passed, 14 xfailed**. The 61 passing tests are
real-backend (live `ovphysx.PhysX` instance, real `TensorBinding` reads,
real sim steps) — port of the PhysX `test_rigid_object.py` structure
with the canonical `SimulationContext` +
`UsdFileCfg(ISAAC_NUCLEUS_DIR/Props/Blocks/DexCube/dex_cube_instanceable.usd)`
pattern Cartpole/Newton already use. Catches two production bugs the
previous mock-based suite missed (\`hasattr\` swallow on \`body_names\`,
\`self._device\` always falling back to \`cuda:0\`).

### Cross-backend interface tests

```
./scripts/run_ovphysx.sh -m pytest source/isaaclab/test/assets/test_rigid_object_iface.py -v -k ovphysx
```

Current state: **252 passed, 120 fixed shape-mismatch failures fixed in
this branch** (4 distinct bugs caught: full-write row-count guard, 1-D
mask src normalization, COM-pose row-count guard, two unimplemented
\`default_root_pose/vel\` stubs).

### Existing articulation regression check

```
./scripts/run_ovphysx.sh -m pytest source/isaaclab_ovphysx/test/assets/test_articulation.py source/isaaclab_ovphysx/test/assets/test_articulation_data.py -v
```

Verifies the kernel relocation in Task 2 didn't break existing
articulation tests. Recommended before merge.

### Manual end-to-end (Kit + Nucleus)

\`Isaac-Repose-Cube-Allegro-Direct-v0\` with the new \`ovphysx\` preset
— manual smoke test (Kit-required, requires Nucleus access):

```
./scripts/run_ovphysx.sh source/isaaclab_tasks/isaaclab_tasks/direct/allegro_hand/allegro_hand_env.py --num_envs 4 --headless
```

## Wheel-side gaps (for @marcodiiga)

The 14 remaining xfails split as follows; only **10 are wheel-side
blockers** (all in the same category):

| Category | xfailed | Owner |
|---|---|---|
| Material-properties API (`RIGID_BODY_MATERIAL` TensorType or view
helper) | 10 | Wheel — see
[docs/superpowers/specs/2026-04-28-ovphysx-wheel-gaps-for-marco.md](https://github.com/AntoineRichard/IsaacLab/blob/antoiner/feat/ovphysx_rigidobject/docs/superpowers/specs/2026-04-28-ovphysx-wheel-gaps-for-marco.md)
for the proposed contract |
| `test_initialization_with_no_rigid_body` (RuntimeError on missing
prim) | 2 | IsaacLab follow-up — error-handling polish |
| `test_initialization_with_articulation_root` (out-of-scope per spec) |
2 | IsaacLab follow-up — explicit \`NotImplementedError\` stub |

Three additional wheel-side `RIGID_BODY_*` TensorTypes (`ACCELERATION`,
`INV_MASS`, `INV_INERTIA`) are forward-compat — declared via `try/except
AttributeError` aliases on the IsaacLab side, no IsaacLab consumers
depend on them today, but they auto-activate when the wheel ships them.

## Notes

- IsaacLab side is wheel-update-agnostic: `tensor_types.py` defensive
aliases let \`isaaclab_ovphysx\` import cleanly against today's
\`ovphysx 0.3.7\`.
- Local docs at \`docs/superpowers/specs/\` (gitignored) include the
original design spec, the corrected Marco-feedback gap spec, and the
test-gaps follow-up.
- Branch contains 30 commits including Marco's contract corrections
(renames \`RIGID_BODY_ROOT_POSE\` → \`RIGID_BODY_POSE\`, \`MASS\` shape
\`(N, 1)\` → \`(N,)\`).

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…ysx_articulation

# 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
#	source/isaaclab_ovphysx/isaaclab_ovphysx/tensor_types.py
Adopt the RigidObjectData / PhysX pattern: derive instance / body /
joint / tendon counts from the binding metadata
(``next(iter(bindings.values()))``), and assign name lists as
class-level attributes filled in by ``Articulation._initialize_impl``
after construction. The constructor signature collapses from twelve
arguments to ``(bindings, device)``.

Drop the ``binding_getter`` callable. Bindings the data container reads
(LINK_POSE, LINK_VELOCITY, LINK_ACCELERATION, LINK_INCOMING_JOINT_FORCE,
ROOT_VELOCITY, and the fixed / spatial tendon set when counts > 0) are
eagerly created in ``_initialize_impl`` instead of looked up via a
callback, so ``ArticulationData._get_binding`` reduces to a dict lookup.

Guard the forward-kinematics call in ``body_link_pose_w`` against a
missing ``OvPhysxManager`` instance, so the mocked cross-backend iface
tests no longer trip ``WrenchComposer``'s ``hasattr`` probe.

Drop ``test_articulation_data.py`` (covered by real-backend tests) and
the two ``_data`` tendon-name assertions in
``test_articulation_helpers.py`` (propagation now lives in
``_initialize_impl``, not ``_process_tendons``).

Articulation tests: 101 real-backend + 2 helpers + 1080 iface = 1183
passed.
The isaaclab_ov CI job's glob collects all ``test/`` files under
``source/isaaclab_ov*``, including the isaaclab_ovphysx tests. The
sibling ovphysx test files already guard their isaaclab_ovphysx imports
with ``pytest.importorskip("ovphysx.types", reason="ovphysx wheel not
installed")``; only ``test_articulation.py`` went straight to top-level
``from isaaclab_ovphysx.assets import Articulation``, 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 from
test_articulation_helpers.py and the rigid-object fix in isaac-sim#5426.

Also add a ``.skip`` changelog fragment for the isaaclab core package
covering the iface test factory tweak in the prior commit.
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_articulation branch from c6ea8c7 to 3430279 Compare May 13, 2026 14:21
AntoineRichard and others added 3 commits May 13, 2026 20:16
The recent OVPhysX preset hookup for the Allegro Repose direct task adds
``from isaaclab_ovphysx.physics import OvPhysxCfg`` at module top in
``source/isaaclab_tasks/.../allegro_hand/allegro_hand_env_cfg.py``.

When sphinx walks ``isaaclab_mimic.datagen`` for autodoc, the chain
``generation.py`` → ``isaaclab_tasks.utils.parse_cfg`` →
``isaaclab_tasks.__init__.import_packages`` recursively imports every
task package's ``__init__.py``. ``isaaclab_ovphysx/tensor_types.py``
does ``from ovphysx.types import TensorType`` at module load; the
``ovphysx`` wheel is not in ``docs/requirements.txt``, so the import
raises ``ModuleNotFoundError`` and sphinx reports the failure at the
outermost autodoc'd module:

    WARNING: Failed to import isaaclab_mimic.datagen.
    Possible hints:
    * AttributeError: module 'isaaclab_mimic' has no attribute 'datagen'
    * TypeError: unsupported operand type(s) for *: 'float' and 'pi'

(the ``float * pi`` hint is the assemble_trocar ``np.pi`` mock issue
surfacing as a sphinx fallback; not the primary cause.)

Add ``ovphysx`` to ``autodoc_mock_imports`` so sphinx mocks the wheel
during doc build, mirroring how ``omni`` / ``carb`` / ``isaacsim`` are
handled. No source-side change needed.
…5400)

## 1. Summary

Make IK / OSC / RMPFlow task-space controllers backend-agnostic so
Franka manipulation envs run under Newton.

The action terms previously called PhysX-only methods on
`asset.root_view` directly, which crashed under Newton with
`AttributeError`. The Franka reach envs worked around this by hardcoding
`self.sim.physics = PhysxCfg(...)` with the comment *"{IK,OSC} control
is not supported with Newton physics; use PhysX only"*. This PR removes
that workaround.

End-to-end result: `Isaac-Reach-Franka-{IK-Abs,IK-Rel,OSC}-v0` run on
either backend, picked via `presets=newton`.

## 2. Design

Four new properties on `BaseArticulationData`:

- `body_link_jacobian_w` — geometric Jacobian, linear rows at the link
origin (USD prim frame). What IK / OSC consumers want.
- `body_com_jacobian_w` — geometric Jacobian, linear rows at the center
of mass. Engine-natural form; useful for dynamics reasoning.
- `mass_matrix` — joint-space generalized mass matrix `M(q)`.
- `gravity_compensation_forces` — joint-space gravity-loading torques
`g(q)`.

Plus one metadata property on `BaseArticulation`:

- `num_base_dofs` — number of free DoFs of the floating base. `0` for
fixed-base, `6` for floating-base. Maps an actuated-joint id `j` to its
column in J / M / g via `j + num_base_dofs`.

All four data properties are concrete-with-`NotImplementedError`
defaults; backends override what they support.

### 2.1 DoF axis: `num_joints + num_base_dofs` (industry-standard)

The DoF axis prepends `num_base_dofs` columns at the front of the joint
axis: `0` for fixed-base, `6` for floating-base. The 6 columns are the
floating-base spatial velocity in world frame, ordered `[lin_x, lin_y,
lin_z, ang_x, ang_y, ang_z]`.

This matches every major rigid-body library:

| Library | Floating-base layout |
|---------|----------------------|
| Pinocchio | Free-flyer joint contributes 6 to `nv`; reduced-coordinate
`nv = 6 + n_actuated`. `pinocchio::computeJointJacobians` writes `(6,
nv)`. |
| Drake | `MultibodyPlant` ephemeral floating joint;
`MultibodyPlant::CalcJacobianSpatialVelocity` returns `(6, nv)` with
leading 6 free-floating cols. |
| MuJoCo | `<freejoint/>` introduces 6 DoFs at the front of `qvel`;
`mj_jac*` returns `(_, nv)`. |
| RBDL | `JointTypeFloatingBase` adds 6 to `dof_count`;
`CalcPointJacobian` returns `(_, dof_count)`. |
| OCS2 | `generalizedCoordinatesNum = 6 + actuatedJointsNum` for
floating-base robots. |
| iDynTree | `getFreeFloatingMassMatrix` returns `(6 + dofs, 6 + dofs)`.
|

Consumers operating in actuated-joint space (action terms keyed by
`joint_ids`) compute `[j + asset.num_base_dofs for j in joint_ids]` once
at init — same application-level reduction as every surveyed library.

### 2.2 Output shapes by backend

Concrete shapes for each engine output and the wrapper transform
applied. `N` = num_instances, `B` = num_bodies (full count incl. root),
`J` = num_joints (actuated only). "Default" is the engine-native
per-view shape (post-gather). "passthrough" = wrapper applies no shape
change. The COM→origin shift is values-only (does not change shape).

| Property | Base | Newton (default ⟶ transform) | PhysX (default ⟶
transform) | Aligned |
|---|---|---|---|---|
| `body_link_jacobian_w` | fixed | `(N, B, 6, J)` ⟶ drop fixed-root row
+ COM→origin shift | `(N, B−1, 6, J)` ⟶ COM→origin shift | `(N, B−1, 6,
J)` |
| `body_link_jacobian_w` | floating | `(N, B, 6, J+6)` ⟶ COM→origin
shift | `(N, B, 6, J+6)` ⟶ COM→origin shift | `(N, B, 6, J+6)` |
| `body_com_jacobian_w` | fixed | `(N, B, 6, J)` ⟶ drop fixed-root row |
`(N, B−1, 6, J)` ⟶ passthrough | `(N, B−1, 6, J)` |
| `body_com_jacobian_w` | floating | `(N, B, 6, J+6)` ⟶ passthrough |
`(N, B, 6, J+6)` ⟶ passthrough | `(N, B, 6, J+6)` |
| `mass_matrix` | fixed | `(N, J, J)` ⟶ passthrough | `(N, J, J)` ⟶
passthrough | `(N, J, J)` |
| `mass_matrix` | floating | `(N, J+6, J+6)` ⟶ passthrough | `(N, J+6,
J+6)` ⟶ passthrough | `(N, J+6, J+6)` |
| `gravity_compensation_forces` | fixed | (no upstream primitive) ⟶
`NotImplementedError` | `(N, J)` ⟶ passthrough | `(N, J)` |
| `gravity_compensation_forces` | floating | (no upstream primitive) ⟶
`NotImplementedError` | `(N, J+6)` ⟶ passthrough | `(N, J+6)` |

The aligned column collapses across base type using two derived symbols:

- `num_base_dofs` = `0` fixed | `6` floating — exposed as
`BaseArticulation.num_base_dofs`
- `num_jacobi_bodies` = `B − 1` fixed | `B` floating — fixed-root row
excluded for fixed-base

| Property | Aligned (generalized) |
|---|---|
| `body_link_jacobian_w`, `body_com_jacobian_w` | `(N,
num_jacobi_bodies, 6, J + num_base_dofs)` |
| `mass_matrix` | `(N, J + num_base_dofs, J + num_base_dofs)` |
| `gravity_compensation_forces` | `(N, J + num_base_dofs)` |

Two observations:
1. The only body-axis asymmetry is **Newton's fixed-base Jacobian**:
Newton's `eval_jacobian` includes a zero row for the fixed-root joint
that the wrapper drops. PhysX's engine already drops it.
2. The DoF axis is in the industry-standard `J + num_base_dofs` form on
both engines natively. The only DoF-axis asymmetry is
`gravity_compensation_forces` (Newton NIE pending upstream primitive).

### 2.3 Why on `BaseArticulationData`, not `BaseArticulation`

`BaseArticulationData` already exposes per-body / per-joint state as
cached lazy `@property` accessors with `TimestampedBuffer` invalidation
(`body_link_pose_w`, `joint_pos`, etc.). The four new accessors fit the
same shape — read-only state indexed by body / joint, refreshed per sim
step — so they reuse that infrastructure and let consumer code stay
symmetric:

```python
# IK action term
ee_pose = articulation.data.body_link_pose_w.torch[:, ee_idx]
ee_jac  = articulation.data.body_link_jacobian_w.torch[:, ee_jac_idx]   # same prefix
```

## 3. Fixed: latent PhysX IK / OSC frame mismatch

PhysX's `_root_view.get_jacobians()` returns linear rows referenced at
each body's **center of mass**, not the link origin. Undocumented
behavior — verified empirically by bypassing the IsaacLab wrapper and
confirming `J · q̇` matches `body_com_lin_vel_w` to 1e-8 and differs
from `body_link_lin_vel_w` by exactly `ω × r_com_world` (the rigid-body
shift). The PhysX data layer already encoded this convention for
velocities: `body_com_vel_w` is the raw passthrough of
`_root_view.get_link_velocities()`; `body_link_vel_w` is **derived** via
a shift kernel.

Before this PR, IK / OSC / RMPFlow action terms on PhysX consumed
`_root_view.get_jacobians()` directly while using
`data.body_link_pose_w` as the EE pose setpoint. The frame mismatch
contributes `ω × r_com_world` per body to the linear-row contract —
undetected in CI because no existing test compared `J · q̇` against
`body_link_lin_vel_w` directly. The new contract test
`test_get_jacobians_link_origin_contract` parametrized to `anymal`
catches it explicitly: 0.32 m/s residual on PhysX without the fix.

After this PR, PhysX's `body_link_jacobian_w` applies the same
COM→origin shift kernel that Newton uses, mirroring the existing
`body_link_vel_w` derivation. Both backends now satisfy
`body_link_jacobian_w · q̇ == body_link_vel_w` to numerical precision
(test tolerance 5e-3 absolute, 1e-2 relative).

The COM-referenced sibling `body_com_jacobian_w` is exposed for callers
that intentionally want the engine-native form (e.g. dynamics-side
reasoning at the COM).

Also: the three PhysX passthrough properties (`body_com_jacobian_w`,
`mass_matrix`, `gravity_compensation_forces`) now pin a single
`ProxyArray` in `_create_buffers` instead of allocating one per property
read. PhysX tensor-view getters return pointer-stable buffers for the
articulation's lifetime — verified manually across `sim.step`, a manual
joint write, and `sim.reset`.

## 4. Newton-side details

### 4.1 Pre-allocation for capture safety

Dynamics-scratch allocation is grouped in a private
`ArticulationData._create_jacobian_buffers(model)` helper called from
`_create_buffers`. The helper is sectioned by which property each buffer
feeds, and names reflect each buffer's physical role:

- **Shared scratch** (eval_jacobian output, reused as eval_mass_matrix's
`J` input to skip a re-compute): `_jacobian_buf_flat`, `_joint_S_s_buf`
(Featherstone motion subspace — shared between the two evals, hence the
property-prefix-free name).
- **Per-view gather config**: `_jacobian_link_offset` (fixed-base row-0
skip), `_jacobian_view_art_ids` (flattened view-to-model index map).
- **`body_com_jacobian_w`**: `_jacobian_buf` (zero-copy 4-D view of
`_jacobian_buf_flat`, kernel input) and `_body_com_jacobian_w_buf`
(gather output, per-view).
- **`body_link_jacobian_w`**: `_body_link_jacobian_w_buf`
(Center-Of-Mass-to-link-origin shift kernel output, per-view).
- **`mass_matrix`**: `_mass_matrix_full_buf` (model-wide `H` scratch —
Composite Rigid Body Algorithm (CRBA) output),
`_mass_matrix_body_I_s_buf` (Featherstone per-body spatial inertia aux,
CRBA-only), `_mass_matrix_buf` (gather output, per-view).

Properties are allocation-free at step time. The kernel-launch sequence
runs against fixed buffer pointers, which is what makes the per-step
path safe under CUDA-graph capture.

### 4.2 View-level row gather

Newton's `eval_jacobian` / `eval_mass_matrix` write every articulation
in the model into a single buffer (shape includes
`model.articulation_count`), regardless of which `ArticulationView`
invoked them. PhysX returns view-scoped data already. Two Warp kernels
(`gather_jacobian_rows` 4-D, `gather_mass_matrix_rows` 3-D) gather just
this view's rows into a contiguous view-sized destination so the
caller-facing shape contract matches PhysX. The view-to-model index map
is reused across both gathers.

### 4.3 `eval_mass_matrix` "J as input" gotcha

Newton's `eval_mass_matrix(state, H, J=None, body_I_s=None,
joint_S_s=None)` treats `J` as **input** when provided — it skips the
internal `eval_jacobian` and uses the buffer as-is. Passing an empty
pre-allocated `J` produces `H = J^T·M·J = 0` → singular → `LinAlgError`
in OSC's `torch.inverse(mass_matrix)`. The wrapper explicitly populates
`J` by calling `eval_jacobian` first; we reuse `_jacobian_buf_flat`
(same shape) so no separate scratch is needed.

### 4.4 COM→origin shift on `body_link_jacobian_w`

Newton's `eval_jacobian` writes linear-velocity rows at each link's
**center of mass**. After `gather_jacobian_rows`, the
`shift_jacobian_com_to_origin` Warp kernel applies `v_origin = v_com - ω
× (R · body_com_pos_b)` per `(env, body, dof)` thread, writing to
`_body_link_jacobian_w_buf`. The COM-referenced source buffer is reused
as-is for `body_com_jacobian_w` and `mass_matrix`.

### 4.5 FK staleness

`eval_jacobian` and `eval_mass_matrix` read `state.body_q` (per-body
world transforms). After a manual `write_joint_position_to_sim_*` (no
sim step), `state.joint_q` is updated but `state.body_q` is stale until
`eval_fk` runs. The new properties match the existing `body_link_pose_w`
convention — refresh FK lazily via `_ensure_fk_fresh()` (Python-guarded
`SimulationManager.forward()`) before invoking the eval kernels. PhysX
has its own internal refresh on the equivalent getters (verified
empirically by the new manual-write tests passing on PhysX without an
explicit trigger).

## 5. Action-term gating

`OperationalSpaceControllerAction._compute_dynamic_quantities`
previously fetched mass matrix and gravity-compensation forces
unconditionally on every step. Under the new abstraction this would
still call Newton's gravity-comp stub even when the user disabled
gravity compensation in the controller config.

The fetches are now gated to match what the controller actually
consumes:

- Mass matrix fetched when `inertial_dynamics_decoupling=True` **or**
`nullspace_control != "none"` (the null-space torque term in
`OperationalSpaceController.compute()` consumes mass matrix
independently of inertial decoupling).
- Gravity comp fetched only when `gravity_compensation=True`.

Side benefit: skips a per-step engine call on PhysX when neither flag is
set.

## 6. Known limitation: gravity compensation on Newton

Newton's `ArticulationView` has no gravity-compensation primitive (only
`eval_fk` / `eval_jacobian` / `eval_mass_matrix`).
`gravity_compensation_forces` raises `NotImplementedError` on Newton;
OSC users on Newton must set `gravity_compensation=False` until upstream
lands the primitive (newton-physics/newton#2497, isaac-sim#2529, isaac-sim#2625). The
strict-xfail test
`test_get_gravity_compensation_forces_not_implemented_on_newton` flips
to XPASS when that happens, signaling the maintainer to remove the
wrapper stub and OSC guidance.

## 7. Reach-env cfg cleanup

Removed the `self.sim.physics = PhysxCfg(bounce_threshold_velocity=0.2)`
override from `Isaac-Reach-Franka-{IK-Abs,IK-Rel,OSC}-v0`. Tasks now
inherit the parent `ReachPhysicsCfg` preset, so `presets=newton` selects
`NewtonCfg` and `presets=physx` (the default) keeps the previous
behavior — same `bounce_threshold_velocity=0.2` lives on
`ReachPhysicsCfg.default = PhysxCfg(bounce_threshold_velocity=0.2)`. No
information lost.

## 8. Test plan

### 8.1 Existing controller tests migrated

- [x] `test_differential_ik.py` migrated to
`robot.data.body_link_jacobian_w.torch`. Passes on PhysX (2/2).
- [x] `test_operational_space.py` migrated to
`robot.data.body_link_jacobian_w.torch`, `robot.data.mass_matrix.torch`,
`robot.data.gravity_compensation_forces.torch`. PhysX (12/18 — the 6
failures are pre-existing `ContactSensor` env issues unrelated to this
PR; affected tests fail on `omni.physics.tensors.api` import which is
independent of the bridge).
- [x] `test_floating_base_osc_action_term_indexing` cfg unchanged.

### 8.2 New tests in this PR

**Shape contracts** — lock the public DoF / body axes:

| Test | Backend | Purpose | Result |
|------|---------|---------|--------|
| `test_get_jacobians_shape_fixed_base` | PhysX + Newton |
`body_link_jacobian_w` drops the fixed-root row → `(N, B−1, 6, J)`. |
PASSES |
| `test_get_jacobians_shape_floating_base` | PhysX + Newton | Floating
base keeps root row + prepends 6 base cols → `(N, B, 6, J+6)`. | PASSES
|
| `test_get_mass_matrix_shape_and_nonsingular_fixed_base` | PhysX +
Newton | `(N, J, J)` + strictly positive diagonal (catches the
model-wide-padding bug that masks heterogeneous-scene mismatch as a
singular matrix). | PASSES |
| `test_get_mass_matrix_shape_floating_base` | Newton | `(N, J+6, J+6)`
— floating-base includes the 6 free-root DoFs. | PASSES |
| `test_heterogeneous_scene_per_view_shapes` | Newton | Mixed
Franka+Anymal scene: each view returns its OWN asset shape, not
`model.max_*`. Direct regression test for the heterogeneous-padding bug.
| PASSES |

**Math / physics contracts** — values, not just shapes:

| Test | Backend | Purpose | Result |
|------|---------|---------|--------|
| `test_get_jacobians_link_origin_contract[panda \| anymal]` | PhysX |
`J · q̇ == body_link_lin_vel_w` identity (sharp J reference-point
check). Anymal exercise catches the latent COM/link mismatch this PR
fixes. | PASSES |
| `test_get_jacobians_link_origin_contract[panda \| anymal]` | Newton |
Same identity, ground truth from `state.body_qd` minus the COM offset
shift. | PASSES |
| `test_get_mass_matrix_symmetry_pd[panda \| anymal]` | PhysX + Newton |
`M(q)` is square, symmetric, positive-definite. | PASSES (4/4) |

**Freshness contracts** — Forward Kinematics (FK) refresh after manual
writes:

| Test | Backend | Purpose | Result |
|------|---------|---------|--------|
| `test_jacobian_refreshes_after_manual_joint_write[panda \| anymal]` |
PhysX + Newton | After `write_joint_position_to_sim_index` (no sim
step), reading the Jacobian reflects the new joint state. Locks in the
FK-staleness contract on the manual-write code path. | PASSES (4/4) |
| `test_mass_matrix_refreshes_after_manual_joint_write[panda \| anymal]`
| PhysX + Newton | Same contract for mass matrix. | PASSES (4/4) |

**Gravity compensation** — accessor + Operational Space Control (OSC)
integration + negative control:

| Test | Backend | Purpose | Result |
|------|---------|---------|--------|
| `test_get_gravity_compensation_forces_static_equilibrium` | PhysX |
Apply only `τ_gc` to a non-trivial Franka pose; assert it stays static.
Pins the accessor in isolation, no controller masking. | PASSES |
| `test_franka_osc_gravity_compensation_holds_under_gravity` | PhysX |
OSC + `gravity=g(q)` under scene gravity holds the EE pose. Pins (a)
`_jacobi_joint_idx + num_base_dofs` indexing, (b)
`OSC.compute(gravity=...)` torque math, (c) reachability through the
action-term pipeline. | PASSES |
| `test_franka_osc_no_gravity_compensation_sags_under_gravity` | PhysX |
Negative control — OSC **without** gravity comp DOES drift under
gravity. Proves the with-comp test isn't passing because `g(q)=0`. |
PASSES |
| `test_get_gravity_compensation_forces_not_implemented_on_newton` |
Newton | Strict-xfail pin for the upstream Newton gap. Flips to XPASS
when upstream lands the primitive. | XFAIL (correct) |

**End-to-end Inverse Kinematics (IK) and OSC accuracy** — production
sentinels:

| Test | Backend | Purpose | Result |
|------|---------|---------|--------|
| `test_franka_ik_tracking_accuracy` | PhysX + Newton |
Damped-Least-Squares IK convergence sentinel via the new accessors. |
PASSES (PhysX 0.01 mm, Newton 0.00 mm) |
| `test_franka_osc_tracking_accuracy` | PhysX + Newton | OSC convergence
sentinel via Jacobian + mass matrix. | PASSES (both at machine
precision) |

Latest CI: `isaaclab_physx` and `isaaclab_newton` both green on the most
recent push.

### 8.3 Determinism and stability

The accuracy sentinels are bit-for-bit deterministic on both backends at
the 5 cm short-target setup: 20× consecutive runs each give the same
`pos_mean` to 5 decimal places. Both tests assert on tail mean rather
than tail min — the latter is the bottom of any oscillation envelope and
can pass spuriously while the actual tracking error is much larger.
Tight regression sentinels rather than flaky bounds. No
`pytest-rerunfailures` retry decoration — a CI failure should be a real
regression, not noise to retry away.

### 8.4 Smoke runs

- [x] `random_agent` on `Isaac-Reach-Franka-IK-Abs-v0` under Newton:
5,205 physics substeps zero-error.
- [x] `random_agent` on `Isaac-Reach-Franka-IK-Rel-v0` under Newton: 306
substeps zero-error.
- [x] `random_agent` on `Isaac-Reach-Franka-OSC-v0` under Newton: 290
substeps zero-error.

### 8.5 Test-setup notes

The accuracy tests need two fixes that the standalone path doesn't get
for free (production envs do):

- Teleport to `init_state.joint_pos` post-`sim.reset()`. Without it, the
robot sits at the URDF-neutral pose where Franka's wrist axes nearly
align — rank-deficient Jacobian, multi-cm DLS plateau.
- Override `sim_cfg.gravity = (0, 0, 0)` in the Newton `sim` fixture
(`build_simulation_context(gravity_enabled=False)` is silently ignored
when an explicit `sim_cfg` is passed).

The OSC test additionally zeros actuator PD gains so OSC's joint-effort
output isn't opposed by `kp·(target − q)` (same way OSC is wired in
production action terms). With these in place, Newton hits machine
precision and PhysX hits ~10 µm. Newton's PD does have a real
`g_torque/kp` gravity-sag that PhysX's TGS masks via constraint
projection — surfaces only when gravity is on without gravity
compensation, which is the upstream gap in § 6.

## 9. Files

Touched five extensions; per-package changelog fragments under
`source/<pkg>/changelog.d/jichuanh-ik-newton-compat-mvp*.rst`:

- `isaaclab` (minor): four new properties on `BaseArticulationData`;
`num_base_dofs` on `BaseArticulation`; controller-action gating +
migration to data-layer accessors; doc updates.
- `isaaclab_physx`: data-layer impls + `shift_jacobian_com_to_origin`
kernel; docs the latent frame-mismatch fix.
- `isaaclab_newton` (minor): data-layer impls (eval + gather + shift);
model-sized scratch and view-sized output buffers migrated from
articulation to data layer; gravity-comp NIE stub.
- `isaaclab_ovphysx`: bridge methods removed (inherits NIE).
- `isaaclab_tasks`: hardcoded `PhysxCfg` removals + direct-workflow
caller migrations to data-layer accessors.

---------

Signed-off-by: aravind s kumar <aravikumar@nvidia.com>
Co-authored-by: aravind s kumar <aravikumar@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description

The timeout retry logic is doing more harm than good as it's causing
tests to run extremely long and does not actually resolve any of the
timeout issues. Reverting the change to default to 0 retries on
timeouts.

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)


## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] 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 and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->
daniela-hase and others added 30 commits May 13, 2026 19:18
…c-sim#5167)

# Description  
PR [isaac-sim#4142](isaac-sim#4142) added 10
typed RT2 (`RealTimePathTracing`) fields to `RenderCfg` to expose the
carb settings that actually control rendering quality under the RT2 mode
that has been default since Isaac Sim 4.5. The PhysicsManager refactor
in PR [isaac-sim#4564](isaac-sim#4564) (commit
`0ba9c5cb`) accidentally removed all 10 of these fields during a large
`simulation_cfg.py` rewrite, while the `.kit` preset files that
reference the same carb paths were left intact.
This PR restores the removed fields and their `field_to_setting`
mappings, along with test coverage.
**Restored fields:**
| Field | Carb Path |
|-------|-----------|
| `max_bounces` | `/rtx/rtpt/maxBounces` |
| `split_glass` | `/rtx/rtpt/splitGlass` |
| `split_clearcoat` | `/rtx/rtpt/splitClearcoat` |
| `split_rough_reflection` | `/rtx/rtpt/splitRoughReflection` |
| `ambient_light_intensity` | `/rtx/sceneDb/ambientLightIntensity` |
| `ambient_occlusion_denoiser_mode` |
`/rtx/ambientOcclusion/denoiserMode` |
| `subpixel_mode` | `/rtx/raytracing/subpixel/mode` |
| `enable_cached_raytracing` | `/rtx/raytracing/cached/enabled` |
| `max_samples_per_launch` | `/rtx/pathtracing/maxSamplesPerLaunch` |
| `view_tile_limit` | `/rtx/viewTile/limit` |
Without these fields, users can only reach RT2 quality knobs through the
`carb_settings` escape-hatch dict, while the existing named fields
(`samples_per_pixel`, `enable_translucency`, `enable_reflections`,
`enable_direct_lighting`) map to RT1 Legacy carb paths that RT2 ignores
entirely.
## Type of change
- Bug fix (non-breaking change which fixes an issue)
## Checklist
- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Signed-off-by: Nicholas Blauch <nblauch@gmail.com>
Co-authored-by: ooctipus <zhengyuz@nvidia.com>
Co-authored-by: Antoine RICHARD <antoiner@nvidia.com>
Co-authored-by: isaaclab-review-bot[bot] <270793704+isaaclab-review-bot[bot]@users.noreply.github.com>
CommandManager terms call self._env.extras during reset().
LeappDeploymentEnv bypasses the standard ManagerBasedRLEnv init path and
never initializes this dict, causing an AttributeError on the first
reset() call.

Add self.extras: dict = {} to __init__ to fix the crash.

# Description

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link:
https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html

💡 Please try to keep PRs small and focused. Large PRs are harder to
review and merge.
-->

Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context.
List any dependencies that are required for this change.

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (existing functionality will not work without user
modification)
- Documentation update

## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->
Fixes some nvbugs:

- NVBug 6141356: added `pip` to `environment.yml` so aarch64 conda envs
(e.g. DGX Spark) get pip seeded, which conda-forge does not pull in
transitively.
- NVBug 6129956: wrapped the four command blocks in the kitless install
page in synced Linux / Windows tab-sets so Windows users have copy-paste
commands.
- NVBug 5984996: `isaaclab.bat` and `isaaclab.sh` now warn when
`_isaac_sim` is present but `setup_conda_env.bat` is missing; shipping
the file is a Sim-side follow-up.
- NVBug 6125054 (follow-up): moved `viser` to an opt-in extra in
`python_packages.toml` since `viser>=1.0.16` pulls `websockets>=13.1`
which collides with `isaacsim==6.0.0.0`'s `websockets==12.0`.

Also fixed a broken link surfaced by the docs link checker:
`https://robosuite.ai/` (404) ->
`https://robosuite.ai/docs/overview.html` in `ecosystem.rst`.

Drive-by: removed `numpy` from `autodoc_mock_imports` in `docs/conf.py`
since it's already in `docs/requirements.txt`; fixes pre-existing `Build
Latest Docs` regression from PR#5082.

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) 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
- [x] I have added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package (do **not** edit
`CHANGELOG.rst` or bump `extension.toml` since CI handles that)
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Signed-off-by: Kelly Guo <kellyg@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…saac-sim#5566)

## Summary

- `mujoco` and `mujoco-warp` are already declared under Newton's `[sim]`
optional-dependency extra. Re-listing them in IsaacLab's `setup.py` was
redundant and pinned them at IsaacLab's chosen versions instead of
Newton's.
- The pins also lived in `source/isaaclab/setup.py`'s top-level
`INSTALL_REQUIRES`, so users installing only the PhysX or Kit backends
were forced to pull MuJoCo even though nothing in `isaaclab` core
imports it.
- Switch the Newton spec to `newton[sim] @ git+...` and remove the
direct pins from both setup.py files plus the wheel builder manifest.
Newton becomes the single source of truth for those versions.

## Verification

- `grep -rn "import mujoco\|import mujoco_warp\|MjModel\|MjData"` across
the repo: zero direct usages. The Newton backend uses
`newton.solvers.SolverMuJoCo` (transitive). MJCF asset import goes
through Isaac Sim's `isaacsim.asset.importer.mjcf`, not the `mujoco`
Python package.
- Newton's
[`pyproject.toml`](https://github.com/newton-physics/newton/blob/v1.2.0rc2/pyproject.toml)
`[sim]` extra: `mujoco~=3.8.0`, `mujoco-warp>=3.8.0.1,~=3.8.0`. Newton's
compatible-release spec already covers the patch versions IsaacLab was
hard-pinning.
- All other install pathways (`./isaaclab.sh -i`,
`cli/commands/install.py`, `docker/Dockerfile.*`, `environment.yml`,
root `pyproject.toml`, per-package `pyproject.toml`) read deps from
these three files; no other version pins exist.
- `./isaaclab.sh -f` passes.

## Test plan

- [ ] Fresh `./isaaclab.sh -i --install newton` install resolves Newton,
`mujoco`, and `mujoco-warp` transitively.
- [ ] Smoke-test a Newton-backed task to confirm the MuJoCo Warp solver
still loads.
- [ ] Wheel build with `tools/wheel_builder` produces wheels with the
same set of MuJoCo packages as before.

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…sim#5433) (isaac-sim#5437)

## Summary

Adds Newton backend support for \`Isaac-Shadow-Hand-Over-Direct-v0\`
(multi-agent MAPPO/IPPO). Selectable via \`--preset newton\` / Hydra
preset resolution; PhysX path unchanged.

The headline calibration finding: Newton needs
\`ImplicitActuatorCfg(stiffness=20.0, damping=2.0)\` on the Shadow Hand
fingers — vs PhysX's \`1.0/0.1\` on fingers and \`5.0/0.5\` on wrists —
because PhysX layers \`fixed_tendons_props(limit_stiffness=30,
damping=0.1)\` and runs \`solver_position_iteration_count=8\` per
substep. Both amplify the effective torque per unit nominal gain.
Newton's MJWarp implicit-PD path has neither, so larger nominal gains
are needed for comparable joint authority. With the bump, MAPPO mean
reward at iter 200 / 2048 envs goes from ~27 (no catch learned) to ~777,
vs the PhysX baseline of ~247.

## Stack

Builds on top of:

- **isaac-sim#5433** [Newton] Rename per-env labels in physics replication —
required for Shadow Hand fixed tendons to parse correctly under Newton.

After isaac-sim#5433 merges, this PR's diff vs develop drops to the 3
shadow-hand-specific files.

## What this PR does

### Newton port wiring (\`shadow_hand_over_env_cfg.py\`)

The Newton variant of the Shadow Hand articulation is built as a **delta
of the single-agent \`ShadowHandRobotCfg.newton_mjwarp\`** (cross-task
import from \`direct/shadow_hand\`), parameterized per-robot
\`prim_path\` / \`init_pos\` / \`init_rot\`. Reuses the single-agent's
USD path, \`rot\` reapplication workaround, effort limits, and joint
regex.

Two \`ImplicitActuatorCfg\` overrides on top of the single-agent cfg:

* **\`fingers\`** (wrist + per-finger joints): \`stiffness=20.0\` /
\`damping=2.0\`. The catch-task gain calibration fix.
* **\`distal_passive\`** (the four \`robot0_(FF|MF|RF|LF)J0\` joints):
\`stiffness=10.0\` / \`damping=0.1\`. The Newton USD bakes
\`stiffness=286 / damping=57\` on these joints from the MJCF→USD
translation (which fights the \`MjcTendon\` coupling and bounces the
ball). \`stiffness=10\` keeps the joints near-passive while the tendon
constraint dominates. PhysX uses tendon coupling on these joints
directly and does not need an analogous override.

\`PresetCfg\` subclasses follow the established
\`physx\`/\`newton_mjwarp\`/\`default=physx\` pattern — same shape as
the single-agent Shadow Hand port already on develop. Newton
\`ObjectCfg\` drops PhysX-only \`rigid_props\` knobs (per-shape solver
iterations, sleep thresholds, max depenetration velocity, custom physics
material). Newton scene cloning sets \`clone_in_fabric=False\`.

### Backend portability fix (\`shadow_hand_over_env.py\`)

One line: \`self.right_hand.root_view.get_dof_limits()\` →
\`self.right_hand.data.joint_limits\`. \`root_view\` is PhysX-only;
\`data.joint_limits\` is the backend-portable accessor available on both
PhysX and Newton articulations.

### Drift alignment with develop

* Newton-preset slot name \`newton_mjwarp\` (matches develop's current
convention).
* \`PhysxCfg(bounce_threshold_velocity=0.2,
gpu_max_rigid_contact_count=2**23, gpu_max_rigid_patch_count=2**23)\` —
matches single-agent Shadow Hand's contact-buffer sizing for 2048-env
scale.

## Numbers (200 iter / 2048 envs / seed 42 / MAPPO)

Captured from tfevents in
\`~/workspaces/IsaacLab/logs/skrl/shadow_hand_over/\`:

| Setting | Stiffness | Damping | Reward (mean) | Catch learned? |
|---|---:|---:|---:|:---:|
| PhysX baseline | 1.0 (5.0 wrist) | 0.1 (0.5 wrist) + tendon=30/0.1 |
**246.7** | yes |
| Newton, develop default | 1.0 | 0.1 | 23.4 | **no** |
| Newton, pinned default | 1.0 | 0.1 | 27.7 | **no** |
| Newton, h1 probe | 50.0 | 5.0 | 617.8 | yes |
| **Newton, this PR** | **20.0** | **2.0** | **777.1** | yes |

20/2 was chosen because it stays closer to the nominal effort-limit
budget while still providing enough control authority. Anything in
roughly \`[10, 50]\` works.

## Test plan

- [x] PhysX 200-iter baseline at 2048 envs (mean 246.7).
- [x] Newton 200-iter at stiffness 20/2 — mean 777.1, catch learned.
- [x] Newton 200-iter at stiffness 50/5 cross-check — mean 617.8, catch
learned.
- [x] \`./isaaclab.sh -f\` clean (pre-commit hooks).
- [x] Changelog fragment under
\`source/isaaclab_tasks/changelog.d/jichuanh-shadow-hand-newton-parity.minor.rst\`
(CI-driven version bump on merge; no per-PR \`extension.toml\` edit).

## Out of scope (follow-ups)

- **\`EventCfg\` not wired into the env class.** The multi-agent's
\`EventCfg\` is defined but never referenced by
\`ShadowHandOverEnvCfg.events\` — single-agent's pattern (\`events:
ShadowHandEventCfg = ShadowHandEventCfg()\` with PhysX/Newton variants)
hasn't been ported. Independent feature add; not a parity blocker.
- **Migrating Shadow Hand Newton USD to the
\`mujoco-usd-converter\`-produced asset in
\`newton-physics/newton-assets/shadow_hand/usd_structured/\`.** That
asset uses \`MjcActuator + MjcTendon\` natively (no baked stiffness=286
problem). Switching would let the \`distal_passive\` override be
deleted. Requires Nucleus/S3 asset migration + matching the right-hand
asset (newton-assets has only left); separate work.
- **Behavior-level parity** beyond shaped reward (catch rate, drop rate,
ball-trajectory smoothness) is left for a follow-up evaluation.
Bumped packages:
- isaaclab: 5.1.1 → 5.2.0
- isaaclab_mimic: 1.2.6 → 1.2.7
- isaaclab_newton: 0.8.1 → 0.9.0
- isaaclab_ov: 0.1.8 → 0.1.9
- isaaclab_ovphysx: 0.1.4 → 1.0.0
- isaaclab_physx: 0.6.4 → 0.7.0
- isaaclab_tasks: 1.5.38 → 1.6.0
# Description

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link:
https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html

💡 Please try to keep PRs small and focused. Large PRs are harder to
review and merge.
-->

Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context.
List any dependencies that are required for this change.

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (existing functionality will not work without user
modification)
- Documentation update

## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [ ] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) 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 and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->
…ents (isaac-sim#5611)

## Summary

`Build Latest Docs` is failing on every open PR with:
```
source/isaaclab/docs/CHANGELOG.rst:35: ERROR: Unexpected indentation. [docutils]
build finished with problems, 1 warning (with warnings treated as errors).
```

(failing job:
https://github.com/isaac-sim/IsaacLab/actions/runs/25847575509/job/75946304877)

The 2026-05-14 auto-version-bump (`b65a1ac2b73`) compiled fragment
`source/isaaclab/changelog.d/jichuanh-ik-newton-compat-mvp.minor.rst`
verbatim into the new `5.2.0` block. That fragment contained a
flush-left paragraph inside the `Added` bullet list, which Sphinx `-W`
rejects.

## Fix

1. **Repromote the orphan paragraph to a `*` bullet** in
`source/isaaclab/docs/CHANGELOG.rst` so the bullet list under `Added`
stays well-formed.
2. **Catch the same shape in future fragments**: `Fragment.validate()`
now scans every section body and rejects any non-blank line that is
neither a `* ` bullet start nor a continuation (leading whitespace). The
error message points back at the exact offending line so the contributor
sees it in the `Changelog Fragment Check` PR gate output before merge.

Replayed the new check against all 131 historical fragments — it flags
exactly one, the one that caused this incident. Zero false positives
elsewhere.

## Test plan

- [x] `pytest tools/changelog/test/` — 83 pass (24 prior validate tests
+ 1 new orphan-paragraph fixture test).
- [x] Validator on the historical bad fragment returns the new
orphan-paragraph error message.
- [x] `pre-commit run` on all touched files — clean.
- [x] `Build Latest Docs` will pass on this PR (the `Unexpected
indentation` line is gone).

## Files

- `source/isaaclab/docs/CHANGELOG.rst` — bullet-prefix the orphan
paragraph.
- `tools/changelog/cli.py` — orphan-paragraph rejection in
`Fragment.validate()`.
- `tools/changelog/test/test_validate.py` +
`test/invalid_content/3004.rst` — fixture + test for the new rule.
-
`source/isaaclab/changelog.d/jichuanh-fix-docs-changelog-indentation.skip`
— satisfies the gate (CHANGELOG-only edit, no user-visible entry
needed).
…saac-sim#5609)

# Description

When calculating the "look-at" quaternion for a camera, an
**orthonormal** rotation matrix is first calculated using the camera's
eye position, look-at target, and world up vectors:
- `forward = target - eye` *("camera forward")*
- `camera_z = -normalize(forward)` *("camera backward")*
- `camera_x = world_up × camera_z ` *("camera right")*
- `camera_y = camera_z × camera_x` *("camera up")*
- return `R = [camera_x, camera_y, camera_z ]` *(OpenGL convention)*

However, if `forward` is parallel to `world_up` then the cross product
`camera_x` is zero, leading to a **singular** non-invertible matrix
returned from `create_rotation_matrix_from_view()`. Then
`quat_from_matrix()` would silently convert this to a non-unit
quaternion and return this garbage back to the caller.

This change fixes both issues as follows:

**`create_rotation_matrix_from_view`:**
- When the cross product collapses, it falls back on the world X-axis as
an alternate `world_up` vector and re-calculates the matrix.
- Previously, `camera_y × camera_z` was used as the fallback, which was
already zero due to the problem described above.
- X-axis is guaranteed to be perpendicular to `world_up` since
`world_up` is restricted to Y or Z.
- When truly undefined input is provided (`eye == target` or non-finite
values) it now returns per-row `NaN` that the caller can detect and
handle.

**`quat_from_matrix`:**
- Now returns `NaN` when the input is not a valid rotation matrix
(singular, reflection, or non-orthonormal).

All callers in IsaacLab have been updated to detect `NaN` where
appropriate and fail gracefully, or avoid passing degenerate input
altogether where possible.

Added 11 new unit tests and removed the 0.1 x-nudge workaround from the
integration tests (PR isaac-sim#5470 and isaac-sim#5380)

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
Bumped packages:
- isaaclab: 5.2.0 → 5.2.1
- isaaclab_newton: 0.9.0 → 0.9.1
- isaaclab_physx: 0.7.0 → 0.7.1
# Description

This PR is based on and includes the changes from isaac-sim#5620, then adds one
CI fix on top: it unsets `HUB__ARGS__DETECT_ONLY` inside the Docker test
container before running Isaac Lab commands. Some base images set this
flag, which prevents OmniHub from starting and makes cold Nucleus asset
retrieval fall back to slow repeated retries.

This was reproduced from the failing Actions job:

https://github.com/isaac-sim/IsaacLab/actions/runs/25904143763/job/76158743634

The affected `test_rsl_rl_export_flow.py` Dexsuite Kuka-Allegro export
timed out at 600 s with the flag set, then completed in about 73 s with
the flag unset after clearing the local KukaAllegro mirror.

Fixes # N/A

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Screenshots

N/A - CI-only change.

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation (N/A -
CI-only change)
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works (validated with the affected Docker export test)
- [x] I have added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package (N/A for the
CI-only commit; isaac-sim#5620 carries its own changelog fragments)
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

## Test Plan

- `./isaaclab.sh -f`
- Docker reproduction with `HUB__ARGS__DETECT_ONLY=true`:
`test_export_flow[Isaac-Dexsuite-Kuka-Allegro-Reorient-v0]` timed out
after 600 s.
- Docker reproduction with `HUB__ARGS__DETECT_ONLY` unset after clearing
the KukaAllegro mirror:
`test_export_flow[Isaac-Dexsuite-Kuka-Allegro-Reorient-v0]` passed in
72.75 s.

---------

Co-authored-by: Piotr Barejko <pbarejko@nvidia.com>
…c-sim#5613)

## Summary

- Training/playing the drone-ARL skrl tasks
(`Isaac-TrackPositionNoObstacles-ARL-Robot-1-*`,
`Isaac-Navigation-3DObstacles-ARL-Robot-1-*`) crashed at `Runner(env,
agent_cfg)` with `AttributeError: 'NoneType' object has no attribute
'shape'` in `LazyLinear.initialize_parameters`.
- Root cause: the two drone-ARL skrl YAMLs declare `input: STATES` for
the policy and value networks. In skrl 2.0 the model instantiator now
resolves `STATES` against `state_space`, which is `None` for
single-agent envs, so the generated `compute()` calls
`self.mlp_container(None)` and the first `LazyLinear` raises on
`input.shape[-1]`.
- Switched both configs to `input: OBSERVATIONS`, matching every other
single-agent skrl config in IsaacLab. The two multi-agent MAPPO configs
that legitimately use `STATES` (`direct/shadow_hand_over`,
`direct/cart_double_pendulum`) are unaffected.

## Files changed

-
`source/isaaclab_tasks/.../drone_arl/track_position_state_based/config/arl_robot_1/agents/skrl_ppo_cfg.yaml`
-
`source/isaaclab_tasks/.../drone_arl/navigation/config/arl_robot_1/agents/skrl_rough_ppo_cfg.yaml`
-
`source/isaaclab_tasks/changelog.d/proth-fix-skrl-drone-arl-states-input.rst`
(patch fragment)

## Test plan

- [x] Reproduced the exact stack trace (`<string>` line 48 →
`container.py:253` → `lazy.py:263` → `linear.py:323`) with `input:
STATES` using a standalone skrl `shared_model` instantiation against a
single-agent obs/action space.
- [x] After the YAML change, the same standalone instantiation loads
both edited YAMLs from disk and successfully runs
`init_state_dict(role='policy')` and `init_state_dict(role='value')` on
a `SharedModel`.
- [x] `./isaaclab.sh -f` (pre-commit) passes locally.
- [ ] Full end-to-end `train.py`/`play.py` smoke test against:
  - `Isaac-TrackPositionNoObstacles-ARL-Robot-1-v0`
- `Isaac-TrackPositionNoObstacles-ARL-Robot-1-Play-v0
--use_pretrained_checkpoint`
  - `Isaac-Navigation-3DObstacles-ARL-Robot-1-v0`

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description

Adds a permanent, decoupled CI entry-point for replaying captured teleop
sessions against an Isaac Lab environment. Replaces the runtime patch
the
`teleop-cicd` pipeline currently applies to
`scripts/environments/teleoperation/teleop_se3_agent.py` so the
user-journey script is no longer mutated at CI time.

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

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

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Co-authored-by: Andrei Aristarkhov <aaristarkhov@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description
Fixed rlinf install docs to run RLinf RL posting training

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] 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 and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->
…ual environments (isaac-sim#5623)

# Description

This PR refactors the reinforcement learning train/play scripts into
unified entry points while preserving the existing library folder
structure and adding a lightweight `uv` workflow for fresh source
checkouts.

The main changes are:

- Added unified RL entrypoints:
  - `scripts/reinforcement_learning/train.py --library <library>`
  - `scripts/reinforcement_learning/play.py --library <library>`
- Added library-specific implementation files under the existing library
folders, for example:
  - `scripts/reinforcement_learning/rsl_rl/train_rsl_rl.py`
  - `scripts/reinforcement_learning/rsl_rl/play_rsl_rl.py`
- Kept the old per-library `train.py` and `play.py` scripts intact, with
deprecation warnings and migration examples.
- Added shared RL entrypoint utilities in
`scripts/reinforcement_learning/common.py`.
- Added direct Isaac Lab CLI commands:
  - `./isaaclab.sh train --library <library> ...`
  - `./isaaclab.sh play --library <library> ...`
- Kept bare script aliases for `./isaaclab.sh -p train.py ...` and
`./isaaclab.sh -p play.py ...`.
- Added Python package entry points so installed environments can run:
  - `train --library <library> ...`
  - `play --library <library> ...`
- Added a root source-checkout `pyproject.toml` project so a fresh clone
can run kitless Newton training with:
- `uv run train --library rsl_rl --task Isaac-Cartpole-Direct-v0
presets=newton_mjwarp --num_envs 4096`
- Pinned the source-checkout `uv` environment to the same PyTorch family
used by the Isaac Lab installer to avoid CUDA stack churn when users
switch between `uv run` and `./isaaclab.sh`.
- Updated docs, tests, tools, and pretrained checkpoint helpers to use
the unified train/play entrypoints.
- Fixed CLI Python discovery so `./isaaclab.sh` prefers an active or
repo-local virtual environment before falling back to system Python.
- Fixed `./isaaclab.sh --install` in uv-created virtual environments
that do not include the `pip` module by using `uv pip` for venv-targeted
pip operations.

Motivation:

The previous RL scripts duplicated substantial train/play setup logic
across libraries. This made behavior harder to keep consistent and
increased maintenance cost when updating shared functionality. The new
structure keeps library-specific logic in each library folder while
centralizing shared dispatch and common helpers.

The `uv` workflow gives users a fast path from a fresh clone to kitless
Newton training without manually creating an environment first. Isaac
Sim / Kit workflows, including PhysX, continue to use the existing full
installation path.

Dependencies:

No new required runtime dependencies are added to Isaac Lab packages.
The root source-checkout `pyproject.toml` describes the local
development environment used by `uv run`.

Fixes # N/A

## Type of change

- New feature (non-breaking change which adds functionality)
- Documentation update

## Screenshots

Not applicable.

## Validation

Ran:

- `uv lock`
- `uv lock --check`
- `uv run --frozen train --help`
- `./isaaclab.sh --install`
- `./isaaclab.sh -p -m pytest source/isaaclab/test/cli/test_install.py
-q`
- `./isaaclab.sh -p -m pytest
source/isaaclab/test/cli/test_install.py::TestGetPipCommand
source/isaaclab/test/cli/test_install.py::TestExtractPythonExe -q`
- Help smoke tests for unified train/play entrypoints and `./isaaclab.sh
-p train.py --help` / `./isaaclab.sh -p play.py --help`


## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…ac-sim#5523) (isaac-sim#5433)

## Summary

Extends `_rename_builder_labels` in
`isaaclab_newton.cloner.newton_replicate` so that every label-bearing
column on the merged Newton `ModelBuilder` is rewritten to per-env USD
paths after replication. Previously, only the built-in
body/joint/shape/articulation columns were rewritten; tendon labels (and
any other string-typed custom-attribute column) kept the source proto
path on every replicated environment.

## Stack / dependencies

- **Depends on isaac-sim#5523** (\"[Newton] Bump Newton pin to v1.2.0rc2\").
After isaac-sim#5523 lands, this PR rebases cleanly on develop.

The Newton 1.2 release
([newton-physics/newton#2659](newton-physics/newton#2659))
also includes the upstream tendon-scoping fix that obsoletes the
IsaacLab-side \`_scope_custom_frequencies\` workaround a previous
version of this PR carried — that workaround has been removed in favor
of relying on the Newton bump in isaac-sim#5523.

## Why the rename is needed

Newton's \`add_builder\` copies each proto's bodies, joints, shapes,
articulations, etc. into the merged builder verbatim, and tags each row
with a \`*_world\` integer column to track env identity. Labels (path
strings) are copied as-is. So after cloning N environments from one
proto, the merged builder has N copies of every row, all with the **same
proto-path string label**, distinguished only by the integer \`*_world\`
column.

IsaacLab keys most of its data flow off **USD prim paths** (sensor
binding, event-term scope, visualization, logging). It needs labels to
be unique per-env paths so a body called
\`/World/envs/env_3/Robot/Forearm\` is reachable by path lookup. The
rename function is the bridge: it walks every label-bearing column
post-replication and rewrites the source-root prefix to the per-env
destination root using each row's \`*_world\` value.

Until this PR, the rename only walked **5 built-in label arrays**.
Tendon labels and any string-typed custom-attribute column were missed,
so e.g. \`mujoco:tendon_label\` showed \`/World/envs/env_0/...\` for
every env — surfaced on Shadow Hand fixed tendons.

## What this PR changes

### \`_rename_builder_labels\` extension

* **Pass 1 (built-in label arrays)** — extended from 5 to 6 entity
types: \`body\`, \`joint\`, \`shape\`, \`articulation\`,
\`constraint_mimic\`, **\`equality_constraint\`** (the latter was
missing — would have surfaced for any env using \`MjcEquality\`
constraints, currently none).
* **Pass 2 (string custom-attribute columns)** — new. Walks every
registered custom attribute, finds string-typed columns whose frequency
has a \`references=\"world\"\` companion column, and applies the same
prefix rewrite. Any future solver-registered string column at such a
frequency is handled automatically without changes here.
* **Path-separator boundary** on the prefix match:
\`startswith(src_path.rstrip(\"/\") + \"/\")\`. Prevents source paths
that are string prefixes of one another (\`/Sources/protoA\` vs
\`/Sources/protoAB\`) from cross-contaminating when both feed the same
envs.
* **Hard error on length mismatch**: raises \`ValueError\` if the
parallel \`(labels, worlds)\` arrays differ in length, instead of
silently truncating. By contract Newton's \`add_builder\` keeps them in
lockstep.

### Tests

New \`source/isaaclab_newton/test/cloner/test_rename_builder_labels.py\`
with 10 cases covering:

- Both passes with built-ins and \`mujoco:tendon_label\` rewrite
correctly per world.
- Cross-pass consistency: every renamed label lives under the per-env
root.
- Guards: non-path strings pass through untouched; rows whose world id
is not in \`env_ids\` keep their original label.
- \`test_sparse_env_ids\` — non-contiguous env ids \`[10, 20, 30]\`.
- \`TestRenamePass2Generality\` — multiple coexisting custom
frequencies, multiple string columns at one frequency,
registered-but-empty string column.
-
\`TestRenameMultiSource::test_prefix_overlap_does_not_cross_contaminate\`
— explicit regression for the \`/Sources/protoA\` vs
\`/Sources/protoAB\` boundary fix; both sources feed the same envs so
the world-id guard cannot mask the boundary bug. Fails without the fix;
passes with it.

## Test plan

- [x] All 10 unit tests pass.
- [x] \`./isaaclab.sh -f\` clean (pre-commit hooks).
- [x] Verified the boundary-prefix regression test fails when the
boundary terminator is removed and passes when it's restored.
- [x] Smoke (Shadow-Hand-Over MAPPO 4 envs / iter 1) shows tendon labels
go from \`/World/envs/env_0/.../T_FFJ\` (every env) to
\`/World/envs/env_<wid>/.../T_FFJ\` (per-env paths) after the rename.

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…c-sim#5394)

# Description

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link:
https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html

💡 Please try to keep PRs small and focused. Large PRs are harder to
review and merge.
-->

Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context.
List any dependencies that are required for this change.

In Isaac Sim, we have added more capabilities to handle joint presets,
fixed joints, and other properties to the importers, so we can simplify
the isaac lab importer workflow.

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (existing functionality will not work without user
modification)
- Documentation update

## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [ ] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) 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 and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…m#5587)

## Summary

Adds typed preset selection via Hydra-style tokens — `physics=NAME` /
`renderer=NAME` / `presets=NAME[,...]` — that fold into the existing
`presets=<csv>` Hydra-decorator flow. Makes `--task=X --help` list the
actual `PresetCfg` variants present in that task's env_cfg, bucketed by
typed target. Adopted in all 16 Hydra-using scripts
(`rl_games/sb3/skrl/rsl_rl` train+play, `environments/*`,
`benchmarks/*`, `sim2sim_transfer`, `leapp/rsl_rl/export`).

## API shape

```python
parser = argparse.ArgumentParser(...)
# ... script-specific args ...
add_launcher_args(parser)
args_cli, hydra_args = setup_preset_cli(parser)
sys.argv = [sys.argv[0]] + hydra_args
```

`setup_preset_cli` returns `(args, hydra_argv)` without mutating
`sys.argv`. It registers no argparse flags for preset selection — the
typed selectors are recognized as Hydra-style tokens in the
`parse_known_args` remainder and folded into `hydra_argv[0]` as a single
`presets=<csv>` token.

## Grammar

```
python train.py --task=X  physics=newton_mjwarp  renderer=newton_renderer  presets=albedo,depth
```

- `physics=NAME` — typed selector for `PhysicsCfg` variants
- `renderer=NAME` — typed selector for `RendererCfg` variants
- `presets=NAME[,NAME,...]` — broadcast: applied to every matching
`PresetCfg`

All three fold into one `presets=<csv>` token:
`presets=newton_mjwarp,newton_renderer,albedo,depth`.

The grammar matches Hydra's, so the same line can carry path-targeted
overrides (`env.sim.dt=0.001`) that flow through untouched.

## Namespace contract

No preset selector is registered with argparse, so the parsed `args`
namespace gains no `physics` / `renderer` / `presets` attribute.
AppLauncher's name-based forwarding (`set(_SIM_APP_CFG_TYPES) &
set(vars(args))`, `app_launcher.py:681`) therefore cannot pick up a
preset value and push it into `SimulationApp.config` — the historical
`--renderer` → `config["renderer"]` → `None.lower()` crash class is
structurally impossible. Two regression tests lock the contract.

## Help text layout

`--task=Isaac-Cartpole-v0 --help` renders each selector with its
available variants inline directly below it (bullets aligned with the
description column):

```
preset selection:
  Select named PresetCfg alternatives via Hydra-style overrides (key=value, no leading dashes):
      physics=NAME              (typed) selects a PhysicsCfg variant. Available:
                                - newton_kamino
                                - newton_mjwarp
                                - physx
      renderer=NAME             (typed) selects a RendererCfg variant. Available:
                                (none)
      presets=NAME[,NAME,...]   broadcast: applied to every matching PresetCfg. Available:
                                (none)

  Hydra also accepts path-targeted overrides like env.sim.physics=NAME.
```

Typed variants appear only under their own typed selector. The
`presets=` listing shows only DOMAIN-bucket variants (cfgs whose type
doesn't subclass any typed target's base class).

Without `--task`, each row shows just the selector + description and the
section adds a `Pass --task=X` hint on its own paragraph.

## Test plan

- [x] `pytest source/isaaclab_tasks/test/test_preset_cli.py` — 24 tests
pass. Coverage: enum wiring, token folding/dedupe/passthrough,
`_ArgvHelper` semantics, type-based bucketing, all four help-text
branches (parametrized), no-`sys.argv`-mutation contract,
namespace-clean contract, AppLauncher intersection contract,
`hydra_args[0]` preserves the `presets=` token for benchmark telemetry.
- [x] `pytest source/isaaclab_tasks/test/test_hydra.py` — 76 tests pass;
legacy-alias `FutureWarning` behavior unchanged.
- [x] `pre-commit` clean.
- [x] Manual: `--task=Isaac-Cartpole-v0 --help` and
`--task=Isaac-Cartpole-RGB-Camera-Direct-v0 --help` render correctly.
- [x] Manual: `physics=newton_mjwarp renderer=newton_renderer
presets=albedo` folds into one `presets=<csv>` token at `hydra_argv[0]`.
- [x] Manual: unknown name → grouped error from resolver; legacy alias
`newton` → `FutureWarning` and resolves to `newton_mjwarp`.

---------

Co-authored-by: ooctipus <zhengyuz@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
## Summary

Bumps the Newton pin from `v1.2.0rc2` (current develop) directly to the
[`v1.2.0` stable
release](https://github.com/newton-physics/newton/releases/tag/v1.2.0)
across all five pin sites, keeping the canonical `newton[sim] @ git+...`
form everywhere.

Per Kelly Guo's suggestion: skip the rc bump and go straight to stable.
Upstream published `v1.2.0` on 2026-05-12.

**Alternative**:
[isaac-sim#5614](isaac-sim#5614)
(rc3 bump) — pick whichever target based on CI signal. This one is the
most forward target.

> Branch is still named `jichuanh/newton-1.2.0rc4-bump` from when this
PR was originally proposing rc4 — the branch name doesn't match the
current target but the diff is correct. Force-pushing the rename would
close/reopen the PR, which adds noise without changing the artifact.

## What's new in Newton v1.2.0 vs v1.2.0rc2

Full release notes: [newton-physics/newton release
v1.2.0](https://github.com/newton-physics/newton/releases/tag/v1.2.0).
Notable IsaacLab-relevant fixes:

-
[newton-physics/newton#2651](newton-physics/newton#2651)
— MPR/GJK no longer assumes convex hulls are centered around the origin.
-
[newton-physics/newton#2703](newton-physics/newton#2703)
— Kamino FK solver performance.
-
[newton-physics/newton#2721](newton-physics/newton#2721)
— HDR color output for tiled camera sensors.
-
[newton-physics/newton#2743](newton-physics/newton#2743)
— Collada textures in URDF import.
-
[newton-physics/newton#2823](newton-physics/newton#2823)
— Gravity-data device allocation in Kamino (multi-GPU).
-
[newton-physics/newton#2632](newton-physics/newton#2632)
— CollisionPipeline small fixes.
-
[newton-physics/newton#2734](newton-physics/newton#2734)
— `DelassusOperator` attribute refactor. Not used in IsaacLab source
today (verified by grep), no adapt needed.
- SolverMuJoCo fixes: planar meshes, contact-anchor computation,
distance conversion.

## Required dep bumps

None on the IsaacLab side. The `mjwarp 3.8.0.1 → 3.8.0.3` bump flows in
transitively through `newton[sim]`, since
[isaac-sim#5566](isaac-sim#5566)
dropped IsaacLab's explicit `mujoco` / `mujoco-warp` pins.

`warp-lang` stays at `1.13.0` (set by
[isaac-sim#5523](isaac-sim#5523)).

## Pins updated

| File | Change |
|---|---|
| `source/isaaclab_newton/setup.py` | `v1.2.0rc2` → `v1.2.0` |
| `source/isaaclab_physx/setup.py` | `v1.2.0rc2` → `v1.2.0` |
| `source/isaaclab_visualizers/setup.py` | 3× `v1.2.0rc2` → `v1.2.0` |
| `tools/wheel_builder/res/python_packages.toml` | `v1.2.0rc2` →
`v1.2.0` |

## Test plan

- [x] Pre-commit clean.
- [ ] CI smoke verifies clean install picks up `newton 1.2.0` and
downstream `mjwarp 3.8.0.3`.
…isaac-sim#5596)

# Make locomanipulation SDG GR00T flow runnable without flash-attn

## Summary

Two small fixes that let users finetune and roll out the
locomanipulation SDG
GR00T policy on hardware where `flash-attn` is unavailable (e.g.
Blackwell, or
any environment where the wheel fails to build).

## Changes

-
**`scripts/imitation_learning/locomanipulation_sdg/gr00t/no_flash_attn.patch`**
(new): patch against the Isaac-GR00T repo that switches the bundled
Eagle 2.5
VL model from `flash_attention_2` to PyTorch SDPA, and guards the RADIO
  vision module's `flash_attn` imports so the package becomes importable
without flash-attn installed. SigLIP path works; RADIO path is
unsupported
  without flash-attn (documented in the patch).

- **`docs/source/overview/imitation-learning/humanoids_imitation.rst`**:
adds
  a note in the GR00T install section explaining when to apply the patch
(build failure, or `RuntimeError: FlashAttention only supports Ampere
GPUs
  or newer`) and how to apply it from the sibling Isaac-GR00T checkout.

-
**`scripts/imitation_learning/locomanipulation_sdg/gr00t/rollout_policy.py`**:
override `env_cfg.recorders` with `ActionStateRecorderManagerCfg()` so
the
rollout doesn't try to record `env._locomanipulation_sdg_output_data`,
which
is only populated by the data-generation state machine in
`generate_data.py`
  and is absent during policy rollout. Without this, the recorder raises
  `AttributeError` on the first pre-step.


<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)
## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [ ] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) 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 and the corresponding version in the
extension's `config/extension.toml` file
- [ ] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description

Background: The _cubric.py ctypes shim was pinned to IAdapter v0.1
vtable offsets, but newer Kit builds ship v0.2 — compute calls were
silently landing on unbind, disabling cubric's GPU transform hierarchy
propagation. carb accepts the version mismatch with only a stderr
warning.

Originally, this change updated offsets to the v0.2 layout, requested
v0.2 from the framework, and added a runtime InterfaceDesc check that
refused to acquire on any unexpected version.

The kit team is fixing the ABI-breaking semver contract violation
upstream, so it won't actually make it into a release. So the pinned
version in Isaac Lab remains on v0.1 but keeps the validation code as a
safety net.

This problem will go away once we have official python bindings for
cubric in a future kit release.

If usdrt eventually exposes the required `eRigidBody` options via the
`IFabricHierarchy` API then that would massively simplify the
implementation of newton manager. Will pursue a feature request.

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation *(N/A)*
- [ ] My changes generate no new warnings *(New warnings on ABI mismatch
are intentional)*
- [ ] I have added tests that prove my fix is effective or that my
feature works *(N/A - spoofing kit versions for unit test would be
non-trivial; verified manually)*
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
## Summary

* Fixed an invalid inline `::` comment in the Windows batch code block
on the kit-less installation page. In Windows batch, `::` only works as
a comment at the start of a line — when placed inline after a command,
the tokens are passed as arguments, causing a runtime error. Moved the
shorthand hint (`or: isaaclab.bat -i`) to its own comment line.

## Test plan

- [ ] Verify the rendered docs page shows the corrected batch snippet.
- [ ] Confirm the `isaaclab.bat --install` command runs without
unexpected extra arguments on Windows.

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description

<!--
Thank you for your interest in sending a pull request. Please make sure
to check the contribution guidelines.

Link:
https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html

💡 Please try to keep PRs small and focused. Large PRs are harder to
review and merge.
-->

This PR adds a deterministic training path and documentation for Isaac
Lab RL workflows.

- Added apps/isaaclab.python.headless.determinism.kit as a deterministic
headless rendering experience.
- Updated scripts/reinforcement_learning/rl_games/train.py to add opt-in
--deterministic and use configure_seed(env_cfg.seed,
args_cli.deterministic).
- Updated docs/source/features/reproducibility.rst to document
--experience isaaclab.python.headless.determinism.kit
and clarify that strict PyTorch determinism is currently exposed only
for RL-Games.

Test command example:

./isaaclab.sh -p scripts/reinforcement_learning/rl_games/train.py --task
Isaac-Cartpole-RGB-v0 --enable_cameras --headless --seed 42
--max_iteration 20 **--deterministic --experience
isaaclab.python.headless.determinism.kit**

Fixes # (issue)

<!-- As a practice, it is recommended to open an issue to have
discussions on the proposed pull request.
This makes it easier for the community to keep track of what is being
developed or added, and if a given feature
is demanded by more than one party. -->

isaac-sim#3505 Non-reproducible
training results in vision-based tasks with identical seeds

## Type of change

<!-- As you go through the list, delete the ones that are not
applicable. -->

- Bug fix (non-breaking change which fixes an issue)

## Screenshots

| Before | After |
| ------ | ----- |
| <img width="200" height="250" alt="Before"
src="https://github.com/user-attachments/assets/57b52d82-ed32-4f79-8ac2-db19b32df54a"
/> | <img width="200" height="250" alt="After"
src="https://github.com/user-attachments/assets/5da0b220-7fef-445a-8efb-f0e1c6dab6a3"
/> |

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------

Co-authored-by: r-schmitt <139814266+r-schmitt@users.noreply.github.com>
Co-authored-by: nvsekkin <72572910+nvsekkin@users.noreply.github.com>
Co-authored-by: vidurv-nvidia <vidurv@nvidia.com>
Co-authored-by: ooctipus <zhengyuz@nvidia.com>
Co-authored-by: Yuchen Deng <yuchenkit@gmail.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
Co-authored-by: isaaclab-bot[bot] <282401363+isaaclab-bot[bot]@users.noreply.github.com>
Co-authored-by: hujc <jichuanh@nvidia.com>
Co-authored-by: Antoine RICHARD <antoiner@nvidia.com>
## Description

Fixes `OVRTXRenderer` crash on multi-GPU systems when `sim.device` is
not `cuda:0`.

**Root cause:** A hardcoded `DEVICE = "cuda:0"` constant in
`ovrtx_renderer_kernels.py` was imported and used for all Warp kernel
launches and buffer allocations. Additionally, `AttributeBinding.map()`
calls used `device_id=0`, pinning attribute mapping to GPU 0 regardless
of the simulation device.

**Fix:** 
- Remove the `DEVICE` constant and use `self._device` (set from
`CameraRenderSpec.device`) for all Warp operations (11 locations)
- Add `_device_id` property to extract the CUDA device index from the
device string
- Pass `device_id=self._device_id` to `AttributeBinding.map()` calls (2
locations: object binding and camera binding)

**Note on `RenderVarOutput.map()` calls:** These remain unchanged
(`device=Device.CUDA` only) because the OVRTX C API for render output
mapping (`ovrtx_map_output_description_t`) does not accept a `device_id`
parameter — the output is inherently mapped on whichever GPU OVRTX
rendered on.

**Total:** 13 hardcoded GPU-0 references fixed (11 Warp + 2
AttributeBinding).

This is the same bug class fixed for `NewtonRenderer` in isaac-sim#5019 — OVRTX
was not updated at that time.

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and added my name to the
[`CONTRIBUTORS.md`](https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md)
or my organization to the
[`CONTRIBUTORS.md`](https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md)
list

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
# Description

* Added :func:`~isaaclab.cloner.cloner_utils.is_homogeneous` to detect
whether a :class:`~isaaclab.cloner.ClonePlan`
assigns every environment from every source (a homogeneous clone mask).

* Fixed cloned environments disappearing from tiled camera output if
:attr:`~isaaclab_ov.renderers.OVRTXRendererCfg.use_ovrtx_cloning` is set
to ``True``,
by correcting scene-partition attribute creation on env roots and
cameras.

* Renamed the ``use_cloning`` field on
:class:`~isaaclab_ov.renderers.OVRTXRendererCfg` to
``use_ovrtx_cloning``.
Changed its default value to ``True``. This will bring notable speedup
for the total startup time (Launch to Train),
esp. for large-scale env setups. On Isaac-Dexsuite-Kuka-Allegro-Lift-v0
with 1024 env clones, the total startup time
dropped from ~78s to ~43s. Note that if ``use_ovrtx_cloning`` is enabled
but the env setup is heterogeneous, the
OVRTX renderer will disable the internal cloning path and logs a
warning, exporting the full multi-environment stage
instead (same effect as setting ``use_ovrtx_cloning`` to ``False`` for
that run).


## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Screenshots

Please attach before and after screenshots of the change if applicable.

<!--
Example:

| Before | After |
| ------ | ----- |
| _gif/png before_ | _gif/png after_ |

To upload images to a PR -- simply drag and drop an image while in edit
mode and it should upload the image directly. You can then paste that
source into the above before/after sections.
-->

## Checklist

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

<!--
As you go through the checklist above, you can mark something as done by
putting an x character in it

For example,
- [x] I have done this task
- [ ] I have not done this task
-->

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.