Skip to content

Commit 86102b5

Browse files
kellyguo11matthewtreptegreptile-apps[bot]AntoineRichard
authored
[Newton] Adding visualizer support for Newton (#4099)
# Description Building on top of #3979 from @matthewtrepte > Initial design to support multiple visualizers with Isaac Lab 3.0 > > The visualizers > > OV Visualizer > Newton OpenGL Visualizer > Newton Rerun Visualizer > Each visualizer comes with a config class. > > CLI newton_visualizer argument is removed. > > Discussion on how we can best enable visualizer with CLI arg is ongoing. > Currently, visualizers can be selected in the code by visualizer_cfgs in simulation_cfg > and the --headless arg disables any selected visualizer > Also there is a new abstraction called scene data provider which manages per step data flow to visualizers. > This is a pretty thin abstraction right now and subject to change with the renderers design and ov sdk. Adds additional `--visualizer` flag for specifying which visualizer(s) to enable. Updates numpy to >=2 to support new rerun package that resolves a flashing screen issue. Introduces default visualizer configs in the simulation config so that users can launch any visualizer without explicitly adding new configs into the environments. Example: > ./isaaclab.sh -p scripts/reinforcement_learning/rsl_rl/train.py --task Isaac-Cartpole-Direct-v0 --num_envs 4096 --headless --max_iterations 300 --visualizer newton ## Type of change - New feature (non-breaking change which adds functionality) - Breaking change (existing functionality will not work without user modification) --------- Signed-off-by: matthewtrepte <mtrepte@nvidia.com> Signed-off-by: Kelly Guo <kellyg@nvidia.com> Co-authored-by: mtrepte <mtrepte@nvidia.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Antoine Richard <antoiner@nvidia.com>
1 parent 14c7936 commit 86102b5

File tree

8 files changed

+313
-58
lines changed

8 files changed

+313
-58
lines changed

source/isaaclab/isaaclab/app/app_launcher.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ def __init__(self, launcher_args: argparse.Namespace | dict | None = None, **kwa
115115
self._livestream: Literal[0, 1, 2] # 0: Disabled, 1: WebRTC public, 2: WebRTC private
116116
self._offscreen_render: bool # 0: Disabled, 1: Enabled
117117
self._sim_experience_file: str # Experience file to load
118+
self._visualizer: (
119+
list[str] | None
120+
) # Visualizer backends to use: None or list of ["rerun", "newton", "omniverse"]
118121

119122
# Exposed to train scripts
120123
self.device_id: int # device ID for GPU simulation (defaults to 0)
@@ -174,6 +177,16 @@ def app(self) -> SimulationApp:
174177
else:
175178
raise RuntimeError("The `AppLauncher.app` member cannot be retrieved until the class is initialized.")
176179

180+
@property
181+
def visualizer(self) -> list[str] | None:
182+
"""The visualizer backend(s) to use.
183+
184+
Returns:
185+
List of visualizer backend names (e.g., ["rerun", "newton"]) or None if no visualizers specified.
186+
Empty list means no visualizers should be initialized.
187+
"""
188+
return self._visualizer
189+
177190
"""
178191
Operations.
179192
"""
@@ -226,6 +239,21 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
226239
Arguments should be combined into a single string separated by space.
227240
Example usage: --kit_args "--ext-folder=/path/to/ext1 --ext-folder=/path/to/ext2"
228241
242+
* ``visualizer`` (list[str]): The visualizer backend(s) to use for the simulation.
243+
Valid options are:
244+
245+
- ``rerun``: Use Rerun visualizer.
246+
- ``newton``: Use Newton visualizer.
247+
- ``omniverse``: Use Omniverse visualizer.
248+
- Multiple visualizers can be specified: ``--visualizer rerun newton``
249+
- If not specified (default), NO visualizers will be initialized and headless mode is auto-enabled.
250+
251+
Note: If visualizer configs are not defined in the simulation config, default configs will be
252+
automatically created with all default parameters.
253+
If --headless is specified, it takes precedence and NO visualizers will be initialized.
254+
When omniverse visualizer is specified, the app will launch in non-headless mode automatically.
255+
When only non-GUI visualizers (rerun, newton) are specified, headless mode is auto-enabled.
256+
229257
Args:
230258
parser: An argument parser instance to be extended with the AppLauncher specific options.
231259
"""
@@ -362,6 +390,19 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
362390
" exceeded, then the animation is not recorded."
363391
),
364392
)
393+
arg_group.add_argument(
394+
"--visualizer",
395+
type=str,
396+
nargs="*",
397+
default=None,
398+
help=(
399+
"The visualizer backend(s) to use for the simulation."
400+
' Can be one or more of: "rerun", "newton", "omniverse".'
401+
" Multiple visualizers can be specified: --visualizer rerun newton."
402+
" If not specified (default), NO visualizers will be created and headless mode is auto-enabled."
403+
" If no visualizer configs are defined in sim config, default configs will be created automatically."
404+
),
405+
)
365406
# special flag for backwards compatibility
366407

367408
# Corresponding to the beginning of the function,
@@ -382,6 +423,7 @@ def add_app_launcher_args(parser: argparse.ArgumentParser) -> None:
382423
"device": ([str], "cuda:0"),
383424
"experience": ([str], ""),
384425
"rendering_mode": ([str], "balanced"),
426+
"visualizer": ([list, type(None)], None),
385427
}
386428
"""A dictionary of arguments added manually by the :meth:`AppLauncher.add_app_launcher_args` method.
387429
@@ -479,6 +521,7 @@ def _config_resolution(self, launcher_args: dict):
479521
# Handle core settings
480522
livestream_arg, livestream_env = self._resolve_livestream_settings(launcher_args)
481523
self._resolve_headless_settings(launcher_args, livestream_arg, livestream_env)
524+
self._resolve_visualizer_settings(launcher_args)
482525
self._resolve_camera_settings(launcher_args)
483526
self._resolve_xr_settings(launcher_args)
484527
self._resolve_viewport_settings(launcher_args)
@@ -570,9 +613,16 @@ def _resolve_headless_settings(self, launcher_args: dict, livestream_arg: int, l
570613
raise ValueError(
571614
f"Invalid value for environment variable `HEADLESS`: {headless_env} . Expected: {headless_valid_vals}."
572615
)
616+
617+
# Check if visualizers are requested and if omniverse visualizer is among them
618+
visualizers = launcher_args.get("visualizer", AppLauncher._APPLAUNCHER_CFG_INFO["visualizer"][1])
619+
visualizers_requested = visualizers is not None and len(visualizers) > 0
620+
omniverse_visualizer_requested = visualizers_requested and "omniverse" in visualizers
621+
573622
# We allow headless kwarg to supersede HEADLESS envvar if headless_arg does not have the default value
574623
# Note: Headless is always true when livestreaming
575624
if headless_arg is True:
625+
# User explicitly requested headless mode
576626
self._headless = headless_arg
577627
elif self._livestream in {1, 2}:
578628
# we are always headless on the host machine
@@ -588,12 +638,56 @@ def _resolve_headless_settings(self, launcher_args: dict, livestream_arg: int, l
588638
f"[INFO][AppLauncher]: Environment variable `LIVESTREAM={self._livestream}` has implicitly"
589639
f" overridden the environment variable `HEADLESS={headless_env}` to True."
590640
)
641+
elif omniverse_visualizer_requested and headless_env == 0:
642+
# Omniverse visualizer requires non-headless mode (needs Isaac Sim viewport)
643+
self._headless = False
644+
print(
645+
"[INFO][AppLauncher]: Omniverse visualizer requested via --visualizer flag. "
646+
"Launching in non-headless mode to enable viewport visualization."
647+
)
648+
elif visualizers_requested and not omniverse_visualizer_requested and headless_env == 0:
649+
# Newton and Rerun don't need Isaac Sim viewport (they create their own windows or are web-based)
650+
self._headless = True
651+
print(
652+
f"[INFO][AppLauncher]: Visualizer(s) {visualizers} requested. "
653+
"Enabling headless mode (Isaac Sim viewport disabled)."
654+
)
655+
elif not visualizers_requested and headless_env == 0:
656+
# No visualizers requested and headless not explicitly set -> enable headless mode
657+
self._headless = True
658+
print(
659+
"[INFO][AppLauncher]: No visualizers specified via --visualizer flag. "
660+
"Automatically enabling headless mode. Use --visualizer <type> to enable GUI."
661+
)
591662
else:
592663
# Headless needs to be a bool to be ingested by SimulationApp
593664
self._headless = bool(headless_env)
594665
# Headless needs to be passed to the SimulationApp so we keep it here
595666
launcher_args["headless"] = self._headless
596667

668+
def _resolve_visualizer_settings(self, launcher_args: dict):
669+
"""Resolve visualizer related settings."""
670+
# Get visualizer setting from launcher_args
671+
visualizers = launcher_args.get("visualizer", AppLauncher._APPLAUNCHER_CFG_INFO["visualizer"][1])
672+
673+
# Validate visualizer names
674+
valid_visualizers = ["rerun", "newton", "omniverse"]
675+
if visualizers is not None and len(visualizers) > 0:
676+
invalid = [v for v in visualizers if v not in valid_visualizers]
677+
if invalid:
678+
raise ValueError(f"Invalid visualizer(s) specified: {invalid}. Valid options are: {valid_visualizers}")
679+
680+
# Store visualizer setting for later use
681+
# Convert empty list to None for consistency
682+
self._visualizer = visualizers if visualizers and len(visualizers) > 0 else None
683+
684+
# Check if both headless and visualizer are specified
685+
if self._headless and self._visualizer is not None:
686+
print(
687+
f"[WARN][AppLauncher]: Both headless mode and visualizer(s) {self._visualizer} were specified."
688+
" Headless mode is enabled, so no visualizers will be initialized."
689+
)
690+
597691
def _resolve_camera_settings(self, launcher_args: dict):
598692
"""Resolve camera related settings."""
599693
enable_cameras_env = int(os.environ.get("ENABLE_CAMERAS", 0))
@@ -831,6 +925,14 @@ def _load_extensions(self):
831925
# for example: the `Camera` sensor class
832926
carb_settings_iface.set_bool("/isaaclab/render/rtx_sensors", False)
833927

928+
# set carb setting to store the visualizer backend(s) specified via command-line
929+
# this allows SimulationContext to filter visualizers based on the --visualizer flag
930+
# Store as comma-separated string for carb settings compatibility
931+
if self._visualizer is not None:
932+
carb_settings_iface.set_string("/isaaclab/visualizer", ",".join(self._visualizer))
933+
else:
934+
carb_settings_iface.set_string("/isaaclab/visualizer", "")
935+
834936
# set fabric update flag to disable updating transforms when rendering is disabled
835937
carb_settings_iface.set_bool("/physics/fabricUpdateTransformations", self._rendering_enabled())
836938

source/isaaclab/isaaclab/sim/simulation_cfg.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from typing import Literal
1313

1414
from isaaclab.utils import configclass
15-
from isaaclab.visualizers import NewtonVisualizerCfg, OVVisualizerCfg, RerunVisualizerCfg, VisualizerCfg # noqa: F401
15+
from isaaclab.visualizers import VisualizerCfg
1616

1717
from ._impl.newton_manager_cfg import NewtonCfg
1818
from .spawners.materials import RigidBodyMaterialCfg
@@ -216,7 +216,7 @@ class SimulationCfg:
216216
# Disable all visualizers
217217
cfg.sim.visualizer_cfgs = []
218218
219-
# Use default visualizer (NewtonVisualizerCfg)
219+
# No visualizers (default behavior)
220220
cfg = SimulationCfg()
221221
222222
# Single custom visualizer

0 commit comments

Comments
 (0)