Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds live plots to managers #893

Merged
merged 58 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bf4079c
add live plots:
renezurbruegg Jul 10, 2024
d73e0b4
enable actions and obs live plots in ManagerBasedEnv
jtigue-bdai Aug 29, 2024
a475f23
wip move live visualization out of manager inheritance
jtigue-bdai Sep 3, 2024
6d42d89
switched to a LiveVisualizationTerm instead of MixIn
jtigue-bdai Sep 4, 2024
f0e0f2b
added support for RL env command, curriculum, reward, and termination
jtigue-bdai Sep 4, 2024
ebc4493
reorganize files for live visualizer
jtigue-bdai Sep 4, 2024
e785c3e
remove ui_tools
jtigue-bdai Sep 4, 2024
8a81c0e
add UiVisualizerMixIn inheritance to ManagerLiveVisualizer
jtigue-bdai Sep 4, 2024
cbbac11
wip setting up cartpole with camera to test image viewer
jtigue-bdai Sep 5, 2024
4fbd136
add image plotter based on data shape
jtigue-bdai Sep 6, 2024
31b37a5
add configurability of which terms are visualize, defaults to all
jtigue-bdai Sep 9, 2024
be867ae
adding docstrings and adding abstract methods in manager_base
jtigue-bdai Sep 12, 2024
79985af
change how groups terms are handled in live visualizer
jtigue-bdai Sep 13, 2024
072c7cc
clean up old action manager changes
jtigue-bdai Sep 13, 2024
fc561f9
undo type annotation change
jtigue-bdai Sep 13, 2024
1c2d721
remove commented code
jtigue-bdai Sep 13, 2024
7e8f022
add EnvLiveManager to RL env and setup only if window is present
jtigue-bdai Sep 13, 2024
44b2d7c
format
jtigue-bdai Sep 13, 2024
180eacb
Merge branch 'main' into feature/manager-live-plots
jtigue-bdai Sep 18, 2024
3f4a42e
incorporate jsmith review and clean up
jtigue-bdai Sep 19, 2024
3a89418
fix rl env implementation if None cfg
jtigue-bdai Sep 20, 2024
56edd66
remove configs and env_live_vis
jtigue-bdai Oct 8, 2024
00195e7
Merge branch 'main' into feature/manager-live-plots
jtigue-bdai Oct 8, 2024
9ba5200
revert debug and testing codes in cartpole examples
jtigue-bdai Oct 8, 2024
3987e4b
add type hinting to ImagePlot
jtigue-bdai Oct 8, 2024
8d8fdfb
move manager_live_visualizer to ui and rename ui_visualizer_mixin to …
jtigue-bdai Oct 8, 2024
3a479ef
formatting
jtigue-bdai Oct 8, 2024
4fc4be5
fixes and formatting
pascal-roth Oct 11, 2024
27e88ed
formatter
pascal-roth Oct 11, 2024
821b00f
remove types
pascal-roth Oct 11, 2024
f2eceb9
Merge branch 'main' into feature/manager-live-plots
pascal-roth Oct 11, 2024
ddf37f5
fix for image
pascal-roth Oct 12, 2024
44d2e9c
formatter
pascal-roth Oct 12, 2024
12f885a
fix ui widget in headless mode
pascal-roth Oct 14, 2024
ea89f0e
formatter
pascal-roth Oct 14, 2024
a0d6475
Update source/extensions/omni.isaac.lab/omni/isaac/lab/ui/widgets/man…
pascal-roth Oct 14, 2024
79f9bcf
fix image plot
pascal-roth Oct 18, 2024
3336eeb
formatter
pascal-roth Oct 18, 2024
b3eafc5
Merge branch 'main' into feature/manager-live-plots
pascal-roth Oct 18, 2024
34a74d8
Merge branch 'main' into feature/manager-live-plots
Dhoeller19 Oct 21, 2024
bb5ffeb
Merge branch 'main' into feature/manager-live-plots
Dhoeller19 Oct 24, 2024
0d85d77
fix image plot
pascal-roth Oct 24, 2024
df59bc3
fix when no series is selected
pascal-roth Oct 25, 2024
7fbf0a3
rename normal mode
pascal-roth Oct 25, 2024
79c87b2
Update source/extensions/omni.isaac.lab/omni/isaac/lab/ui/widgets/man…
Dhoeller19 Oct 31, 2024
2151ba7
Merge remote-tracking branch 'external/main' into feature/manager-liv…
Dhoeller19 Oct 31, 2024
007044e
Merge branch 'feature/manager-live-plots' of github.com:isaac-sim/Isa…
Dhoeller19 Oct 31, 2024
c8ddced
Merge branch 'main' into feature/manager-live-plots
Dhoeller19 Oct 31, 2024
9029438
Merge branch 'main' into feature/manager-live-plots
Dhoeller19 Oct 31, 2024
124178d
Update source/extensions/omni.isaac.lab/omni/isaac/lab/ui/widgets/man…
pascal-roth Dec 13, 2024
eeea1a7
Merge branch 'main' into feature/manager-live-plots
kellyguo11 Dec 13, 2024
75e68f1
Merge branch 'main' of github.com:isaac-sim/IsaacLab into feature/man…
kellyguo11 Dec 15, 2024
4fe879a
fix docs
kellyguo11 Dec 15, 2024
63ef587
format
kellyguo11 Dec 15, 2024
80a4e48
format
kellyguo11 Dec 15, 2024
be0acf3
Merge branch 'main' into feature/manager-live-plots
kellyguo11 Dec 15, 2024
8c3d4bd
Merge branch 'main' of github.com:isaac-sim/IsaacLab into feature/man…
kellyguo11 Dec 16, 2024
f7fd969
add changelog
kellyguo11 Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ repos:
- id: pyupgrade
args: ["--py310-plus"]
# FIXME: This is a hack because Pytorch does not like: torch.Tensor | dict aliasing
exclude: "source/extensions/omni.isaac.lab/omni/isaac/lab/envs/common.py"
exclude: "source/extensions/omni.isaac.lab/omni/isaac/lab/envs/common.py|source/extensions/omni.isaac.lab/omni/isaac/lab/ui/widgets/image_plot.py"
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/omni.isaac.lab/config/extension.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

