Creating Visualization Markers#

Visualization markers render debug geometry (frames, arrows, spheres, custom meshes) 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.

Supported on Kit, Newton GL, Rerun, and Viser; not yet on Newton RTX. See Visualization for enabling markers on a given visualizer.

Quick Start#

This guide is accompanied by markers.py in IsaacLab/scripts/demos.

uv run --extra isaacsim python scripts/demos/markers.py
./isaaclab.sh -p scripts/demos/markers.py

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

../../_images/markers.jpg

Every marker prototype from the demo 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    uv run python scripts/demos/markers.py
 12
 13"""
 14
 15"""Parse CLI first so we can decide whether to launch Isaac Sim Kit."""
 16
 17import argparse
 18from typing import TYPE_CHECKING
 19
 20from isaaclab.app import add_launcher_args, launch_simulation
 21
 22# add argparse arguments
 23parser = argparse.ArgumentParser(
 24    description="This script demonstrates different types of markers.",
 25    conflict_handler="resolve",
 26)
 27parser.add_argument("--physics", default="isaacsim_physx", choices=["isaacsim_physx"], help="Physics backend.")
 28add_launcher_args(parser)
 29parser.set_defaults(visualizer=["kit"])
 30args_cli = parser.parse_args()
 31
 32import torch
 33
 34import isaaclab.sim as sim_utils
 35
 36##
 37# Pre-defined configs
 38##
 39from isaaclab.markers.visualization_markers_cfg import VisualizationMarkersCfg
 40from isaaclab.physics import PhysicsCfg
 41from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR
 42from isaaclab.utils.math import quat_from_angle_axis
 43
 44if TYPE_CHECKING:
 45    from isaaclab.markers import VisualizationMarkers
 46
 47
 48def define_markers() -> "VisualizationMarkers":
 49    """Define markers with various different shapes."""
 50    marker_cfg = VisualizationMarkersCfg(
 51        prim_path="/Visuals/myMarkers",
 52        markers={
 53            "frame": sim_utils.UsdFileCfg(
 54                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/frame_prim.usd",
 55                scale=(0.5, 0.5, 0.5),
 56            ),
 57            "arrow_x": sim_utils.UsdFileCfg(
 58                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd",
 59                scale=(1.0, 0.5, 0.5),
 60                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 1.0)),
 61            ),
 62            "cube": sim_utils.CuboidCfg(
 63                size=(1.0, 1.0, 1.0),
 64                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)),
 65            ),
 66            "sphere": sim_utils.SphereCfg(
 67                radius=0.5,
 68                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)),
 69            ),
 70            "cylinder": sim_utils.CylinderCfg(
 71                radius=0.5,
 72                height=1.0,
 73                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)),
 74            ),
 75            "cone": sim_utils.ConeCfg(
 76                radius=0.5,
 77                height=1.0,
 78                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 1.0, 0.0)),
 79            ),
 80            "mesh": 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            ),
 84            "mesh_recolored": sim_utils.UsdFileCfg(
 85                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
 86                scale=(10.0, 10.0, 10.0),
 87                visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.25, 0.0)),
 88            ),
 89            "robot_mesh": sim_utils.UsdFileCfg(
 90                usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Robots/ANYbotics/ANYmal-C/anymal_c.usd",
 91                scale=(2.0, 2.0, 2.0),
 92                visual_material=sim_utils.GlassMdlCfg(glass_color=(0.0, 0.1, 0.0)),
 93            ),
 94        },
 95    )
 96    return marker_cfg.class_type(marker_cfg)
 97
 98
 99def main():
100    """Main function."""
101    with launch_simulation(cfg=PhysicsCfg(), launcher_args=args_cli) as physics_cfg:
102        # Load kit helper
103        sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device, physics=physics_cfg)
104        sim = sim_utils.SimulationContext(sim_cfg)
105        # Set main camera
106        sim.set_camera_view([0.0, 18.0, 12.0], [0.0, 3.0, 0.0])
107
108        # Spawn things into stage
109        # Lights
110        cfg = sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75))
111        cfg.func("/World/Light", cfg)
112
113        # create markers
114        my_visualizer = define_markers()
115
116        # define a grid of positions where the markers should be placed
117        num_markers_per_type = 5
118        grid_spacing = 2.0
119        # Calculate the half-width and half-height
120        half_width = (num_markers_per_type - 1) / 2.0
121        half_height = (my_visualizer.num_prototypes - 1) / 2.0
122        # Create the x and y ranges centered around the origin
123        x_range = torch.arange(-half_width * grid_spacing, (half_width + 1) * grid_spacing, grid_spacing)
124        y_range = torch.arange(-half_height * grid_spacing, (half_height + 1) * grid_spacing, grid_spacing)
125        # Create the grid
126        x_grid, y_grid = torch.meshgrid(x_range, y_range, indexing="ij")
127        x_grid = x_grid.reshape(-1)
128        y_grid = y_grid.reshape(-1)
129        z_grid = torch.zeros_like(x_grid)
130        # marker locations
131        marker_locations = torch.stack([x_grid, y_grid, z_grid], dim=1)
132        marker_indices = torch.arange(my_visualizer.num_prototypes).repeat(num_markers_per_type)
133
134        # Play the simulator
135        sim.reset()
136        # Now we are ready!
137        print("[INFO]: Setup complete...")
138
139        # Yaw angle
140        yaw = torch.zeros_like(marker_locations[:, 0])
141        # Step while a visualizer window is still open (or none exist, e.g. headless); works for kit and newton.
142        while sim.is_headless_or_exist_active_visualizer():
143            # rotate the markers around the z-axis for visualization
144            marker_orientations = quat_from_angle_axis(yaw, torch.tensor([0.0, 0.0, 1.0]))
145            # visualize
146            my_visualizer.visualize(marker_locations, marker_orientations, marker_indices=marker_indices)
147            # roll corresponding indices to show how marker prototype can be changed
148            if yaw[0].item() % (0.5 * torch.pi) < 0.01:
149                marker_indices = torch.roll(marker_indices, 1)
150            # perform step
151            sim.step()
152            # increment yaw
153            yaw += 0.01
154
155
156if __name__ == "__main__":
157    # run the main function
158    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.

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 marker_cfg.class_type(marker_cfg)

Drawing markers#

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

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)

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