Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions docs/pages/concepts/variations/variations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ defaults:
camera_extrinsics_wrist_camera (CameraExtrinsicsVariation, run-time)
Enable: droid_abs_joint_pos.camera_extrinsics_wrist_camera.enabled=true (default: False)
Fields:
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.high = [0.005,0.005,0.005]
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.low = [-0.005,-0.005,-0.005]
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.high = [0.06,0.03,0.06]
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.low = [-0.06,-0.06,-0.06]

Asset: light
hdr_image (HDRImageVariation, build-time)
Expand Down Expand Up @@ -114,9 +114,13 @@ The same run with tunable variation control parameters spelled out:
"droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.high=[0.01,0.01,0.01]"

The ``hdr_names`` list restricts HDR sampling to the three named maps instead of the full
registered set. The ``sampler_cfg.low`` / ``sampler_cfg.high`` vectors widen the camera
registered set. The ``sampler_cfg.low`` / ``sampler_cfg.high`` vectors set the camera
extrinsics jitter range to ±10 mm per axis.

Defaults can differ per embodiment. DROID narrows the wrist camera's ``sampler_cfg.high`` +Y
bound to 0.03 m, because the camera is mounted just above the gripper and sinks into it beyond
that; an override like the one above still takes precedence.

To see the available variations and control parameters for a specific environment,
see :ref:`discovering-available-variations`.

Expand Down
23 changes: 23 additions & 0 deletions isaaclab_arena/embodiments/droid/droid.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
if TYPE_CHECKING:
import trimesh

from isaaclab_arena.variations.camera_extrinsics_variation import CameraExtrinsicsVariationCfg

_DROID_ROBOT_PRIM = RobotPrimSpec(
robot_usd_path=f"{ARENA_NUCLEUS_DIR}/Arena/assets/robot_library/droid/franka_robotiq_2f_85_flattened.usd",
root_prim_path="/panda",
Expand Down Expand Up @@ -75,6 +77,13 @@
"left_inner_finger_knuckle_joint",
"left_inner_finger_joint",
)
_DROID_WRIST_CAMERA_NAME = "wrist_camera"
# Decalibration bounds for the wrist camera, in its ROS optical frame (+X right, +Y down,
# +Z forward). +Y is capped well below the other five bounds because the camera sits just above the
# Robotiq gripper: past roughly 0.03 m the camera sinks into the gripper body and its view of the
# scene is occluded.
_DROID_WRIST_CAMERA_DECALIBRATION_LOW = (-0.06, -0.06, -0.06)
_DROID_WRIST_CAMERA_DECALIBRATION_HIGH = (0.06, 0.03, 0.06)