# Note: Semantic Versioning is used: https://semver.org/
version = "0.28.0"
version = "0.29.0"

# Description
title = "Isaac Lab framework for Robot Learning"
Expand Down
16 changes: 15 additions & 1 deletion source/extensions/omni.isaac.lab/docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
Changelog
---------

0.29.0 (2024-12-15)
~~~~~~~~~~~~~~~~~~~

Added
^^^^^

* Added UI interface to the Managers in the ManagerBasedEnv and MangerBasedRLEnv classes.
* Added UI widgets for :class:`LiveLinePlot` and :class:`ImagePlot`.
* Added ``ManagerLiveVisualizer/Cfg``: Given a ManagerBase (i.e. action_manager, observation_manager, etc) and a config file this class creates the the interface between managers and the UI.
* Added :class:`EnvLiveVisualizer`: A 'manager' of ManagerLiveVisualizer. This is added to the ManagerBasedEnv but is only called during the initialization of the managers in load_managers
* Added ``get_active_iterable_terms`` implementation methods to ActionManager, ObservationManager, CommandsManager, CurriculumManager, RewardManager, and TerminationManager. This method exports the active term data and labels for each manager and is called by ManagerLiveVisualizer.
* Additions to :class:`BaseEnvWindow` and :class:`RLEnvWindow` to register ManagerLiveVisualizer UI interfaces for the chosen managers.


0.28.0 (2024-12-15)
~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -93,7 +107,7 @@ Changed


0.27.21 (2024-12-06)
~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~

Fixed
^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from omni.isaac.lab.managers import ActionManager, EventManager, ObservationManager, RecorderManager
from omni.isaac.lab.scene import InteractiveScene
from omni.isaac.lab.sim import SimulationContext
from omni.isaac.lab.ui.widgets import ManagerLiveVisualizer
from omni.isaac.lab.utils.timer import Timer

from .common import VecEnvObs
Expand Down Expand Up @@ -148,6 +149,8 @@ def __init__(self, cfg: ManagerBasedEnvCfg):
# we need to do this here after all the managers are initialized
# this is because they dictate the sensors and commands right now
if self.sim.has_gui() and self.cfg.ui_window_class_type is not None:
# setup live visualizers
self.setup_manager_visualizers()
self._window = self.cfg.ui_window_class_type(self, window_name="IsaacLab")
else:
# if no window, then we don't need to store the window
Expand Down Expand Up @@ -233,6 +236,14 @@ def load_managers(self):
if self.__class__ == ManagerBasedEnv and "startup" in self.event_manager.available_modes:
self.event_manager.apply(mode="startup")

def setup_manager_visualizers(self):
"""Creates live visualizers for manager terms."""

self.manager_visualizers = {
"action_manager": ManagerLiveVisualizer(manager=self.action_manager),
"observation_manager": ManagerLiveVisualizer(manager=self.observation_manager),
}

"""
Operations - MDP.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from omni.isaac.version import get_version

from omni.isaac.lab.managers import CommandManager, CurriculumManager, RewardManager, TerminationManager
from omni.isaac.lab.ui.widgets import ManagerLiveVisualizer

from .common import VecEnvStepReturn
from .manager_based_env import ManagerBasedEnv
Expand Down Expand Up @@ -132,6 +133,18 @@ def load_managers(self):
if "startup" in self.event_manager.available_modes:
self.event_manager.apply(mode="startup")

def setup_manager_visualizers(self):
jtigue-bdai marked this conversation as resolved.
Show resolved Hide resolved
"""Creates live visualizers for manager terms."""

self.manager_visualizers = {
"action_manager": ManagerLiveVisualizer(manager=self.action_manager),
"observation_manager": ManagerLiveVisualizer(manager=self.observation_manager),
"command_manager": ManagerLiveVisualizer(manager=self.command_manager),
"termination_manager": ManagerLiveVisualizer(manager=self.termination_manager),
"reward_manager": ManagerLiveVisualizer(manager=self.reward_manager),
"curriculum_manager": ManagerLiveVisualizer(manager=self.curriculum_manager),
}

"""
Operations - MDP
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import omni.usd
from pxr import PhysxSchema, Sdf, Usd, UsdGeom, UsdPhysics

from omni.isaac.lab.ui.widgets import ManagerLiveVisualizer

if TYPE_CHECKING:
import omni.ui

Expand Down Expand Up @@ -57,6 +59,9 @@ def __init__(self, env: ManagerBasedEnv, window_name: str = "IsaacLab"):
*self.env.scene.articulations.keys(),
]

# Listeners for environment selection changes
self._ui_listeners: list[ManagerLiveVisualizer] = []

print("Creating window for environment.")
# create window for UI
self.ui_window = omni.ui.Window(
Expand All @@ -80,6 +85,10 @@ def __init__(self, env: ManagerBasedEnv, window_name: str = "IsaacLab"):
self._build_viewer_frame()
# create collapsable frame for debug visualization
self._build_debug_vis_frame()
with self.ui_window_elements["debug_frame"]:
with self.ui_window_elements["debug_vstack"]:
self._visualize_manager(title="Actions", class_name="action_manager")
self._visualize_manager(title="Observations", class_name="observation_manager")

def __del__(self):
"""Destructor for the window."""
Expand Down Expand Up @@ -200,9 +209,6 @@ def _build_debug_vis_frame(self):
that has it implemented. If the element does not have a debug visualization implemented,
a label is created instead.
"""
# import omni.isaac.ui.ui_utils as ui_utils
# import omni.ui

# create collapsable frame for debug visualization
self.ui_window_elements["debug_frame"] = omni.ui.CollapsableFrame(
title="Scene Debug Visualization",
Expand Down Expand Up @@ -234,6 +240,26 @@ def _build_debug_vis_frame(self):
if elem is not None:
self._create_debug_vis_ui_element(name, elem)

def _visualize_manager(self, title: str, class_name: str) -> None:
"""Checks if the attribute with the name 'class_name' can be visualized. If yes, create vis interface.

Args:
title: The title of the manager visualization frame.
class_name: The name of the manager to visualize.
"""

if hasattr(self.env, class_name) and class_name in self.env.manager_visualizers:
manager = self.env.manager_visualizers[class_name]
if hasattr(manager, "has_debug_vis_implementation"):
self._create_debug_vis_ui_element(title, manager)
else:
print(
f"ManagerLiveVisualizer cannot be created for manager: {class_name}, has_debug_vis_implementation"
" does not exist"
)
else:
print(f"ManagerLiveVisualizer cannot be created for manager: {class_name}, Manager does not exist")

"""
Custom callbacks for UI elements.
"""
Expand Down Expand Up @@ -357,6 +383,9 @@ def _set_viewer_env_index_fn(self, model: omni.ui.SimpleIntModel):
raise ValueError("Viewport camera controller is not initialized! Please check the rendering mode.")
# store the desired env index, UI is 1-indexed
vcc.set_view_env_index(model.as_int - 1)
# notify additional listeners
for listener in self._ui_listeners:
listener.set_env_selection(model.as_int - 1)

"""
Helper functions - UI building.
Expand All @@ -379,14 +408,30 @@ def _create_debug_vis_ui_element(self, name: str, elem: object):
alignment=omni.ui.Alignment.LEFT_CENTER,
tooltip=text,
)
has_cfg = hasattr(elem, "cfg") and elem.cfg is not None
is_checked = False
if has_cfg:
is_checked = (hasattr(elem.cfg, "debug_vis") and elem.cfg.debug_vis) or (
hasattr(elem, "debug_vis") and elem.debug_vis
)
self.ui_window_elements[f"{name}_cb"] = SimpleCheckBox(
model=omni.ui.SimpleBoolModel(),
enabled=elem.has_debug_vis_implementation,
checked=elem.cfg.debug_vis if elem.cfg else False,
checked=is_checked,
on_checked_fn=lambda value, e=weakref.proxy(elem): e.set_debug_vis(value),
)
omni.isaac.ui.ui_utils.add_line_rect_flourish()

