Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds IMU sensor #619

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bd2eb53
add imu sensor outdated version
pascal-roth Dec 11, 2023
530ab8a
update to physix prim view and update init
pascal-roth Dec 11, 2023
2bcf653
add check for sensor
pascal-roth Dec 11, 2023
e11defb
run formatter
pascal-roth Dec 11, 2023
cbd47fa
adjust changelog
pascal-roth Dec 11, 2023
baf5132
change frame of vel and acc, only accept rigid body prim, typo
pascal-roth Dec 19, 2023
50135d3
Merge branch 'devel' into feature/imu
pascal-roth Dec 19, 2023
6394262
remove outdated call as made in latest PR for other sensors
pascal-roth Dec 19, 2023
4f30a56
run formatter
pascal-roth Dec 19, 2023
914a39b
Merge branch 'devel' into feature/imu
pascal-roth Dec 21, 2023
c5ace7a
Merge branch 'main' into feature/imu
pascal-roth Mar 17, 2024
ec7da0d
start update
pascal-roth Mar 17, 2024
ef4c544
fixes
pascal-roth Mar 17, 2024
d90a7f1
run formatter
pascal-roth Mar 17, 2024
b88bf40
fix check imu sensor
pascal-roth Mar 17, 2024
3d5bceb
Merge branch 'main' into feature/imu
pascal-roth Apr 23, 2024
39594de
Merge branch 'feature/imu' of github.com:isaac-orbit/orbit into featu…
pascal-roth Apr 23, 2024
b966d04
run formatter
pascal-roth Apr 23, 2024
a040b72
Merge branch 'main' into feature/imu
pascal-roth Jul 2, 2024
45c0add
move to new sensor location
pascal-roth Jul 2, 2024
e168ee4
cleanup changelog, update imu sensor to isaaclab
pascal-roth Jul 2, 2024
6cb55fc
visualization fix
pascal-roth Jul 2, 2024
8d2f272
fixes and move utils to math utils
pascal-roth Jul 3, 2024
c76ca6d
add unittest, move utils to math.utils
pascal-roth Jul 3, 2024
d17fbeb
formatter
pascal-roth Jul 3, 2024
10759fa
wip
pascal-roth Jul 7, 2024
4fdb670
add doc and formatter
pascal-roth Jul 7, 2024
3399e3a
Apply suggestions from code review
pascal-roth Jul 24, 2024
3acd014
Apply suggestions from code review
pascal-roth Jul 24, 2024
869287b
started fix imu offset
pascal-roth Jul 24, 2024
28aab42
add offset test and compare with isaac sim imu
pascal-roth Jul 25, 2024
0cc85e2
add test for convention transform, small changes to code
pascal-roth Jul 25, 2024
1bd896d
renaming from all captial letters
pascal-roth Jul 25, 2024
cba977b
wip
pascal-roth Aug 2, 2024
b952d63
fix tests
pascal-roth Aug 9, 2024
18fc6d5
formatter
pascal-roth Aug 9, 2024
e01b6bf
use new physx api and store everything in world frame
pascal-roth Aug 13, 2024
f4f92e2
Merge branch 'feature/imu' of https://github.com/isaac-sim/IsaacLab i…
pascal-roth Aug 13, 2024
7068197
bugfix
pascal-roth Aug 13, 2024
f024f66
Merge branch 'main' into feature/imu
pascal-roth Aug 13, 2024
33e22a1
update test, physx bug detected
pascal-roth Aug 13, 2024
9ff23bc
com compensation
pascal-roth Aug 15, 2024
a0be30a
increasing spawn height
pascal-roth Aug 16, 2024
e34922e
add pendulum test and switch to numerical derivative of acceleration
jtigue-bdai Aug 16, 2024
cbb96bf
fix ground truth pendulum Z linear acceleration
jtigue-bdai Aug 19, 2024
0115b2c
add imu observations
jtigue-bdai Aug 26, 2024
ac3886a
clean up imu observation doc strings
jtigue-bdai Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions source/extensions/omni.isaac.lab/docs/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
---------

* Added IMU sensor implementation that directly accessess the physx view :class:`omni.isaac.lab.sensors.IMU`. The
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
sensor comes with a configuration class :class:`omni.isaac.lab.sensors.IMUCfg` and data class
:class:`omni.isaac.lab.sensors.IMUData`.


0.18.6 (2024-07-01)
~~~~~~~~~~~~~~~~~~~

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@
+---------------------+---------------------------+---------------------------------------------------------------+
| Frame Transformer | /World/robot/base | Leaf exists and is a physics body (Articulation / Rigid Body) |
+---------------------+---------------------------+---------------------------------------------------------------+
| IMU | /World/robot/base | Leaf exists and is a physics body (Articulation / Rigid Body) |
+---------------------+---------------------------+---------------------------------------------------------------+

"""

from .camera import * # noqa: F401, F403
from .contact_sensor import * # noqa: F401, F403
from .frame_transformer import * # noqa: F401
from .imu import * # noqa: F401, F403
from .ray_caster import * # noqa: F401, F403
from .sensor_base import SensorBase # noqa: F401
from .sensor_base_cfg import SensorBaseCfg # noqa: F401
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""
IMU Sensor
"""

from __future__ import annotations

from .imu import IMU
from .imu_cfg import IMUCfg
from .imu_data import IMUData

__all__ = ["IMU", "IMUCfg", "IMUData"]
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
197 changes: 197 additions & 0 deletions source/extensions/omni.isaac.lab/omni/isaac/lab/sensors/imu/imu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

import torch
from collections.abc import Sequence
from typing import TYPE_CHECKING

import omni.physics.tensors.impl.api as physx
from pxr import UsdPhysics

import omni.isaac.lab.sim as sim_utils
import omni.isaac.lab.utils.math as math_utils
from omni.isaac.lab.markers import VisualizationMarkers

from ..camera.utils import convert_orientation_convention, create_rotation_matrix_from_view
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
from ..sensor_base import SensorBase
from .imu_data import IMUData

if TYPE_CHECKING:
from .imu_cfg import IMUCfg


class IMU(SensorBase):
"""The inertia measurement unit."""
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved

cfg: IMUCfg
"""The configuration parameters."""

def __init__(self, cfg: IMUCfg):
"""Initializes the IMU sensor.

Args:
cfg: The configuration parameters.
"""
# initialize base class
super().__init__(cfg)
# Create empty variables for storing output data
self._data = IMUData()

def __str__(self) -> str:
"""Returns: A string containing information about the instance."""
return (
f"IMU sensor @ '{self.cfg.prim_path}': \n"
f"\tview type : {self._view.__class__}\n"
f"\tupdate period (s) : {self.cfg.update_period}\n"
f"\tnumber of sensors : {self._view.count}\n"
)

"""
Properties
"""

@property
def data(self) -> IMUData:
# update sensors if needed
self._update_outdated_buffers()
# return the data
return self._data

@property
def num_instances(self) -> int:
return self._view.count

"""
Operations
"""

def reset(self, env_ids: Sequence[int] | None = None):
# reset the timestamps
super().reset(env_ids)
# resolve None
if env_ids is None:
env_ids = slice(None)
# reset accumulative data buffers
self._data.quat_w[env_ids] = 0.0
self._data.ang_vel_b[env_ids] = 0.0
self._data.lin_acc_b[env_ids] = 0.0

def update(self, dt: float, force_recompute: bool = False):
# save timestamp
self._dt = dt
# execute updating
super().update(dt, force_recompute)

"""
Implementation.
"""

def _initialize_impl(self):
"""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.

Raises:
RuntimeError: If the number of imu prims in the view does not match the number of environments.
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
RuntimeError: If the imu prim is not a RigidBodyPrim
"""
# Initialize parent class
super()._initialize_impl()
# create simulation view
self._physics_sim_view = physx.create_simulation_view(self._backend)
self._physics_sim_view.set_subspace_roots("/")
# check if the prim at path is a rigid prim
prim = sim_utils.find_first_matching_prim(self.cfg.prim_path)
if prim is None:
raise RuntimeError(f"Failed to find a prim at path expression: {self.cfg.prim_path}")
# check if it is a RigidBody Prim
if prim.HasAPI(UsdPhysics.RigidBodyAPI):
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
self._view = self._physics_sim_view.create_rigid_body_view(self.cfg.prim_path.replace(".*", "*"))
else:
raise RuntimeError(f"Failed to find a RigidBodyAPI for the prim paths: {self.cfg.prim_path}")

# Create internal buffers
self._initialize_buffers_impl()

def _update_buffers_impl(self, env_ids: Sequence[int]):
"""Fills the buffers of the sensor data."""
# check if self._dt is set (this is set in the update function)
if not hasattr(self, "_dt"):
raise RuntimeError(
"The update function must be called before the data buffers are accessed the first time."
)
# obtain the poses of the sensors
pos_w, quat_w = self._view.get_transforms()[env_ids].split([3, 4], dim=-1)
quat_w = math_utils.convert_quat(quat_w, to="wxyz")
# store the poses
# note: we clone here because the obtained tensors are read-only
self._data.pos_w[env_ids] = pos_w.clone() + math_utils.quat_rotate(quat_w.clone(), self._offset_pos)
self._data.quat_w[env_ids] = math_utils.quat_mul(quat_w.clone(), self._offset_quat)
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved

# obtain the velocities of the sensors
lin_vel_w, ang_vel_w = self._view.get_velocities()[env_ids].split([3, 3], dim=-1)
# store the velocities
# note: we clone here because the obtained tensors are read-only
self._data.ang_vel_b[env_ids] = math_utils.quat_rotate_inverse(self._data.quat_w[env_ids], ang_vel_w.clone())
self._data.lin_acc_b[env_ids] = math_utils.quat_rotate_inverse(
self._data.quat_w[env_ids],
(lin_vel_w.clone() - self._last_lin_vel_w[env_ids]) / max(self._dt, self.cfg.update_period),
)
self._last_lin_vel_w[env_ids] = lin_vel_w.clone()

