diff --git a/scripts/demos/arms.py b/scripts/demos/arms.py index b08686a8e52c..7575ddb81a52 100644 --- a/scripts/demos/arms.py +++ b/scripts/demos/arms.py @@ -8,53 +8,55 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/arms.py + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/arms.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/arms.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/arms.py --visualizer newton --physics newton_mjwarp + """ -"""Launch Isaac Sim Simulator first.""" +"""Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" import argparse +from typing import TYPE_CHECKING -from isaaclab.app import AppLauncher +from isaaclab.app import add_launcher_args, launch_simulation -# add argparse arguments -parser = argparse.ArgumentParser(description="This script demonstrates different single-arm manipulators.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) -# demos should open Kit visualizer by default +parser = argparse.ArgumentParser( + description="This script demonstrates different single-arm manipulators.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_launcher_args(parser) 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 -from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR ## # Pre-defined configs ## -# isort: off -from isaaclab_assets import ( - FRANKA_PANDA_CFG, - UR10_CFG, - KINOVA_JACO2_N7S300_CFG, - KINOVA_JACO2_N6S300_CFG, - KINOVA_GEN3_N7_CFG, - SAWYER_CFG, -) +from isaaclab.physics import PhysicsCfg +from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR + +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg # isort:skip +from isaaclab_assets.robots.franka import FRANKA_PANDA_CFG # isort:skip +from isaaclab_assets.robots.kinova import KINOVA_GEN3_N7_CFG, KINOVA_JACO2_N6S300_CFG, KINOVA_JACO2_N7S300_CFG # isort:skip +from isaaclab_assets.robots.sawyer import SAWYER_CFG # isort:skip +from isaaclab_assets.robots.universal_robots import UR10_CFG # isort:skip -# isort: on +if TYPE_CHECKING: + from isaaclab.assets import Articulation def define_origins(num_origins: int, spacing: float) -> list[list[float]]: @@ -93,7 +95,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot franka_arm_cfg = FRANKA_PANDA_CFG.replace(prim_path="/World/Origin1/Robot") franka_arm_cfg.init_state.pos = (0.0, 0.0, 1.05) - franka_panda = Articulation(cfg=franka_arm_cfg) + franka_panda = franka_arm_cfg.class_type(franka_arm_cfg) # Origin 2 with UR10 sim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1]) @@ -105,7 +107,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot ur10_cfg = UR10_CFG.replace(prim_path="/World/Origin2/Robot") ur10_cfg.init_state.pos = (0.0, 0.0, 1.03) - ur10 = Articulation(cfg=ur10_cfg) + ur10 = ur10_cfg.class_type(ur10_cfg) # Origin 3 with Kinova JACO2 (7-Dof) arm sim_utils.create_prim("/World/Origin3", "Xform", translation=origins[2]) @@ -115,7 +117,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot kinova_arm_cfg = KINOVA_JACO2_N7S300_CFG.replace(prim_path="/World/Origin3/Robot") kinova_arm_cfg.init_state.pos = (0.0, 0.0, 0.8) - kinova_j2n7s300 = Articulation(cfg=kinova_arm_cfg) + kinova_j2n7s300 = kinova_arm_cfg.class_type(kinova_arm_cfg) # Origin 4 with Kinova JACO2 (6-Dof) arm sim_utils.create_prim("/World/Origin4", "Xform", translation=origins[3]) @@ -125,7 +127,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot kinova_arm_cfg = KINOVA_JACO2_N6S300_CFG.replace(prim_path="/World/Origin4/Robot") kinova_arm_cfg.init_state.pos = (0.0, 0.0, 0.8) - kinova_j2n6s300 = Articulation(cfg=kinova_arm_cfg) + kinova_j2n6s300 = kinova_arm_cfg.class_type(kinova_arm_cfg) # Origin 5 with Sawyer sim_utils.create_prim("/World/Origin5", "Xform", translation=origins[4]) @@ -135,7 +137,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot kinova_arm_cfg = KINOVA_GEN3_N7_CFG.replace(prim_path="/World/Origin5/Robot") kinova_arm_cfg.init_state.pos = (0.0, 0.0, 1.05) - kinova_gen3n7 = Articulation(cfg=kinova_arm_cfg) + kinova_gen3n7 = kinova_arm_cfg.class_type(kinova_arm_cfg) # Origin 6 with Kinova Gen3 (7-Dof) arm sim_utils.create_prim("/World/Origin6", "Xform", translation=origins[5]) @@ -147,7 +149,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot sawyer_arm_cfg = SAWYER_CFG.replace(prim_path="/World/Origin6/Robot") sawyer_arm_cfg.init_state.pos = (0.0, 0.0, 1.03) - sawyer = Articulation(cfg=sawyer_arm_cfg) + sawyer = sawyer_arm_cfg.class_type(sawyer_arm_cfg) # return the scene information scene_entities = { @@ -161,14 +163,14 @@ 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): +def run_simulator(sim: "sim_utils.SimulationContext", entities: dict[str, "Articulation"], origins: torch.Tensor): """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(): + # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. + while sim.is_headless_or_exist_active_visualizer(): # reset if count % 200 == 0: # reset counters @@ -214,24 +216,34 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula def main(): """Main function.""" - # Initialize the simulation context - sim_cfg = sim_utils.SimulationCfg(device=args_cli.device) - sim = sim_utils.SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view([3.5, 0.0, 3.2], [0.0, 0.0, 0.5]) - # 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 launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # The default newton mjwarp solver configuration needs to be tuned for these arms. + if isinstance(physics_cfg, NewtonCfg) and isinstance(physics_cfg.solver_cfg, MJWarpSolverCfg): + physics_cfg.solver_cfg.njmax = 70 + physics_cfg.solver_cfg.nconmax = 70 + physics_cfg.solver_cfg.ls_iterations = 40 + physics_cfg.solver_cfg.cone = "elliptic" + physics_cfg.solver_cfg.impratio = 100 + physics_cfg.solver_cfg.ls_parallel = False + physics_cfg.solver_cfg.integrator = "implicitfast" + physics_cfg.num_substeps = 2 + + # Initialize the simulation context + sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view([3.5, 0.0, 3.2], [0.0, 0.0, 0.5]) + # 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) if __name__ == "__main__": # run the main function main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/bin_packing.py b/scripts/demos/bin_packing.py index 989c71554285..273f633fecd4 100644 --- a/scripts/demos/bin_packing.py +++ b/scripts/demos/bin_packing.py @@ -13,49 +13,60 @@ .. code-block:: bash - # Usage - ./isaaclab.sh -p scripts/demos/bin_packing.py --num_envs 32 + # Usage with default PhysX physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/bin_packing.py + + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/bin_packing.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/bin_packing.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/bin_packing.py --visualizer newton --physics newton_mjwarp """ from __future__ import annotations -"""Launch Isaac Sim Simulator first.""" - +"""Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" import argparse +from typing import TYPE_CHECKING -from isaaclab.app import AppLauncher +from isaaclab.app import add_launcher_args, launch_simulation -# add argparse arguments -parser = argparse.ArgumentParser(description="Demo usage of RigidObjectCollection through bin packing example") +parser = argparse.ArgumentParser( + description="Demo usage of RigidObjectCollection through bin packing example", + conflict_handler="resolve", +) parser.add_argument("--num_envs", type=int, default=16, help="Number of environments to spawn.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) -# demos should open Kit visualizer by default +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_launcher_args(parser) 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 math import torch +## +# Pre-defined configs +## +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg + import isaaclab.sim as sim_utils import isaaclab.utils.math as math_utils -from isaaclab.assets import AssetBaseCfg, RigidObjectCfg, RigidObjectCollection, RigidObjectCollectionCfg +from isaaclab.assets import AssetBaseCfg, RigidObjectCfg, RigidObjectCollectionCfg +from isaaclab.physics import PhysicsCfg from isaaclab.scene import InteractiveScene, InteractiveSceneCfg -from isaaclab.sim import SimulationContext from isaaclab.utils import Timer from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.configclass import configclass +if TYPE_CHECKING: + from isaaclab.assets import RigidObjectCollection + ## # Scene Configuration ## @@ -131,7 +142,7 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): # Instantiate four grocery variants per layer and replicate across all layers in each environment. rigid_objects={ f"Object_{label}_Layer{layer}": RigidObjectCfg( - prim_path=f"/World/envs/env_.*/Object_{label}_Layer{layer}", + prim_path=f"/World/envs/env_.*/Groceries/Object_{label}_Layer{layer}", init_state=RigidObjectCfg.InitialStateCfg(pos=(x, y, 0.2 + (layer) * 0.2)), spawn=GROCERIES.get(f"OBJECT_{label}"), ) @@ -263,7 +274,7 @@ def build_grocery_defaults( ## -def run_simulator(sim: SimulationContext, scene: InteractiveScene) -> None: +def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene) -> None: """Runs the simulation loop that coordinates spawn randomization and stepping. Returns: @@ -295,8 +306,8 @@ def run_simulator(sim: SimulationContext, scene: InteractiveScene) -> None: # Precompute a helper mask to toggle objects between active and cached sets. # Precompute XY bounds [[x_min,y_min],[x_max,y_max]] bounds_xy = torch.as_tensor(BIN_XY_BOUND, device=device, dtype=spawn_poses_w.dtype) - # Simulation loop - while simulation_app.is_running(): + # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. + while sim.is_headless_or_exist_active_visualizer(): # Reset if count % 250 == 0: # reset counter @@ -343,27 +354,32 @@ def main() -> None: Returns: None: The function drives the simulation for its side-effects. """ - # Load kit helper - sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device) - sim = SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view((2.5, 0.0, 4.0), (0.0, 0.0, 2.0)) - - # Design scene - scene_cfg = MultiObjectSceneCfg(num_envs=args_cli.num_envs, env_spacing=1.0, replicate_physics=True) - with Timer("[INFO] Time to create scene: "): - scene = InteractiveScene(scene_cfg) - - # Play the simulator - sim.reset() - # Now we are ready! - print("[INFO]: Setup complete...") - # Run the simulator - run_simulator(sim, scene) + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # The default newton mjwarp solver configuration needs to be tuned for this demo. + if isinstance(physics_cfg, NewtonCfg) and isinstance(physics_cfg.solver_cfg, MJWarpSolverCfg): + physics_cfg.solver_cfg.nconmax = 128 + physics_cfg.solver_cfg.naconmax = 2048 + physics_cfg.solver_cfg.njmax = 512 + + # Load kit helper + sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view((2.5, 0.0, 4.0), (0.0, 0.0, 2.0)) + + # Design scene + scene_cfg = MultiObjectSceneCfg(num_envs=args_cli.num_envs, env_spacing=1.0, replicate_physics=True) + with Timer("[INFO] Time to create scene: "): + scene = scene_cfg.class_type(scene_cfg) + + # Play the simulator + sim.reset() + # Now we are ready! + print("[INFO]: Setup complete...") + # Run the simulator + run_simulator(sim, scene) if __name__ == "__main__": # run the main execution main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/bipeds.py b/scripts/demos/bipeds.py index 68f79db14fcb..36a51e7def2e 100644 --- a/scripts/demos/bipeds.py +++ b/scripts/demos/bipeds.py @@ -8,47 +8,54 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/bipeds.py + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/bipeds.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/bipeds.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/bipeds.py --visualizer newton --physics newton_mjwarp + """ -"""Launch Isaac Sim Simulator first.""" +"""Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" import argparse +from typing import TYPE_CHECKING -from isaaclab.app import AppLauncher +from isaaclab.app import add_launcher_args, launch_simulation -# add argparse arguments -parser = argparse.ArgumentParser(description="This script demonstrates how to simulate bipedal robots.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) -# demos should open Kit visualizer by default +parser = argparse.ArgumentParser( + description="This script demonstrates how to simulate bipedal robots.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_launcher_args(parser) 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 torch import isaaclab.sim as sim_utils -from isaaclab.assets import Articulation -from isaaclab.sim import SimulationContext ## # Pre-defined configs ## +from isaaclab.physics import PhysicsCfg + +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg # isort:skip from isaaclab_assets.robots.cassie import CASSIE_CFG # isort:skip -from isaaclab_assets import H1_CFG # isort:skip -from isaaclab_assets import G1_CFG # isort:skip +from isaaclab_assets.robots.unitree import G1_CFG, H1_CFG # isort:skip +if TYPE_CHECKING: + from isaaclab.assets import Articulation -def design_scene(sim: sim_utils.SimulationContext) -> tuple[list, torch.Tensor]: + +def design_scene(sim: "sim_utils.SimulationContext") -> tuple[list, torch.Tensor]: """Designs the scene.""" # Ground-plane cfg = sim_utils.GroundPlaneCfg() @@ -67,22 +74,25 @@ def design_scene(sim: sim_utils.SimulationContext) -> tuple[list, torch.Tensor]: ).to(device=sim.device) # Robots - cassie = Articulation(CASSIE_CFG.replace(prim_path="/World/Cassie")) - h1 = Articulation(H1_CFG.replace(prim_path="/World/H1")) - g1 = Articulation(G1_CFG.replace(prim_path="/World/G1")) + cassie_cfg = CASSIE_CFG.replace(prim_path="/World/Cassie") + cassie = cassie_cfg.class_type(cassie_cfg) + h1_cfg = H1_CFG.replace(prim_path="/World/H1") + h1 = h1_cfg.class_type(h1_cfg) + g1_cfg = G1_CFG.replace(prim_path="/World/G1") + g1 = g1_cfg.class_type(g1_cfg) robots = [cassie, h1, g1] return robots, origins -def run_simulator(sim: sim_utils.SimulationContext, robots: list[Articulation], origins: torch.Tensor): +def run_simulator(sim: "sim_utils.SimulationContext", robots: list["Articulation"], origins: torch.Tensor): """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(): + # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. + while sim.is_headless_or_exist_active_visualizer(): # reset if count % 200 == 0: # reset counters @@ -120,27 +130,36 @@ def run_simulator(sim: sim_utils.SimulationContext, robots: list[Articulation], def main(): """Main function.""" - # Load kit helper - sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device) - sim = SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view(eye=[3.0, 0.0, 2.25], target=[0.0, 0.0, 1.0]) - - # design scene - robots, origins = design_scene(sim) - - # Play the simulator - sim.reset() - - # Now we are ready! - print("[INFO]: Setup complete...") - - # Run the simulator - run_simulator(sim, robots, origins) + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # The default newton mjwarp solver configuration needs to be tuned for these bipeds. + if isinstance(physics_cfg, NewtonCfg) and isinstance(physics_cfg.solver_cfg, MJWarpSolverCfg): + physics_cfg.solver_cfg.njmax = 70 + physics_cfg.solver_cfg.nconmax = 70 + physics_cfg.solver_cfg.ls_iterations = 40 + physics_cfg.solver_cfg.cone = "elliptic" + physics_cfg.solver_cfg.impratio = 100 + physics_cfg.solver_cfg.ls_parallel = False + physics_cfg.solver_cfg.integrator = "implicitfast" + physics_cfg.num_substeps = 2 + # Load kit helper + sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view(eye=[3.0, 0.0, 2.25], target=[0.0, 0.0, 1.0]) + + # design scene + robots, origins = design_scene(sim) + + # Play the simulator + sim.reset() + + # Now we are ready! + print("[INFO]: Setup complete...") + + # Run the simulator + run_simulator(sim, robots, origins) if __name__ == "__main__": # run the main function main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/hands.py b/scripts/demos/hands.py index 4b9a12d7b433..9523d5fcde26 100644 --- a/scripts/demos/hands.py +++ b/scripts/demos/hands.py @@ -8,43 +8,53 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/hands.py + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/hands.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/hands.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/hands.py --visualizer newton --physics newton_mjwarp + """ -"""Launch Isaac Sim Simulator first.""" +"""Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" import argparse +from typing import TYPE_CHECKING -from isaaclab.app import AppLauncher +from isaaclab.app import add_launcher_args, launch_simulation -# add argparse arguments -parser = argparse.ArgumentParser(description="This script demonstrates different dexterous hands.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) -# demos should open Kit visualizer by default +parser = argparse.ArgumentParser( + description="This script demonstrates different dexterous hands.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_launcher_args(parser) 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 ## # Pre-defined configs ## +from isaaclab.physics import PhysicsCfg + +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg # isort:skip from isaaclab_assets.robots.allegro import ALLEGRO_HAND_CFG # isort:skip from isaaclab_assets.robots.shadow_hand import SHADOW_HAND_CFG # isort:skip +from isaaclab_tasks.core.shadow_hand.shadow_hand_env_cfg import ShadowHandRobotCfg # isort:skip + +if TYPE_CHECKING: + from isaaclab.assets import Articulation def define_origins(num_origins: int, spacing: float) -> list[list[float]]: @@ -78,12 +88,15 @@ def design_scene() -> tuple[dict, list[list[float]]]: # Origin 1 with Allegro Hand sim_utils.create_prim("/World/Origin1", "Xform", translation=origins[0]) # -- Robot - allegro = Articulation(ALLEGRO_HAND_CFG.replace(prim_path="/World/Origin1/Robot")) + allegro_cfg = ALLEGRO_HAND_CFG.replace(prim_path="/World/Origin1/Robot") + allegro = allegro_cfg.class_type(allegro_cfg) # Origin 2 with Shadow Hand sim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1]) # -- Robot - shadow_hand = Articulation(SHADOW_HAND_CFG.replace(prim_path="/World/Origin2/Robot")) + shadow_hand_cfg = ShadowHandRobotCfg().newton_mjwarp if args_cli.physics == "newton_mjwarp" else SHADOW_HAND_CFG + shadow_hand_cfg = shadow_hand_cfg.replace(prim_path="/World/Origin2/Robot") + shadow_hand = shadow_hand_cfg.class_type(shadow_hand_cfg) # return the scene information scene_entities = { @@ -93,7 +106,7 @@ 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): +def run_simulator(sim: "sim_utils.SimulationContext", entities: dict[str, "Articulation"], origins: torch.Tensor): """Runs the simulation loop.""" # Define simulation stepping sim_dt = sim.get_physics_dt() @@ -101,8 +114,8 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula count = 0 # Start with hand open grasp_mode = 0 - # Simulate physics - while simulation_app.is_running(): + # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. + while sim.is_headless_or_exist_active_visualizer(): # reset if count % 1000 == 0: # reset counters @@ -149,24 +162,34 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Articula def main(): """Main function.""" - # Initialize the simulation context - sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device) - sim = sim_utils.SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view(eye=[0.0, -0.5, 1.5], target=[0.0, -0.2, 0.5]) - # 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 launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # The default newton mjwarp solver configuration needs to be tuned for these hands. + if isinstance(physics_cfg, NewtonCfg) and isinstance(physics_cfg.solver_cfg, MJWarpSolverCfg): + physics_cfg.solver_cfg.njmax = 200 + physics_cfg.solver_cfg.nconmax = 70 + physics_cfg.solver_cfg.impratio = 10.0 + physics_cfg.solver_cfg.cone = "elliptic" + physics_cfg.solver_cfg.update_data_interval = 2 + physics_cfg.solver_cfg.ccd_iterations = 50 + physics_cfg.num_substeps = 2 + physics_cfg.debug_mode = False + + # Initialize the simulation context + sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view(eye=[0.0, -0.5, 1.5], target=[0.0, -0.05, 0.45]) + # 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) if __name__ == "__main__": # run the main execution main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/quadcopter.py b/scripts/demos/quadcopter.py index f3e15ff899d2..2c8215cbc152 100644 --- a/scripts/demos/quadcopter.py +++ b/scripts/demos/quadcopter.py @@ -8,121 +8,123 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/quadcopter.py + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/quadcopter.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/quadcopter.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/quadcopter.py --visualizer newton --physics newton_mjwarp + """ -"""Launch Isaac Sim Simulator first.""" +"""Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" import argparse -from isaaclab.app import AppLauncher +from isaaclab.app import add_launcher_args, launch_simulation -# add argparse arguments -parser = argparse.ArgumentParser(description="This script demonstrates how to simulate a quadcopter.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) -# demos should open Kit visualizer by default +parser = argparse.ArgumentParser( + description="This script demonstrates how to simulate a quadcopter.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_launcher_args(parser) 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 torch import isaaclab.sim as sim_utils -from isaaclab.assets import Articulation -from isaaclab.sim import SimulationContext ## # Pre-defined configs ## +from isaaclab.physics import PhysicsCfg + from isaaclab_assets import CRAZYFLIE_CFG # isort:skip def main(): """Main function.""" - # Load kit helper - sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device) - sim = SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view(eye=[0.5, 0.5, 1.0], target=[0.0, 0.0, 0.5]) - - # Spawn things into stage - # Ground-plane - cfg = sim_utils.GroundPlaneCfg() - cfg.func("/World/defaultGroundPlane", cfg) - # Lights - cfg = sim_utils.DistantLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)) - cfg.func("/World/Light", cfg) - - # Robots - robot_cfg = CRAZYFLIE_CFG.replace(prim_path="/World/Crazyflie") - robot_cfg.spawn.func("/World/Crazyflie", robot_cfg.spawn, translation=robot_cfg.init_state.pos) - - # create handles for the robots - robot = Articulation(robot_cfg) - - # Play the simulator - sim.reset() - - # Fetch relevant parameters to make the quadcopter hover in place - prop_body_ids = robot.find_bodies("m.*_prop")[0] - robot_mass = robot.data.body_mass.torch[0].sum() - gravity = torch.tensor(sim.cfg.gravity, device=sim.device).norm() - - # Now we are ready! - print("[INFO]: Setup complete...") - - # Define simulation stepping - sim_dt = sim.get_physics_dt() - sim_time = 0.0 - count = 0 - # Simulate physics - while simulation_app.is_running(): - # reset - if count % 2000 == 0: - # reset counters - sim_time = 0.0 - count = 0 - # reset dof state - joint_pos, joint_vel = robot.data.default_joint_pos.torch, robot.data.default_joint_vel.torch - robot.write_joint_position_to_sim_index(position=joint_pos) - robot.write_joint_velocity_to_sim_index(velocity=joint_vel) - default_root_pose = robot.data.default_root_pose.torch - robot.write_root_pose_to_sim_index(root_pose=default_root_pose) - default_root_vel = robot.data.default_root_vel.torch - robot.write_root_velocity_to_sim_index(root_velocity=default_root_vel) - robot.reset() - # reset command - print(">>>>>>>> Reset!") - # apply action to the robot (make the robot float in place) - forces = torch.zeros(robot.num_instances, 4, 3, device=sim.device) - torques = torch.zeros_like(forces) - forces[..., 2] = robot_mass * gravity / 4.0 - robot.permanent_wrench_composer.set_forces_and_torques( - forces=forces, - torques=torques, - body_ids=prop_body_ids, - ) - robot.write_data_to_sim() - # perform step - sim.step() - # update sim-time - sim_time += sim_dt - count += 1 - # update buffers - robot.update(sim_dt) + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # Load kit helper + sim_cfg = sim_utils.SimulationCfg(dt=0.005, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view(eye=[0.25, -0.25, 0.7], target=[0.0, 0.0, 0.5]) + + # Spawn things into stage + # Ground-plane + cfg = sim_utils.GroundPlaneCfg() + cfg.func("/World/defaultGroundPlane", cfg) + # Lights + cfg = sim_utils.DistantLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)) + cfg.func("/World/Light", cfg) + + # Robots + robot_cfg = CRAZYFLIE_CFG.replace(prim_path="/World/Crazyflie") + robot_cfg.spawn.func("/World/Crazyflie", robot_cfg.spawn, translation=robot_cfg.init_state.pos) + + # create handles for the robots + robot = robot_cfg.class_type(robot_cfg) + + # Play the simulator + sim.reset() + + # Fetch relevant parameters to make the quadcopter hover in place + prop_body_ids = robot.find_bodies("m.*_prop")[0] + robot_mass = robot.data.body_mass.torch[0].sum() + gravity = torch.tensor(sim.cfg.gravity, device=sim.device).norm() + + # Now we are ready! + print("[INFO]: Setup complete...") + + # Define simulation stepping + sim_dt = sim.get_physics_dt() + sim_time = 0.0 + count = 0 + # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. + while sim.is_headless_or_exist_active_visualizer(): + # reset + if count % 2000 == 0: + # reset counters + sim_time = 0.0 + count = 0 + # reset dof state + joint_pos, joint_vel = robot.data.default_joint_pos.torch, robot.data.default_joint_vel.torch + robot.write_joint_position_to_sim_index(position=joint_pos) + robot.write_joint_velocity_to_sim_index(velocity=joint_vel) + default_root_pose = robot.data.default_root_pose.torch + robot.write_root_pose_to_sim_index(root_pose=default_root_pose) + default_root_vel = robot.data.default_root_vel.torch + robot.write_root_velocity_to_sim_index(root_velocity=default_root_vel) + robot.reset() + # reset command + print(">>>>>>>> Reset!") + # apply action to the robot (make the robot float in place) + forces = torch.zeros(robot.num_instances, 4, 3, device=sim.device) + torques = torch.zeros_like(forces) + forces[..., 2] = robot_mass * gravity / 4.0 + robot.permanent_wrench_composer.set_forces_and_torques_index( + forces=forces, + torques=torques, + body_ids=prop_body_ids, + ) + robot.write_data_to_sim() + # perform step + sim.step() + # update sim-time + sim_time += sim_dt + count += 1 + # update buffers + robot.update(sim_dt) if __name__ == "__main__": # run the main function main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/quadrupeds.py b/scripts/demos/quadrupeds.py index ec3718ce830f..901c91e0b425 100644 --- a/scripts/demos/quadrupeds.py +++ b/scripts/demos/quadrupeds.py @@ -8,12 +8,18 @@ .. code-block:: bash - # Usage with the default PhysX backend (launches Isaac Sim Kit). + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/quadrupeds.py - # Usage with the kit-less Newton (MJWarp) backend. + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/quadrupeds.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/quadrupeds.py --physics newton_mjwarp + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/quadrupeds.py --visualizer newton --physics newton_mjwarp + """ """Parse CLI first so we can decide whether to launch Isaac Sim Kit.""" @@ -21,15 +27,15 @@ import argparse from typing import TYPE_CHECKING +from isaaclab.app import add_launcher_args, launch_simulation + parser = argparse.ArgumentParser( description="This script demonstrates different legged robots.", conflict_handler="resolve", ) -from isaaclab.app import add_launcher_args, launch_simulation - parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") add_launcher_args(parser) -# parse the arguments +parser.set_defaults(visualizer=["kit"]) args_cli = parser.parse_args() import numpy as np @@ -50,7 +56,7 @@ from isaaclab.assets import Articulation -def define_origins(num_origins: int, spacing: float) -> list[list[float]]: +def define_origins(num_origins: int, spacing: float) -> torch.Tensor: """Defines the origins of the scene.""" # create tensor based on number of environments env_origins = torch.zeros(num_origins, 3) @@ -62,10 +68,10 @@ def define_origins(num_origins: int, spacing: float) -> list[list[float]]: env_origins[:, 1] = spacing * yy.flatten()[:num_origins] - spacing * (num_cols - 1) / 2 env_origins[:, 2] = 0.0 # return the origins - return env_origins.tolist() + return env_origins -def design_scene() -> tuple[dict, list[list[float]]]: +def design_scene() -> tuple[dict, torch.Tensor]: """Designs the scene.""" # Ground-plane cfg = sim_utils.GroundPlaneCfg() @@ -83,6 +89,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: # -- Robot anymal_b_cfg = ANYMAL_B_CFG.replace(prim_path="/World/Origin1/Robot") anymal_b = anymal_b_cfg.class_type(anymal_b_cfg) + # Origin 2 with Anymal C sim_utils.create_prim("/World/Origin2", "Xform", translation=origins[1]) # -- Robot @@ -132,18 +139,16 @@ def design_scene() -> tuple[dict, list[list[float]]]: return scene_entities, origins -def run_simulator(sim, entities: dict[str, "Articulation"], origins: torch.Tensor): +def run_simulator(sim: "sim_utils.SimulationContext", entities: dict[str, "Articulation"], origins: torch.Tensor): """Runs the simulation loop.""" # Define simulation stepping sim_dt = sim.get_physics_dt() - sim_time = 0.0 count = 0 # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton. - while not sim.visualizers or any(v.is_running() and not v.is_closed for v in sim.visualizers): - # reset + while sim.is_headless_or_exist_active_visualizer(): + # Reset robots every 200 steps. if count % 200 == 0: # reset counters - sim_time = 0.0 count = 0 # reset robots for index, robot in enumerate(entities.values()): @@ -154,16 +159,14 @@ def run_simulator(sim, entities: dict[str, "Articulation"], origins: torch.Tenso root_vel = robot.data.default_root_vel.torch.clone() robot.write_root_velocity_to_sim_index(root_velocity=root_vel) # joint state - joint_pos, joint_vel = ( - robot.data.default_joint_pos.torch.clone(), - robot.data.default_joint_vel.torch.clone(), - ) + joint_pos = robot.data.default_joint_pos.torch.clone() robot.write_joint_position_to_sim_index(position=joint_pos) + joint_vel = robot.data.default_joint_vel.torch.clone() robot.write_joint_velocity_to_sim_index(velocity=joint_vel) # reset the internal state robot.reset() - print("[INFO]: Resetting robots state...") - # apply default actions to the quadrupedal robots + print("[INFO]: Reset robots' state...") + # Apply default actions to the quadrupedal robots. for robot in entities.values(): # generate random joint positions joint_pos_target = robot.data.default_joint_pos.torch + torch.randn_like(robot.data.joint_pos.torch) * 0.1 @@ -173,8 +176,7 @@ def run_simulator(sim, entities: dict[str, "Articulation"], origins: torch.Tenso robot.write_data_to_sim() # perform step sim.step() - # update sim-time - sim_time += sim_dt + # update counter count += 1 # update buffers for robot in entities.values(): @@ -185,16 +187,11 @@ def main(): """Main function.""" with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: dt = 1 / 200 - sim = sim_utils.SimulationContext( - sim_utils.SimulationCfg( - dt=dt, - device=args_cli.device, - physics=physics_cfg, - ) - ) + sim_cfg: sim_utils.SimulationCfg = sim_utils.SimulationCfg(dt=dt, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) sim.set_camera_view(eye=[2.5, 2.5, 2.5], target=[0.0, 0.0, 0.0]) scene_entities, scene_origins = design_scene() - scene_origins = torch.tensor(scene_origins, device=sim.device) + scene_origins = scene_origins.to(sim.device) sim.reset() print("[INFO]: Setup complete...") run_simulator(sim, scene_entities, scene_origins) diff --git a/source/isaaclab/changelog.d/yizew-support-multi-backends.rst b/source/isaaclab/changelog.d/yizew-support-multi-backends.rst new file mode 100644 index 000000000000..3b07fde871f0 --- /dev/null +++ b/source/isaaclab/changelog.d/yizew-support-multi-backends.rst @@ -0,0 +1,14 @@ +Added +^^^^^ + +* Added :attr:`~isaaclab.scene.InteractiveSceneCfg.class_type` so scene configs + can instantiate custom scene classes. +* Added :meth:`~isaaclab.sim.SimulationContext.is_headless_or_exist_active_visualizer` + to let kitless and external-visualizer demos share a visualizer-aware stepping + condition. + +Changed +^^^^^^^ + +* Updated demo scripts to support selectable PhysX or Newton MJWarp physics + backends and Kit or Newton visualizers. diff --git a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py index 034307017219..be5fa82fd7d0 100644 --- a/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py +++ b/source/isaaclab/isaaclab/scene/interactive_scene_cfg.py @@ -3,10 +3,16 @@ # # SPDX-License-Identifier: BSD-3-Clause +from __future__ import annotations + from dataclasses import MISSING +from typing import TYPE_CHECKING from isaaclab.utils.configclass import configclass +if TYPE_CHECKING: + from .interactive_scene import InteractiveScene + @configclass class InteractiveSceneCfg: @@ -67,6 +73,12 @@ class MySceneCfg(InteractiveSceneCfg): """ + class_type: type[InteractiveScene] | str = "{DIR}.interactive_scene:InteractiveScene" + """The class to use for the interactive scene. + + Defaults to :class:`isaaclab.scene.InteractiveScene`. + """ + num_envs: int = MISSING """Number of environment instances handled by the scene.""" diff --git a/source/isaaclab/isaaclab/sim/simulation_context.py b/source/isaaclab/isaaclab/sim/simulation_context.py index c068469ec599..8b1400b6d8ed 100644 --- a/source/isaaclab/isaaclab/sim/simulation_context.py +++ b/source/isaaclab/isaaclab/sim/simulation_context.py @@ -391,6 +391,10 @@ def has_active_visualizers(self) -> bool: self.get_setting("/isaaclab/video/auto_start_kit") ) + def is_headless_or_exist_active_visualizer(self) -> bool: + """Return whether the simulation should keep stepping without visualizers or with an active visualizer.""" + return not self._visualizers or any(viz.is_running() and not viz.is_closed for viz in self._visualizers) + def can_render_rgb_array(self) -> bool: """Return whether rgb-array rendering is currently available.""" return self.has_gui or self.has_offscreen_render or self.has_active_visualizers() diff --git a/source/isaaclab_visualizers/changelog.d/yizew-support-multi-backends.rst b/source/isaaclab_visualizers/changelog.d/yizew-support-multi-backends.rst new file mode 100644 index 000000000000..7a1cd353d7ec --- /dev/null +++ b/source/isaaclab_visualizers/changelog.d/yizew-support-multi-backends.rst @@ -0,0 +1,6 @@ +Added +^^^^^ + +* Added :meth:`~isaaclab_visualizers.newton.NewtonVisualizer.set_camera_view` so + the Newton visualizer follows :meth:`~isaaclab.sim.SimulationContext.set_camera_view` + camera updates. diff --git a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py index 3dbecdec794f..7a8ecfc2bb2b 100644 --- a/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py +++ b/source/isaaclab_visualizers/isaaclab_visualizers/newton/newton_visualizer.py @@ -742,6 +742,20 @@ def _apply_camera_focal_length(self) -> None: return self._viewer.camera.fov = self._focal_length_to_vertical_fov_degrees() + def set_camera_view( + self, eye: tuple[float, float, float] | list[float], target: tuple[float, float, float] | list[float] + ) -> None: + """Set active viewer camera eye/target. + + Args: + eye: Camera eye position. + target: Camera look-at target. + """ + if not self._is_initialized: + logger.debug("[NewtonVisualizer] set_camera_view() ignored because visualizer is not initialized.") + return + self._apply_camera_pose((tuple(eye), tuple(target))) + def supports_markers(self) -> bool: """Newton OpenGL viewer supports Isaac Lab markers through viewer-side meshes and lines.""" return bool(self.cfg.enable_markers)