Skip to content

Commit b6afa8e

Browse files
matthewtrepteisaaclab-bot[bot]
authored andcommitted
Fix OVD Recorder hanging silently on non-PhysX backends (#7507)
## Description The OVD Recorder (`AnimationRecorder`) only works with the PhysX backend, but most tasks now default to Newton. As a result, `--anim_recording_enabled` silently did nothing: recording never started, `--anim_recording_stop_time` was never reached, and the process ran until manually killed with nothing saved. This PR: - Raises a clear error at simulation startup when `--anim_recording_enabled` is set on a physics backend that doesn't support the OVD Recorder (via a new `PhysicsManager.supports_anim_recording` flag, set `True` only on `PhysxManager`), naming the active backend and pointing the user to `physics=isaacsim_physx`. - Adds an `overrides` parameter to `parse_env_cfg()` so standalone (non-Hydra) scripts can apply `physics=`/`renderer=`/`presets=` overrides, and updates the `run_cartpole_rl_env.py` tutorial to forward unrecognized CLI args this way. - Updates `docs/source/how-to/record_animation.rst` to note the PhysX requirement and fix the example command so it actually works as documented. ## Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing - Reran the exact documented repro command on the default (Newton) backend: now fails fast with a clear `ValueError` instead of hanging. - Reran with `physics=isaacsim_physx`: recording stops at `--anim_recording_stop_time`, saves `baked_animation_recording.usda`, process exits cleanly (exit code 0). - `uv run isaaclab -f` passes for all touched files. - Targeted tests (`test_physics_manager_lifecycle.py`, `test_hydra.py`): 94 passed. - Docs build (`make -C docs current-docs`, warnings-as-errors): succeeded with no warnings. ## Checklist - [x] I have read and understood the contribution guidelines - [x] I have run the `pre-commit` checks with `uv run isaaclab -f` - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added a changelog fragment under `source/<pkg>/changelog.d/` for every touched package - [x] I have added my name to `CONTRIBUTORS.md` (already present) ## Release backport - [x] <!-- backport-active-release --> Backport this pull request to the active release branch after it merges into `develop` (cherry picked from commit 69de9b2)
1 parent 0305853 commit b6afa8e

22 files changed

Lines changed: 169 additions & 38 deletions

File tree

docs/source/how-to/record_animation.rst

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ This method is more scalable and better suited for large-scale training scenario
9797

9898
It’s not UI-controlled—the whole process is enabled through CLI flags and runs automatically.
9999

100+
.. note::
101+
102+
The OVD Recorder uses OmniPVD, which only records **PhysX** simulations. If the active physics backend is not
103+
PhysX (for example, Newton, which is the default for many tasks), Isaac Lab raises an error at startup naming
104+
the active backend instead of recording. Select the PhysX backend by adding ``physics=isaacsim_physx`` to the
105+
command line, as shown below.
106+
107+
The PhysX backend requires Isaac Sim. If it isn't installed yet, add ``--extra isaacsim`` to the ``uv run``
108+
command; see :ref:`installation-optional-extras` for details.
109+
100110

101111
Workflow Summary
102112
~~~~~~~~~~~~~~~~
@@ -120,14 +130,14 @@ To record an animation:
120130

121131
.. code-block:: bash
122132
123-
uv run python scripts/tutorials/03_envs/run_cartpole_rl_env.py --anim_recording_enabled --anim_recording_start_time 1 --anim_recording_stop_time 3
133+
uv run python scripts/tutorials/03_envs/run_cartpole_rl_env.py --anim_recording_enabled --anim_recording_start_time 1 --anim_recording_stop_time 3 physics=isaacsim_physx
124134
125135
.. tab-item:: :icon:`fa-brands fa-windows` Windows
126136
:sync: windows
127137

128138
.. code-block:: batch
129139
130-
uv run python scripts\tutorials\03_envs\run_cartpole_rl_env.py --anim_recording_enabled --anim_recording_start_time 1 --anim_recording_stop_time 3
140+
uv run python scripts\tutorials\03_envs\run_cartpole_rl_env.py --anim_recording_enabled --anim_recording_start_time 1 --anim_recording_stop_time 3 physics=isaacsim_physx
131141
132142
.. note::
133143

scripts/benchmarks/benchmark_cameras.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@
236236

237237

238238
AppLauncher.add_app_launcher_args(parser)
239-
args_cli = parser.parse_args()
239+
# forward unrecognized args as Hydra-style task config overrides
240+
args_cli, hydra_overrides = parser.parse_known_args()
240241
args_cli.enable_cameras = True
241242

242243
if args_cli.autotune:
@@ -527,7 +528,7 @@ def inject_cameras_into_task(
527528
num_cameras_per_env: int = 1,
528529
) -> gym.Env:
529530
"""Loads the task, sticks cameras into the config, and creates the environment."""
530-
cfg = parse_env_cfg(task, device=args_cli.device, use_fabric=args_cli.use_fabric)
531+
cfg = parse_env_cfg(task, device=args_cli.device, use_fabric=args_cli.use_fabric, overrides=hydra_overrides)
531532
scene_cfg = cfg.scene
532533

533534
num_envs = int(num_cams / num_cameras_per_env)

scripts/environments/export_IODescriptors.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
parser.add_argument("--output_dir", type=str, default=None, help="Path to the output directory.")
2020
# append AppLauncher cli args
2121
AppLauncher.add_app_launcher_args(parser)
22-
# parse the arguments
23-
args_cli = parser.parse_args()
22+
# parse the arguments, forwarding unrecognized ones as Hydra-style task config overrides
23+
args_cli, hydra_overrides = parser.parse_known_args()
2424
args_cli.headless = True
2525

2626
# launch omniverse app
@@ -44,7 +44,9 @@
4444
def main():
4545
"""Random actions agent with Isaac Lab environment."""
4646
# create environment configuration
47-
env_cfg = parse_env_cfg(args_cli.task, device=args_cli.device, num_envs=1, use_fabric=True)
47+
env_cfg = parse_env_cfg(
48+
args_cli.task, device=args_cli.device, num_envs=1, use_fabric=True, overrides=hydra_overrides
49+
)
4850
# create environment
4951
env = gym.make(args_cli.task, cfg=env_cfg)
5052

scripts/environments/state_machine/lift_cube_sm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
3030
# append AppLauncher cli args
3131
AppLauncher.add_app_launcher_args(parser)
32-
# parse the arguments
33-
args_cli = parser.parse_args()
32+
# parse the arguments, forwarding unrecognized ones as Hydra-style task config overrides
33+
args_cli, hydra_overrides = parser.parse_known_args()
3434

3535
# launch omniverse app
3636
app_launcher = AppLauncher(args_cli)
@@ -257,6 +257,7 @@ def main():
257257
device=args_cli.device,
258258
num_envs=args_cli.num_envs,
259259
use_fabric=not args_cli.disable_fabric,
260+
overrides=hydra_overrides,
260261
)
261262
# create environment
262263
env = gym.make("IsaacContrib-Lift-Cube-Franka-IK-Abs", cfg=env_cfg)

