Skip to content

Commit ac0a8a9

Browse files
committed
Enable pipelined IsaacTeleop retargeting
Default IsaacTeleop to deadline-paced pipelined retargeting. Pass the execution config directly into TeleopSessionConfig. Document why the 25 ms pacing margin staggers IsaacTeleop Python work. It lets native rendering overlap instead of competing for the GIL.
1 parent b82b93b commit ac0a8a9

6 files changed

Lines changed: 35 additions & 171 deletions

File tree

docs/source/features/isaac_teleop.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -686,10 +686,13 @@ Key ``IsaacTeleopCfg`` fields:
686686
* ``plugins`` -- list of Isaac Teleop plugin configurations (e.g. Manus).
687687
* ``sim_device`` -- torch device string (default ``"cuda:0"``).
688688
* ``retargeting_execution`` -- IsaacTeleop retargeting execution settings.
689-
Defaults to ``RetargetingExecutionConfig(mode="pipelined")`` so retargeting
690-
can run on the IsaacTeleop worker instead of blocking the simulation loop
691-
when the installed IsaacTeleop version supports that API. Older IsaacTeleop
692-
versions ignore this setting and use their historical execution behavior.
689+
Defaults to ``RetargetingExecutionConfig(mode="pipelined")`` with
690+
``DeadlinePacingConfig(safety_margin_s=0.025)`` so retargeting can run on
691+
the IsaacTeleop worker instead of blocking the simulation loop.
692+
The 25 ms safety margin staggers IsaacTeleop's Python work behind Isaac
693+
Lab's step Python, giving native work such as rendering time to overlap
694+
instead of having both Python stacks contend for the GIL at the start of
695+
the step.
693696

694697
.. warning::
695698

source/isaaclab_teleop/changelog.d/hougantc-pipelined-retargeting.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ Changed
88
^^^^^^^
99

1010
* Changed :class:`~isaaclab_teleop.IsaacTeleopCfg` to enable IsaacTeleop
11-
deadline-paced pipelined retargeting by default when supported by the
12-
installed IsaacTeleop version. This returns the latest completed retargeting
13-
output while the current frame is submitted, using
14-
``DeadlinePacingConfig(safety_margin_s=0.025)`` to sample close to
15-
the next simulation consumption point. Set
11+
deadline-paced pipelined retargeting by default. This returns the latest
12+
completed retargeting output while the current frame is submitted, using
13+
``DeadlinePacingConfig(safety_margin_s=0.025)`` to sample close to the next
14+
simulation consumption point and stagger IsaacTeleop's Python work behind
15+
Isaac Lab's step Python. Set
1616
``retargeting_execution=RetargetingExecutionConfig(mode="sync")`` to restore
1717
exact current-frame retargeting.

source/isaaclab_teleop/docs/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,14 @@ rendering without blocking.
120120
| `retargeters_to_tune` | `Callable[[], list[BaseRetargeter]] \| None` | `None` | Retargeters to expose in the tuning UI |
121121
| `plugins` | `list[PluginConfig]` | `[]` | IsaacTeleop plugin configurations |
122122
| `sim_device` | `str` | `"cuda:0"` | Torch device for output action tensors |
123-
| `retargeting_execution` | `RetargetingExecutionConfig \| None` | `mode="pipelined", pacing=DeadlinePacingConfig(safety_margin_s=0.025)` when supported | IsaacTeleop retargeting execution settings |
123+
| `retargeting_execution` | `RetargetingExecutionConfig` | `mode="pipelined", pacing=DeadlinePacingConfig(safety_margin_s=0.025)` | IsaacTeleop retargeting execution settings |
124124
| `teleoperation_active_default` | `bool` | `False` | Whether teleoperation is active on session start |
125125
| `app_name` | `str` | `"IsaacLabTeleop"` | Application name for the IsaacTeleop session |
126126

127+
The 25 ms `DeadlinePacingConfig` safety margin staggers IsaacTeleop's Python work behind Isaac Lab's
128+
step Python, giving native work such as rendering time to overlap instead of having both Python stacks
129+
contend for the GIL at the start of the step.
130+
127131
### `XrCfg`
128132

129133
| Field | Type | Default | Description |

