Skip to content
Draft
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
50 changes: 49 additions & 1 deletion scripts/reinforcement_learning/rsl_rl/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@
"--ray-proc-id", "-rid", type=int, default=None, help="Automatically configured by Ray integration, otherwise None."
)
parser.add_argument("--external_callback", default=None, help="Fully qualified path to an externally defined callback.")
parser.add_argument(
"--frontend",
type=str,
default="stable",
choices=["stable", "warp"],
help=(
"Runtime backend for the env. 'stable' uses isaaclab.envs.* (torch);"
" 'warp' routes through isaaclab_experimental.envs.warp_frontend.WarpFrontend,"
" which adapts a manager-based stable cfg onto ManagerBasedRLEnvWarp or dispatches"
" a direct task to its registered warp env class."
),
)
cli_args.add_rsl_rl_args(parser)
add_launcher_args(parser)
args_cli, remaining_args = parser.parse_known_args()
Expand All @@ -86,6 +98,34 @@
# The remaining arguments are the arguments that were not consumed by both this scripts
# argparser and (optionally) the external callback function.
remaining_args = list_intersection(remaining_args, remaining_args_env_registration)

# When the warp frontend is selected on a stable manager-based task, the cfg
# must resolve any PresetCfg wrappers to their ``newton`` field. Hydra
# resolves presets *before* the WarpFrontend runs, so we inject
# ``presets=newton`` here. We only inject for tasks whose env_cfg_entry_point
# is under ``isaaclab_tasks.manager_based``; direct tasks and the pre-warp
# ``*-Warp-v0`` registrations don't carry the preset system, and injecting
# ``presets=newton`` against them causes Hydra to error before the frontend
# can produce its own diagnostic. Note: ``spec.entry_point`` is the env
# *class* path (e.g. ``isaaclab.envs:ManagerBasedRLEnv``) — we check the
# *cfg* entry point in ``spec.kwargs``.
if args_cli.frontend == "warp" and args_cli.task is not None:
try:
_spec = gym.spec(args_cli.task)
except gym.error.NameNotFound:
_spec = None
_cfg_entry = _spec.kwargs.get("env_cfg_entry_point") if _spec is not None else None
_is_stable_manager = isinstance(_cfg_entry, str) and _cfg_entry.startswith("isaaclab_tasks.manager_based")
_explicit_preset = next((a for a in remaining_args if a.startswith("presets=")), None)
if _is_stable_manager and _explicit_preset is None:
remaining_args.append("presets=newton")
elif _is_stable_manager and _explicit_preset != "presets=newton":
logger.warning(
"--frontend=warp on %r expects presets=newton; got %r — adapter may fail to find a Newton physics cfg.",
args_cli.task,
_explicit_preset,
)

sys.argv = [sys.argv[0]] + remaining_args

# -- check RSL-RL version ----------------------------------------------------
Expand Down Expand Up @@ -171,7 +211,15 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen
env_cfg.log_dir = log_dir

# create isaac environment
env = gym.make(args_cli.task, cfg=env_cfg, render_mode="rgb_array" if args_cli.video else None)
render_mode = "rgb_array" if args_cli.video else None
if args_cli.frontend == "warp":
# Lazy: first warp-side import. Calling this after SimulationApp
# is already alive avoids racing pxr extension init.
from isaaclab_experimental.envs.warp_frontend import WarpFrontend

env = WarpFrontend().build(env_cfg, task_id=args_cli.task, render_mode=render_mode)
else:
env = gym.make(args_cli.task, cfg=env_cfg, render_mode=render_mode)

# convert to single-agent instance if required by the RL algorithm
if isinstance(env.unwrapped.cfg, DirectMARLEnvCfg):
Expand Down
32 changes: 32 additions & 0 deletions source/isaaclab_experimental/changelog.d/warp-manager-bridge.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Added
^^^^^

* Added :class:`~isaaclab_experimental.envs.warp_frontend.WarpFrontend`, a
runtime adapter that lets any stable manager-based RL task config run on the
experimental warp runtime (:class:`~isaaclab_experimental.envs.ManagerBasedRLEnvWarp`)
without a parallel ``-Warp-v0`` registration. The adapter is built on a
pluggable :class:`~isaaclab_experimental.envs.warp_frontend.CompatRule`
pipeline; new incompatibilities (sensor types, term-cfg fields, action
classes) are added by writing a small rule subclass instead of editing the
dispatcher. The default rules cover physics-preset resolution, dropping
unsupported sensors, in-place :class:`SceneEntityCfg` promotion, mdp
function swaps, and action-class swaps. The frontend also dispatches
direct envs by verifying their registered entry-point class lives under
``isaaclab_experimental`` / ``isaaclab_tasks_experimental`` and routing
through :func:`gym.make` unchanged.

* Added a ``--frontend={stable,warp}`` flag to ``rsl_rl/train.py``. When set
to ``warp`` the script auto-injects ``presets=newton`` (so Hydra picks the
Newton physics preset before the adapter runs), warns on conflicting
``presets=`` overrides, and dispatches the env through ``WarpFrontend``
instead of :func:`gym.make`. ``render_mode`` is forwarded so ``--video``
keeps working under the warp frontend.

Fixed
^^^^^

* Fixed a regression in :class:`~isaaclab_experimental.envs.ManagerBasedRLEnvWarp`
introduced when the ``SimulationContext.get_setting`` API was reshaped:
the warp env now mirrors the stable env and probes
:meth:`~isaaclab.sim.SimulationContext.has_active_visualizers` instead of
splitting a string setting that no longer exists.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from isaaclab.ui.widgets import ManagerLiveVisualizer
from isaaclab.utils.seed import configure_seed
from isaaclab.utils.timer import Timer
from isaaclab.utils.version import has_kit

from isaaclab_experimental.envs.interactive_scene_warp import InteractiveSceneWarp as InteractiveScene
from isaaclab_experimental.utils.manager_call_switch import ManagerCallMode, ManagerCallSwitch
Expand Down Expand Up @@ -154,13 +155,11 @@ def __init__(self, cfg: ManagerBasedEnvCfg):
# Persistent scalar buffer for global env step count (stable pointer for capture).
self._global_env_step_count_wp = wp.zeros((1,), dtype=wp.int32, device=self.device)

# set up camera viewport controller
# viewport is not available in other rendering modes so the function will throw a warning
# FIXME: This needs to be fixed in the future when we unify the UI functionalities even for
# non-rendering modes.
viz_str = self.sim.get_setting("/isaaclab/visualizer") or ""
available_visualizers = [v.strip() for v in viz_str.split(",") if v.strip()]
if "kit" in available_visualizers and bool(viz_str):
# set up camera viewport controller — mirror stable env (PR #5297 changed
# the visualizer API; this branch was missed). ViewportCameraController
# uses omni.kit; skip it in kitless Newton-only runs.
has_visualizers = self.sim.has_active_visualizers()
if (self.sim.has_gui or has_visualizers) and has_kit():
self.viewport_camera_controller = ViewportCameraController(self, self.cfg.viewer)
else:
self.viewport_camera_controller = None
Expand Down
Loading
Loading