8080
8181
8282def _prepare_env_cfg (task : str , num_envs : int , device : str ) -> ManagerBasedRLEnvCfg :
83- """Build and tweak an env config suitable for non-interactive replay."""
83+ """Build and tweak an env config suitable for non-interactive replay.
84+
85+ Mirrors the env-config mutations performed by ``record_demos.py``'s
86+ :func:`create_environment_config` so the replay-side environment behaves
87+ identically to the recording-side one. In particular the ``success`` and
88+ ``time_out`` termination terms are cleared -- if either fired mid-replay
89+ it would call ``_reset_idx`` and snap the robot back to its initial
90+ pose, masking any IK tracking.
91+ """
8492 env_cfg = parse_env_cfg (task , device = device , num_envs = num_envs )
85- env_cfg .env_name = task
93+ env_cfg .env_name = task . split ( ":" )[ - 1 ]
8694 if not isinstance (env_cfg , ManagerBasedRLEnvCfg ):
8795 raise ValueError (
8896 "teleop_replay_agent only supports ManagerBasedRLEnv environments. "
8997 f"Received environment config type: { type (env_cfg ).__name__ } "
9098 )
9199 env_cfg .terminations .time_out = None
100+ if hasattr (env_cfg .terminations , "success" ):
101+ env_cfg .terminations .success = None
92102 env_cfg = remove_camera_configs (env_cfg )
93103 env_cfg .sim .render .antialiasing_mode = "DLSS"
94104 return env_cfg
@@ -107,10 +117,10 @@ def _create_replay_teleop_device(
107117 Args:
108118 env_cfg: The environment configuration.
109119 task: Task identifier, used for diagnostic messages.
110- callbacks: Teleop-command callbacks (``"START"``, ``"STOP"``,
111- ``"RESET"`` ) registered on the device. The XCR replay dispatches
112- the recorded user's start/stop/reset gestures through Kit's
113- OpenXR message bus, which the legacy
120+ callbacks: Teleop-command callbacks (typically just ``"START"`` for
121+ replay; see :func:`main` ) registered on the device. The XCR
122+ replay dispatches the recorded user's start gesture through
123+ Kit's OpenXR message bus, which the legacy
114124 :class:`~isaaclab.devices.openxr.OpenXRDevice` translates into
115125 calls into this dictionary.
116126 """
@@ -182,13 +192,19 @@ def main() -> None:
182192
183193 The loop deliberately does not call ``env.step()`` until the legacy
184194 :class:`OpenXRDevice` dispatches a ``"START"`` callback. The XCR replay
185- streams the recorded user's start/stop/reset gestures through Kit's
186- OpenXR message bus, and the device routes those into the callbacks
187- registered here -- exactly the path ``record_demos.py`` uses to know
188- when to start recording. Until that ``"START"`` arrives, the OpenXR
189- runtime is silent and the device's :meth:`advance` would otherwise
190- return a default zero pose for both wrists, which stepping the env
191- with would drive Pink IK toward the world origin.
195+ restores the recorded user's start gesture through Kit's OpenXR message
196+ bus, and the device routes that into the callback registered here --
197+ exactly the path ``record_demos.py`` uses to know when to start
198+ recording. Until that ``"START"`` arrives, the OpenXR runtime is silent
199+ and the device's :meth:`advance` would otherwise return a default zero
200+ pose for both wrists, which stepping the env with would drive Pink IK
201+ toward the world origin.
202+
203+ Unlike :file:`record_demos.py`, the replay agent does **not** subscribe
204+ to the ``"STOP"`` callback: Kit's ``teleop_command`` bus drains queued
205+ events as a batch when the AR profile is enabled, so a recorded STOP
206+ gesture fires within milliseconds of START and would gate the env-step
207+ loop off again before Pink IK had time to converge.
192208
193209 Resource cleanup is wrapped in a ``try/finally`` so that ``env.close()``
194210 always runs, even when device construction or any subsequent setup
@@ -207,16 +223,27 @@ def _on_start() -> None:
207223 teleop_active [0 ] = True
208224 print ("Teleop START received from XCR replay; forwarding actions to env.step()." )
209225
210- def _on_stop () -> None :
211- if teleop_active [0 ]:
212- teleop_active [0 ] = False
213- print ("Teleop STOP received from XCR replay; pausing env.step()." )
214-
215- callbacks : dict [str , Callable [[], None ]] = {"START" : _on_start , "STOP" : _on_stop }
226+ # Intentionally only subscribe to START, not STOP. The XCR replay
227+ # restores both the recorded user's start and stop gestures from the
228+ # capture file, and Kit's ``teleop_command`` message bus appears to
229+ # drain queued events as a batch when the AR profile is enabled --
230+ # so a STOP fires within milliseconds of START and would shut the env
231+ # step loop off before Pink IK has had a chance to converge. For the
232+ # replay agent's one-shot CI use case the only valid termination is
233+ # the driver's ``post_quit`` (or a real exception in the loop).
234+ callbacks : dict [str , Callable [[], None ]] = {"START" : _on_start }
216235
217236 teleop_interface = _create_replay_teleop_device (env_cfg , args_cli .task , callbacks )
218237 print (f"Using teleop device: { teleop_interface } " )
219238
239+ # Mirror the reset sequence used by ``record_demos.py``: ``sim.reset()``
240+ # does a hard physics reinit (re-binds articulation views, plays the
241+ # timeline) that ``env.reset()`` alone does not perform. Pink IK reads
242+ # ``data.joint_pos.torch`` every step to seed Pinocchio's configuration
243+ # and to compute ``target = curr + delta``; if the articulation view is
244+ # stale, every IK call produces zero-delta arm targets while the
245+ # hand-finger path (which bypasses IK) keeps tracking. See PR #5507.
246+ env .sim .reset ()
220247 env .reset ()
221248 teleop_interface .reset ()
222249
0 commit comments