Skip to content

Commit 7a4b6d2

Browse files
Add Debug Visualization Markers to Newton Based Visualizers (#5473)
# Description <!-- Thank you for your interest in sending a pull request. Please make sure to check the contribution guidelines. Link: https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html 💡 Please try to keep PRs small and focused. Large PRs are harder to review and merge. --> Extend debug Visualization Markers, which are supported in the Kit Visualizer, to the Newton Visualizers. These Visualization Markers are various shapes and models which can be added to envs for debugging / showing extra information. Also added filtering for partial visualization (when we filtered which envs are shown the in the visualizer, we also filter the markers) For general USD mesh marker support in Newton, a followup PR will be required, once a Newton API for general USD -> Newton Mesh conversion is added (see newton-physics/newton#2667) Checked velocity arrows, dexcubes, raycasts, frames, goal markers <!-- As a practice, it is recommended to open an issue to have discussions on the proposed pull request. This makes it easier for the community to keep track of what is being developed or added, and if a given feature is demanded by more than one party. --> ## Type of change <!-- As you go through the list, delete the ones that are not applicable. --> - New feature (non-breaking change which adds functionality) - Documentation update ## Screenshots Please attach before and after screenshots of the change if applicable. <!-- Example: | Before | After | | ------ | ----- | | _gif/png before_ | _gif/png after_ | To upload images to a PR -- simply drag and drop an image while in edit mode and it should upload the image directly. You can then paste that source into the above before/after sections. --> ## Checklist - [ ] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [ ] I have added my name to the `CONTRIBUTORS.md` or my name already exists there <!-- As you go through the checklist above, you can mark something as done by putting an x character in it For example, - [x] I have done this task - [ ] I have not done this task --> --------- Signed-off-by: matthewtrepte <mtrepte@nvidia.com>
1 parent c0ae5ee commit 7a4b6d2

16 files changed

Lines changed: 1490 additions & 382 deletions

docs/source/features/visualization.rst

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ Isaac Lab supports four visualizer backends, each optimized for different use ca
2222
- Key Features
2323
* - **Omniverse**
2424
- High-fidelity, Isaac Sim integration
25-
- USD, visual markers, live plots
25+
- USD, visualization markers, live plots
2626
* - **Newton**
2727
- Fast iteration
28-
- Low overhead, visual markers
28+
- Low overhead, visualization markers
2929
* - **Rerun**
3030
- Remote viewing, replay
31-
- Webviewer, time scrubbing, recording export
31+
- Webviewer, time scrubbing, recording export, visualization markers
3232
* - **Viser**
3333
- Web-based remote visualization, sharing, recording
34-
- Warp-based rendering, browser-based, share URL
34+
- Warp-based rendering, browser-based, share URL, visualization markers
3535

3636

3737
*The following visualizers are shown training the Isaac-Velocity-Flat-Anymal-D-v0 environment.*
@@ -284,9 +284,9 @@ Omniverse Visualizer
284284
**Main Features:**
285285

286286
- Native USD stage integration
287-
- Visualization markers for debugging (arrows, frames, points, etc.)
288287
- Live plots for monitoring training metrics
289288
- Full Isaac Sim rendering capabilities and tooling
289+
- Visualization markers for debugging (arrows, frames, object targets, etc.)
290290

291291
**Core Configuration:**
292292

@@ -316,10 +316,10 @@ Newton Visualizer
316316
**Main Features:**
317317

318318
- Lightweight OpenGL rendering with low overhead
319-
- Visualization markers (joints, contacts, springs, COM)
320319
- Simulation and rendering pause controls
321320
- Adjustable update frequency for performance tuning
322321
- Some customizable rendering options (shadows, sky, wireframe)
322+
- Visualization markers (joints, contacts, springs, COM, debug markers)
323323

324324

325325
**Interactive Controls:**
@@ -388,6 +388,7 @@ Rerun Visualizer
388388
- Metadata logging and filtering
389389
- Recording to .rrd files for offline replay (.rrd files can be opened with ctrl+O from the web viewer)
390390
- Timeline scrubbing and playback controls of recordings
391+
- Visualization debug markers
391392

392393
**Core Configuration:**
393394

@@ -432,6 +433,7 @@ server, allowing you to view and interact with the scene from any browser.
432433
- Optional public share URL for remote viewing
433434
- Recording to ``.viser`` format for replay
434435
- Environment filtering to control which environments are rendered
436+
- Visualization debug markers
435437

436438
**Launch with Viser:**
437439

@@ -464,7 +466,7 @@ server, allowing you to view and interact with the scene from any browser.
464466

465467
.. note::
466468

467-
The Viser visualizer does not currently support markers or live plots.
469+
The Viser visualizer does not currently support live plots.
468470

469471

470472
Performance Note
@@ -497,15 +499,9 @@ the num of environments can be overwritten and decreased using ``--num_envs``:
497499
The FPS control in the Rerun visualizer UI may not affect the visualization frame rate in all configurations.
498500

499501

500-
**Newton Visualizer Contact and Center of Mass Markers**
502+
**Live Plots**
501503

502-
Contact and center of mass markers are not yet supported in the Newton visualizer. This will be addressed in a future release.
503-
504-
505-
**Viser Visualizer Markers and Live Plots**
506-
507-
The Viser visualizer does not currently support visualization markers or live plots. For these features, use the
508-
Omniverse or Newton visualizers.
504+
Currently, live plots are only available in the Kit Visualizer.
509505

510506

511507
**Viser Visualizer Renderer Requirement**

source/isaaclab/changelog.d/mtrepte-expand_viz_markers-2.skip

Whitespace-only changes.

source/isaaclab/isaaclab/assets/asset_base.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,13 @@ def set_debug_vis(self, debug_vis: bool) -> bool:
205205
if debug_vis:
206206
if self._debug_vis_handle is None:
207207
sim_ctx = SimulationContext.instance()
208-
if "physx" in sim_ctx.physics_manager.__name__.lower():
209-
import omni.kit.app
210-
211-
app_interface = omni.kit.app.get_app_interface()
212-
self._debug_vis_handle = app_interface.get_post_update_event_stream().create_subscription_to_pop(
213-
lambda event, obj=weakref.proxy(self): obj._debug_vis_callback(event)
214-
)
208+
if sim_ctx is not None:
209+
self._debug_vis_handle = sim_ctx.vis_marker_registry.add_debug_vis_callback(self)
215210
else:
216-
if self._debug_vis_handle is not None:
217-
self._debug_vis_handle.unsubscribe()
211+
sim_ctx = SimulationContext.instance()
212+
if sim_ctx is not None:
213+
sim_ctx.vis_marker_registry.clear_debug_vis_callback(self)
214+
else:
218215
self._debug_vis_handle = None
219216
# return success
220217
return True
@@ -404,8 +401,10 @@ def _initialize_callback(self, event):
404401
def _invalidate_initialize_callback(self, event):
405402
"""Invalidates the scene elements."""
406403
self._is_initialized = False
407-
if self._debug_vis_handle is not None:
408-
self._debug_vis_handle.unsubscribe()
404+
sim_ctx = SimulationContext.instance()
405+
if sim_ctx is not None:
406+
sim_ctx.vis_marker_registry.clear_debug_vis_callback(self)
407+
else:
409408
self._debug_vis_handle = None
410409

411410
def _on_prim_deletion(self, event) -> None:
@@ -435,6 +434,8 @@ def _clear_callbacks(self) -> None:
435434
if self._prim_deletion_handle is not None:
436435
self._prim_deletion_handle.deregister()
437436
self._prim_deletion_handle = None
438-
if self._debug_vis_handle is not None:
439-
self._debug_vis_handle.unsubscribe()
437+
sim_ctx = SimulationContext.instance()
438+
if sim_ctx is not None:
439+
sim_ctx.vis_marker_registry.clear_debug_vis_callback(self)
440+
else:
440441
self._debug_vis_handle = None

source/isaaclab/isaaclab/managers/action_manager.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,13 @@
99

1010
import inspect
1111
import re
12-
import weakref
1312
from abc import abstractmethod
1413
from collections.abc import Sequence
1514
from typing import TYPE_CHECKING, Any
1615

1716
import torch
1817
from prettytable import PrettyTable
1918

20-
from isaaclab.utils.version import has_kit
21-
22-
if has_kit():
23-
import omni.kit.app
24-
2519
from isaaclab.envs.utils.io_descriptors import GenericActionIODescriptor
2620

2721
from .manager_base import ManagerBase, ManagerTermBase
@@ -66,9 +60,11 @@ def __init__(self, cfg: ActionTermCfg, env: ManagerBasedEnv):
6660

6761
def __del__(self):
6862
"""Unsubscribe from the callbacks."""
69-
if self._debug_vis_handle:
70-
self._debug_vis_handle.unsubscribe()
71-
self._debug_vis_handle = None
63+
env = getattr(self, "_env", None)
64+
sim = getattr(env, "sim", None)
65+
registry = getattr(sim, "vis_marker_registry", None)
66+
if registry is not None:
67+
registry.clear_debug_vis_callback(self)
7268

7369
"""
7470
Properties.
@@ -135,15 +131,10 @@ def set_debug_vis(self, debug_vis: bool) -> bool:
135131
if debug_vis:
136132
# create a subscriber for the post update event if it doesn't exist
137133
if self._debug_vis_handle is None:
138-
app_interface = omni.kit.app.get_app_interface()
139-
self._debug_vis_handle = app_interface.get_post_update_event_stream().create_subscription_to_pop(
140-
lambda event, obj=weakref.proxy(self): obj._debug_vis_callback(event)
141-
)
134+
self._debug_vis_handle = self._env.sim.vis_marker_registry.add_debug_vis_callback(self)
142135
else:
143136
# remove the subscriber if it exists
144-
if self._debug_vis_handle is not None:
145-
self._debug_vis_handle.unsubscribe()
146-
self._debug_vis_handle = None
137+
self._env.sim.vis_marker_registry.clear_debug_vis_callback(self)
147138
# return success
148139
return True
149140

source/isaaclab/isaaclab/managers/command_manager.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,19 @@
88
from __future__ import annotations
99

1010
import inspect
11-
import weakref
1211
from abc import abstractmethod
1312
from collections.abc import Sequence
1413
from typing import TYPE_CHECKING
1514

1615
import torch
1716
from prettytable import PrettyTable
1817

19-
from isaaclab.utils.version import has_kit
20-
2118
from .manager_base import ManagerBase, ManagerTermBase
2219
from .manager_term_cfg import CommandTermCfg
2320

2421
if TYPE_CHECKING:
2522
from isaaclab.envs import ManagerBasedRLEnv
2623

27-
if has_kit():
28-
import omni.kit.app
29-
3024

3125
class CommandTerm(ManagerTermBase):
3226
"""The base class for implementing a command term.
@@ -65,9 +59,11 @@ def __init__(self, cfg: CommandTermCfg, env: ManagerBasedRLEnv):
6559

6660
def __del__(self):
6761
"""Unsubscribe from the callbacks."""
68-
if self._debug_vis_handle:
69-
self._debug_vis_handle.unsubscribe()
70-
self._debug_vis_handle = None
62+
env = getattr(self, "_env", None)
63+
sim = getattr(env, "sim", None)
64+
registry = getattr(sim, "vis_marker_registry", None)
65+
if registry is not None:
66+
registry.clear_debug_vis_callback(self)
7167

7268
"""
7369
Properties
@@ -106,18 +102,12 @@ def set_debug_vis(self, debug_vis: bool) -> bool:
106102
# toggle debug visualization objects
107103
self._set_debug_vis_impl(debug_vis)
108104
# toggle debug visualization handles
109-
if debug_vis and has_kit():
110-
# create a subscriber for the post update event if it doesn't exist
105+
if debug_vis:
111106
if self._debug_vis_handle is None:
112-
app_interface = omni.kit.app.get_app_interface()
113-
self._debug_vis_handle = app_interface.get_post_update_event_stream().create_subscription_to_pop(
114-
lambda event, obj=weakref.proxy(self): obj._debug_vis_callback(event)
115-
)
107+
self._debug_vis_handle = self._env.sim.vis_marker_registry.add_debug_vis_callback(self)
116108
else:
117109
# remove the subscriber if it exists
118-
if self._debug_vis_handle is not None:
119-
self._debug_vis_handle.unsubscribe()
120-
self._debug_vis_handle = None
110+
self._env.sim.vis_marker_registry.clear_debug_vis_callback(self)
121111
# return success
122112
return True
123113

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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+
"""Registry for visualization marker state."""
7+
8+
from __future__ import annotations
9+
10+
import weakref
11+
from collections.abc import Callable
12+
from typing import Any
13+
14+
15+
class VisMarkerRegistry:
16+
"""Tracks visualization marker callbacks and active marker groups."""
17+
18+
def __init__(self):
19+
self._callbacks: dict[str, Callable[[Any], None]] = {}
20+
self._groups: dict[str, Any] = {}
21+
22+
def add_callback(self, name: str, callback: Callable[[Any], None]) -> str:
23+
"""Register a callback invoked before marker-capable visualizers step each render tick."""
24+
self._callbacks[name] = callback
25+
return name
26+
27+
def add_debug_vis_callback(self, owner: Any) -> str:
28+
"""Register an owner's debug visualization callback.
29+
30+
Args:
31+
owner: Object implementing ``_debug_vis_callback(event)``.
32+
33+
Returns:
34+
Callback identifier that can be passed to :meth:`remove_callback`.
35+
"""
36+
callback_id = f"visualization_marker:{type(owner).__name__}:{id(owner)}"
37+
owner_ref = weakref.proxy(owner)
38+
return self.add_callback(callback_id, lambda event: owner_ref._debug_vis_callback(event))
39+
40+
def clear_debug_vis_callback(self, owner: Any) -> None:
41+
"""Clear an owner's registered debug visualization callback, if any."""
42+
callback_id = getattr(owner, "_debug_vis_handle", None)
43+
if callback_id is not None:
44+
self.remove_callback(callback_id)
45+
owner._debug_vis_handle = None
46+
47+
def remove_callback(self, callback_id: str) -> None:
48+
"""Remove a visualization marker callback if it exists."""
49+
self._callbacks.pop(callback_id, None)
50+
51+
def dispatch_callbacks(self, event: Any = None) -> None:
52+
"""Invoke all registered visualization marker callbacks."""
53+
for callback in list(self._callbacks.values()):
54+
callback(event)
55+
56+
def set_group(self, group_id: str, state: Any) -> None:
57+
"""Set or replace one visualization marker group state."""
58+
self._groups[group_id] = state
59+
60+
def remove_group(self, group_id: str) -> None:
61+
"""Remove one visualization marker group state if present."""
62+
self._groups.pop(group_id, None)
63+
64+
def get_groups(self) -> dict[str, Any]:
65+
"""Return all active visualization marker groups."""
66+
return self._groups

0 commit comments

Comments
 (0)