diff --git a/scripts/demos/arl_robot_1.py b/scripts/demos/arl_robot_1.py index 987e6be6b2ec..bc9f368d11c4 100644 --- a/scripts/demos/arl_robot_1.py +++ b/scripts/demos/arl_robot_1.py @@ -3,37 +3,42 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -Script to view ARL Robot 1. +"""Script to view ARL Robot 1. + +.. code-block:: bash + + # Usage with default PhysX physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/arl_robot_1.py + + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/arl_robot_1.py --visualizer newton -Launch Isaac Sim Simulator first. """ -# Create argparser +"""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 -parser = argparse.ArgumentParser(description="View ARL Robot 1 with Lee Position Controller.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) +parser = argparse.ArgumentParser( + description="View ARL Robot 1 with Lee Position Controller.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx"], help="Physics backend.") +add_launcher_args(parser) +parser.set_defaults(visualizer=["kit"]) args_cli = parser.parse_args() -# launch omniverse app -app_launcher = AppLauncher(args_cli) -simulation_app = app_launcher.app - -"""Rest everything follows.""" - import torch -import omni.usd -from pxr import Gf, UsdLux - import isaaclab.sim as sim_utils -from isaaclab.sim import SimulationContext -from isaaclab_contrib.assets import Multirotor +## +# Pre-defined configs +## +from isaaclab.physics import PhysicsCfg + from isaaclab_contrib.controllers.lee_position_control import LeePosController from isaaclab_contrib.controllers.lee_position_control_cfg import LeePosControllerCfg @@ -42,72 +47,68 @@ def main(): """Main function to spawn arl_robot_1.""" - - # Create simulation context - sim_cfg = sim_utils.SimulationCfg(dt=0.01) - sim = SimulationContext(sim_cfg) - - # Create a dome light with light blue color - stage = omni.usd.get_context().get_stage() - dome_light = UsdLux.DomeLight.Define(stage, "/World/DomeLight") - dome_light.CreateColorAttr(Gf.Vec3f(0.53, 0.81, 0.92)) # Light blue - dome_light.CreateIntensityAttr(1000.0) - - # Spawn ground plane - cfg = sim_utils.GroundPlaneCfg() - cfg.func("/World/defaultGroundPlane", cfg) - - # Spawn robot - robot_cfg = ARL_ROBOT_1_CFG.replace(prim_path="/World/Robot") - robot_cfg.actuators["thrusters"].dt = sim_cfg.dt - robot = Multirotor(robot_cfg) - - # Play the simulator - sim.reset() - - # Create Lee position controller - controller_cfg = LeePosControllerCfg( - K_pos_range=((2.5, 2.5, 1.5), (3.5, 3.5, 2.0)), - K_vel_range=((2.5, 2.5, 1.5), (3.5, 3.5, 2.0)), - K_rot_range=((1.6, 1.6, 0.25), (1.85, 1.85, 0.4)), - K_angvel_range=((0.4, 0.4, 0.075), (0.5, 0.5, 0.09)), - max_inclination_angle_rad=1.0471975511965976, - max_yaw_rate=1.0471975511965976, - ) - controller = LeePosController(controller_cfg, robot, num_envs=1, device=str(sim.device)) - - # Get allocation matrix and compute pseudoinverse - allocation_matrix = torch.tensor(robot_cfg.allocation_matrix, device=sim.device, dtype=torch.float32) - # allocation_matrix is (6, num_thrusters), we need pseudoinverse for wrench -> thrust - alloc_pinv = torch.linalg.pinv(allocation_matrix) # Shape: (num_thrusters, 6) - - # Position command: hover in place (zero position, zero yaw) - pos_command = torch.zeros((1, 4), device=sim.device) # [x, y, z, yaw] - pos_command[0, 2] = 1.0 # Hover at 1 meter height - - # Simulation loop - print("[INFO] Starting demo with Lee Position Controller. Press Ctrl+C to stop.") - - while simulation_app.is_running(): - # Compute wrench from velocity controller - wrench = controller.compute(pos_command) # Shape: (1, 6) - - # Allocate wrench to thrusters: thrust = pinv(A) @ wrench - thrust_cmd = torch.matmul(wrench, alloc_pinv.T) # Shape: (1, num_thrusters) - thrust_cmd = thrust_cmd.clamp(min=0.0) # Ensure non-negative thrust - - # Apply thrust - robot.set_thrust_target(thrust_cmd) - - # Step simulation - robot.write_data_to_sim() - sim.step() - - # Update robot - robot.update(sim_cfg.dt) - - # Cleanup - simulation_app.close() + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # Create simulation context + sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + + # Create a dome light with light blue color + light_cfg = sim_utils.DomeLightCfg(intensity=1000.0, color=(0.53, 0.81, 0.92)) + light_cfg.func("/World/DomeLight", light_cfg) + + # Spawn ground plane + ground_cfg = sim_utils.GroundPlaneCfg() + ground_cfg.func("/World/defaultGroundPlane", ground_cfg) + + # Spawn robot + robot_cfg = ARL_ROBOT_1_CFG.replace(prim_path="/World/Robot") + robot_cfg.actuators["thrusters"].dt = sim_cfg.dt + robot = robot_cfg.class_type(robot_cfg) + + # Play the simulator + sim.reset() + + # Create Lee position controller + controller_cfg = LeePosControllerCfg( + K_pos_range=((2.5, 2.5, 1.5), (3.5, 3.5, 2.0)), + K_vel_range=((2.5, 2.5, 1.5), (3.5, 3.5, 2.0)), + K_rot_range=((1.6, 1.6, 0.25), (1.85, 1.85, 0.4)), + K_angvel_range=((0.4, 0.4, 0.075), (0.5, 0.5, 0.09)), + max_inclination_angle_rad=1.0471975511965976, + max_yaw_rate=1.0471975511965976, + ) + controller = LeePosController(controller_cfg, robot, num_envs=1, device=str(sim.device)) + + # Get allocation matrix and compute pseudoinverse + allocation_matrix = torch.tensor(robot_cfg.allocation_matrix, device=sim.device, dtype=torch.float32) + # allocation_matrix is (6, num_thrusters), we need pseudoinverse for wrench -> thrust + alloc_pinv = torch.linalg.pinv(allocation_matrix) # Shape: (num_thrusters, 6) + + # Position command: hover in place (zero position, zero yaw) + pos_command = torch.zeros((1, 4), device=sim.device) # [x, y, z, yaw] + pos_command[0, 2] = 1.0 # Hover at 1 meter height + + # Simulation loop + print("[INFO] Starting demo with Lee Position Controller. Press Ctrl+C to stop.") + + # 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(): + # Compute wrench from position controller + wrench = controller.compute(pos_command) # Shape: (1, 6) + + # Allocate wrench to thrusters: thrust = pinv(A) @ wrench + thrust_cmd = torch.matmul(wrench, alloc_pinv.T) # Shape: (1, num_thrusters) + thrust_cmd = thrust_cmd.clamp(min=0.0) # Ensure non-negative thrust + + # Apply thrust + robot.set_thrust_target(thrust_cmd) + + # Step simulation + robot.write_data_to_sim() + sim.step() + + # Update robot + robot.update(sim_cfg.dt) if __name__ == "__main__": diff --git a/scripts/demos/arms.py b/scripts/demos/arms.py index 7575ddb81a52..edaa0a7f0684 100644 --- a/scripts/demos/arms.py +++ b/scripts/demos/arms.py @@ -3,8 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -This script demonstrates different single-arm manipulators. +"""This script demonstrates different single-arm manipulators. .. code-block:: bash @@ -60,7 +59,7 @@ def define_origins(num_origins: int, spacing: float) -> list[list[float]]: - """Defines the origins of the the scene.""" + """Defines the origins of the scene.""" # create tensor based on number of environments env_origins = torch.zeros(num_origins, 3) # create a grid of origins diff --git a/scripts/demos/bin_packing.py b/scripts/demos/bin_packing.py index 273f633fecd4..ba7fb6e1e5f3 100644 --- a/scripts/demos/bin_packing.py +++ b/scripts/demos/bin_packing.py @@ -50,13 +50,12 @@ import torch +import isaaclab.sim as sim_utils +import isaaclab.utils.math as math_utils + ## # 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, RigidObjectCollectionCfg from isaaclab.physics import PhysicsCfg from isaaclab.scene import InteractiveScene, InteractiveSceneCfg @@ -64,6 +63,8 @@ from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.configclass import configclass +from isaaclab_newton.physics import MJWarpSolverCfg, NewtonCfg # isort:skip + if TYPE_CHECKING: from isaaclab.assets import RigidObjectCollection @@ -348,7 +349,7 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene) -> scene.update(sim_dt) -def main() -> None: +def main(): """Main function. Returns: diff --git a/scripts/demos/bipeds.py b/scripts/demos/bipeds.py index 36a51e7def2e..8bd47a711e13 100644 --- a/scripts/demos/bipeds.py +++ b/scripts/demos/bipeds.py @@ -3,8 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -This script demonstrates how to simulate bipedal robots. +"""This script demonstrates how to simulate bipedal robots. .. code-block:: bash diff --git a/scripts/demos/deformables.py b/scripts/demos/deformables.py index 1da56e1df004..eb2283135b5b 100644 --- a/scripts/demos/deformables.py +++ b/scripts/demos/deformables.py @@ -7,32 +7,33 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/deformables.py + # Usage with Newton MJWarp backend and default kit visualizer. + ./isaaclab.sh -p scripts/demos/deformables.py --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 # create argparser -parser = argparse.ArgumentParser(description="This script demonstrates how to spawn deformable prims into the scene.") -parser.add_argument("--backend", type=str, default="physx", choices=["physx", "newton"], help="Physics backend.") -# 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 spawn deformable prims into the scene.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +# Newton visualizer not supported for deformables +parser.add_argument("--visualizer", default="kit", choices=["kit"], help="Visualizer 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 random @@ -41,10 +42,20 @@ import tqdm import isaaclab.sim as sim_utils -from isaaclab.assets import DeformableObject, DeformableObjectCfg -from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR -if args_cli.backend == "newton": +## +# Pre-defined configs +## +from isaaclab_newton.physics import NewtonCfg # isort:skip +from isaaclab.assets import DeformableObjectCfg # isort:skip +from isaaclab.physics import PhysicsCfg # isort:skip +from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR # isort:skip + +if TYPE_CHECKING: + from isaaclab.assets import DeformableObject + +if args_cli.physics == "newton_mjwarp": + from isaaclab_contrib.deformable.newton_manager_cfg import VBDSolverCfg # isort:skip from isaaclab_newton.sim.schemas import NewtonDeformableBodyPropertiesCfg as DeformableBodyPropertiesCfg from isaaclab_newton.sim.spawners.materials import ( NewtonDeformableBodyMaterialCfg as VolumeDeformableMaterialCfg, @@ -165,13 +176,13 @@ def design_scene() -> tuple[dict, list[list[float]]]: obj_name = random.choice(list(objects_cfg.keys())) obj_cfg = objects_cfg[obj_name] # randomize the deformable material stiffness - if args_cli.backend == "newton" and obj_name == "cloth": + if args_cli.physics == "newton_mjwarp" and obj_name == "cloth": obj_cfg.physics_material.tri_ke = random.uniform(5e3, 5e4) obj_cfg.physics_material.tri_ka = random.uniform(5e3, 5e4) else: youngs_modulus = random.uniform(5e5, 1e8) poissons_ratio = random.uniform(0.25, 0.45) - if args_cli.backend == "newton": + if args_cli.physics == "newton_mjwarp": obj_cfg.physics_material.k_mu = youngs_modulus / (2.0 * (1.0 + poissons_ratio)) obj_cfg.physics_material.k_lambda = ( youngs_modulus * poissons_ratio / ((1.0 + poissons_ratio) * (1.0 - 2.0 * poissons_ratio)) @@ -189,7 +200,7 @@ def design_scene() -> tuple[dict, list[list[float]]]: spawn=obj_cfg, init_state=DeformableObjectCfg.InitialStateCfg(pos=origin), ) - scene_entities[f"Surface{idx:02d}"] = DeformableObject(cfg=cfg) + scene_entities[f"Surface{idx:02d}"] = cfg.class_type(cfg) else: prim_path = f"/World/Origin/Volume{idx:02d}" cfg = DeformableObjectCfg( @@ -197,21 +208,21 @@ def design_scene() -> tuple[dict, list[list[float]]]: spawn=obj_cfg, init_state=DeformableObjectCfg.InitialStateCfg(pos=origin), ) - scene_entities[f"Volume{idx:02d}"] = DeformableObject(cfg=cfg) + scene_entities[f"Volume{idx:02d}"] = cfg.class_type(cfg) # return the scene information return scene_entities, origins -def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, DeformableObject]): +def run_simulator(sim: "sim_utils.SimulationContext", entities: dict[str, "DeformableObject"]): """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 % int(3.0 / sim_dt) == 0: # reset counters @@ -236,42 +247,33 @@ def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, Deformab def main(): """Main function.""" - # Initialize the simulation context - if args_cli.backend == "newton": - from isaaclab_newton.physics import NewtonCfg - - from isaaclab_contrib.deformable.newton_manager_cfg import VBDSolverCfg - - physics_cfg = NewtonCfg( - solver_cfg=VBDSolverCfg( - iterations=5, - particle_enable_self_contact=True, - particle_self_contact_radius=0.0001, - particle_self_contact_margin=0.1, - ), - num_substeps=4, - ) - else: - from isaaclab_physx.physics import PhysxCfg - - physics_cfg = PhysxCfg() - 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([4.0, 4.0, 3.0], [0.5, 0.5, 0.0]) - - # Design scene by adding assets to it - scene_entities, _ = design_scene() - # Play the simulator - sim.reset() - # Now we are ready! - print("[INFO]: Setup complete...") - run_simulator(sim, scene_entities) - print("[INFO]: Simulation complete...") + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + if args_cli.physics == "newton_mjwarp": + physics_cfg = NewtonCfg( + solver_cfg=VBDSolverCfg( + iterations=5, + particle_enable_self_contact=True, + particle_self_contact_radius=0.0001, + particle_self_contact_margin=0.1, + ), + num_substeps=4, + ) + # 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([4.0, 4.0, 3.0], [0.5, 0.5, 0.0]) + + # Design scene by adding assets to it + scene_entities, _ = design_scene() + # Play the simulator + sim.reset() + # Now we are ready! + print("[INFO]: Setup complete...") + run_simulator(sim, scene_entities) + print("[INFO]: Simulation complete...") if __name__ == "__main__": # run the main function main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/h1_locomotion.py b/scripts/demos/h1_locomotion.py index 04b394a13479..321bd331608d 100644 --- a/scripts/demos/h1_locomotion.py +++ b/scripts/demos/h1_locomotion.py @@ -43,8 +43,6 @@ app_launcher = AppLauncher(args_cli) simulation_app = app_launcher.app -"""Rest everything follows.""" - import torch from rsl_rl.runners import OnPolicyRunner diff --git a/scripts/demos/hands.py b/scripts/demos/hands.py index 3f64104d945a..06cb367c90a1 100644 --- a/scripts/demos/hands.py +++ b/scripts/demos/hands.py @@ -3,8 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -This script demonstrates different dexterous hands. +"""This script demonstrates different dexterous hands. .. code-block:: bash @@ -58,7 +57,7 @@ def define_origins(num_origins: int, spacing: float) -> list[list[float]]: - """Defines the origins of the the scene.""" + """Defines the origins of the scene.""" # create tensor based on number of environments env_origins = torch.zeros(num_origins, 3) # create a grid of origins diff --git a/scripts/demos/haply_teleoperation.py b/scripts/demos/haply_teleoperation.py index 9880cae46f2e..f28626ecb6c3 100644 --- a/scripts/demos/haply_teleoperation.py +++ b/scripts/demos/haply_teleoperation.py @@ -14,9 +14,18 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/haply_teleoperation.py + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/haply_teleoperation.py --visualizer newton + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/haply_teleoperation.py --physics newton_mjwarp + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/haply_teleoperation.py --visualizer newton --physics newton_mjwarp + # With custom WebSocket URI ./isaaclab.sh -p scripts/demos/haply_teleoperation.py --websocket_uri ws://localhost:10001 @@ -29,15 +38,20 @@ 3. Connect Inverse3 and VerseGrip devices """ -"""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="Demonstration of Haply device teleoperation with Isaac Lab.") +parser = argparse.ArgumentParser( + description="Demonstration of Haply device teleoperation with Isaac Lab.", + conflict_handler="resolve", +) parser.add_argument("--num_envs", type=int, default=1, help="Number of environments to spawn.") +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") parser.add_argument( "--websocket_uri", type=str, @@ -51,26 +65,35 @@ help="Position sensitivity scaling factor.", ) -AppLauncher.add_app_launcher_args(parser) +add_launcher_args(parser) parser.set_defaults(visualizer=["kit"]) args_cli = parser.parse_args() -app_launcher = AppLauncher(args_cli) -simulation_app = app_launcher.app import numpy as np import torch import isaaclab.sim as sim_utils -from isaaclab.assets import Articulation, AssetBaseCfg, RigidObject, RigidObjectCfg + +## +# Pre-defined configs +## +from isaaclab.assets import ArticulationCfg, AssetBaseCfg, RigidObjectCfg from isaaclab.controllers import DifferentialIKController, DifferentialIKControllerCfg -from isaaclab.devices import HaplyDevice, HaplyDeviceCfg -from isaaclab.scene import InteractiveScene, InteractiveSceneCfg -from isaaclab.sensors import ContactSensor, ContactSensorCfg +from isaaclab.devices import HaplyDeviceCfg +from isaaclab.physics import PhysicsCfg +from isaaclab.scene import InteractiveSceneCfg +from isaaclab.sensors import ContactSensorCfg from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR from isaaclab.utils.configclass import configclass from isaaclab_assets import FRANKA_PANDA_HIGH_PD_CFG # isort: skip +if TYPE_CHECKING: + from isaaclab.assets import Articulation, RigidObject + from isaaclab.devices import HaplyDevice + from isaaclab.scene import InteractiveScene + from isaaclab.sensors import ContactSensor + # Workspace mapping constants HAPLY_Z_OFFSET = 0.35 WORKSPACE_LIMITS = { @@ -141,7 +164,8 @@ class FrankaHaplySceneCfg(InteractiveSceneCfg): init_state=AssetBaseCfg.InitialStateCfg(pos=(0.50, 0.0, 1.05), rot=(0.707, 0, 0, 0.707)), ) - robot: Articulation = FRANKA_PANDA_HIGH_PD_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot") + robot_cfg: ArticulationCfg = FRANKA_PANDA_HIGH_PD_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot") + robot = robot_cfg.class_type(robot_cfg) robot.init_state.pos = (-0.02, 0.0, 1.05) robot.spawn.activate_contact_sensors = True @@ -176,9 +200,9 @@ class FrankaHaplySceneCfg(InteractiveSceneCfg): def run_simulator( - sim: sim_utils.SimulationContext, - scene: InteractiveScene, - haply_device: HaplyDevice, + sim: "sim_utils.SimulationContext", + scene: "InteractiveScene", + haply_device: "HaplyDevice", ): """Runs the simulation loop with Haply teleoperation.""" sim_dt = sim.get_physics_dt() @@ -246,7 +270,7 @@ def run_simulator( print(" Move handler: Control pose of the end-effector") print(" Button A: Open | Button B: Close | Button C: Rotate EE (60°)\n") - while simulation_app.is_running(): + while sim.is_headless_or_exist_active_visualizer(): if count % 10000 == 0: count = 1 root_pose = robot.data.default_root_pose.torch.clone() @@ -343,30 +367,30 @@ def run_simulator( def main(): """Main function to set up and run the Haply teleoperation demo.""" - sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, dt=1 / 200) - sim = sim_utils.SimulationContext(sim_cfg) - - # set the simulation view - sim.set_camera_view([1.6, 1.0, 1.70], [0.4, 0.0, 1.0]) - - scene_cfg = FrankaHaplySceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0) - scene = InteractiveScene(scene_cfg) - - # Create Haply device - haply_cfg = HaplyDeviceCfg( - websocket_uri=args_cli.websocket_uri, - pos_sensitivity=args_cli.pos_sensitivity, - sim_device=args_cli.device, - limit_force=2.0, - ) - haply_device = HaplyDevice(cfg=haply_cfg) - print(f"[INFO] Haply connected: {args_cli.websocket_uri}") + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + sim_cfg = sim_utils.SimulationCfg(device=args_cli.device, dt=1 / 200, physics=physics_cfg) + sim = sim_utils.SimulationContext(sim_cfg) + + # set the simulation view + sim.set_camera_view([1.6, 1.0, 1.70], [0.4, 0.0, 1.0]) + + scene_cfg = FrankaHaplySceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0) + scene = scene_cfg.class_type(scene_cfg) + + # Create Haply device + haply_cfg = HaplyDeviceCfg( + websocket_uri=args_cli.websocket_uri, + pos_sensitivity=args_cli.pos_sensitivity, + sim_device=args_cli.device, + limit_force=2.0, + ) + haply_device = haply_cfg.class_type(cfg=haply_cfg) + print(f"[INFO] Haply connected: {args_cli.websocket_uri}") - sim.reset() + sim.reset() - run_simulator(sim, scene, haply_device) + run_simulator(sim, scene, haply_device) if __name__ == "__main__": main() - simulation_app.close() diff --git a/scripts/demos/markers.py b/scripts/demos/markers.py index bd44a887d3ed..78e54c559675 100644 --- a/scripts/demos/markers.py +++ b/scripts/demos/markers.py @@ -7,42 +7,47 @@ .. code-block:: bash - # Usage + # Usage with default PhysX physics and default kit visualizer. ./isaaclab.sh -p scripts/demos/markers.py """ -"""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 types of markers.") -# 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 types of markers.", + conflict_handler="resolve", +) +parser.add_argument("--physics", default="physx", choices=["physx"], help="Physics backend.") +# Newton visualizer not supported for markers +parser.add_argument("--visualizer", default="kit", choices=["kit"], help="Visualizer 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.markers import VisualizationMarkers, VisualizationMarkersCfg -from isaaclab.sim import SimulationContext + +## +# Pre-defined configs +## +from isaaclab.markers.visualization_markers_cfg import VisualizationMarkersCfg +from isaaclab.physics import PhysicsCfg from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR from isaaclab.utils.math import quat_from_angle_axis +if TYPE_CHECKING: + from isaaclab.markers import VisualizationMarkers + -def define_markers() -> VisualizationMarkers: +def define_markers() -> "VisualizationMarkers": """Define markers with various different shapes.""" marker_cfg = VisualizationMarkersCfg( prim_path="/Visuals/myMarkers", @@ -90,67 +95,66 @@ def define_markers() -> VisualizationMarkers: ), }, ) - return VisualizationMarkers(marker_cfg) + return marker_cfg.class_type(marker_cfg) def main(): """Main function.""" - # Load kit helper - sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device) - sim = SimulationContext(sim_cfg) - # Set main camera - sim.set_camera_view([0.0, 18.0, 12.0], [0.0, 3.0, 0.0]) - - # Spawn things into stage - # Lights - cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)) - cfg.func("/World/Light", cfg) - - # create markers - my_visualizer = define_markers() - - # define a grid of positions where the markers should be placed - num_markers_per_type = 5 - grid_spacing = 2.0 - # Calculate the half-width and half-height - half_width = (num_markers_per_type - 1) / 2.0 - half_height = (my_visualizer.num_prototypes - 1) / 2.0 - # Create the x and y ranges centered around the origin - x_range = torch.arange(-half_width * grid_spacing, (half_width + 1) * grid_spacing, grid_spacing) - y_range = torch.arange(-half_height * grid_spacing, (half_height + 1) * grid_spacing, grid_spacing) - # Create the grid - x_grid, y_grid = torch.meshgrid(x_range, y_range, indexing="ij") - x_grid = x_grid.reshape(-1) - y_grid = y_grid.reshape(-1) - z_grid = torch.zeros_like(x_grid) - # marker locations - marker_locations = torch.stack([x_grid, y_grid, z_grid], dim=1) - marker_indices = torch.arange(my_visualizer.num_prototypes).repeat(num_markers_per_type) - - # Play the simulator - sim.reset() - # Now we are ready! - print("[INFO]: Setup complete...") - - # Yaw angle - yaw = torch.zeros_like(marker_locations[:, 0]) - # Simulate physics - while simulation_app.is_running(): - # rotate the markers around the z-axis for visualization - marker_orientations = quat_from_angle_axis(yaw, torch.tensor([0.0, 0.0, 1.0])) - # visualize - my_visualizer.visualize(marker_locations, marker_orientations, marker_indices=marker_indices) - # roll corresponding indices to show how marker prototype can be changed - if yaw[0].item() % (0.5 * torch.pi) < 0.01: - marker_indices = torch.roll(marker_indices, 1) - # perform step - sim.step() - # increment yaw - yaw += 0.01 + with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg: + # Load kit helper + 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([0.0, 18.0, 12.0], [0.0, 3.0, 0.0]) + + # Spawn things into stage + # Lights + cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)) + cfg.func("/World/Light", cfg) + + # create markers + my_visualizer = define_markers() + + # define a grid of positions where the markers should be placed + num_markers_per_type = 5 + grid_spacing = 2.0 + # Calculate the half-width and half-height + half_width = (num_markers_per_type - 1) / 2.0 + half_height = (my_visualizer.num_prototypes - 1) / 2.0 + # Create the x and y ranges centered around the origin + x_range = torch.arange(-half_width * grid_spacing, (half_width + 1) * grid_spacing, grid_spacing) + y_range = torch.arange(-half_height * grid_spacing, (half_height + 1) * grid_spacing, grid_spacing) + # Create the grid + x_grid, y_grid = torch.meshgrid(x_range, y_range, indexing="ij") + x_grid = x_grid.reshape(-1) + y_grid = y_grid.reshape(-1) + z_grid = torch.zeros_like(x_grid) + # marker locations + marker_locations = torch.stack([x_grid, y_grid, z_grid], dim=1) + marker_indices = torch.arange(my_visualizer.num_prototypes).repeat(num_markers_per_type) + + # Play the simulator + sim.reset() + # Now we are ready! + print("[INFO]: Setup complete...") + + # Yaw angle + yaw = torch.zeros_like(marker_locations[:, 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(): + # rotate the markers around the z-axis for visualization + marker_orientations = quat_from_angle_axis(yaw, torch.tensor([0.0, 0.0, 1.0])) + # visualize + my_visualizer.visualize(marker_locations, marker_orientations, marker_indices=marker_indices) + # roll corresponding indices to show how marker prototype can be changed + if yaw[0].item() % (0.5 * torch.pi) < 0.01: + marker_indices = torch.roll(marker_indices, 1) + # perform step + sim.step() + # increment yaw + yaw += 0.01 if __name__ == "__main__": # run the main function main() - # close sim app - simulation_app.close() diff --git a/scripts/demos/multi_asset.py b/scripts/demos/multi_asset.py index 907c993e9549..be30806b76b6 100644 --- a/scripts/demos/multi_asset.py +++ b/scripts/demos/multi_asset.py @@ -7,62 +7,63 @@ .. code-block:: bash - # Usage - ./isaaclab.sh -p scripts/demos/multi_asset.py --num_envs 2048 + # Usage with default PhysX physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/multi_asset.py --num_envs 1024 + + # Usage with Newton visualizer and default PhysX physics. + ./isaaclab.sh -p scripts/demos/multi_asset.py --visualizer newton --num_envs 1024 + + # Usage with Newton (MJWarp) physics and default kit visualizer. + ./isaaclab.sh -p scripts/demos/multi_asset.py --physics newton_mjwarp --num_envs 1024 + + # Usage with Newton visualizer and Newton (MJWarp) physics. + ./isaaclab.sh -p scripts/demos/multi_asset.py --visualizer newton --physics newton_mjwarp --num_envs 1024 """ 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 on spawning different objects in multiple environments.") +parser = argparse.ArgumentParser( + description="Demo on spawning different objects in multiple environments.", + conflict_handler="resolve", +) parser.add_argument("--num_envs", type=int, default=512, help="Number of environments to spawn.") -# append AppLauncher cli args -AppLauncher.add_app_launcher_args(parser) +parser.add_argument("--physics", default="physx", choices=["physx", "newton_mjwarp"], help="Physics backend.") +add_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 +import random -"""Rest everything follows.""" +import isaaclab.sim as sim_utils -import random +## +# Pre-defined configs +## +from isaaclab.assets import ArticulationCfg, AssetBaseCfg, RigidObjectCfg, RigidObjectCollectionCfg +from isaaclab.physics import PhysicsCfg +from isaaclab.scene import InteractiveSceneCfg -from pxr import Gf, Sdf +from isaaclab_assets.robots.anymal import ANYDRIVE_3_LSTM_ACTUATOR_CFG # isort: skip -import isaaclab.sim as sim_utils -from isaaclab.assets import ( - Articulation, - ArticulationCfg, - AssetBaseCfg, - RigidObject, - RigidObjectCfg, - RigidObjectCollection, - RigidObjectCollectionCfg, -) -from isaaclab.scene import InteractiveScene, InteractiveSceneCfg -from isaaclab.sim import SimulationContext from isaaclab.sim.utils.stage import get_current_stage from isaaclab.utils import Timer from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR from isaaclab.utils.configclass import configclass -## -# Pre-defined Configuration -## - -from isaaclab_assets.robots.anymal import ANYDRIVE_3_LSTM_ACTUATOR_CFG # isort: skip +if TYPE_CHECKING: + from isaaclab.assets import Articulation, RigidObject, RigidObjectCollection + from isaaclab.scene import InteractiveScene ## @@ -72,6 +73,8 @@ def randomize_shape_color(prim_path_expr: str): """Randomize the color of the geometry.""" + from pxr import Gf, Sdf + # get stage handle stage = get_current_stage() # resolve prim paths for spawning and cloning @@ -86,7 +89,8 @@ def randomize_shape_color(prim_path_expr: str): # Note: Just need to acquire the right attribute about the property you want to set # Here is an example on setting color randomly color_spec = prim_spec.GetAttributeAtPath(prim_path + "/geometry/material/Shader.inputs:diffuseColor") - color_spec.default = Gf.Vec3f(random.random(), random.random(), random.random()) + if color_spec is not None: + color_spec.default = Gf.Vec3f(random.random(), random.random(), random.random()) ## @@ -111,7 +115,7 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): prim_path="/World/envs/env_.*/Object", spawn=sim_utils.MultiAssetSpawnerCfg( assets_cfg=[ - sim_utils.ConeCfg( + (sim_utils.CylinderCfg)( radius=0.3, height=0.6, visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0), metallic=0.2), @@ -166,7 +170,7 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): ), "object_C": RigidObjectCfg( prim_path="/World/envs/env_.*/Object_C", - spawn=sim_utils.ConeCfg( + spawn=sim_utils.CylinderCfg( radius=0.1, height=0.3, visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0), metallic=0.2), @@ -223,7 +227,7 @@ class MultiObjectSceneCfg(InteractiveSceneCfg): ## -def run_simulator(sim: SimulationContext, scene: InteractiveScene): +def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): """Runs the simulation loop.""" # Extract scene entities # note: we only do this here for readability. @@ -233,8 +237,8 @@ def run_simulator(sim: SimulationContext, scene: InteractiveScene): # Define simulation stepping sim_dt = sim.get_physics_dt() count = 0 - # 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 @@ -257,13 +261,11 @@ def run_simulator(sim: SimulationContext, scene: InteractiveScene): root_pose = robot.data.default_root_pose.torch.clone() root_pose[:, :3] += scene.env_origins robot.write_root_pose_to_sim_index(root_pose=root_pose) - root_vel = robot.data.default_root_vel.torch.clone() + root_vel = robot.data.default_root_vel.torch 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 + joint_vel = 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) # clear internal buffers @@ -284,33 +286,35 @@ def run_simulator(sim: SimulationContext, scene: InteractiveScene): 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([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=2.0, replicate_physics=True) - with Timer("[INFO] Time to create scene: "): - scene = InteractiveScene(scene_cfg) - - with Timer("[INFO] Time to randomize scene: "): - # DO YOUR OWN OTHER KIND OF RANDOMIZATION HERE! - # Note: Just need to acquire the right attribute about the property you want to set - # Here is an example on setting color randomly - randomize_shape_color(scene_cfg.object.prim_path) - - # 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: + 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=2.0, replicate_physics=True) + if args_cli.physics == "newton_mjwarp": + # Newton/MJWarp requires a uniform layout, not supporting different setups across environments. + scene_cfg.object.spawn.assets_cfg = scene_cfg.object.spawn.assets_cfg[1:2] + scene_cfg.robot.spawn.usd_path = scene_cfg.robot.spawn.usd_path[0] + with Timer("[INFO] Time to create scene: "): + scene = scene_cfg.class_type(scene_cfg) + + with Timer("[INFO] Time to randomize scene: "): + # DO YOUR OWN OTHER KIND OF RANDOMIZATION HERE! + # Note: Just need to acquire the right attribute about the property you want to set + # Here is an example on setting color randomly + randomize_shape_color(scene_cfg.object.prim_path) + + # 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/pick_and_place.py b/scripts/demos/pick_and_place.py index aa99cb1480fd..b55fd5ca44ec 100644 --- a/scripts/demos/pick_and_place.py +++ b/scripts/demos/pick_and_place.py @@ -23,8 +23,6 @@ app_launcher = AppLauncher(args_cli) simulation_app = app_launcher.app -"""Rest everything follows.""" - from collections.abc import Sequence import torch diff --git a/scripts/demos/procedural_terrain.py b/scripts/demos/procedural_terrain.py index 98bcd0276789..6e26a8b26536 100644 --- a/scripts/demos/procedural_terrain.py +++ b/scripts/demos/procedural_terrain.py @@ -27,14 +27,20 @@ """ -"""Launch Isaac Sim Simulator first.""" +from __future__ import annotations + +"""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 procedural terrain generation.") +parser = argparse.ArgumentParser( + description="This script demonstrates procedural terrain generation.", + conflict_handler="resolve", +) parser.add_argument( "--color_scheme", type=str, @@ -54,33 +60,30 @@ default=False, help="Whether to show the flat patches computed during the terrain generation.", ) -# 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"], 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 random import torch import isaaclab.sim as sim_utils -from isaaclab.assets import AssetBase -from isaaclab.markers import VisualizationMarkers, VisualizationMarkersCfg -from isaaclab.terrains import FlatPatchSamplingCfg, TerrainImporter, TerrainImporterCfg ## # Pre-defined configs ## +from isaaclab.markers.visualization_markers_cfg import VisualizationMarkersCfg +from isaaclab.physics import PhysicsCfg +from isaaclab.terrains.sub_terrain_cfg import FlatPatchSamplingCfg +from isaaclab.terrains.terrain_importer_cfg import TerrainImporterCfg + from isaaclab.terrains.config.rough import ROUGH_TERRAINS_CFG # isort:skip +if TYPE_CHECKING: + from isaaclab.assets import AssetBase + def design_scene() -> tuple[dict, torch.Tensor]: """Designs the scene.""" @@ -115,7 +118,7 @@ def design_scene() -> tuple[dict, torch.Tensor]: if args_cli.color_scheme in ["height", "random"]: terrain_importer_cfg.visual_material = None # Create terrain importer - terrain_importer = TerrainImporter(terrain_importer_cfg) + terrain_importer = terrain_importer_cfg.class_type(terrain_importer_cfg) # Show the flat patches computed if args_cli.show_flat_patches: @@ -127,7 +130,7 @@ def design_scene() -> tuple[dict, torch.Tensor]: height=0.1, visual_material=sim_utils.GlassMdlCfg(glass_color=(random.random(), random.random(), random.random())), ) - flat_patches_visualizer = VisualizationMarkers(vis_cfg) + flat_patches_visualizer = vis_cfg.class_type(vis_cfg) # Visualize the flat patches all_patch_locations = [] @@ -147,31 +150,30 @@ def design_scene() -> tuple[dict, torch.Tensor]: def run_simulator(sim: sim_utils.SimulationContext, entities: dict[str, AssetBase], origins: torch.Tensor): """Runs the simulation loop.""" - # 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(): # perform step sim.step() 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=[5.0, 5.0, 5.0], target=[0.0, 0.0, 0.0]) - # design scene - scene_entities, scene_origins = design_scene() - # 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: + # 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=[15.0, 15.0, 15.0], target=[0.0, 0.0, 0.0]) + # design scene + scene_entities, scene_origins = design_scene() + # 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/quadcopter.py b/scripts/demos/quadcopter.py index 2c8215cbc152..1558960812c8 100644 --- a/scripts/demos/quadcopter.py +++ b/scripts/demos/quadcopter.py @@ -3,8 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -This script demonstrates how to simulate a quadcopter. +"""This script demonstrates how to simulate a quadcopter. .. code-block:: bash diff --git a/scripts/demos/quadrupeds.py b/scripts/demos/quadrupeds.py index 901c91e0b425..02aeb864f358 100644 --- a/scripts/demos/quadrupeds.py +++ b/scripts/demos/quadrupeds.py @@ -3,8 +3,7 @@ # # SPDX-License-Identifier: BSD-3-Clause -""" -This script demonstrates different legged robots. +"""This script demonstrates different legged robots. .. code-block:: bash diff --git a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py index b7cc2002ccf0..6c6b98ec3268 100644 --- a/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py +++ b/source/isaaclab/isaaclab/markers/visualization_markers_cfg.py @@ -8,10 +8,14 @@ from __future__ import annotations from dataclasses import MISSING +from typing import TYPE_CHECKING from isaaclab.sim.spawners import SpawnerCfg from isaaclab.utils.configclass import configclass +if TYPE_CHECKING: + from isaaclab.markers import VisualizationMarkers + @configclass class VisualizationMarkersCfg: @@ -26,3 +30,9 @@ class VisualizationMarkersCfg: The key is the name of the marker, and the value is the configuration of the marker. The key is used to identify the marker in the class. """ + + class_type: type[VisualizationMarkers] | str = "{DIR}.visualization_markers:VisualizationMarkers" + """The class to use for the visualization markers. + + Defaults to :class:`isaaclab.markers.VisualizationMarkers`. + """ diff --git a/source/isaaclab/isaaclab/terrains/utils.py b/source/isaaclab/isaaclab/terrains/utils.py index 766fe13c6f9c..739f80940021 100644 --- a/source/isaaclab/isaaclab/terrains/utils.py +++ b/source/isaaclab/isaaclab/terrains/utils.py @@ -52,7 +52,8 @@ def color_meshes_by_height(meshes: list[trimesh.Trimesh], **kwargs) -> trimesh.T # clip lower and upper bounds to have better color mapping heights_normalized = np.clip(heights_normalized, 0.1, 0.9) # Get the color for each vertex based on the height - color_map = kwargs.pop("color_map", "turbo") + # Valid options are ["viridis", "inferno", "plasma", "magma"] + color_map = kwargs.pop("color_map", "inferno") colors = trimesh.visual.color.interpolate(heights_normalized, color_map=color_map) # Set the vertex colors mesh.visual.vertex_colors = colors