Creating visualization markers#

This guide shows how to draw visualization markers: debug geometry (frames, arrows, spheres, custom meshes) rendered over the scene through markers.VisualizationMarkers. Markers are display-only: they carry no physics and do not affect the simulation.

For plain points, lines, and splines, Isaac Sim’s own isaacsim.util.debug_draw extension is lighter-weight. Use VisualizationMarkers when you need more complex shapes.

Markers are supported on Kit, Newton GL, Rerun, and Viser, but not yet on Newton RTX. See Visualization for how to enable them on a given visualizer.

Quick Start#

Run the packaged markers example:

uv run --extra isaacsim isaaclab example markers

Pass --visualizer newton_gl (or another supported backend) to switch visualizers; defaults to kit.

../../_images/../_static/demos/markers.jpg

Every marker prototype from the example script, arranged in a grid. Each column rotates in place and periodically rolls forward to the next prototype.#

To stop, close the window or press Ctrl+C.

Code for markers.py
  1# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
  2# All rights reserved.
  3#
  4# SPDX-License-Identifier: BSD-3-Clause
  5
  6"""This script demonstrates different types of markers.
  7
  8.. code-block:: bash
  9
 10    # Usage with default PhysX physics and default kit visualizer.
 11    uvx --from 'isaaclab[isaacsim]' isaaclab example markers
 12
 13"""
 14
 15import argparse
 16from typing import TYPE_CHECKING
 17
 18from isaaclab.app import add_launcher_args, launch_simulation
 19from isaaclab.utils import instantiate
 20
 21parser = argparse.ArgumentParser(
 22    description="This script demonstrates different types of markers.",
 23    conflict_handler="resolve",
 24)
 25parser.add_argument("--physics", default="isaacsim_physx", choices=["isaacsim_physx"], help="Physics backend.")
 26parser.add_argument("--max_steps", type=int, default=-1, help="Stop after this many steps; negative runs forever.")
 27add_launcher_args(parser)
 28parser.set_defaults(visualizer=["kit"])
 29args_cli = parser.parse_args()
 30if args_cli.max_steps == 0 or args_cli.max_steps < -1:
 31    parser.error("--max_steps must be positive or -1.")
 32
 33import torch
 34
 35import isaaclab.sim as sim_utils
 36from isaaclab.markers.visualization_markers_cfg import VisualizationMarkersCfg
 37from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR
 38from isaaclab.utils.math import quat_from_angle_axis
 39
 40if TYPE_CHECKING:
 41    from isaaclab.markers import VisualizationMarkers
 42
 43
 44def define_markers() -> "VisualizationMarkers":
 45    """Define markers with various different shapes."""
 46    marker_cfg = VisualizationMarkersCfg(
 47        prim_path="/Visuals/myMarkers",
 48        markers={
 49            "frame": sim_utils.UsdFileCfg(
 50                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/frame_prim.usd",
 51                scale=(0.5, 0.5, 0.5),
 52            ),
 53            "arrow_x": sim_utils.UsdFileCfg(
 54                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd",
 55                scale=(1.0, 0.5, 0.5),
 56                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 1.0)),
 57            ),
 58            "cube": sim_utils.CuboidCfg(
 59                size=(1.0, 1.0, 1.0),
 60                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)),
 61            ),
 62            "sphere": sim_utils.SphereCfg(
 63                radius=0.5,
 64                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)),
 65            ),
 66            "cylinder": sim_utils.CylinderCfg(
 67                radius=0.5,
 68                height=1.0,
 69                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)),
 70            ),
 71            "cone": sim_utils.ConeCfg(
 72                radius=0.5,
 73                height=1.0,
 74                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 1.0, 0.0)),
 75            ),
 76            "mesh": sim_utils.UsdFileCfg(
 77                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
 78                scale=(10.0, 10.0, 10.0),
 79            ),
 80            "mesh_recolored": sim_utils.UsdFileCfg(
 81                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
 82                scale=(10.0, 10.0, 10.0),
 83                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.25, 0.0)),
 84            ),
 85            "robot_mesh": sim_utils.UsdFileCfg(
 86                usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd",
 87                scale=(2.0, 2.0, 2.0),
 88                visual_material=sim_utils.GlassMdlCfg(glass_color=(0.0, 0.1, 0.0)),
 89            ),
 90        },
 91    )
 92    return instantiate(marker_cfg)
 93
 94
 95def main():
 96    """Main function."""
 97    sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device)
 98    with launch_simulation(sim_cfg, args_cli):
 99        sim = sim_utils.SimulationContext(sim_cfg)