source/isaaclab_teleop/isaaclab_teleop/isaac_teleop_cfg.py

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from collections.abc import Callable
1111
from dataclasses import MISSING, field
1212
from pathlib import Path
13-
from typing import TYPE_CHECKING, Any
13+
from typing import TYPE_CHECKING
14+
15+
from isaacteleop.teleop_session_manager import DeadlinePacingConfig, RetargetingExecutionConfig
1416

1517
from isaaclab.utils import configclass
1618

@@ -30,33 +32,6 @@
3032
from isaacteleop.teleop_session_manager import PluginConfig
3133

3234

33-
def _retargeting_execution_is_supported(teleop_session_manager: Any | None) -> bool:
34-
"""Check whether IsaacTeleop exposes retargeting execution configuration."""
35-
return (
36-
teleop_session_manager is not None
37-
and hasattr(teleop_session_manager, "RetargetingExecutionConfig")
38-
and hasattr(teleop_session_manager, "DeadlinePacingConfig")
39-
)
40-
41-
42-
try:
43-
import isaacteleop.teleop_session_manager as _tsm
44-
except ImportError:
45-
_tsm = None
46-
47-
_RETARGETING_EXECUTION_SUPPORTED = _retargeting_execution_is_supported(_tsm)
48-
49-
50-
def _default_retargeting_execution_config() -> Any | None:
51-
"""Build Isaac Lab's default IsaacTeleop retargeting execution config."""
52-
if not _RETARGETING_EXECUTION_SUPPORTED:
53-
return None
54-
return _tsm.RetargetingExecutionConfig(
55-
mode="pipelined",
56-
pacing=_tsm.DeadlinePacingConfig(safety_margin_s=0.025),
57-
)
58-
59-
6035
@configclass
6136
class IsaacTeleopCfg:
6237
"""Configuration for IsaacTeleop-based teleoperation.
@@ -122,14 +97,17 @@ def build_pipeline():
12297
sim_device: str = "cuda:0"
12398
"""Torch device string for placing output action tensors."""
12499

125-
retargeting_execution: Any | None = field(default_factory=_default_retargeting_execution_config)
100+
retargeting_execution: RetargetingExecutionConfig = field(
101+
default_factory=lambda: RetargetingExecutionConfig(
102+
mode="pipelined",
103+
pacing=DeadlinePacingConfig(safety_margin_s=0.025),
104+
)
105+
)
126106
"""IsaacTeleop retargeting execution settings.
127107
128-
Isaac Lab opts into IsaacTeleop's pipelined execution by default when the
129-
installed IsaacTeleop package exposes ``RetargetingExecutionConfig``.
130-
Older IsaacTeleop releases leave this as ``None`` and use their historical
131-
execution behavior. Set this to ``RetargetingExecutionConfig(mode="sync")``
132-
for exact current-frame retargeting while debugging or comparing behavior.
108+
Isaac Lab opts into IsaacTeleop's pipelined execution by default. Set this
109+
to ``RetargetingExecutionConfig(mode="sync")`` for exact current-frame
110+
retargeting while debugging or comparing behavior.
133111
"""
134112

135113
teleoperation_active_default: bool = False

source/isaaclab_teleop/isaaclab_teleop/session_lifecycle.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from isaacteleop.teleop_session_manager import TeleopSession
2424

2525
from .control_events import _NO_OP_EVENTS, ControlEvents
26-
from .isaac_teleop_cfg import _RETARGETING_EXECUTION_SUPPORTED, IsaacTeleopCfg
26+
from .isaac_teleop_cfg import IsaacTeleopCfg
2727
from .teleop_message_processor import TeleopMessageProcessor
2828

2929

@@ -495,24 +495,14 @@ def _try_start_session(self) -> bool:
495495
self._session_start_deferred_logged = True
496496
return False
497497

498-
extra_kwargs: dict[str, Any] = {}
499-
if self._cfg.retargeting_execution is not None:
500-
if _RETARGETING_EXECUTION_SUPPORTED:
501-
extra_kwargs["retargeting_execution"] = self._cfg.retargeting_execution
502-
else:
503-
logger.warning(
504-
"Installed IsaacTeleop does not support retargeting_execution; "
505-
"using its default retargeting behavior."
506-
)
507-
508498
session_config = TeleopSessionConfig(
509499
app_name=self._cfg.app_name,
510500
trackers=[],
511501
pipeline=self._pipeline,
512502
teleop_control_pipeline=self._teleop_control_pipeline,
513503
plugins=self._cfg.plugins,
514504
oxr_handles=oxr_handles,
515-
**extra_kwargs,
505+
retargeting_execution=self._cfg.retargeting_execution,
516506
)
517507

518508
# Create and enter the TeleopSession

source/isaaclab_teleop/test/test_cloudxr_lifecycle.py

Lines changed: 4 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -150,24 +150,6 @@ def _make_lifecycle(
150150
)
151151

152152

153-
def _make_supported_tsm_module() -> ModuleType:
154-
"""Build a fake ``teleop_session_manager`` with the new execution config API."""
155-
156-
@dataclass
157-
class DeadlinePacingConfig:
158-
safety_margin_s: float = 0.025
159-
160-
@dataclass
161-
class RetargetingExecutionConfig:
162-
mode: str = "sync"
163-
pacing: DeadlinePacingConfig | None = None
164-
165-
tsm = ModuleType("isaacteleop.teleop_session_manager")
166-
tsm.DeadlinePacingConfig = DeadlinePacingConfig # type: ignore[attr-defined]
167-
tsm.RetargetingExecutionConfig = RetargetingExecutionConfig # type: ignore[attr-defined]
168-
return tsm
169-
170-
171153
# ============================================================================
172154
# Shipped .env profile paths
173155
# ============================================================================
@@ -206,48 +188,14 @@ def test_profiles_are_in_same_directory(self):
206188
class TestRetargetingExecutionConfig:
207189
"""Tests for Isaac Lab's IsaacTeleop retargeting execution defaults."""
208190

