From 827a989379a1e5672d20b89aaa169d9fd29acb4a Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Wed, 30 Sep 2026 15:54:17 +0200 Subject: [PATCH 1/9] Fall back to the env device for RSL-RL runners without CUDA --- ...ntoiner-fix-rsl-rl-runner-cpu-fallback.rst | 5 ++ .../isaaclab_rl/isaaclab_rl/rsl_rl/utils.py | 11 ++- source/isaaclab_rl/test/test_rsl_rl_runner.py | 69 +++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst create mode 100644 source/isaaclab_rl/test/test_rsl_rl_runner.py diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst new file mode 100644 index 000000000000..1ed4157a25ea --- /dev/null +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst @@ -0,0 +1,5 @@ +Fixed +^^^^^ + +* Fixed RSL-RL training, playback, and export failing on hosts without CUDA, such as macOS, by + building the runner on the environment's device when the agent's default ``cuda:0`` is unavailable. diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py index 8505e2b4a7a4..4204f3761ccd 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py @@ -12,6 +12,7 @@ from dataclasses import MISSING from typing import TYPE_CHECKING, Any +import torch from packaging import version from rsl_rl.runners import DistillationRunner, OnPolicyRunner @@ -60,6 +61,9 @@ def create_rsl_rl_runner( ) -> OnPolicyRunner | DistillationRunner: """Instantiate the RSL-RL runner selected by ``agent_cfg.class_name``. + The runner uses ``agent_cfg.device``, or the environment's device when that is a CUDA device and + CUDA is unavailable (e.g. on macOS), since the agent configuration defaults to ``cuda:0``. + Args: env: Wrapped environment the runner trains or evaluates on. agent_cfg: Runner configuration. @@ -68,10 +72,13 @@ def create_rsl_rl_runner( Raises: ValueError: If the configured runner class is not supported. """ + device = agent_cfg.device + if device.startswith("cuda") and not torch.cuda.is_available(): + device = str(env.device) if agent_cfg.class_name == "OnPolicyRunner": - return OnPolicyRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=agent_cfg.device) + return OnPolicyRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=device) if agent_cfg.class_name == "DistillationRunner": - return DistillationRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=agent_cfg.device) + return DistillationRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=device) raise ValueError(f"Unsupported runner class: {agent_cfg.class_name}") diff --git a/source/isaaclab_rl/test/test_rsl_rl_runner.py b/source/isaaclab_rl/test/test_rsl_rl_runner.py new file mode 100644 index 000000000000..ba01cca82c5c --- /dev/null +++ b/source/isaaclab_rl/test/test_rsl_rl_runner.py @@ -0,0 +1,69 @@ +# 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 + +"""Tests for create_rsl_rl_runner device selection.""" + +import torch +from rsl_rl.env import VecEnv +from tensordict import TensorDict + +from isaaclab_rl.rsl_rl import RslRlMLPModelCfg, RslRlOnPolicyRunnerCfg, RslRlPpoAlgorithmCfg +from isaaclab_rl.rsl_rl.utils import check_rsl_rl_version, create_rsl_rl_runner, handle_deprecated_rsl_rl_cfg + + +class _CpuEnv(VecEnv): + """Minimal CPU environment exposing the attributes the runner reads at construction.""" + + def __init__(self): + self.num_envs = 2 + self.num_actions = 1 + self.max_episode_length = 10 + self.episode_length_buf = torch.zeros(self.num_envs, dtype=torch.long) + self.device = "cpu" + self.cfg = {} + + def get_observations(self) -> TensorDict: + return TensorDict({"policy": torch.zeros(self.num_envs, 3)}, batch_size=[self.num_envs]) + + def step(self, actions): + raise NotImplementedError + + +def _runner_cfg() -> RslRlOnPolicyRunnerCfg: + model = RslRlMLPModelCfg(hidden_dims=[8], activation="elu") + cfg = RslRlOnPolicyRunnerCfg( + num_steps_per_env=4, + max_iterations=1, + save_interval=1, + experiment_name="test", + obs_groups={"actor": ["policy"], "critic": ["policy"]}, + actor=model.replace(stochastic=True, init_noise_std=1.0), + critic=model, + algorithm=RslRlPpoAlgorithmCfg( + num_learning_epochs=1, + num_mini_batches=1, + learning_rate=1e-3, + schedule="fixed", + gamma=0.99, + lam=0.95, + entropy_coef=0.0, + desired_kl=0.01, + max_grad_norm=1.0, + ), + ) + # the entrypoints adapt the configuration to the installed rsl-rl before building the runner + return handle_deprecated_rsl_rl_cfg(cfg, check_rsl_rl_version()) + + +def test_runner_falls_back_to_env_device_without_cuda(monkeypatch): + """The ``cuda:0`` default must not be used on hosts without CUDA, such as macOS.""" + monkeypatch.setattr(torch.cuda, "is_available", lambda: False) + agent_cfg = _runner_cfg() + assert agent_cfg.device == "cuda:0" + + runner = create_rsl_rl_runner(_CpuEnv(), agent_cfg) + + assert runner.device == "cpu" + assert all(p.device.type == "cpu" for p in runner.alg.actor.parameters()) From f2cf68f4afa27ace8da51d67560d3dbabb387c2b Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Wed, 30 Sep 2026 16:08:01 +0200 Subject: [PATCH 2/9] Warn when the RSL-RL runner falls back to the env device --- .../antoiner-fix-rsl-rl-runner-cpu-fallback.rst | 3 ++- source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py | 9 +++++++++ source/isaaclab_rl/test/test_rsl_rl_runner.py | 10 +++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst index 1ed4157a25ea..7286b5d6ba1f 100644 --- a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst @@ -2,4 +2,5 @@ Fixed ^^^^^ * Fixed RSL-RL training, playback, and export failing on hosts without CUDA, such as macOS, by - building the runner on the environment's device when the agent's default ``cuda:0`` is unavailable. + building the runner on the environment's device, with a warning, when the agent's default + ``cuda:0`` is unavailable. diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py index 4204f3761ccd..a8b2b1a14d81 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py @@ -9,6 +9,7 @@ import importlib.metadata import inspect +import logging from dataclasses import MISSING from typing import TYPE_CHECKING, Any @@ -31,6 +32,8 @@ if TYPE_CHECKING: from rsl_rl.env import VecEnv +logger = logging.getLogger(__name__) + RSL_RL_MIN_VERSION = "5.0.1" """Oldest rsl-rl-lib release supported by the entrypoints.""" @@ -74,6 +77,12 @@ def create_rsl_rl_runner( """ device = agent_cfg.device if device.startswith("cuda") and not torch.cuda.is_available(): + logger.warning( + "Agent device '%s' is unavailable because CUDA is not available; using the environment device '%s'" + " instead.", + device, + env.device, + ) device = str(env.device) if agent_cfg.class_name == "OnPolicyRunner": return OnPolicyRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=device) diff --git a/source/isaaclab_rl/test/test_rsl_rl_runner.py b/source/isaaclab_rl/test/test_rsl_rl_runner.py index ba01cca82c5c..f6d5b9c3c715 100644 --- a/source/isaaclab_rl/test/test_rsl_rl_runner.py +++ b/source/isaaclab_rl/test/test_rsl_rl_runner.py @@ -5,6 +5,8 @@ """Tests for create_rsl_rl_runner device selection.""" +import logging + import torch from rsl_rl.env import VecEnv from tensordict import TensorDict @@ -57,13 +59,15 @@ def _runner_cfg() -> RslRlOnPolicyRunnerCfg: return handle_deprecated_rsl_rl_cfg(cfg, check_rsl_rl_version()) -def test_runner_falls_back_to_env_device_without_cuda(monkeypatch): - """The ``cuda:0`` default must not be used on hosts without CUDA, such as macOS.""" +def test_runner_falls_back_to_env_device_without_cuda(monkeypatch, caplog): + """The ``cuda:0`` default must not be used on hosts without CUDA, such as macOS, and the switch is reported.""" monkeypatch.setattr(torch.cuda, "is_available", lambda: False) agent_cfg = _runner_cfg() assert agent_cfg.device == "cuda:0" - runner = create_rsl_rl_runner(_CpuEnv(), agent_cfg) + with caplog.at_level(logging.WARNING, logger="isaaclab_rl.rsl_rl.utils"): + runner = create_rsl_rl_runner(_CpuEnv(), agent_cfg) assert runner.device == "cpu" assert all(p.device.type == "cpu" for p in runner.alg.actor.parameters()) + assert any("'cuda:0' is unavailable" in record.getMessage() for record in caplog.records) From 2284297965141af0e3f0d91f7d6905e146a579e9 Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Wed, 30 Sep 2026 18:52:49 +0200 Subject: [PATCH 3/9] Drop the runner device test and docstring note --- .../isaaclab_rl/isaaclab_rl/rsl_rl/utils.py | 3 - source/isaaclab_rl/test/test_rsl_rl_runner.py | 73 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 source/isaaclab_rl/test/test_rsl_rl_runner.py diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py index a8b2b1a14d81..0f853c68bec7 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py @@ -64,9 +64,6 @@ def create_rsl_rl_runner( ) -> OnPolicyRunner | DistillationRunner: """Instantiate the RSL-RL runner selected by ``agent_cfg.class_name``. - The runner uses ``agent_cfg.device``, or the environment's device when that is a CUDA device and - CUDA is unavailable (e.g. on macOS), since the agent configuration defaults to ``cuda:0``. - Args: env: Wrapped environment the runner trains or evaluates on. agent_cfg: Runner configuration. diff --git a/source/isaaclab_rl/test/test_rsl_rl_runner.py b/source/isaaclab_rl/test/test_rsl_rl_runner.py deleted file mode 100644 index f6d5b9c3c715..000000000000 --- a/source/isaaclab_rl/test/test_rsl_rl_runner.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 - -"""Tests for create_rsl_rl_runner device selection.""" - -import logging - -import torch -from rsl_rl.env import VecEnv -from tensordict import TensorDict - -from isaaclab_rl.rsl_rl import RslRlMLPModelCfg, RslRlOnPolicyRunnerCfg, RslRlPpoAlgorithmCfg -from isaaclab_rl.rsl_rl.utils import check_rsl_rl_version, create_rsl_rl_runner, handle_deprecated_rsl_rl_cfg - - -class _CpuEnv(VecEnv): - """Minimal CPU environment exposing the attributes the runner reads at construction.""" - - def __init__(self): - self.num_envs = 2 - self.num_actions = 1 - self.max_episode_length = 10 - self.episode_length_buf = torch.zeros(self.num_envs, dtype=torch.long) - self.device = "cpu" - self.cfg = {} - - def get_observations(self) -> TensorDict: - return TensorDict({"policy": torch.zeros(self.num_envs, 3)}, batch_size=[self.num_envs]) - - def step(self, actions): - raise NotImplementedError - - -def _runner_cfg() -> RslRlOnPolicyRunnerCfg: - model = RslRlMLPModelCfg(hidden_dims=[8], activation="elu") - cfg = RslRlOnPolicyRunnerCfg( - num_steps_per_env=4, - max_iterations=1, - save_interval=1, - experiment_name="test", - obs_groups={"actor": ["policy"], "critic": ["policy"]}, - actor=model.replace(stochastic=True, init_noise_std=1.0), - critic=model, - algorithm=RslRlPpoAlgorithmCfg( - num_learning_epochs=1, - num_mini_batches=1, - learning_rate=1e-3, - schedule="fixed", - gamma=0.99, - lam=0.95, - entropy_coef=0.0, - desired_kl=0.01, - max_grad_norm=1.0, - ), - ) - # the entrypoints adapt the configuration to the installed rsl-rl before building the runner - return handle_deprecated_rsl_rl_cfg(cfg, check_rsl_rl_version()) - - -def test_runner_falls_back_to_env_device_without_cuda(monkeypatch, caplog): - """The ``cuda:0`` default must not be used on hosts without CUDA, such as macOS, and the switch is reported.""" - monkeypatch.setattr(torch.cuda, "is_available", lambda: False) - agent_cfg = _runner_cfg() - assert agent_cfg.device == "cuda:0" - - with caplog.at_level(logging.WARNING, logger="isaaclab_rl.rsl_rl.utils"): - runner = create_rsl_rl_runner(_CpuEnv(), agent_cfg) - - assert runner.device == "cpu" - assert all(p.device.type == "cpu" for p in runner.alg.actor.parameters()) - assert any("'cuda:0' is unavailable" in record.getMessage() for record in caplog.records) From 566ac34fe7ed0d7668aed993e14f929d83892be7 Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Thu, 1 Oct 2026 17:51:46 +0200 Subject: [PATCH 4/9] Run RL agents on the resolved simulation device and fall back to CPU without CUDA --- ...r-fix-rsl-rl-runner-cpu-fallback.changed.rst | 2 ++ ...ner-fix-rsl-rl-runner-cpu-fallback.fixed.rst | 2 ++ source/isaaclab/isaaclab/app/sim_launcher.py | 6 +++++- .../rl_games/benchmark_play_rl_games.py | 2 ++ .../rl_games/benchmark_train_rl_games.py | 8 ++++---- .../backends/rsl_rl/benchmark_play_rsl_rl.py | 2 ++ .../backends/rsl_rl/benchmark_train_rsl_rl.py | 7 ++++--- .../backends/sb3/benchmark_play_sb3.py | 2 +- .../backends/sb3/benchmark_train_sb3.py | 2 ++ ...r-fix-rsl-rl-runner-cpu-fallback.changed.rst | 6 ++++++ ...ntoiner-fix-rsl-rl-runner-cpu-fallback.major | 0 .../antoiner-fix-rsl-rl-runner-cpu-fallback.rst | 6 ------ .../entrypoints/backends/export_rl_games.py | 2 ++ .../entrypoints/backends/export_rsl_rl.py | 2 ++ .../entrypoints/backends/play_rl_games.py | 2 ++ .../entrypoints/backends/play_rsl_rl.py | 2 ++ .../entrypoints/backends/play_sb3.py | 2 +- .../entrypoints/backends/train_rl_games.py | 4 ++-- .../entrypoints/backends/train_rsl_rl.py | 3 ++- .../entrypoints/backends/train_sb3.py | 2 ++ source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py | 17 ++--------------- 21 files changed, 47 insertions(+), 34 deletions(-) create mode 100644 source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst create mode 100644 source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst create mode 100644 source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst create mode 100644 source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.major delete mode 100644 source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst diff --git a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst new file mode 100644 index 000000000000..35d6c2474aa2 --- /dev/null +++ b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst @@ -0,0 +1,2 @@ +* Changed the RSL-RL, RL-Games, and Stable-Baselines3 benchmark workflows to run the agent on the device + :func:`~isaaclab.app.launch_simulation` resolved for the simulation, matching the training workflows. diff --git a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst new file mode 100644 index 000000000000..f84d060eeb84 --- /dev/null +++ b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst @@ -0,0 +1,2 @@ +* Fixed runs failing on hosts without CUDA, such as macOS, when the resolved device was a CUDA device. + :func:`~isaaclab.app.launch_simulation` now falls back to ``cpu`` with a warning. diff --git a/source/isaaclab/isaaclab/app/sim_launcher.py b/source/isaaclab/isaaclab/app/sim_launcher.py index e0d47fdd47ed..cf6dab4811d4 100644 --- a/source/isaaclab/isaaclab/app/sim_launcher.py +++ b/source/isaaclab/isaaclab/app/sim_launcher.py @@ -508,11 +508,15 @@ def _resolve_device(sim_cfg, args: dict, launchers: list[SimulationLauncher]) -> """Write the run's device to ``sim_cfg.device`` and the ``device`` launcher argument, once. Starts from the ``device`` launcher argument resolved before launch; a started runtime may refine it - (e.g. XR selects the CPU), and a bare ``"cuda"`` is pinned to the physics GPU index. + (e.g. XR selects the CPU), a CUDA device falls back to the CPU when CUDA is unavailable (e.g. on macOS), + and a bare ``"cuda"`` is pinned to the physics GPU index. """ device = args.get("device") for launcher in launchers: device = launcher.device or device + if device is not None and device.startswith("cuda") and not torch.cuda.is_available(): + logger.warning("Device '%s' is unavailable because CUDA is not available; using 'cpu' instead.", device) + device = "cpu" if device == "cuda": cuda_device = get_settings_manager().get("/physics/cudaDevice") device = f"cuda:{max(0, int(cuda_device) if cuda_device is not None else 0)}" diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py index d7fa1cdbf81f..4467c5ddc73e 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py @@ -186,6 +186,8 @@ def run(argv: list[str]) -> BenchmarkResult: ) # Read rl_games device / clipping config. + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg["params"]["config"]["device"] = agent_cfg["params"]["config"]["device_name"] = env_cfg.sim.device rl_device = agent_cfg["params"]["config"]["device"] clip_obs = agent_cfg["params"]["env"].get("clip_observations", math.inf) clip_actions = agent_cfg["params"]["env"].get("clip_actions", math.inf) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py index e004f0ecac4b..f3f334c8e2fa 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py @@ -198,12 +198,12 @@ def run(argv: list[str]) -> BenchmarkResult | None: if args_cli.max_iterations is not None: agent_cfg["params"]["config"]["max_epochs"] = args_cli.max_iterations + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg["params"]["config"]["device"] = agent_cfg["params"]["config"]["device_name"] = env_cfg.sim.device if distributed.enabled: - # Mirror the regular training entrypoint: the launcher pinned this rank to its own - # device, and offsetting the seed by the rank decorrelates exploration across ranks. + # Mirror the regular training entrypoint: offsetting the seed by the rank decorrelates + # exploration across ranks. agent_cfg["params"]["seed"] += distributed.rank - agent_cfg["params"]["config"]["device"] = env_cfg.sim.device - agent_cfg["params"]["config"]["device_name"] = env_cfg.sim.device agent_cfg["params"]["config"]["multi_gpu"] = True env_cfg.seed = agent_cfg["params"]["seed"] horizon_length = agent_cfg["params"]["config"].get("horizon_length", 16) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py index 78a83cf463da..7fde3dabf711 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py @@ -145,6 +145,8 @@ def run(argv: list[str]) -> BenchmarkResult: installed_rsl_rl = metadata.version("rsl-rl-lib") agent_cfg = handle_deprecated_rsl_rl_cfg(agent_cfg, installed_rsl_rl) + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg.device = env_cfg.sim.device log_root_path = os.path.abspath(os.path.join("logs", "rsl_rl", agent_cfg.experiment_name)) if args.checkpoint in common.CHECKPOINT_SELECTORS: diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py index 876d54c28368..760f8e3a7b5f 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py @@ -191,10 +191,11 @@ def run(argv: list[str]) -> BenchmarkResult | None: agent_cfg = handle_deprecated_rsl_rl_cfg(agent_cfg, installed_rsl_rl) env_cfg.seed = agent_cfg.seed + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg.device = env_cfg.sim.device if distributed.enabled: - # Mirror the regular training entrypoint: the launcher pinned this rank to its own - # device, and offsetting the seed by the rank decorrelates exploration across ranks. - agent_cfg.device = env_cfg.sim.device + # Mirror the regular training entrypoint: offsetting the seed by the rank decorrelates + # exploration across ranks. agent_cfg.seed += distributed.rank env_cfg.seed = agent_cfg.seed reported_num_envs, _ = distributed.global_work(env_cfg.scene.num_envs, agent_cfg.num_steps_per_env) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py index ecea49d98402..bbb6de5afd25 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py @@ -213,7 +213,7 @@ def run(argv: list[str]) -> BenchmarkResult: ) # Load the trained policy. - agent = PPO.load(resume_path, env, print_system_info=True) + agent = PPO.load(resume_path, env, device=env_cfg.sim.device, print_system_info=True) def policy(obs): """Map an observation batch to a deterministic action batch via the sb3 agent. diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py index dabc72fc394b..4065ad073996 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py @@ -239,6 +239,8 @@ def run(argv: list[str]) -> BenchmarkResult: agent_cfg["seed"] = args_cli.seed if args_cli.seed is not None else agent_cfg.get("seed", 0) env_cfg.seed = agent_cfg["seed"] + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg["device"] = env_cfg.sim.device # Convert the iteration override to SB3 total timesteps. n_steps_cfg = agent_cfg.get("n_steps", 2048) diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst new file mode 100644 index 000000000000..150adf6f6eef --- /dev/null +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst @@ -0,0 +1,6 @@ +* **Breaking:** Changed the RSL-RL, RL-Games, and Stable-Baselines3 train, play, and export workflows to run the + agent on the device :func:`~isaaclab.app.launch_simulation` resolved for the simulation, as skrl and TorchRL + already did. Outside distributed runs they used the agent configuration's device, ``cuda:0`` by default, so + ``--device cpu`` trained on ``cuda:0``, ``--device cuda:1`` trained on GPU 0, and hosts without CUDA, such as + macOS, failed. Pass ``--device`` to choose where both the simulation and the agent run; the agent + configuration's ``device`` is now overwritten by these workflows. diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.major b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.major new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst deleted file mode 100644 index 7286b5d6ba1f..000000000000 --- a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.rst +++ /dev/null @@ -1,6 +0,0 @@ -Fixed -^^^^^ - -* Fixed RSL-RL training, playback, and export failing on hosts without CUDA, such as macOS, by - building the runner on the environment's device, with a warning, when the agent's default - ``cuda:0`` is unavailable. diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py index 48ac9f627876..f710f7343b87 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py @@ -103,6 +103,8 @@ def export_rl_games_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: from leapp import annotate params = agent_cfg["params"] + # the agent runs on the device launch_simulation resolved for the simulation + params["config"]["device"] = params["config"]["device_name"] = env_cfg.sim.device env_cfg.scene.num_envs = 1 env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py index c97db09d2d2a..c6cb9d40abe4 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py @@ -140,6 +140,8 @@ def export_rsl_rl_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: R if args_cli.experiment_name is not None: agent_cfg.experiment_name = args_cli.experiment_name agent_cfg = handle_deprecated_rsl_rl_cfg(agent_cfg, installed_version) + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg.device = env_cfg.sim.device env_cfg.scene.num_envs = 1 # certain randomizations occur in the environment initialization so we set the seed here diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py index f1b33a7ab270..f7fe9c566f95 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py @@ -110,6 +110,8 @@ def run(argv: list[str]) -> None: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="play") apply_env_overrides(args_cli, env_cfg) params = agent_cfg["params"] + # the agent runs on the device launch_simulation resolved for the simulation + params["config"]["device"] = params["config"]["device_name"] = env_cfg.sim.device args_cli.seed = resolve_seed(args_cli.seed) if args_cli.seed is not None: params["seed"] = args_cli.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py index efeca2b79e9b..2ccb79760c70 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py @@ -111,6 +111,8 @@ def run(argv: list[str]) -> None: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="play") agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) agent_cfg = handle_deprecated_rsl_rl_cfg(agent_cfg, installed_version) + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg.device = env_cfg.sim.device apply_env_overrides(args_cli, env_cfg) # certain randomizations occur in the environment initialization so we set the seed here env_cfg.seed = agent_cfg.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py index aec7d0440aab..e07a68975f85 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py @@ -144,7 +144,7 @@ def run(argv: list[str]) -> None: ) print(f"Loading checkpoint from: {checkpoint_path}") - agent = PPO.load(checkpoint_path, env, print_system_info=True) + agent = PPO.load(checkpoint_path, env, device=env_cfg.sim.device, print_system_info=True) # configure_seed must run after PPO.load so torch determinism does not disturb SB3's initialization if args_cli.deterministic: configure_seed(env_cfg.seed, torch_deterministic=True) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py index c0263b6748bc..00d34bd4afa5 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py @@ -118,11 +118,11 @@ def run(argv: list[str]) -> None: params["seed"] = args_cli.seed if args_cli.max_iterations is not None: config["max_epochs"] = args_cli.max_iterations + # the agent runs on the device launch_simulation resolved for the simulation + config["device"] = config["device_name"] = env_cfg.sim.device rank = int(os.getenv("RANK", "0")) if args_cli.distributed else None if rank is not None: params["seed"] += rank - config["device"] = env_cfg.sim.device - config["device_name"] = env_cfg.sim.device config["multi_gpu"] = True env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py index beeab8d41679..6a0ec3acf174 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py @@ -128,9 +128,10 @@ def _run(args_cli: argparse.Namespace) -> None: apply_env_overrides(args_cli, env_cfg) if args_cli.max_iterations is not None: agent_cfg.max_iterations = args_cli.max_iterations + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg.device = env_cfg.sim.device rank = int(os.getenv("RANK", "0")) if args_cli.distributed else None if rank is not None: - agent_cfg.device = env_cfg.sim.device agent_cfg.seed += rank env_cfg.seed = agent_cfg.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py index 92daf0bff64f..491b667f9bd2 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py @@ -118,6 +118,8 @@ def run(argv: list[str]) -> None: if args_cli.max_iterations is not None: agent_cfg["n_timesteps"] = args_cli.max_iterations * agent_cfg["n_steps"] * env_cfg.scene.num_envs env_cfg.seed = agent_cfg["seed"] + # the agent runs on the device launch_simulation resolved for the simulation + agent_cfg["device"] = env_cfg.sim.device log_root_path = os.path.abspath(os.path.join("logs", "sb3", args_cli.task)) print(f"[INFO] Logging experiment in directory: {log_root_path}") diff --git a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py index 0f853c68bec7..8505e2b4a7a4 100644 --- a/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py +++ b/source/isaaclab_rl/isaaclab_rl/rsl_rl/utils.py @@ -9,11 +9,9 @@ import importlib.metadata import inspect -import logging from dataclasses import MISSING from typing import TYPE_CHECKING, Any -import torch from packaging import version from rsl_rl.runners import DistillationRunner, OnPolicyRunner @@ -32,8 +30,6 @@ if TYPE_CHECKING: from rsl_rl.env import VecEnv -logger = logging.getLogger(__name__) - RSL_RL_MIN_VERSION = "5.0.1" """Oldest rsl-rl-lib release supported by the entrypoints.""" @@ -72,19 +68,10 @@ def create_rsl_rl_runner( Raises: ValueError: If the configured runner class is not supported. """ - device = agent_cfg.device - if device.startswith("cuda") and not torch.cuda.is_available(): - logger.warning( - "Agent device '%s' is unavailable because CUDA is not available; using the environment device '%s'" - " instead.", - device, - env.device, - ) - device = str(env.device) if agent_cfg.class_name == "OnPolicyRunner": - return OnPolicyRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=device) + return OnPolicyRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=agent_cfg.device) if agent_cfg.class_name == "DistillationRunner": - return DistillationRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=device) + return DistillationRunner(env, to_dict(agent_cfg), log_dir=log_dir, device=agent_cfg.device) raise ValueError(f"Unsupported runner class: {agent_cfg.class_name}") From 0c81e675ec59fe8a76d642f9e3b0566a4804231a Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Mon, 5 Oct 2026 09:27:06 +0200 Subject: [PATCH 5/9] Set the agent device in each RL library's shared config setup and raise without CUDA --- ...fix-rsl-rl-runner-cpu-fallback.changed.rst | 4 +- ...r-fix-rsl-rl-runner-cpu-fallback.fixed.rst | 4 +- source/isaaclab/isaaclab/app/sim_launcher.py | 9 ++-- .../rl_games/benchmark_play_rl_games.py | 6 +-- .../rl_games/benchmark_train_rl_games.py | 17 +------- .../backends/rsl_rl/benchmark_play_rsl_rl.py | 7 +--- .../backends/rsl_rl/benchmark_train_rsl_rl.py | 2 - .../backends/sb3/benchmark_play_sb3.py | 3 +- .../backends/sb3/benchmark_train_sb3.py | 5 +-- ...fix-rsl-rl-runner-cpu-fallback.changed.rst | 8 ++-- .../entrypoints/backends/cli_args_rl_games.py | 41 +++++++++++++++++++ .../entrypoints/backends/cli_args_rsl_rl.py | 14 ++++--- .../entrypoints/backends/cli_args_sb3.py | 32 +++++++++++++++ .../entrypoints/backends/export_rl_games.py | 4 +- .../entrypoints/backends/export_rsl_rl.py | 12 ++---- .../entrypoints/backends/play_rl_games.py | 8 +--- .../entrypoints/backends/play_rsl_rl.py | 2 - .../entrypoints/backends/play_sb3.py | 6 +-- .../entrypoints/backends/train_rl_games.py | 13 +----- .../entrypoints/backends/train_rsl_rl.py | 2 - .../entrypoints/backends/train_sb3.py | 8 +--- .../test/test_entrypoints_common.py | 4 +- 22 files changed, 121 insertions(+), 90 deletions(-) create mode 100644 source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py create mode 100644 source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py diff --git a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst index 35d6c2474aa2..a8fe1c93da74 100644 --- a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst +++ b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst @@ -1,2 +1,2 @@ -* Changed the RSL-RL, RL-Games, and Stable-Baselines3 benchmark workflows to run the agent on the device - :func:`~isaaclab.app.launch_simulation` resolved for the simulation, matching the training workflows. +* Changed the RSL-RL, RL-Games, and Stable-Baselines3 benchmark workflows to set up their agent configuration with + the same shared overrides as the training and play workflows, including the device the simulation runs on. diff --git a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst index f84d060eeb84..82d1534f9b60 100644 --- a/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst +++ b/source/isaaclab/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.fixed.rst @@ -1,2 +1,2 @@ -* Fixed runs failing on hosts without CUDA, such as macOS, when the resolved device was a CUDA device. - :func:`~isaaclab.app.launch_simulation` now falls back to ``cpu`` with a warning. +* Fixed runs on hosts without CUDA, such as macOS, failing inside PyTorch when the resolved device was a CUDA + device. :func:`~isaaclab.app.launch_simulation` now raises a clear error that asks for ``--device cpu``. diff --git a/source/isaaclab/isaaclab/app/sim_launcher.py b/source/isaaclab/isaaclab/app/sim_launcher.py index da00f5fc2380..64bcd84c8249 100644 --- a/source/isaaclab/isaaclab/app/sim_launcher.py +++ b/source/isaaclab/isaaclab/app/sim_launcher.py @@ -503,15 +503,16 @@ def _resolve_device(sim_cfg, args: dict, launchers: list[SimulationLauncher]) -> """Write the run's device to ``sim_cfg.device`` and the ``device`` launcher argument, once. Starts from the ``device`` launcher argument resolved before launch; a started runtime may refine it - (e.g. XR selects the CPU), a CUDA device falls back to the CPU when CUDA is unavailable (e.g. on macOS), - and a bare ``"cuda"`` is pinned to the physics GPU index. + (e.g. XR selects the CPU), and a bare ``"cuda"`` is pinned to the physics GPU index. + + Raises: + RuntimeError: When the device is a CUDA device and CUDA is unavailable (e.g. on macOS). """ device = args.get("device") for launcher in launchers: device = launcher.device or device if device is not None and device.startswith("cuda") and not torch.cuda.is_available(): - logger.warning("Device '%s' is unavailable because CUDA is not available; using 'cpu' instead.", device) - device = "cpu" + raise RuntimeError(f"Device '{device}' requires CUDA, which is not available; pass --device cpu.") if device == "cuda": cuda_device = get_settings_manager().get("/physics/cudaDevice") device = f"cuda:{max(0, int(cuda_device) if cuda_device is not None else 0)}" diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py index 3fd9b690cf10..e928c9ec9fde 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py @@ -113,6 +113,7 @@ def run(argv: list[str]) -> BenchmarkResult: from isaaclab.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, BenchmarkResult, builders, capture, stepping from isaaclab.benchmark.schema import StartupTime + from isaaclab_rl.entrypoints.backends import cli_args_rl_games as cli_args from isaaclab_rl.rl_games import RlGamesGpuEnv, RlGamesVecEnvWrapper # Importing the task packages registers their gym environments so the @@ -139,8 +140,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - if args_cli.seed is not None: - agent_cfg["params"]["seed"] = args_cli.seed + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["params"]["seed"] config_name = agent_cfg["params"]["config"]["name"] @@ -185,8 +185,6 @@ def run(argv: list[str]) -> BenchmarkResult: ) # Read rl_games device / clipping config. - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg["params"]["config"]["device"] = agent_cfg["params"]["config"]["device_name"] = env_cfg.sim.device rl_device = agent_cfg["params"]["config"]["device"] clip_obs = agent_cfg["params"]["env"].get("clip_observations", math.inf) clip_actions = agent_cfg["params"]["env"].get("clip_actions", math.inf) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py index 8bd9f6289560..1582cfd45887 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py @@ -129,7 +129,6 @@ def run(argv: list[str]) -> BenchmarkResult | None: import contextlib import math import os - import random from datetime import datetime from rl_games.common import env_configurations, vecenv @@ -150,6 +149,7 @@ def run(argv: list[str]) -> BenchmarkResult | None: from isaaclab.benchmark.metrics import RL_LIBRARY_DESCRIPTORS, parse_tf_logs from isaaclab.benchmark.schema import StartupTime + from isaaclab_rl.entrypoints.backends import cli_args_rl_games as cli_args from isaaclab_rl.rl_games import RlGamesGpuEnv, RlGamesVecEnvWrapper import isaaclab_tasks # noqa: F401 @@ -194,20 +194,7 @@ def run(argv: list[str]) -> BenchmarkResult | None: apply_env_overrides(args_cli, env_cfg) - if args_cli.seed == -1: - args_cli.seed = random.randint(0, 10000) - agent_cfg["params"]["seed"] = args_cli.seed if args_cli.seed is not None else agent_cfg["params"]["seed"] - - if args_cli.max_iterations is not None: - agent_cfg["params"]["config"]["max_epochs"] = args_cli.max_iterations - - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg["params"]["config"]["device"] = agent_cfg["params"]["config"]["device_name"] = env_cfg.sim.device - if distributed.enabled: - # Mirror the regular training entrypoint: offsetting the seed by the rank decorrelates - # exploration across ranks. - agent_cfg["params"]["seed"] += distributed.rank - agent_cfg["params"]["config"]["multi_gpu"] = True + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["params"]["seed"] horizon_length = agent_cfg["params"]["config"].get("horizon_length", 16) reported_num_envs, _ = distributed.global_work(env_cfg.scene.num_envs, horizon_length) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py index 2b001635805d..c5a5a2b53a25 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py @@ -111,6 +111,7 @@ def run(argv: list[str]) -> BenchmarkResult: from isaaclab.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, BenchmarkResult, builders, capture, stepping from isaaclab.benchmark.schema import StartupTime + from isaaclab_rl.entrypoints.backends import cli_args_rsl_rl as cli_args from isaaclab_rl.rsl_rl import RslRlVecEnvWrapper # Importing the task packages registers their gym environments so the @@ -137,13 +138,9 @@ def run(argv: list[str]) -> BenchmarkResult: if args.num_envs is not None: env_cfg.scene.num_envs = args.num_envs - if args.seed is not None: - agent_cfg.seed = args.seed + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args) env_cfg.seed = agent_cfg.seed - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg.device = env_cfg.sim.device - log_root_path = os.path.abspath(os.path.join("logs", "rsl_rl", agent_cfg.experiment_name)) if args.checkpoint in common.CHECKPOINT_SELECTORS: resume_path = common.resolve_checkpoint_selector( diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py index 59a4d884c64f..dfd5f2af1e61 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py @@ -191,8 +191,6 @@ def run(argv: list[str]) -> BenchmarkResult | None: ) env_cfg.seed = agent_cfg.seed - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg.device = env_cfg.sim.device if distributed.enabled: # Mirror the regular training entrypoint: offsetting the seed by the rank decorrelates # exploration across ranks. diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py index 4ffd3550e233..8d4671cc7415 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py @@ -116,6 +116,7 @@ def run(argv: list[str]) -> BenchmarkResult: from isaaclab.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, BenchmarkResult, builders, capture, stepping from isaaclab.benchmark.schema import StartupTime + from isaaclab_rl.entrypoints.backends import cli_args_sb3 as cli_args from isaaclab_rl.sb3 import Sb3VecEnvWrapper, process_sb3_cfg # Importing the task packages registers their gym environments so the @@ -142,7 +143,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - agent_cfg["seed"] = args_cli.seed if args_cli.seed is not None else agent_cfg.get("seed", 0) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", args_cli.task)) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py index ea59d7819b2a..7b23c0cfb17f 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py @@ -204,6 +204,7 @@ def run(argv: list[str]) -> BenchmarkResult: from isaaclab.benchmark.metrics import RL_LIBRARY_DESCRIPTORS, parse_tf_logs from isaaclab.benchmark.schema import StartupTime + from isaaclab_rl.entrypoints.backends import cli_args_sb3 as cli_args from isaaclab_rl.sb3 import Sb3VecEnvWrapper, process_sb3_cfg import isaaclab_tasks # noqa: F401 @@ -240,10 +241,8 @@ def run(argv: list[str]) -> BenchmarkResult: apply_env_overrides(args_cli, env_cfg) - agent_cfg["seed"] = args_cli.seed if args_cli.seed is not None else agent_cfg.get("seed", 0) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg["device"] = env_cfg.sim.device # Convert the iteration override to SB3 total timesteps. n_steps_cfg = agent_cfg.get("n_steps", 2048) diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst index 150adf6f6eef..dec9ccf74c4c 100644 --- a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst @@ -1,6 +1,8 @@ * **Breaking:** Changed the RSL-RL, RL-Games, and Stable-Baselines3 train, play, and export workflows to run the agent on the device :func:`~isaaclab.app.launch_simulation` resolved for the simulation, as skrl and TorchRL already did. Outside distributed runs they used the agent configuration's device, ``cuda:0`` by default, so - ``--device cpu`` trained on ``cuda:0``, ``--device cuda:1`` trained on GPU 0, and hosts without CUDA, such as - macOS, failed. Pass ``--device`` to choose where both the simulation and the agent run; the agent - configuration's ``device`` is now overwritten by these workflows. + ``--device cpu`` trained on ``cuda:0`` and ``--device cuda:1`` trained on GPU 0. Pass ``--device`` to choose + where both the simulation and the agent run; the agent configuration's ``device`` is now overwritten. +* Changed each library's agent configuration overrides to live in one shared function that every train, play, + export, and benchmark workflow calls: ``update_rsl_rl_cfg``, and the new ``update_rl_games_cfg`` and + ``update_sb3_cfg``. diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py new file mode 100644 index 000000000000..a85d04135d32 --- /dev/null +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py @@ -0,0 +1,41 @@ +# 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 + +"""Agent configuration overrides shared by the RL-Games entrypoints.""" + +from __future__ import annotations + +import argparse +import os + +from ..common import resolve_seed + + +def update_rl_games_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: + """Override an RL-Games agent configuration with the command-line arguments. + + Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on + the simulation's device. + + Args: + agent_cfg: The configuration for RL-Games agent. + args_cli: The command line arguments. + + Returns: + The updated RL-Games agent configuration. + """ + params = agent_cfg["params"] + config = params["config"] + config["device"] = config["device_name"] = args_cli.device + if getattr(args_cli, "seed", None) is not None: + args_cli.seed = resolve_seed(args_cli.seed) + params["seed"] = args_cli.seed + if getattr(args_cli, "max_iterations", None) is not None: + config["max_epochs"] = args_cli.max_iterations + if getattr(args_cli, "distributed", False): + # offsetting the seed by the rank decorrelates exploration across ranks + params["seed"] += int(os.getenv("RANK", "0")) + config["multi_gpu"] = True + return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py index 0c953c4b44ca..484858cff748 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py @@ -87,6 +87,9 @@ def parse_rsl_rl_cfg(task_name: str, args_cli: argparse.Namespace) -> RslRlBaseR def update_rsl_rl_cfg(agent_cfg: RslRlBaseRunnerCfg, args_cli: argparse.Namespace) -> RslRlBaseRunnerCfg: """Override an RSL-RL agent configuration with the command-line arguments. + Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on + the simulation's device. + Args: agent_cfg: The configuration for RSL-RL agent. args_cli: The command line arguments. @@ -94,18 +97,19 @@ def update_rsl_rl_cfg(agent_cfg: RslRlBaseRunnerCfg, args_cli: argparse.Namespac Returns: The updated RSL-RL agent configuration. """ + agent_cfg.device = args_cli.device if getattr(args_cli, "seed", None) is not None: args_cli.seed = resolve_seed(args_cli.seed) agent_cfg.seed = args_cli.seed - if args_cli.checkpoint is not None: + if getattr(args_cli, "checkpoint", None) is not None: agent_cfg.load_checkpoint = args_cli.checkpoint - if args_cli.experiment_name is not None: + if getattr(args_cli, "experiment_name", None) is not None: agent_cfg.experiment_name = args_cli.experiment_name - if args_cli.run_name is not None: + if getattr(args_cli, "run_name", None) is not None: agent_cfg.run_name = args_cli.run_name - if args_cli.logger is not None: + if getattr(args_cli, "logger", None) is not None: agent_cfg.logger = args_cli.logger - if agent_cfg.logger in {"wandb", "neptune"} and args_cli.log_project_name: + if agent_cfg.logger in {"wandb", "neptune"} and getattr(args_cli, "log_project_name", None): agent_cfg.wandb_project = args_cli.log_project_name agent_cfg.neptune_project = args_cli.log_project_name return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py new file mode 100644 index 000000000000..71f3d2cb8cf0 --- /dev/null +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py @@ -0,0 +1,32 @@ +# 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 + +"""Agent configuration overrides shared by the Stable-Baselines3 entrypoints.""" + +from __future__ import annotations + +import argparse + +from ..common import resolve_seed + + +def update_sb3_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: + """Override a Stable-Baselines3 agent configuration with the command-line arguments. + + Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on + the simulation's device. Loading a checkpoint still needs that device passed to ``PPO.load``. + + Args: + agent_cfg: The configuration for Stable-Baselines3 agent. + args_cli: The command line arguments. + + Returns: + The updated Stable-Baselines3 agent configuration. + """ + agent_cfg["device"] = args_cli.device + if getattr(args_cli, "seed", None) is not None: + args_cli.seed = resolve_seed(args_cli.seed) + agent_cfg["seed"] = args_cli.seed + return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py index e1cbb305ee5b..7601ceb1be0f 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py @@ -32,6 +32,7 @@ from ...rl_games import RlGamesVecEnvWrapper, register_rl_games_env from ..common import resolve_published_checkpoint +from . import cli_args_rl_games as cli_args from .export_common import ( add_common_export_args, finalize_export_args, @@ -105,9 +106,8 @@ def export_rl_games_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: # initialized the selected backend from leapp import annotate + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] - # the agent runs on the device launch_simulation resolved for the simulation - params["config"]["device"] = params["config"]["device_name"] = env_cfg.sim.device env_cfg.scene.num_envs = 1 env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py index 17ae66f527f4..e1fcfd329bb7 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py @@ -30,7 +30,8 @@ import isaaclab_tasks # noqa: F401 from ...rsl_rl import RslRlBaseRunnerCfg, RslRlVecEnvWrapper -from ..common import resolve_published_checkpoint, resolve_seed +from ..common import resolve_published_checkpoint +from . import cli_args_rsl_rl as cli_args from .export_common import ( add_common_export_args, finalize_export_args, @@ -131,14 +132,7 @@ def export_rsl_rl_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: R # the LEAPP runtime loads simulation modules, so import it only after the launch from leapp import annotate - if args_cli.seed is not None: - agent_cfg.seed = resolve_seed(args_cli.seed) - if args_cli.checkpoint is not None: - agent_cfg.load_checkpoint = args_cli.checkpoint - if args_cli.experiment_name is not None: - agent_cfg.experiment_name = args_cli.experiment_name - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg.device = env_cfg.sim.device + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) env_cfg.scene.num_envs = 1 # certain randomizations occur in the environment initialization so we set the seed here diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py index 3ca493db61e7..b4376e0356e1 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py @@ -38,12 +38,12 @@ pre_launch_video_config, resolve_checkpoint_selector, resolve_published_checkpoint, - resolve_seed, run_playback, set_hydra_args, show_run_summary, startup_screen, ) +from . import cli_args_rl_games as cli_args logger = logging.getLogger(__name__) @@ -112,12 +112,8 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="play") apply_env_overrides(args_cli, env_cfg) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] - # the agent runs on the device launch_simulation resolved for the simulation - params["config"]["device"] = params["config"]["device_name"] = env_cfg.sim.device - args_cli.seed = resolve_seed(args_cli.seed) - if args_cli.seed is not None: - params["seed"] = args_cli.seed env_cfg.seed = params["seed"] log_root_path = os.path.abspath(os.path.join("logs", "rl_games", params["config"]["name"])) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py index 5797e367d2ca..46c32aceef74 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py @@ -108,8 +108,6 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="play") agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg.device = env_cfg.sim.device apply_env_overrides(args_cli, env_cfg) # certain randomizations occur in the environment initialization so we set the seed here env_cfg.seed = agent_cfg.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py index ef096a6b0da6..3371ffd8e936 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py @@ -36,12 +36,12 @@ pre_launch_video_config, resolve_checkpoint_selector, resolve_published_checkpoint, - resolve_seed, run_playback, set_hydra_args, show_run_summary, startup_screen, ) +from . import cli_args_sb3 as cli_args logger = logging.getLogger(__name__) @@ -105,9 +105,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="play") apply_env_overrides(args_cli, env_cfg) - args_cli.seed = resolve_seed(args_cli.seed) - if args_cli.seed is not None: - agent_cfg["seed"] = args_cli.seed + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", normalize_task_name(args_cli.task))) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py index 88a6c95a94e5..3d503feb2a5b 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py @@ -41,13 +41,13 @@ enable_cameras_for_video, pre_launch_video_config, resolve_checkpoint_selector, - resolve_seed, set_hydra_args, show_run_summary, startup_screen, wrap_sensor_capture, write_run_manifest, ) +from . import cli_args_rl_games as cli_args logger = logging.getLogger(__name__) @@ -114,19 +114,10 @@ def run(argv: list[str]) -> None: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="train") apply_env_overrides(args_cli, env_cfg) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] config = params["config"] - args_cli.seed = resolve_seed(args_cli.seed) - if args_cli.seed is not None: - params["seed"] = args_cli.seed - if args_cli.max_iterations is not None: - config["max_epochs"] = args_cli.max_iterations - # the agent runs on the device launch_simulation resolved for the simulation - config["device"] = config["device_name"] = env_cfg.sim.device rank = int(os.getenv("RANK", "0")) if args_cli.distributed else None - if rank is not None: - params["seed"] += rank - config["multi_gpu"] = True env_cfg.seed = params["seed"] config_name = config["name"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py index 7b05d83ce1dc..d24a6f9d798f 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py @@ -124,8 +124,6 @@ def _run(args_cli: argparse.Namespace) -> None: apply_env_overrides(args_cli, env_cfg) if args_cli.max_iterations is not None: agent_cfg.max_iterations = args_cli.max_iterations - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg.device = env_cfg.sim.device rank = int(os.getenv("RANK", "0")) if args_cli.distributed else None if rank is not None: agent_cfg.seed += rank diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py index d7b085c99ca5..8c81957dce36 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py @@ -42,13 +42,13 @@ enable_cameras_for_video, pre_launch_video_config, resolve_checkpoint_selector, - resolve_seed, set_hydra_args, show_run_summary, startup_screen, wrap_sensor_capture, write_run_manifest, ) +from . import cli_args_sb3 as cli_args logger = logging.getLogger(__name__) @@ -115,14 +115,10 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="train") apply_env_overrides(args_cli, env_cfg) - args_cli.seed = resolve_seed(args_cli.seed) - if args_cli.seed is not None: - agent_cfg["seed"] = args_cli.seed + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) if args_cli.max_iterations is not None: agent_cfg["n_timesteps"] = args_cli.max_iterations * agent_cfg["n_steps"] * env_cfg.scene.num_envs env_cfg.seed = agent_cfg["seed"] - # the agent runs on the device launch_simulation resolved for the simulation - agent_cfg["device"] = env_cfg.sim.device log_root_path = os.path.abspath(os.path.join("logs", "sb3", args_cli.task)) logger.info(f"Logging experiment in directory: {log_root_path}") diff --git a/source/isaaclab_rl/test/test_entrypoints_common.py b/source/isaaclab_rl/test/test_entrypoints_common.py index 76396385ce02..95095be0da72 100644 --- a/source/isaaclab_rl/test/test_entrypoints_common.py +++ b/source/isaaclab_rl/test/test_entrypoints_common.py @@ -295,9 +295,9 @@ def test_run_summary_reports_the_launch_resolved_visualizers_and_device() -> Non """A run without visualizers shows headless, and the device is the one the launch resolved (e.g. per rank).""" from isaaclab.app import launch_simulation - sim_cfg = SimulationCfg(physics=NewtonCfg(), device="cuda:0") + sim_cfg = SimulationCfg(physics=NewtonCfg(), device="cpu") env_cfg = SimpleNamespace(sim=sim_cfg, scene=SimpleNamespace(num_envs=4)) - args_cli = argparse.Namespace(task="Isaac-Test", device="cuda:0", num_envs=None, visualizer=None) + args_cli = argparse.Namespace(task="Isaac-Test", device="cpu", num_envs=None, visualizer=None) screen = _RecordingScreen() with launch_simulation(env_cfg, args_cli): From 062dd2adc443ad16832094830a9180a483c11016 Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Mon, 5 Oct 2026 09:56:16 +0200 Subject: [PATCH 6/9] Pass the agent device and seed to the shared RL config setup explicitly --- .../rl_games/benchmark_play_rl_games.py | 2 +- .../rl_games/benchmark_train_rl_games.py | 11 +++- .../backends/rsl_rl/benchmark_play_rsl_rl.py | 2 +- .../backends/rsl_rl/benchmark_train_rsl_rl.py | 11 +++- .../backends/sb3/benchmark_play_sb3.py | 4 +- .../backends/sb3/benchmark_train_sb3.py | 2 +- ...fix-rsl-rl-runner-cpu-fallback.changed.rst | 11 +--- .../entrypoints/backends/cli_args_rl_games.py | 28 +++----- .../entrypoints/backends/cli_args_rsl_rl.py | 64 +++++++++++++------ .../entrypoints/backends/cli_args_sb3.py | 18 ++---- .../entrypoints/backends/export_rl_games.py | 2 +- .../entrypoints/backends/export_rsl_rl.py | 8 ++- .../entrypoints/backends/play_rl_games.py | 2 +- .../entrypoints/backends/play_rsl_rl.py | 11 +++- .../entrypoints/backends/play_sb3.py | 4 +- .../entrypoints/backends/train_rl_games.py | 7 +- .../entrypoints/backends/train_rsl_rl.py | 11 +++- .../entrypoints/backends/train_sb3.py | 2 +- 18 files changed, 124 insertions(+), 76 deletions(-) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py index e928c9ec9fde..b251cfa5e3a4 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py @@ -140,7 +140,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) env_cfg.seed = agent_cfg["params"]["seed"] config_name = agent_cfg["params"]["config"]["name"] diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py index 1582cfd45887..03b0dfadf2df 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py @@ -194,7 +194,16 @@ def run(argv: list[str]) -> BenchmarkResult | None: apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + + if args_cli.max_iterations is not None: + agent_cfg["params"]["config"]["max_epochs"] = args_cli.max_iterations + + if distributed.enabled: + # Mirror the regular training entrypoint: offsetting the seed by the rank decorrelates + # exploration across ranks. + agent_cfg["params"]["seed"] += distributed.rank + agent_cfg["params"]["config"]["multi_gpu"] = True env_cfg.seed = agent_cfg["params"]["seed"] horizon_length = agent_cfg["params"]["config"].get("horizon_length", 16) reported_num_envs, _ = distributed.global_work(env_cfg.scene.num_envs, horizon_length) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py index c5a5a2b53a25..61e05f3fdaa9 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py @@ -138,7 +138,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args.num_envs is not None: env_cfg.scene.num_envs = args.num_envs - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args) + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args.device, seed=args.seed) env_cfg.seed = agent_cfg.seed log_root_path = os.path.abspath(os.path.join("logs", "rsl_rl", agent_cfg.experiment_name)) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py index dfd5f2af1e61..b412c74f456c 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py @@ -185,7 +185,16 @@ def run(argv: list[str]) -> BenchmarkResult | None: app_t1 = time.perf_counter_ns() apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rsl_rl_cfg( + agent_cfg, + args_cli.device, + seed=args_cli.seed, + checkpoint=args_cli.checkpoint, + experiment_name=args_cli.experiment_name, + run_name=args_cli.run_name, + logger=args_cli.logger, + log_project_name=args_cli.log_project_name, + ) agent_cfg.max_iterations = ( args_cli.max_iterations if args_cli.max_iterations is not None else agent_cfg.max_iterations ) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py index 8d4671cc7415..b8dc85631d0f 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py @@ -143,7 +143,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", args_cli.task)) @@ -213,7 +213,7 @@ def run(argv: list[str]) -> BenchmarkResult: ) # Load the trained policy. - agent = PPO.load(resume_path, env, device=env_cfg.sim.device, print_system_info=True) + agent = PPO.load(resume_path, env, device=agent_cfg["device"], print_system_info=True) def policy(obs): """Map an observation batch to a deterministic action batch via the sb3 agent. diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py index 7b23c0cfb17f..090cdf9a856c 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py @@ -241,7 +241,7 @@ def run(argv: list[str]) -> BenchmarkResult: apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) env_cfg.seed = agent_cfg["seed"] # Convert the iteration override to SB3 total timesteps. diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst index dec9ccf74c4c..fabe2c924fa1 100644 --- a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.changed.rst @@ -1,8 +1,3 @@ -* **Breaking:** Changed the RSL-RL, RL-Games, and Stable-Baselines3 train, play, and export workflows to run the - agent on the device :func:`~isaaclab.app.launch_simulation` resolved for the simulation, as skrl and TorchRL - already did. Outside distributed runs they used the agent configuration's device, ``cuda:0`` by default, so - ``--device cpu`` trained on ``cuda:0`` and ``--device cuda:1`` trained on GPU 0. Pass ``--device`` to choose - where both the simulation and the agent run; the agent configuration's ``device`` is now overwritten. -* Changed each library's agent configuration overrides to live in one shared function that every train, play, - export, and benchmark workflow calls: ``update_rsl_rl_cfg``, and the new ``update_rl_games_cfg`` and - ``update_sb3_cfg``. +* **Breaking:** Changed the RSL-RL, RL-Games, and Stable-Baselines3 workflows to run the agent on the simulation's + device, as skrl and TorchRL do. They ignored ``--device`` for the agent outside distributed runs and used the + agent configuration's ``cuda:0``; pass ``--device`` to choose where both run. diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py index a85d04135d32..373368be2746 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py @@ -7,35 +7,23 @@ from __future__ import annotations -import argparse -import os - from ..common import resolve_seed -def update_rl_games_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: +def update_rl_games_cfg(agent_cfg: dict, device: str, *, seed: int | None = None) -> dict: """Override an RL-Games agent configuration with the command-line arguments. - Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on - the simulation's device. - Args: agent_cfg: The configuration for RL-Games agent. - args_cli: The command line arguments. + device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` + has resolved it. + seed: Seed override; ``-1`` draws a random seed. Returns: The updated RL-Games agent configuration. """ - params = agent_cfg["params"] - config = params["config"] - config["device"] = config["device_name"] = args_cli.device - if getattr(args_cli, "seed", None) is not None: - args_cli.seed = resolve_seed(args_cli.seed) - params["seed"] = args_cli.seed - if getattr(args_cli, "max_iterations", None) is not None: - config["max_epochs"] = args_cli.max_iterations - if getattr(args_cli, "distributed", False): - # offsetting the seed by the rank decorrelates exploration across ranks - params["seed"] += int(os.getenv("RANK", "0")) - config["multi_gpu"] = True + config = agent_cfg["params"]["config"] + config["device"] = config["device_name"] = device + if seed is not None: + agent_cfg["params"]["seed"] = resolve_seed(seed) return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py index 484858cff748..9c461453c6df 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py @@ -81,35 +81,57 @@ def parse_rsl_rl_cfg(task_name: str, args_cli: argparse.Namespace) -> RslRlBaseR from isaaclab_tasks.utils.parse_cfg import load_cfg_from_registry agent_cfg: RslRlBaseRunnerCfg = load_cfg_from_registry(task_name, "rsl_rl_cfg_entry_point") - return update_rsl_rl_cfg(agent_cfg, args_cli) + return update_rsl_rl_cfg( + agent_cfg, + args_cli.device, + seed=args_cli.seed, + checkpoint=args_cli.checkpoint, + experiment_name=args_cli.experiment_name, + run_name=args_cli.run_name, + logger=args_cli.logger, + log_project_name=args_cli.log_project_name, + ) -def update_rsl_rl_cfg(agent_cfg: RslRlBaseRunnerCfg, args_cli: argparse.Namespace) -> RslRlBaseRunnerCfg: +def update_rsl_rl_cfg( + agent_cfg: RslRlBaseRunnerCfg, + device: str, + *, + seed: int | None = None, + checkpoint: str | None = None, + experiment_name: str | None = None, + run_name: str | None = None, + logger: str | None = None, + log_project_name: str | None = None, +) -> RslRlBaseRunnerCfg: """Override an RSL-RL agent configuration with the command-line arguments. - Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on - the simulation's device. - Args: agent_cfg: The configuration for RSL-RL agent. - args_cli: The command line arguments. + device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` + has resolved it. + seed: Seed override; ``-1`` draws a random seed. + checkpoint: Checkpoint file to load. + experiment_name: Experiment folder name. + run_name: Run name suffix. + logger: Logger to use. + log_project_name: Project name for the ``wandb`` and ``neptune`` loggers. Returns: The updated RSL-RL agent configuration. """ - agent_cfg.device = args_cli.device - if getattr(args_cli, "seed", None) is not None: - args_cli.seed = resolve_seed(args_cli.seed) - agent_cfg.seed = args_cli.seed - if getattr(args_cli, "checkpoint", None) is not None: - agent_cfg.load_checkpoint = args_cli.checkpoint - if getattr(args_cli, "experiment_name", None) is not None: - agent_cfg.experiment_name = args_cli.experiment_name - if getattr(args_cli, "run_name", None) is not None: - agent_cfg.run_name = args_cli.run_name - if getattr(args_cli, "logger", None) is not None: - agent_cfg.logger = args_cli.logger - if agent_cfg.logger in {"wandb", "neptune"} and getattr(args_cli, "log_project_name", None): - agent_cfg.wandb_project = args_cli.log_project_name - agent_cfg.neptune_project = args_cli.log_project_name + agent_cfg.device = device + if seed is not None: + agent_cfg.seed = resolve_seed(seed) + if checkpoint is not None: + agent_cfg.load_checkpoint = checkpoint + if experiment_name is not None: + agent_cfg.experiment_name = experiment_name + if run_name is not None: + agent_cfg.run_name = run_name + if logger is not None: + agent_cfg.logger = logger + if agent_cfg.logger in {"wandb", "neptune"} and log_project_name: + agent_cfg.wandb_project = log_project_name + agent_cfg.neptune_project = log_project_name return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py index 71f3d2cb8cf0..658e28dec74a 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py @@ -7,26 +7,22 @@ from __future__ import annotations -import argparse - from ..common import resolve_seed -def update_sb3_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: +def update_sb3_cfg(agent_cfg: dict, device: str, *, seed: int | None = None) -> dict: """Override a Stable-Baselines3 agent configuration with the command-line arguments. - Call it inside :func:`~isaaclab.app.launch_simulation`, which resolves ``args_cli.device``: the agent runs on - the simulation's device. Loading a checkpoint still needs that device passed to ``PPO.load``. - Args: agent_cfg: The configuration for Stable-Baselines3 agent. - args_cli: The command line arguments. + device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` + has resolved it. Pass ``agent_cfg["device"]`` to ``PPO.load`` as well. + seed: Seed override; ``-1`` draws a random seed. Returns: The updated Stable-Baselines3 agent configuration. """ - agent_cfg["device"] = args_cli.device - if getattr(args_cli, "seed", None) is not None: - args_cli.seed = resolve_seed(args_cli.seed) - agent_cfg["seed"] = args_cli.seed + agent_cfg["device"] = device + if seed is not None: + agent_cfg["seed"] = resolve_seed(seed) return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py index 7601ceb1be0f..1d9fe4faa208 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py @@ -106,7 +106,7 @@ def export_rl_games_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: # initialized the selected backend from leapp import annotate - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device) params = agent_cfg["params"] env_cfg.scene.num_envs = 1 env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py index e1fcfd329bb7..74be73564f07 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py @@ -132,7 +132,13 @@ def export_rsl_rl_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: R # the LEAPP runtime loads simulation modules, so import it only after the launch from leapp import annotate - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rsl_rl_cfg( + agent_cfg, + args_cli.device, + seed=args_cli.seed, + checkpoint=args_cli.checkpoint, + experiment_name=args_cli.experiment_name, + ) env_cfg.scene.num_envs = 1 # certain randomizations occur in the environment initialization so we set the seed here diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py index b4376e0356e1..4cc727222518 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py @@ -112,7 +112,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="play") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) params = agent_cfg["params"] env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py index 46c32aceef74..ac8911616d9e 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py @@ -107,7 +107,16 @@ def run(argv: list[str]) -> None: screen.stage("Launching simulation") with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="play") - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rsl_rl_cfg( + agent_cfg, + args_cli.device, + seed=args_cli.seed, + checkpoint=args_cli.checkpoint, + experiment_name=args_cli.experiment_name, + run_name=args_cli.run_name, + logger=args_cli.logger, + log_project_name=args_cli.log_project_name, + ) apply_env_overrides(args_cli, env_cfg) # certain randomizations occur in the environment initialization so we set the seed here env_cfg.seed = agent_cfg.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py index 3371ffd8e936..d6669241cf27 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py @@ -105,7 +105,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="play") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", normalize_task_name(args_cli.task))) @@ -145,7 +145,7 @@ def run(argv: list[str]) -> None: ) print(f"Loading checkpoint from: {checkpoint_path}") - agent = PPO.load(checkpoint_path, env, device=env_cfg.sim.device, print_system_info=True) + agent = PPO.load(checkpoint_path, env, device=agent_cfg["device"], print_system_info=True) # configure_seed must run after PPO.load so torch determinism does not disturb SB3's initialization if args_cli.deterministic: configure_seed(env_cfg.seed, torch_deterministic=True) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py index 3d503feb2a5b..325f39f4451e 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py @@ -114,10 +114,15 @@ def run(argv: list[str]) -> None: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="train") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) params = agent_cfg["params"] config = params["config"] + if args_cli.max_iterations is not None: + config["max_epochs"] = args_cli.max_iterations rank = int(os.getenv("RANK", "0")) if args_cli.distributed else None + if rank is not None: + params["seed"] += rank + config["multi_gpu"] = True env_cfg.seed = params["seed"] config_name = config["name"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py index d24a6f9d798f..668128fabce6 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py @@ -120,7 +120,16 @@ def _run(args_cli: argparse.Namespace) -> None: screen.stage("Launching simulation") with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="train") - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_rsl_rl_cfg( + agent_cfg, + args_cli.device, + seed=args_cli.seed, + checkpoint=args_cli.checkpoint, + experiment_name=args_cli.experiment_name, + run_name=args_cli.run_name, + logger=args_cli.logger, + log_project_name=args_cli.log_project_name, + ) apply_env_overrides(args_cli, env_cfg) if args_cli.max_iterations is not None: agent_cfg.max_iterations = args_cli.max_iterations diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py index 8c81957dce36..b79dd093b6e3 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py @@ -115,7 +115,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="train") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) if args_cli.max_iterations is not None: agent_cfg["n_timesteps"] = args_cli.max_iterations * agent_cfg["n_steps"] * env_cfg.scene.num_envs env_cfg.seed = agent_cfg["seed"] From 9d208ef7b62dae5c9f1b0ff993b74bb5034c206e Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Mon, 5 Oct 2026 10:55:51 +0200 Subject: [PATCH 7/9] Restore update_rsl_rl_cfg's args_cli signature and set the device there --- .../backends/rsl_rl/benchmark_play_rsl_rl.py | 5 +- .../backends/rsl_rl/benchmark_train_rsl_rl.py | 11 +--- .../entrypoints/backends/cli_args_rsl_rl.py | 61 ++++++------------- .../entrypoints/backends/export_rsl_rl.py | 17 +++--- .../entrypoints/backends/play_rsl_rl.py | 11 +--- .../entrypoints/backends/train_rsl_rl.py | 11 +--- 6 files changed, 32 insertions(+), 84 deletions(-) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py index 61e05f3fdaa9..418154991ab5 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_play_rsl_rl.py @@ -111,7 +111,6 @@ def run(argv: list[str]) -> BenchmarkResult: from isaaclab.benchmark import BaseIsaacLabBenchmark, BenchmarkMonitor, BenchmarkResult, builders, capture, stepping from isaaclab.benchmark.schema import StartupTime - from isaaclab_rl.entrypoints.backends import cli_args_rsl_rl as cli_args from isaaclab_rl.rsl_rl import RslRlVecEnvWrapper # Importing the task packages registers their gym environments so the @@ -138,7 +137,9 @@ def run(argv: list[str]) -> BenchmarkResult: if args.num_envs is not None: env_cfg.scene.num_envs = args.num_envs - agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args.device, seed=args.seed) + if args.seed is not None: + agent_cfg.seed = args.seed + agent_cfg.device = args.device env_cfg.seed = agent_cfg.seed log_root_path = os.path.abspath(os.path.join("logs", "rsl_rl", agent_cfg.experiment_name)) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py index b412c74f456c..dfd5f2af1e61 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rsl_rl/benchmark_train_rsl_rl.py @@ -185,16 +185,7 @@ def run(argv: list[str]) -> BenchmarkResult | None: app_t1 = time.perf_counter_ns() apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rsl_rl_cfg( - agent_cfg, - args_cli.device, - seed=args_cli.seed, - checkpoint=args_cli.checkpoint, - experiment_name=args_cli.experiment_name, - run_name=args_cli.run_name, - logger=args_cli.logger, - log_project_name=args_cli.log_project_name, - ) + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) agent_cfg.max_iterations = ( args_cli.max_iterations if args_cli.max_iterations is not None else agent_cfg.max_iterations ) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py index 9c461453c6df..df9996093480 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rsl_rl.py @@ -81,57 +81,32 @@ def parse_rsl_rl_cfg(task_name: str, args_cli: argparse.Namespace) -> RslRlBaseR from isaaclab_tasks.utils.parse_cfg import load_cfg_from_registry agent_cfg: RslRlBaseRunnerCfg = load_cfg_from_registry(task_name, "rsl_rl_cfg_entry_point") - return update_rsl_rl_cfg( - agent_cfg, - args_cli.device, - seed=args_cli.seed, - checkpoint=args_cli.checkpoint, - experiment_name=args_cli.experiment_name, - run_name=args_cli.run_name, - logger=args_cli.logger, - log_project_name=args_cli.log_project_name, - ) + return update_rsl_rl_cfg(agent_cfg, args_cli) -def update_rsl_rl_cfg( - agent_cfg: RslRlBaseRunnerCfg, - device: str, - *, - seed: int | None = None, - checkpoint: str | None = None, - experiment_name: str | None = None, - run_name: str | None = None, - logger: str | None = None, - log_project_name: str | None = None, -) -> RslRlBaseRunnerCfg: +def update_rsl_rl_cfg(agent_cfg: RslRlBaseRunnerCfg, args_cli: argparse.Namespace) -> RslRlBaseRunnerCfg: """Override an RSL-RL agent configuration with the command-line arguments. Args: agent_cfg: The configuration for RSL-RL agent. - device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` - has resolved it. - seed: Seed override; ``-1`` draws a random seed. - checkpoint: Checkpoint file to load. - experiment_name: Experiment folder name. - run_name: Run name suffix. - logger: Logger to use. - log_project_name: Project name for the ``wandb`` and ``neptune`` loggers. + args_cli: The command line arguments. Returns: The updated RSL-RL agent configuration. """ - agent_cfg.device = device - if seed is not None: - agent_cfg.seed = resolve_seed(seed) - if checkpoint is not None: - agent_cfg.load_checkpoint = checkpoint - if experiment_name is not None: - agent_cfg.experiment_name = experiment_name - if run_name is not None: - agent_cfg.run_name = run_name - if logger is not None: - agent_cfg.logger = logger - if agent_cfg.logger in {"wandb", "neptune"} and log_project_name: - agent_cfg.wandb_project = log_project_name - agent_cfg.neptune_project = log_project_name + agent_cfg.device = args_cli.device + if getattr(args_cli, "seed", None) is not None: + args_cli.seed = resolve_seed(args_cli.seed) + agent_cfg.seed = args_cli.seed + if args_cli.checkpoint is not None: + agent_cfg.load_checkpoint = args_cli.checkpoint + if args_cli.experiment_name is not None: + agent_cfg.experiment_name = args_cli.experiment_name + if args_cli.run_name is not None: + agent_cfg.run_name = args_cli.run_name + if args_cli.logger is not None: + agent_cfg.logger = args_cli.logger + if agent_cfg.logger in {"wandb", "neptune"} and args_cli.log_project_name: + agent_cfg.wandb_project = args_cli.log_project_name + agent_cfg.neptune_project = args_cli.log_project_name return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py index 74be73564f07..baf46a74c11a 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rsl_rl.py @@ -30,8 +30,7 @@ import isaaclab_tasks # noqa: F401 from ...rsl_rl import RslRlBaseRunnerCfg, RslRlVecEnvWrapper -from ..common import resolve_published_checkpoint -from . import cli_args_rsl_rl as cli_args +from ..common import resolve_published_checkpoint, resolve_seed from .export_common import ( add_common_export_args, finalize_export_args, @@ -132,13 +131,13 @@ def export_rsl_rl_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: R # the LEAPP runtime loads simulation modules, so import it only after the launch from leapp import annotate - agent_cfg = cli_args.update_rsl_rl_cfg( - agent_cfg, - args_cli.device, - seed=args_cli.seed, - checkpoint=args_cli.checkpoint, - experiment_name=args_cli.experiment_name, - ) + if args_cli.seed is not None: + agent_cfg.seed = resolve_seed(args_cli.seed) + if args_cli.checkpoint is not None: + agent_cfg.load_checkpoint = args_cli.checkpoint + if args_cli.experiment_name is not None: + agent_cfg.experiment_name = args_cli.experiment_name + agent_cfg.device = args_cli.device env_cfg.scene.num_envs = 1 # certain randomizations occur in the environment initialization so we set the seed here diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py index ac8911616d9e..46c32aceef74 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rsl_rl.py @@ -107,16 +107,7 @@ def run(argv: list[str]) -> None: screen.stage("Launching simulation") with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="play") - agent_cfg = cli_args.update_rsl_rl_cfg( - agent_cfg, - args_cli.device, - seed=args_cli.seed, - checkpoint=args_cli.checkpoint, - experiment_name=args_cli.experiment_name, - run_name=args_cli.run_name, - logger=args_cli.logger, - log_project_name=args_cli.log_project_name, - ) + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) apply_env_overrides(args_cli, env_cfg) # certain randomizations occur in the environment initialization so we set the seed here env_cfg.seed = agent_cfg.seed diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py index 668128fabce6..d24a6f9d798f 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rsl_rl.py @@ -120,16 +120,7 @@ def _run(args_cli: argparse.Namespace) -> None: screen.stage("Launching simulation") with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rsl_rl", action="train") - agent_cfg = cli_args.update_rsl_rl_cfg( - agent_cfg, - args_cli.device, - seed=args_cli.seed, - checkpoint=args_cli.checkpoint, - experiment_name=args_cli.experiment_name, - run_name=args_cli.run_name, - logger=args_cli.logger, - log_project_name=args_cli.log_project_name, - ) + agent_cfg = cli_args.update_rsl_rl_cfg(agent_cfg, args_cli) apply_env_overrides(args_cli, env_cfg) if args_cli.max_iterations is not None: agent_cfg.max_iterations = args_cli.max_iterations From 202545fa937e9a6251c496b3e8813278cd96ad84 Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Mon, 5 Oct 2026 11:01:37 +0200 Subject: [PATCH 8/9] Pass args_cli to update_sb3_cfg like update_rsl_rl_cfg --- .../backends/sb3/benchmark_play_sb3.py | 2 +- .../backends/sb3/benchmark_train_sb3.py | 2 +- .../entrypoints/backends/cli_args_sb3.py | 15 ++++++++------- .../isaaclab_rl/entrypoints/backends/play_sb3.py | 2 +- .../isaaclab_rl/entrypoints/backends/train_sb3.py | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py index b8dc85631d0f..cdd296a7863e 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_play_sb3.py @@ -143,7 +143,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", args_cli.task)) diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py index 090cdf9a856c..7b23c0cfb17f 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/sb3/benchmark_train_sb3.py @@ -241,7 +241,7 @@ def run(argv: list[str]) -> BenchmarkResult: apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] # Convert the iteration override to SB3 total timesteps. diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py index 658e28dec74a..25e44c3df8a8 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_sb3.py @@ -7,22 +7,23 @@ from __future__ import annotations +import argparse + from ..common import resolve_seed -def update_sb3_cfg(agent_cfg: dict, device: str, *, seed: int | None = None) -> dict: +def update_sb3_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: """Override a Stable-Baselines3 agent configuration with the command-line arguments. Args: agent_cfg: The configuration for Stable-Baselines3 agent. - device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` - has resolved it. Pass ``agent_cfg["device"]`` to ``PPO.load`` as well. - seed: Seed override; ``-1`` draws a random seed. + args_cli: The command line arguments. Returns: The updated Stable-Baselines3 agent configuration. """ - agent_cfg["device"] = device - if seed is not None: - agent_cfg["seed"] = resolve_seed(seed) + agent_cfg["device"] = args_cli.device + if args_cli.seed is not None: + args_cli.seed = resolve_seed(args_cli.seed) + agent_cfg["seed"] = args_cli.seed return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py index d6669241cf27..150abc2fa724 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_sb3.py @@ -105,7 +105,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="play") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["seed"] log_root_path = os.path.abspath(os.path.join("logs", "sb3", normalize_task_name(args_cli.task))) diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py index b79dd093b6e3..8c81957dce36 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_sb3.py @@ -115,7 +115,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="sb3", action="train") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_sb3_cfg(agent_cfg, args_cli) if args_cli.max_iterations is not None: agent_cfg["n_timesteps"] = args_cli.max_iterations * agent_cfg["n_steps"] * env_cfg.scene.num_envs env_cfg.seed = agent_cfg["seed"] From 9055fc6c522e45d7e9f0ffdfffb5a197be87fec2 Mon Sep 17 00:00:00 2001 From: Antoine RICHARD Date: Mon, 5 Oct 2026 11:09:58 +0200 Subject: [PATCH 9/9] Add --seed to RL-Games export and pass args_cli to update_rl_games_cfg --- .../backends/rl_games/benchmark_play_rl_games.py | 2 +- .../backends/rl_games/benchmark_train_rl_games.py | 2 +- ...oiner-fix-rsl-rl-runner-cpu-fallback.added.rst | 1 + .../entrypoints/backends/cli_args_rl_games.py | 15 ++++++++------- .../entrypoints/backends/export_rl_games.py | 3 ++- .../entrypoints/backends/play_rl_games.py | 2 +- .../entrypoints/backends/train_rl_games.py | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.added.rst diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py index b251cfa5e3a4..e928c9ec9fde 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_play_rl_games.py @@ -140,7 +140,7 @@ def run(argv: list[str]) -> BenchmarkResult: if args_cli.num_envs is not None: env_cfg.scene.num_envs = args_cli.num_envs - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) env_cfg.seed = agent_cfg["params"]["seed"] config_name = agent_cfg["params"]["config"]["name"] diff --git a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py index 03b0dfadf2df..0aa892748095 100644 --- a/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py +++ b/source/isaaclab/isaaclab/benchmark/entrypoints/backends/rl_games/benchmark_train_rl_games.py @@ -194,7 +194,7 @@ def run(argv: list[str]) -> BenchmarkResult | None: apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) if args_cli.max_iterations is not None: agent_cfg["params"]["config"]["max_epochs"] = args_cli.max_iterations diff --git a/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.added.rst b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.added.rst new file mode 100644 index 000000000000..5eb347d43b69 --- /dev/null +++ b/source/isaaclab_rl/changelog.d/antoiner-fix-rsl-rl-runner-cpu-fallback.added.rst @@ -0,0 +1 @@ +* Added ``--seed`` to the RL-Games export command, matching the RSL-RL export command. diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py index 373368be2746..56cd204318fe 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/cli_args_rl_games.py @@ -7,23 +7,24 @@ from __future__ import annotations +import argparse + from ..common import resolve_seed -def update_rl_games_cfg(agent_cfg: dict, device: str, *, seed: int | None = None) -> dict: +def update_rl_games_cfg(agent_cfg: dict, args_cli: argparse.Namespace) -> dict: """Override an RL-Games agent configuration with the command-line arguments. Args: agent_cfg: The configuration for RL-Games agent. - device: Device the agent runs on, the simulation's device once :func:`~isaaclab.app.launch_simulation` - has resolved it. - seed: Seed override; ``-1`` draws a random seed. + args_cli: The command line arguments. Returns: The updated RL-Games agent configuration. """ config = agent_cfg["params"]["config"] - config["device"] = config["device_name"] = device - if seed is not None: - agent_cfg["params"]["seed"] = resolve_seed(seed) + config["device"] = config["device_name"] = args_cli.device + if args_cli.seed is not None: + args_cli.seed = resolve_seed(args_cli.seed) + agent_cfg["params"]["seed"] = args_cli.seed return agent_cfg diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py index 1d9fe4faa208..0c4b1f60ab94 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/export_rl_games.py @@ -51,6 +51,7 @@ def parse_export_args(argv: list[str] | None = None) -> tuple[argparse.Namespace """Parse export arguments and return the remaining Hydra overrides.""" parser = argparse.ArgumentParser(description="Export an RL agent with RL-Games.") add_common_export_args(parser, agent_default="rl_games_cfg_entry_point") + parser.add_argument("--seed", type=int, default=None, help="Seed used for the environment.") parser.add_argument( "--use_last_checkpoint", action="store_true", @@ -106,7 +107,7 @@ def export_rl_games_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: # initialized the selected backend from leapp import annotate - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] env_cfg.scene.num_envs = 1 env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py index 4cc727222518..b4376e0356e1 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/play_rl_games.py @@ -112,7 +112,7 @@ def run(argv: list[str]) -> None: with launch_simulation(env_cfg, args_cli), contextlib.ExitStack() as cleanup: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="play") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] env_cfg.seed = params["seed"] diff --git a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py index 325f39f4451e..883e5a4029b5 100644 --- a/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py +++ b/source/isaaclab_rl/isaaclab_rl/entrypoints/backends/train_rl_games.py @@ -114,7 +114,7 @@ def run(argv: list[str]) -> None: show_run_summary(screen, args_cli, env_cfg, library="rl_games", action="train") apply_env_overrides(args_cli, env_cfg) - agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli.device, seed=args_cli.seed) + agent_cfg = cli_args.update_rl_games_cfg(agent_cfg, args_cli) params = agent_cfg["params"] config = params["config"] if args_cli.max_iterations is not None: