Skip to content

[OVPHYSX] RigidObjectCollection asset#5570

Merged
AntoineRichard merged 1 commit into
isaac-sim:developfrom
AntoineRichard:antoiner/feat/ovphysx_rigidobjectcollection
May 19, 2026
Merged

[OVPHYSX] RigidObjectCollection asset#5570
AntoineRichard merged 1 commit into
isaac-sim:developfrom
AntoineRichard:antoiner/feat/ovphysx_rigidobjectcollection

Conversation

@AntoineRichard

@AntoineRichard AntoineRichard commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Description

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

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

Fixes #5317

Stacked on:

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

Type of change

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

Screenshots

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

Checklist

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

Test plan

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

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

@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label May 11, 2026

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review for PR #5570: [OVPHYSX] RigidObjectCollection asset

Thanks for this substantial contribution implementing RigidObjectCollection for the OVPhysX backend! I've thoroughly reviewed the changes and have the following observations:

✅ Strengths

  1. Well-designed _FusedRigidBodyBinding adapter: The approach of wrapping multiple per-body RIGID_BODY_* bindings as a single (N, B, D) view is elegant and maintains API consistency with the articulation architecture.

  2. Comprehensive test coverage: 636 interface tests passing plus 72 backend-specific tests shows good coverage.

  3. Proper device handling: The staging buffer management respects CPU vs GPU device boundaries, which is critical for CPU-only quantities like mass and inertia.

  4. Good documentation: The docstrings thoroughly explain the architecture decisions (e.g., why articulation bindings can't be used for plain rigid bodies).

🔍 Potential Issues / Questions

1. Memory efficiency in _FusedRigidBodyBinding.write()

In the write method (lines ~148-165 of rigid_object_collection.py), you're using staging[b].assign(wp.from_torch(...)). This creates a temporary warp array from torch on each write. Consider:

# Current (creates intermediate wp.array):
staging[b].assign(wp.from_torch(col, dtype=wp.float32))
binding.write(staging[b], indices=indices, mask=mask)

# Alternative (direct copy into pre-allocated staging):
wp.copy(staging[b], wp.from_torch(col, dtype=wp.float32))

However, if wp.from_torch is already zero-copy via DLPack, this may be optimal. Could you confirm the intention here?

2. Potential race condition with _sim_timestamp

In RigidObjectCollectionData.update() (data file ~line 160), the timestamp is incremented:

self._sim_timestamp += dt

If update() is called concurrently with property accessors that check timestamps, there could be a race. This mirrors the existing pattern in other backends, but worth noting if multi-threaded access is ever considered.

3. Cherry-picked commit dependency

The PR notes it carries a cherry-pick from #5545 for ovphysx_manager.py. Please ensure this gets rebased cleanly once #5545 lands to avoid duplicate history.

💡 Suggestions

  1. Consider adding a warning for unimplemented material properties: The 4 xfailed tests are for material properties. A clear log message when users try to access these would improve UX:

    if tensor_type == TT.RIGID_BODY_MATERIAL:
        warnings.warn("Material properties not yet supported on OVPhysX backend")
  2. Type stub completeness: The __init__.pyi updates look good, but consider adding return type hints for the @property methods in the data class for better IDE support.

📋 Validation Checklist

  • CI passing (labeler ✓)
  • Tests: 636 iface + 72 backend-specific passing
  • Code follows existing OVPhysX articulation patterns
  • Documentation includes architecture rationale
  • Changelog fragment present

Verdict

This is solid work that correctly implements the collection abstraction using the binding adapter pattern. The design cleanly handles the OVPhysX constraint that ARTICULATION_LINK_* types don't resolve for plain rigid bodies.

Minor suggestions above are non-blocking. LGTM pending the cherry-pick rebase once #5545 lands.

@greptile-apps

greptile-apps Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements RigidObjectCollection and RigidObjectCollectionData for the OVPhysX simulation backend, completing the three-asset rigid-body surface alongside the already-merged RigidObject and Articulation. A _FusedRigidBodyBinding adapter wraps B per-body RIGID_BODY_* bindings as a single (N, B, D) view, and OvPhysxManager is refactored to cache the C++ PhysX instance across SimulationContext resets.

  • New RigidObjectCollection + RigidObjectCollectionData: 1 582- and 1 070-line files exposing six pose/velocity write pairs (index and mask variants), three property setters (mass, COM, inertia), wrench composers, and a full timestamped lazy-read data container mirroring the Articulation architecture.
  • _FusedRigidBodyBinding adapter: assembles B independent (N, D) per-body bindings into a unified (N, B, D) interface; read and write paths use per-device staging buffers to avoid cross-device copies for CPU-only quantities.
  • OvPhysxManager overhaul: replaces the previous physx = None teardown with a physx.reset() soft-reset and a _locked_device process-global device guard to survive the Carbonite destructor race in ovphysx \u2264 0.3.7.

Confidence Score: 3/5

The new RigidObjectCollection is mostly correct, but two mask-based write methods will silently return wrong data in the same sim step they are called, which is exactly the scenario reset-heavy RL environments hit on every episode rollout.

The mask-based pose writer and mask-based COM-velocity writer both omit the freshness timestamp assignments their index-based counterparts explicitly set. Any caller writing via the mask API and immediately reading back within the same sim step will receive stale or physics-evolved data instead of the just-written value.

Focus on rigid_object_collection.py: write_body_link_pose_to_sim_mask (around line 511) and write_body_com_velocity_to_sim_mask (around line 906) need the missing timestamp assignments before this is merged.

Important Files Changed

Filename Overview
source/isaaclab_ovphysx/isaaclab_ovphysx/assets/rigid_object_collection/rigid_object_collection.py New 1 582-line implementation of RigidObjectCollection for OVPhysX. Two P1 inconsistencies: mask-based pose and COM-velocity writers omit the freshness timestamp marks their index-based counterparts set, causing stale OVPhysX re-reads within the same sim step.
source/isaaclab_ovphysx/isaaclab_ovphysx/assets/rigid_object_collection/rigid_object_collection_data.py New 1 070-line data container mirroring ArticulationData patterns. Lazy timestamped reads, CPU-staging for CPU-only bindings, and ProxyArray wrappers all look correct.
source/isaaclab_ovphysx/isaaclab_ovphysx/physics/ovphysx_manager.py Refactored to cache the ovphysx C++ PhysX instance across SimulationContext resets (HACK for Carbonite destructor race) and adds a _locked_device guard for the process-global device-mode lock. Logic for reuse path and device validation looks sound.
source/isaaclab_ovphysx/isaaclab_ovphysx/assets/articulation/articulation.py Large refactor completing the Articulation OVPhysX backend. Mirrors the same index/mask writer pattern as RigidObjectCollection but appears consistent — no missing timestamp marks observed in the articulation writers.
source/isaaclab_ovphysx/isaaclab_ovphysx/assets/kernels.py New 1 401-line shared kernel library (body pose/velocity transforms, warp kernels). Straightforward warp kernel implementations following established patterns.
source/isaaclab_ovphysx/test/assets/test_rigid_object_collection.py 957-line test suite with 72 passing / 4 xfailed cases. No test exercises mask-based pose/velocity write followed by an immediate read, which would expose the timestamp issue.

Sequence Diagram

sequenceDiagram
    participant Env as Training Loop
    participant ROC as RigidObjectCollection
    participant FRB as _FusedRigidBodyBinding
    participant OVP as OVPhysX Runtime
    participant Data as RigidObjectCollectionData

    Env->>ROC: write_body_link_pose_to_sim_index(poses, env_ids, body_ids)
    ROC->>ROC: wp.launch(set_body_link_pose_to_sim)
    ROC->>Data: "_body_link_pose_w.timestamp = _sim_timestamp"
    ROC->>FRB: "binding.write(float32_view, indices=env_ids)"
    FRB->>FRB: deassemble (N,B,D) to B x (N,D) staging
    FRB->>OVP: per_body[b].write(staging[b], indices)

    Env->>ROC: write_data_to_sim()
    ROC->>ROC: _body_wrench_to_world kernel
    ROC->>FRB: LINK_WRENCH binding.write(wrench_buf)
    FRB->>OVP: per_body[b].write(wrench_b)
    ROC->>ROC: inst_wrench_composer.reset()

    Env->>ROC: update(dt)
    ROC->>Data: update(dt) increments _sim_timestamp
    Data->>OVP: _read_transform_binding(LINK_POSE) if stale
    OVP-->>Data: (N,B,7) poses
    Data->>OVP: _read_spatial_vector_binding(LINK_VELOCITY) if stale
    OVP-->>Data: (N,B,6) velocities
Loading

Reviews (1): Last reviewed commit: "Fix RigidObjectCollection test failures:..." | Re-trigger Greptile

Comment on lines +511 to +519
binding = self._get_binding(TT.LINK_POSE)
view = self._make_float32_view(self.data._body_link_pose_w.data, binding)
binding.write(view, indices=env_ids)

def write_body_com_pose_to_sim_index(
self,
*,
body_poses: wp.array,
body_ids: Sequence[int] | wp.array | None = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Mask-based pose writer does not mark _body_link_pose_w as fresh

write_body_link_pose_to_sim_index explicitly sets self.data._body_link_pose_w.timestamp = self.data._sim_timestamp (line 459) to prevent the property getter from re-fetching from OVPhysX within the same sim step before update() is called. write_body_link_pose_to_sim_mask omits that assignment entirely, so the next access to body_link_pose_w triggers _read_transform_binding, pulling the physics-evolved pose rather than the just-written value. The corresponding fix (add self.data._body_link_pose_w.timestamp = self.data._sim_timestamp after the kernel launch) is the same pattern used by the index variant and the comment there explains exactly why it is needed.

Comment on lines +906 to +914
binding = self._get_binding(TT.LINK_VELOCITY)
view = self._make_float32_view(self.data._body_com_vel_w.data, binding)
binding.write(view, indices=env_ids)

# ------------------------------------------------------------------
# Property setters (3 pairs)
# ------------------------------------------------------------------

def set_masses_index(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Mask-based COM velocity writer does not mark _body_com_vel_w as fresh

write_body_com_velocity_to_sim_index sets self.data._body_com_vel_w.timestamp = self.data._sim_timestamp (line 848) and also invalidates _body_link_vel_w. The mask counterpart sets _body_link_vel_w.timestamp = -1.0 but never advances _body_com_vel_w.timestamp. Consequently, the next call to body_com_vel_w within the same sim step triggers a re-read from OVPhysX, potentially returning the pre-write velocity rather than the value just pushed by the kernel and the binding write.

Comment on lines +1344 to +1352
# Step 5: Read num_instances from the LINK_POSE fused binding.
# All fused bindings share the same N (verified implicitly by construction).
self._num_instances = self._bindings[TT.LINK_POSE].count

# Step 6: Create the data container.
self._data = RigidObjectCollectionData(
root_view=self._bindings,
num_bodies=self._num_bodies,
device=self._device,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Per-body binding counts are never verified to be equal

N is taken from per_body[0].count but there is no assertion that the remaining B−1 bindings return the same count. If an env-replication mistake or a glob-pattern typo causes one body type to match a different number of prims than the others, the _FusedRigidBodyBinding will have an incorrect shape, producing silently wrong (N, B, D) data and hard-to-diagnose downstream tensor mismatches. A short guard — e.g., asserting all(b.count == N for b in per_body) before constructing the fused adapter — would surface the misconfiguration immediately at initialization.

@kellyguo11 kellyguo11 moved this to In review in Isaac Lab May 11, 2026
AntoineRichard added a commit to AntoineRichard/IsaacLab that referenced this pull request May 15, 2026
Verification agents flagged five issues against the previous two
commits and one issue-isaac-sim#876 gap that the first pass missed.

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

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

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

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

5. Issue isaac-sim#876 asked to "review the limitations list and update it." The
   previous pass only reworded the intro. Refresh the task list against
   develop's actual newton_mjwarp coverage, add Shadow Hand / Shadow
   Hand Over / cabinet / dexsuite / rough-terrain locomotion, and
   replace the rigid bullet list with a discovery recipe so the list
   stops bit-rotting.
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_rigidobjectcollection branch from 7a26cfb to 2af401f Compare May 19, 2026 08:50
Implements RigidObjectCollection and RigidObjectCollectionData for the
OVPhysX backend, completing the rigid-body asset surface alongside
RigidObject and Articulation. The collection manages N distinct rigid
bodies per environment with (env, body) dual indexing.

The asset creates one native fused TensorBinding per tensor type via the
ovphysx 0.4.3 create_tensor_binding(prim_paths=...) API, mirroring how
PhysX's RigidBodyView aggregates multiple body prims into a single flat
view. Each binding spans num_instances * num_bodies prims and returns
body-major flat data. Strided-view reshape helpers convert between body-
major view layout and the instance-major (N, B, D) layout exposed to
users -- no Warp kernels added beyond the resolve_view_ids helper, no
per-body Python fan-out at runtime.

Also updates the kitless guard in test_rigid_object_collection_iface so
the shared interface test can run under run_ovphysx.sh.

Fixes isaac-sim#5317
@AntoineRichard AntoineRichard force-pushed the antoiner/feat/ovphysx_rigidobjectcollection branch from 2af401f to 920f2a6 Compare May 19, 2026 08:52

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review: [OVPHYSX] RigidObjectCollection asset

This PR implements RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend, completing the rigid-body asset surface alongside RigidObject and Articulation. I've reviewed the implementation across all 10 changed files.

✅ Strengths

  1. Efficient fused binding approach: The use of ovphysx 0.4.3's create_tensor_binding(prim_paths=[...]) API creates one native binding per tensor type spanning all num_instances * num_bodies prims. This avoids per-body Python fan-out at runtime.

  2. Well-designed layout conversion: The strided-view reshape helpers (_reshape_view_to_data_2d/_3d and reshape_data_to_view_2d/_3d) cleanly handle body-major ↔ instance-major layout conversion without requiring Warp kernels.

  3. Proper dual-path dispatching: The _binding_write and _read_binding_into_instance_major methods correctly dispatch between articulation-mode mock (for iface tests) and native fused binding layouts based on binding.shape.

  4. Comprehensive test coverage: The test file covers initialization, state setting, external forces, material properties (xfailed), and state consistency across COM/link frames.

  5. Good kitless test guard: The update to test_rigid_object_collection_iface.py properly handles the ovphysx kitless execution path with appropriate module stubbing.

🔍 Observations

1. Timestamp invalidation consistency (minor)

In the pose/velocity write methods, timestamp invalidation is applied to dependent buffers. The pattern is consistent but worth verifying:

  • write_body_link_pose_to_sim_index: Sets _body_link_pose_w.timestamp fresh, invalidates COM-derived buffers
  • write_body_com_velocity_to_sim_index: Sets _body_com_vel_w.timestamp fresh, invalidates link-derived buffers

This follows the expected dependency graph, but confirm that _body_link_state_w.timestamp and _body_com_state_w.timestamp (deprecated concat properties) are consistently invalidated when their source buffers change.

2. resolve_view_ids kernel placement

The new resolve_view_ids Warp kernel in kernels.py is cleanly isolated. The docstring accurately describes the body-major ordering (view_id = body_id * num_total_envs + env_id).

3. CPU-only binding staging

The CPU staging buffers (_cpu_body_mass, _cpu_body_coms, _cpu_body_inertia) are correctly pinned when the device is GPU. The _get_cpu_env_ids helper efficiently reuses _cpu_all_env_ids for the common all-env case.

💡 Suggestions (non-blocking)

  1. Consider explicit material property warning: Since RIGID_BODY_MATERIAL isn't yet exposed by the wheel, consider adding a clear log warning when users attempt operations that would require it, rather than silently failing via the xfailed test gap.

  2. Docstring for _TT_MAP constant: The tensor-type mapping tuple in _initialize_impl could benefit from a brief comment explaining the store-key vs rigid-body-TT relationship.

Verdict

This is a well-structured implementation that correctly extends the OVPhysX backend to support rigid object collections with proper (env, body) dual indexing. The fused binding approach and strided-view reshaping are elegant solutions to the layout differences between backends.

LGTM — pending resolution of the changelog CI failure and rebase once #5545 lands (as noted in the PR description).


📝 Update (920f2a6)

Reviewed SHA changed: 2af401fe920f2a62 (force-push/rebase)

Changelog CI now passing — The proper changelog fragment antoiner-feat-ovphysx_rigidobjectcollection.major.rst has been added with a clear feature description.

No concerns from previous review require re-evaluation — the codebase changes appear consistent with the original implementation.

Status: LGTM

@AntoineRichard AntoineRichard merged commit bac793f into isaac-sim:develop May 19, 2026
63 of 65 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Isaac Lab May 19, 2026
AntoineRichard added a commit that referenced this pull request May 19, 2026
…5637)

# Description

Restructures the physics-backend documentation to give each backend a
first-class home and to add the cross-backend orientation pages asked
for in
[isaac-sim/IsaacLab-Internal#876](isaac-sim/IsaacLab-Internal#876).

**Structure**

```
docs/source/overview/core-concepts/physical-backends/
├── index.rst                 ← user-facing hub + feature-support matrix
├── solver-comparison.rst     ← cross-backend behavioural differences
├── physx/
│   ├── index.rst
│   ├── installation.rst
│   ├── configuration.rst     ← PhysxCfg tuning knobs
│   └── supported-features.rst
├── newton/
│   ├── index.rst
│   ├── installation.rst
│   ├── supported-features.rst   (was experimental-features/.../limitations-and-known-bugs.rst)
│   ├── mjwarp-solver.rst        (was experimental-features/.../solver-transitioning.rst)
│   └── kamino-solver.rst        (was experimental-features/.../using-kamino.rst)
└── ovphysx/
    └── index.rst             ← stub flagged as highly experimental; tracking issue #5634
```

**Highlights**

- The Newton subdir moves wholesale out of `experimental-features/` via
`git mv`; framing changes from "experimental feature branch" to "beta
backend." The Experimental Features toctree now contains only
`bleeding-edge`.
- Per-solver Newton pages (`mjwarp-solver`, `kamino-solver`) replace the
old `solver-transitioning` / `using-kamino` files, matching #876's
"sub-sections for the Newton solvers" ask.
- New PhysX page set written from `isaaclab_physx.physics.PhysxCfg`,
mirroring the Newton structure.
- OvPhysX stub references the full in-flight PR set (#5421, #5422,
#5426, #5459, #5570, #5589) and is gated by a follow-up issue (#5634)
for expansion after those land.
- `solver-comparison.rst` covers friction, contact pipeline,
restitution, stabilization, convergence, articulation coordinates,
substepping, and GPU buffers across PhysX TGS, Newton MJWarp, and Newton
Kamino. Each cell points at the concrete config attribute that controls
the behavior, with a porting checklist at the end.
- Newton's `supported-features.rst` list refreshed against `develop`'s
actual `newton_mjwarp` coverage (adds Shadow Hand, Shadow Hand Over,
cabinet, dexsuite, rough-terrain locomotion). Replaces the bit-rotting
bullet list with a discovery recipe (`grep -rln newton_mjwarp
source/isaaclab_tasks/`).
- Cross-references updated in `docs/index.rst`,
`core-concepts/index.rst`, `multi_backend_architecture.rst`,
`features/visualization.rst`, and
`overview/reinforcement-learning/rl_existing_scripts.rst`.

Fixes isaac-sim/IsaacLab-Internal#876

## Type of change

- Documentation update

## Screenshots

N/A — docs-only restructure. New page tree visible from the
table-of-contents in `core-concepts/`.

## 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 *(N/A — docs-only)*
- [ ] I have added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package *(N/A — docs-only;
matches precedent of #5512 and 4aeb4d6 which shipped without
fragments)*
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
@AntoineRichard AntoineRichard mentioned this pull request May 20, 2026
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants