Skip to content

Commit 2222079

Browse files
authored
[Fix] Allow Warp environments to launch with the Kit visualizer (#6111)
## Summary - Fixes a crash when launching the experimental Warp environments with a Kit visualizer (`--visualizer kit`): `AttributeError: 'dict' object has no attribute 'split'` at environment construction. Reported by QA across ~10 manager-based Warp tasks (velocity/reach, e.g. `Isaac-Velocity-Flat-Unitree-Go2-Warp-Play-v0`). - Aligns the Warp environments' visualizer / rendering checks with the stable (non-Warp) environments. ## 1. Problem `play.py --task Isaac-Velocity-Flat-Unitree-Go2-Warp-Play-v0 --visualizer kit` crashes during `gym.make`: ``` File ".../isaaclab_experimental/envs/manager_based_env_warp.py", line 165, in __init__ available_visualizers = [v.strip() for v in viz_str.split(",") if v.strip()] AttributeError: 'dict' object has no attribute 'split' ``` ## 2. Root cause The Warp environments read the **parent** settings node `/isaaclab/visualizer`, which carb returns as a **dict** of the `{types, explicit, disable_all, max_visible_envs}` subtree that `AppLauncher` writes — then call `.split(",")` on it. The stable environments never do this: they read `/isaaclab/visualizer/types` via `SimulationContext.has_active_visualizers()` and the `is_rendering` property, which also guard against non-string values. The sibling `bool(...)` reads of the same parent node are silently always-true (the subtree always exists after launch). ## 3. Fix Align the Warp environments with the stable ones: - `manager_based_env_warp.py`: viewport setup → `has_active_visualizers()` + `has_kit()` (mirrors `manager_based_env.py`); in-loop `is_rendering` → `self.sim.is_rendering`. - `direct_rl_env_warp.py`: UI-window gate → `self.sim.has_gui` (mirrors `direct_rl_env.py`); in-loop `is_rendering` → `self.sim.is_rendering` (drops a redundant manual rtx-sensor check folded into the property). ## 4. Validation Reproduced the exact reported command on a GPU, before and after the fix: - **Without the fix:** crashes at `gym.make` with `AttributeError: 'dict' object has no attribute 'split'` (never reaches checkpoint loading). - **With the fix:** environment constructs, the checkpoint loads, and play runs (`--visualizer kit`). `train.py` for the same task also completes.
1 parent 6b66819 commit 2222079

3 files changed

Lines changed: 21 additions & 11 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Fixed
2+
^^^^^
3+
4+
* Fixed a crash (``AttributeError: 'dict' object has no attribute 'split'``) when
5+
launching the experimental Warp environments
6+
(:class:`~isaaclab_experimental.envs.ManagerBasedRLEnvWarp`,
7+
:class:`~isaaclab_experimental.envs.DirectRLEnvWarp`) with a Kit visualizer
8+
requested (e.g. ``--visualizer kit``). The environments now resolve the active
9+
visualizer through :meth:`~isaaclab.sim.SimulationContext.has_active_visualizers`
10+
and the :attr:`~isaaclab.sim.SimulationContext.is_rendering` property, matching the
11+
stable environments, instead of parsing the ``/isaaclab/visualizer`` settings node
12+
(which is a dictionary) as a string.

source/isaaclab_experimental/isaaclab_experimental/envs/direct_rl_env_warp.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ def __init__(self, cfg: DirectRLEnvCfg, render_mode: str | None = None, **kwargs
214214
# extend UI elements
215215
# we need to do this here after all the managers are initialized
216216
# this is because they dictate the sensors and commands right now
217-
if bool(self.sim.settings.get("/isaaclab/visualizer")) and self.cfg.ui_window_class_type is not None:
217+
if self.sim.has_gui and self.cfg.ui_window_class_type is not None:
218218
self._window = self.cfg.ui_window_class_type(self, window_name="IsaacLab")
219219
else:
220220
# if no window, then we don't need to store the window
@@ -417,9 +417,8 @@ def step(self, action: torch.Tensor) -> VecEnvStepReturn:
417417
) # Creates a tensor and discards it. Not graphable unless training loop reuses the same pointer.
418418

419419
# check if we need to do rendering within the physics loop
420-
# note: checked here once to avoid multiple checks within the loop
421-
_has_rtx = hasattr(self.sim, "has_rtx_sensors") and self.sim.has_rtx_sensors()
422-
is_rendering = bool(self.sim.settings.get("/isaaclab/visualizer")) or _has_rtx
420+
# note: hoisted out of the decimation loop; is_rendering does live settings lookups
421+
is_rendering = self.sim.is_rendering
423422

424423
# perform physics stepping
425424
with Timer(name="physics_loop", msg="Physics loop took:", enable=DEBUG_TIMERS):

source/isaaclab_experimental/isaaclab_experimental/envs/manager_based_env_warp.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from isaaclab.ui.widgets import ManagerLiveVisualizer
3636
from isaaclab.utils.seed import configure_seed
3737
from isaaclab.utils.timer import Timer
38+
from isaaclab.utils.version import has_kit
3839

3940
from isaaclab_experimental.envs.interactive_scene_warp import InteractiveSceneWarp as InteractiveScene
4041
from isaaclab_experimental.utils.manager_call_switch import ManagerCallMode, ManagerCallSwitch
@@ -161,9 +162,9 @@ def __init__(self, cfg: ManagerBasedEnvCfg):
161162
# viewport is not available in other rendering modes so the function will throw a warning
162163
# FIXME: This needs to be fixed in the future when we unify the UI functionalities even for
163164
# non-rendering modes.
164-
viz_str = self.sim.get_setting("/isaaclab/visualizer") or ""
165-
available_visualizers = [v.strip() for v in viz_str.split(",") if v.strip()]
166-
if "kit" in available_visualizers and bool(viz_str):
165+
# Initialize when a Kit viewport exists. ViewportCameraController uses omni.kit (renderer camera);
166+
# skip in kitless Newton-only runs (e.g. --viz rerun) where no Kit app is running.
167+
if (self.sim.has_gui or self.sim.has_active_visualizers()) and has_kit():
167168
self.viewport_camera_controller = ViewportCameraController(self, self.cfg.viewer)
168169
else:
169170
self.viewport_camera_controller = None
@@ -537,10 +538,8 @@ def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]:
537538
self.recorder_manager.record_pre_step()
538539

539540
# check if we need to do rendering within the physics loop
540-
# note: checked here once to avoid multiple checks within the loop
541-
is_rendering = bool(self.sim.settings.get("/isaaclab/visualizer")) or self.sim.settings.get(
542-
"/isaaclab/render/rtx_sensors"
543-
)
541+
# note: hoisted out of the decimation loop; is_rendering does live settings lookups
542+
is_rendering = self.sim.is_rendering
544543

545544
# perform physics stepping
546545
for _ in range(self.cfg.decimation):

0 commit comments

Comments
 (0)