Unverified Commit 7ac5588c authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Fixes rendering of RTX-related sensors in `RLTaskEnv` (#501)

# Description

Previously, rendering of RTX-related sensors was not happening within
the step call of the environment. Instead, it was expected that users
call `env.render()` instead. However, this was unintuitive when sensors
are involved.

Fixes https://github.com/NVIDIA-Omniverse/orbit/discussions/291

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./orbit.sh --format`
- [x] 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
- [ ] I have run all the tests with `./orbit.sh --test` and they pass
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
parent fa4a3410
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.15.10" version = "0.15.11"
# Description # Description
title = "ORBIT framework for Robot Learning" title = "ORBIT framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.15.11 (2024-04-15)
~~~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added the :meth:`omni.isaac.orbit.sim.SimulationContext.has_rtx_sensors` method to check if any
RTX-related sensors such as cameras have been created in the simulation. This is useful to determine
if simulation requires RTX rendering during step or not.
Fixed
^^^^^
* Fixed the rendering of RTX-related sensors such as cameras inside the :class:`omni.isaac.orbit.envs.RLTaskEnv` class.
Earlier the rendering did not happen inside the step function, which caused the sensor data to be empty.
0.15.10 (2024-04-11) 0.15.10 (2024-04-11)
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
......
...@@ -534,7 +534,12 @@ class AppLauncher: ...@@ -534,7 +534,12 @@ class AppLauncher:
# set carb setting to indicate orbit's offscreen_render pipeline should be enabled # set carb setting to indicate orbit's offscreen_render pipeline should be enabled
# this flag is used by the SimulationContext class to enable the offscreen_render pipeline # this flag is used by the SimulationContext class to enable the offscreen_render pipeline
# when the render() method is called. # when the render() method is called.
carb_settings_iface.set_bool("/orbit/offscreen_render/enabled", self._offscreen_render) carb_settings_iface.set_bool("/orbit/render/offscreen", self._offscreen_render)
# set carb setting to indicate no RTX sensors are used
# this flag is set to True when an RTX-rendering related sensor is created
# for example: the `Camera` sensor class
carb_settings_iface.set_bool("/orbit/render/rtx_sensors", False)
# enable extensions for off-screen rendering # enable extensions for off-screen rendering
# Depending on the app file, some extensions might not be available in it. # Depending on the app file, some extensions might not be available in it.
......
...@@ -101,7 +101,9 @@ class BaseEnv: ...@@ -101,7 +101,9 @@ class BaseEnv:
# create a simulation context to control the simulator # create a simulation context to control the simulator
if SimulationContext.instance() is None: if SimulationContext.instance() is None:
self.sim = SimulationContext(self.cfg.sim) # the type-annotation is required to avoid a type-checking error
# since it gets confused with Isaac Sim's SimulationContext class
self.sim: SimulationContext = SimulationContext(self.cfg.sim)
else: else:
raise RuntimeError("Simulation context already exists. Cannot create a new one.") raise RuntimeError("Simulation context already exists. Cannot create a new one.")
...@@ -252,7 +254,7 @@ class BaseEnv: ...@@ -252,7 +254,7 @@ class BaseEnv:
# return observations # return observations
return self.observation_manager.compute(), self.extras return self.observation_manager.compute(), self.extras
def step(self, action: torch.Tensor) -> VecEnvObs: def step(self, action: torch.Tensor) -> tuple[VecEnvObs, dict]:
"""Execute one time-step of the environment's dynamics. """Execute one time-step of the environment's dynamics.
The environment steps forward at a fixed time-step, while the physics simulation is The environment steps forward at a fixed time-step, while the physics simulation is
...@@ -280,7 +282,7 @@ class BaseEnv: ...@@ -280,7 +282,7 @@ class BaseEnv:
# update buffers at sim dt # update buffers at sim dt
self.scene.update(dt=self.physics_dt) self.scene.update(dt=self.physics_dt)
# perform rendering if gui is enabled # perform rendering if gui is enabled
if self.sim.has_gui(): if self.sim.has_gui() or self.sim.has_rtx_sensors():
self.sim.render() self.sim.render()
# post-step: step interval event # post-step: step interval event
......
...@@ -173,7 +173,7 @@ class RLTaskEnv(BaseEnv, gym.Env): ...@@ -173,7 +173,7 @@ class RLTaskEnv(BaseEnv, gym.Env):
# update buffers at sim dt # update buffers at sim dt
self.scene.update(dt=self.physics_dt) self.scene.update(dt=self.physics_dt)
# perform rendering if gui is enabled # perform rendering if gui is enabled
if self.sim.has_gui(): if self.sim.has_gui() or self.sim.has_rtx_sensors():
self.sim.render() self.sim.render()
# post-step: # post-step:
...@@ -203,7 +203,7 @@ class RLTaskEnv(BaseEnv, gym.Env): ...@@ -203,7 +203,7 @@ class RLTaskEnv(BaseEnv, gym.Env):
# return observations, rewards, resets and extras # return observations, rewards, resets and extras
return self.obs_buf, self.reward_buf, self.reset_terminated, self.reset_time_outs, self.extras return self.obs_buf, self.reward_buf, self.reset_terminated, self.reset_time_outs, self.extras
def render(self) -> np.ndarray | None: def render(self, recompute: bool = False) -> np.ndarray | None:
"""Run rendering without stepping through the physics. """Run rendering without stepping through the physics.
By convention, if mode is: By convention, if mode is:
...@@ -212,6 +212,10 @@ class RLTaskEnv(BaseEnv, gym.Env): ...@@ -212,6 +212,10 @@ class RLTaskEnv(BaseEnv, gym.Env):
- **rgb_array**: Return an numpy.ndarray with shape (x, y, 3), representing RGB values for an - **rgb_array**: Return an numpy.ndarray with shape (x, y, 3), representing RGB values for an
x-by-y pixel image, suitable for turning into a video. x-by-y pixel image, suitable for turning into a video.
Args:
recompute: Whether to force a render even if the simulator has already rendered the scene.
Defaults to False.
Returns: Returns:
The rendered image as a numpy array if mode is "rgb_array". Otherwise, returns None. The rendered image as a numpy array if mode is "rgb_array". Otherwise, returns None.
...@@ -222,7 +226,9 @@ class RLTaskEnv(BaseEnv, gym.Env): ...@@ -222,7 +226,9 @@ class RLTaskEnv(BaseEnv, gym.Env):
NotImplementedError: If an unsupported rendering mode is specified. NotImplementedError: If an unsupported rendering mode is specified.
""" """
# run a rendering step of the simulator # run a rendering step of the simulator
self.sim.render() # if we have rtx sensors, we do not need to render again sin
if not self.sim.has_rtx_sensors() and not recompute:
self.sim.render()
# decide the rendering mode # decide the rendering mode
if self.render_mode == "human" or self.render_mode is None: if self.render_mode == "human" or self.render_mode is None:
return None return None
......
...@@ -13,10 +13,10 @@ from collections.abc import Sequence ...@@ -13,10 +13,10 @@ from collections.abc import Sequence
from tensordict import TensorDict from tensordict import TensorDict
from typing import TYPE_CHECKING, Any, Literal from typing import TYPE_CHECKING, Any, Literal
import carb
import omni.kit.commands import omni.kit.commands
import omni.usd import omni.usd
from omni.isaac.core.prims import XFormPrimView from omni.isaac.core.prims import XFormPrimView
from omni.syntheticdata.scripts.SyntheticData import SyntheticData
from pxr import UsdGeom from pxr import UsdGeom
import omni.isaac.orbit.sim as sim_utils import omni.isaac.orbit.sim as sim_utils
...@@ -106,6 +106,11 @@ class Camera(SensorBase): ...@@ -106,6 +106,11 @@ class Camera(SensorBase):
# initialize base class # initialize base class
super().__init__(cfg) super().__init__(cfg)
# toggle rendering of rtx sensors as True
# this flag is read by SimulationContext to determine if rtx sensors should be rendered
carb_settings_iface = carb.settings.get_settings()
carb_settings_iface.set_bool("/orbit/render/rtx_sensors", True)
# spawn the asset # spawn the asset
if self.cfg.spawn is not None: if self.cfg.spawn is not None:
# compute the rotation offset # compute the rotation offset
...@@ -355,6 +360,7 @@ class Camera(SensorBase): ...@@ -355,6 +360,7 @@ class Camera(SensorBase):
RuntimeError: If the number of camera prims in the view does not match the number of environments. RuntimeError: If the number of camera prims in the view does not match the number of environments.
""" """
import omni.replicator.core as rep import omni.replicator.core as rep
from omni.syntheticdata.scripts.SyntheticData import SyntheticData
# Initialize parent class # Initialize parent class
super()._initialize_impl() super()._initialize_impl()
......
...@@ -147,7 +147,7 @@ class SimulationContext(_SimulationContext): ...@@ -147,7 +147,7 @@ class SimulationContext(_SimulationContext):
# read flag for whether the orbit viewport capture pipeline will be used, # read flag for whether the orbit viewport capture pipeline will be used,
# casting None to False if the flag doesn't exist # casting None to False if the flag doesn't exist
# this flag is set from the AppLauncher class # this flag is set from the AppLauncher class
self._offscreen_render = bool(carb_settings_iface.get("/orbit/offscreen_render/enabled")) self._offscreen_render = bool(carb_settings_iface.get("/orbit/render/offscreen"))
# flag for whether any GUI will be rendered (local, livestreamed or viewport) # flag for whether any GUI will be rendered (local, livestreamed or viewport)
self._has_gui = self._local_gui or self._livestream_gui self._has_gui = self._local_gui or self._livestream_gui
...@@ -237,6 +237,21 @@ class SimulationContext(_SimulationContext): ...@@ -237,6 +237,21 @@ class SimulationContext(_SimulationContext):
""" """
return self._has_gui return self._has_gui
def has_rtx_sensors(self) -> bool:
"""Returns whether the simulation has any RTX-rendering related sensors.
This function returns the value of the simulation parameter ``"/orbit/render/rtx_sensors"``.
The parameter is set to True when instances of RTX-related sensors (cameras or LiDARs) are
created using Orbit's sensor classes.
True if the simulation has RTX sensors (such as USD Cameras or LiDARs).
For more information, please check `NVIDIA RTX documentation`_.
.. _NVIDIA RTX documentation: https://www.nvidia.com/design-visualization/solutions/rendering/
"""
return self._settings.get_as_bool("/orbit/render/rtx_sensors")
def is_fabric_enabled(self) -> bool: def is_fabric_enabled(self) -> bool:
"""Returns whether the fabric interface is enabled. """Returns whether the fabric interface is enabled.
......
...@@ -92,6 +92,8 @@ class TestCamera(unittest.TestCase): ...@@ -92,6 +92,8 @@ class TestCamera(unittest.TestCase):
"""Test camera initialization.""" """Test camera initialization."""
# Create camera # Create camera
camera = Camera(self.camera_cfg) camera = Camera(self.camera_cfg)
# Check simulation parameter is set correctly
self.assertTrue(self.sim.has_rtx_sensors())
# Play sim # Play sim
self.sim.reset() self.sim.reset()
# Check if camera is initialized # Check if camera is initialized
......
...@@ -63,6 +63,7 @@ class TestSimulationContext(unittest.TestCase): ...@@ -63,6 +63,7 @@ class TestSimulationContext(unittest.TestCase):
# check valid settings # check valid settings
self.assertEqual(sim.get_physics_dt(), cfg.dt) self.assertEqual(sim.get_physics_dt(), cfg.dt)
self.assertEqual(sim.get_rendering_dt(), cfg.dt * cfg.substeps) self.assertEqual(sim.get_rendering_dt(), cfg.dt * cfg.substeps)
self.assertFalse(sim.has_rtx_sensors())
# check valid paths # check valid paths
self.assertTrue(prim_utils.is_prim_path_valid("/Physics/PhysX")) self.assertTrue(prim_utils.is_prim_path_valid("/Physics/PhysX"))
self.assertTrue(prim_utils.is_prim_path_valid("/Physics/PhysX/defaultMaterial")) self.assertTrue(prim_utils.is_prim_path_valid("/Physics/PhysX/defaultMaterial"))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment