Unverified Commit 46ba0536 authored by Pascal Roth's avatar Pascal Roth Committed by GitHub

Fixes issues related to the `Camera` sensor (#260)

# Description

This MR does the following:

* Fixes pose computation in the `omni.isaac.orbit.sensors.Camera` class
to obtain them from XFormPrimView instead of using the
`UsdGeomCamera.ComputeLocalToWorldTransform` method. The latter is not
updated correctly during GPU simulation.
* Fixes initialization of the annotator info in the `Camera`.
Previously, all info dicts had the same memory address, which caused all
annotators to have the same info.
* Fixes the conversion of `uint32` warp arrays inside the
`omni.isaac.orbit.utils.array.convert_to_torch` method. PyTorch does not
support this type, so it is converted to `int32` before converting to
PyTorch tensor. This is needed to work with the replicator's output for
semantic segmentation images.
* Adds render call inside `omni.isaac.orbit.sim.SimulationContext.reset`
to initialize Replicator buffers when the simulation is reset.

## 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`
- [ ] 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 efabf203
[package]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.9.53"
version = "0.9.54"
# Description
title = "ORBIT framework for Robot Learning"
......
Changelog
---------
0.9.54 (2023-11-29)
~~~~~~~~~~~~~~~~~~~
Fixed
^^^^^
* Fixed pose computation in the :class:`omni.isaac.orbit.sensors.Camera` class to obtain them from XFormPrimView
instead of using ``UsdGeomCamera.ComputeLocalToWorldTransform`` method. The latter is not updated correctly
during GPU simulation.
* Fixed initialization of the annotator info in the class :class:`omni.isaac.orbit.sensors.Camera`. Previously
all dicts had the same memory address which caused all annotators to have the same info.
* Fixed the conversion of ``uint32`` warp arrays inside the :meth:`omni.isaac.orbit.utils.array.convert_to_torch`
method. PyTorch does not support this type, so it is converted to ``int32`` before converting to PyTorch tensor.
* Added render call inside :meth:`omni.isaac.orbit.sim.SimulationContext.reset` to initialize Replicator
buffers when the simulation is reset.
0.9.53 (2023-11-29)
~~~~~~~~~~~~~~~~~~~
......
......@@ -16,7 +16,7 @@ import omni.isaac.core.utils.prims as prim_utils
import omni.kit.commands
import omni.usd
from omni.isaac.core.prims import XFormPrimView
from pxr import Usd, UsdGeom
from pxr import UsdGeom
# omni-isaac-orbit
from omni.isaac.orbit.utils import to_camel_case
......@@ -450,7 +450,7 @@ class Camera(SensorBase):
# since the size of the output data is not known in advance, we leave it as None
# the memory will be allocated when the buffer() function is called for the first time.
self._data.output = TensorDict({}, batch_size=self._view.count, device=self.device)
self._data.info = [{name: None for name in self.cfg.data_types}] * self._view.count
self._data.info = [{name: None for name in self.cfg.data_types} for _ in range(self._view.count)]
def _update_intrinsic_matrices(self, env_ids: Sequence[int]):
"""Compute camera's matrix of intrinsic parameters.
......@@ -494,25 +494,10 @@ class Camera(SensorBase):
if len(self._sensor_prims) == 0:
raise RuntimeError("Camera prim is None. Please call 'sim.play()' first.")
# iterate over all cameras
prim_tf_all = np.zeros((len(env_ids), 4, 4))
for i in env_ids:
# obtain corresponding sensor prim
sensor_prim = self._sensor_prims[i]
# get camera's location in world space
prim_tf = sensor_prim.ComputeLocalToWorldTransform(Usd.TimeCode.Default())
# GfVec datatypes are row vectors that post-multiply matrices to effect transformations,
# which implies, for example, that it is the fourth row of a GfMatrix4d that specifies
# the translation of the transformation. Thus, we take transpose here to make it post multiply.
prim_tf = np.transpose(prim_tf)
prim_tf_all[i] = prim_tf
# extract the position (convert it to SI units-- assumed that stage units is 1.0)
self._data.pos_w[env_ids] = torch.tensor(prim_tf_all[:, 0:3, 3], device=self._device, dtype=torch.float32)
# save quat in world convention
quat_opengl = quat_from_matrix(torch.tensor(prim_tf_all[:, 0:3, 0:3], device=self._device, dtype=torch.float32))
self._data.quat_w_world[env_ids] = convert_orientation_convention(quat_opengl, origin="opengl", target="world")
# get the poses from the view
poses, quat = self._view.get_world_poses(env_ids)
self._data.pos_w[env_ids] = poses
self._data.quat_w_world[env_ids] = convert_orientation_convention(quat, origin="opengl", target="world")
def _create_annotator_data(self):
"""Create the buffers to store the annotator data.
......
......@@ -347,6 +347,14 @@ class SimulationContext(_SimulationContext):
Operations - Override (standalone)
"""
def reset(self, soft: bool = False):
super().reset(soft=soft)
# perform additional rendering steps to warm up replicator buffers
# this is only needed for the first time we set the simulation
if not soft:
for _ in range(2):
self.render()
def step(self, render: bool = True):
"""Steps the physics simulation with the pre-defined time-step.
......
......@@ -58,6 +58,10 @@ def convert_to_torch(
If ``device`` is :obj:`None`, then the function deduces the current device of the data. For numpy arrays,
this defaults to "cpu", for torch tensors it is "cpu" or "cuda", and for warp arrays it is "cuda".
Note:
Since PyTorch does not support unsigned integer types, unsigned integer arrays are converted to
signed integer arrays. This is done by casting the array to the corresponding signed integer type.
Args:
array: The input array. It can be a numpy array, warp array, python list/tuple, or torch tensor.
dtype: Target data-type for the tensor.
......@@ -67,16 +71,18 @@ def convert_to_torch(
The converted array as torch tensor.
"""
# Convert array to tensor
# if the datatype is not currently supported by torch we need to improvise
# supported types are: https://pytorch.org/docs/stable/tensors.html
if isinstance(array, torch.Tensor):
tensor = array
elif isinstance(array, np.ndarray):
# if the datatype is not currently supported by torch we need to improvise
# supported types are: https://pytorch.org/docs/stable/tensors.html
if array.dtype == np.uint32:
array = array.astype(np.int64)
# need to deal with object arrays (np.void) separatelyWWW
# need to deal with object arrays (np.void) separately
tensor = torch.from_numpy(array)
elif isinstance(array, wp.array):
if array.dtype == wp.uint32:
array = array.view(wp.int32)
tensor = wp.to_torch(array)
else:
tensor = torch.Tensor(array)
......
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