scripts/environments/state_machine/open_cabinet_sm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
3030
# append AppLauncher cli args
3131
AppLauncher.add_app_launcher_args(parser)
32-
# parse the arguments
33-
args_cli = parser.parse_args()
32+
# parse the arguments, forwarding unrecognized ones as Hydra-style task config overrides
33+
args_cli, hydra_overrides = parser.parse_known_args()
3434

3535
# launch omniverse app
3636
app_launcher = AppLauncher(args_cli)
@@ -276,6 +276,7 @@ def main():
276276
device=args_cli.device,
277277
num_envs=args_cli.num_envs,
278278
use_fabric=not args_cli.disable_fabric,
279+
overrides=hydra_overrides,
279280
)
280281
# create environment
281282
env = gym.make("IsaacContrib-Open-Drawer-Franka-IK-Abs", cfg=env_cfg)

scripts/environments/teleoperation/teleop_replay_agent.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,8 @@
372372
),
373373
)
374374
AppLauncher.add_app_launcher_args(parser)
375-
args_cli = parser.parse_args()
375+
# forward unrecognized args as Hydra-style task config overrides
376+
args_cli, hydra_overrides = parser.parse_known_args()
376377

377378
app_launcher_args = vars(args_cli)
378379
# Enable external camera rendering by default so the replay mimics the production teleop env (perf
@@ -392,6 +393,7 @@
392393
import statistics
393394
import sys
394395
import time
396+
from collections.abc import Sequence
395397
from contextlib import contextmanager
396398
from dataclasses import dataclass, field
397399
from pathlib import Path
@@ -1044,7 +1046,11 @@ def _ensure_replicator_loaded() -> None:
10441046

10451047

10461048
def _prepare_env_cfg(
1047-
task: str, num_envs: int, device: str, apply_rtx_settings: bool = False
1049+
task: str,
1050+
num_envs: int,
1051+
device: str,
1052+
apply_rtx_settings: bool = False,
1053+
overrides: Sequence[str] = (),
10481054
) -> tuple[ManagerBasedRLEnvCfg, object | None]:
10491055
"""Build and tweak an env config suitable for non-interactive replay.
10501056
@@ -1083,12 +1089,13 @@ def _prepare_env_cfg(
10831089
RTX/DLSS global settings applied; a headless replay that renders
10841090
nothing neither needs them nor has the extensions loaded to apply
10851091
them. See :func:`_rtx_rendering_requested`.
1092+
overrides: Hydra-style ``key=value`` overrides forwarded to :func:`parse_env_cfg`.
10861093
10871094
Returns:
10881095
Tuple ``(env_cfg, success_term)``. ``success_term`` is ``None`` when
10891096
the env doesn't define a ``success`` termination term.
10901097
"""
1091-
env_cfg = parse_env_cfg(task, device=device, num_envs=num_envs)
1098+
env_cfg = parse_env_cfg(task, device=device, num_envs=num_envs, overrides=overrides)
10921099
env_cfg.env_name = task.split(":")[-1]
10931100
if not isinstance(env_cfg, ManagerBasedRLEnvCfg):
10941101
raise ValueError(
@@ -1628,6 +1635,7 @@ def main() -> int:
16281635
args_cli.num_envs,
16291636
args_cli.device,
16301637
apply_rtx_settings=_rtx_rendering_requested(args_cli),
1638+
overrides=hydra_overrides,
16311639
)
16321640

16331641
if not hasattr(env_cfg, "isaac_teleop") or env_cfg.isaac_teleop is None:

scripts/imitation_learning/isaaclab_mimic/annotate_demos.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@
5252
external_callback_function = string_to_callable(args_cli.external_callback, separator=".")
5353
remaining_args_env_registration = external_callback_function()
5454

55-
# Error on unrecognized arguments.
56-
unrecognized_args = list_intersection(remaining_args, remaining_args_env_registration)
55+
# Extras of the form "key=value" are Hydra-style task config overrides forwarded to
56+
# parse_env_cfg(); everything else must be consumed by the external callback or is an error.
57+
hydra_overrides = [arg for arg in remaining_args if "=" in arg]
58+
unrecognized_args = list_intersection(
59+
[arg for arg in remaining_args if arg not in hydra_overrides], remaining_args_env_registration
60+
)
5761
if unrecognized_args:
5862
parser.error(f"unrecognized arguments: {' '.join(unrecognized_args)}")
5963

@@ -195,7 +199,7 @@ def main():
195199
if env_name is None:
196200
raise ValueError("Task/env name was not specified nor found in the dataset.")
197201

198-
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1)
202+
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1, overrides=hydra_overrides)
199203

200204
env_cfg.env_name = env_name
201205

scripts/imitation_learning/isaaclab_mimic/consolidated_demo.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@
6262
)
6363
# append AppLauncher cli args
6464
AppLauncher.add_app_launcher_args(parser)
65-
# parse the arguments
66-
args_cli = parser.parse_args()
65+
# parse the arguments, forwarding unrecognized ones as Hydra-style task config overrides
66+
args_cli, hydra_overrides = parser.parse_known_args()
6767

6868
# launch the simulator
6969
app_launcher = AppLauncher(args_cli)
@@ -371,7 +371,7 @@ def main():
371371
raise ValueError("Task/env name was not specified nor found in the dataset.")
372372

373373
# parse configuration
374-
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=num_envs)
374+
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=num_envs, overrides=hydra_overrides)
375375
env_cfg.env_name = env_name
376376

377377
# extract success checking function to invoke manually

scripts/imitation_learning/locomanipulation_sdg/generate_data.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@
123123
)
124124

125125
AppLauncher.add_app_launcher_args(parser)
126-
args_cli = parser.parse_args()
126+
# forward unrecognized args as Hydra-style task config overrides
127+
args_cli, hydra_overrides = parser.parse_known_args()
127128

128129
app_launcher = AppLauncher(args_cli)
129130
simulation_app = app_launcher.app
@@ -993,7 +994,7 @@ def replay(
993994
if env_name is None:
994995
raise ValueError("Task/env name was not specified nor found in the dataset.")
995996

996-
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1)
997+
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1, overrides=hydra_overrides)
997998
env_cfg.sim.device = "cpu"
998999
env_cfg.recorders.dataset_export_dir_path = os.path.dirname(args_cli.output_file)
9991000
env_cfg.recorders.dataset_filename = os.path.basename(args_cli.output_file)

scripts/imitation_learning/locomanipulation_sdg/gr00t/rollout_policy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"Converts env observations/actions to match. Default is 'xyzw'.",
3232
)
3333
AppLauncher.add_app_launcher_args(parser)
34-
args_cli = parser.parse_args()
34+
# forward unrecognized args as Hydra-style task config overrides
35+
args_cli, hydra_overrides = parser.parse_known_args()
3536

3637
app_launcher = AppLauncher(args_cli)
3738
simulation_app = app_launcher.app
@@ -374,7 +375,7 @@ def eval_policy(
374375

375376
policy = Policy(model_path=args_cli.model_path, embodiment_tag=args_cli.embodiment_tag)
376377

377-
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1)
378+
env_cfg = parse_env_cfg(env_name, device=args_cli.device, num_envs=1, overrides=hydra_overrides)
378379
env_cfg.sim.device = args_cli.device
379380
# Drop the SDG output-data recorder term: it pulls env._locomanipulation_sdg_output_data,
380381
# which is only populated by the data-generation state machine, not during policy rollout.

0 commit comments

Comments
 (0)