From 9a61666c8b1ec83585cba74007c409c557c1e285 Mon Sep 17 00:00:00 2001 From: Mayank Mittal <12863862+Mayankm96@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:47:11 -0400 Subject: [PATCH] Fixes the visualization markers to use the SpawnerCfg class (#162) # Description * This MR fixes the `VisualizationMarkers` class to use the `SpawnerCfg`. This makes it consistent with how assets are spawned and allows more variations. * The markers also support instanceable meshes. This is done by disabling their instancing (as PointInstancer does not support pre-instanced assets) and removing any rigid body APIs from the asset. Fixes #144 ## Type of change - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - This change requires a documentation update ## Screenshots https://github.com/isaac-orbit/orbit/assets/12863862/9d1b095b-99ee-4759-ba30-6175cc3b7d78 ## Checklist - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./orbit.sh --format` - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file --- .vscode/tools/settings.template.json | 3 +- .../omni.isaac.orbit/config/extension.toml | 2 +- .../omni.isaac.orbit/docs/CHANGELOG.rst | 16 ++ .../position_command_generator.py | 6 +- .../velocity_command_generator.py | 14 +- .../omni/isaac/orbit/markers/__init__.py | 1 + .../isaac/orbit/markers/config/__init__.py | 142 +++++++++++------- .../orbit/markers/visualization_markers.py | 118 ++++----------- .../isaac/orbit/sensors/camera/camera_cfg.py | 2 +- .../sensors/contact_sensor/contact_sensor.py | 3 +- .../orbit/sensors/ray_caster/ray_caster.py | 3 +- .../isaac/orbit/sim/loaders/urdf_loader.py | 3 +- .../omni/isaac/orbit/sim/schemas/schemas.py | 5 + .../sim/spawners/from_files/from_files.py | 50 ++++-- .../sim/spawners/from_files/from_files_cfg.py | 38 ++++- .../isaac/orbit/sim/spawners/spawner_cfg.py | 30 ++-- .../omni/isaac/orbit/sim/utils.py | 65 ++++++-- .../isaac/orbit/terrains/terrain_importer.py | 4 +- .../omni/isaac/orbit/utils/configclass.py | 8 +- .../markers/test_visualization_markers.py | 37 +++-- .../locomotion/locomotion_env_cfg.py | 6 +- source/standalone/demo/play_markers.py | 142 ++++++++++++++++++ 22 files changed, 481 insertions(+), 217 deletions(-) create mode 100644 source/standalone/demo/play_markers.py diff --git a/.vscode/tools/settings.template.json b/.vscode/tools/settings.template.json index 6e71abcf1c..7d67875bbe 100644 --- a/.vscode/tools/settings.template.json +++ b/.vscode/tools/settings.template.json @@ -50,7 +50,8 @@ "downsampled", "arange", "discretization", - "trimesh" + "trimesh", + "uninstanceable" ], // This enables python language server. Seems to work slightly better than jedi: "python.languageServer": "Pylance", diff --git a/source/extensions/omni.isaac.orbit/config/extension.toml b/source/extensions/omni.isaac.orbit/config/extension.toml index 816a63a5ef..95910d5228 100644 --- a/source/extensions/omni.isaac.orbit/config/extension.toml +++ b/source/extensions/omni.isaac.orbit/config/extension.toml @@ -1,7 +1,7 @@ [package] # Note: Semantic Versioning is used: https://semver.org/ -version = "0.9.6" +version = "0.9.7" # Description title = "ORBIT framework for Robot Learning" diff --git a/source/extensions/omni.isaac.orbit/docs/CHANGELOG.rst b/source/extensions/omni.isaac.orbit/docs/CHANGELOG.rst index bb40e88437..745b7c42eb 100644 --- a/source/extensions/omni.isaac.orbit/docs/CHANGELOG.rst +++ b/source/extensions/omni.isaac.orbit/docs/CHANGELOG.rst @@ -1,6 +1,22 @@ Changelog --------- +0.9.7 (2023-09-26) +~~~~~~~~~~~~~~~~~~ + +Fixed +^^^^^ + +* Modified the :class:`omni.isaac.orbit.markers.VisualizationMarkers` to use the + :class:`omni.isaac.orbit.sim.spawner.SpawnerCfg` class instead of their + own configuration objects. This makes it consistent with the other ways to spawn assets in the scene. + +Added +^^^^^ + +* Added the method :meth:`copy` to configclass to allow copying of configuration objects. + + 0.9.6 (2023-09-26) ~~~~~~~~~~~~~~~~~~ diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/position_command_generator.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/position_command_generator.py index 5ac096705b..cf9c612e59 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/position_command_generator.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/position_command_generator.py @@ -123,9 +123,9 @@ def _update_metrics(self): def _debug_vis_impl(self): # create the box marker if necessary if self.box_goal_visualizer is None: - marker_cfg = CUBOID_MARKER_CFG - marker_cfg.markers["cuboid"].color = (1.0, 0.0, 0.0) + marker_cfg = CUBOID_MARKER_CFG.copy() + marker_cfg.prim_path = "/Visuals/Command/position_goal" marker_cfg.markers["cuboid"].scale = (0.1, 0.1, 0.1) - self.box_goal_visualizer = VisualizationMarkers("/Visuals/Command/position_goal", marker_cfg) + self.box_goal_visualizer = VisualizationMarkers(marker_cfg) # update the box marker self.box_goal_visualizer.visualize(self.pos_command_w) diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/velocity_command_generator.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/velocity_command_generator.py index e25b992e4e..a2f4eda604 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/velocity_command_generator.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/command_generators/velocity_command_generator.py @@ -13,7 +13,7 @@ import omni.isaac.orbit.utils.math as math_utils from omni.isaac.orbit.assets import Articulation from omni.isaac.orbit.markers import VisualizationMarkers -from omni.isaac.orbit.markers.config import ARROW_X_MARKER_CFG +from omni.isaac.orbit.markers.config import BLUE_ARROW_X_MARKER_CFG, GREEN_ARROW_X_MARKER_CFG from .command_generator_base import CommandGeneratorBase @@ -163,16 +163,16 @@ def _debug_vis_impl(self): # create markers if necessary # -- goal if self.base_vel_goal_visualizer is None: - marker_cfg = ARROW_X_MARKER_CFG - marker_cfg.markers["arrow"].color = (0.0, 1.0, 0.0) + marker_cfg = GREEN_ARROW_X_MARKER_CFG.copy() + marker_cfg.prim_path = "/Visuals/Command/velocity_goal" marker_cfg.markers["arrow"].scale = (2.5, 0.1, 0.1) - self.base_vel_goal_visualizer = VisualizationMarkers("/Visuals/Command/velocity_goal", marker_cfg) + self.base_vel_goal_visualizer = VisualizationMarkers(marker_cfg) # -- current if self.base_vel_visualizer is None: - marker_cfg = ARROW_X_MARKER_CFG - marker_cfg.markers["arrow"].color = (0.0, 0.0, 1.0) + marker_cfg = BLUE_ARROW_X_MARKER_CFG.copy() + marker_cfg.prim_path = "/Visuals/Command/velocity_current" marker_cfg.markers["arrow"].scale = (2.5, 0.1, 0.1) - self.base_vel_visualizer = VisualizationMarkers("/Visuals/Command/velocity_current", marker_cfg) + self.base_vel_visualizer = VisualizationMarkers(marker_cfg) # get marker location # -- base state base_pos_w = self.robot.data.root_pos_w.clone() diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/__init__.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/__init__.py index a5484cd1d4..35d5139933 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/__init__.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/__init__.py @@ -24,6 +24,7 @@ from __future__ import annotations +from .config import * # noqa: F401, F403 from .visualization_markers import VisualizationMarkers, VisualizationMarkersCfg __all__ = ["VisualizationMarkersCfg", "VisualizationMarkers"] diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/config/__init__.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/config/__init__.py index 47d1dba569..abf1b42303 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/config/__init__.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/config/__init__.py @@ -1,86 +1,124 @@ +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + from __future__ import annotations +import omni.isaac.orbit.sim as sim_utils +from omni.isaac.orbit.markers.visualization_markers import VisualizationMarkersCfg from omni.isaac.orbit.utils.assets import ISAAC_NUCLEUS_DIR -from ..visualization_markers import VisualizationMarkersCfg +## +# Sensors. +## -FRAME_MARKER_CFG = VisualizationMarkersCfg( +RAY_CASTER_MARKER_CFG = VisualizationMarkersCfg( markers={ - "frame": VisualizationMarkersCfg.FileMarkerCfg( - usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/frame_prim.usd", - scale=(0.5, 0.5, 0.5), - ) + "hit": sim_utils.SphereCfg( + radius=0.02, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), + ), } ) -"""Configuration for the frame marker.""" +"""Configuration for the ray-caster marker.""" -POSITION_GOAL_MARKER_CFG = VisualizationMarkersCfg( + +CONTACT_SENSOR_MARKER_CFG = VisualizationMarkersCfg( markers={ - "target_far": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - color=(1.0, 0.0, 0.0), - attributes={"radius": 0.01}, + "contact": sim_utils.SphereCfg( + radius=0.02, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), ), - "target_near": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - color=(0.0, 1.0, 0.0), - attributes={"radius": 0.01}, - ), - "target_invisible": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - color=(0.0, 0.0, 1.0), - attributes={"radius": 0.01}, + "no_contact": sim_utils.SphereCfg( + radius=0.02, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), visible=False, ), - } + }, ) -"""Configuration for the end-effector tracking marker.""" +"""Configuration for the contact sensor marker.""" -RAY_CASTER_MARKER_CFG = VisualizationMarkersCfg( + +## +# Frames. +## + +FRAME_MARKER_CFG = VisualizationMarkersCfg( markers={ - "hit": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - color=(1.0, 0.0, 0.0), - attributes={"radius": 0.02}, - ), - }, + "frame": sim_utils.UsdFileCfg( + usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/frame_prim.usd", + scale=(0.5, 0.5, 0.5), + ) + } ) -"""Configuration for the ray-caster marker.""" +"""Configuration for the frame marker.""" -CONTACT_SENSOR_MARKER_CFG = VisualizationMarkersCfg( +RED_ARROW_X_MARKER_CFG = VisualizationMarkersCfg( markers={ - "contact": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - color=(1.0, 0.0, 0.0), - attributes={"radius": 0.02}, - ), - "no_contact": VisualizationMarkersCfg.MarkerCfg( - visible=False, - prim_type="Sphere", - color=(0.0, 1.0, 0.0), - attributes={"radius": 0.02}, - ), - }, + "arrow": sim_utils.UsdFileCfg( + usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd", + scale=(1.0, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), + ) + } ) -"""Configuration for the contact sensor marker.""" +"""Configuration for the red arrow marker (along x-direction).""" -ARROW_X_MARKER_CFG = VisualizationMarkersCfg( +BLUE_ARROW_X_MARKER_CFG = VisualizationMarkersCfg( markers={ - "arrow": VisualizationMarkersCfg.FileMarkerCfg( + "arrow": sim_utils.UsdFileCfg( usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd", - scale=[1.0, 0.1, 0.1], + scale=(1.0, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)), ) } ) -"""Configuration for the arrow marker (along x-direction).""" +"""Configuration for the blue arrow marker (along x-direction).""" -CUBOID_MARKER_CFG = VisualizationMarkersCfg( +GREEN_ARROW_X_MARKER_CFG = VisualizationMarkersCfg( markers={ - "cuboid": VisualizationMarkersCfg.MarkerCfg( - prim_type="Cube", + "arrow": sim_utils.UsdFileCfg( + usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/UIElements/arrow_x.usd", + scale=(1.0, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), ) } ) +"""Configuration for the green arrow marker (along x-direction).""" + + +## +# Goals. +## + +CUBOID_MARKER_CFG = VisualizationMarkersCfg( + markers={ + "cuboid": sim_utils.CuboidCfg( + size=(0.1, 0.1, 0.1), + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), + ), + } +) """Configuration for the cuboid marker.""" + +POSITION_GOAL_MARKER_CFG = VisualizationMarkersCfg( + markers={ + "target_far": sim_utils.SphereCfg( + radius=0.01, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), + ), + "target_near": sim_utils.SphereCfg( + radius=0.01, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), + ), + "target_invisible": sim_utils.SphereCfg( + radius=0.01, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 0.0, 1.0)), + visible=False, + ), + } +) +"""Configuration for the end-effector tracking marker.""" diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/visualization_markers.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/visualization_markers.py index 5608c58a84..74b5da0d96 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/visualization_markers.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/markers/visualization_markers.py @@ -21,16 +21,15 @@ import numpy as np import torch from dataclasses import MISSING -from typing import Any import omni.isaac.core.utils.prims as prim_utils import omni.isaac.core.utils.stage as stage_utils import omni.kit.commands -from omni.isaac.core.materials import PreviewSurface -from omni.isaac.core.prims import GeometryPrim +import omni.physx.scripts.utils as physx_utils from pxr import Gf, Sdf, UsdGeom, Vt -from omni.isaac.orbit.utils.assets import check_file_path +import omni.isaac.orbit.sim as sim_utils +from omni.isaac.orbit.sim.spawners import SpawnerCfg from omni.isaac.orbit.utils.configclass import configclass from omni.isaac.orbit.utils.math import convert_quat @@ -39,39 +38,10 @@ class VisualizationMarkersCfg: """A class to configure a :class:`VisualizationMarkers`.""" - @configclass - class MarkerCfg: - """A class to configure a marker prototype prim.""" + prim_path: str = MISSING + """The prim path where the :class:`UsdGeom.PointInstancer` will be created.""" - visible: bool = True - """The visibility of the marker. Defaults to True.""" - prim_type: str = MISSING - """The prim type of the marker. - - This can be any valid USD prim type, such as "Sphere" or "Cone". - """ - scale: list[float] | None = None - """The scale of the marker. Defaults to None.""" - color: list[float] | None = None - """The RGB color of the marker. Defaults to None. - - If not :obj:`None`, the marker will be colored with the given color. The color is - applied to the material of the marker prim with a preview surface shader that has a - precedence over any existing material on the prim. - """ - attributes: dict[str, Any] | None = None - """The attributes of the marker. Defaults to None.""" - - @configclass - class FileMarkerCfg(MarkerCfg): - """A class to configure a marker prototype prim from a USD file.""" - - prim_type: str = "Xform" - """The prim type of the marker. Defaults to "Xform".""" - usd_path: str = MISSING - """The path to the USD file of the marker.""" - - markers: dict[str, MarkerCfg] = MISSING + markers: dict[str, SpawnerCfg] = MISSING """The dictionary of marker configurations. The key is the name of the marker, and the value is the configuration of the marker. @@ -110,27 +80,28 @@ class VisualizationMarkers: .. code-block:: python + import omni.isaac.orbit.sim as sim_utils from omni.isaac.orbit.markers import VisualizationMarkersCfg, VisualizationMarkers # Create the markers configuration # This creates two marker prototypes, "marker1" and "marker2" which are spheres with a radius of 1.0. # The color of "marker1" is red and the color of "marker2" is green. cfg = VisualizationMarkersCfg( + prim_path="/World/Visuals/testMarkers", markers={ - "marker1": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - attributes={"radius": 1.0}, - color=(1.0, 0.0, 0.0), + "marker1": sim_utils.SphereCfg( + radius=1.0, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)), + ), + "marker2": VisualizationMarkersCfg.SphereCfg( + radius=1.0, + visual_material=sim_utils.PreviewSurfaceCfg(diffuse_color=(0.0, 1.0, 0.0)), ), - "marker2": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - attributes={"radius": 1.0}, - color=(0.0, 1.0, 0.0), } ) # Create the markers instance # This will create a UsdGeom.PointInstancer prim at the given path along with the marker prototypes. - marker = VisualizationMarkers("/World/Visuals/testMarkers", cfg) + marker = VisualizationMarkers(cfg) # Set position of the marker # -- randomly sample translations between -1.0 and 1.0 @@ -155,21 +126,24 @@ class VisualizationMarkers: """ - def __init__(self, prim_path: str, cfg: VisualizationMarkersCfg): + def __init__(self, cfg: VisualizationMarkersCfg): """Initialize the class. When the class is initialized, the :class:`UsdGeom.PointInstancer` is created into the stage and the marker prims are registered into it. + .. note:: + If a prim already exists at the given path, the function will find the next free path + and create the :class:`UsdGeom.PointInstancer` prim there. + Args: - prim_path: The prim path where the PointInstancer will be created. cfg: The configuration for the markers. Raises: ValueError: When no markers are provided in the :obj:`cfg`. """ # get next free path for the prim - prim_path = stage_utils.get_next_free_path(prim_path) + prim_path = stage_utils.get_next_free_path(cfg.prim_path) # create a new prim prim = prim_utils.define_prim(prim_path, "PointInstancer") self._instancer_manager = UsdGeom.PointInstancer(prim) @@ -189,11 +163,7 @@ def __init__(self, prim_path: str, cfg: VisualizationMarkersCfg): self._count = 1 def __str__(self) -> str: - """Return a string representation of the class. - - Returns: - A string representation of the class. - """ + """Return: A string representation of the class.""" msg = f"VisualizationMarkers(prim_path={self.prim_path})" msg += f"\n\tCount: {self.count}" msg += f"\n\tNumber of prototypes: {self.num_prototypes}" @@ -368,40 +338,19 @@ def visualize( Helper functions. """ - def _add_markers_prototypes(self, markers_cfg: dict[str, VisualizationMarkersCfg.MarkerCfg]): + def _add_markers_prototypes(self, markers_cfg: dict[str, sim_utils.SpawnerCfg]): """Adds markers prototypes to the scene and sets the markers instancer to use them.""" # add markers based on config for name, cfg in markers_cfg.items(): - # handle basic marker config - # if it is a file marker, check if the file exists - # if it is a prim marker, check that prim type is not Xform since that is an empty prim! - if not isinstance(cfg, VisualizationMarkersCfg.FileMarkerCfg): - # check if prim type is valid - if cfg.prim_type == "Xform": - raise ValueError("Please use `FileMarkerCfg` for `prim_type` of `Xform`.") - # set usd path as None - usd_path = None - else: - # check if the file exists - if not check_file_path(cfg.usd_path): - raise FileNotFoundError(f"USD file for the marker not found at: {cfg.usd_path}") - # make sure user doesn't override the prim type - if cfg.prim_type != "Xform": - raise ValueError( - f"Please use `prim_type` of `Xform` for `FileMarkerCfg`. Received: {cfg.prim_type}." - ) - # set usd path - usd_path = cfg.usd_path # resolve prim path marker_prim_path = f"{self.prim_path}/{name}" # create a child prim for the marker - prim = prim_utils.create_prim( - prim_path=marker_prim_path, - prim_type=cfg.prim_type, - usd_path=usd_path, - scale=cfg.scale, - attributes=cfg.attributes, - ) + prim = cfg.func(prim_path=marker_prim_path, cfg=cfg) + # make the asset uninstanceable (in case it is) + # point instancer defines its own prototypes so if an asset is already instanced, this doesn't work. + sim_utils.make_uninstanceable(marker_prim_path) + # remove any physics on the markers because they are only for visualization! + physx_utils.removeRigidBodySubtree(prim) # make marker invisible to secondary rays omni.kit.commands.execute( "ChangePropertyCommand", @@ -410,12 +359,5 @@ def _add_markers_prototypes(self, markers_cfg: dict[str, VisualizationMarkersCfg prev=None, type_to_create_if_not_exist=Sdf.ValueTypeNames.Bool, ) - # set visibility - prim_utils.set_prim_visibility(prim, visible=cfg.visible) - # create color attribute - if cfg.color is not None: - geom_prim = GeometryPrim(marker_prim_path) - material = PreviewSurface(f"{marker_prim_path}/material", color=np.asarray(cfg.color)) - geom_prim.apply_visual_material(material, weaker_than_descendants=False) # add child reference to point instancer self._instancer_manager.GetPrototypesRel().AddTarget(marker_prim_path) diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/camera/camera_cfg.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/camera/camera_cfg.py index f43f523aac..206821e730 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/camera/camera_cfg.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/camera/camera_cfg.py @@ -71,7 +71,7 @@ class OffsetCfg: type “class” will be retrieved. More information available at: - https://docs.omniverse.nvidia.com/app_code/prod_extensions/ext_replicator/semantic_schema_editor.html + https://docs.omniverse.nvidia.com/extensions/latest/ext_replicator/semantics_schema_editor.html#semantics-filtering """ colorize: bool = False diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/contact_sensor/contact_sensor.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/contact_sensor/contact_sensor.py index e55ee9881a..002c3fc7c3 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/contact_sensor/contact_sensor.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/contact_sensor/contact_sensor.py @@ -269,7 +269,8 @@ def _update_buffers_impl(self, env_ids: Sequence[int]): def _debug_vis_impl(self): # visualize the contacts if self.contact_visualizer is None: - self.contact_visualizer = VisualizationMarkers("/Visuals/ContactSensor", cfg=CONTACT_SENSOR_MARKER_CFG) + visualizer_cfg = CONTACT_SENSOR_MARKER_CFG.replace(prim_path="/Visuals/ContactSensor") + self.contact_visualizer = VisualizationMarkers(visualizer_cfg) # marker indices # 0: contact, 1: no contact net_contact_force_w = torch.norm(self._data.net_forces_w, dim=-1) diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/ray_caster/ray_caster.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/ray_caster/ray_caster.py index a364cbb4b7..bdad5a1d5d 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/ray_caster/ray_caster.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sensors/ray_caster/ray_caster.py @@ -211,6 +211,7 @@ def _update_buffers_impl(self, env_ids: Sequence[int]): def _debug_vis_impl(self): # visualize the point hits if self.ray_visualizer is None: - self.ray_visualizer = VisualizationMarkers("/Visuals/RayCaster", cfg=RAY_CASTER_MARKER_CFG) + visualizer_cfg = RAY_CASTER_MARKER_CFG.replace(prim_path="/Visuals/RayCaster") + self.ray_visualizer = VisualizationMarkers(visualizer_cfg) # check if prim is visualized self.ray_visualizer.visualize(self._data.ray_hits_w.view(-1, 3)) diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/loaders/urdf_loader.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/loaders/urdf_loader.py index 2c6f237b57..0307fd44f1 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/loaders/urdf_loader.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/loaders/urdf_loader.py @@ -14,6 +14,7 @@ import omni.kit.commands from omni.isaac.urdf import _urdf as omni_urdf +from omni.isaac.orbit.utils.assets import check_file_path from omni.isaac.orbit.utils.io import dump_yaml from .urdf_loader_cfg import UrdfLoaderCfg @@ -73,7 +74,7 @@ def __init__(self, cfg: UrdfLoaderCfg): ValueError: When provided URDF file does not exist. """ # check if the urdf file exists - if not os.path.isfile(cfg.urdf_path): + if not check_file_path(cfg.urdf_path): raise ValueError(f"The URDF path does not exist: ({cfg.urdf_path})!") # resolve USD directory name diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/schemas/schemas.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/schemas/schemas.py index 70311c4c5f..b61b4b3476 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/schemas/schemas.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/schemas/schemas.py @@ -384,6 +384,11 @@ def modify_mass_properties(prim_path: str, cfg: schemas_cfg.MassPropertiesCfg, s return True +""" +Contact sensor. +""" + + def activate_contact_sensors(prim_path: str, threshold: float = 0.0, stage: Usd.Stage = None): """Activate the contact sensor on all rigid bodies under a specified prim path. diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files.py index 7704130350..e50f1b2405 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files.py @@ -13,7 +13,8 @@ from pxr import Gf, Sdf, Usd from omni.isaac.orbit.sim import loaders, schemas -from omni.isaac.orbit.sim.utils import bind_physics_material, clone +from omni.isaac.orbit.sim.utils import bind_physics_material, bind_visual_material, clone +from omni.isaac.orbit.utils.assets import check_file_path if TYPE_CHECKING: from . import from_files_cfg @@ -25,7 +26,6 @@ def spawn_from_usd( cfg: from_files_cfg.UsdFileCfg, translation: tuple[float, float, float] | None = None, orientation: tuple[float, float, float, float] | None = None, - scale: tuple[float, float, float] | None = None, ) -> Usd.Prim: """Spawn an asset from a USD file and override the settings with the given config. @@ -45,16 +45,19 @@ def spawn_from_usd( prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. - translation: The translation to apply to the prim - w.r.t. its parent prim. Defaults to None. - orientation: The orientation in (w, x, y, z) to apply to - the prim w.r.t. its parent prim. Defaults to None. - scale: The scale of the imported prim. Defaults to None. + translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None. + orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None. Returns: The prim of the spawned asset. + + Raises: + FileNotFoundError: If the USD file does not exist at the given path. """ - # -- spawn asset if it doesn't exist. + # check file path exists + if not check_file_path(cfg.usd_path): + raise FileNotFoundError(f"USD file not found at path: '{cfg.usd_path}'.") + # spawn asset if it doesn't exist. if not prim_utils.is_prim_path_valid(prim_path): # add prim as reference to stage prim_utils.create_prim( @@ -62,7 +65,7 @@ def spawn_from_usd( usd_path=cfg.usd_path, translation=translation, orientation=orientation, - scale=scale, + scale=cfg.scale, ) else: carb.log_warn(f"A prim already exists at prim path: '{prim_path}'.") @@ -76,6 +79,16 @@ def spawn_from_usd( # modify articulation root properties if cfg.articulation_props is not None: schemas.modify_articulation_root_properties(prim_path, cfg.articulation_props) + # apply visual material + if cfg.visual_material is not None: + if not cfg.visual_material_path.startswith("/"): + material_path = f"{prim_path}/{cfg.visual_material_path}" + else: + material_path = cfg.visual_material_path + # create material + cfg.visual_material.func(material_path, cfg.visual_material) + # apply material + bind_visual_material(prim_path, material_path) # return the prim return prim_utils.get_prim_at_path(prim_path) @@ -86,7 +99,6 @@ def spawn_from_urdf( cfg: from_files_cfg.UrdfFileCfg, translation: tuple[float, float, float] | None = None, orientation: tuple[float, float, float, float] | None = None, - scale: tuple[float, float, float] | None = None, ) -> Usd.Prim: """Spawn an asset from a URDF file and override the settings with the given config. @@ -108,12 +120,14 @@ def spawn_from_urdf( cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None. - scale: The scale of the imported prim. Defaults to None. Returns: The prim of the spawned asset. + + Raises: + FileNotFoundError: If the URDF file does not exist at the given path. """ - # -- spawn asset if it doesn't exist. + # spawn asset if it doesn't exist. if not prim_utils.is_prim_path_valid(prim_path): # urdf loader urdf_loader = loaders.UrdfLoader(cfg) @@ -123,7 +137,7 @@ def spawn_from_urdf( usd_path=urdf_loader.usd_path, translation=translation, orientation=orientation, - scale=scale, + scale=cfg.scale, ) else: carb.log_warn(f"A prim already exists at prim path: '{prim_path}'. Skipping...") @@ -137,6 +151,16 @@ def spawn_from_urdf( # modify articulation root properties if cfg.articulation_props is not None: schemas.modify_articulation_root_properties(prim_path, cfg.articulation_props) + # apply visual material + if cfg.visual_material is not None: + if not cfg.visual_material_path.startswith("/"): + material_path = f"{prim_path}/{cfg.visual_material_path}" + else: + material_path = cfg.visual_material_path + # create material + cfg.visual_material.func(material_path, cfg.visual_material) + # apply material + bind_visual_material(prim_path, material_path) # return the prim return prim_utils.get_prim_at_path(prim_path) diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files_cfg.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files_cfg.py index ef5dcce9a4..d8ca15a9c9 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files_cfg.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/from_files/from_files_cfg.py @@ -8,9 +8,9 @@ from dataclasses import MISSING from typing import Callable -from omni.isaac.orbit.sim import loaders +from omni.isaac.orbit.sim import loaders, schemas from omni.isaac.orbit.sim.spawners import materials -from omni.isaac.orbit.sim.spawners.spawner_cfg import ArticulationSpawnerCfg, SpawnerCfg +from omni.isaac.orbit.sim.spawners.spawner_cfg import RigidObjectSpawnerCfg, SpawnerCfg from omni.isaac.orbit.utils import configclass from omni.isaac.orbit.utils.assets import ISAAC_NUCLEUS_DIR @@ -18,7 +18,37 @@ @configclass -class UsdFileCfg(ArticulationSpawnerCfg): +class FileCfg(RigidObjectSpawnerCfg): + """Configuration parameters for spawning an asset from a file. + + Note: + By default, all properties are set to None. This means that no properties will be added or modified + to the prim outside of the properties available by default when spawning the prim. + """ + + scale: tuple[float, float, float] | None = None + """Scale of the asset. Defaults to None, in which case the scale is not modified.""" + + articulation_props: schemas.ArticulationPropertiesCfg | None = None + """Properties to apply to the articulation root.""" + + visual_material_path: str = "material" + """Path to the visual material to use for the prim. Defaults to "material". + + If the path is relative, then it will be relative to the prim's path. + This parameter is ignored if `visual_material` is not None. + """ + + visual_material: materials.VisualMaterialCfg | None = None + """Visual material properties to override the visual material properties in the URDF file. + + Note: + If None, then no visual material will be added. + """ + + +@configclass +class UsdFileCfg(FileCfg): """USD file to spawn asset from. See :meth:`spawn_from_usd` for more information. @@ -35,7 +65,7 @@ class UsdFileCfg(ArticulationSpawnerCfg): @configclass -class UrdfFileCfg(ArticulationSpawnerCfg, loaders.UrdfLoaderCfg): +class UrdfFileCfg(FileCfg, loaders.UrdfLoaderCfg): """URDF file to spawn asset from. It uses the :class:`UrdfLoader` class to create a USD file from URDF and spawns the imported diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/spawner_cfg.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/spawner_cfg.py index 4ff39d72f8..1b18ff4c2c 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/spawner_cfg.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/spawners/spawner_cfg.py @@ -8,9 +8,10 @@ from dataclasses import MISSING from typing import Callable -from omni.isaac.orbit.utils import configclass +from pxr import Usd -from .. import schemas +from omni.isaac.orbit.sim import schemas +from omni.isaac.orbit.utils import configclass @configclass @@ -27,8 +28,15 @@ class SpawnerCfg: parameter. """ - func: Callable = MISSING - """Function to use for spawning the asset.""" + func: Callable[..., Usd.Prim] = MISSING + """Function to use for spawning the asset. + + The function takes in the prim path (or expression) to spawn the asset at, the configuration instance + and transformation, and returns the source prim spawned. + """ + + visible: bool = True + """Whether the spawned asset should be visible. Defaults to True.""" copy_from_source: bool = True """Whether to copy the asset from the source prim or inherit it. Defaults to True. @@ -58,21 +66,9 @@ class RigidObjectSpawnerCfg(SpawnerCfg): """Rigid body properties.""" collision_props: schemas.CollisionPropertiesCfg | None = None """Properties to apply to all collision meshes.""" + activate_contact_sensors: bool = False """Activate contact reporting on all rigid bodies. Defaults to False. This adds the PhysxContactReporter API to all the rigid bodies in the given prim path and its children. """ - - -@configclass -class ArticulationSpawnerCfg(RigidObjectSpawnerCfg): - """Configuration parameters for spawning an articulation asset. - - Note: - By default, all properties are set to None. This means that no properties will be added or modified - to the prim outside of the properties available by default when spawning the prim. - """ - - articulation_props: schemas.ArticulationPropertiesCfg | None = None - """Properties to apply to the articulation root.""" diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/utils.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/utils.py index 11932225c1..c43b8aca7c 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/utils.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/sim/utils.py @@ -130,16 +130,20 @@ def apply_nested(func: Callable) -> Callable: Args: func: The function to apply to all prims under a specified prim-path. The function - must take the prim-path, the configuration object and the stage as inputs. It should return - a boolean indicating whether the function succeeded or not. + must take the prim-path and other arguments. It should return a boolean indicating whether + the function succeeded or not. Returns: The wrapped function that applies the function to all prims under a specified prim-path. + + Raises: + ValueError: If the prim-path does not exist on the stage. """ @functools.wraps(func) - def wrapper(prim_path: str, cfg: object, stage: Usd.Stage | None = None, **kwargs): + def wrapper(prim_path: str, *args, **kwargs): # get current stage + stage = kwargs.get("stage") if stage is None: stage = stage_utils.get_current_stage() # get USD prim @@ -160,7 +164,7 @@ def wrapper(prim_path: str, cfg: object, stage: Usd.Stage | None = None, **kwarg carb.log_warn(f"Cannot perform '{func.__name__}' on instanced prim: '{child_prim_path}'") continue # set properties - success = func(child_prim_path, cfg, stage=stage, **kwargs) + success = func(child_prim_path, *args, **kwargs) # if successful, do not look at children # this is based on the physics behavior that nested schemas are not allowed if not success: @@ -186,7 +190,7 @@ def clone(func: Callable) -> Callable: Returns: The decorated function that spawns the prim and clones it at each matching prim path. - It returns the spawned prim. + It returns the spawned source prim, i.e., the first prim in the list of matching prim paths. """ @functools.wraps(func) @@ -213,6 +217,9 @@ def wrapper(prim_path: str, cfg: SpawnerCfg, *args, **kwargs): prim_paths = [f"{source_prim_path}/{asset_path}" for source_prim_path in source_prim_paths] # spawn single instance prim = func(prim_paths[0], cfg, *args, **kwargs) + # set the prim visibility + if hasattr(cfg, "visible"): + prim_utils.set_prim_visibility(prim, cfg.visible) # activate rigid body contact sensors if hasattr(cfg, "activate_contact_sensors") and cfg.activate_contact_sensors: schemas.activate_contact_sensors(prim_paths[0], cfg.activate_contact_sensors) @@ -255,8 +262,8 @@ def bind_visual_material( Args: prim_path: The prim path where to apply the material. material_path: The prim path of the material to apply. - stage: The stage where the prim and material exist. Defaults to None, - in which case the current stage is used. + stage: The stage where the prim and material exist. + Defaults to None, in which case the current stage is used. stronger_than_descendants: Whether the material should override the material of its descendants. Defaults to True. @@ -309,8 +316,8 @@ def bind_physics_material( Args: prim_path: The prim path where to apply the material. material_path: The prim path of the material to apply. - stage: The stage where the prim and material exist. Defaults to None, - in which case the current stage is used. + stage: The stage where the prim and material exist. + Defaults to None, in which case the current stage is used. stronger_than_descendants: Whether the material should override the material of its descendants. Defaults to True. @@ -355,3 +362,43 @@ def bind_physics_material( material_binding_api.Bind(material, bindingStrength=binding_strength, materialPurpose="physics") # type: ignore # return success return True + + +""" +USD Prim properties. +""" + + +def make_uninstanceable(prim_path: str, stage: Usd.Stage | None = None): + """Check if a prim and its descendants are instanced and make them uninstanceable. + + This function checks if the prim at the specified prim path and its descendants are instanced. + If so, it makes the respective prim uninstanceable by disabling instancing on the prim. + + This is useful when we want to modify the properties of a prim that is instanced. For example, if we + want to apply a different material on an instanced prim, we need to make the prim uninstanceable first. + + Args: + prim_path: The prim path to check. + stage: The stage where the prim exists. + Defaults to None, in which case the current stage is used. + """ + # get current stage + if stage is None: + stage = stage_utils.get_current_stage() + # get prim + prim: Usd.Prim = stage.GetPrimAtPath(prim_path) + # check if prim is valid + if not prim.IsValid(): + raise ValueError(f"Prim at path '{prim_path}' is not valid.") + # iterate over all prims under prim-path + all_prims = [prim] + while len(all_prims) > 0: + # get current prim + child_prim = all_prims.pop(0) + # check if prim is instanced + if child_prim.IsInstance(): + # make the prim uninstanceable + child_prim.SetInstanceable(False) + # add children to list + all_prims += child_prim.GetChildren() diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/terrains/terrain_importer.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/terrains/terrain_importer.py index bea703031d..f1846b7dda 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/terrains/terrain_importer.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/terrains/terrain_importer.py @@ -80,7 +80,9 @@ def __init__(self, cfg: TerrainImporterCfg): self.terrain_origins = None # marker for visualization if self.cfg.debug_vis: - self.origin_visualizer = VisualizationMarkers("/Visuals/TerrainOrigin", cfg=FRAME_MARKER_CFG) + self.origin_visualizer = VisualizationMarkers( + cfg=FRAME_MARKER_CFG.replace(prim_path="/Visuals/TerrainOrigin") + ) else: self.origin_visualizer = None diff --git a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/utils/configclass.py b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/utils/configclass.py index 3860386505..2f0b8a40c6 100644 --- a/source/extensions/omni.isaac.orbit/omni/isaac/orbit/utils/configclass.py +++ b/source/extensions/omni.isaac.orbit/omni/isaac/orbit/utils/configclass.py @@ -16,7 +16,7 @@ __all__ = ["configclass"] -_CONFIGCLASS_METHODS = ["to_dict", "from_dict", "replace"] +_CONFIGCLASS_METHODS = ["to_dict", "from_dict", "replace", "copy"] """List of class methods added at runtime to dataclass.""" """ @@ -83,6 +83,7 @@ class EnvCfg: setattr(cls, "to_dict", _class_to_dict) setattr(cls, "from_dict", _update_class_from_dict) setattr(cls, "replace", _replace_class_with_kwargs) + setattr(cls, "copy", _copy_class) # wrap around dataclass cls = dataclass(cls, **kwargs) # return wrapped class @@ -145,6 +146,11 @@ class C: return replace(obj, **kwargs) +def _copy_class(obj: object) -> object: + """Return a new object with the same fields as the original.""" + return replace(obj) + + """ Private helper functions. """ diff --git a/source/extensions/omni.isaac.orbit/test/markers/test_visualization_markers.py b/source/extensions/omni.isaac.orbit/test/markers/test_visualization_markers.py index baa9fda099..84b4288a79 100644 --- a/source/extensions/omni.isaac.orbit/test/markers/test_visualization_markers.py +++ b/source/extensions/omni.isaac.orbit/test/markers/test_visualization_markers.py @@ -22,8 +22,10 @@ import unittest import carb +import omni.isaac.core.utils.stage as stage_utils from omni.isaac.core.simulation_context import SimulationContext +import omni.isaac.orbit.sim as sim_utils from omni.isaac.orbit.markers import VisualizationMarkers, VisualizationMarkersCfg from omni.isaac.orbit.markers.config import FRAME_MARKER_CFG, POSITION_GOAL_MARKER_CFG from omni.isaac.orbit.utils.math import random_orientation @@ -37,6 +39,8 @@ def setUp(self): """Create a blank new stage for each test.""" # Simulation time-step self.dt = 0.01 + # Open a new stage + stage_utils.create_new_stage() # Load kit helper self.sim = SimulationContext(physics_dt=self.dt, rendering_dt=self.dt, backend="torch", device="cuda:0") @@ -44,19 +48,20 @@ def tearDown(self) -> None: """Stops simulator after each test.""" # stop simulation self.sim.stop() - self.sim.clear() + # close stage + stage_utils.close_stage() + # clear the simulation context + self.sim.clear_instance() def test_instantiation(self): """Test that the class can be initialized properly.""" config = VisualizationMarkersCfg( + prim_path="/World/Visuals/test", markers={ - "test": VisualizationMarkersCfg.MarkerCfg( - prim_type="Sphere", - attributes={"radius": 1.0}, - ) - } + "test": sim_utils.SphereCfg(radius=1.0), + }, ) - test_marker = VisualizationMarkers("/World/Visuals/test", config) + test_marker = VisualizationMarkers(config) print(test_marker) # check number of markers self.assertEqual(test_marker.num_prototypes, 1) @@ -64,7 +69,8 @@ def test_instantiation(self): def test_usd_marker(self): """Test with marker from a USD.""" # create a marker - test_marker = VisualizationMarkers("/World/Visuals/test_frames", FRAME_MARKER_CFG) + config = FRAME_MARKER_CFG.replace(prim_path="/World/Visuals/test_frames") + test_marker = VisualizationMarkers(config) # play the simulation self.sim.reset() @@ -87,8 +93,10 @@ def test_usd_marker(self): def test_usd_marker_color(self): """Test with marker from a USD with its color modified.""" # create a marker - FRAME_MARKER_CFG.markers["frame"].color = (0.0, 1.0, 0.0) - test_marker = VisualizationMarkers("/World/Visuals/test_frames", FRAME_MARKER_CFG) + config = FRAME_MARKER_CFG.copy() + config.prim_path = "/World/Visuals/test_frames" + config.markers["frame"].visual_material = sim_utils.PreviewSurfaceCfg(diffuse_color=(1.0, 0.0, 0.0)) + test_marker = VisualizationMarkers(config) # play the simulation self.sim.reset() @@ -107,7 +115,8 @@ def test_usd_marker_color(self): def test_multiple_prototypes_marker(self): """Test with multiple prototypes of spheres.""" # create a marker - test_marker = VisualizationMarkers("/World/Visuals/test_protos", POSITION_GOAL_MARKER_CFG) + config = POSITION_GOAL_MARKER_CFG.replace(prim_path="/World/Visuals/test_protos") + test_marker = VisualizationMarkers(config) # play the simulation self.sim.reset() @@ -128,7 +137,8 @@ def test_visualization_time_based_on_prototypes(self): """Test with time taken when number of prototypes is increased.""" # create a marker - test_marker = VisualizationMarkers("/World/Visuals/test_protos", POSITION_GOAL_MARKER_CFG) + config = POSITION_GOAL_MARKER_CFG.replace(prim_path="/World/Visuals/test_protos") + test_marker = VisualizationMarkers(config) # play the simulation self.sim.reset() @@ -160,7 +170,8 @@ def test_visualization_time_based_on_visibility(self): """Test with visibility of markers. When invisible, the visualize call should return.""" # create a marker - test_marker = VisualizationMarkers("/World/Visuals/test_protos", POSITION_GOAL_MARKER_CFG) + config = POSITION_GOAL_MARKER_CFG.replace(prim_path="/World/Visuals/test_protos") + test_marker = VisualizationMarkers(config) # play the simulation self.sim.reset() diff --git a/source/extensions/omni.isaac.orbit_envs/omni/isaac/orbit_envs/locomotion/locomotion_env_cfg.py b/source/extensions/omni.isaac.orbit_envs/omni/isaac/orbit_envs/locomotion/locomotion_env_cfg.py index 0760978ecd..bdfcf95b67 100644 --- a/source/extensions/omni.isaac.orbit_envs/omni/isaac/orbit_envs/locomotion/locomotion_env_cfg.py +++ b/source/extensions/omni.isaac.orbit_envs/omni/isaac/orbit_envs/locomotion/locomotion_env_cfg.py @@ -66,10 +66,10 @@ class TerrainSceneCfg(InteractiveSceneCfg): offset=RayCasterCfg.OffsetCfg(pos=(0.0, 0.0, 20.0)), attach_yaw_only=True, pattern_cfg=patterns.GridPatternCfg(resolution=0.1, size=[1.6, 1.0]), - debug_vis=False, + debug_vis=True, mesh_prim_paths=["/World/ground"], ) - contact_forces = ContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, debug_vis=False) + contact_forces = ContactSensorCfg(prim_path="{ENV_REGEX_NS}/Robot/.*", history_length=3, debug_vis=True) # lights light = AssetBaseCfg( prim_path="/World/light", @@ -274,7 +274,7 @@ class LocomotionEnvRoughCfg(RLEnvCfg): rel_standing_envs=0.02, rel_heading_envs=1.0, heading_command=True, - debug_vis=False, + debug_vis=True, ranges=UniformVelocityCommandGeneratorCfg.Ranges( lin_vel_x=(-1.0, 1.0), lin_vel_y=(-1.0, 1.0), ang_vel_z=(-1.0, 1.0), heading=(-math.pi, math.pi) ), diff --git a/source/standalone/demo/play_markers.py b/source/standalone/demo/play_markers.py new file mode 100644 index 0000000000..9472078e68 --- /dev/null +++ b/source/standalone/demo/play_markers.py @@ -0,0 +1,142 @@ +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""This script demonstrates how to create different types of markers in Orbit.""" + +from __future__ import annotations + +"""Launch Isaac Sim Simulator first.""" + + +import argparse + +from omni.isaac.orbit.app import AppLauncher + +# add argparse arguments +parser = argparse.ArgumentParser( + description="This script demonstrates how to create different types of markers in Orbit." +) +parser.add_argument("--headless", action="store_true", default=False, help="Force display off at all times.") +args_cli = parser.parse_args() + +# launch omniverse app +app_launcher = AppLauncher(headless=args_cli.headless) +simulation_app = app_launcher.app + +"""Rest everything follows.""" + +import torch + +import omni.isaac.orbit.sim as sim_utils +from omni.isaac.orbit.markers import VisualizationMarkers, VisualizationMarkersCfg +from omni.isaac.orbit.sim import SimulationContext +from omni.isaac.orbit.utils.assets import ISAAC_NUCLEUS_DIR +from omni.isaac.orbit.utils.math import quat_from_angle_axis + + +def main(): + """Spawns lights in the stage and sets the camera view.""" + + # Load kit helper + sim = SimulationContext(sim_utils.SimulationCfg(dt=0.01, substeps=1)) + # Set main camera + sim.set_camera_view([0.0, 17.0, 12.0], [0.0, 2.0, 0.0]) + + # Spawn things into stage + # Lights-1 + cfg = sim_utils.SphereLightCfg(intensity=600.0, color=(0.75, 0.75, 0.75), radius=2.5) + cfg.func("/World/Light/greyLight", cfg, translation=(4.5, 3.5, 10.0)) + # Lights-2 + cfg = sim_utils.SphereLightCfg(intensity=600.0, color=(1.0, 1.0, 1.0), radius=2.5) + cfg.func("/World/Light/whiteSphere", cfg, translation=(-4.5, 3.5, 10.0)) + + # Create 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)), + ), + }, + ) + my_visualizer = VisualizationMarkers(marker_cfg) + + # marker locations + num_marker_types = len(marker_cfg.markers) + 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 = (num_marker_types - 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(num_marker_types).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) + # perform step + sim.step() + # increment yaw + yaw += 0.01 + + +if __name__ == "__main__": + # Run empty stage + main() + # Close the simulator + simulation_app.close()