Skip to content
Open
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
87 changes: 83 additions & 4 deletions isaaclab_arena/assets/object_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
# SPDX-License-Identifier: Apache-2.0


from typing import TYPE_CHECKING, Any
from typing import Any

import isaaclab.sim as sim_utils

if TYPE_CHECKING:
from isaaclab_arena.assets.hdr_image import HDRImage
from isaaclab.assets import RigidObjectCfg
from isaaclab.sim.spawners.from_files.from_files_cfg import GroundPlaneCfg
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR, ISAACLAB_NUCLEUS_DIR
Expand Down Expand Up @@ -285,6 +282,88 @@ def __init__(
super().__init__(instance_name=instance_name, prim_path=prim_path, initial_pose=initial_pose, scale=scale)


# NIST insertion assets use Arena's high-precision assembly preset to mirror
# Factory/Forge dense-contact settings for small-clearance gear insertion.
@register_asset
class GearsAndBase(LibraryObject):
"""NIST gear base with SDF collision (gear_base as root prim)."""

name = "gears_and_base"
tags = ["object"]
usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Arena/assets/object_library/NIST/gearbase_and_gears_gearbase_root.usd"

spawn_cfg_addon = {
"rigid_props": RIGID_BODY_PROPS_HIGH_PRECISION.replace(disable_gravity=False, kinematic_enabled=True),
"mass_props": sim_utils.MassPropertiesCfg(mass=0.012),
"collision_props": sim_utils.CollisionPropertiesCfg(contact_offset=0.005, rest_offset=0.0),
}

def __init__(
self, instance_name: str | None = None, prim_path: str | None = None, initial_pose: Pose | None = None
):
super().__init__(instance_name=instance_name, prim_path=prim_path, initial_pose=initial_pose)


@register_asset
class MediumNistGear(LibraryObject):
"""NIST medium gear (held asset for gear insertion)."""

name = "medium_nist_gear"
tags = ["object"]
usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Arena/assets/object_library/NIST/gear_medium.usd"

spawn_cfg_addon = {
"rigid_props": RIGID_BODY_PROPS_HIGH_PRECISION.replace(disable_gravity=True),
"mass_props": sim_utils.MassPropertiesCfg(mass=0.012),
"collision_props": sim_utils.CollisionPropertiesCfg(contact_offset=0.005, rest_offset=0.0),
}

def __init__(
self, instance_name: str | None = None, prim_path: str | None = None, initial_pose: Pose | None = None
):
super().__init__(instance_name=instance_name, prim_path=prim_path, initial_pose=initial_pose)


@register_asset
class NistBoardAssembled(LibraryObject):
"""NIST fully assembled taskboard."""

name = "nist_board_assembled"
tags = ["nist", "object"]
usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Arena/assets/object_library/NIST/nist_board_assembled.usd"
object_type = ObjectType.BASE
# The assembled board is visual context for this task; gears_and_base owns
# the physical peg and gear contacts used by observations, rewards, and success.
spawn_cfg_addon = {
"rigid_props": sim_utils.RigidBodyPropertiesCfg(rigid_body_enabled=False),
"collision_props": sim_utils.CollisionPropertiesCfg(collision_enabled=False),
}

def __init__(
self, instance_name: str | None = None, prim_path: str | None = None, initial_pose: Pose | None = None
):
super().__init__(instance_name=instance_name, prim_path=prim_path, initial_pose=initial_pose)


@register_asset
class NistGearBase(LibraryObject):
"""NIST standalone gear base."""

name = "nist_gear_base"
tags = ["nist", "object"]
usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Arena/assets/object_library/NIST/gear_base.usd"
spawn_cfg_addon = {
"rigid_props": RIGID_BODY_PROPS_HIGH_PRECISION.replace(disable_gravity=False, kinematic_enabled=True),
"mass_props": sim_utils.MassPropertiesCfg(mass=0.012),
"collision_props": sim_utils.CollisionPropertiesCfg(contact_offset=0.005, rest_offset=0.0),
}

def __init__(
self, instance_name: str | None = None, prim_path: str | None = None, initial_pose: Pose | None = None
):
super().__init__(instance_name=instance_name, prim_path=prim_path, initial_pose=initial_pose)


@register_asset
class BlueSortingBin(LibraryObject):
"""
Expand Down
1 change: 1 addition & 0 deletions isaaclab_arena/embodiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .agibot.agibot import *
from .droid.droid import *
from .franka.franka import *
from .franka.nist_gear_insertion.franka_osc import *
from .g1.g1 import *
from .galbot.galbot import *
from .gr1t2.gr1t2 import *
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) 2025-2026, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

"""Franka embodiment components for NIST gear insertion."""

from .franka_osc import FrankaNistGearInsertionOscEmbodiment

__all__ = [
"FrankaNistGearInsertionOscEmbodiment",
]
193 changes: 193 additions & 0 deletions isaaclab_arena/embodiments/franka/nist_gear_insertion/franka_osc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Copyright (c) 2025-2026, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

"""Franka OSC embodiment for the NIST gear insertion task."""

from __future__ import annotations

import isaaclab.sim as sim_utils
from isaaclab.actuators import ImplicitActuatorCfg
from isaaclab.assets.articulation.articulation_cfg import ArticulationCfg
from isaaclab.sensors.frame_transformer.frame_transformer_cfg import FrameTransformerCfg, OffsetCfg
from isaaclab.utils.assets import ISAACLAB_NUCLEUS_DIR

from isaaclab_arena.assets.register import register_asset
from isaaclab_arena.embodiments.common.arm_mode import ArmMode
from isaaclab_arena.embodiments.franka.franka import _DEFAULT_CAMERA_OFFSET, FrankaEmbodimentBase
from isaaclab_arena.embodiments.franka.nist_gear_insertion.gear_grasp import get_franka_nist_gear_insertion_grasp_config
from isaaclab_arena.tasks.nist_gear_insertion.events import GraspCfg
from isaaclab_arena.utils.pose import Pose

__all__ = [
"FrankaNistGearInsertionOscEmbodiment",
]

_FRANKA_MIMIC_OSC_USD_PATH = f"{ISAACLAB_NUCLEUS_DIR}/Factory/franka_mimic.usd"

# Mirrors Isaac Lab Factory's franka_mimic OSC setup in
# isaaclab_tasks.direct.factory.factory_env_cfg.
_FRANKA_MIMIC_OSC_RIGID_PROPS = sim_utils.RigidBodyPropertiesCfg(
disable_gravity=True,
max_depenetration_velocity=5.0,
linear_damping=0.0,
angular_damping=0.0,
max_linear_velocity=1000.0,
max_angular_velocity=3666.0,
enable_gyroscopic_forces=True,
solver_position_iteration_count=192,
solver_velocity_iteration_count=1,
max_contact_impulse=1e32,
)

_FRANKA_MIMIC_OSC_ARTICULATION_PROPS = sim_utils.ArticulationRootPropertiesCfg(
enabled_self_collisions=False,
solver_position_iteration_count=192,
solver_velocity_iteration_count=1,
)

_FRANKA_MIMIC_OSC_COLLISION_PROPS = sim_utils.CollisionPropertiesCfg(contact_offset=0.005, rest_offset=0.0)

# Default pose from the Factory-style Franka mimic asset.
_FRANKA_MIMIC_OSC_JOINT_POS = {
"panda_joint1": 0.0,
"panda_joint2": -0.569,
"panda_joint3": 0.0,
"panda_joint4": -2.810,
"panda_joint5": 0.0,
"panda_joint6": 3.037,
"panda_joint7": 0.741,
"panda_finger_joint2": 0.04,
}

# OSC writes joint torques directly for the arm; the hand remains position controlled.
_FRANKA_MIMIC_OSC_ACTUATORS = {
"panda_arm1": ImplicitActuatorCfg(
joint_names_expr=["panda_joint[1-4]"],
stiffness=0.0,
damping=0.0,
friction=0.0,
armature=0.0,
effort_limit_sim=87,
velocity_limit_sim=124.6,
),
"panda_arm2": ImplicitActuatorCfg(
joint_names_expr=["panda_joint[5-7]"],
stiffness=0.0,
damping=0.0,
friction=0.0,
armature=0.0,
effort_limit_sim=12,
velocity_limit_sim=149.5,
),
"panda_hand": ImplicitActuatorCfg(
joint_names_expr=["panda_finger_joint[1-2]"],
effort_limit_sim=40.0,
velocity_limit_sim=0.04,
stiffness=7500.0,
damping=173.0,
friction=0.1,
armature=0.0,
),
}

# Franka mimic USD with contact sensors enabled for the wrist force body.
_FRANKA_MIMIC_OSC_CFG = ArticulationCfg(
prim_path="{ENV_REGEX_NS}/Robot",
spawn=sim_utils.UsdFileCfg(
usd_path=_FRANKA_MIMIC_OSC_USD_PATH,
activate_contact_sensors=True,
rigid_props=_FRANKA_MIMIC_OSC_RIGID_PROPS,
articulation_props=_FRANKA_MIMIC_OSC_ARTICULATION_PROPS,
collision_props=_FRANKA_MIMIC_OSC_COLLISION_PROPS,
),
init_state=ArticulationCfg.InitialStateCfg(
joint_pos=_FRANKA_MIMIC_OSC_JOINT_POS,
pos=(0.0, 0.0, 0.0),
rot=(0.0, 0.0, 0.0, 1.0),
),
actuators=_FRANKA_MIMIC_OSC_ACTUATORS,
)

_GEAR_INSERTION_INITIAL_JOINT_POSE = [
0.561824,
0.287201,
-0.543103,
-2.410188,
0.507908,
2.847644,
0.454298,
0.04,
0.04,
]


def _gear_insertion_ee_frame_cfg() -> FrameTransformerCfg:
"""Return Franka frames used by the gear insertion OSC policy.

The policy commands the centered fingertip frame, while the finger frames
are exposed for grasp/reset diagnostics.
"""
return FrameTransformerCfg(
prim_path="{ENV_REGEX_NS}/Robot/panda_link0",
debug_vis=False,
target_frames=[
FrameTransformerCfg.FrameCfg(
prim_path="{ENV_REGEX_NS}/Robot/panda_fingertip_centered",
name="end_effector",
offset=OffsetCfg(pos=(0.0, 0.0, 0.0)),
),
FrameTransformerCfg.FrameCfg(
prim_path="{ENV_REGEX_NS}/Robot/panda_rightfinger",
name="tool_rightfinger",
offset=OffsetCfg(pos=(0.0, 0.0, 0.046)),
),
FrameTransformerCfg.FrameCfg(
prim_path="{ENV_REGEX_NS}/Robot/panda_leftfinger",
name="tool_leftfinger",
offset=OffsetCfg(pos=(0.0, 0.0, 0.046)),
),
],
)


@register_asset
class FrankaNistGearInsertionOscEmbodiment(FrankaEmbodimentBase):
"""Franka embodiment for NIST gear insertion with OSC torque control.

The embodiment owns the Franka mimic robot setup, OSC command frame, and
grasp metadata. Environments wire scene-specific asset names and insertion
geometry into the action, observation, and reward configs.
"""

name = "franka_nist_gear_insertion_osc"

def __init__(
self,
enable_cameras: bool = False,
initial_pose: Pose | None = None,
initial_joint_pose: list[float] | None = None,
concatenate_observation_terms: bool = False,
arm_mode: ArmMode | None = None,
camera_offset: Pose | None = _DEFAULT_CAMERA_OFFSET,
is_tiled_camera: bool = False,
):
super().__init__(
enable_cameras=enable_cameras,
initial_pose=initial_pose,
initial_joint_pose=initial_joint_pose,
concatenate_observation_terms=concatenate_observation_terms,
arm_mode=arm_mode,
camera_offset=camera_offset,
is_tiled_camera=is_tiled_camera,
)
self.scene_config.robot = _FRANKA_MIMIC_OSC_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")
self.scene_config.ee_frame = _gear_insertion_ee_frame_cfg()
self.set_initial_joint_pose(initial_joint_pose or _GEAR_INSERTION_INITIAL_JOINT_POSE)

def get_command_body_name(self) -> str:
return "panda_fingertip_centered"

def get_gear_insertion_grasp_config(self) -> GraspCfg:
return get_franka_nist_gear_insertion_grasp_config()
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright (c) 2025-2026, The Isaac Lab Arena Project Developers (https://github.com/isaac-sim/IsaacLab-Arena/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

"""Franka-specific grasp parameters for NIST gear insertion."""

from __future__ import annotations

import torch
from collections.abc import Sequence

from isaaclab_arena.tasks.nist_gear_insertion.events import GraspCfg


def franka_gripper_joint_setter(
joint_pos: torch.Tensor,
row_indices: Sequence[int],
finger_joint_indices: Sequence[int],
width: float,
) -> None:
"""Set Franka Panda finger joints from a total gripper opening width."""
for jid in finger_joint_indices:
joint_pos[row_indices, jid] = width / 2.0


def get_franka_nist_gear_insertion_grasp_config() -> GraspCfg:
"""Return reset grasp parameters for the Franka NIST gear insertion policy.

The reset first opens the fingers to 3 cm while inverse kinematics moves
``panda_hand`` to the gear, then closes the fingers to 0 m to hold the gear.
In Isaac Lab 3.0 xyzw convention, ``[1, 0, 0, 0]`` is an intentional
180-degree rotation about X. The ``[0.02, 0.0, -0.128]`` translation is the
held-gear-root to ``panda_hand`` offset that centers the gear between the
fingertips.
"""
return GraspCfg(
hand_grasp_width=0.03,
hand_close_width=0.0,
gripper_joint_setter_func=franka_gripper_joint_setter,
end_effector_body_name="panda_hand",
finger_joint_names="panda_finger_joint[1-2]",
grasp_rot_offset=[1.0, 0.0, 0.0, 0.0],
grasp_offset=[0.02, 0.0, -0.128],
arm_joint_names="panda_joint[1-7]",
)
1 change: 1 addition & 0 deletions isaaclab_arena/policy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
from .action_chunking import ActionChunkingState
from .action_chunking_client import *
from .replay_action_policy import *
from .rl_games_action_policy import *
from .rsl_rl_action_policy import *
from .zero_action_policy import *
Loading
Loading