Unverified Commit 576b1ea4 authored by Mayank Mittal's avatar Mayank Mittal Committed by GitHub

Adds documentation on sensors (#5)

* adds sensor docs
* adds docs for array ops
* fixes docs about the device used for camera
parent 199a1444
......@@ -78,7 +78,7 @@ autodoc_default_options = {
# generate links to the documentation of objects in external projects
intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"numpy": ("http://docs.scipy.org/doc/numpy/", None),
"numpy": ("http://docs.scipy.org/doc/numpy", None),
"torch": ("https://pytorch.org/docs/stable/", None),
}
......@@ -94,6 +94,7 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "README.md", "licenses/*
autodoc_mock_imports = [
"torch",
"numpy",
"matplotlib",
"scipy",
"carb",
"warp",
......
......@@ -10,3 +10,4 @@ sphinx-copybutton
# basic python
numpy
matplotlib
warp-lang
......@@ -86,3 +86,14 @@
pages={2497-2503},
doi={10.1109/IROS47612.2022.9981198}
}
@ARTICLE{frankhauser2018probabilistic,
author={Fankhauser, Péter and Bloesch, Michael and Hutter, Marco},
journal={IEEE Robotics and Automation Letters},
title={Probabilistic Terrain Mapping for Mobile Robots With Uncertain Localization},
year={2018},
volume={3},
number={4},
pages={3019-3026},
doi={10.1109/LRA.2018.2849506}
}
......@@ -14,6 +14,7 @@ omni.isaac.orbit extension
orbit.objects.articulated
orbit.objects.rigid
orbit.robots
orbit.sensors
orbit.utils
orbit.utils.assets
orbit.utils.kit
......
omni.isaac.orbit.sensors
========================
Sensor Base
-----------
.. automodule:: omni.isaac.orbit.sensors.sensor_base
:members:
:undoc-members:
:show-inheritance:
Camera
------
.. autoclass:: omni.isaac.orbit.sensors.camera.camera.Camera
:members:
:undoc-members:
:show-inheritance:
Data
^^^^
.. autoclass:: omni.isaac.orbit.sensors.camera.camera.CameraData
:members:
:undoc-members:
:show-inheritance:
Configuration
^^^^^^^^^^^^^
.. automodule:: omni.isaac.orbit.sensors.camera.camera_cfg
:members:
:undoc-members:
:show-inheritance:
Utilities
^^^^^^^^^
.. automodule:: omni.isaac.orbit.sensors.camera.utils
:members:
:undoc-members:
:show-inheritance:
Height Scanner
--------------
.. autoclass:: omni.isaac.orbit.sensors.height_scanner.height_scanner.HeightScanner
:members:
:undoc-members:
:show-inheritance:
Data
^^^^
.. autoclass:: omni.isaac.orbit.sensors.height_scanner.height_scanner.HeightScannerData
:members:
:undoc-members:
:show-inheritance:
Configuration
^^^^^^^^^^^^^
.. automodule:: omni.isaac.orbit.sensors.height_scanner.height_scanner_cfg
:members:
:undoc-members:
:show-inheritance:
Utilities
^^^^^^^^^
.. automodule:: omni.isaac.orbit.sensors.height_scanner.utils
:members:
:undoc-members:
:show-inheritance:
......@@ -19,6 +19,14 @@ Loading and saving data
:undoc-members:
:show-inheritance:
Array operations
~~~~~~~~~~~~~~~~
.. automodule:: omni.isaac.orbit.utils.array
:members:
:undoc-members:
:show-inheritance:
Dictionary operations
~~~~~~~~~~~~~~~~~~~~~
......
......@@ -4,7 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause
"""
Camera.
Camera wrapper around USD camera prim to provide an interface that follows the robotics convention.
"""
from .camera import Camera, CameraData
......
......@@ -28,6 +28,8 @@ from omni.isaac.orbit.utils.math import convert_quat
from ..sensor_base import SensorBase
from .camera_cfg import FisheyeCameraCfg, PinholeCameraCfg
__all__ = ["Camera", "CameraData"]
@dataclass
class CameraData:
......@@ -44,55 +46,52 @@ class CameraData:
output: Dict[str, Any] = None
"""The retrieved sensor data with sensor types as key.
The format of the data is available at [1].
The format of the data is available in the `Replicator Documentation`_.
References:
[1] https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_replicator/annotators_details.html#annotator-output
.. _Replicator Documentation: https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_replicator/annotators_details.html#annotator-output
"""
class Camera(SensorBase):
"""The camera sensor for acquiring visual data.
Summarizing from the replicator extension [1], the following sensor types are supported:
Summarizing from the `replicator extension`_, the following sensor types are supported:
- "rgb": A rendered color image.
- "distance_to_camera": An image containing the distance to camera optical center.
- "distance_to_image_plane": An image containing distances of 3D points from camera plane along camera's z-axis.
- "normals": An image containing the local surface normal vectors at each pixel.
- "motion_vectors": An image containing the motion vector data at each pixel.
- "instance_segmentation": The instance segmentation data.
- "semantic_segmentation": The semantic segmentation data.
- "bounding_box_2d_tight": The tight 2D bounding box data (only contains non-occluded regions).
- "bounding_box_2d_loose": The loose 2D bounding box data (contains occluded regions).
- "bounding_box_3d": The 3D view space bounding box data.
- "occlusion": The occlusion information (such as instance id, semantic id and occluded ratio).
- ``"rgb"``: A rendered color image.
- ``"distance_to_camera"``: An image containing the distance to camera optical center.
- ``"distance_to_image_plane"``: An image containing distances of 3D points from camera plane along camera's z-axis.
- ``"normals"``: An image containing the local surface normal vectors at each pixel.
- ``"motion_vectors"``: An image containing the motion vector data at each pixel.
- ``"instance_segmentation"``: The instance segmentation data.
- ``"semantic_segmentation"``: The semantic segmentation data.
- ``"bounding_box_2d_tight"``: The tight 2D bounding box data (only contains non-occluded regions).
- ``"bounding_box_2d_loose"``: The loose 2D bounding box data (contains occluded regions).
- ``"bounding_box_3d"``: The 3D view space bounding box data.
- ``"occlusion"``: The occlusion information (such as instance id, semantic id and occluded ratio).
Typically, the sensor comprises of two prims:
1. Camera rig: A dummy Xform prim to which the camera is attached to.
2. Camera prim: An instance of the USDGeom Camera. [3]
1. **Camera rig**: A dummy Xform prim to which the camera is attached to.
2. **Camera prim**: An instance of the `USDGeom Camera`_.
However, for the sake of generality, we allow omission of the camera rig prim. This is mostly the case when
the camera is static. In such cases, any request to set the camera pose is directly set on the camera prim,
instead of setting the pose of the camera rig Xform prim.
Reference:
[1] https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_replicator/annotators_details.html#annotator-output
[2] https://graphics.pixar.com/usd/docs/api/class_usd_geom_camera.html
.. _replicator extension: https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_replicator/annotators_details.html#annotator-output
.. _USDGeom Camera: https://graphics.pixar.com/usd/docs/api/class_usd_geom_camera.html
"""
def __init__(self, cfg: Union[PinholeCameraCfg, FisheyeCameraCfg], device: str = "cpu"):
"""Initializes the scanner object.
"""Initializes the camera sensor.
Warning:
Replicator currently ignores the device and returns the data only on cpu.
This behavior will be fixed in the future, when replicator improves.
If the ``device`` is ``"cpu"``, the output data is returned as a numpy array. If the ``device`` is
``"cuda"``, then a Warp array is returned. Note that only the valid sensor types will be moved to GPU.
Args:
cfg (Union[PinholeCameraCfg, FisheyeCameraCfg]): The configuration parameters.
device (str): The device on which to receive data.
device (str): The device on which to receive data. Defaults to "cpu".
"""
# store inputs
self.cfg = cfg
......@@ -184,10 +183,9 @@ class Camera(SensorBase):
def set_intrinsic_matrix(self, matrix: np.ndarray, focal_length: float = 1.0):
"""Set parameters of the USD camera from its intrinsic matrix.
Note:
Due to limitations of Omniverse camera, we need to assume that the camera is a spherical lens,
i.e. has square pixels, and the optical center is centered at the camera eye. If this assumption
is not true in the input intrinsic matrix, then the camera will not set up correctly.
Due to limitations of Omniverse camera, we need to assume that the camera is a spherical lens,
i.e. has square pixels, and the optical center is centered at the camera eye. If this assumption
is not true in the input intrinsic matrix, then the camera will not set up correctly.
Args:
intrinsic_matrix (np.ndarray): The intrinsic matrix for the camera.
......@@ -237,7 +235,7 @@ class Camera(SensorBase):
quat (Sequence[float], optional): The quaternion orientation in (w, x, y, z). Defaults to None.
Raises:
RuntimeError: If the camera prim is not set. Need to call `initialize(...)` first.
RuntimeError: If the camera prim is not set. Need to call :meth:`initialize` method first.
"""
# add note that this function is not working correctly
# FIXME: Fix this function. Getting the camera pose and setting back over here doesn't work.
......@@ -263,7 +261,7 @@ class Camera(SensorBase):
# set the pose
if self._sensor_rig_prim is None:
# Note: Technically, we should prefer not to do this.
cam_prim = XFormPrimView(self.prim_path, reset_xform_properties=False)
cam_prim = XFormPrimView(self.prim_path, reset_xform_properties=True)
cam_prim.set_world_poses(pos, quat_gl)
else:
self._sensor_rig_prim.set_world_poses(pos, quat_gl)
......@@ -281,11 +279,11 @@ class Camera(SensorBase):
yaw (float): Yaw angle in degrees (up, down).
pitch (float): Pitch angle in degrees around up vector.
roll (float): Roll angle in degrees around forward vector.
up_axis (str): Either 'y/Y' or 'z/Z' axis up.
up_axis (str): The up axis for the camera. Either 'y', 'Y' or 'z', 'Z' axis.
Raises:
RuntimeError: If the camera prim is not set. Need to call `initialize(...)` first.
ValueError: When the `up_axis` is not "y/Y" or "z/Z".
RuntimeError: If the camera prim is not set. Need to call :meth:`initialize` method first.
ValueError: When the ``up_axis`` is not "y/Y" or "z/Z".
"""
# sanity conversion
camera_target_position = np.asarray(target_position)
......@@ -351,6 +349,9 @@ class Camera(SensorBase):
def spawn(self, parent_prim_path: str, translation: Sequence[float] = None, orientation: Sequence[float] = None):
"""Spawns the sensor into the stage.
The sensor is spawned under the parent prim at the path ``parent_prim_path`` with the provided input
rotation and translation. The USD Camera prim is attached to the parent prim.
Args:
parent_prim_path (str): The path of the parent prim to attach sensor to.
translation (Sequence[float], optional): The local position offset w.r.t. parent prim. Defaults to None.
......@@ -387,6 +388,14 @@ class Camera(SensorBase):
def initialize(self, cam_prim_path: str = None, has_rig: bool = False):
"""Initializes the sensor handles and internal buffers.
This function creates handles and registers the provided data types with the replicator registry to
be able to access the data from the sensor. It also initializes the internal buffers to store the data.
The function also allows initializing to a camera not spawned by using the :meth:`spawn` method.
For instance, connecting to the default viewport camera "/Omniverse_persp". In such cases, it is
the user's responsibility to ensure that the camera is valid and inform the sensor class whether
the camera is part of a rig or not.
Args:
cam_prim_path (str, optional): The prim path to existing camera. Defaults to None.
has_rig (bool, optional): Whether the passed camera prim path is attached to a rig. Defaults to False.
......@@ -406,7 +415,7 @@ class Camera(SensorBase):
self._sensor_prim = UsdGeom.Camera(cam_prim)
# Check rig
if has_rig:
self._sensor_rig_prim = XFormPrimView(cam_prim_path.rsplit("/", 1)[0], reset_xform_properties=False)
self._sensor_rig_prim = XFormPrimView(cam_prim_path.rsplit("/", 1)[0], reset_xform_properties=True)
else:
self._sensor_rig_prim = None
......@@ -431,7 +440,7 @@ class Camera(SensorBase):
else:
init_params = None
# create annotator node
rep_annotator = rep.AnnotatorRegistry.get_annotator(name, init_params)
rep_annotator = rep.AnnotatorRegistry.get_annotator(name, init_params, device=self.device)
rep_annotator.attach([self._render_product_path])
# add to registry
self._rep_registry[name] = rep_annotator
......@@ -453,15 +462,16 @@ class Camera(SensorBase):
self._data.image_shape = self.image_shape
self._data.output = dict.fromkeys(self._data.output, None)
def update(self, dt: float):
"""Updates the buffers at sensor frequency.
def buffer(self):
"""Updates the internal buffer with the latest data from the sensor.
Args:
dt (float): The simulation time-step.
"""
super().update(dt)
This function reads the intrinsic matrix and pose of the camera. It also reads the data from
the annotator registry and updates the internal buffer.
def buffer(self):
Note:
When running in standalone mode, the function renders the scene a few times to fill all the buffers.
During this time, the physics simulation is paused. This is a known issue with Isaac Sim.
"""
# When running in standalone mode, need to render a few times to fill all the buffers
# FIXME: Check with simulation team to get rid of this. What if someone has render or other callbacks?
if builtins.ISAAC_LAUNCHED_FROM_TERMINAL is False:
......@@ -473,7 +483,7 @@ class Camera(SensorBase):
self._data.position, self._data.orientation = self._compute_ros_pose()
# -- read the data from annotator registry
for name in self._rep_registry:
self._data.output[name] = self._rep_registry[name].get_data(device=self.device)
self._data.output[name] = self._rep_registry[name].get_data()
# -- update the trigger call data (needed by replicator BasicWriter method)
self._data.output["trigger_outputs"] = {"on_time": self.frame}
......
......@@ -12,25 +12,12 @@ from typing import List, Tuple
# omni-isaac-orbit
from omni.isaac.orbit.utils import configclass
__all__ = ["PinholeCameraCfg", "FisheyeCameraCfg"]
@configclass
class PinholeCameraCfg:
"""Configuration for a pinhole camera sensor.
The following sensor types are supported:
- "rgb": A rendered color image.
- "distance_to_camera": An image containing the distance to camera optical center.
- "distance_to_image_plane": An image containing distances of 3D points from camera plane along camera's z-axis.
- "normals": An image containing the local surface normal vectors at each pixel.
- "motion_vectors": An image containing the motion vector data at each pixel.
- "instance_segmentation": The instance segmentation data.
- "semantic_segmentation": The semantic segmentation data.
- "bounding_box_2d_tight": The tight 2D bounding box data (only contains non-occluded regions).
- "bounding_box_2d_loose": The loose 2D bounding box data (contains occluded regions).
- "bounding_box_3d": The 3D view space bounding box data.
- "occlusion": The occlusion information (such as instance id, semantic id and occluded ratio).
"""
"""Configuration for a pinhole camera sensor."""
sensor_tick: float = 0.0
"""Simulation seconds between sensor buffers. Defaults to 0.0."""
......@@ -43,7 +30,7 @@ class PinholeCameraCfg:
semantic_types: List[str] = ["class"]
"""List of allowed semantic types the types. Defaults to ["class"].
For example, if `semantic_types` is [“class”], only the bounding boxes for prims with semantics of
For example, if semantic types is [“class”], only the bounding boxes for prims with semantics of
type “class” will be retrieved.
More information available at:
......@@ -52,12 +39,11 @@ class PinholeCameraCfg:
@configclass
class UsdCameraCfg:
"""USD related configuration for the sensor.
"""USD related configuration of the sensor.
Note:
The parameter is kept default from USD if it is set to :obj:`None`. This includes the default
parameters (in case the sensor is created) or the ones set by the user (in case the sensor is
loaded from existing USD stage).
The parameter is kept default from USD if it is set to :obj:`None`. This includes the default
parameters (in case the sensor is created) or the ones set by the user (in case the sensor is
loaded from existing USD stage).
Reference:
* https://docs.omniverse.nvidia.com/prod_extensions/prod_extensions/ext_replicator/annotators_details.html
......@@ -91,37 +77,10 @@ class PinholeCameraCfg:
@configclass
class FisheyeCameraCfg(PinholeCameraCfg):
"""Configuration for a fisheye camera sensor.
The following sensor types are supported:
- "rgb": A rendered color image.
- "distance_to_camera": An image containing the distance to camera optical center.
- "distance_to_image_plane": An image containing distances of 3D points from camera plane along camera's z-axis.
- "normals": An image containing the local surface normal vectors at each pixel.
- "motion_vectors": An image containing the motion vector data at each pixel.
- "instance_segmentation": The instance segmentation data.
- "semantic_segmentation": The semantic segmentation data.
- "bounding_box_2d_tight": The tight 2D bounding box data (only contains non-occluded regions).
- "bounding_box_2d_loose": The loose 2D bounding box data (contains occluded regions).
- "bounding_box_3d": The 3D view space bounding box data.
- "occlusion": The occlusion information (such as instance id, semantic id and occluded ratio).
"""
"""Configuration for a fisheye camera sensor."""
@configclass
class UsdCameraCfg(PinholeCameraCfg.UsdCameraCfg):
"""USD related configuration for the sensor.
Note:
The parameter is kept default from USD if it is set to :obj:`None`. This includes the default
parameters (in case the sensor is created) or the ones set by the user (in case the sensor is
loaded from existing USD stage).
Reference:
* https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/manual_replicator_composer_parameter_list.html#camera-parameters
* https://graphics.pixar.com/usd/docs/api/class_usd_geom_camera.html
"""
fisheye_nominal_width: float = None
"""Nominal width of fisheye lens model."""
......
......@@ -4,7 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause
"""
Height-scanner.
Height-scanner based on ray-casting operations using PhysX ray-caster.
"""
from .height_scanner import HeightScanner, HeightScannerData
......
......@@ -3,19 +3,11 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""
@author Mayank Mittal
@email mittalma@ethz.ch
@author David Hoeller
@email dhoeller@nvidia.com
@brief Height-map scanner in Omniverse workflows.
"""
import numpy as np
import scipy.spatial.transform as tf
from dataclasses import dataclass
from typing import List, Union
from typing import List, Sequence
import omni
import omni.isaac.core.utils.prims as prim_utils
......@@ -29,6 +21,8 @@ from ..sensor_base import SensorBase
from .height_scanner_cfg import HeightScannerCfg
from .height_scanner_marker import HeightScannerMarker
__all__ = ["HeightScanner", "HeightScannerData"]
@dataclass
class HeightScannerData:
......@@ -37,13 +31,19 @@ class HeightScannerData:
position: np.ndarray = None
"""Position of the sensor origin in world frame."""
orientation: np.ndarray = None
"""Orientation of the sensor origin in quaternion (w, x, y, z) in world frame."""
"""Orientation of the sensor origin in quaternion ``(w, x, y, z)`` in world frame."""
hit_points: np.ndarray = None
"""The end point locations of ray-casted rays."""
"""The end point locations of ray-casted rays. Shape is (N, 3), where ``N`` is
the number of scan points."""
hit_distance: np.ndarray = None
"""The ray-cast travel distance from query point."""
"""The ray-cast travel distance from query point. Shape is (N,), where ``N`` is
the number of scan points."""
hit_status: np.ndarray = None
"""Whether the ray hit an object or not."""
"""Whether the ray hit an object or not. Shape is (N,), where ``N`` is
the number of scan points.
It is set to ``1`` if the ray hit an object, and ``0`` otherwise.
"""
class HeightScanner(SensorBase):
......@@ -53,27 +53,20 @@ class HeightScanner(SensorBase):
often we care about the terrain for locomotion. The height-map, also called elevation map, simplifies the
terrain as a two-dimensional surface. Each grid-cell represents the height of the terrain.
Unlike algorithms which fuse depth measurements to create an elevation map [1], in this method we directly use
the PhysX API for ray-casting and query the height of the terrain from a set of query scan points. These points
represent the location of the grid cells.
Unlike algorithms which fuse depth measurements to create an elevation map :cite:p:`frankhauser2018probabilistic`,
in this method we directly use the PhysX API for ray-casting and query the height of the terrain from a set
of query scan points. These points represent the location of the grid cells.
The height-scanner uses PhysX for ray-casting to collision bodies. To prevent the casting to certain prims
in the scene (such as the robot on which height-scanner is present), one needs to provide the names of the
prims to not check collision with as a part of the dictionary config.
The scanner offset :math:`(x_o, y_o, z_o)` is the offset of the sensor from the frame it is attached to. During the
`update(...)` or `buffer(...)`, the pose of the mounted frame needs to be provided.
The scanner offset :math:`(x_o, y_o, z_o)` is the offset of the sensor from the frame it is attached to.
During the :meth:`update` or :meth:`buffer`, the pose of the mounted frame needs to be provided.
If visualization is enabled, rays that have a hit are displayed in red, while a miss is displayed in blue.
During a miss, the point's distance is set to the maximum ray-casting distance.
References:
[1] Fankhauser, P., Bloesch, M., & Hutter, M. (2018). Probabilistic terrain mapping for mobile robots
with uncertain localization. IEEE Robotics and Automation Letters, 3(4), 3019-3026.
TODO:
Move this class to use generic range sensor from Isaac Sim.
Reference: https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/ext_omni_isaac_range_sensor.html#isaac-sim-generic-range-sensor-example
"""
def __init__(self, cfg: HeightScannerCfg):
......@@ -82,6 +75,8 @@ class HeightScanner(SensorBase):
Args:
cfg (HeightScannerCfg): The configuration parameters.
"""
# TODO: Use generic range sensor from Isaac Sim?
# Reference: https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/ext_omni_isaac_range_sensor.html#isaac-sim-generic-range-sensor-example
# store inputs
self.cfg = cfg
# initialize base class
......@@ -122,12 +117,12 @@ class HeightScanner(SensorBase):
@property
def prim_path(self) -> str:
"""Returns: The path to the height-map sensor."""
"""The path to the height-map sensor."""
return self._sensor_prim.prim_paths[0]
@property
def data(self) -> HeightScannerData:
"""Returns: Data related to height scanner."""
"""Data related to height scanner."""
return self._data
"""
......@@ -137,8 +132,8 @@ class HeightScanner(SensorBase):
def set_visibility(self, visible: bool):
"""Enables drawing of the scan points in the viewport.
Arguments:
visible {bool} -- Whether to draw scan points or not. (default: {True})
Args:
visible (bool) -- Whether to draw scan points or not.
"""
# copy argument
self._visualize = visible
......@@ -151,7 +146,7 @@ class HeightScanner(SensorBase):
If None is passed into argument, then no filtering is performed.
Args:
names (List[str]): A list of prim names to ignore raycast collisions with.
names (List[str]): A list of prim names to ignore ray-cast collisions with.
"""
# default
if names is None:
......@@ -202,27 +197,26 @@ class HeightScanner(SensorBase):
self._data.position = None
self._data.orientation = None
def update(self, dt: float, pos: Union[np.ndarray, List[float]], quat: Union[np.ndarray, List[float]]):
def update(self, dt: float, pos: Sequence[float], quat: Sequence[float]):
"""Updates the buffers at sensor frequency.
Args:
dt (float): The simulation time-step.
pos (Union[np.ndarray, List[float]]): Position of the frame to which the sensor is attached.
quat (Union[np.ndarray, List[float]]): Quaternion (w, x, y, z) of the frame to which the sensor is attached.
pos (Sequence[float]): Position of the frame to which the sensor is attached.
quat (Sequence[float]): Quaternion (w, x, y, z) of the frame to which the sensor is attached.
"""
super().update(dt, pos, quat)
def buffer(self, pos: Union[np.ndarray, List[float]], quat: Union[np.ndarray, List[float]]):
def buffer(self, pos: Sequence[float], quat: Sequence[float]):
"""Fills the buffers of the sensor data.
This function does not perform any time-based checks and directly fills the data into the data container.
Warning:
Although this method is public, `update(dt)` should be the preferred way of filling buffers.
This function uses the input position and orientation to compute the ray-casting queries
and fill the buffers. If a collision is detected, then the hit distance is stored in the buffer.
Otherwise, the hit distance is set to the maximum value specified in the configuration.
Args:
pos (Union[np.ndarray, List[float]]): Position of the frame to which the sensor is attached.
quat (Union[np.ndarray, List[float]]): Quaternion (w, x, y, z) of the frame to which the sensor is attached.
pos (Sequence[float]): Position of the frame to which the sensor is attached.
quat (Sequence[float]): Quaternion (w, x, y, z) of the frame to which the sensor is attached.
"""
# convert to numpy for sanity
pos = np.asarray(pos)
......
......@@ -19,12 +19,12 @@ class HeightScannerMarker:
It creates two spherical markers of different colors. Based on the indices provided the referenced
marker is activated.
Proto Indices (status):
- 0 -> ray miss (blue sphere).
- 1 -> successful ray hit (red sphere).
- 2 -> invisible ray (disabled visualization)
The status marker (proto-indices) of the point instancer is used to store the following information:
- :obj:`0` -> ray miss (blue sphere).
- :obj:`1` -> successful ray hit (red sphere).
- :obj:`2` -> invisible ray (disabled visualization)
TODO: Make this generic marker for all and put inside the `omni.isaac.orbit.marker` directory.
"""
def __init__(self, prim_path: str, count: int, radius: float = 1.0) -> None:
......@@ -44,6 +44,7 @@ class HeightScannerMarker:
self._radius = radius
# create manager for handling instancing of frame markers
self._instancer_manager = UsdGeom.PointInstancer.Define(stage, prim_path)
# TODO: Make this generic marker for all and put inside the `omni.isaac.orbit.marker` directory.
# create a child prim for the marker
# -- target missed
prim = prim_utils.create_prim(f"{prim_path}/point_miss", "Sphere", attributes={"radius": self._radius})
......
......@@ -16,23 +16,24 @@ __all__ = ["create_points_from_grid", "plot_height_grid"]
def create_points_from_grid(size: Tuple[float, float], resolution: float) -> np.ndarray:
"""Creates a list of points from 2D meshgrid.
"""Creates a list of points from 2D mesh-grid.
The terrain scan is approximated with a grid map of the input resolution.
By default, we consider the origin as the center of the local map and the scan size (X, Y) is the map size.
Given these settings, the elevation map spans from: `(- X / 2, - Y / 2)` to `(+ X / 2, + Y / 2)`.
By default, we consider the origin as the center of the local map and the scan size ``(X, Y)`` is the
map size. Given these settings, the elevation map spans from: ``(- X / 2, - Y / 2)`` to
``(+ X / 2, + Y / 2)``.
Example:
For a grid of size (0.2, 0.2) with resolution of 0.1, the created points will first x-axis fixed, while the
y-axis changes, i.e.:
```
.. code-block:: none
[
[-0.1, -0.1], [-0.1, 0.0], [-0.1, 0.1],
[0.0, -0.1], [0.0, 0.], [0.0, 0.1],
[0.1, -0.1], [0.1, 0.0], [0.1, 0.1],
]
```
Args:
size (Tuple[float, float]): The 2D scan region along x and y directions (in meters).
......@@ -57,9 +58,11 @@ def plot_height_grid(
) -> AxesImage:
"""Plots the sensor height-map distances using matplotlib.
If the axes is not provided, a new figure is created.
Note:
This method currently only supports if the grid is evenly spaced, i.e. the scan points are created using
`create_points_from_grid(...)` method.
:meth:`create_points_from_grid` method.
Args:
hit_distance (dict): The ray hit distance measured from the sensor.
......
......@@ -3,9 +3,10 @@
#
# SPDX-License-Identifier: BSD-3-Clause
"""Base class for sensors in Omniverse workflows.
"""Base class for sensors.
This class defines an interface similar to how the RobotBase class works.
This class defines an interface for sensors similar to how the :class:`omni.isaac.orbit.robot.robot_base.RobotBase` class works.
Each sensor class should inherit from this class and implement the abstract methods.
"""
......@@ -14,21 +15,18 @@ from typing import Any
class SensorBase:
"""
The base class for implementation of a sensor.
"""The base class for implementing a sensor.
Note:
These sensors are not vectorized yet.
Attributes:
frame (int) - Frame number when the measurement took place.
timestamp (float) - Simulation time of the measurement (in seconds).
sensor_tick (float) - Simulation seconds between sensor buffers (ticks).
"""
def __init__(self, sensor_tick: float = 0.0):
"""Initialize the sensor class.
The sensor tick is the time between two sensor buffers. If the sensor tick is zero, then the sensor
buffers are filled at every simulation step.
Args:
sensor_tick (float, optional): Simulation seconds between sensor buffers. Defaults to 0.0.
"""
......@@ -109,6 +107,10 @@ class SensorBase:
def update(self, dt: float, *args, **kwargs):
"""Updates the buffers at sensor frequency.
This function performs time-based checks and fills the data into the data container. It
calls the function :meth:`buffer()` to fill the data. The function :meth:`buffer()` should
not be called directly.
Args:
dt (float): The simulation time-step.
args (tuple): Other positional arguments passed to function :meth:`buffer()`.
......
......@@ -28,22 +28,26 @@ __all__ = [
# General
"wrap_to_pi",
"saturate",
"copysign",
# General-Isaac Sim
"normalize",
"scale_transform",
"unscale_transform",
"copysign",
# Rotation
"quat_mul",
"matrix_from_quat",
"quat_inv",
"quat_conjugate",
"quat_apply",
"quat_rotate",
"quat_rotate_inverse",
"quat_from_euler_xyz",
"quat_apply_yaw",
"quat_box_minus",
"euler_xyz_from_quat",
"axis_angle_from_quat",
# Rotation-Isaac Sim
"quat_apply",
"quat_from_angle_axis",
"quat_mul",
"quat_conjugate",
"quat_rotate",
"quat_rotate_inverse",
# Transformations
"combine_frame_transforms",
"subtract_frame_transforms",
......
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