-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Support Demo Scripts With Multiple Backends #5771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
05b1434
c73177b
1beee90
e85e64a
35b051d
34a07b1
018a1e6
9181225
a427283
b20cdab
5e57982
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| # 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 | ||
|
|
||
| """ | ||
| This script contains helper functions for the demos. | ||
| """ | ||
|
|
||
| import textwrap | ||
| from contextlib import contextmanager | ||
|
|
||
|
|
||
| @contextmanager | ||
| def resolve_backend_and_visualizer(args, physx_cfg=None, newton_cfg=None): | ||
| """Resolve physics + visualizer cfgs from ``--physics`` / ``--visualizer``. | ||
|
|
||
| Yields ``(physics_cfg, visualizer_cfg)``. Kit is launched when required by | ||
| either the visualizer or the physics backend, and closed automatically on exit. | ||
| """ | ||
|
|
||
| # Resolve physics cfg | ||
| if args.physics == "physx": | ||
| from isaaclab_physx.physics import PhysxCfg | ||
|
|
||
| physics_cfg = physx_cfg or PhysxCfg() | ||
| elif args.physics == "newton_mjwarp": | ||
| from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg | ||
|
|
||
| DEFAULT_NEWTON_CFG = NewtonCfg( | ||
| solver_cfg=MJWarpSolverCfg( | ||
| njmax=70, | ||
| nconmax=70, | ||
| ls_iterations=40, | ||
| cone="elliptic", | ||
| impratio=100, | ||
| ls_parallel=False, | ||
| integrator="implicitfast", | ||
| ), | ||
| num_substeps=2, | ||
| ) | ||
| physics_cfg = newton_cfg or DEFAULT_NEWTON_CFG | ||
| else: | ||
| raise ValueError(f"Unsupported --physics value: {args.physics}") | ||
|
|
||
| # Resolve visualizer cfg | ||
| if not isinstance(args.visualizer, list) or len(args.visualizer) != 1: | ||
| raise ValueError("Demos support exactly one --visualizer value: kit or newton.") | ||
| viz_type = args.visualizer[0] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we only parse the first element? is there a limitation to supporting multiple visualizers like the training scripts?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not that we expect multiple visualizer. Let me take a look at training scripts and get back to you. |
||
| if viz_type == "kit": | ||
| from isaaclab_visualizers.kit import KitVisualizerCfg | ||
|
|
||
| visualizer_cfg = KitVisualizerCfg() | ||
| elif viz_type == "newton": | ||
| from isaaclab_visualizers.newton import NewtonVisualizerCfg | ||
|
|
||
| visualizer_cfg = NewtonVisualizerCfg() | ||
| else: | ||
| raise ValueError(f"Unsupported --visualizer value: {viz_type}") | ||
|
|
||
| # PhysX requires Isaac Sim Kit extensions even when rendering through a | ||
| # standalone visualizer such as Newton. | ||
| close_fn = None | ||
| if viz_type == "kit" or args.physics == "physx": | ||
| from isaaclab.app import AppLauncher | ||
|
|
||
| args.visualizer = [viz_type] | ||
| close_fn = AppLauncher(args).app.close | ||
|
|
||
| try: | ||
| yield physics_cfg, visualizer_cfg | ||
| finally: | ||
| # No-op for the newton visualizer; close Kit automatically upon exit | ||
| if close_fn is not None: | ||
| close_fn() | ||
|
|
||
|
|
||
| def has_no_alive_visualizer_window(sim) -> bool: | ||
| """Check if there are no alive visualizer windows.""" | ||
| visualizers = sim.visualizers or () | ||
| return not any(v.is_running() and not v.is_closed for v in visualizers) | ||
|
|
||
|
|
||
| def get_usage_examples(doc: str) -> str: | ||
| """Return usage examples from the module docstring.""" | ||
| marker = ".. code-block:: bash" | ||
| if marker not in doc: | ||
| return "" | ||
|
|
||
| examples = textwrap.dedent(doc.split(marker, maxsplit=1)[1]).strip() | ||
| return "Examples:\n" + textwrap.indent(examples, " ") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,45 +8,50 @@ | |
|
|
||
| .. code-block:: bash | ||
|
|
||
| # Usage | ||
| # Usage with the default PhysX backend (launches Isaac Sim Kit by default). | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --visualizer kit | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics physx | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics physx --visualizer kit | ||
|
|
||
| # Usage with the kit-less Newton (MJWarp) backend (launches Isaac Sim Kit by default). | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics newton_mjwarp | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics newton_mjwarp --visualizer kit | ||
|
|
||
| # Usage with the Newton (MJWarp) backend without Kit (launches Newton visualizer). | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics newton_mjwarp --visualizer newton | ||
|
|
||
| # Usage with the PhysX backend and Newton visualizer. | ||
| # PhysX still launches Isaac Sim Kit headless because it depends on Kit extensions. | ||
| ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics physx --visualizer newton | ||
|
Comment on lines
+11
to
+26
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be simplified a bit to avoid duplicates
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. Tried to exhaust all combinations. I can reduce to the minimal. |
||
|
|
||
| """ | ||
|
|
||
| """Launch Isaac Sim Simulator first.""" | ||
| """Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" | ||
|
|
||
| import argparse | ||
|
|
||
| from demo_helper import get_usage_examples, has_no_alive_visualizer_window, resolve_backend_and_visualizer | ||
|
|
||
| from isaaclab.app import AppLauncher | ||
|
|
||
| # add argparse arguments | ||
| parser = argparse.ArgumentParser(description="This script demonstrates different legged robots.") | ||
| parser = argparse.ArgumentParser( | ||
| description="This script demonstrates different legged robots.", | ||
| epilog=get_usage_examples(str(__doc__)), | ||
| formatter_class=argparse.RawDescriptionHelpFormatter, | ||
| ) | ||
| parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") | ||
| # append AppLauncher cli args | ||
| AppLauncher.add_app_launcher_args(parser) | ||
| # demos should open Kit visualizer by default | ||
| parser.set_defaults(visualizer=["kit"]) | ||
| # parse the arguments | ||
| args_cli = parser.parse_args() | ||
|
|
||
| # launch omniverse app | ||
| app_launcher = AppLauncher(args_cli) | ||
| simulation_app = app_launcher.app | ||
|
|
||
| """Rest everything follows.""" | ||
|
|
||
| import numpy as np | ||
| import torch | ||
|
|
||
| import isaaclab.sim as sim_utils | ||
| from isaaclab.assets import Articulation | ||
|
hujc7 marked this conversation as resolved.
|
||
|
|
||
| ## | ||
| # Pre-defined configs | ||
| ## | ||
| from isaaclab_assets.robots.anymal import ANYMAL_B_CFG, ANYMAL_C_CFG, ANYMAL_D_CFG # isort:skip | ||
| from isaaclab_assets.robots.spot import SPOT_CFG # isort:skip | ||
| from isaaclab_assets.robots.unitree import UNITREE_A1_CFG, UNITREE_GO1_CFG, UNITREE_GO2_CFG # isort:skip | ||
|
|
||
|
|
||
| def define_origins(num_origins: int, spacing: float) -> list[list[float]]: | ||
| """Defines the origins of the scene.""" | ||
|
|
@@ -65,6 +70,12 @@ def define_origins(num_origins: int, spacing: float) -> list[list[float]]: | |
|
|
||
| def design_scene() -> tuple[dict, list[list[float]]]: | ||
| """Designs the scene.""" | ||
| import isaaclab.sim as sim_utils | ||
| from isaaclab.assets import Articulation | ||
| from isaaclab_assets.robots.anymal import ANYMAL_B_CFG, ANYMAL_C_CFG, ANYMAL_D_CFG # isort:skip | ||
| from isaaclab_assets.robots.spot import SPOT_CFG # isort:skip | ||
| from isaaclab_assets.robots.unitree import UNITREE_A1_CFG, UNITREE_GO1_CFG, UNITREE_GO2_CFG # isort:skip | ||
|
|
||
| # Ground-plane | ||
| cfg = sim_utils.GroundPlaneCfg() | ||
| cfg.func("/World/defaultGroundPlane", cfg) | ||
|
|
@@ -124,14 +135,15 @@ def design_scene() -> tuple[dict, list[list[float]]]: | |
| return scene_entities, origins | ||
|
|
||
|
|
||
| def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articulation], origins: torch.Tensor): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason to remove the type hint here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same reason as my last comment: https://github.com/isaac-sim/IsaacLab/pull/5771/changes#r3310474409. These types are not defined yet (these packages not imported). Let me know if my understanding in the previous comment is wrong.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will update this. |
||
| def run_simulator(sim, entities: dict, origins): | ||
| """Runs the simulation loop.""" | ||
| # Define simulation stepping | ||
| sim_dt = sim.get_physics_dt() | ||
| sim_time = 0.0 | ||
| count = 0 | ||
| # Simulate physics | ||
| while simulation_app.is_running(): | ||
| # Exit when every visualizer window has been closed (works for kit and newton) | ||
| while not has_no_alive_visualizer_window(sim): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the double negative can be a bit confusing. would it make sense to make the utility has_alive_visualizer_window instead?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
| # reset | ||
| if count % 200 == 0: | ||
| # reset counters | ||
|
|
@@ -175,24 +187,29 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula | |
|
|
||
| def main(): | ||
| """Main function.""" | ||
|
|
||
| # Initialize the simulation context | ||
| sim = sim_utils.SimulationContext(sim_utils.SimulationCfg(dt=0.01)) | ||
| # Set main camera | ||
| sim.set_camera_view(eye=[2.5, 2.5, 2.5], target=[0.0, 0.0, 0.0]) | ||
| # design scene | ||
| scene_entities, scene_origins = design_scene() | ||
| scene_origins = torch.tensor(scene_origins, device=sim.device) | ||
| # Play the simulator | ||
| sim.reset() | ||
| # Now we are ready! | ||
| print("[INFO]: Setup complete...") | ||
| # Run the simulator | ||
| run_simulator(sim, scene_entities, scene_origins) | ||
| with resolve_backend_and_visualizer(args_cli) as (physics_cfg, visualizer_cfg): | ||
| import isaaclab.sim as sim_utils | ||
|
|
||
| # define simulation configuration | ||
| sim_cfg = sim_utils.SimulationCfg( | ||
| dt=0.005, device=args_cli.device, physics=physics_cfg, visualizer_cfgs=[visualizer_cfg] | ||
| ) | ||
| # Initialize the simulation context | ||
| sim = sim_utils.SimulationContext(sim_cfg) | ||
| # Set main camera | ||
| sim.set_camera_view(eye=[2.5, 2.5, 2.5], target=[0.0, 0.0, 0.0]) | ||
| # design scene | ||
| scene_entities, scene_origins = design_scene() | ||
| # convert origins to tensor | ||
| scene_origins = torch.tensor(scene_origins, device=sim.device) | ||
| # Play the simulator | ||
| sim.reset() | ||
| # Now we are ready! | ||
| print("[INFO]: Setup complete...") | ||
| # Run the simulator | ||
| run_simulator(sim, scene_entities, scene_origins) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| # run the main function | ||
| main() | ||
| # close sim app | ||
| simulation_app.close() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how generalizable is this cfg? do we expect this to work for most scripts? if not, it may be better to just keep this as a default MjWarpSolverCfg() object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This currently works for most scripts while the default one barely works.