# Create a panel for the debug visualization
if isinstance(elem, ManagerLiveVisualizer):
self.ui_window_elements[f"{name}_panel"] = omni.ui.Frame(width=omni.ui.Fraction(1))
if not elem.set_vis_frame(self.ui_window_elements[f"{name}_panel"]):
print(f"Frame failed to set for ManagerLiveVisualizer: {name}")

# Add listener for environment selection changes
if isinstance(elem, ManagerLiveVisualizer):
self._ui_listeners.append(elem)

async def _dock_window(self, window_title: str):
"""Docks the custom UI window to the property window."""
# wait for the window to be created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ def __init__(self, env: ManagerBasedRLEnv, window_name: str = "IsaacLab"):
with self.ui_window_elements["main_vstack"]:
with self.ui_window_elements["debug_frame"]:
with self.ui_window_elements["debug_vstack"]:
self._create_debug_vis_ui_element("commands", self.env.command_manager)
self._create_debug_vis_ui_element("actions", self.env.action_manager)
self._visualize_manager(title="Commands", class_name="command_manager")
self._visualize_manager(title="Rewards", class_name="reward_manager")
self._visualize_manager(title="Curriculum", class_name="curriculum_manager")
self._visualize_manager(title="Termination", class_name="termination_manager")
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def set_debug_vis(self, debug_vis: bool) -> bool:
# check if debug visualization is supported
if not self.has_debug_vis_implementation:
return False

# toggle debug visualization objects
self._set_debug_vis_impl(debug_vis)
# toggle debug visualization handles
Expand Down Expand Up @@ -262,7 +263,26 @@ def has_debug_vis_implementation(self) -> bool:
Operations.
"""

def set_debug_vis(self, debug_vis: bool) -> bool:
def get_active_iterable_terms(self, env_idx: int) -> Sequence[tuple[str, Sequence[float]]]:
jtigue-bdai marked this conversation as resolved.
Show resolved Hide resolved
"""Returns the active terms as iterable sequence of tuples.

The first element of the tuple is the name of the term and the second element is the raw value(s) of the term.

Args:
env_idx: The specific environment to pull the active terms from.

Returns:
The active terms.
"""
terms = []
idx = 0
for name, term in self._terms.items():
term_actions = self._action[env_idx, idx : idx + term.action_dim].cpu()
terms.append((name, term_actions.tolist()))
idx += term.action_dim
return terms

def set_debug_vis(self, debug_vis: bool):
"""Sets whether to visualize the action data.
Args:
debug_vis: Whether to visualize the action data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,26 @@ def has_debug_vis_implementation(self) -> bool:
Operations.
"""

def set_debug_vis(self, debug_vis: bool) -> bool:
def get_active_iterable_terms(self, env_idx: int) -> Sequence[tuple[str, Sequence[float]]]:
jtigue-bdai marked this conversation as resolved.
Show resolved Hide resolved
"""Returns the active terms as iterable sequence of tuples.

The first element of the tuple is the name of the term and the second element is the raw value(s) of the term.

Args:
env_idx: The specific environment to pull the active terms from.

Returns:
The active terms.
"""

terms = []
idx = 0
for name, term in self._terms.items():
terms.append((name, term.command[env_idx].cpu().tolist()))
idx += term.command.shape[1]
return terms

def set_debug_vis(self, debug_vis: bool):
"""Sets whether to visualize the command data.

Args:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,40 @@ def compute(self, env_ids: Sequence[int] | None = None):
state = term_cfg.func(self._env, env_ids, **term_cfg.params)
self._curriculum_state[name] = state

def get_active_iterable_terms(self, env_idx: int) -> Sequence[tuple[str, Sequence[float]]]:
"""Returns the active terms as iterable sequence of tuples.

The first element of the tuple is the name of the term and the second element is the raw value(s) of the term.

Args:
env_idx: The specific environment to pull the active terms from.

Returns:
The active terms.
"""

terms = []

for term_name, term_state in self._curriculum_state.items():
if term_state is not None:
# deal with dict
data = []

if isinstance(term_state, dict):
# each key is a separate state to log
for key, value in term_state.items():
if isinstance(value, torch.Tensor):
value = value.item()
terms[term_name].append(value)
else:
# log directly if not a dict
if isinstance(term_state, torch.Tensor):
term_state = term_state.item()
data.append(term_state)
terms.append((term_name, data))

return terms

"""
Helper functions.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,16 @@ def find_terms(self, name_keys: str | Sequence[str]) -> list[str]:
# return the matching names
return string_utils.resolve_matching_names(name_keys, list_of_strings)[1]

def get_active_iterable_terms(self, env_idx: int) -> Sequence[tuple[str, Sequence[float]]]:
"""Returns the active terms as iterable sequence of tuples.

The first element of the tuple is the name of the term and the second element is the raw value(s) of the term.

Returns:
The active terms.
"""
raise NotImplementedError

"""
Implementation specific.
"""
Expand Down
Loading
Loading