209-
def test_cfg_defaults_to_deadline_paced_pipelined_retargeting(self):
210-
"""Isaac Lab defaults to deadline-paced pipelined retargeting."""
211-
import isaaclab_teleop.isaac_teleop_cfg as cfg_module
212-
213-
with (
214-
patch.object(cfg_module, "_RETARGETING_EXECUTION_SUPPORTED", True),
215-
patch.object(cfg_module, "_tsm", _make_supported_tsm_module()),
216-
):
217-
cfg = _make_cfg()
191+
def test_session_config_receives_deadline_paced_pipelined_retargeting(self):
192+
"""The default retargeting execution config is passed into TeleopSession."""
193+
cfg = _make_cfg()
218194

219195
assert cfg.retargeting_execution.mode == "pipelined"
220196
assert cfg.retargeting_execution.pacing.safety_margin_s == 0.025
221197

222-
def test_cfg_defaults_to_none_with_legacy_isaacteleop(self):
223-
"""Older IsaacTeleop releases can still construct IsaacTeleopCfg."""
224-
import isaaclab_teleop.isaac_teleop_cfg as cfg_module
225-
226-
with patch.object(cfg_module, "_RETARGETING_EXECUTION_SUPPORTED", False):
227-
cfg = _make_cfg()
228-
229-
assert cfg.retargeting_execution is None
230-
231-
def test_retargeting_execution_support_requires_new_config_classes(self):
232-
"""IsaacTeleop must expose both execution config classes to enable the default."""
233-
import isaaclab_teleop.isaac_teleop_cfg as cfg_module
234-
235-
legacy_tsm = ModuleType("isaacteleop.teleop_session_manager")
236-
assert not cfg_module._retargeting_execution_is_supported(legacy_tsm)
237-
238-
legacy_tsm.RetargetingExecutionConfig = object
239-
assert not cfg_module._retargeting_execution_is_supported(legacy_tsm)
240-
241-
legacy_tsm.DeadlinePacingConfig = object
242-
assert cfg_module._retargeting_execution_is_supported(legacy_tsm)
243-
244-
def test_session_config_receives_cfg_retargeting_execution(self):
245-
"""The configured IsaacTeleop execution mode is passed into TeleopSession."""
246-
import isaaclab_teleop.session_lifecycle as lifecycle_module
247-
248-
cfg = _make_cfg()
249-
sentinel_execution = object()
250-
cfg.retargeting_execution = sentinel_execution
198+
sentinel_execution = cfg.retargeting_execution
251199