100        # Set main camera
101        sim.set_camera_view([0.0, 18.0, 12.0], [0.0, 3.0, 0.0])
102
103        # Spawn things into stage
104        # Lights
105        cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
106        cfg.func("/World/Light", cfg)
107
108        # create markers
109        my_visualizer = define_markers()
110
111        # define a grid of positions where the markers should be placed
112        num_markers_per_type = 5
113        grid_spacing = 2.0
114        # Calculate the half-width and half-height
115        half_width = (num_markers_per_type - 1) / 2.0
116        half_height = (my_visualizer.num_prototypes - 1) / 2.0
117        # Create the x and y ranges centered around the origin
118        x_range = torch.arange(-half_width * grid_spacing, (half_width + 1) * grid_spacing, grid_spacing)
119        y_range = torch.arange(-half_height * grid_spacing, (half_height + 1) * grid_spacing, grid_spacing)
120        # Create the grid
121        x_grid, y_grid = torch.meshgrid(x_range, y_range, indexing="ij")
122        x_grid = x_grid.reshape(-1)
123        y_grid = y_grid.reshape(-1)
124        z_grid = torch.zeros_like(x_grid)
125        # marker locations
126        marker_locations = torch.stack([x_grid, y_grid, z_grid], dim=1)
127        marker_indices = torch.arange(my_visualizer.num_prototypes).repeat(num_markers_per_type)
128
129        sim.reset()
130        print("[INFO]: Setup complete...")
131
132        # Yaw angle
133        yaw = torch.zeros_like(marker_locations[:, 0])
134        step_count = 0
135        # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton.
136        while sim.is_running() and (args_cli.max_steps < 0 or step_count < args_cli.max_steps):
137            # rotate the markers around the z-axis for visualization
138            marker_orientations = quat_from_angle_axis(yaw, torch.tensor([0.0, 0.0, 1.0]))
139            # visualize
140            my_visualizer.visualize(marker_locations, marker_orientations, marker_indices=marker_indices)
141            # roll corresponding indices to show how marker prototype can be changed
142            if yaw[0].item() % (0.5 * torch.pi) < 0.01:
143                marker_indices = torch.roll(marker_indices, 1)
144            # perform step
145            sim.step()
146            step_count += 1
147            # increment yaw
148            yaw += 0.01
149
150
151if __name__ == "__main__":
152    main()

Configuring markers#

VisualizationMarkersCfg takes:

  • prim_path: prim path where the marker UsdGeom.PointInstancer is created.

  • markers: a dict of marker prototypes. The key names the prototype; the value is its spawn config (any SpawnerCfg, including USD file references).

Note

Physics properties on a marker prototype’s spawn config are stripped on creation, since markers are not simulated.

def define_markers() -> "VisualizationMarkers":
    """Define markers with various different shapes."""
    marker_cfg = VisualizationMarkersCfg(
        prim_path="/Visuals/myMarkers",
        markers={
            "frame": sim_utils.UsdFileCfg(
                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/frame_prim.usd",
                scale=(0.5, 0.5, 0.5),
            ),
            "arrow_x": sim_utils.UsdFileCfg(
                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd",
                scale=(1.0, 0.5, 0.5),
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 1.0)),
            ),
            "cube": sim_utils.CuboidCfg(
                size=(1.0, 1.0, 1.0),
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)),
            ),
            "sphere": sim_utils.SphereCfg(
                radius=0.5,
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)),
            ),
            "cylinder": sim_utils.CylinderCfg(
                radius=0.5,
                height=1.0,
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)),
            ),
            "cone": sim_utils.ConeCfg(
                radius=0.5,
                height=1.0,
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 1.0, 0.0)),
            ),
            "mesh": sim_utils.UsdFileCfg(
                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
                scale=(10.0, 10.0, 10.0),
            ),
            "mesh_recolored": sim_utils.UsdFileCfg(
                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
                scale=(10.0, 10.0, 10.0),
                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.25, 0.0)),
            ),
            "robot_mesh": sim_utils.UsdFileCfg(
                usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd",
                scale=(2.0, 2.0, 2.0),
                visual_material=sim_utils.GlassMdlCfg(glass_color=(0.0, 0.1, 0.0)),
            ),
        },
    )
    return instantiate(marker_cfg)

Drawing markers#

visualize() sets marker poses and, optionally, which prototype each marker instance uses via marker_indices.

my_visualizer.visualize(marker_locations, marker_orientations, marker_indices=marker_indices)

Arguments left as None keep their previous value. Passing a different number of rows than the last call resizes the marker instance count. See visualize()’s docstring for the full argument list (translations, orientations, scales, marker_indices, environment_ids).

Markers in practice#

Markers are commonly used to debug per-environment state during training, such as commanded vs. current velocity, or contact events:

Velocity arrow marker on an AnymalD robot

Velocity command (green) and current velocity (blue) arrow markers on an AnymalD robot.

Joint arrow markers on a Franka arm and contact sensor markers on a cube

Joint arrow markers on a Franka arm, with contact sensor markers on a cube.

See also#

  • Visualization: enabling markers per visualizer, and other visualizer features

  • Recording video: recording a marker-annotated scene to video