Commit 0eb83edf authored by David Hoeller's avatar David Hoeller

Fixes the camera frame delay (#124)

# Description

- Fixes the camera frame delay
- Adds `update_articulations_kinematic` in various places to make sure
link poses are always up to date

## 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
`./isaaclab.sh --format`
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] 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
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
parent e92c001b
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
[package] [package]
title = "Isaac Lab Python Headless" title = "Isaac Lab Python Headless"
description = "An app for running Isaac Lab headlessly" description = "An app for running Isaac Lab headlessly"
version = "1.1.0" version = "1.2.0"
# That makes it browsable in UI with "experience" filter # That makes it browsable in UI with "experience" filter
keywords = ["experience", "app", "isaaclab", "python", "headless"] keywords = ["experience", "app", "isaaclab", "python", "headless"]
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
[package] [package]
title = "Isaac Lab Python Headless Camera" title = "Isaac Lab Python Headless Camera"
description = "An app for running Isaac Lab headlessly with rendering enabled" description = "An app for running Isaac Lab headlessly with rendering enabled"
version = "1.1.0" version = "1.2.0"
# That makes it browsable in UI with "experience" filter # That makes it browsable in UI with "experience" filter
keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"] keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"]
...@@ -24,6 +24,10 @@ keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"] ...@@ -24,6 +24,10 @@ keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"]
"omni.kit.material.library" = {} "omni.kit.material.library" = {}
"omni.kit.viewport.rtx" = {} "omni.kit.viewport.rtx" = {}
[settings.isaaclab]
# This is used to check that this experience file is loaded when using cameras
cameras_enabled = true
[settings] [settings]
# Note: This path was adapted to be respective to the kit-exe file location # Note: This path was adapted to be respective to the kit-exe file location
app.versionFile = "${exe-path}/VERSION" app.versionFile = "${exe-path}/VERSION"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
[package] [package]
title = "Isaac Lab Python" title = "Isaac Lab Python"
description = "An app for running Isaac Lab" description = "An app for running Isaac Lab"
version = "1.1.0" version = "1.2.0"
# That makes it browsable in UI with "experience" filter # That makes it browsable in UI with "experience" filter
keywords = ["experience", "app", "usd"] keywords = ["experience", "app", "usd"]
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
[package] [package]
title = "Isaac Lab Python Camera" title = "Isaac Lab Python Camera"
description = "An app for running Isaac Lab with rendering enabled" description = "An app for running Isaac Lab with rendering enabled"
version = "1.1.0" version = "1.2.0"
# That makes it browsable in UI with "experience" filter # That makes it browsable in UI with "experience" filter
keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"] keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"]
...@@ -24,6 +24,10 @@ keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"] ...@@ -24,6 +24,10 @@ keywords = ["experience", "app", "isaaclab", "python", "camera", "minimal"]
# Rendering # Rendering
"omni.kit.material.library" = {} "omni.kit.material.library" = {}
[settings.isaaclab]
# This is used to check that this experience file is loaded when using cameras
cameras_enabled = true
[settings] [settings]
# Note: This path was adapted to be respective to the kit-exe file location # Note: This path was adapted to be respective to the kit-exe file location
app.versionFile = "${exe-path}/VERSION" app.versionFile = "${exe-path}/VERSION"
...@@ -86,7 +90,6 @@ fabricUpdateVelocities = false ...@@ -86,7 +90,6 @@ fabricUpdateVelocities = false
fabricUpdateForceSensors = false fabricUpdateForceSensors = false
fabricUpdateJointStates = false fabricUpdateJointStates = false
[settings.exts."omni.kit.registry.nucleus"] [settings.exts."omni.kit.registry.nucleus"]
registries = [ registries = [
{ name = "kit/default", url = "https://ovextensionsprod.blob.core.windows.net/exts/kit/prod/shared" }, { name = "kit/default", url = "https://ovextensionsprod.blob.core.windows.net/exts/kit/prod/shared" },
......
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.24.11" version = "0.24.12"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
...@@ -2,6 +2,19 @@ Changelog ...@@ -2,6 +2,19 @@ Changelog
--------- ---------
0.24.12 (2024-09-18)
~~~~~~~~~~~~~~~~~~~~
Fixed
^^^^^
* Fixed outdated fetching of articulation data by using the method ``update_articulations_kinematic`` in
:class:`omni.isaac.lab.assets.ArticulationData`. Before if an articulation was moved during a reset, the pose of the
links were outdated if fetched before the next physics step. Adding this method ensures that the pose of the links
is always up-to-date. Similarly ``update_articulations_kinematic`` was added before any render step to ensure that the
articulation displays correctly after a reset.
0.24.11 (2024-09-11) 0.24.11 (2024-09-11)
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
......
...@@ -304,6 +304,8 @@ class Articulation(AssetBase): ...@@ -304,6 +304,8 @@ class Articulation(AssetBase):
# convert root quaternion from wxyz to xyzw # convert root quaternion from wxyz to xyzw
root_poses_xyzw = self._data.root_state_w[:, :7].clone() root_poses_xyzw = self._data.root_state_w[:, :7].clone()
root_poses_xyzw[:, 3:] = math_utils.convert_quat(root_poses_xyzw[:, 3:], to="xyzw") root_poses_xyzw[:, 3:] = math_utils.convert_quat(root_poses_xyzw[:, 3:], to="xyzw")
# Need to invalidate the buffer to trigger the update with the new root pose.
self._data._body_state_w.timestamp = -1.0
# set into simulation # set into simulation
self.root_physx_view.set_root_transforms(root_poses_xyzw, indices=physx_env_ids) self.root_physx_view.set_root_transforms(root_poses_xyzw, indices=physx_env_ids)
...@@ -356,6 +358,8 @@ class Articulation(AssetBase): ...@@ -356,6 +358,8 @@ class Articulation(AssetBase):
self._data.joint_vel[env_ids, joint_ids] = velocity self._data.joint_vel[env_ids, joint_ids] = velocity
self._data._previous_joint_vel[env_ids, joint_ids] = velocity self._data._previous_joint_vel[env_ids, joint_ids] = velocity
self._data.joint_acc[env_ids, joint_ids] = 0.0 self._data.joint_acc[env_ids, joint_ids] = 0.0
# Need to invalidate the buffer to trigger the update with the new root pose.
self._data._body_state_w.timestamp = -1.0
# set into simulation # set into simulation
self.root_physx_view.set_dof_positions(self._data.joint_pos, indices=physx_env_ids) self.root_physx_view.set_dof_positions(self._data.joint_pos, indices=physx_env_ids)
self.root_physx_view.set_dof_velocities(self._data.joint_vel, indices=physx_env_ids) self.root_physx_view.set_dof_velocities(self._data.joint_vel, indices=physx_env_ids)
......
...@@ -48,9 +48,9 @@ class ArticulationData: ...@@ -48,9 +48,9 @@ class ArticulationData:
self._sim_timestamp = 0.0 self._sim_timestamp = 0.0
# Obtain global physics sim view # Obtain global physics sim view
physics_sim_view = physx.create_simulation_view("torch") self._physics_sim_view = physx.create_simulation_view("torch")
physics_sim_view.set_subspace_roots("/") self._physics_sim_view.set_subspace_roots("/")
gravity = physics_sim_view.get_gravity() gravity = self._physics_sim_view.get_gravity()
# Convert to direction vector # Convert to direction vector
gravity_dir = torch.tensor((gravity[0], gravity[1], gravity[2]), device=self.device) gravity_dir = torch.tensor((gravity[0], gravity[1], gravity[2]), device=self.device)
gravity_dir = math_utils.normalize(gravity_dir.unsqueeze(0)).squeeze(0) gravity_dir = math_utils.normalize(gravity_dir.unsqueeze(0)).squeeze(0)
...@@ -285,6 +285,7 @@ class ArticulationData: ...@@ -285,6 +285,7 @@ class ArticulationData:
velocities are of the articulation links's center of mass frame. velocities are of the articulation links's center of mass frame.
""" """
if self._body_state_w.timestamp < self._sim_timestamp: if self._body_state_w.timestamp < self._sim_timestamp:
self._physics_sim_view.update_articulations_kinematic()
# read data from simulation # read data from simulation
poses = self._root_physx_view.get_link_transforms().clone() poses = self._root_physx_view.get_link_transforms().clone()
poses[..., 3:7] = math_utils.convert_quat(poses[..., 3:7], to="wxyz") poses[..., 3:7] = math_utils.convert_quat(poses[..., 3:7], to="wxyz")
......
...@@ -270,6 +270,10 @@ class DirectRLEnv(gym.Env): ...@@ -270,6 +270,10 @@ class DirectRLEnv(gym.Env):
indices = torch.arange(self.num_envs, dtype=torch.int64, device=self.device) indices = torch.arange(self.num_envs, dtype=torch.int64, device=self.device)
self._reset_idx(indices) self._reset_idx(indices)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# return observations # return observations
return self._get_observations(), self.extras return self._get_observations(), self.extras
...@@ -339,6 +343,9 @@ class DirectRLEnv(gym.Env): ...@@ -339,6 +343,9 @@ class DirectRLEnv(gym.Env):
reset_env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) reset_env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1)
if len(reset_env_ids) > 0: if len(reset_env_ids) > 0:
self._reset_idx(reset_env_ids) self._reset_idx(reset_env_ids)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# post-step: step interval event # post-step: step interval event
if self.cfg.events: if self.cfg.events:
...@@ -529,10 +536,6 @@ class DirectRLEnv(gym.Env): ...@@ -529,10 +536,6 @@ class DirectRLEnv(gym.Env):
""" """
self.scene.reset(env_ids) self.scene.reset(env_ids)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if len(env_ids) > 0 and self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# apply events such as randomization for environments that need a reset # apply events such as randomization for environments that need a reset
if self.cfg.events: if self.cfg.events:
if "reset" in self.event_manager.available_modes: if "reset" in self.event_manager.available_modes:
......
...@@ -251,6 +251,10 @@ class ManagerBasedEnv: ...@@ -251,6 +251,10 @@ class ManagerBasedEnv:
indices = torch.arange(self.num_envs, dtype=torch.int64, device=self.device) indices = torch.arange(self.num_envs, dtype=torch.int64, device=self.device)
self._reset_idx(indices) self._reset_idx(indices)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# return observations # return observations
return self.observation_manager.compute(), self.extras return self.observation_manager.compute(), self.extras
...@@ -350,9 +354,7 @@ class ManagerBasedEnv: ...@@ -350,9 +354,7 @@ class ManagerBasedEnv:
""" """
# reset the internal buffers of the scene elements # reset the internal buffers of the scene elements
self.scene.reset(env_ids) self.scene.reset(env_ids)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if len(env_ids) > 0 and self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# apply events such as randomization for environments that need a reset # apply events such as randomization for environments that need a reset
if "reset" in self.event_manager.available_modes: if "reset" in self.event_manager.available_modes:
env_step_count = self._sim_step_counter // self.cfg.decimation env_step_count = self._sim_step_counter // self.cfg.decimation
......
...@@ -194,6 +194,10 @@ class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env): ...@@ -194,6 +194,10 @@ class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env):
reset_env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1) reset_env_ids = self.reset_buf.nonzero(as_tuple=False).squeeze(-1)
if len(reset_env_ids) > 0: if len(reset_env_ids) > 0:
self._reset_idx(reset_env_ids) self._reset_idx(reset_env_ids)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# -- update command # -- update command
self.command_manager.compute(dt=self.step_dt) self.command_manager.compute(dt=self.step_dt)
# -- step interval events # -- step interval events
...@@ -319,9 +323,6 @@ class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env): ...@@ -319,9 +323,6 @@ class ManagerBasedRLEnv(ManagerBasedEnv, gym.Env):
self.curriculum_manager.compute(env_ids=env_ids) self.curriculum_manager.compute(env_ids=env_ids)
# reset the internal buffers of the scene elements # reset the internal buffers of the scene elements
self.scene.reset(env_ids) self.scene.reset(env_ids)
# if sensors are added to the scene, make sure we render to reflect changes in reset
if len(env_ids) > 0 and self.sim.has_rtx_sensors() and self.cfg.rerender_on_reset:
self.sim.render()
# apply events such as randomizations for environments that need a reset # apply events such as randomizations for environments that need a reset
if "reset" in self.event_manager.available_modes: if "reset" in self.event_manager.available_modes:
env_step_count = self._sim_step_counter // self.cfg.decimation env_step_count = self._sim_step_counter // self.cfg.decimation
......
...@@ -378,12 +378,14 @@ class Camera(SensorBase): ...@@ -378,12 +378,14 @@ 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.
RuntimeError: If replicator was not found. RuntimeError: If replicator was not found.
""" """
try: carb_settings_iface = carb.settings.get_settings()
import omni.replicator.core as rep if not carb_settings_iface.get("/isaaclab/cameras_enabled"):
except ModuleNotFoundError:
raise RuntimeError( raise RuntimeError(
"Replicator was not found for rendering. Please use --enable_cameras to enable rendering." "A camera was spawned without the --enable_cameras flag. Please use --enable_cameras to enable"
" rendering."
) )
import omni.replicator.core as rep
from omni.syntheticdata.scripts.SyntheticData import SyntheticData from omni.syntheticdata.scripts.SyntheticData import SyntheticData
# Initialize parent class # Initialize parent class
......
...@@ -12,6 +12,7 @@ from collections.abc import Sequence ...@@ -12,6 +12,7 @@ from collections.abc import Sequence
from tensordict import TensorDict from tensordict import TensorDict
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
import carb
import omni.usd import omni.usd
import warp as wp import warp as wp
from omni.isaac.core.prims import XFormPrimView from omni.isaac.core.prims import XFormPrimView
...@@ -146,13 +147,15 @@ class TiledCamera(Camera): ...@@ -146,13 +147,15 @@ class TiledCamera(Camera):
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.
RuntimeError: If replicator was not found. RuntimeError: If replicator was not found.
""" """
try: carb_settings_iface = carb.settings.get_settings()
import omni.replicator.core as rep if not carb_settings_iface.get("/isaaclab/cameras_enabled"):
except ModuleNotFoundError:
raise RuntimeError( raise RuntimeError(
"Replicator was not found for rendering. Please use --enable_cameras to enable rendering." "A camera was spawned without the --enable_cameras flag. Please use --enable_cameras to enable"
" rendering."
) )
import omni.replicator.core as rep
# Initialize parent class # Initialize parent class
SensorBase._initialize_impl(self) SensorBase._initialize_impl(self)
# Create a view for the sensor # Create a view for the sensor
......
...@@ -7,6 +7,7 @@ import builtins ...@@ -7,6 +7,7 @@ import builtins
import enum import enum
import numpy as np import numpy as np
import sys import sys
import torch
import traceback import traceback
import weakref import weakref
from collections.abc import Iterator from collections.abc import Iterator
...@@ -205,6 +206,12 @@ class SimulationContext(_SimulationContext): ...@@ -205,6 +206,12 @@ class SimulationContext(_SimulationContext):
# note: we do it once here because it reads the VERSION file from disk and is not expected to change. # note: we do it once here because it reads the VERSION file from disk and is not expected to change.
self._isaacsim_version = get_version() self._isaacsim_version = get_version()
# create a tensor for gravity
# note: this line is needed to create a "tensor" in the device to avoid issues with torch 2.1 onwards.
# the issue is with some heap memory corruption when torch tensor is created inside the asset class.
# you can reproduce the issue by commenting out this line and running the test `test_articulation.py`.
self._gravity_tensor = torch.tensor(self.cfg.gravity, dtype=torch.float32, device=self.cfg.device)
# add callback to deal the simulation app when simulation is stopped. # add callback to deal the simulation app when simulation is stopped.
# this is needed because physics views go invalid once we stop the simulation # this is needed because physics views go invalid once we stop the simulation
if not builtins.ISAAC_LAUNCHED_FROM_TERMINAL: if not builtins.ISAAC_LAUNCHED_FROM_TERMINAL:
...@@ -455,7 +462,10 @@ class SimulationContext(_SimulationContext): ...@@ -455,7 +462,10 @@ class SimulationContext(_SimulationContext):
else: else:
# manually flush the fabric data to update Hydra textures # manually flush the fabric data to update Hydra textures
if self._fabric_iface is not None: if self._fabric_iface is not None:
self._fabric_iface.update(0.0, 0.0) if self.physics_sim_view is not None and self.is_playing():
# Update the articulations' link's poses before rendering
self.physics_sim_view.update_articulations_kinematic()
self._update_fabric(0.0, 0.0)
# render the simulation # render the simulation
# note: we don't call super().render() anymore because they do above operation inside # note: we don't call super().render() anymore because they do above operation inside
# and we don't want to do it twice. We may remove it once we drop support for Isaac Sim 2022.2. # and we don't want to do it twice. We may remove it once we drop support for Isaac Sim 2022.2.
...@@ -569,6 +579,15 @@ class SimulationContext(_SimulationContext): ...@@ -569,6 +579,15 @@ class SimulationContext(_SimulationContext):
# acquire fabric interface # acquire fabric interface
self._fabric_iface = get_physx_fabric_interface() self._fabric_iface = get_physx_fabric_interface()
if hasattr(self._fabric_iface, "force_update"):
# The update method in the fabric interface only performs an update if a physics step has occurred.
# However, for rendering, we need to force an update since any element of the scene might have been
# modified in a reset (which occurs after the physics step) and we want the renderer to be aware of
# these changes.
self._update_fabric = self._fabric_iface.force_update
else:
# Needed for backward compatibility with older Isaac Sim versions
self._update_fabric = self._fabric_iface.update
""" """
Callbacks. Callbacks.
......
...@@ -43,7 +43,7 @@ class ShadowHandVisionEnvCfg(ShadowHandEnvCfg): ...@@ -43,7 +43,7 @@ class ShadowHandVisionEnvCfg(ShadowHandEnvCfg):
feature_extractor = FeatureExtractorCfg() feature_extractor = FeatureExtractorCfg()
# env # env
num_observations = 188 + 27 # state observation + vision CNN embedding num_observations = 164 + 27 # state observation + vision CNN embedding
num_states = 187 + 27 # asymettric states + vision CNN embedding num_states = 187 + 27 # asymettric states + vision CNN embedding
......
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