class DroidEmbodimentBase(EmbodimentBase, ABC):
Expand Down Expand Up @@ -132,6 +141,20 @@ def __init__(
self.mimic_env = None
self.add_camera_variations(self.camera_config)

def get_camera_extrinsics_variation_cfg(self, camera_name: str) -> CameraExtrinsicsVariationCfg | None:
"""Return the wrist camera's mount-constrained decalibration bounds, or ``None`` for the rest."""
from isaaclab_arena.variations.camera_extrinsics_variation import CameraExtrinsicsVariationCfg
from isaaclab_arena.variations.uniform_sampler import UniformSamplerCfg

if camera_name != _DROID_WRIST_CAMERA_NAME:
return None
return CameraExtrinsicsVariationCfg(
sampler_cfg=UniformSamplerCfg(
low=list(_DROID_WRIST_CAMERA_DECALIBRATION_LOW),
high=list(_DROID_WRIST_CAMERA_DECALIBRATION_HIGH),
)
)

def get_bounding_box(self) -> AxisAlignedBoundingBox:
"""Return root-relative placement bounds from the composed on-stand USD spawn.

Expand Down
19 changes: 18 additions & 1 deletion isaaclab_arena/embodiments/embodiment_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
if TYPE_CHECKING:
import trimesh

from isaaclab_arena.variations.camera_extrinsics_variation import CameraExtrinsicsVariationCfg


@dataclass(frozen=True)
class ArticulationGeometrySpec:
Expand Down Expand Up @@ -231,13 +233,28 @@ def get_camera_cfg(self) -> Any:
), f"Expected camera_config to inherit from ArenaCameraCfg; got {type(self.camera_config).__name__}."
return self.camera_config.get_cfg()

def get_camera_extrinsics_variation_cfg(self, camera_name: str) -> CameraExtrinsicsVariationCfg | None:
"""Return the extrinsics variation cfg for ``camera_name``, or ``None`` for the variation's own default.

Override this where an embodiment's mounting geometry bounds how far a camera may be
decalibrated before it intersects the robot. The returned cfg becomes the variation's
starting point, so an Experiment or Hydra override still wins over it.

Args:
camera_name: Scene-entity name of the camera the variation targets.
"""

def add_camera_variations(self, camera_rig: ArenaCameraCfg) -> None:
"""Register extrinsics and intrinsics variations for every camera in ``camera_rig``."""
from isaaclab_arena.variations.camera_extrinsics_variation import CameraExtrinsicsVariation
from isaaclab_arena.variations.camera_intrinsics_variation import CameraIntrinsicsVariation

for camera_name in camera_rig.camera_names():
self.add_variation(CameraExtrinsicsVariation(camera_name=camera_name))
self.add_variation(
CameraExtrinsicsVariation(
camera_name=camera_name, cfg=self.get_camera_extrinsics_variation_cfg(camera_name)
)
)
self.add_variation(CameraIntrinsicsVariation(camera_name=camera_name, camera_rig=camera_rig))

def _update_scene_cfg_with_robot_initial_pose(self, scene_config: Any, pose: Pose) -> Any:
Expand Down
31 changes: 31 additions & 0 deletions isaaclab_arena/tests/test_camera_extrinsics_variation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
EVENT_NAME = f"{CAMERA_NAME}_extrinsics_variation"
TEST_DECALIBRATION_VECTOR = [0.01, -0.02, 0.03]

DROID_WRIST_CAMERA_DECALIBRATION_LOW = [-0.06, -0.06, -0.06]
DROID_WRIST_CAMERA_DECALIBRATION_HIGH = [0.06, 0.03, 0.06]


def get_test_environment(*, camera_extrinsics_enabled: bool):
"""Build a minimal arena env with an optional enabled camera extrinsics variation."""
Expand Down Expand Up @@ -124,6 +127,25 @@ def _test_camera_extrinsics_variation_realized_at_runtime(simulation_app):
return True


def _test_droid_wrist_camera_extrinsics_bounds_clear_the_gripper(simulation_app):
from isaaclab_arena.embodiments.droid.droid import DroidAbsoluteJointPositionEmbodiment
from isaaclab_arena.variations.camera_extrinsics_variation import CameraExtrinsicsVariationCfg

embodiment = DroidAbsoluteJointPositionEmbodiment(enable_cameras=ENABLE_CAMERAS)

# The wrist camera is mounted just above the gripper, so DROID narrows its +Y bound.
wrist_sampler_cfg = embodiment.get_variation("camera_extrinsics_wrist_camera").cfg.sampler_cfg
assert list(wrist_sampler_cfg.low) == DROID_WRIST_CAMERA_DECALIBRATION_LOW
assert list(wrist_sampler_cfg.high) == DROID_WRIST_CAMERA_DECALIBRATION_HIGH

# The externally mounted cameras are unobstructed and keep the variation's own default bounds.
default_sampler_cfg = CameraExtrinsicsVariationCfg().sampler_cfg
external_sampler_cfg = embodiment.get_variation("camera_extrinsics_external_camera").cfg.sampler_cfg
assert list(external_sampler_cfg.low) == list(default_sampler_cfg.low)
assert list(external_sampler_cfg.high) == list(default_sampler_cfg.high)
return True


@pytest.mark.with_cameras
def test_disabled_camera_extrinsics_variation_not_in_events_cfg():
assert run_function_with_persistent_simulation_app(
Expand All @@ -149,3 +171,12 @@ def test_camera_extrinsics_variation_realized_at_runtime():
headless=HEADLESS,
enable_cameras=ENABLE_CAMERAS,
)


@pytest.mark.with_cameras
def test_droid_wrist_camera_extrinsics_bounds_clear_the_gripper():
assert run_function_with_persistent_simulation_app(
_test_droid_wrist_camera_extrinsics_bounds_clear_the_gripper,
headless=HEADLESS,
enable_cameras=ENABLE_CAMERAS,
)
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ runs:
num_episodes: 10
# The variations to enable for this run.
variations:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
droid_abs_joint_pos.camera_extrinsics_wrist_camera.enabled: true
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.low: [-0.03, -0.03, -0.03]
droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.high: [0.03, 0.03, 0.03]
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ shared:
hdr_names: ["home_office_robolab"]
droid_abs_joint_pos:
camera_extrinsics_wrist_camera:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
enabled: false
sampler_cfg:
low: [-0.05, -0.05, -0.05]
high: [0.05, 0.05, 0.05]

runs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ shared:
hdr_names: ["home_office_robolab"]
droid_abs_joint_pos:
camera_extrinsics_wrist_camera:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
enabled: false
sampler_cfg:
low: [-0.05, -0.05, -0.05]
high: [0.05, 0.05, 0.05]

runs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ shared:
hdr_names: ["home_office_robolab"]
droid_abs_joint_pos:
camera_extrinsics_wrist_camera:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
enabled: false
sampler_cfg:
low: [-0.05, -0.05, -0.05]
high: [0.05, 0.05, 0.05]

runs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ shared:
hdr_names: ["home_office_robolab"]
droid_abs_joint_pos:
camera_extrinsics_wrist_camera:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
enabled: false
sampler_cfg:
low: [-0.05, -0.05, -0.05]
high: [0.05, 0.05, 0.05]

runs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ shared:
hdr_names: ["home_office_robolab"]
droid_abs_joint_pos:
camera_extrinsics_wrist_camera:
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
enabled: false
sampler_cfg:
low: [-0.05, -0.05, -0.05]
high: [0.05, 0.05, 0.05]

runs:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@
PLATFORM = "ovx-l40s"
VARIATIONS = (
"light.hdr_image.enabled=true "
# Decalibration bounds come from the DROID embodiment, which caps +Y at the gripper.
"droid_abs_joint_pos.camera_extrinsics_wrist_camera.enabled=true "
"droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.high=[0.05,0.05,0.05] "
"droid_abs_joint_pos.camera_extrinsics_wrist_camera.sampler_cfg.low=[-0.05,-0.05,-0.05] "
"light.hdr_image.hdr_names=[home_office_robolab]"
)

Expand Down
Loading