252200
lifecycle = TeleopSessionLifecycle(cfg)
253201
lifecycle._pipeline = MagicMock()
@@ -258,7 +206,6 @@ def test_session_config_receives_cfg_retargeting_execution(self):
258206
fake_tsm_module = sys.modules["isaacteleop.teleop_session_manager"]
259207

260208
with (
261-
patch.object(lifecycle_module, "_RETARGETING_EXECUTION_SUPPORTED", True),
262209
patch.object(fake_tsm_module, "TeleopSessionConfig", session_config_cls),
263210
patch.object(fake_tsm_module, "TeleopSession", session_cls),
264211
patch.object(lifecycle, "_ensure_xr_ar_profile_enabled"),
@@ -268,64 +215,6 @@ def test_session_config_receives_cfg_retargeting_execution(self):
268215

269216
assert session_config_cls.call_args.kwargs["retargeting_execution"] is sentinel_execution
270217

271-
def test_session_config_omits_retargeting_execution_for_legacy_isaacteleop(self, caplog):
272-
"""When IsaacTeleop lacks the new API, the kwarg is never passed."""
273-
import isaaclab_teleop.isaac_teleop_cfg as cfg_module
274-
import isaaclab_teleop.session_lifecycle as lifecycle_module
275-
276-
with patch.object(cfg_module, "_RETARGETING_EXECUTION_SUPPORTED", False):
277-
cfg = _make_cfg()
278-
279-
cfg.retargeting_execution = object()
280-
281-
lifecycle = TeleopSessionLifecycle(cfg)
282-
lifecycle._pipeline = MagicMock()
283-
lifecycle._teleop_control_pipeline = None
284-
285-
session_config_cls = MagicMock(return_value=MagicMock())
286-
session_cls = MagicMock()
287-
fake_tsm_module = sys.modules["isaacteleop.teleop_session_manager"]
288-
289-
with (
290-
patch.object(lifecycle_module, "_RETARGETING_EXECUTION_SUPPORTED", False),
291-
patch.object(fake_tsm_module, "TeleopSessionConfig", session_config_cls),
292-
patch.object(fake_tsm_module, "TeleopSession", session_cls),
293-
patch.object(lifecycle, "_ensure_xr_ar_profile_enabled"),
294-
patch.object(lifecycle, "_acquire_kit_oxr_handles", return_value=object()),
295-
caplog.at_level("WARNING", logger="isaaclab_teleop.session_lifecycle"),
296-
):
297-
assert lifecycle.try_start_session() is True
298-
299-
assert "retargeting_execution" not in session_config_cls.call_args.kwargs
300-
assert session_config_cls.call_args.kwargs["app_name"] == cfg.app_name
301-
assert "does not support retargeting_execution" in caplog.text
302-
303-
def test_session_config_propagates_typeerror_from_supported_isaacteleop(self):
304-
"""A TypeError from a supported IsaacTeleop must surface, not be swallowed."""
305-
import isaaclab_teleop.session_lifecycle as lifecycle_module
306-
307-
cfg = _make_cfg()
308-
cfg.retargeting_execution = object()
309-
310-
lifecycle = TeleopSessionLifecycle(cfg)
311-
lifecycle._pipeline = MagicMock()
312-
lifecycle._teleop_control_pipeline = None
313-
314-
def raising_session_config(**kwargs):
315-
raise TypeError("invalid value for retargeting_execution")
316-
317-
fake_tsm_module = sys.modules["isaacteleop.teleop_session_manager"]
318-
319-
with (
320-
patch.object(lifecycle_module, "_RETARGETING_EXECUTION_SUPPORTED", True),
321-
patch.object(fake_tsm_module, "TeleopSessionConfig", raising_session_config),
322-
patch.object(fake_tsm_module, "TeleopSession", MagicMock()),
323-
patch.object(lifecycle, "_ensure_xr_ar_profile_enabled"),
324-
patch.object(lifecycle, "_acquire_kit_oxr_handles", return_value=object()),
325-
):
326-
with pytest.raises(TypeError, match="retargeting_execution"):
327-
lifecycle.try_start_session()
328-
329218

330219
# ============================================================================
331220
# _ensure_cloudxr_runtime

0 commit comments

Comments
 (0)