def _initialize_buffers_impl(self):
"""Create buffers for storing data."""
# data buffers
self._data.pos_w = torch.zeros(self._view.count, 3, device=self._device)
self._data.quat_w = torch.zeros(self._view.count, 4, device=self._device)
self._data.quat_w[:, 0] = 1.0
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
self._data.lin_acc_b = torch.zeros(self._view.count, 3, device=self._device)
self._data.ang_vel_b = torch.zeros(self._view.count, 3, device=self._device)
# internal buffers
self._last_lin_vel_w = torch.zeros(self._view.count, 3, device=self._device)
# store sensor offset transformation
self._offset_pos = torch.tensor(list(self.cfg.offset.pos), device=self._device).repeat(self._view.count, 1)
self._offset_quat = torch.tensor(list(self.cfg.offset.rot), device=self._device).repeat(self._view.count, 1)

jtigue-bdai marked this conversation as resolved.
Show resolved Hide resolved
def _set_debug_vis_impl(self, debug_vis: bool):
# set visibility of markers
# note: parent only deals with callbacks. not their visibility
if debug_vis:
# create markers if necessary for the first tome
if not hasattr(self, "acceleration_visualizer"):
self.acceleration_visualizer = VisualizationMarkers(self.cfg.visualizer_cfg)
# set their visibility to true
self.acceleration_visualizer.set_visibility(True)
else:
if hasattr(self, "acceleration_visualizer"):
self.acceleration_visualizer.set_visibility(False)

def _debug_vis_callback(self, event):
# safely return if view becomes invalid
# note: this invalidity happens because of isaac sim view callbacks
# TODO: check if necessary
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
if self._view is None:
return
# get marker location
# -- base state
base_pos_w = self._data.pos_w.clone()
base_pos_w[:, 2] += 0.5
# -- resolve the scales
default_scale = self.acceleration_visualizer.cfg.markers["arrow"].scale
arrow_scale = torch.tensor(default_scale, device=self.device).repeat(self._data.lin_acc_b.shape[0], 1)
# arrow-direction
quat_opengl = math_utils.quat_from_matrix(
create_rotation_matrix_from_view(
self._data.pos_w,
self._data.pos_w + math_utils.quat_rotate(self._data.quat_w, self._data.lin_acc_b),
device=self._device,
)
)
quat_w = convert_orientation_convention(quat_opengl, "opengl", "world")
# display markers
self.acceleration_visualizer.visualize(base_pos_w, quat_w, arrow_scale)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

from omni.isaac.lab.markers import VisualizationMarkersCfg
from omni.isaac.lab.markers.config import RED_ARROW_X_MARKER_CFG
from omni.isaac.lab.utils import configclass

from ..sensor_base_cfg import SensorBaseCfg
from .imu import IMU


@configclass
class IMUCfg(SensorBaseCfg):
"""Configuration for a camera sensor."""
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved

class_type: type = IMU

@configclass
class OffsetCfg:
"""The offset pose of the sensor's frame from the sensor's parent frame."""

pos: tuple[float, float, float] = (0.0, 0.0, 0.0)
"""Translation w.r.t. the parent frame. Defaults to (0.0, 0.0, 0.0)."""

rot: tuple[float, float, float, float] = (1.0, 0.0, 0.0, 0.0)
"""Quaternion rotation (w, x, y, z) w.r.t. the parent frame. Defaults to (1.0, 0.0, 0.0, 0.0)."""

offset: OffsetCfg = OffsetCfg()
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved

visualizer_cfg: VisualizationMarkersCfg = RED_ARROW_X_MARKER_CFG.replace(prim_path="/Visuals/Command/velocity_goal")
"""The configuration object for the visualization markers. Defaults to RED_ARROW_X_MARKER_CFG.

Note:
This attribute is only used when debug visualization is enabled.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

import torch
from dataclasses import dataclass


@dataclass
class IMUData:
"""Data container for the IMU sensor."""

pos_w: torch.Tensor = None
"""Position of the sensor origin in world frame.

Shape is (N, 3), where ``N`` is the number of sensors.
"""

quat_w: torch.Tensor = None
"""Orientation of the sensor origin in quaternion ``(w, x, y, z)`` in world frame.

Shape is (N, 4), where ``N`` is the number of sensors.
"""

ang_vel_b: torch.Tensor = None
"""Root angular velocity in body frame.

Shape is (N, B, 3), where ``N`` is the number of sensors and ``B`` is the number of bodies in each sensor.
pascal-roth marked this conversation as resolved.
Show resolved Hide resolved
"""

lin_acc_b: torch.Tensor = None
"""Root linear acceleration in body frame.

Shape is (N, B, 3), where ``N`` is the number of sensors and ``B`` is the number of bodies in each sensor.
"""
Loading
Loading