[pull] develop from isaac-sim:develop - #60
Merged
Merged
Conversation
(cherry picked from commit e5f1bdb) # Description Remove confusing field from Camera spec 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 ## Release backport - [ ] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## 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 Docker and GPU tests run on demand. Push the commits you want tested, then comment `run-ci` on the pull request. - [ ] 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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [ ] 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 -->
# Description Third PR in a series that splits the core cleanup in #7949 into small, reviewable pieces. This one fixes five small bugs in `isaaclab.utils` and the camera utilities. Each fix has a regression test that fails without it. **Fixes** - `utils.math.unproject_depth`: homogeneous pixel coordinates were built as `(1, u, v)` instead of `(u, v, 1)` (`pad(..., (0, 0, 1, 0))` instead of `(0, 0, 0, 1)`), so every unprojected point was wrong and the first image row became `inf`/`nan`. This regressed in #4437 (quaternion convention change), where the padding was flipped together with the quaternion code. It also affects `create_pointcloud_from_depth` / `create_pointcloud_from_rgbd` and the `run_usd_camera.py` / `run_ray_caster_camera.py` tutorials. This fix is in its own commit. - `sensors.camera.utils.create_pointcloud_from_rgbd`: a color tuple or `rgb=None` called `torch.Tensor(data, device=..., dtype=...)`, which always raises `TypeError`. The uniform color is now built with `torch.tensor(...).repeat(...)` on the point cloud's device. The comment and docstring claiming the default color is white are corrected (it is black). - `utils.math.quat_slerp`: `q2 *= -1.0` negated the caller's tensor in place when taking the shorter arc; it is now `q2 = -q2`. - `utils.sensors.convert_camera_intrinsics_to_usd`: `abs()` was applied to the boolean `(c_x - w/2) > 1e-4 or ...`, so a principal point left of or above the image center never triggered the aperture-offset warning. - `utils.datasets.HDF5DatasetFileHandler.create`: a bare file name gave `os.path.dirname(...) == ""` and `os.makedirs("")` raised. The directory is now only created when there is one. **Tests** (focused checks, no new test files; together they run in about 2.5 s). The aperture-offset warning fix is a one-line `abs()` correction and is not covered by a dedicated test. - `test/utils/test_math.py`: new `test_unproject_depth` (compares against the pinhole model); the existing `test_quat_slerp` now also asserts that `q2` is left unchanged. - `test/utils/test_hdf5_dataset_file_handler.py`: `test_create_dataset_file` also covers a bare file name. - `test/sensors/test_opencv_distortion.py` (kit-less): `test_pointcloud_from_rgbd_uniform_color` (color tuple and `None`). All of these fail against `develop` sources and pass with the fixes; the three touched test files pass in full. ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## 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 - [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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
…7982) Sum each rank's success samples across the process group at every iteration boundary so all ranks record the same global history and stop at the same iteration. Remove the validation that rejected --check_success for distributed runs. # 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 ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## 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 Docker and GPU tests run on demand. Push the commits you want tested, then comment `run-ci` on the pull request. - [ ] 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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [ ] 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 -->
# Description Fourth PR in a series that splits the core cleanup in #7949 into small, reviewable pieces. This one fixes four small bugs in `isaaclab.terrains` and `isaaclab.sim`, and adds a spawner for meshes (e.g. terrain from an `.obj` file) ported from #5407 by @ooctipus (closed as stale). The PhysX `useEnvIds` and clone-spawner changes from that PR are not included. Each fix has a regression test that fails without it. ## Fixes - `terrains.trimesh.mesh_terrains.repeated_objects_terrain`: the built-in `MeshRepeatedBoxesTerrainCfg`, `MeshRepeatedCylindersTerrainCfg`, and `MeshRepeatedPyramidsTerrainCfg` default `object_type` to strings such as `"{DIR}.utils:make_box"`, which configclass turns into a `ResolvableString` (a callable `str` subclass). The function checked `isinstance(str)` before `callable`, looked up `make_{DIR}.utils:make_box` in the module, got `None`, and raised `ValueError`. Callables are now checked first; plain names such as `"box"` still resolve to `make_box`. The error message now shows the configured value instead of `None`. - `sim.schemas.modify_articulation_root_properties`: with `fix_root_link` set, the existing fixed joint was searched for on the current stage instead of the `stage` argument. - `sim.utils.queries.find_global_fixed_joint_prim`: the signature accepts `Sdf.Path`, but `prim_path.startswith(...)` raised `AttributeError` for one. The path is converted to `str` first, like the sibling query functions. - `sim.converters.MeshConverter`: the prim name was taken from `basename.split(".")`, which raised `ValueError` for file names with more than one dot (e.g. `duck.v2.obj`). It now uses `os.path.splitext`; the existing invalid-identifier fallback turns `duck.v2` into `duck_v2`. The unused `mesh_file_format` variable is removed. ## Mesh spawner **Added** - `sim_utils.MeshFileCfg` / `sim_utils.spawn_from_mesh` spawn a mesh from: - a mesh file path (`.obj`, `.stl`, `.fbx`, ...), converted with `MeshConverter` and referenced, or - in-memory triangle data: `MeshFileCfg.TriangleMeshCfg(vertices, faces, vertex_colors)` or a `trimesh.Trimesh` through `MeshFileCfg.TrimeshObjectCfg`, authored as an Xform with a USD mesh at `{prim_path}/mesh`. - The mesh is visual-only by default. `collision_props`, `mesh_collision_props` (e.g. triangle mesh for terrain, convex hull for objects), `rigid_props`, `mass_props`, `visual_material`, and `physics_material` are optional and accept both the legacy cfgs and schema fragments, like the other spawners. ```python terrain = sim_utils.MeshFileCfg( mesh="/path/to/terrain.obj", collision_props=[sim_utils.UsdPhysicsCollisionCfg(collision_enabled=True)], mesh_collision_props=[sim_utils.UsdPhysicsMeshCollisionCfg(mesh_approximation_name="none")], ) terrain.func("/World/ground", terrain) ``` **Changed** - `isaaclab.terrains.utils.create_prim_from_mesh` (used by `TerrainImporter.import_mesh`) now spawns through `MeshFileCfg`. I checked that the authored USD is byte-for-byte identical to `develop` for a colored terrain mesh with visual and physics materials. The only difference: its `translation`/`orientation` kwargs now apply to the root prim instead of the `mesh` child (same world pose). **Differences from #5407** - Mesh file paths reuse `MeshConverter` for collision, mass, and rigid body properties, so both legacy cfgs and fragments work. The in-memory path follows the same legacy-or-fragment routing as the mesh-primitive spawners. - Dropped the `.usd` branch (use `UsdFileCfg` for USD files), the duplicate "prim already exists" check (`create_prim` already raises), and the new public `utils.mesh` helpers; mesh-data validation is a private helper in the spawner. ## Tests - `test/sim/test_spawn_from_files.py`: `test_spawn_mesh_from_triangle_data` (colored rigid body with a convex-hull collider) and `test_spawn_mesh_from_obj_file`. - The trimesh terrain path is covered by the existing terrain importer, ray caster, and material fragment tests; `test_terrain_importer.py` passes in full. **Fix tests** (one focused check per fix, extending existing tests where possible) - `test/terrains/test_terrain_generator.py`: new `test_repeated_objects_default_object_type` (the three repeated-object configs share the code path, so one config is covered). - `test/sim/test_schemas.py`: new `test_modify_articulation_root_fix_root_link_uses_given_stage` (in-memory stage that is not the current stage). - `test/sim/test_utils_queries.py`: `test_find_global_fixed_joint_prim` also passes an `Sdf.Path`. - `test/sim/test_mesh_converter.py`: `test_convert_obj` now converts the OBJ from a dotted file name (`duck.v2.obj`) and checks the prim name, instead of adding a separate conversion. All of these fail against `develop` sources and pass with the fixes. ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## 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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package (do **not** edit `CHANGELOG.rst` or bump `extension.toml` — CI handles that) - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
# Description
`WrenchComposer.compose_to_body_frame()` was called unconditionally by
every backend writer on every physics step. It reads `body_com_pos_w`
and `body_link_quat_w`, which on PhysX chains to `_ensure_fk_fresh()` →
`update_articulations_kinematic()` (a scene-global articulation FK pass)
plus `root_view.get_link_transforms()`, then launches the composition
kernel.
That work is unnecessary whenever the buffered wrench is already in a
frame the consumer accepts. This PR moves the frame decision into the
composer, which hands each writer the cheapest valid representation:
- **all-local content** → the local buffers, untouched. An all-local
wrench composes to exactly itself, because the pose data is multiplied
by zero.
- **all-global-at-CoM content** → the global buffers, untouched,
submitted in the world frame by backends that accept one.
- **anything else** → composed as before.
Writers declare what they can consume once at construction
(`supports_world_at_com`) and call `get_forces_and_torques()` per step.
All frame logic stays inside the composer; no writer touches a raw
buffer or knows a frame rule.
## New public API
```python
class WrenchComposer:
def __init__(self, asset, *, supports_world_at_com: bool = False) -> None: ...
def get_forces_and_torques(self) -> tuple[wp.array, wp.array, bool]: ...
forces, torques, is_global = composer.get_forces_and_torques()
```
Additive only. `compose_to_body_frame()`, `out_force_b`, and
`out_torque_b` remain public and behaviorally unchanged, so no
deprecation cycle is needed. `supports_world_at_com` defaults to
`False`, which is exactly the previous behavior.
The composer tracks local contributions, global contributions, and
positioned global forces with plain booleans. These flags stay
conservative across partial resets and clear on a full `reset()`;
checking whether a partial reset removed every contribution would
require scanning the buffers. The returned `is_global` boolean follows
the existing asset API and is passed directly to the backend. No frame
enum, content bitmask, or classification helper is needed.
## Equivalence
All three branches are exact, from `compose_wrench_to_body_frame`:
- **local only**: the three global buffers are zero, so both
`quat_rotate_inv` terms vanish and the output is identically
`(local_force_b, local_torque_b)`. Note a *positioned* local force also
qualifies — the kernel folds `cross(P_b, F_b)` straight into
`local_torque_b`.
- **global-at-CoM only**: `global_force_w` and the local buffers are
zero, so `corrected_torque_w == global_torque_w`. Submitting the world
buffers is the same wrench, provided the consumer applies force at the
CoM — verified against the vendored `omni.physics.tensors` API, where
`position_data=None` means "at the link transform" for **both**
`is_global` values, so the flag only reinterprets the vectors' frame and
never moves the application point.
- **anything else**: unchanged.
## Backend coverage
| backend | `supports_world_at_com` | change |
|---|---|---|
| PhysX | `True` | writer passes the returned `is_global` directly |
| OvPhysX | `True` | writer passes `is_global` to the packing kernel to
skip the inline rotate |
| Newton | `False` (default) | writer swaps to
`get_forces_and_torques()`; no constructor change |
OvPhysX is the largest win. Its wrench binding wants a world-frame
wrench, so a global wrench previously round-tripped: the composer
rotated world→body, then the packing kernel rotated body→world. Measured
on this branch with a body rotated 90° about +z, a local force `(1,0,0)`
emits `(0,1,0)` while a global force `(1,0,0)` emits `(1,0,0)` — an
exact round trip, so skipping both rotations is output-preserving. The
packed `[6:9]` link position is still written unconditionally on both
paths.
Newton keeps the body frame because it binds a body-frame array to the
solver. Worth noting for a follow-up: its writer kernel already performs
the same body→world rotation OvPhysX's does, so Newton could plausibly
take the same flag and win the global-at-CoM path too. That is
deliberately out of scope here.
## Validation
The existing composer rotation, positioned-force, merge, reset, and
overwrite tests now exercise `get_forces_and_torques()` directly.
Existing frame/position cases cover the four add/set index/mask APIs.
The existing articulation body-ordering test checks local/world
forwarding, composition avoidance, and packed link positions. One shared
rigid-object writer check replaces the separate backend/equivalence
suites and their duplicate rotation kernel.
Local results:
- `test_wrench_composer.py`: **375 passed** on CPU/CUDA.
- Articulation ordering and rigid-object wrench writer checks: **24
passed** across PhysX, Newton, and OvPhysX.
- `uv run isaaclab -f`: all pre-commit checks passed.
- Structural audit: no new test classes or kernels; no production
changes in the test consolidation. The composer architecture regression
still rejects frame/content enums, the classification layer, and the old
submission alias.
Regression sensitivity was checked by temporarily restoring
unconditional composition and stale-pose caching: the updated existing
tests fail on unnecessary pose reads and on a second read after the body
rotates, respectively.
The previously recorded paired Python dispatch microbenchmark changed
local buffer selection from about 99 ns to 46 ns per call, and
global-at-CoM selection from 127 ns to 60 ns. This measures buffer
selection only, not end-to-end simulation; production code is unchanged
by the test consolidation.
## Performance
The end-to-end measurements below were recorded for the original
optimization; they were not rerun for the boolean/API simplification.
Real PhysX, RTX 5090, ANYMAL-C (17 bodies) × 4096 envs = 69,632 links.
Same-process paired A/B with the arm order alternated per iteration,
400–500 pairs, 95% CI on paired differences. Baseline is
`compose_to_body_frame()` — what the writers previously called
unconditionally.
| scope | content | baseline | patched | paired delta | 95% CI |
|---|---|---:|---:|---:|---|
| composition path only | global-at-CoM | 0.2194 ms | 0.0191 ms |
**−0.2003 ms** (−91.3%) | [0.1915, 0.2091] |
| composition path only | local | 0.1926 ms | 0.0159 ms | **−0.1767 ms**
(−91.8%) | [0.1712, 0.1822] |
| full `write_data_to_sim` | global-at-CoM | 1.0799 ms | 0.9897 ms |
**−0.0902 ms** (−8.4%) | [0.0797, 0.1007] |
| full `write_data_to_sim` | local | 1.0072 ms | 0.9305 ms | **−0.0767
ms** (−7.6%) | [0.0719, 0.0815] |
Read the end-to-end row as the honest headline: **~0.08 ms per asset per
physics step**. Roughly half the isolated saving does not reach the
caller, because other work in `write_data_to_sim` touches body poses
anyway and pays part of that cost regardless. A cuboid rigid-object
scene at the same env count saved only ~6.5 µs, as expected — there is
no articulation FK pass to skip.
Scope caveat: no in-tree task sets `is_global=True` today (both shipped
`apply_external_force_torque` events use the local default), so the
local path is what delivers value now, and it is the one that reaches
every backend.
## Supersedes
This replaces two open PRs, both of which found one half of this
problem:
- **#7431** (@NeoZng) identified the global-at-CoM case and the cost of
the pose read. Its mechanism is correct and its writer-level measurement
is corroborated here. It is superseded on placement rather than
substance: it put the frame decision in three PhysX writers behind a
public eligibility flag, leaving Newton and OvPhysX unserved. The credit
for the world-at-CoM half of this mechanism is theirs.
- **#7362** cached composition for unchanged local wrenches. Superseded
because not composing at all is strictly stronger — the cache does
nothing when the wrench is rewritten every step, which is the common
case for a per-step randomized push.
## 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 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 added a changelog fragment under
`source/<pkg>/changelog.d/` for every touched package
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
# Description Migrates the remaining in-repo users of the legacy physics schema cfgs to schema fragments. These classes are deprecated in 3.1 and removed in 3.2 (#7839), so every site here currently emits `DeprecationWarning`s and would break at removal. The earlier migration (#7838) converted `source/` but never covered `scripts/`, and its grep missed the `*BaseCfg` spellings. On top of that, some new legacy usage has landed since. An AST scan (comments and docstrings excluded) finds **144 live legacy constructions or writer calls in 37 files on `develop`. This PR brings that to 0**, outside the deliberate exclusions listed below. **Scope** - **`scripts/` (29 files):** all tutorials, demos (including the MPM demos), `convert_mesh` / `convert_instanceable`, and three benchmarks. - **Task configs:** `core/reach/reach_env_cfg.py`, `contrib/ur10_particle_push`, and one of the five `contrib/nist` sites. - **Tests:** the camera-pose, first-frame-rendering and OpenCV-distortion sensor tests; the rigid/mass/collision parts of the cable and MPM tests; `test_ovphysx_gravity`. ## Mapping rules Every one of these has caused a real regression in an earlier migration: - **A bare fragment when one covers the fields.** This keeps in-place tuning such as `cfg.rigid_props.disable_gravity = True` working downstream. - **Empty legacy cfgs map only to the core `UsdPhysics*` fragment.** A fragment applies its schema even when all of its fields are `None`. `RigidBodyPropertiesCfg()` therefore becomes `UsdPhysicsRigidBodyCfg()` alone. Adding `PhysxRigidBodyCfg()` would add a `PhysxRigidBodyAPI` the legacy class never applied. - **`enabled_self_collisions` keeps its Newton mirror.** The legacy writer also authors `newton:selfCollisionEnabled`, so these sites emit `NewtonArticulationCfg` next to `PhysxArticulationCfg`. - **`disable_gravity` goes to `PhysxRigidBodyCfg`**, including on Newton-flavoured cfgs. - **`define_*` writer calls become `apply_*(..., create_if_missing=True)`**, which preserves creation. ## Verification **USD-attribute parity at every site, with negative controls.** Legacy and fragment forms are authored through the real spawners on sibling prims, and the harness diffs `GetPrimTypeInfo().GetAppliedAPISchemas()`, every authored attribute, and relationships. - `scripts/`: 64 groups. 60 are identical. The other 4 differ only in that legacy authored `physics:approximation="none"` explicitly, while the fragment leaves it unset and it resolves to the same `"none"`. 8 of 8 negative controls are flagged. - `source/`: every site is identical, and every negative control is flagged. **Task configs** are also checked end to end. Each task is loaded through its gym `env_cfg_entry_point` on both `develop` and this branch, across presets. All 22 affected spawned entities author identical USD, including the nist Franka on the real asset. A task-level negative control produces a diff. **Execution:** 13 scripts ran headless for 100–300 steps with no schema deprecations: the tutorials, bin_packing, multi_asset, the sensor demos, and tacsl. `convert_mesh` was run end to end for 6 approximations. All 29 scripts compile. The migrated test files pass. **Not runnable in the dev environment, so covered by parity only:** the Newton-physics script paths, including the MPM demos, which need a newer `newton` than was installed locally. Also the `omni.replicator`, ovrtx, and Haply scripts. CI covers the rest. ## Deliberately left on the legacy API - **Spawner, converter, and schema routing internals.** They *are* the legacy path, which must keep working until 3.2. - **Tests that exercise or compare the legacy path**, such as `test_schemas*`, the parity tests, and the legacy arms of the fragment tests. - **`integration_scene_cfgs.py`.** It is a backend-neutral core module, and its `disable_gravity` has no non-PhysX fragment home. - **Everything deformable.** The deformable deprecation PR, stacked on #6673, handles it. - **4 of the 5 `contrib/nist` sites.** They nest a mesh-collision cfg inside `collision_props`. The deprecation messages point users to a spawner `mesh_collision_props` slot, but that slot exists only on `MeshConverterCfg`, not on `UsdFileCfg` or the shape and mesh spawners. There is no supported fragment path for these sites yet. That gap needs its own fix before 3.2. ## Found along the way, not changed here - `RigidBodyMaterialCfg` is deprecated too, and 8 scripts still use it. - `convert_instanceable.py`'s mesh branch is already broken on `develop`: it passes `collision_approximation=` to `MeshConverterCfg`, which raises `TypeError`. - `docs/source/how-to/write_articulation_cfg.rst` still describes the legacy names. ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Checklist - [x] I have read and understood the contribution guidelines - [x] I have run the `pre-commit` checks with `./isaaclab.sh --format` - [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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there
# Description Route rigid-body transforms from physics to renderers through `SceneDataProvider` (SDP). Physics publishes its native buffer and increments its version after changes. SDP binds that buffer when format and ordering match, or converts it once per requested layout and reuses the result until the next change. Readers never reset the producer's version. ## What changes - **OVRTX:** converts physics poses directly into its matrix format, without first copying them into a Newton render state. - **Newton rendering:** borrows SDP's transform buffer instead of copying into a second buffer. Newton physics and rendering continue sharing their existing model/state. - **Isaac RTX:** shares one registry-owned Fabric binding with Kit, preserves authored scale, and updates the transform hierarchy on GPU. PhysX publishes its native Fabric matrices without fetching packed poses. - **Ownership:** physics refreshes native data; SDP borrows or converts arrays. One `FabricBackend` in `isaaclab_physx` owns the stage/hierarchy handles and shared transform bindings, identified by stage/device. Consumers pass SDP explicitly to updates; it is not part of backend identity. Core `RenderContext` has no Fabric methods or state. `FabricMatrix44` contains only matrix storage. - **Rendering updates:** removes repeated renderer-driven physics refreshes. Writes made between physics steps remain visible on the next render. For Newton → Isaac RTX, cached bindings and GPU hierarchy updates replace repeated binding setup and the CPU hierarchy fallback. This is the main runtime saving measured below. Particle/deformable transport is outside this change. OVRTX retains its existing Newton geometry bridge. ## Transform movement Counts are SDP output-writing passes, not total SDK-internal copies. Conversion, reordering, and scale are combined in one pass; unchanged data reuses the result. | Physics | Renderer | Published → requested format | SDP passes | | --- | --- | --- | ---: | | Newton | Newton Warp | `Transform` → `Transform` | 0 | | Newton | OVRTX | `Transform` → `TransposedMatrix44d` | 1 | | Newton | Isaac RTX | `Transform` → `FabricMatrix44` | 1 | | OVPhysX | Newton Warp | `Transform` → `Transform` | 0 if ordering matches; otherwise 1 | | OVPhysX | OVRTX | `Transform` → `TransposedMatrix44d` | 1 | | Isaac PhysX | Newton Warp | `Transform` → `Transform` | 0 if ordering matches; otherwise 1 | | Isaac PhysX | Isaac RTX | Native Fabric → borrowed Fabric | 0 with `use_fabric=True` | OVPhysX and OVRTX cannot run with Kit. Fabric hierarchy propagation and OVRTX's native attribute write happen after the SDP pass and are not included in these counts. ## Performance Kuka Allegro Camera, 4096 environments, 64×64 RGB, Newton MJWarp → Isaac RTX, RTX 5090, no interactive visualizer. Two warm-cache runs per revision; runtime excludes 25 warmup steps and measures 200 synchronized full environment steps, including rendering and observations. | Metric | PR | Develop | | --- | ---: | ---: | | Warm startup | 135.47 s | 133.19 s | | Runtime step | 172.32 ms | 554.49 ms | | Environment frames/s | 23,770 | 7,387 | The measured step time was **68.9% lower (3.22× throughput)**. No startup improvement was measured. All 4096 camera images were finite and nonconstant. Measured revisions: PR `257e54d5f` and develop `53a7f1c0a`, with identical dependencies. Subsequent cleanup has not been rebenchmarked. ## Migration Custom scene-data backends must initialize `transforms_version=0` and increment it after native pose writes or buffer swaps. The consumer-facing `get_transforms(output)` API binds shared, read-only arrays, including converted outputs. Pass `allow_passthrough=False` for caller-owned writable or preallocated arrays; conversion writes directly into them. Single-format backends keep their existing `transforms` property; the base `get_transforms(output_format)` delegates to it. Multi-format backends may override that method and `native_transform_formats`. ## Validation - Focused CPU tests and GPU 0 Newton/PhysX Fabric tests cover pointer sharing, publication versions, transform formats, ordering, authored scale, buffer reallocation, same-step writes, and resets. The existing cache regression now also checks that independent SDP readers cannot hide producer changes from one another. - All 11 native Newton/Fabric tests and the native PhysX Fabric test passed after moving bindings out of core. They also check one shared RTX/Kit resource and explicit Newton-to-Fabric updates without an RTX camera or Kit viewer. - Removed obsolete synchronization tests and duplicate mock-only checks; native and numerical regressions remain. - Native OVPhysX → OVRTX rendered scale, pose changes, and camera calibration passed for both legacy and ovstage APIs. Matched OVRTX timings have not been collected. ## Type of change - Refactor and bug fixes - Breaking custom scene-data backend interface change, with migration above ## Release backport - [x] <!-- backport-active-release --> Backport to the active release branch ## Checklist - [x] Contribution guidelines reviewed - [x] Changelog fragments and migration documentation updated - [x] Retained focused tests and formatting checks passed after test cleanup - [ ] Full GPU CI passed for the latest revision - [x] Native OVPhysX → OVRTX rendering validated
## Description Observation terms lacked a built-in delay tied to recorded samples. Added `delay_min_lag`, `delay_max_lag`, and `delay_hold_prob` directly to `ObservationTermCfg`, using the same `DelayBuffer` as `DelayedPDActuator`: ```python joint_pos = ObservationTermCfg(func=mdp.joint_pos_rel, delay_min_lag=1, delay_max_lag=3, delay_hold_prob=0.8) ``` Each environment samples its lag uniformly at initialization/reset. On each recorded sample, `delay_hold_prob=0.8` retains the current lag with probability 0.8 and otherwise draws a new lag, which may equal the previous one. The default of 1.0 holds the lag for the episode, preserving Isaac Lab's delayed actuator policy; 0.0 redraws every recorded sample. Holding a lag keeps latency constant as frames advance. Equal bounds give a fixed delay; both zero disable it. The field names, lag-retention semantics, and placement follow mjlab: delay runs after modifiers, noise, clipping, and scaling, before history. `compute(update_history=True)` records a sample; extra `compute()` or `compute_group()` reads leave delay/history and lag sampling unchanged. Before the first recorded sample after reset, delay returns the current input; during warm-up it returns the oldest available sample. Observation lag counts recorded samples, while actuator lag retains its physics-step clock. `DelayBuffer` owns ring storage and optional lag sampling. Its default `hold_prob=None` preserves externally selected lags and `compute(data)` for existing actuator callers. Actuator configuration, execution, and reset behavior are preserved. Tests extend existing suites; the diff adds no runtime module or test file. Related: #3471. ## Validation - 145 core tests passed; one CUDA-graph case skipped on CPU. Covered delay/history recording, extra reads without RNG advancement, processing order, config validation, partial resets, output isolation, hold probabilities 0/0.5/1, and CUDA graph replay with lag sampling. - Four actuator reset integration tests passed, running Newton and PhysX separately. - Confirmed regression tests fail when extra reads advance delay or the lag-retention mask is removed. - Eight-file architecture audit and `uv run --no-sync isaaclab -f` passed. - CPU/CUDA buffer benchmarks at batch 1024, width 48, and history lengths 4/32/128 showed lower runtime than the base implementation's history shifting. Adding optional sampling kept the existing manually selected-lag path within timing variation and introduced no RNG draws. These are local microbenchmarks, not end-to-end simulation measurements. ## Type of change - New feature - Bug fix - Documentation update ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## Checklist - [x] Ran pre-commit checks. - [x] Updated public documentation and changelog. - [x] Extended existing tests. - [ ] Passed PR CI.
# Description Some CLI unit tests still assume that they run from an Isaac Lab source checkout. They read the root `pyproject.toml`, copy the repository launcher, or execute scripts that are not shipped in the installed package. This makes the tests fail when the suite is run against an installed wheel or conda package. Use the existing `source_checkout_root` fixture for these remaining tests, following the pattern introduced in #7389. The launcher and teleoperation tests also resolve checkout-only files from the path returned by the fixture. ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## 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` - [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 added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there Local validation: ```text uvx pre-commit run --files <changed files>: passed 56 passed in 9.22s ```
## Description Newton's joint wrench sensor omitted welded tool and wrist-sensor bodies because it inherited the articulation's control-joint filter. A fixed connection still transmits force and torque. Select reportable tree joints from the Newton model while reusing the articulation's cached body bindings. This includes fixed joints between bodies and continues to exclude free joints, world-fixed roots, and loop-closing constraints. The articulation's control-joint selection and the wrench conversion kernel remain unchanged. Callers should select sensor entries by body name. Extended the existing cartpole gravity-wrench test with a fixed pole joint. Three selection cases cover distinct root types, loop exclusion, reordered bodies, and per-environment anchors. The PR adds no USD fixture or test file, and only the existing Newton sensor test file changes. Fixes #7969. ## Validation - All 13 Newton sensor cases passed after merging current develop, using Newton 1.6.0 and Warp 1.17.0. - The fixed-pole physics case and all three selection cases failed with the original implementation; the movable-pole case still passed. - Verified cached body bindings are reused, no second sensing view is created, and control-joint selection stays unchanged. - Six-file architecture audit and `uv run --no-sync isaaclab -f` passed. ## Type of change - Bug fix - Documentation update ## Release backport - [ ] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` ## Checklist - [x] I have read and understood the contribution guidelines - [x] I have run the pre-commit checks - [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 - [x] I have added a changelog fragment for every touched package - [x] My name already exists in `CONTRIBUTORS.md`
## Description Fixes #3513 and the PhysX joint-wrench frame discrepancy discussed in #7978. `forge_utils.change_FT_frame` used the inverse rotation and the wrong lever-arm sign when expressing a wrench in another frame. It now computes the source pose in the target frame and applies: - `f_t = R_ts f_s` - `tau_t = R_ts tau_s + p_ts × f_t` The existing independent point-force test covers translated frames with identity and arbitrary rotations. FORGE's current force-only observation remains unchanged because its call uses identity rotations. The PhysX sensor had a separate error: `get_link_incoming_joint_force()` already returns forces and torques in the child-side joint frame, about its anchor. The sensor applied `localPos1` and `localRot1` again. It now exposes the native components directly, removing the redundant USD traversal, frame buffers, and GPU transformations. This follows the [PhysX tensor API contract](https://docs.omniverse.nvidia.com/kit/docs/omni_physics/108.0/extensions/runtime/source/omni.physics.tensors/docs/api/python.html#omni.physics.tensors.impl.api.ArticulationView.get_link_incoming_joint_force). Replaced the circular PhysX frame test with one shared integration fixture used by both Newton and PhysX. It uses the same timestep, geometry, loads, expected values, and tolerances: a passive vertical hinge supports a 2 kg body through an offset child frame rotated 90 degrees relative to the body. The articulation is also rotated about world Z. Both backends must report `F=(0, 0, 19.62) N` and `tau=(-1.962, 4.905, 0) N·m`, calculated from gravity and the known lever arm. The test checks that the body stays at rest. No raw wrench or production conversion is used to calculate the reference. The fixture reuses the existing simple articulation asset with temporary USD overrides; no asset is added to the repository. The existing backend test files invoke the shared check. Removed the old circular reference helper. ## Validation - Newton sensor suite and FORGE tests: 12 passed. - PhysX sensor suite: 16 passed, including eager/recorded updates and reset behavior. - The shared physical test failed with the original PhysX implementation, with a 19.62 N force-component error, and passed after removing the duplicate transform. Newton passes the same analytic contract. - Architecture audit and `uv run --no-sync isaaclab -f` passed. - Recorded GPU update microbenchmark at 1024 environments × 8 links: approximately 3 microseconds per launch before and after. Initialization no longer scans USD joint frames or allocates their buffers. ## Type of change - Bug fix - Documentation update ## Checklist - [x] I have read and understood the contribution guidelines - [x] I have run the pre-commit checks - [x] I have made corresponding changes to the documentation - [x] I have added tests that prove my fix is effective - [x] I have added a changelog fragment for every touched package - [x] My name already exists in `CONTRIBUTORS.md` ## Follow-up FORGE names its raw incoming-joint wrench `force_sensor_world`, although the API reports the child joint frame. Moving FORGE's observation to an explicitly selected frame is separate work because it can change policy inputs. This PR preserves its current force observation. --------- Signed-off-by: Lynn <lynnhe02@gmail.com> Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )