Unverified Commit a29bea03 authored by Kyle Morgenstein's avatar Kyle Morgenstein Committed by GitHub

Adds body tracking option to ViewerCfg (#1620)

# Description

Resolves Issue #1534 by adding non-root body tracking as an option in
the ViewerCfg. See the issue for justification/discussion. API as
follows:

- Adds origin_type "asset_body"
- Adds field "body_name"

When origin_type = "asset_body" and "body_name" matches a body in the
asset specified in "asset_name", the viewport is configured to track the
link frame of the body in world coordinates. By using the existing eye
and lookat fields in ViewerCfg the additional features are enabled:

- Tracking the end effector of an arm during manipulation (see below)
- Tracking a fixed offset from the end effector of an arm (e.g.
mimicking the view of a wrist-mounted camera)
- Tracking a mobile base using the currently-supported workaround for
lack of wheel physics by defining a virtual arm to mimic the motion of
the robot

## Type of change

- New feature (non-breaking change which adds functionality)

## Training Video from "Isaac-Lift-Cube-Franka-v0" after 1000
iterations.

Viewer defined as follows:

viewer = ViewerCfg(eye=(0.5, 0.5, 0.0), origin_type="asset_body",
env_index=0, asset_name="robot", body_name="panda_hand")

No other changes were made from the default environment definition.


https://github.com/user-attachments/assets/fff24b50-c9d0-48dd-b4c2-3641a11f0d4d

## 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

---------
Signed-off-by: 's avatarKelly Guo <kellyg@nvidia.com>
Signed-off-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
Co-authored-by: 's avatarKelly Guo <kellyg@nvidia.com>
Co-authored-by: 's avatarKelly Guo <kellyguo123@hotmail.com>
parent 7ea72c40
[package] [package]
# Note: Semantic Versioning is used: https://semver.org/ # Note: Semantic Versioning is used: https://semver.org/
version = "0.30.2" version = "0.30.3"
# Description # Description
title = "Isaac Lab framework for Robot Learning" title = "Isaac Lab framework for Robot Learning"
......
Changelog Changelog
--------- ---------
0.30.3 (2025-01-02)
~~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added body tracking as an origin type to :class:`omni.isaac.lab.envs.ViewerCfg` and :class:`omni.isaac.lab.envs.ui.ViewportCameraController`.
0.30.2 (2024-12-22) 0.30.2 (2024-12-22)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
......
...@@ -202,7 +202,7 @@ class RigidObjectData: ...@@ -202,7 +202,7 @@ class RigidObjectData:
@property @property
def body_link_state_w(self): def body_link_state_w(self):
"""State of all bodies `[pos, quat, lin_vel, ang_vel]` in simulation world frame. """State of all bodies `[pos, quat, lin_vel, ang_vel]` in simulation world frame.
Shape is (num_instances,1, 13). Shape is (num_instances, 1, 13).
The position, quaternion, and linear/angular velocity are of the body's link frame relative to the world. The position, quaternion, and linear/angular velocity are of the body's link frame relative to the world.
""" """
......
...@@ -36,7 +36,7 @@ class ViewerCfg: ...@@ -36,7 +36,7 @@ class ViewerCfg:
Default is (1280, 720). Default is (1280, 720).
""" """
origin_type: Literal["world", "env", "asset_root"] = "world" origin_type: Literal["world", "env", "asset_root", "asset_body"] = "world"
"""The frame in which the camera position (eye) and target (lookat) are defined in. Default is "world". """The frame in which the camera position (eye) and target (lookat) are defined in. Default is "world".
Available options are: Available options are:
...@@ -44,6 +44,7 @@ class ViewerCfg: ...@@ -44,6 +44,7 @@ class ViewerCfg:
* ``"world"``: The origin of the world. * ``"world"``: The origin of the world.
* ``"env"``: The origin of the environment defined by :attr:`env_index`. * ``"env"``: The origin of the environment defined by :attr:`env_index`.
* ``"asset_root"``: The center of the asset defined by :attr:`asset_name` in environment :attr:`env_index`. * ``"asset_root"``: The center of the asset defined by :attr:`asset_name` in environment :attr:`env_index`.
* ``"asset_body"``: The center of the body defined by :attr:`body_name` in asset defined by :attr:`asset_name` in environment :attr:`env_index`.
""" """
env_index: int = 0 env_index: int = 0
...@@ -58,6 +59,12 @@ class ViewerCfg: ...@@ -58,6 +59,12 @@ class ViewerCfg:
This quantity is only effective if :attr:`origin` is set to "asset_root". This quantity is only effective if :attr:`origin` is set to "asset_root".
""" """
body_name: str | None = None
"""The name of the body in :attr:`asset_name` in the interactive scene for the frame origin. Default is None.
This quantity is only effective if :attr:`origin` is set to "asset_body".
"""
## ##
# Types. # Types.
......
...@@ -15,6 +15,8 @@ from typing import TYPE_CHECKING ...@@ -15,6 +15,8 @@ from typing import TYPE_CHECKING
import omni.kit.app import omni.kit.app
import omni.timeline import omni.timeline
from omni.isaac.lab.assets.articulation.articulation import Articulation
if TYPE_CHECKING: if TYPE_CHECKING:
from omni.isaac.lab.envs import DirectRLEnv, ManagerBasedEnv, ViewerCfg from omni.isaac.lab.envs import DirectRLEnv, ManagerBasedEnv, ViewerCfg
...@@ -59,12 +61,15 @@ class ViewportCameraController: ...@@ -59,12 +61,15 @@ class ViewportCameraController:
self.set_view_env_index(self.cfg.env_index) self.set_view_env_index(self.cfg.env_index)
# set the camera origin to the center of the environment # set the camera origin to the center of the environment
self.update_view_to_env() self.update_view_to_env()
elif self.cfg.origin_type == "asset_root": elif self.cfg.origin_type == "asset_root" or self.cfg.origin_type == "asset_body":
# note: we do not yet update camera for tracking an asset origin, as the asset may not yet be # note: we do not yet update camera for tracking an asset origin, as the asset may not yet be
# in the scene when this is called. Instead, we subscribe to the post update event to update the camera # in the scene when this is called. Instead, we subscribe to the post update event to update the camera
# at each rendering step. # at each rendering step.
if self.cfg.asset_name is None: if self.cfg.asset_name is None:
raise ValueError(f"No asset name provided for viewer with origin type: '{self.cfg.origin_type}'.") raise ValueError(f"No asset name provided for viewer with origin type: '{self.cfg.origin_type}'.")
if self.cfg.origin_type == "asset_body":
if self.cfg.body_name is None:
raise ValueError(f"No body name provided for viewer with origin type: '{self.cfg.origin_type}'.")
else: else:
# set the camera origin to the center of the world # set the camera origin to the center of the world
self.update_view_to_world() self.update_view_to_world()
...@@ -160,6 +165,41 @@ class ViewportCameraController: ...@@ -160,6 +165,41 @@ class ViewportCameraController:
# update the camera view # update the camera view
self.update_view_location() self.update_view_location()
def update_view_to_asset_body(self, asset_name: str, body_name: str):
"""Updates the viewer's origin based upon the body of an asset in the scene.
Args:
asset_name: The name of the asset in the scene. The name should match the name of the
asset in the scene.
body_name: The name of the body in the asset.
Raises:
ValueError: If the asset is not in the scene or the body is not valid.
"""
# check if the asset is in the scene
if self.cfg.asset_name != asset_name:
asset_entities = [*self._env.scene.rigid_objects.keys(), *self._env.scene.articulations.keys()]
if asset_name not in asset_entities:
raise ValueError(f"Asset '{asset_name}' is not in the scene. Available entities: {asset_entities}.")
# check if the body is in the asset
asset: Articulation = self._env.scene[asset_name]
if body_name not in asset.body_names:
raise ValueError(
f"'{body_name}' is not a body of Asset '{asset_name}'. Available bodies: {asset.body_names}."
)
# get the body index
body_id, _ = asset.find_bodies(body_name)
# update the asset name
self.cfg.asset_name = asset_name
# set origin type to asset_body
self.cfg.origin_type = "asset_body"
# update the camera origins
self.viewer_origin = (
self._env.scene[self.cfg.asset_name].data.body_link_pos_w[self.cfg.env_index, body_id].view(3)
)
# update the camera view
self.update_view_location()
def update_view_location(self, eye: Sequence[float] | None = None, lookat: Sequence[float] | None = None): def update_view_location(self, eye: Sequence[float] | None = None, lookat: Sequence[float] | None = None):
"""Updates the camera view pose based on the current viewer origin and the eye and lookat positions. """Updates the camera view pose based on the current viewer origin and the eye and lookat positions.
...@@ -190,3 +230,5 @@ class ViewportCameraController: ...@@ -190,3 +230,5 @@ class ViewportCameraController:
# in other cases, the camera view is static and does not need to be updated continuously # in other cases, the camera view is static and does not need to be updated continuously
if self.cfg.origin_type == "asset_root" and self.cfg.asset_name is not None: if self.cfg.origin_type == "asset_root" and self.cfg.asset_name is not None:
self.update_view_to_asset_root(self.cfg.asset_name) self.update_view_to_asset_root(self.cfg.asset_name)
if self.cfg.origin_type == "asset_body" and self.cfg.asset_name is not None and self.cfg.body_name is not None:
self.update_view_to_asset_body(self.cfg.asset_name, self.cfg.body_name)
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