From a836e74f4e8ad3b0dc5b7e06839a52d1f6ae7bf0 Mon Sep 17 00:00:00 2001 From: Jiwen Cai Date: Fri, 28 Aug 2026 04:35:42 +0000 Subject: [PATCH] examples(retargeting): package under isaacteleop_examples multi_retargeter_tuning_demo_imgui.py imported example_retargeters as a top-level module, which resolved only because the invoked script's own directory landed on sys.path -- so it broke under `python -m`, broke when copied into another project, and claimed the bare name `example_retargeters`. Move the tree to the layout in examples/README.md. Six co-equal demos, so no __main__.py: each is a submodule and the new README lists them. The example had no README before; the docs were the only place its scripts were named. The sharpa and wuji pages now open their command blocks with the install step, so each is runnable on its own rather than assuming a checkout layout. Part of NVIDIA/IsaacTeleop#985. Signed-off-by: Jiwen Cai --- docs/source/device/trackers.rst | 6 ++--- docs/source/references/retargeting/sharpa.rst | 11 ++++---- docs/source/references/retargeting/wuji.rst | 8 +++--- examples/retargeting/CMakeLists.txt | 4 +-- examples/retargeting/README.md | 25 ++++++++++++++++++ examples/retargeting/pyproject.toml | 26 +++++++++++++++++++ .../retargeting/__init__.py | 2 ++ .../dual_source_teleop_example.py | 2 +- .../retargeting}/example_retargeters.py | 2 +- .../multi_retargeter_tuning_demo_imgui.py | 4 +-- .../sharpa_hand_retargeter_demo.py | 0 .../retargeting}/sources_example.py | 0 .../retargeting}/wuji_hand_retargeter_demo.py | 0 examples/retargeting/python/pyproject.toml | 12 --------- 14 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 examples/retargeting/README.md create mode 100644 examples/retargeting/pyproject.toml create mode 100644 examples/retargeting/python/isaacteleop_examples/retargeting/__init__.py rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/dual_source_teleop_example.py (98%) rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/example_retargeters.py (98%) rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/multi_retargeter_tuning_demo_imgui.py (96%) rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/sharpa_hand_retargeter_demo.py (100%) rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/sources_example.py (100%) rename examples/retargeting/python/{ => isaacteleop_examples/retargeting}/wuji_hand_retargeter_demo.py (100%) delete mode 100644 examples/retargeting/python/pyproject.toml diff --git a/docs/source/device/trackers.rst b/docs/source/device/trackers.rst index d32b6cd101..ba6bbfcfed 100644 --- a/docs/source/device/trackers.rst +++ b/docs/source/device/trackers.rst @@ -212,7 +212,7 @@ Tracks articulated hand joints (26 joints per hand, following the OpenXR - :code-file:`examples/oxr/cpp/oxr_simple_api_demo.cpp` - :code-file:`examples/oxr/python/modular_example.py` - - :code-file:`examples/retargeting/python/sources_example.py` + - :code-file:`examples/retargeting/python/isaacteleop_examples/retargeting/sources_example.py` ControllerTracker ~~~~~~~~~~~~~~~~~ @@ -232,7 +232,7 @@ axis inputs. Uses standard OpenXR action bindings. - Examples: - - :code-file:`examples/retargeting/python/sources_example.py` + - :code-file:`examples/retargeting/python/isaacteleop_examples/retargeting/sources_example.py` - :code-file:`examples/teleop/python/locomotion_retargeting_example.py` - :code-file:`examples/teleop/python/gripper_retargeting_example_simple.py` @@ -382,6 +382,6 @@ For end-to-end usage patterns combining trackers with a ``DeviceIOSession``, see For higher-level usage with the teleop session manager and retargeting, see: -- :code-file:`examples/retargeting/python/sources_example.py` +- :code-file:`examples/retargeting/python/isaacteleop_examples/retargeting/sources_example.py` - :code-file:`examples/teleop/python/gripper_retargeting_example_simple.py` - :code-file:`examples/teleop/python/locomotion_retargeting_example.py` diff --git a/docs/source/references/retargeting/sharpa.rst b/docs/source/references/retargeting/sharpa.rst index ae53362362..3e8f508b29 100644 --- a/docs/source/references/retargeting/sharpa.rst +++ b/docs/source/references/retargeting/sharpa.rst @@ -125,19 +125,20 @@ produced in your target joint order. Run the example --------------- -The repo ships a bimanual demo at -``examples/retargeting/python/sharpa_hand_retargeter_demo.py``: +The repo ships a bimanual demo in ``examples/retargeting``: .. code-block:: console + $ uv pip install -e ./examples/retargeting + # Synthetic curl animation (no headset, no GUI required): - $ python examples/retargeting/python/sharpa_hand_retargeter_demo.py --synthetic + $ python -m isaacteleop_examples.retargeting.sharpa_hand_retargeter_demo --synthetic # Live bimanual from a connected Quest headset: - $ python examples/retargeting/python/sharpa_hand_retargeter_demo.py + $ python -m isaacteleop_examples.retargeting.sharpa_hand_retargeter_demo # Custom MJCFs (e.g. the mesh-bearing variants): - $ python examples/retargeting/python/sharpa_hand_retargeter_demo.py \ + $ python -m isaacteleop_examples.retargeting.sharpa_hand_retargeter_demo \ --left-mjcf /path/to/left_sharpawave.xml \ --right-mjcf /path/to/right_sharpawave.xml diff --git a/docs/source/references/retargeting/wuji.rst b/docs/source/references/retargeting/wuji.rst index 0c95cb45fc..08201fa3c9 100644 --- a/docs/source/references/retargeting/wuji.rst +++ b/docs/source/references/retargeting/wuji.rst @@ -69,16 +69,18 @@ real Wuji hand: .. code-block:: console + $ uv pip install -e ./examples/retargeting + # Device-free smoke test - $ python examples/retargeting/python/wuji_hand_retargeter_demo.py \ + $ python -m isaacteleop_examples.retargeting.wuji_hand_retargeter_demo \ --mode synthetic --model wuji_hand_2 --hand right # Replay MediaPipe-format keypoints - $ python examples/retargeting/python/wuji_hand_retargeter_demo.py \ + $ python -m isaacteleop_examples.retargeting.wuji_hand_retargeter_demo \ --mode replay --replay my_take.pkl --model wuji_hand_2 --hand right # Drive real hardware and let TeleopSession launch the Wuji glove plugin - $ python examples/retargeting/python/wuji_hand_retargeter_demo.py \ + $ python -m isaacteleop_examples.retargeting.wuji_hand_retargeter_demo \ --mode drive --hand right --plugin-path build/src/plugins Drive mode needs a running CloudXR runtime, an OpenXR hand-tracking source, and diff --git a/examples/retargeting/CMakeLists.txt b/examples/retargeting/CMakeLists.txt index 1286a8b55c..1e42335683 100644 --- a/examples/retargeting/CMakeLists.txt +++ b/examples/retargeting/CMakeLists.txt @@ -1,7 +1,7 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20) include(${CMAKE_SOURCE_DIR}/cmake/InstallPythonExample.cmake) -install_python_example(DESTINATION examples/retargeting/python) +install_python_example(DESTINATION examples/retargeting) diff --git a/examples/retargeting/README.md b/examples/retargeting/README.md new file mode 100644 index 0000000000..7cb862fe86 --- /dev/null +++ b/examples/retargeting/README.md @@ -0,0 +1,25 @@ + + +# Retargeting engine examples + +Standalone demos of the retargeting engine. Install once, then run whichever +one you want: + +```bash +uv pip install -e ./examples/retargeting +python -m isaacteleop_examples.retargeting.sources_example +``` + +| Module | What it shows | +| --- | --- | +| `sources_example` | Wiring DeviceIO sources into a retargeting graph | +| `dual_source_teleop_example` | Two input sources driving one session | +| `example_retargeters` | `GainOffsetRetargeter`, a minimal custom retargeter | +| `multi_retargeter_tuning_demo_imgui` | Tuning several retargeters live, with an ImGui panel | +| `sharpa_hand_retargeter_demo` | Bimanual Sharpa hands; `--synthetic` needs no headset | +| `wuji_hand_retargeter_demo` | Wuji hand, in `synthetic` / `replay` / `drive` modes | + +Docs: `docs/source/references/retargeting/`. diff --git a/examples/retargeting/pyproject.toml b/examples/retargeting/pyproject.toml new file mode 100644 index 0000000000..e23a686158 --- /dev/null +++ b/examples/retargeting/pyproject.toml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +# The dist name mirrors the import path, so an installed example claims no bare +# top-level name in site-packages. +name = "isaacteleop-examples-retargeting" +version = "0.0.0" # Internal examples - not versioned +description = "Retargeting Engine Python Examples" +requires-python = ">=3.11,<3.14" +dependencies = [ + "isaacteleop[ui,retargeters-lite]", + "numpy>=1.23.0", +] + +# `isaacteleop_examples` is a PEP 420 namespace shared by every example dist and +# must stay without an __init__.py. `only-include` + `sources`, not `packages`: +# the latter keeps only the last path component and would root the wheel at a +# bare top-level `retargeting/`. +[tool.hatch.build.targets.wheel] +only-include = ["python/isaacteleop_examples/retargeting"] +sources = ["python"] diff --git a/examples/retargeting/python/isaacteleop_examples/retargeting/__init__.py b/examples/retargeting/python/isaacteleop_examples/retargeting/__init__.py new file mode 100644 index 0000000000..52a7a9daf0 --- /dev/null +++ b/examples/retargeting/python/isaacteleop_examples/retargeting/__init__.py @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/examples/retargeting/python/dual_source_teleop_example.py b/examples/retargeting/python/isaacteleop_examples/retargeting/dual_source_teleop_example.py similarity index 98% rename from examples/retargeting/python/dual_source_teleop_example.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/dual_source_teleop_example.py index 97d5632756..8f2e60a711 100644 --- a/examples/retargeting/python/dual_source_teleop_example.py +++ b/examples/retargeting/python/isaacteleop_examples/retargeting/dual_source_teleop_example.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ diff --git a/examples/retargeting/python/example_retargeters.py b/examples/retargeting/python/isaacteleop_examples/retargeting/example_retargeters.py similarity index 98% rename from examples/retargeting/python/example_retargeters.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/example_retargeters.py index 66e0a7e216..6b867162f9 100644 --- a/examples/retargeting/python/example_retargeters.py +++ b/examples/retargeting/python/isaacteleop_examples/retargeting/example_retargeters.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ diff --git a/examples/retargeting/python/multi_retargeter_tuning_demo_imgui.py b/examples/retargeting/python/isaacteleop_examples/retargeting/multi_retargeter_tuning_demo_imgui.py similarity index 96% rename from examples/retargeting/python/multi_retargeter_tuning_demo_imgui.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/multi_retargeter_tuning_demo_imgui.py index a3236636cb..ad7049f957 100755 --- a/examples/retargeting/python/multi_retargeter_tuning_demo_imgui.py +++ b/examples/retargeting/python/isaacteleop_examples/retargeting/multi_retargeter_tuning_demo_imgui.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """ @@ -25,7 +25,7 @@ import numpy as np -from example_retargeters import GainOffsetRetargeter +from .example_retargeters import GainOffsetRetargeter from isaacteleop.retargeting_engine.interface import TensorGroup from isaacteleop.retargeting_engine_ui import ( MultiRetargeterTuningUIImGui, diff --git a/examples/retargeting/python/sharpa_hand_retargeter_demo.py b/examples/retargeting/python/isaacteleop_examples/retargeting/sharpa_hand_retargeter_demo.py similarity index 100% rename from examples/retargeting/python/sharpa_hand_retargeter_demo.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/sharpa_hand_retargeter_demo.py diff --git a/examples/retargeting/python/sources_example.py b/examples/retargeting/python/isaacteleop_examples/retargeting/sources_example.py similarity index 100% rename from examples/retargeting/python/sources_example.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/sources_example.py diff --git a/examples/retargeting/python/wuji_hand_retargeter_demo.py b/examples/retargeting/python/isaacteleop_examples/retargeting/wuji_hand_retargeter_demo.py similarity index 100% rename from examples/retargeting/python/wuji_hand_retargeter_demo.py rename to examples/retargeting/python/isaacteleop_examples/retargeting/wuji_hand_retargeter_demo.py diff --git a/examples/retargeting/python/pyproject.toml b/examples/retargeting/python/pyproject.toml deleted file mode 100644 index 6eff918fc4..0000000000 --- a/examples/retargeting/python/pyproject.toml +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -[project] -name = "retargeting-examples" -version = "0.0.0" # Internal examples - not versioned -description = "Retargeting Engine Python Examples" -requires-python = ">=3.11,<3.14" -dependencies = [ - "isaacteleop[ui,retargeters-lite]", - "numpy>=1.23.0", -]