Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/source/_static/css/environment-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@
["IsaacContrib-Open-Drawer-Franka-IK-Abs", "rsl_rl", "isaacsim_physx,newton_mjwarp,ovphysx", "", "", "environment-previews/franka_open_drawer.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||", "newton_mjwarp||", "ovphysx||"]],
["IsaacContrib-Open-Drawer-Franka-IK-Rel", "rsl_rl", "isaacsim_physx,newton_mjwarp,ovphysx", "", "", "environment-previews/franka_open_drawer.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||", "newton_mjwarp||", "ovphysx||"]],
["IsaacContrib-Open-Drawer-OpenArm", "rl_games,rsl_rl", "", "", "", "environment-previews/openarm_uni_open_drawer.jpg"],
["IsaacContrib-PickPlace-FixedBaseUpperBodyIK-G1-Abs", "", "", "isaacsim_rtx,newton_renderer", "", "environment-previews/g1_pick_place_fixed_base.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|"]],
["IsaacContrib-PickPlace-FixedBaseUpperBodyIK-G1-Abs", "", "isaacsim_physx,newton_mjwarp,ovphysx", "isaacsim_rtx,newton_renderer,ovrtx", "", "environment-previews/g1_pick_place_fixed_base.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|", "isaacsim_physx||", "isaacsim_physx|isaacsim_rtx|", "isaacsim_physx|newton_renderer|", "newton_mjwarp||", "newton_mjwarp|isaacsim_rtx|", "newton_mjwarp|newton_renderer|", "newton_mjwarp|ovrtx|", "ovphysx|newton_renderer|", "ovphysx|ovrtx|"]],
["IsaacContrib-PickPlace-G1-InspireFTP-Abs", "", "", "", "", "environment-previews/g1_pick_place.jpg"],
["IsaacContrib-PickPlace-GR1T2-Abs", "", "", "isaacsim_rtx,newton_renderer", "", "environment-previews/gr_1_pick_place.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|"]],
["IsaacContrib-PickPlace-GR1T2-WaistEnabled-Abs", "", "", "", "", "environment-previews/gr_1_pick_place_waist.jpg"],
["IsaacContrib-PickPlace-Locomanipulation-G1-Abs", "", "", "isaacsim_rtx,newton_renderer", "", "environment-previews/g1_pick_place_locomanipulation.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|"]],
["IsaacContrib-PickPlace-GR1T2-Abs", "", "isaacsim_physx,newton_mjwarp,ovphysx", "isaacsim_rtx,newton_renderer,ovrtx", "", "environment-previews/gr_1_pick_place.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|", "isaacsim_physx||", "isaacsim_physx|isaacsim_rtx|", "isaacsim_physx|newton_renderer|", "newton_mjwarp||", "newton_mjwarp|isaacsim_rtx|", "newton_mjwarp|newton_renderer|", "newton_mjwarp|ovrtx|", "ovphysx|newton_renderer|", "ovphysx|ovrtx|"]],
["IsaacContrib-PickPlace-GR1T2-WaistEnabled-Abs", "", "isaacsim_physx,newton_mjwarp,ovphysx", "", "", "environment-previews/gr_1_pick_place_waist.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||", "newton_mjwarp||", "ovphysx||"]],
["IsaacContrib-PickPlace-Locomanipulation-G1-Abs", "", "isaacsim_physx,newton_mjwarp,ovphysx", "isaacsim_rtx,newton_renderer,ovrtx", "", "environment-previews/g1_pick_place_locomanipulation.jpg", false, {}, {}, "", null, ["||", "|isaacsim_rtx|", "|newton_renderer|", "isaacsim_physx||", "isaacsim_physx|isaacsim_rtx|", "isaacsim_physx|newton_renderer|", "newton_mjwarp||", "newton_mjwarp|isaacsim_rtx|", "newton_mjwarp|newton_renderer|", "newton_mjwarp|ovrtx|", "ovphysx|newton_renderer|", "ovphysx|ovrtx|"]],
["IsaacContrib-Place-Mug-Agibot-Left-Arm-RmpFlow", "", "isaacsim_physx", "", "", "environment-previews/agibot_place_mug.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Place-Toy2Box-Agibot-Right-Arm-RmpFlow", "", "isaacsim_physx", "", "", "environment-previews/agibot_place_toy.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Reach-OpenArm", "rl_games,rsl_rl,skrl", "", "", "", "environment-previews/openarm_uni_reach.jpg"],
Expand All @@ -129,7 +129,7 @@
["IsaacContrib-Stack-Cube-Instance-Randomize-Franka-IK-Rel", "", "", "", "", "environment-previews/franka_instance_randomize_stack.jpg"],
["IsaacContrib-Stack-Cube-RedGreen-Franka-IK-Rel", "", "isaacsim_physx", "", "", "environment-previews/franka_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Stack-Cube-RedGreenBlue-Franka-IK-Rel", "", "isaacsim_physx", "", "", "environment-previews/franka_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Stack-Cube-SO101-IK-Abs-v0", "", "isaacsim_physx", "", "", "environment-previews/so101_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Stack-Cube-SO101-IK-Abs-v0", "", "isaacsim_physx,newton_mjwarp,ovphysx", "", "", "environment-previews/so101_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||", "newton_mjwarp||", "ovphysx||"]],
["IsaacContrib-Stack-Cube-SO101-Joint-Teleop-v0", "", "isaacsim_physx", "", "", "environment-previews/so101_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Stack-Cube-SO101-v0", "", "isaacsim_physx", "", "", "environment-previews/so101_stack.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
["IsaacContrib-Stack-Cube-UR10-Long-Suction-IK-Rel", "", "isaacsim_physx", "", "", "environment-previews/ur10_stack_surface_gripper.jpg", false, {}, {}, "", null, ["||", "isaacsim_physx||"]],
Expand Down
40 changes: 35 additions & 5 deletions docs/source/features/isaac_teleop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,34 @@ through this variable -- later runs no longer prompt. The variable applies to ev
that launches the runtime, including the process-scoped launcher in
``teleop_replay_agent.py``.

.. _isaac-teleop-ovxr-mcap:

Replay MCAP with a Kitless OVXR Viewer
--------------------------------------

The MCAP replay entrypoint can present recorded input through an independent
OVXR viewer. Follow the :ref:`kitless XR guide <kitless-xr>` to install Isaac
Capture with its OVXR extra and select the compatible rendering dependencies.

.. code-block:: bash

uv run --extra ovxr python \
scripts/environments/teleoperation/teleop_replay_agent.py \
--task IsaacContrib-PickPlace-GR1T2-Abs --physics ovphysx \
--device cpu --num_envs 1 --ovxr \
--replay_file /absolute/path/to/session.mcap

Use ``--physics newton_mjwarp`` for Newton. MCAP supplies tracker and control
input; the connected client supplies the view. A recorded STOP, task success
or failure, or the replay duration limit ends the run. The separate
``isaaclab teleop replay`` command replays HDF5 demonstrations through Kit.

Add ``--ovxr_pip_camera robot_pov_cam --ovxr_pip_head_locked`` for a camera
panel. OVXR live and MCAP replay open a desktop mirror by default; add
``--headless`` to disable that window while keeping XR and PiP rendering.
See the kitless XR guide for installation, camera selection and display requirements.


.. _isaac-teleop-xr-anchor:

Configure the XR Anchor
Expand Down Expand Up @@ -1485,7 +1513,7 @@ unchanged. For either G1 task, set ``env.isaac_teleop.xr_camera_feeds=[]`` to di
the camera sensor and any configured camera observations.

The reference feeds request render-product-local DLSS Ray Reconstruction and ``quality`` execution
mode through :class:`~isaaclab_teleop.XrCameraFeedCfg`. The private PiP adapter authors those two
mode through :class:`~isaaclab_teleop.XrCameraFeedCfg`. The Kit Scene UI presenter authors those two
attributes only on the selected camera render product. Ray Reconstruction also requires the
process-global responsive-denoising setting, which the teleoperation and recording scripts
enable before environment construction when a selected feed requests Ray Reconstruction.
Expand Down Expand Up @@ -1596,8 +1624,10 @@ Kit Scene UI presentation
PiP presentation uses Kit Scene UI and ``SpatialSource`` placement. Kit imports are deferred until
an enabled feed is requested. If the Scene UI modules cannot be imported, the scripts log a warning
and continue without PiP; task-owned cameras and recording observations remain unchanged. This keeps
the camera selection configuration usable when a future kitless entry point no longer provides
Scene UI. Render-product tuning is also best-effort: backends without a compatible render product
the camera selection configuration usable without Scene UI. The
:ref:`kitless XR guide <kitless-xr>` uses separate OpenXR quad
layers and does not import Scene UI; those layers are visible in the headset/client, not in the
passive eye-image desktop mirror. Render-product tuning is also best-effort: backends without a compatible render product
keep the Camera-buffer path, and schema-authoring failures warn while PiP continues. Other
configuration, camera-buffer, and panel-initialization errors still fail during startup.

Expand Down Expand Up @@ -2031,8 +2061,8 @@ Optimize XR Performance

.. note::

The ``--headless`` CLI flag was removed in Isaac Lab 3.0. Headless is now the **default**
(omit ``--visualizer``), or set ``HEADLESS=1`` in the environment.
For Kit XR, omit ``--visualizer`` to run without the local viewport.
For kitless ``--ovxr``, pass ``--headless`` to disable its default desktop mirror.

.. dropdown:: Configure retargeting execution
:open:
Expand Down
7 changes: 4 additions & 3 deletions docs/source/how-to/cloudxr_teleoperation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ to capture demonstrations, and ``replay`` to play a dataset back. :ref:`The next

.. note::

``teleop`` cannot be combined with ``ov`` or ``ovphysx`` in a single ``uv run``: the
bundled Isaac Sim pins ``packaging==26.0`` while those runtimes require ``<24``. Install
the OV runtimes separately when you need them.
For XR teleoperation without Isaac Sim, follow the
:ref:`kitless XR guide <kitless-xr>`. It explains how to select a public
Isaac Capture wheel and its optional OVXR dependencies. The Kit setup below
uses the published ``teleop`` extra.

Complete these steps first:

Expand Down
12 changes: 12 additions & 0 deletions docs/source/how-to/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,12 @@ How-to Guides

:guide-tag:`PhysX` :guide-tag:`Isaac Sim`

.. container:: guide-entry

:doc:`Kitless XR with Isaac Capture </source/how-to/kitless_xr>`

Install Isaac Capture and OVXR packages for live teleoperation and MCAP replay.

.. container:: guide-entry

:doc:`Setting up Haply teleoperation </source/how-to/haply_teleoperation>`
Expand Down Expand Up @@ -556,3 +562,9 @@ How-to Guides

This collection is a work in progress. If a question is not answered here, open an issue on the
`Isaac Lab GitHub repository <https://github.com/isaac-sim/IsaacLab>`_.

.. toctree::
:hidden:

kitless_xr
kitless_xr_source
15 changes: 15 additions & 0 deletions docs/source/how-to/kitless_xr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
myst:
heading_anchors: 2
---

<!--
Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
-->

(kitless-xr)=

```{include} ../../../source/isaaclab_teleop/docs/kitless_xr.md
```
8 changes: 8 additions & 0 deletions docs/source/how-to/kitless_xr_source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!--
Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
-->

```{include} ../../../source/isaaclab_teleop/docs/kitless_xr_source.md
```
2 changes: 2 additions & 0 deletions source/isaaclab/changelog.d/kitless-ovxr-lifecycle.fixed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Allowed asynchronous XR owners to drain before releasing the simulation context.
* Supported empty CPU Warp arrays in Torch conversion.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Preserved configured hand joint ordering when applying Pink action targets.
13 changes: 12 additions & 1 deletion source/isaaclab/isaaclab/app/sim_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,12 +412,19 @@ def validate_launch( # noqa: C901
def launch_simulation(
cfg,
launcher_args: argparse.Namespace | dict | None = None,
*,
cleanup_context: bool = True,
) -> Generator[PhysicsCfg | None, None, None]:
"""Context manager that launches the appropriate simulation runtime for *cfg*.

Resolves configured consumers before starting their required runtimes. Each runtime starts
once and closes on exit. Cameras are auto-enabled for Kit-renderer sensors.

On exit, stops and releases a simulation context created inside the scope before closing
its runtimes. An existing context owned by the caller is preserved. Applications
with asynchronous resource owners can set ``cleanup_context=False`` and take
responsibility for context cleanup, including failed construction and drain.

Resolve viewer and physics choices on the same config used to construct the simulation::

sim_cfg = SimulationCfg()
Expand All @@ -437,6 +444,10 @@ def launch_simulation(
a tool that reaches a Kit-only extension API. This is additive -- it can only turn a
kitless launch into a Kit one, never the reverse, so a config that already needs Kit
still launches it when the key is absent or ``False``.
cleanup_context: Automatically release a context created inside this scope.
Disable only when the application owns its cleanup and must retain it
if asynchronous consumers cannot prove they have drained. This controls
context release only; configured runtime launchers still close on exit.
"""
launcher_args = {} if launcher_args is None else launcher_args
launcher_args = vars(launcher_args) if isinstance(launcher_args, argparse.Namespace) else launcher_args
Expand Down Expand Up @@ -500,7 +511,7 @@ def launch_simulation(
finally:
# Release runtime consumers before a launcher can terminate the process.
context = SimulationContext.instance()
if context is not None and context is not previous_context:
if cleanup_context and context is not None and context is not previous_context:
SimulationContext.clear_instance()
except KeyboardInterrupt:
exit_code = 130
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ def _initialize_joint_info(self) -> None:
self._isaaclab_all_joint_ids = list(range(len(self._asset.data.joint_names)))
self.cfg.controller.all_joint_names = self._asset.data.joint_names

# Resolve hand joints
self._hand_joint_ids, self._hand_joint_names = self._asset.find_joints(self.cfg.hand_joint_names)
# Hand targets occupy the action tensor in configuration order. Newton's
# articulation order groups these joints differently, so preserve the
# declared order when resolving ids instead of silently permuting targets.
self._hand_joint_ids, self._hand_joint_names = self._asset.find_joints(
self.cfg.hand_joint_names, preserve_order=True
)

# Combine all joint information
self._controlled_joint_ids = self._isaaclab_controlled_joint_ids + self._hand_joint_ids
Expand Down
18 changes: 16 additions & 2 deletions source/isaaclab/isaaclab/utils/warp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ def _wp_to_torch_with_proxyarray(a, requires_grad=None):

The shim routes :class:`ProxyArray` arguments to their cached ``.torch``
view (a zero-copy :class:`torch.Tensor` of the same underlying memory) and
emits a one-shot :class:`DeprecationWarning`. For any other input type,
the original :func:`warp.to_torch` handles the call as before.
emits a one-shot :class:`DeprecationWarning`. Empty CPU arrays use DLPack
because NumPy rejects their null-pointer array interface. Other native
arrays keep the original :func:`warp.to_torch` conversion.
"""
global _WP_TO_TORCH_WARNED
if isinstance(a, ProxyArray):
Expand All @@ -51,6 +52,19 @@ def _wp_to_torch_with_proxyarray(a, requires_grad=None):
stacklevel=2,
)
return a.torch
if isinstance(a, wp.array) and a.size == 0 and a.device is not None and a.device.is_cpu:
# Preserve Warp's structured-array rejection before the DLPack fallback.
if isinstance(a.dtype, wp._src.codegen.Struct): # noqa: SLF001
return _WP_TO_TORCH_ORIGINAL(a, requires_grad=requires_grad)
import torch

if requires_grad is None:
requires_grad = a.requires_grad
tensor = torch.from_dlpack(wp.to_dlpack(a))
tensor.requires_grad = requires_grad
if requires_grad and a.requires_grad:
tensor.grad = torch.from_dlpack(wp.to_dlpack(a.grad))
return tensor
return _WP_TO_TORCH_ORIGINAL(a, requires_grad=requires_grad)


Expand Down
37 changes: 37 additions & 0 deletions source/isaaclab/test/app/test_launch_simulation_require_kit.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,40 @@ def test_legacy_recording_reuses_the_display_producer(kit_branch_taken, visualiz
assert cfg.sim.visualizer_cfgs[0].headless == (not visualizers)
assert args.get("enable_cameras", False) == (source == "kit")
assert kit_branch_taken == [True] * (source == "kit")


@pytest.mark.parametrize(
"application_owns_cleanup,drain_failed", [(False, False), (False, True), (True, False), (True, True)]
)
def test_context_cleanup_respects_application_drain_ownership(monkeypatch, application_owns_cleanup, drain_failed):
"""An outer launcher must not release a context retained by a failed native drain."""
from isaaclab.sim import SimulationContext

state = {"context": None}
released = []

def clear():
released.append(state["context"])
state["context"] = None

monkeypatch.setattr(SimulationContext, "instance", lambda: state["context"])
monkeypatch.setattr(SimulationContext, "clear_instance", clear)
context = object()

def run():
with launch_simulation(PhysicsCfg(), cleanup_context=not application_owns_cleanup):
state["context"] = context
if drain_failed:
raise RuntimeError("consumer drain unproved")
if application_owns_cleanup:
# The already-owning environment/worker performs normal release.
SimulationContext.clear_instance()

if drain_failed:
with pytest.raises(RuntimeError, match="consumer drain unproved"):
run()
else:
run()
retained = application_owns_cleanup and drain_failed
assert released == ([] if retained else [context])
assert state["context"] is (context if retained else None)
Loading
Loading