Skip to content

[OVPHYSX] RigidObjectCollection asset #5317

@AntoineRichard

Description

@AntoineRichard

Summary

Implement RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend. Extends RigidObject to manage multiple distinct rigid bodies per environment with dual (env, body) indexing.

Validation environment: Franka cube stacking task — multiple cubes (RigidObjectCollection) + Franka arm (Articulation).

Scope

  • rigid_object_collection.py (~1200 lines), rigid_object_collection_data.py (~800 lines)
  • View-order reshaping kernels (body-major ↔ instance-major)
  • Copy PhysX tests + add OVPhysX to interface tests

Key Complexity

Dual indexing with view-order reshaping: methods accept (env_ids, body_ids) but ovphysx bindings use flat body-major ordering. Reshape kernels handle the mapping.

OVPhysX API Requirements

Confirm ovphysx TensorBindings can create a single view spanning multiple distinct rigid body prims (not just clones). @marcodiiga to verify.

Dependencies

Spec

Full design spec: `docs/superpowers/specs/2026-04-20-ovphysx-rigid-object-collection-design.md`

Parent issue: #5315


📋 Full Design Spec (click to expand)

OVPhysX RigidObjectCollection — Design Spec

Issue: #5317 — [OVPHYSX] RigidObjectCollection asset
Date: 2026-04-20
Status: Draft

Summary

Implement RigidObjectCollection and RigidObjectCollectionData for the OVPhysX backend, satisfying the BaseRigidObjectCollection and BaseRigidObjectCollectionData contracts. This extends the single-body RigidObject to manage multiple distinct rigid bodies per environment, adding dual (env, body) indexing.

Validation environment: Franka cube stacking task (stack_joint_pos_instance_randomize_env_cfg.py) — uses a Franka arm (Articulation) and multiple cubes (RigidObjectCollection).

Depends on: #5316 (RigidObject) — shares kernels and patterns.

Guiding Principles

Same as RigidObject spec:

  • Mirror PhysX structure
  • Reuse patterns from OVPhysX Articulation and RigidObject
  • Do not modify contracts
  • Tests copied with only setup changes

Key Difference from RigidObject

RigidObjectCollection manages N distinct rigid bodies across M environments. This adds:

  1. Dual indexing — methods accept both env_ids and body_ids (or env_mask and body_mask)
  2. View-order reshaping — ovphysx tensor bindings likely use body-major ordering [body0_env0, body0_env1, ..., body1_env0, ...]. Methods must reshape between instance-order (num_instances, num_bodies, ...) and view-order (num_bodies * num_instances, ...)
  3. Per-body properties — mass, inertia, COM are independent per body per environment
  4. Data dimensionality — all state tensors are (num_instances, num_bodies, ...) instead of (num_instances, ...)

Contract to Satisfy

BaseRigidObjectCollection

Abstract properties (7): Same as RigidObject — data, num_instances, num_bodies, body_names, root_view, instantaneous_wrench_composer, permanent_wrench_composer.

Abstract methods — core (4):

  • reset(env_ids, object_ids, env_mask) — note extra object_ids parameter vs RigidObject
  • write_data_to_sim()
  • update(dt)
  • find_bodies(name_keys, preserve_order) -> (torch.Tensor, list[str]) — returns Tensor not list[int]

Abstract methods — body pose writers (6):

  • write_body_pose_to_sim_index/mask(body_poses, body_ids, env_ids)
  • write_body_link_pose_to_sim_index/mask(body_poses, body_ids, env_ids)
  • write_body_com_pose_to_sim_index/mask(body_poses, body_ids, env_ids)

Abstract methods — body velocity writers (6):

  • write_body_velocity_to_sim_index/mask(body_velocities, body_ids, env_ids)
  • write_body_link_velocity_to_sim_index/mask(body_velocities, body_ids, env_ids)
  • write_body_com_velocity_to_sim_index/mask(body_velocities, body_ids, env_ids)

Abstract methods — body property setters (6):

  • set_masses_index/mask(masses, body_ids, env_ids)
  • set_coms_index/mask(coms, body_ids, env_ids)
  • set_inertias_index/mask(inertias, body_ids, env_ids)

Abstract methods — deprecated (3):

  • write_body_state_to_sim, write_body_com_state_to_sim, write_body_link_state_to_sim

Internal hooks (4):

  • _initialize_impl, _create_buffers, _process_cfg, _invalidate_initialize_callback

BaseRigidObjectCollectionData

Abstract properties (~30): Body state (link/com pose, velocity, acceleration), body properties (mass, inertia, com), derived quantities (projected_gravity, heading, body-frame velocities), sliced components. All shapes are (num_instances, num_bodies, ...).

Abstract method (1): update(dt: float)

Architecture

File Layout

source/isaaclab_ovphysx/isaaclab_ovphysx/assets/
├── __init__.py          (add RigidObjectCollection exports)
├── __init__.pyi
├── articulation/        (existing)
├── rigid_object/        (from #5316)
└── rigid_object_collection/
    ├── __init__.py
    ├── __init__.pyi
    ├── rigid_object_collection.py       (~1200 lines)
    └── rigid_object_collection_data.py  (~800 lines)

Implementation Pattern

rigid_object_collection.py:

  1. _initialize_impl():

    • Get OvPhysxManager instance
    • Resolve all rigid body prim paths (multiple bodies)
    • Create TensorBindings for all bodies as a single batched view
    • Build _env_body_ids_to_view_ids() mapping for dual indexing
    • Create RigidObjectCollectionData with lazy binding getter
  2. View-order reshaping (critical complexity):

    • reshape_view_to_data(view_tensor) — convert (num_bodies * num_instances, D) to (num_instances, num_bodies, D)
    • reshape_data_to_view(data_tensor) — reverse
    • _env_body_ids_to_view_ids(env_ids, body_ids) — map (env_id, body_id) pairs to flat view indices
    • These reshaping functions use Warp kernels for GPU-native operation
  3. Body state writers — same pattern as RigidObject but with dual indexing:

    • Accept (num_selected_envs, num_selected_bodies, D) input
    • Map to view-order flat indices
    • Write via TensorBinding with flat indices
  4. Wrench application — wrenches applied per (env, body) pair, reshaped to view order

rigid_object_collection_data.py:

  • Same lazy binding + timestamped caching pattern
  • All properties return (num_instances, num_bodies, ...) tensors
  • Derived properties (gravity projection, heading, body-frame velocities) computed per body

OVPhysX API Requirements

Same tensor types as RigidObject, but the bindings must handle multiple bodies:

  • The ovphysx binding shape becomes (num_bodies * num_instances, D) in view order
  • Need to confirm ovphysx supports batched rigid body views across multiple distinct prims

Blocker for @marcodiiga: Confirm that ovphysx TensorBindings can create a single view spanning multiple distinct rigid body prims (not just clones of the same prim). This is how PhysX's RigidBodyView works with a glob expression matching multiple body paths.

Warp Kernels

In addition to kernels shared with RigidObject:

Kernel Purpose
reshape_view_to_data_2d Reorder body-major to instance-major
reshape_data_to_view_2d Reorder instance-major to body-major
env_body_to_view_scatter Scatter write for dual-indexed updates

Tests

Backend-specific tests (copy from PhysX)

Source: source/isaaclab_physx/test/assets/test_rigid_object_collection.py
Target: source/isaaclab_ovphysx/test/assets/test_rigid_object_collection.py

Changes: swap PhysxCfg for OvPhysxCfg, adapt USD asset paths.

Interface tests

File: source/isaaclab/test/assets/test_rigid_object_collection_iface.py

Add "ovphysx" backend parametrization with mock bindings.

Validation

Franka cube stacking — exercises RigidObjectCollection with multiple cubes, Franka arm (Articulation), and FrameTransformer sensor.

Validation steps:

  1. Run stacking env with OVPhysX for 100 steps
  2. Verify each cube in the collection has independent state
  3. Verify dual-indexed resets (reset specific cubes in specific envs)
  4. Verify body property reads return correct per-body values

Dependencies

Estimated Scope

  • rigid_object_collection.py: ~1200 lines
  • rigid_object_collection_data.py: ~800 lines
  • Reshape kernels: ~100 lines
  • Tests: ~250 lines of adaptations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions