Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* 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``.
5 changes: 5 additions & 0 deletions source/isaaclab/isaaclab/app/sim_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,10 +504,15 @@ def _resolve_device(sim_cfg, args: dict, launchers: list[SimulationLauncher]) ->

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.

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():

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id rather raise an error than fall back if device startwith("cuda")

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)}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -194,19 +194,15 @@ 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"]
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

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def run(argv: list[str]) -> BenchmarkResult:
env_cfg.scene.num_envs = args.num_envs
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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,8 @@ def run(argv: list[str]) -> BenchmarkResult | None:
env_cfg.seed = agent_cfg.seed

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand Down Expand Up @@ -212,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=agent_cfg["device"], print_system_info=True)

def policy(obs):
"""Map an observation batch to a deterministic action batch via the sb3 agent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -240,7 +241,7 @@ 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"]

# Convert the iteration override to SB3 total timesteps.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added ``--seed`` to the RL-Games export command, matching the RSL-RL export command.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* **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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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

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.

Args:
agent_cfg: The configuration for RL-Games agent.
args_cli: The command line arguments.

Returns:
The updated RL-Games agent configuration.
"""
config = agent_cfg["params"]["config"]
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
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ 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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 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.

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 args_cli.seed is not None:
args_cli.seed = resolve_seed(args_cli.seed)
agent_cfg["seed"] = args_cli.seed
return agent_cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -50,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",
Expand Down Expand Up @@ -105,6 +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)
params = agent_cfg["params"]
env_cfg.scene.num_envs = 1
env_cfg.seed = params["seed"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def export_rsl_rl_agent(args_cli: argparse.Namespace, env_cfg: Any, agent_cfg: R
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -112,10 +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"]
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"]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -147,7 +145,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=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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -114,18 +114,14 @@ 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
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"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ def _run(args_cli: argparse.Namespace) -> None:
agent_cfg.max_iterations = args_cli.max_iterations
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -115,9 +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)
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"]
Expand Down
4 changes: 2 additions & 2 deletions source/isaaclab_rl/test/test_entrypoints_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading