Skip to content

Robot Presets

Robot presets are RobotOptions subclasses with the asset, DOF order, PD gains, armature and limits of one robot already filled in; construct one and put it in a scene. Composite rigs bundle several presets (an arm and a gripper, a humanoid and two hands) into one validated options object. The robots guide covers the catalog, composite rigs and adding a robot.

import eden as en
from eden.options.robots import COMPOSITE_ROBOT_REGISTRY, ROBOT_REGISTRY, Droid

robot = en.robots.Go1()
rig = Droid(base_pos=(0.0, 0.0, 0.4))           # hand-less Panda + Robotiq 2F-85
cls = COMPOSITE_ROBOT_REGISTRY.get("ur5e_2f85")  # -> UR5eRobotiq

ROBOT_REGISTRY looks unit robots up by key and COMPOSITE_ROBOT_REGISTRY composite rigs. Each section below renders one module of eden.options.robots.

Humanoids

Unitree G1

Unitree G1 humanoid configurations (23/29 DOF variants).

Attributes

G1_DOFS23_NAME module-attribute

G1_DOFS23_NAME: list[str] = ['left_hip_pitch_joint', 'left_hip_roll_joint', 'left_hip_yaw_joint', 'left_knee_joint', 'left_ankle_pitch_joint', 'left_ankle_roll_joint', 'right_hip_pitch_joint', 'right_hip_roll_joint', 'right_hip_yaw_joint', 'right_knee_joint', 'right_ankle_pitch_joint', 'right_ankle_roll_joint', 'waist_yaw_joint', 'left_shoulder_pitch_joint', 'left_shoulder_roll_joint', 'left_shoulder_yaw_joint', 'left_elbow_joint', 'left_wrist_roll_joint', 'right_shoulder_pitch_joint', 'right_shoulder_roll_joint', 'right_shoulder_yaw_joint', 'right_elbow_joint', 'right_wrist_roll_joint']

G1_UCSD_NO_WRIST_DOFS_NAME module-attribute

G1_UCSD_NO_WRIST_DOFS_NAME: list[str] = ['left_hip_pitch_joint', 'left_hip_roll_joint', 'left_hip_yaw_joint', 'left_knee_joint', 'left_ankle_pitch_joint', 'left_ankle_roll_joint', 'right_hip_pitch_joint', 'right_hip_roll_joint', 'right_hip_yaw_joint', 'right_knee_joint', 'right_ankle_pitch_joint', 'right_ankle_roll_joint', 'waist_yaw_joint', 'waist_roll_joint', 'waist_pitch_joint', 'left_shoulder_pitch_joint', 'left_shoulder_roll_joint', 'left_shoulder_yaw_joint', 'left_elbow_joint', 'right_shoulder_pitch_joint', 'right_shoulder_roll_joint', 'right_shoulder_yaw_joint', 'right_elbow_joint']
G1_FEET_LINKS_NAME: tuple[str, ...] = ('left_ankle_roll_link', 'right_ankle_roll_link')

G1_DOFS29_NAME module-attribute

G1_DOFS29_NAME: list[str] = ['left_hip_pitch_joint', 'left_hip_roll_joint', 'left_hip_yaw_joint', 'left_knee_joint', 'left_ankle_pitch_joint', 'left_ankle_roll_joint', 'right_hip_pitch_joint', 'right_hip_roll_joint', 'right_hip_yaw_joint', 'right_knee_joint', 'right_ankle_pitch_joint', 'right_ankle_roll_joint', 'waist_yaw_joint', 'waist_roll_joint', 'waist_pitch_joint', 'left_shoulder_pitch_joint', 'left_shoulder_roll_joint', 'left_shoulder_yaw_joint', 'left_elbow_joint', 'left_wrist_roll_joint', 'left_wrist_pitch_joint', 'left_wrist_yaw_joint', 'right_shoulder_pitch_joint', 'right_shoulder_roll_joint', 'right_shoulder_yaw_joint', 'right_elbow_joint', 'right_wrist_roll_joint', 'right_wrist_pitch_joint', 'right_wrist_yaw_joint']
G1_DOFS29_TRACKED_LINKS_NAME: list[str] = ['pelvis', 'left_hip_pitch_link', 'right_hip_pitch_link', 'waist_yaw_link', 'left_hip_roll_link', 'right_hip_roll_link', 'waist_roll_link', 'left_hip_yaw_link', 'right_hip_yaw_link', 'torso_link', 'left_knee_link', 'right_knee_link', 'left_shoulder_pitch_link', 'right_shoulder_pitch_link', 'left_ankle_pitch_link', 'right_ankle_pitch_link', 'left_shoulder_roll_link', 'right_shoulder_roll_link', 'left_ankle_roll_link', 'right_ankle_roll_link', 'left_shoulder_yaw_link', 'right_shoulder_yaw_link', 'left_elbow_link', 'right_elbow_link', 'left_wrist_roll_link', 'right_wrist_roll_link', 'left_wrist_pitch_link', 'right_wrist_pitch_link', 'left_wrist_yaw_link', 'right_wrist_yaw_link']

G1_DEX3_LEFT_DOFS_NAME module-attribute

G1_DEX3_LEFT_DOFS_NAME: list[str] = ['left_hand_thumb_0_joint', 'left_hand_thumb_1_joint', 'left_hand_thumb_2_joint', 'left_hand_middle_0_joint', 'left_hand_middle_1_joint', 'left_hand_index_0_joint', 'left_hand_index_1_joint']

G1_DEX3_RIGHT_DOFS_NAME module-attribute

G1_DEX3_RIGHT_DOFS_NAME: list[str] = [name.replace('left_', 'right_', 1) for name in G1_DEX3_LEFT_DOFS_NAME]

G1_ACTION_SCALE module-attribute

G1_ACTION_SCALE: dict[str, float] = {p: s.RAIBERT_HEURISTIC_ACTION_SCALE() for p, s in _G1Dofs29_DOFS_SPEC.items()}

G1_IK_PROFILE module-attribute

G1_IK_PROFILE: BipedalIKProfile = BipedalIKProfile(robot_options=G1Dofs29(), left_foot_link='left_ankle_roll_link', right_foot_link='right_ankle_roll_link', left_foot_local_offset=(0.04, 0.0, -0.037), right_foot_local_offset=(0.04, 0.0, -0.037), upper_body_joint_patterns=('*_shoulder_*_joint', '*_elbow_joint', '*_wrist_*_joint'), base_lin_bias_weights=(1.0, 1.0, 1.0), base_ang_bias_weights=(1.0, 1.0, 2.5), joint_bias_patterns={'*_hip_roll_joint': 0.0, '*_hip_yaw_joint': 0.0, '*_ankle_roll_joint': 0.0}, qpos_bias_scale=0.5, ee_weights=(1.0, 1.0, 1.0, 1.0, 1.0), damping=0.001, dq_bound=10.0)

Classes

G1Dofs23

G1Dofs23(**data)

Bases: RobotOptions

Unitree 23 DOF G1 Configuration.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/unitree_g1/g1_23dof.xml'
links_to_keep: list[str] = list(G1_FEET_LINKS_NAME)
feet_links_name: tuple[str, ...] = G1_FEET_LINKS_NAME
dofs_name class-attribute instance-attribute
dofs_name: list[str] = G1_DOFS23_NAME
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.783675)
metadata class-attribute
metadata: dict[str, Any] = {'robot_height': 1.32}

G1Dofs29NoWristDof

G1Dofs29NoWristDof(**data)

Bases: G1Dofs23

23-active-DOF G1 built from the 29-DOF G1 with wrists welded (the AMO / GMT training robot).

Same active-DOF count as :class:G1Dofs23 but a different joint set: the default g1_23dof.xml is the official g1_23dof_rev_1_0 (1-DOF waist + wrist_roll), whereas this keeps the full 3-DOF waist (yaw/roll/pitch) and drops both wrists. file and dofs_name are bound here so they can't drift.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/unitree_g1/g1_29dof_wrist_disabled.xml'
dofs_name class-attribute instance-attribute

G1Dofs29

G1Dofs29(**data)

Bases: RobotOptions

Unitree G1 29DoF Configuration.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _G1Dofs29_DOFS_SPEC
file class-attribute instance-attribute
file: str = 'humanoids/unitree_g1/g1_29dof_rev_1_0.xml'
links_to_keep: list[str] = list(G1_FEET_LINKS_NAME)
feet_links_name: tuple[str, ...] = G1_FEET_LINKS_NAME
dofs_name class-attribute instance-attribute
dofs_name: list[str] = G1_DOFS29_NAME
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.783675)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'*_hip_pitch_joint': -0.1, '*_hip_roll_joint': 0.0, '*_hip_yaw_joint': 0.0, '*_knee_joint': 0.3, '*_ankle_pitch_joint': -0.2, '*_ankle_roll_joint': 0.0, 'waist_yaw_joint': 0.0, 'waist_roll_joint': 0.0, 'waist_pitch_joint': 0.0, '*_shoulder_pitch_joint': 0.0, 'left_shoulder_roll_joint': 0.15, '*_shoulder_yaw_joint': 0.0, '*_elbow_joint': 1.5, '*_wrist_roll_joint': 0.0, '*_wrist_pitch_joint': 0.0, '*_wrist_yaw_joint': 0.0, 'right_shoulder_roll_joint': -0.15}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KP() for p, s in _G1Dofs29_DOFS_SPEC.items()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KD() for p, s in _G1Dofs29_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _G1Dofs29_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _G1Dofs29_DOFS_SPEC.items()}
metadata class-attribute
metadata: dict[str, Any] = {'robot_height': 1.32}

G1Dofs29NoHands

G1Dofs29NoHands(**data)

Bases: G1Dofs29

Unitree G1 29DoF Configuration with no hands.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/unitree_g1/g1_29dof_rev_1_0_no_hand.xml'

G1Dofs29Dex3

G1Dofs29Dex3(**data)

Bases: G1Dofs29

Unitree G1 29DoF + two 7-DOF Dex3 hands (43 actuated DOFs) — the SONIC teleop sim body.

Body joints keep the :class:G1Dofs29 actuator spec. Hand gains are the Dex3 controller's values (kp 1.5 / kd 0.1, dex3_hands.hpp); hand armature and force limits come from the source MJCF (armature 0.01, actuatorfrcrange ±2.45 thumb base / ±1.4 other fingers).

The MJCF is the GEAR-SONIC model, kept separate from g1_29dof_rev_1_0: the SONIC calibration offsets are pinned to this model's torso_link frame (10 mm lower than rev_1_0's). file is the single source of truth for the asset path — the operator-side FK and visualizer read it too.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/unitree_g1/g1_29dof_with_hand_sonic.xml'
dofs_name class-attribute instance-attribute
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {**G1Dofs29().default_dofs_pos, '*_hand_*_joint': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {**G1Dofs29().default_dofs_kp, '*_hand_*_joint': 1.5}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {**G1Dofs29().default_dofs_kd, '*_hand_*_joint': 0.1}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {**G1Dofs29().default_dofs_armature, '*_hand_*_joint': 0.01}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {**G1Dofs29().default_dofs_force_limits, '*_hand_thumb_0_joint': 2.45, '*_hand_thumb_[12]_joint': 1.4, '*_hand_middle_*_joint': 1.4, '*_hand_index_*_joint': 1.4}

LimX Oli

LimX Dynamics Oli (HU_D04) humanoid configuration (31 DoF).

Attributes

OLI_DOFS_NAME module-attribute

OLI_DOFS_NAME: list[str] = ['left_hip_pitch_joint', 'left_hip_roll_joint', 'left_hip_yaw_joint', 'left_knee_joint', 'left_ankle_pitch_joint', 'left_ankle_roll_joint', 'right_hip_pitch_joint', 'right_hip_roll_joint', 'right_hip_yaw_joint', 'right_knee_joint', 'right_ankle_pitch_joint', 'right_ankle_roll_joint', 'waist_yaw_joint', 'waist_roll_joint', 'waist_pitch_joint', 'head_yaw_joint', 'head_pitch_joint', 'left_shoulder_pitch_joint', 'left_shoulder_roll_joint', 'left_shoulder_yaw_joint', 'left_elbow_joint', 'left_wrist_yaw_joint', 'left_wrist_pitch_joint', 'left_wrist_roll_joint', 'right_shoulder_pitch_joint', 'right_shoulder_roll_joint', 'right_shoulder_yaw_joint', 'right_elbow_joint', 'right_wrist_yaw_joint', 'right_wrist_pitch_joint', 'right_wrist_roll_joint']

OLI_NATURAL_FREQ module-attribute

OLI_NATURAL_FREQ: float = 2.0 * np.pi * 5.0
OLI_FEET_LINKS_NAME: tuple[str, ...] = ('left_ankle_roll_link', 'right_ankle_roll_link')
OLI_GANTRY_LINKS_NAME: tuple[str, ...] = ('left_shoulder_pitch_link', 'right_shoulder_pitch_link')

OLI_ACTION_SCALE module-attribute

OLI_ACTION_SCALE: dict[str, float] = {p: raibert_heuristic_action_scale(s, OLI_NATURAL_FREQ) for p, s in _OLI_DOFS_SPEC.items()}

Classes

Oli

Oli(**data)

Bases: RobotOptions

LimX Dynamics Oli (HU_D04) 31-DoF humanoid configuration.

Assets: humanoids/limx_oli in the Kashu7100/eden_assets dataset. The serial HU_D04_01.urdf is used (its 31-DoF tree matches dofs_name); the sibling HU_D04_01.xml MJCF is the parallel model whose ankle/waist are expressed as *_achilles / waist_A/B closed-loop joints and does not expose the serial ankle_pitch/roll DoFs directly.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _OLI_DOFS_SPEC
file class-attribute instance-attribute
file: str = 'humanoids/limx_oli/HU_D04_01.urdf'
links_to_keep: list[str] = list(OLI_FEET_LINKS_NAME)
feet_links_name: tuple[str, ...] = OLI_FEET_LINKS_NAME
gantry_links_name: tuple[str, ...] = OLI_GANTRY_LINKS_NAME
dofs_name class-attribute instance-attribute
dofs_name: list[str] = OLI_DOFS_NAME
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.903)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'left_hip_pitch_joint': -0.16, 'left_hip_roll_joint': 0.0, 'left_hip_yaw_joint': -0.05, 'left_knee_joint': 0.28, 'left_ankle_pitch_joint': -0.16, 'left_ankle_roll_joint': 0.0, 'right_hip_pitch_joint': -0.16, 'right_hip_roll_joint': 0.0, 'right_hip_yaw_joint': 0.05, 'right_knee_joint': 0.28, 'right_ankle_pitch_joint': -0.16, 'right_ankle_roll_joint': 0.0, 'waist_yaw_joint': 0.0, 'waist_roll_joint': 0.0, 'waist_pitch_joint': 0.0, 'head_yaw_joint': 0.0, 'head_pitch_joint': 0.0, 'left_shoulder_pitch_joint': -0.063, 'left_shoulder_roll_joint': 0.146, 'left_shoulder_yaw_joint': -0.297, 'left_elbow_joint': -0.086, 'left_wrist_yaw_joint': 0.0, 'left_wrist_pitch_joint': 0.0, 'left_wrist_roll_joint': 0.0, 'right_shoulder_pitch_joint': -0.063, 'right_shoulder_roll_joint': -0.146, 'right_shoulder_yaw_joint': 0.297, 'right_elbow_joint': -0.086, 'right_wrist_yaw_joint': 0.0, 'right_wrist_pitch_joint': 0.0, 'right_wrist_roll_joint': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {p: raibert_heuristic_kp(s, OLI_NATURAL_FREQ) for p, s in _OLI_DOFS_SPEC.items()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: raibert_heuristic_kd(s, OLI_NATURAL_FREQ) for p, s in _OLI_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _OLI_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _OLI_DOFS_SPEC.items()}
metadata class-attribute
metadata: dict[str, Any] = {'robot_height': 1.65}

OliNoHands

OliNoHands(**data)

Bases: Oli

Oli with its moulded rubber hands removed, for welding a real end effector.

Teleoperating a Dex3 / XHand / Dex1 gripper (or a Wuji Hand2 — see :class:~eden.options.robots.composite.oli_wuji.OliWuji) means welding that end effector onto the wrist, and the stock moulded hand would then sit stacked inside it. This variant points at HU_D04_01_no_hand.urdf; every joint and every other link is identical to the stock URDF, so DOFs, gains and pose are inherited unchanged from :class:Oli. Four things differ:

  • *_wrist_roll_link points at *_wrist_roll_link_nohand.STL. This is what removes the hand: it is baked into the stock wrist mesh, not carried on a link, and so is the silver end-cap it bolted onto. Dropping both leaves 72 of 322 cm^3 and exposes the real flange.
  • The wrist inertial is recomputed for the bare metal — 0.205 kg with its CoM 2.9 cm down, against the stock 0.581 kg at 8.9 cm, which was dominated by the rubber hand. So the arm carries the end effector you weld on, not that plus a phantom 0.38 kg per wrist.
  • Two *_hand_mount frames are added on the exposed flange (z = -0.058 in *_wrist_roll_link, the plane upstream's own stock URDF puts *_hand_contact on). They carry no mass and no geometry; welding an end effector to them needs no offset.
  • The four fixed *_hand_{manip,contact} links/joints are dropped. Despite the names these are not the hand, just 1 cm / 1 g marker boxes for the grasp and contact frames LimX's stack targets; they are fixed children Genesis merges away regardless.
Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/limx_oli/HU_D04_01_no_hand.urdf'
links_to_keep: list[str] = [*OLI_FEET_LINKS_NAME, 'left_hand_mount', 'right_hand_mount']

Functions:

SMPL humanoids

SMPL and SMPL-X humanoid configurations.

Attributes

SMPL_BODY_NAMES module-attribute

SMPL_BODY_NAMES = ['Pelvis', 'L_Hip', 'L_Knee', 'L_Ankle', 'L_Toe', 'R_Hip', 'R_Knee', 'R_Ankle', 'R_Toe', 'Torso', 'Spine', 'Chest', 'Neck', 'Head', 'L_Thorax', 'L_Shoulder', 'L_Elbow', 'L_Wrist', 'L_Hand', 'R_Thorax', 'R_Shoulder', 'R_Elbow', 'R_Wrist', 'R_Hand']

SMPL_DOF_NAMES module-attribute

SMPL_DOF_NAMES = [f'{body}_{axis}' for body in SMPL_BODY_NAMES[1:] for axis in 'xyz']

PHC_SMPL_BODY_STIFFNESS module-attribute

PHC_SMPL_BODY_STIFFNESS = {'L_Hip': 800.0, 'L_Knee': 800.0, 'L_Ankle': 800.0, 'L_Toe': 500.0, 'R_Hip': 800.0, 'R_Knee': 800.0, 'R_Ankle': 800.0, 'R_Toe': 500.0, 'Torso': 1000.0, 'Spine': 1000.0, 'Chest': 1000.0, 'Neck': 500.0, 'Head': 500.0, 'L_Thorax': 500.0, 'L_Shoulder': 500.0, 'L_Elbow': 500.0, 'L_Wrist': 300.0, 'L_Hand': 300.0, 'R_Thorax': 500.0, 'R_Shoulder': 500.0, 'R_Elbow': 500.0, 'R_Wrist': 300.0, 'R_Hand': 300.0}

PHC_DAMPING_PER_STIFFNESS module-attribute

PHC_DAMPING_PER_STIFFNESS = 0.1

PHC_DRIVE_KP_SCALE module-attribute

PHC_DRIVE_KP_SCALE = 0.4

PHC_DRIVE_KD_SCALE module-attribute

PHC_DRIVE_KD_SCALE = 0.7

PHC_EFFORT_LIMIT module-attribute

PHC_EFFORT_LIMIT = 500.0

Classes

SmplHumanoid

SmplHumanoid(**data)

Bases: RobotOptions

SMPL humanoid robot (24 bodies, 69 hinge DOFs).

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/smpl_humanoid/smpl_humanoid.xml'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = SMPL_DOF_NAMES
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.9)

PhcSmplHumanoid

PhcSmplHumanoid(**data)

Bases: SmplHumanoid

PHC's phc_3 SMPL humanoid: neutral shape, upright start, box body, real weight, PD-driven.

The :class:SmplHumanoid skeleton (same bodies, joints, geoms and DOF order) generated with the robot options PHC trained phc_3 with (real_weight_porpotion_boxes=True, so the five box geoms carry their real-weight densities instead of 1000) and with the floor, light and motors stripped (examples/projects/mimic/assistmimic/scripts/generate_smpl_xml.py). Isaac Gym read each joint's stiffness / damping as a position drive and the motor gear as its effort limit; here they are default_dofs_kp / default_dofs_kd (calibrated for Genesis by PHC_DRIVE_K*_SCALE) and default_dofs_force_limits, and the passive spring and damper are zeroed.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/smpl_humanoid/smpl_humanoid_phc.xml'
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.95)
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = _phc_stiffness_times(PHC_DRIVE_KP_SCALE)
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = _phc_stiffness_times(PHC_DAMPING_PER_STIFFNESS * PHC_DRIVE_KD_SCALE)
default_dofs_stiffness class-attribute instance-attribute
default_dofs_stiffness: dict[str, float] = dict.fromkeys(SMPL_DOF_NAMES, 0.0)
default_dofs_damping class-attribute instance-attribute
default_dofs_damping: dict[str, float] = dict.fromkeys(SMPL_DOF_NAMES, 0.0)
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = dict.fromkeys(SMPL_DOF_NAMES, PHC_EFFORT_LIMIT)

SmplxHumanoid

SmplxHumanoid(**data)

Bases: RobotOptions

Configuration for the SMPL-X humanoid model.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'humanoids/smplx_humanoid/smplx_humanoid.xml'
up class-attribute instance-attribute
up: tuple[int, int, int] = (0, 1, 0)
front class-attribute instance-attribute
front: tuple[int, int, int] = (0, 0, 1)
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.9)
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['L_Hip_x', 'L_Hip_y', 'L_Hip_z', 'L_Knee_x', 'L_Knee_y', 'L_Knee_z', 'L_Ankle_x', 'L_Ankle_y', 'L_Ankle_z', 'L_Toe_x', 'L_Toe_y', 'L_Toe_z', 'R_Hip_x', 'R_Hip_y', 'R_Hip_z', 'R_Knee_x', 'R_Knee_y', 'R_Knee_z', 'R_Ankle_x', 'R_Ankle_y', 'R_Ankle_z', 'R_Toe_x', 'R_Toe_y', 'R_Toe_z', 'Torso_x', 'Torso_y', 'Torso_z', 'Spine_x', 'Spine_y', 'Spine_z', 'Chest_x', 'Chest_y', 'Chest_z', 'Neck_x', 'Neck_y', 'Neck_z', 'Head_x', 'Head_y', 'Head_z', 'L_Thorax_x', 'L_Thorax_y', 'L_Thorax_z', 'L_Shoulder_x', 'L_Shoulder_y', 'L_Shoulder_z', 'L_Elbow_x', 'L_Elbow_y', 'L_Elbow_z', 'L_Wrist_x', 'L_Wrist_y', 'L_Wrist_z', 'L_Index1_x', 'L_Index1_y', 'L_Index1_z', 'L_Index2_x', 'L_Index2_y', 'L_Index2_z', 'L_Index3_x', 'L_Index3_y', 'L_Index3_z', 'L_Middle1_x', 'L_Middle1_y', 'L_Middle1_z', 'L_Middle2_x', 'L_Middle2_y', 'L_Middle2_z', 'L_Middle3_x', 'L_Middle3_y', 'L_Middle3_z', 'L_Pinky1_x', 'L_Pinky1_y', 'L_Pinky1_z', 'L_Pinky2_x', 'L_Pinky2_y', 'L_Pinky2_z', 'L_Pinky3_x', 'L_Pinky3_y', 'L_Pinky3_z', 'L_Ring1_x', 'L_Ring1_y', 'L_Ring1_z', 'L_Ring2_x', 'L_Ring2_y', 'L_Ring2_z', 'L_Ring3_x', 'L_Ring3_y', 'L_Ring3_z', 'L_Thumb1_x', 'L_Thumb1_y', 'L_Thumb1_z', 'L_Thumb2_x', 'L_Thumb2_y', 'L_Thumb2_z', 'L_Thumb3_x', 'L_Thumb3_y', 'L_Thumb3_z', 'R_Thorax_x', 'R_Thorax_y', 'R_Thorax_z', 'R_Shoulder_x', 'R_Shoulder_y', 'R_Shoulder_z', 'R_Elbow_x', 'R_Elbow_y', 'R_Elbow_z', 'R_Wrist_x', 'R_Wrist_y', 'R_Wrist_z', 'R_Index1_x', 'R_Index1_y', 'R_Index1_z', 'R_Index2_x', 'R_Index2_y', 'R_Index2_z', 'R_Index3_x', 'R_Index3_y', 'R_Index3_z', 'R_Middle1_x', 'R_Middle1_y', 'R_Middle1_z', 'R_Middle2_x', 'R_Middle2_y', 'R_Middle2_z', 'R_Middle3_x', 'R_Middle3_y', 'R_Middle3_z', 'R_Pinky1_x', 'R_Pinky1_y', 'R_Pinky1_z', 'R_Pinky2_x', 'R_Pinky2_y', 'R_Pinky2_z', 'R_Pinky3_x', 'R_Pinky3_y', 'R_Pinky3_z', 'R_Ring1_x', 'R_Ring1_y', 'R_Ring1_z', 'R_Ring2_x', 'R_Ring2_y', 'R_Ring2_z', 'R_Ring3_x', 'R_Ring3_y', 'R_Ring3_z', 'R_Thumb1_x', 'R_Thumb1_y', 'R_Thumb1_z', 'R_Thumb2_x', 'R_Thumb2_y', 'R_Thumb2_z', 'R_Thumb3_x', 'R_Thumb3_y', 'R_Thumb3_z']

Quadrupeds

Unitree Go1

Unitree Go1 quadruped configuration.

Attributes

GO1_FEET_LINKS_NAME: tuple[str, ...] = ('FL_foot', 'RL_foot', 'FR_foot', 'RR_foot')

GO1_ACTION_SCALE module-attribute

GO1_ACTION_SCALE: dict[str, float] = {p: s.RAIBERT_HEURISTIC_ACTION_SCALE() for p, s in _Go1_DOFS_SPEC.items()}

Classes

Go1

Go1(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'quadrupeds/unitree_go1/go1.urdf'
dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _Go1_DOFS_SPEC
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['FL_hip_joint', 'RL_hip_joint', 'FR_hip_joint', 'RR_hip_joint', 'FL_thigh_joint', 'RL_thigh_joint', 'FR_thigh_joint', 'RR_thigh_joint', 'FL_calf_joint', 'RL_calf_joint', 'FR_calf_joint', 'RR_calf_joint']
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.36)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'FL_hip_joint': 0.0, 'RL_hip_joint': 0.0, 'FR_hip_joint': -0.0, 'RR_hip_joint': -0.0, 'FL_thigh_joint': 0.8, 'RL_thigh_joint': 1.0, 'FR_thigh_joint': 0.8, 'RR_thigh_joint': 1.0, '*_calf_joint': -1.5}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KP() for p, s in _Go1_DOFS_SPEC.items()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KD() for p, s in _Go1_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _Go1_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _Go1_DOFS_SPEC.items()}
links_to_keep: list[str] = list(GO1_FEET_LINKS_NAME)
feet_links_name: tuple[str, ...] = GO1_FEET_LINKS_NAME

Unitree Go2 and Go2-W

Unitree Go2 and Go2-W quadruped configurations.

Attributes

GO2_FEET_LINKS_NAME: tuple[str, ...] = ('FL_foot', 'RL_foot', 'FR_foot', 'RR_foot')

GO2_ACTION_SCALE module-attribute

GO2_ACTION_SCALE: dict[str, float] = {p: s.RAIBERT_HEURISTIC_ACTION_SCALE() for p, s in _Go2_DOFS_SPEC.items()}

GO2W_ACTION_SCALE module-attribute

GO2W_ACTION_SCALE: dict[str, float] = {p: s.RAIBERT_HEURISTIC_ACTION_SCALE() for p, s in _Go2W_DOFS_SPEC.items()}

Classes

Go2

Go2(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'quadrupeds/unitree_go2/go2.urdf'
dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _Go2_DOFS_SPEC
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['FL_hip_joint', 'RL_hip_joint', 'FR_hip_joint', 'RR_hip_joint', 'FL_thigh_joint', 'RL_thigh_joint', 'FR_thigh_joint', 'RR_thigh_joint', 'FL_calf_joint', 'RL_calf_joint', 'FR_calf_joint', 'RR_calf_joint']
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.36)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'FL_hip_joint': 0.0, 'RL_hip_joint': 0.0, 'FR_hip_joint': -0.0, 'RR_hip_joint': -0.0, 'FL_thigh_joint': 0.8, 'RL_thigh_joint': 1.0, 'FR_thigh_joint': 0.8, 'RR_thigh_joint': 1.0, 'FL_calf_joint': -1.5, 'RL_calf_joint': -1.5, 'FR_calf_joint': -1.5, 'RR_calf_joint': -1.5}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KP() for p, s in _Go2_DOFS_SPEC.items()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KD() for p, s in _Go2_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _Go2_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _Go2_DOFS_SPEC.items()}
links_to_keep: list[str] = list(GO2_FEET_LINKS_NAME)
feet_links_name: tuple[str, ...] = GO2_FEET_LINKS_NAME

Go2W

Go2W(**data)

Bases: Go2

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'quadrupeds/unitree_go2w/go2w.urdf'
feet_links_name: tuple[str, ...] = ()
dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _Go2W_DOFS_SPEC
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['FL_hip_joint', 'RL_hip_joint', 'FR_hip_joint', 'RR_hip_joint', 'FL_thigh_joint', 'RL_thigh_joint', 'FR_thigh_joint', 'RR_thigh_joint', 'FL_calf_joint', 'RL_calf_joint', 'FR_calf_joint', 'RR_calf_joint', 'FL_foot_joint', 'RL_foot_joint', 'FR_foot_joint', 'RR_foot_joint']
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.46)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'FL_hip_joint': 0.0, 'RL_hip_joint': 0.0, 'FR_hip_joint': 0.0, 'RR_hip_joint': 0.0, 'FL_thigh_joint': 0.67, 'RL_thigh_joint': 0.67, 'FR_thigh_joint': 0.67, 'RR_thigh_joint': 0.67, 'FL_calf_joint': -1.3, 'RL_calf_joint': -1.3, 'FR_calf_joint': -1.3, 'RR_calf_joint': -1.3, 'FL_foot_joint': 0.0, 'RL_foot_joint': 0.0, 'FR_foot_joint': 0.0, 'RR_foot_joint': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KP() for p, s in _Go2W_DOFS_SPEC.items()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KD() for p, s in _Go2W_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _Go2W_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _Go2W_DOFS_SPEC.items()}

Arms

Franka Emika Panda and Franka Research 3

Franka Emika Panda and Franka Research 3 arm configurations.

Attributes

Classes

FrankaResearch3

FrankaResearch3(**data)

Bases: RobotOptions

The Franka Research 3, Franka Robotics' successor to the Panda.

From MuJoCo Menagerie's franka_fr3 (Apache-2.0), which derives it from Franka's own franka_description, with two Eden edits baked into the shipped asset: its links and joints are renamed link0…link7 / joint1…joint7 (the naming every other arm here uses, and what this class's DOF maps and the composite rigs address), and the bare attachment_site becomes an attachment body carrying the same mount frame as panda_nohand.xml — 0.107 m up from link7, rotated 135 degrees about z — so a gripper welds onto either Franka arm unchanged.

The FR3 is the same arm mechanically as the Panda (identical link frames, identical collision hulls, 855 mm reach, 3 kg payload, ±87 / ±12 N·m joint torques), and differs in what it declares: its own joint limits (A6 is shifted by 26 degrees, so a Panda home pose is not automatically reachable), Franka's published inertials, and joint friction identified in torque control (armature 0.195 / 0.074, damping 0.21, frictionloss 1.137 … 0.248) where the Panda model has none.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/franka_fr3/fr3.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.0)
links_to_keep: list[str] = ['attachment']
ee_links_name: list[str] = ['attachment']
arm_dofs_name class-attribute
arm_dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 0.0, 'joint3': 0.0, 'joint4': -2.27, 'joint5': 0.0, 'joint6': 2.27, 'joint7': 0.785398}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'joint1': 4500, 'joint2': 4500, 'joint3': 3500, 'joint4': 3500, 'joint5': 2000, 'joint6': 2000, 'joint7': 2000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'joint1': 450, 'joint2': 450, 'joint3': 350, 'joint4': 350, 'joint5': 200, 'joint6': 200, 'joint7': 200}

FrankaEmikaPanda

FrankaEmikaPanda(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'manipulators/franka_panda/panda.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.0)
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7', 'finger_joint1', 'finger_joint2']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 0.0, 'joint3': 0.0, 'joint4': -2.27, 'joint5': 0.0, 'joint6': 2.27, 'joint7': 0.785398, 'finger_joint1': 0.04, 'finger_joint2': 0.04}
links_to_keep: list[str] = ['left_gripper_pad', 'right_gripper_pad']
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'joint1': 4500, 'joint2': 4500, 'joint3': 3500, 'joint4': 3500, 'joint5': 2000, 'joint6': 2000, 'joint7': 2000, 'finger_joint1': 100, 'finger_joint2': 100}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'joint1': 450, 'joint2': 450, 'joint3': 350, 'joint4': 350, 'joint5': 200, 'joint6': 200, 'joint7': 200, 'finger_joint1': 10, 'finger_joint2': 10}
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(ee_link_names=['hand'])
ee_links_name: list[str] = ['hand']
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'finger_joint1': 0.04, 'finger_joint2': 0.04}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'finger_joint1': 0.0, 'finger_joint2': 0.0}

FrankaEmikaPandaNoHand

FrankaEmikaPandaNoHand(**data)

Bases: RobotOptions

The Panda arm without its hand: panda_nohand.xml, the seven joints ending in a flange.

The Panda counterpart of :class:FrankaResearch3, and what a composite rig wants when the end-effector is a separate entity — :class:~eden.options.robots.FrankaEmikaPanda is one MJCF with the fingers inside it, so its hand cannot be swapped or welded to. The two arms expose the same attachment mount frame, so a gripper written for one mounts on the other unchanged.

The Panda's own gains and joint limits, which are not the FR3's: see :class:FrankaResearch3 for what differs between the two robots.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'manipulators/franka_panda/panda_nohand.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
default_root_pos class-attribute instance-attribute
default_root_pos: Vec3FType = (0.0, 0.0, 0.0)
links_to_keep: list[str] = ['attachment']
ee_links_name: list[str] = ['attachment']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(ee_link_names=['attachment'])
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 0.0, 'joint3': 0.0, 'joint4': -2.27, 'joint5': 0.0, 'joint6': 2.27, 'joint7': 0.785398}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'joint1': 4500, 'joint2': 4500, 'joint3': 3500, 'joint4': 3500, 'joint5': 2000, 'joint6': 2000, 'joint7': 2000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'joint1': 450, 'joint2': 450, 'joint3': 350, 'joint4': 350, 'joint5': 200, 'joint6': 200, 'joint7': 200}

Universal Robots UR5e

Universal Robots UR5e arm configuration.

Attributes

Classes

UR5e

UR5e(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/universal_ur5e/ur5e.urdf'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
links_to_keep: list[str] = ['ee_link']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['shoulder_pan', 'shoulder_lift', 'elbow', 'wrist_1', 'wrist_2', 'wrist_3']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'shoulder_pan': 0 * np.pi / 180, 'shoulder_lift': -90 * np.pi / 180, 'elbow': 90 * np.pi / 180, 'wrist_1': -90 * np.pi / 180, 'wrist_2': -90 * np.pi / 180, 'wrist_3': 0 * np.pi / 180}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'shoulder_pan': 4500, 'shoulder_lift': 4500, 'elbow': 3500, 'wrist_1': 3500, 'wrist_2': 1000, 'wrist_3': 1000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'shoulder_pan': 450, 'shoulder_lift': 450, 'elbow': 350, 'wrist_1': 350, 'wrist_2': 200, 'wrist_3': 200}
ee_links_name: list[str] = ['ee_link']

Universal Robots UR10e

Universal Robots UR10e arm configuration.

Attributes

Classes

UR10e

UR10e(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/universal_ur10e/ur10e.urdf'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
links_to_keep: list[str] = ['ee_link']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['shoulder_pan', 'shoulder_lift', 'elbow', 'wrist_1', 'wrist_2', 'wrist_3']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'shoulder_pan': 0 * np.pi / 180, 'shoulder_lift': -90 * np.pi / 180, 'elbow': 90 * np.pi / 180, 'wrist_1': -90 * np.pi / 180, 'wrist_2': -90 * np.pi / 180, 'wrist_3': 0 * np.pi / 180}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'shoulder_pan': 4500, 'shoulder_lift': 4500, 'elbow': 3500, 'wrist_1': 3500, 'wrist_2': 1000, 'wrist_3': 1000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'shoulder_pan': 450, 'shoulder_lift': 450, 'elbow': 350, 'wrist_1': 350, 'wrist_2': 200, 'wrist_3': 200}
ee_links_name: list[str] = ['ee_link']

UFACTORY xArm7

UFACTORY xArm7 arm configuration.

Attributes

Classes

Xarm7

Xarm7(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/ufactory_xarm7/xarm.urdf'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
links_to_keep: list[str] = ['link_eef']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 0.0, 'joint3': 0.0, 'joint4': np.pi / 3, 'joint5': 0.0, 'joint6': np.pi / 3, 'joint7': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'joint1': 4500, 'joint2': 4500, 'joint3': 3500, 'joint4': 3500, 'joint5': 2000, 'joint6': 2000, 'joint7': 2000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'joint1': 450, 'joint2': 450, 'joint3': 350, 'joint4': 350, 'joint5': 200, 'joint6': 200, 'joint7': 200}
ee_links_name: list[str] = ['link_eef']

KUKA LBR iiwa 14

KUKA LBR iiwa 14 arm configuration.

Attributes

Classes

Iiwa14

Iiwa14(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/kuka_iiwa14/iiwa14.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'joint7']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 0.785398, 'joint3': 0.0, 'joint4': -1.5708, 'joint5': 0.0, 'joint6': 0.0, 'joint7': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'joint1': 2000, 'joint2': 2000, 'joint3': 2000, 'joint4': 2000, 'joint5': 2000, 'joint6': 2000, 'joint7': 2000}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'joint1': 200, 'joint2': 200, 'joint3': 200, 'joint4': 200, 'joint5': 200, 'joint6': 200, 'joint7': 200}
default_dofs_damping class-attribute instance-attribute
default_dofs_damping: dict[str, float] = {f'joint{i}': 0.5 for i in range(1, 8)}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {f'joint{i}': 300.0 for i in range(1, 8)}
ee_links_name: list[str] = ['link7']

i2RT YAM

i2RT YAM arm configuration.

Attributes

YAM_ACTION_SCALE module-attribute

YAM_ACTION_SCALE: dict[str, float] = {p: s.RAIBERT_HEURISTIC_ACTION_SCALE() for p, s in _Yam_DOFS_SPEC.items() if not p.endswith('_finger')}

Classes

Yam

Yam(**data)

Bases: RobotOptions

YAM Robot Configuration.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'manipulators/i2rt_yam/yam.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
dofs_spec class-attribute
dofs_spec: dict[str, type[ActuatorSpecOptions]] = _Yam_DOFS_SPEC
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['joint1', 'joint2', 'joint3', 'joint4', 'joint5', 'joint6', 'left_finger', 'right_finger']
links_to_keep: list[str] = ['link_left_finger', 'link_right_finger']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'joint1': 0.0, 'joint2': 1.047, 'joint3': 1.05, 'joint4': 0.0, 'joint5': 0.0, 'joint6': 0.0, 'left_finger': 0.037524, 'right_finger': -0.037524}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {**{p: s.RAIBERT_HEURISTIC_KP() for p, s in _Yam_DOFS_SPEC.items()}, 'left_finger': 5 * DamiaoDM4310.RAIBERT_HEURISTIC_KP(), 'right_finger': 5 * DamiaoDM4310.RAIBERT_HEURISTIC_KP()}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {p: s.RAIBERT_HEURISTIC_KD() for p, s in _Yam_DOFS_SPEC.items()}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {p: s.ARMATURE() for p, s in _Yam_DOFS_SPEC.items()}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {p: s.EFFORT_LIMIT for p, s in _Yam_DOFS_SPEC.items()}
ee_links_name: list[str] = ['link_6']
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'left_finger': 0.037524, 'right_finger': -0.037524}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'left_finger': 0.0, 'right_finger': 0.0}

Trossen WidowX-250

Trossen WidowX-250 arm configuration.

Attributes

Classes

WidowX250

WidowX250(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'manipulators/trossen_wx250s/wx250s.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['waist', 'shoulder', 'elbow', 'forearm_roll', 'wrist_angle', 'wrist_rotate', 'left_finger', 'right_finger']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'waist': 0.0, 'shoulder': 0.0, 'elbow': 0.0, 'forearm_roll': 0.0, 'wrist_angle': 0.0, 'wrist_rotate': 0.0, 'left_finger': 0.037, 'right_finger': -0.037}
links_to_keep: list[str] = ['gripper_link']
arm_dofs_name class-attribute
arm_dofs_name: list[str] = ['waist', 'shoulder', 'elbow', 'forearm_roll', 'wrist_angle', 'wrist_rotate']
gripper_dofs_name class-attribute
gripper_dofs_name: list[str] = ['left_finger', 'right_finger']
open_finger_pos class-attribute
open_finger_pos: float = 0.037
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'waist': 4500 * 0.8, 'shoulder': 4500 * 0.8, 'elbow': 3500 * 0.8, 'forearm_roll': 3500 * 0.8, 'wrist_angle': 2000 * 0.8, 'wrist_rotate': 2000 * 0.8, 'left_finger': 100, 'right_finger': 100}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'waist': 450, 'shoulder': 450, 'elbow': 350, 'forearm_roll': 350, 'wrist_angle': 200, 'wrist_rotate': 200, 'left_finger': 10, 'right_finger': 10}
ee_links_name: list[str] = ['gripper_link']
tcp_offset class-attribute
tcp_offset: tuple[float, float, float] = (0.0935, 0.0, 0.0)
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'left_finger': 0.037, 'right_finger': -0.037}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'left_finger': 0.0, 'right_finger': 0.0}

ALOHA (ViperX 300 S)

ALOHA (ViperX 300 S) bimanual robot configuration.

Attributes

Classes

Aloha

Aloha(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'manipulators/aloha_vx300s/aloha.urdf'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
default_root_quat class-attribute instance-attribute
default_root_quat: UnitVec4FType = (0.7071067811865476, 0.0, 0.0, 0.7071067811865476)
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['left/waist', 'left/shoulder', 'left/elbow', 'left/forearm_roll', 'left/wrist_angle', 'left/wrist_rotate', 'left/left_finger', 'left/right_finger', 'right/waist', 'right/shoulder', 'right/elbow', 'right/forearm_roll', 'right/wrist_angle', 'right/wrist_rotate', 'right/left_finger', 'right/right_finger']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'*/waist': 0.0, '*/shoulder': -1.85, '*/elbow': 1.6, '*/forearm_roll': 0.0, '*/wrist_angle': 0.0, '*/wrist_rotate': 0.0, '*/left_finger': 0.04, '*/right_finger': 0.04}
links_to_keep: list[str] = ['left/gripper_base', 'right/gripper_base']
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'*/waist': 4500 * 0.8, '*/shoulder': 4500 * 0.8, '*/elbow': 3500 * 0.8, '*/forearm_roll': 3500 * 0.8, '*/wrist_angle': 2000 * 0.8, '*/wrist_rotate': 2000 * 0.8, '*/left_finger': 100, '*/right_finger': 100}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'*/waist': 450, '*/shoulder': 450, '*/elbow': 350, '*/forearm_roll': 350, '*/wrist_angle': 200, '*/wrist_rotate': 200, '*/left_finger': 10, '*/right_finger': 10}
ee_links_name: list[str] = ['left/gripper_base', 'right/gripper_base']
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'left/left_finger': 0.04, 'left/right_finger': 0.04, 'right/left_finger': 0.04, 'right/right_finger': 0.04}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'left/left_finger': 0.005, 'left/right_finger': 0.005, 'right/left_finger': 0.005, 'right/right_finger': 0.005}

OmniReset UR5e rig

UWLab's OmniReset rig: a calibrated UR5e carrying a Robotiq 2F-85 with a D415 mount.

Converted once from UWLab's USD (UW-Lab/uwlab-assets), which Genesis cannot load: the gripper's parallel linkage is a PhysX articulation with two parents per inner knuckle, coupled by physxMimicJoint constraints. The MJCF rebuilds it as a tree. Each body is placed from its PhysX joint frames at q = 0, so every joint reads PhysX's coordinate; the five mimic joints become joint equalities and the two loop-closing joints are dropped. One authored gearing is corrected: right_inner_knuckle_joint says -1, but its hinge is flipped relative to its mirror, which opens a 72 mm gap in the right finger's loop when closed; +1 closes it to 0.08 mm, and it matches Isaac Lab's recorded states, which read that joint as -finger_joint. Kinematics match UWLab's calibrated analytic model (uwlab_assets.robots.ur5e_robotiq_gripper.kinematics) to 1e-7 m; joint values are PhysX's, so a pose reads what Isaac Lab reports for it.

One asset rather than a ManipulatorOptions composite of :class:UR5e and :class:Robotiq2f85, because neither part is the same robot: UR5e carries rounded nominal link lengths, putting its flange 1.2 mm (max 2.3 mm) from this calibrated one across random poses — the size of OmniReset's 2.5 mm success tolerance — and Robotiq2f85 is MuJoCo Menagerie's loop-closed 2F-85 (other joints, pad-only colliders, sub-millisecond substeps, no D415 mount), not the mimic linkage whose six joint angles the policy observes.

Inertials are PhysX's. The arm links' are authored in the USD; the gripper links have none, so the ones here are what Isaac Sim 6.1 derives for UWLab's robot (measured), including 1 kg on each collider-less outer knuckle. With them the arm's mass matrix matches PhysX's to 0.01 %, and UWLab's 120 Hz explicit controller is stable.

The dynamics are UWLab's implicit-actuator training/eval configuration (IMPLICIT_UR5E_ROBOTIQ_2F85): the arm is torque-driven by an operational-space controller (kp = kd = 0 here; efforts capped at 150/28 N·m), the gripper's finger_joint is a PD servo (kp 17, kd 5, 60 N·m) and the five passive linkage joints follow it through joint equalities. Every link floats (disable_gravity in the USD), which gravity compensation reproduces, and no DOF carries armature or friction — the USD authors none and the stage-1 policy never had the sysid values the finetune stage randomizes.

Attributes

ARM_DOFS module-attribute

ARM_DOFS = ('shoulder_pan_joint', 'shoulder_lift_joint', 'elbow_joint', 'wrist_1_joint', 'wrist_2_joint', 'wrist_3_joint')

GRIPPER_DRIVER module-attribute

GRIPPER_DRIVER = 'finger_joint'

Classes

OmniResetUR5e2f85

OmniResetUR5e2f85(**data)

Bases: RobotOptions

UR5e + Robotiq 2F-85 exactly as UWLab's OmniReset policies were trained on it.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'arms/omnireset_ur5e_2f85/omnireset_ur5e_2f85.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
dofs_name class-attribute instance-attribute
dofs_name: list[str] = [*ARM_DOFS, GRIPPER_DRIVER]
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'shoulder_pan_joint': 0.0, 'shoulder_lift_joint': -1.5708, 'elbow_joint': 1.5708, 'wrist_1_joint': -1.5708, 'wrist_2_joint': -1.5708, 'wrist_3_joint': -1.5708, GRIPPER_DRIVER: 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {**dict.fromkeys(ARM_DOFS, 0.0), GRIPPER_DRIVER: 17.0}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {**dict.fromkeys(ARM_DOFS, 0.0), GRIPPER_DRIVER: 5.0}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {'shoulder_pan_joint': 150.0, 'shoulder_lift_joint': 150.0, 'elbow_joint': 150.0, 'wrist_1_joint': 28.0, 'wrist_2_joint': 28.0, 'wrist_3_joint': 28.0, GRIPPER_DRIVER: 60.0}
default_armature class-attribute instance-attribute
default_armature: float = 0.0
material class-attribute instance-attribute
material: RigidMaterialOptions = RigidMaterialOptions(gravity_compensation=1.0)
ee_links_name: list[str] = ['wrist_3_link']

LIBERO Panda

LIBERO Panda and mounted-rig options.

Attributes

LIBERO_MOUNT_ENTITY_NAME module-attribute

LIBERO_MOUNT_ENTITY_NAME = 'mount'

LIBERO_ROBOT_ENTITY_NAME module-attribute

LIBERO_ROBOT_ENTITY_NAME = 'robot'

Classes

LiberoPanda

LiberoPanda(**data)

Bases: FrankaEmikaPanda

Panda dynamics used by every LIBERO family.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm_dofs_name class-attribute
arm_dofs_name: tuple[str, ...] = tuple(f'joint{index}' for index in range(1, 8))
gripper_dofs_name class-attribute
gripper_dofs_name: tuple[str, ...] = ('finger_joint1', 'finger_joint2')
grip_site_pos_local class-attribute
grip_site_pos_local: tuple[float, float, float] = (0.0, 0.0, 0.0965)
grip_site_quat_local class-attribute
grip_site_quat_local: tuple[float, float, float, float] = (0.7071067811865475, 0.0, 0.0, -0.7071067811865475)
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = Field(default_factory=lambda: {'joint1': 0.0, 'joint2': -0.161037389, 'joint3': 0.0, 'joint4': -2.44459747, 'joint5': 0.0, 'joint6': 2.2267522, 'joint7': 0.7853981633974483})
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = Field(default_factory=lambda: {'finger_joint1': 1000.0, 'finger_joint2': 1000.0})
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = Field(default_factory=lambda: {'finger_joint1': 0.0, 'finger_joint2': 0.0})
default_dofs_damping class-attribute instance-attribute
default_dofs_damping: dict[str, float] = Field(default_factory=lambda: {'joint1': 0.1, 'joint2': 0.1, 'joint3': 0.1, 'joint4': 0.1, 'joint5': 0.1, 'joint6': 0.01, 'joint7': 0.01, 'finger_joint1': 100.0, 'finger_joint2': 100.0})
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = Field(default_factory=lambda: {name: 5.0 / index for index, name in enumerate(LiberoPanda.arm_dofs_name, start=1)} | {'finger_joint1': 1.0, 'finger_joint2': 1.0})
default_dofs_frictionloss class-attribute instance-attribute
default_dofs_frictionloss: dict[str, float] = Field(default_factory=lambda: {name: 0.1 for name in LiberoPanda.arm_dofs_name} | {'finger_joint1': 1.0, 'finger_joint2': 1.0})
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = Field(default_factory=lambda: {'finger_joint1': 20.0, 'finger_joint2': 20.0})
material class-attribute instance-attribute
material: RigidMaterialOptions = Field(default_factory=lambda: RigidMaterialOptions(gravity_compensation=1.0))

LiberoMountedPanda

LiberoMountedPanda(**data)

Bases: CompositeEntityOptions

Rethink mount and Panda represented as one validated welded rig.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

mount class-attribute instance-attribute
mount: RethinkMount = Field(default_factory=lambda: RethinkMount(material=RigidMaterialOptions(gravity_compensation=1.0)))
robot class-attribute instance-attribute
robot: LiberoPanda = Field(default_factory=lambda: LiberoPanda(default_root_pos=(0.0, 0.0, -0.01), is_fixed_base=False))

Methods:

entity_parts
entity_parts() -> list[tuple[str, BaseEntityOptions]]
Source code in eden/options/robots/libero.py
def entity_parts(self) -> list[tuple[str, BaseEntityOptions]]:
    return [(LIBERO_MOUNT_ENTITY_NAME, self.mount), (LIBERO_ROBOT_ENTITY_NAME, self.robot)]
attachments
attachments() -> dict[str, tuple[str, str]]
Source code in eden/options/robots/libero.py
def attachments(self) -> dict[str, tuple[str, str]]:
    return {
        LIBERO_ROBOT_ENTITY_NAME: (
            LIBERO_MOUNT_ENTITY_NAME,
            "base",
        )
    }

Grippers

Franka Hand

Franka Hand parallel-jaw gripper configuration.

Attributes

Classes

FrankaHand

FrankaHand(**data)

Bases: RobotOptions

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'grippers/franka_hand/franka_hand.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
links_to_keep: list[str] = ['hand', 'left_gripper_pad', 'right_gripper_pad']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['finger_joint1', 'finger_joint2']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'finger_joint1': 0.04, 'finger_joint2': 0.04}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'finger_joint1': 100.0, 'finger_joint2': 100.0}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'finger_joint1': 10.0, 'finger_joint2': 10.0}
ee_links_name: list[str] = ['hand']
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'finger_joint1': 0.04, 'finger_joint2': 0.04}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'finger_joint1': 0.0, 'finger_joint2': 0.0}
tcp_offset class-attribute
tcp_offset: tuple[float, float, float] = (0.0, 0.0, 0.1034)
stroke class-attribute
stroke: float = 0.08

Robotiq 2F-85

Robotiq 2F-85 parallel-jaw gripper configuration.

Attributes

Classes

Robotiq2f85

Robotiq2f85(**data)

Bases: RobotOptions

Robotiq 2F85 parallel-jaw gripper, tuned for stable grasping.

The 2F85 is an underactuated four-bar linkage: MuJoCo closes it with <equality><connect> constraints and drives it through a <tendon> actuator. Genesis has no runtime tendon (the actuator is approximated per driver joint at parse time) and solves the loop closures softly, so the stock asset penetrates/oscillates instead of pinching. These options bake in the validated compensation (tuned by SpaceMouse-teleop A/B against the loop-free Franka Hand; pinned by the grasp-and-lift regression in tests/options/robots/test_robotiq_2f85.py):

  • Pads: flat box collision geoms with high friction (a face contact that resists rotation), instead of the original 5-sphere cluster. Pair with collision_link_patterns=[".*pad"] so only the pads collide and the object isn't wedged against the inner linkage.
  • Drive (default_dofs_force_limits): the kp=100 servo commanded to the closed target would develop ~40 N·m and crush through a small object; the cap reproduces MuJoCo's tendon forcerange -5 5 × wrap coef 0.485 = 2.4 N·m per driver.
  • Loop closure (default_equalities_sol_params): the connects + driver coupling are stiffened to near-rigid so grip force transmits through the four-bar to the pads instead of flexing it.
  • Contact (default_geoms_sol_params): pad contact stiffened to match. Genesis mixes a pair's params as the mean of its two geoms (geom priority is ignored), so graspable objects should carry comparable sol params via their own default_geoms_sol_params — never via a global EnvOptions.constraint_timeconst, which over-stiffens every rigid contact and ejects mesh objects on contact.
  • Spring preload (default_dofs_spring_ref): Genesis drops MJCF springref; re-anchoring the spring-link springs at 2.62 rad restores the preload that keeps the passive pad chain taut.
  • Dissipation (damping/frictionloss/armature): at contact the drivers are torque-saturated, so kp/kd have no authority over contact chatter; always-on joint damping (~critical for the pinch mode) and a dry frictionloss stick band absorb it instead.

Full constraint stiffness needs an effective substep dt <= 0.001 (e.g. sim_dt=0.004, sim_substeps=4); Genesis clamps solref timeconst to 2*substep_dt otherwise.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'grippers/robotiq_2f85/2f85.xml'
is_fixed_base class-attribute instance-attribute
is_fixed_base: bool = True
links_to_keep: list[str] = ['gripper_base_link']
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['left_driver_joint', 'left_spring_link_joint', 'left_follower', 'right_driver_joint', 'right_spring_link_joint', 'right_follower_joint']
default_dofs_pos class-attribute instance-attribute
default_dofs_pos: dict[str, float] = {'left_driver_joint': 0.0, 'left_spring_link_joint': 0.0, 'left_follower': 0.0, 'right_driver_joint': 0.0, 'right_spring_link_joint': 0.0, 'right_follower_joint': 0.0}
default_dofs_kp class-attribute instance-attribute
default_dofs_kp: dict[str, float] = {'left_driver_joint': 100.0, 'right_driver_joint': 100.0}
default_dofs_kd class-attribute instance-attribute
default_dofs_kd: dict[str, float] = {'left_driver_joint': 10.0, 'right_driver_joint': 10.0}
default_dofs_force_limits class-attribute instance-attribute
default_dofs_force_limits: dict[str, float] = {'left_driver_joint': 2.4, 'right_driver_joint': 2.4}
default_dofs_damping class-attribute instance-attribute
default_dofs_damping: dict[str, float] = {'.*_driver_joint': 1.0, '.*_spring_link_joint': 0.05, 'left_follower': 0.05, 'right_follower_joint': 0.05}
default_dofs_frictionloss class-attribute instance-attribute
default_dofs_frictionloss: dict[str, float] = {'.*_driver_joint': 0.1, '.*_spring_link_joint': 0.01, 'left_follower': 0.01, 'right_follower_joint': 0.01}
default_dofs_armature class-attribute instance-attribute
default_dofs_armature: dict[str, float] = {'.*_spring_link_joint': 0.002, 'left_follower': 0.002, 'right_follower_joint': 0.002}
default_dofs_spring_ref class-attribute instance-attribute
default_dofs_spring_ref: dict[str, float] = {'.*_spring_link_joint': 2.62}
default_equalities_sol_params class-attribute instance-attribute
default_equalities_sol_params: SolParamsType | None = _STIFF_SOL_PARAMS
default_geoms_sol_params class-attribute instance-attribute
default_geoms_sol_params: dict[str, SolParamsType] = {'.*pad': _STIFF_SOL_PARAMS}
actuated_dofs_name class-attribute
actuated_dofs_name: list[str] = ['left_driver_joint', 'right_driver_joint']
open_dofs_pos class-attribute
open_dofs_pos: dict[str, float] = {'left_driver_joint': 0.0, 'right_driver_joint': 0.0}
close_dofs_pos class-attribute
close_dofs_pos: dict[str, float] = {'left_driver_joint': 0.8, 'right_driver_joint': 0.8}
tcp_offset class-attribute
tcp_offset: tuple[float, float, float] = (0.0, 0.0, 0.1413)
stroke class-attribute
stroke: float = 0.085
max_substep_dt class-attribute
max_substep_dt: float = 0.001

Dexterous hands

RobotEra XHand1

RobotEra XHand1 dexterous hand configurations (left/right).

Attributes

Classes

XHand1_R

XHand1_R(**data)

Bases: _XHand1

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/robotera_xhand/xhand1_right.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['right_hand_thumb_bend_joint', 'right_hand_thumb_rota_joint1', 'right_hand_thumb_rota_joint2', 'right_hand_index_bend_joint', 'right_hand_index_joint1', 'right_hand_index_joint2', 'right_hand_mid_joint1', 'right_hand_mid_joint2', 'right_hand_ring_joint1', 'right_hand_ring_joint2', 'right_hand_pinky_joint1', 'right_hand_pinky_joint2']
links_to_keep: list[str] = ['right_hand_thumb_rota_tip', 'right_hand_index_rota_tip', 'right_hand_mid_tip', 'right_hand_ring_tip', 'right_hand_pinky_tip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right', target_hand_links=['right_hand_thumb_rota_tip', 'right_hand_index_rota_tip', 'right_hand_mid_tip', 'right_hand_ring_tip', 'right_hand_pinky_tip'], mano_hand_idx=[16, 17, 18, 19, 20])

XHand1_L

XHand1_L(**data)

Bases: _XHand1

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/robotera_xhand/xhand1_left.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['left_hand_thumb_bend_joint', 'left_hand_thumb_rota_joint1', 'left_hand_thumb_rota_joint2', 'left_hand_index_bend_joint', 'left_hand_index_joint1', 'left_hand_index_joint2', 'left_hand_mid_joint1', 'left_hand_mid_joint2', 'left_hand_ring_joint1', 'left_hand_ring_joint2', 'left_hand_pinky_joint1', 'left_hand_pinky_joint2']
links_to_keep: list[str] = ['left_hand_thumb_rota_tip', 'left_hand_index_rota_tip', 'left_hand_mid_tip', 'left_hand_ring_tip', 'left_hand_pinky_tip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left', target_hand_links=['left_hand_thumb_rota_tip', 'left_hand_index_rota_tip', 'left_hand_mid_tip', 'left_hand_ring_tip', 'left_hand_pinky_tip'], mano_hand_idx=[16, 17, 18, 19, 20])

Inspire RH56

Inspire RH56 dexterous hands (left/right): the stock RH56 and the RH56DFQ variant Unitree mounts on the G1.

Both are the same hand: link tree, joint frames and axes, and every link's inertial (0.1918 kg in all) are identical. What differs is the asset each is published with, and so the joint ranges, the coupling ratios and the meshes:

========================== ================================== ========================================= asset InspireRH56_* InspireRH56DFQ_* (Weave's G1 rig) ========================== ================================== ========================================= finger proximal range 0 – 1.47 rad 0 – 1.7 rad thumb distal range 0 – 0.4 rad 0 – 1.2 rad couplings URDF <mimic> (Genesis equality) software, via mimic_dofs (no tags) thumb int. / distal ratio 1.334 / 0.667 1.6 / 2.4 finger intermediate ratio 1.06399, offset -0.04545 1.0 ========================== ================================== =========================================

Six of the twelve joints are mimic followers of the six drivers (actuated_dofs_name). metadata.mimic_dofs publishes the couplings as follower -> (driver, multiplier, offset) for both. The RH56 asset keeps its <mimic> tags, so Genesis couples its followers itself and the mapping is descriptive; the DFQ asset has them stripped, and a task passes the mapping to the joint PD action term, which then commands each follower at driver_target * multiplier + offset (Weave's MimicJointPositionAction)::

ImplicitPDController.configure(entity_name="left_hand", dofs_name=InspireRH56DFQ_L.actuated_dofs_name,
                               mimic_dofs=InspireRH56DFQ_L.metadata.mimic_dofs)

Attributes

INSPIRE_DOFS_NAME module-attribute

INSPIRE_DOFS_NAME: list[str] = ['thumb_proximal_yaw_joint', 'thumb_proximal_pitch_joint', 'thumb_intermediate_joint', 'thumb_distal_joint', 'index_proximal_joint', 'index_intermediate_joint', 'middle_proximal_joint', 'middle_intermediate_joint', 'ring_proximal_joint', 'ring_intermediate_joint', 'pinky_proximal_joint', 'pinky_intermediate_joint']

INSPIRE_ACTUATED_DOFS_NAME module-attribute

INSPIRE_ACTUATED_DOFS_NAME: list[str] = ['thumb_proximal_yaw_joint', 'thumb_proximal_pitch_joint', 'index_proximal_joint', 'middle_proximal_joint', 'ring_proximal_joint', 'pinky_proximal_joint']
INSPIRE_FINGERTIP_LINKS_NAME: list[str] = ['thumb_distal', 'index_intermediate', 'middle_intermediate', 'ring_intermediate', 'pinky_intermediate']

Classes

InspireRH56_R

InspireRH56_R(**data)

Bases: _InspireRH56

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/inspire_hand/inspire_hand_right.urdf'
metadata class-attribute
metadata: MetadataOptions = _metadata('right', _RH56_MIMIC_DOFS)

InspireRH56_L

InspireRH56_L(**data)

Bases: _InspireRH56

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/inspire_hand/inspire_hand_left.urdf'
metadata class-attribute
metadata: MetadataOptions = _metadata('left', _RH56_MIMIC_DOFS)

InspireRH56DFQ_L

InspireRH56DFQ_L(**data)

Bases: _InspireRH56DFQ

Left Inspire RH56DFQ hand.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/inspire_hand/inspire_hand_dfq_left.urdf'
metadata class-attribute
metadata: MetadataOptions = _metadata('left', _DFQ_MIMIC_DOFS)

InspireRH56DFQ_R

InspireRH56DFQ_R(**data)

Bases: _InspireRH56DFQ

Right Inspire RH56DFQ hand.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/inspire_hand/inspire_hand_dfq_right.urdf'
metadata class-attribute
metadata: MetadataOptions = _metadata('right', _DFQ_MIMIC_DOFS)

LEAP Hand

LEAP hand configurations (left/right).

Attributes

Classes

LeapHand_R

LeapHand_R(**data)

Bases: _LeapHand

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/leap_hand/leap_hand_right.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']
links_to_keep: list[str] = ['thumb_tip_head', 'index_tip_head', 'middle_tip_head', 'ring_tip_head']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right', target_hand_links=['thumb_tip_head', 'index_tip_head', 'middle_tip_head', 'ring_tip_head'], mano_hand_idx=[16, 17, 18, 19])

LeapHand_L

LeapHand_L(**data)

Bases: _LeapHand

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/leap_hand/leap_hand_left.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15']
links_to_keep: list[str] = ['thumb_tip_head', 'index_tip_head', 'middle_tip_head', 'ring_tip_head']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left', target_hand_links=['thumb_tip_head', 'index_tip_head', 'middle_tip_head', 'ring_tip_head'], mano_hand_idx=[16, 17, 18, 19])

SharPa Wave

Sharpa Wave dexterous hand configurations (left/right).

Attributes

Classes

SharpaWave_R

SharpaWave_R(**data)

Bases: _SharpaWave

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/sharpa_hand/right_sharpa_ha4_v2_1.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['right_thumb_CMC_FE', 'right_thumb_CMC_AA', 'right_thumb_MCP_FE', 'right_thumb_MCP_AA', 'right_thumb_IP', 'right_index_MCP_FE', 'right_index_MCP_AA', 'right_index_PIP', 'right_index_DIP', 'right_middle_MCP_FE', 'right_middle_MCP_AA', 'right_middle_PIP', 'right_middle_DIP', 'right_ring_MCP_FE', 'right_ring_MCP_AA', 'right_ring_PIP', 'right_ring_DIP', 'right_pinky_CMC', 'right_pinky_MCP_FE', 'right_pinky_MCP_AA', 'right_pinky_PIP', 'right_pinky_DIP']
links_to_keep: list[str] = ['right_thumb_fingertip', 'right_index_fingertip', 'right_middle_fingertip', 'right_ring_fingertip', 'right_pinky_fingertip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right', target_hand_links=['right_thumb_fingertip', 'right_index_fingertip', 'right_middle_fingertip', 'right_ring_fingertip', 'right_pinky_fingertip'], mano_hand_idx=[16, 17, 18, 19, 20])

SharpaWave_L

SharpaWave_L(**data)

Bases: _SharpaWave

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/sharpa_hand/left_sharpa_ha4_v2_1.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['left_thumb_CMC_FE', 'left_thumb_CMC_AA', 'left_thumb_MCP_FE', 'left_thumb_MCP_AA', 'left_thumb_IP', 'left_index_MCP_FE', 'left_index_MCP_AA', 'left_index_PIP', 'left_index_DIP', 'left_middle_MCP_FE', 'left_middle_MCP_AA', 'left_middle_PIP', 'left_middle_DIP', 'left_ring_MCP_FE', 'left_ring_MCP_AA', 'left_ring_PIP', 'left_ring_DIP', 'left_pinky_CMC', 'left_pinky_MCP_FE', 'left_pinky_MCP_AA', 'left_pinky_PIP', 'left_pinky_DIP']
links_to_keep: list[str] = ['left_thumb_fingertip', 'left_index_fingertip', 'left_middle_fingertip', 'left_ring_fingertip', 'left_pinky_fingertip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left', target_hand_links=['left_thumb_fingertip', 'left_index_fingertip', 'left_middle_fingertip', 'left_ring_fingertip', 'left_pinky_fingertip'], mano_hand_idx=[16, 17, 18, 19, 20])

Tesollo DG-5F

Tesollo DG-5F dexterous hand configurations (left/right).

Attributes

Classes

TesolloDG5F_R

TesolloDG5F_R(**data)

Bases: _TesolloDG5F

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/tesollo_hand/dg5f_right.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['rj_dg_1_1', 'rj_dg_1_2', 'rj_dg_1_3', 'rj_dg_1_4', 'rj_dg_2_1', 'rj_dg_2_2', 'rj_dg_2_3', 'rj_dg_2_4', 'rj_dg_3_1', 'rj_dg_3_2', 'rj_dg_3_3', 'rj_dg_3_4', 'rj_dg_4_1', 'rj_dg_4_2', 'rj_dg_4_3', 'rj_dg_4_4', 'rj_dg_5_1', 'rj_dg_5_2', 'rj_dg_5_3', 'rj_dg_5_4']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right')

TesolloDG5F_L

TesolloDG5F_L(**data)

Bases: _TesolloDG5F

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/tesollo_hand/dg5f_left.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['lj_dg_1_1', 'lj_dg_1_2', 'lj_dg_1_3', 'lj_dg_1_4', 'lj_dg_2_1', 'lj_dg_2_2', 'lj_dg_2_3', 'lj_dg_2_4', 'lj_dg_3_1', 'lj_dg_3_2', 'lj_dg_3_3', 'lj_dg_3_4', 'lj_dg_4_1', 'lj_dg_4_2', 'lj_dg_4_3', 'lj_dg_4_4', 'lj_dg_5_1', 'lj_dg_5_2', 'lj_dg_5_3', 'lj_dg_5_4']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left')

Wuji Hand 1

Wuji Hand 1 dexterous hand configurations (left/right, full-mesh or primitive collision).

Twenty-DOF anthropomorphic hand from Wuji Technology (5 fingers x 4 DOF) with a silicone pad on the thumb -- the hand/body-with-soft package, i.e. the hand the upstream in-hand cube-reorientation RL task (wuji-technology/wuji-mjlab) is built around. The successor generation is :mod:eden.options.robots.wuji_hand2beta2; the two are different hands, not variants (different link names, joint names and kinematics).

Two collision flavours per side:

  • WujiHand1_{R,L} -- one full visual mesh per link, reused as the collision mesh. Genesis convexifies each, so the palm becomes a single convex hull that bulges over the finger sockets. Fine for visualization, retargeting and teleop.
  • WujiHand1Simplified_{R,L} -- primitive collision (cylinder + end spheres on the proximal links, simplified meshes for the palm, the thumb pad and the five distal links). This reproduces the collision model the upstream MJCF -- and therefore the upstream RL task -- was tuned against, and is much cheaper at thousands of envs. Use this flavour for contact-rich manipulation.

Assets (URDF + STL meshes, MIT-licensed) live under hands/wuji_hand1 in the Kashu7100/eden_assets dataset, converted from the upstream wuji-technology/wuji-description hand/body-with-soft package.

Not to be confused with the older, unreferenced hands/wuji_hand directory in the same dataset: that is the upstream hand/body package at v1.0.1 -- same kinematics, but no thumb pad, no simplified-collision flavour, unprefixed link/joint names, and looser joint limits and effort ceilings. Nothing in Eden loads it.

Attributes

Classes

WujiHand1_R

WujiHand1_R(**data)

Bases: _WujiHand1

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand1/wuji_hand1_right.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = _sided(_DOFS_NAME_TEMPLATE, 'right')
links_to_keep: list[str] = _sided(_TIP_LINKS_TEMPLATE, 'right') + ['right_finger1_link2_softbody']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right', target_hand_links=_sided(_TIP_LINKS_TEMPLATE, 'right'), mano_hand_idx=[16, 17, 18, 19, 20])

WujiHand1_L

WujiHand1_L(**data)

Bases: _WujiHand1

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand1/wuji_hand1_left.urdf'
dofs_name class-attribute instance-attribute
dofs_name: list[str] = _sided(_DOFS_NAME_TEMPLATE, 'left')
links_to_keep: list[str] = _sided(_TIP_LINKS_TEMPLATE, 'left') + ['left_finger1_link2_softbody']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left', target_hand_links=_sided(_TIP_LINKS_TEMPLATE, 'left'), mano_hand_idx=[16, 17, 18, 19, 20])

WujiHand1Simplified_R

WujiHand1Simplified_R(**data)

Bases: WujiHand1_R

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand1/wuji_hand1_right_simplified.urdf'

WujiHand1Simplified_L

WujiHand1Simplified_L(**data)

Bases: WujiHand1_L

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand1/wuji_hand1_left_simplified.urdf'

Wuji Hand2 (beta2)

Wuji Hand2 (beta2) dexterous hand configurations (left/right, with or without the arm mount).

Twenty-DOF anthropomorphic hand from Wuji Technology (5 fingers x 4 DOF), in two flavours per side: the bare hand (rooted at the wrist) and the WithMount variant that carries the arm-flange adapter plate as its root link. Assets (URDF + STL meshes, MIT-licensed) live under hands/wuji_hand2beta2 in the Kashu7100/eden_assets dataset, converted from the upstream wuji-technology/wuji-description hand2/hand2_beta2 package.

Attributes

Classes

WujiHand2Beta2_R

WujiHand2Beta2_R(**data)

Bases: _WujiHand2Beta2

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand2beta2/wuji_hand2beta2_right.urdf'
up class-attribute instance-attribute
up: tuple[int, int, int] = (0, 0, -1)
front class-attribute instance-attribute
front: tuple[int, int, int] = (0, 1, 0)
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['r_thumb_cmc_flex', 'r_thumb_cmc_abd', 'r_thumb_mcp', 'r_thumb_ip', 'r_index_finger_mcp_flex', 'r_index_finger_mcp_abd', 'r_index_finger_pip', 'r_index_finger_dip', 'r_middle_finger_mcp_flex', 'r_middle_finger_mcp_abd', 'r_middle_finger_pip', 'r_middle_finger_dip', 'r_ring_finger_mcp_flex', 'r_ring_finger_mcp_abd', 'r_ring_finger_pip', 'r_ring_finger_dip', 'r_pinky_mcp_flex', 'r_pinky_mcp_abd', 'r_pinky_pip', 'r_pinky_dip']
links_to_keep: list[str] = ['r_thumb_tip', 'r_index_finger_tip', 'r_middle_finger_tip', 'r_ring_finger_tip', 'r_pinky_tip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right', target_hand_links=['r_thumb_tip', 'r_index_finger_tip', 'r_middle_finger_tip', 'r_ring_finger_tip', 'r_pinky_tip'], mano_hand_idx=[16, 17, 18, 19, 20])

WujiHand2Beta2_L

WujiHand2Beta2_L(**data)

Bases: _WujiHand2Beta2

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand2beta2/wuji_hand2beta2_left.urdf'
up class-attribute instance-attribute
up: tuple[int, int, int] = (0, 0, -1)
front class-attribute instance-attribute
front: tuple[int, int, int] = (0, -1, 0)
dofs_name class-attribute instance-attribute
dofs_name: list[str] = ['l_thumb_cmc_flex', 'l_thumb_cmc_abd', 'l_thumb_mcp', 'l_thumb_ip', 'l_index_finger_mcp_flex', 'l_index_finger_mcp_abd', 'l_index_finger_pip', 'l_index_finger_dip', 'l_middle_finger_mcp_flex', 'l_middle_finger_mcp_abd', 'l_middle_finger_pip', 'l_middle_finger_dip', 'l_ring_finger_mcp_flex', 'l_ring_finger_mcp_abd', 'l_ring_finger_pip', 'l_ring_finger_dip', 'l_pinky_mcp_flex', 'l_pinky_mcp_abd', 'l_pinky_pip', 'l_pinky_dip']
links_to_keep: list[str] = ['l_thumb_tip', 'l_index_finger_tip', 'l_middle_finger_tip', 'l_ring_finger_tip', 'l_pinky_tip']
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left', target_hand_links=['l_thumb_tip', 'l_index_finger_tip', 'l_middle_finger_tip', 'l_ring_finger_tip', 'l_pinky_tip'], mano_hand_idx=[16, 17, 18, 19, 20])

WujiHand2Beta2WithMount_R

WujiHand2Beta2WithMount_R(**data)

Bases: WujiHand2Beta2_R

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand2beta2/wuji_hand2beta2_right_with_mount.urdf'
links_to_keep: list[str] = ['r_wrist', 'r_thumb_tip', 'r_index_finger_tip', 'r_middle_finger_tip', 'r_ring_finger_tip', 'r_pinky_tip']

WujiHand2Beta2WithMount_L

WujiHand2Beta2WithMount_L(**data)

Bases: WujiHand2Beta2_L

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/wuji_hand2beta2/wuji_hand2beta2_left_with_mount.urdf'
links_to_keep: list[str] = ['l_wrist', 'l_thumb_tip', 'l_index_finger_tip', 'l_middle_finger_tip', 'l_ring_finger_tip', 'l_pinky_tip']

MANO hand model

MANO hand model configurations (left/right).

Attributes

Classes

ManoHand_R

ManoHand_R(**data)

Bases: _ManoHand

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/mano_hand/mano_right.urdf'
up class-attribute instance-attribute
up: tuple[int, int, int] = (-1, 0, 0)
front class-attribute instance-attribute
front: tuple[int, int, int] = (0, -1, 0)
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='right')

ManoHand_L

ManoHand_L(**data)

Bases: _ManoHand

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

file class-attribute instance-attribute
file: str = 'hands/mano_hand/mano_left.urdf'
up class-attribute instance-attribute
up: tuple[int, int, int] = (-1, 0, 0)
front class-attribute instance-attribute
front: tuple[int, int, int] = (0, 1, 0)
metadata class-attribute
metadata: MetadataOptions = MetadataOptions(side='left')

Composite robots

A composite is a CompositeEntityOptions: it round-trips through the config system and is added to a scene by name like any other entity, and the scene expands it into its unit robots plus the welds that join them. It exposes the controller metadata as properties (arm_dofs on every arm rig, plus gripper_dofs, gripper_open, gripper_close, ... on a ManipulatorOptions and hand_dofs on a DexManipulatorOptions; robot_dofs, left_hand_dofs, right_hand_dofs on a humanoid rig). ManipulatorOptions carries a two-finger gripper, DexManipulatorOptions a dexterous hand, and HumanoidOptions one end-effector per wrist.

Composite base classes

Base options for composite robots (an arm + an attached gripper, and kin).

A composite robot bundles several unit-robot :class:RobotOptions into one rig that a scene expands into its constituent entities + welds. Unlike the unit robots in eden/options/robots, a composite is itself a validated Options (a :class:~eden.options.entities.CompositeEntityOptions), so it round-trips through the config system, can declare its own fields, and is added to a scene by name like any other entity::

from eden.options.robots import Droid

droid = Droid(base_pos=(0.0, 0.0, 0.4))
scene = en.scenes.Empty(is_support_enabled=True, robot=droid)   # -> robot (arm) + gripper + weld
# ...the same object exposes the controller metadata as shortcut properties:
DifferentialIKController.configure(entity_name=droid.arm_name,
                                   ee_link_name=droid.ee_link,
                                   dofs_name=droid.arm_dofs)

Two end-effector cases share one arm base (:class:_ArmCompositeOptions): :class:ManipulatorOptions bolts on a two-finger gripper (the manipulator); :class:DexManipulatorOptions bolts on a multi-DOF dexterous hand. Concrete rigs (Franka + Robotiq, UR5e, Franka + XHand, …) subclass one of these with default fields and register in :data:COMPOSITE_ROBOT_REGISTRY. A humanoid carrying an end-effector per wrist is the third shape, :class:HumanoidOptions — a sibling of the arm base rather than a subclass, because its root is a humanoid with two welds instead of an arm with one (see :mod:eden.options.robots.composite.oli_wuji). mjlab attaches the end-effector to each arm at a tool frame via with_gripper; Eden's equivalent is the scene attachments_dict, which the composite fills in from :meth:expand.

Attributes

COMPOSITE_ROBOT_REGISTRY module-attribute

COMPOSITE_ROBOT_REGISTRY = Registry('composite_robot')

Classes

ManipulatorOptions

ManipulatorOptions(**data)

Bases: _ArmCompositeOptions

An arm + an attached two-finger gripper, added to a scene as one rig.

The gripper is welded to arm's ee_link (see :meth:attachments). The gripper's driven DOFs and open/close targets are exposed as shortcut properties over the sub-option, so they can't drift from the robot definition.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

gripper instance-attribute
gripper: RobotOptions
gripper_name class-attribute instance-attribute
gripper_name: str = 'gripper'
gripper_dofs property
gripper_dofs: list[str]

Gripper DOFs the binary controller drives (== keys of gripper.open_dofs_pos).

gripper_open property
gripper_open: dict[str, float]
gripper_close property
gripper_close: dict[str, float]
stroke property
stroke: float

Pad-to-pad opening (m) at gripper_open (== gripper.stroke).

tcp_offset property
tcp_offset: tuple[float, float, float]

The tool point — pad midpoint with the gripper open — in the ee_link frame (m).

The gripper's own tcp_offset (declared in its base-link frame) carried through the mounting pose the weld applies, so a rig with an attach_offset / attach_quat reports the point where the pads actually are. The config-time twin of the grasp frame the robosuite terms measure on the built rig.

max_substep_dt property
max_substep_dt: float | None

Finest substep either half of the rig needs (None when neither declares one).

A rig is only as stiff as its stiffest constraint allows: welding a Robotiq onto an arm that imposes nothing still means the rig cannot be stepped above the Robotiq's limit. Read it when choosing EnvOptions.sim_dt / sim_substeps for a scene that holds the rig.

Methods:

hold_width
hold_width(width: float, clearance: float = 0.002) -> dict[str, float]

Finger DOF targets that put the pads width + 2 * clearance apart.

Linear between gripper_open (pads :attr:stroke apart) and gripper_close (pads touching), clamped to that range — so a Franka Hand holding a 4 cm peg gets each prismatic finger at ~2.2 cm, and a Robotiq the matching driver angle. Meant for objects welded to the hand; a free object is grasped by the gripper action instead.

Source code in eden/options/robots/composite/base.py
def hold_width(self, width: float, clearance: float = 0.002) -> dict[str, float]:
    """Finger DOF targets that put the pads ``width + 2 * clearance`` apart.

    Linear between ``gripper_open`` (pads :attr:`stroke` apart) and ``gripper_close`` (pads
    touching), clamped to that range — so a Franka Hand holding a 4 cm peg gets each prismatic
    finger at ~2.2 cm, and a Robotiq the matching driver angle. Meant for objects welded to the
    hand; a free object is grasped by the gripper action instead.
    """
    closed = min(max(1.0 - (width + 2.0 * clearance) / self.stroke, 0.0), 1.0)
    return {
        dof: self.gripper_open[dof] + closed * (self.gripper_close[dof] - self.gripper_open[dof])
        for dof in self.gripper_dofs
    }
preset_hold
preset_hold(width: float, clearance: float = 0.002) -> None

Start the gripper closed on an object width m wide: writes :meth:hold_width into its defaults.

Unlike :meth:hold_width, which clamps because it is asked for a pose and a pose always exists, this refuses a width the gripper cannot close on. The two differ because the outcomes differ: a clamped query is a number the caller can inspect, whereas a clamped preset silently starts the episode with the gripper wide open around an object welded into it — a rig that looks like it is holding something it is not, with nothing to notice.

Source code in eden/options/robots/composite/base.py
def preset_hold(self, width: float, clearance: float = 0.002) -> None:
    """Start the gripper closed on an object ``width`` m wide: writes :meth:`hold_width` into its defaults.

    Unlike :meth:`hold_width`, which clamps because it is asked for a pose and a pose always
    exists, this refuses a width the gripper cannot close on. The two differ because the
    outcomes differ: a clamped *query* is a number the caller can inspect, whereas a clamped
    *preset* silently starts the episode with the gripper wide open around an object welded into
    it — a rig that looks like it is holding something it is not, with nothing to notice.
    """
    if width <= 0.0:
        raise ValueError(
            f"{type(self).__name__}.preset_hold needs the width of an object to hold, got {width} m. "
            f"To start the gripper simply shut, set its defaults from gripper_close instead."
        )
    opening = width + 2.0 * clearance
    if opening > self.stroke:
        raise ValueError(
            f"{type(self).__name__} cannot hold a {width} m object at {clearance} m clearance: that needs a "
            f"{opening} m opening and {type(self.gripper).__name__}'s stroke is {self.stroke} m. Widths up to "
            f"{self.stroke - 2.0 * clearance} m fit at this clearance."
        )
    self.gripper.default_dofs_pos.update(self.hold_width(width, clearance))
entity_parts
entity_parts() -> list[tuple[str, BaseEntityOptions]]
Source code in eden/options/robots/composite/base.py
def entity_parts(self) -> list[tuple[str, BaseEntityOptions]]:
    return [(self.arm_name, self.arm), (self.gripper_name, self.gripper)]
attachments
attachments() -> dict[str, AttachmentSpec]
Source code in eden/options/robots/composite/base.py
def attachments(self) -> dict[str, AttachmentSpec]:
    return {self.gripper_name: self._attach_spec(self.gripper)}

DexManipulatorOptions

DexManipulatorOptions(**data)

Bases: _ArmCompositeOptions

An arm + an attached multi-DOF dexterous hand, added to a scene as one rig.

The dexterous counterpart of :class:ManipulatorOptions: the two-finger gripper is replaced by a hand welded to arm's ee_link and driven in joint space over every DOF (:attr:hand_dofs) — a dexterous hand has no scalar open/close target, so there is deliberately no hand_open / hand_close. The grasp point is the centroid of the fingertip links (the inherited :attr:grasp_links, aliased :attr:fingertip_links for readability at dex call sites). Concrete rigs (Franka + XHand, xArm + XHand, KUKA + SharPa, …) subclass it with default fields and register in :data:COMPOSITE_ROBOT_REGISTRY.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

hand instance-attribute
hand_name class-attribute instance-attribute
hand_name: str = 'hand'
hand_dofs property
hand_dofs: list[str]

Hand DOFs driven in joint space by the PD controller (== hand.dofs_name).

fingertip_links: list[str]

Fingertip links whose centroid is the grasp point (== grasp_links).

Methods:

entity_parts
entity_parts() -> list[tuple[str, BaseEntityOptions]]
Source code in eden/options/robots/composite/base.py
def entity_parts(self) -> list[tuple[str, BaseEntityOptions]]:
    return [(self.arm_name, self.arm), (self.hand_name, self.hand)]
attachments
attachments() -> dict[str, AttachmentSpec]
Source code in eden/options/robots/composite/base.py
def attachments(self) -> dict[str, AttachmentSpec]:
    return {self.hand_name: self._attach_spec(self.hand)}

HumanoidOptions

HumanoidOptions(**data)

Bases: CompositeEntityOptions

Shared base for a bimanual humanoid rig: a hand-stripped humanoid + an end-effector per wrist.

The bimanual counterpart of :class:_ArmCompositeOptions. Where an arm rig welds one end-effector to one arm, a humanoid rig welds a left and a right one — a dexterous hand or a two-finger gripper, whichever the embodiment carries — onto the flange frames the humanoid asset declares, and so expands into three scene entities rather than two::

rig = HumanoidOptions(robot=OliNoHands(), left_hand=..., right_hand=...,
                      left_ee_link="left_hand_mount", right_ee_link="right_hand_mount")
scene = en.scenes.Empty(robot=rig)
# -> entities "robot", "left_hand", "right_hand"

The humanoid is the composite's primary body, so the scene placement key must equal :attr:robot_name. Concrete rigs (LimX Oli + Wuji hands, …) subclass this with default fields and register in :data:COMPOSITE_ROBOT_REGISTRY; the useful default is the hand-stripped humanoid variant (OliNoHands, G1Dofs29NoHands), so the arms carry the real end-effector's mass and not that plus a moulded phantom one.

Everything a controller needs is a shortcut property over the sub-options, so the DOF lists and grasp links cannot drift from the robot definitions::

JointPositionAction.configure(entity_name=rig.robot_name, dofs_name=rig.robot_dofs)
DifferentialIKController.configure(entity_name=rig.robot_name, ee_link_name=rig.right_ee_link)

A gripper-ended rig reads its open/close targets off the sub-option (rig.left_hand.open_dofs_pos): unlike :class:ManipulatorOptions, this base stays end-effector agnostic, since a humanoid can wear a different one per side.

The mount transform is likewise shared by default (:attr:mount_offset / :attr:mount_quat) but overridable per wrist (:attr:left_mount_quat and friends), because "both flanges take the same transform" holds only where the asset frames are mirror images — true of Oli's *_hand_mount pair, not a property of humanoids in general.

What the rig stands on follows the same shape: :attr:feet_links reads the humanoid's own feet_links_name unless :attr:feet_links_name overrides it, for a rig whose ground contact is not the humanoid's declared feet.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

robot instance-attribute
robot: RobotOptions
left_hand instance-attribute
left_hand: RobotOptions
right_hand instance-attribute
right_hand: RobotOptions
left_ee_link: str
right_ee_link: str
left_grasp_links: list[str] = []
right_grasp_links: list[str] = []
feet_links_name: tuple[str, ...] | None = None
robot_name class-attribute instance-attribute
robot_name: str = 'robot'
left_hand_name class-attribute instance-attribute
left_hand_name: str = 'left_hand'
right_hand_name class-attribute instance-attribute
right_hand_name: str = 'right_hand'
base_pos class-attribute instance-attribute
base_pos: tuple[float, float, float] | None = None
mount_offset class-attribute instance-attribute
mount_offset: Vec3FType = (0.0, 0.0, 0.0)
mount_quat class-attribute instance-attribute
mount_quat: UnitVec4FType = (1.0, 0.0, 0.0, 0.0)
left_mount_offset class-attribute instance-attribute
left_mount_offset: Vec3FType | None = None
left_mount_quat class-attribute instance-attribute
left_mount_quat: UnitVec4FType | None = None
right_mount_offset class-attribute instance-attribute
right_mount_offset: Vec3FType | None = None
right_mount_quat class-attribute instance-attribute
right_mount_quat: UnitVec4FType | None = None
robot_dofs property
robot_dofs: list[str]

The humanoid's body DOFs, in the robot's canonical order (== robot.dofs_name).

left_hand_dofs property
left_hand_dofs: list[str]

Left end-effector DOFs driven in joint space by the PD controller (== left_hand.dofs_name).

right_hand_dofs property
right_hand_dofs: list[str]

Right end-effector DOFs driven in joint space by the PD controller (== right_hand.dofs_name).

left_hand_actuated_dofs property
left_hand_actuated_dofs: list[str]

The left end-effector's driven DOFs, its class's actuated_dofs_name.

The action columns of a hand whose other joints follow them (:attr:left_hand_mimic_dofs).

right_hand_actuated_dofs property
right_hand_actuated_dofs: list[str]

The right end-effector's driven DOFs; see :attr:left_hand_actuated_dofs.

left_hand_mimic_dofs property
left_hand_mimic_dofs: dict[str, tuple[str, float, float]]

The left end-effector's couplings, follower -> (driver, multiplier, offset).

Read off its metadata.mimic_dofs (empty for a hand that declares none): what a joint PD action term's mimic_dofs takes.

right_hand_mimic_dofs property
right_hand_mimic_dofs: dict[str, tuple[str, float, float]]

The right end-effector's couplings; see :attr:left_hand_mimic_dofs.

feet_links: tuple[str, ...]

The links this rig stands on: :attr:feet_links_name if set, else the humanoid's own.

The resolved answer, and the one consumers should read — :attr:feet_links_name is the override, and is None for every rig that simply inherits. Resolved on read rather than written back at construction, so swapping robot still moves the feet with it and a saved config records the rig's intent (inherit) instead of freezing today's answer.

Methods:

entity_parts
entity_parts() -> list[tuple[str, BaseEntityOptions]]
Source code in eden/options/robots/composite/base.py
def entity_parts(self) -> list[tuple[str, BaseEntityOptions]]:
    return [
        (self.robot_name, self.robot),
        (self.left_hand_name, self.left_hand),
        (self.right_hand_name, self.right_hand),
    ]
attachments
attachments() -> dict[str, AttachmentSpec]
Source code in eden/options/robots/composite/base.py
def attachments(self) -> dict[str, AttachmentSpec]:
    return {
        self.left_hand_name: self._mount_spec(
            self.left_hand, self.left_ee_link, self.left_mount_offset, self.left_mount_quat
        ),
        self.right_hand_name: self._mount_spec(
            self.right_hand, self.right_ee_link, self.right_mount_offset, self.right_mount_quat
        ),
    }

Functions:

weld_quat

weld_quat(child: EntityOptions, mount_quat: UnitVec4FType) -> tuple[float, float, float, float]

The attach_quat that makes mount_quat a rotation of URDF link frames.

Genesis applies a weld's quaternion before the morph frame offset, and Eden keeps the child's up/front alignment in that offset (Entity._create_morph_from_options), so the parent link gets attach @ align. Right-multiplying by align^-1 cancels it, making mount_quat mean the child's base-link orientation in the parent link frame — the identity when the two URDF frames coincide.

A no-op for a child with the Eden default axes. The Wuji hands' alignment is a 180 deg rotation, so leaving it in welds the hand pointing back up the forearm, silently.

PARAMETER DESCRIPTION
child

The entity being welded, read for its up / front declaration. A primitive has no asset axes to align and is not a supported end-effector.

TYPE: EntityOptions

mount_quat

Intended (w, x, y, z) orientation of the child's base link in the parent link frame. Scaling is harmless (quat_to_R normalizes); zero raises, since Genesis would only surface a bare ZeroDivisionError.

TYPE: array - like[float, float, float, float]

RETURNS DESCRIPTION
tuple[float, float, float, float]

The (w, x, y, z) quaternion to pass as the weld's attach_quat.

Source code in eden/options/robots/composite/base.py
def weld_quat(child: EntityOptions, mount_quat: UnitVec4FType) -> tuple[float, float, float, float]:
    """The ``attach_quat`` that makes ``mount_quat`` a rotation of URDF *link* frames.

    Genesis applies a weld's quaternion **before** the morph frame offset, and Eden keeps the
    child's ``up``/``front`` alignment in that offset (``Entity._create_morph_from_options``), so
    the parent link gets ``attach @ align``. Right-multiplying by ``align^-1`` cancels it, making
    ``mount_quat`` mean the child's base-link orientation in the parent link frame — the identity
    when the two URDF frames coincide.

    A no-op for a child with the Eden default axes. The Wuji hands' alignment is a 180 deg
    rotation, so leaving it in welds the hand pointing back up the forearm, silently.

    Parameters
    ----------
    child : EntityOptions
        The entity being welded, read for its ``up`` / ``front`` declaration. A primitive has no
        asset axes to align and is not a supported end-effector.
    mount_quat : array-like[float, float, float, float]
        Intended (w, x, y, z) orientation of the child's base link in the parent link frame.
        Scaling is harmless (``quat_to_R`` normalizes); zero raises, since Genesis would only
        surface a bare ``ZeroDivisionError``.

    Returns
    -------
    tuple[float, float, float, float]
        The (w, x, y, z) quaternion to pass as the weld's ``attach_quat``.
    """
    quat = np.asarray(mount_quat, dtype=np.float64)
    if np.linalg.norm(quat) < 1e-12:
        raise ValueError(f"mount_quat must be a non-zero quaternion (w, x, y, z); got {tuple(mount_quat)}.")
    align = align_up_and_front(child.up, child.front)
    return tuple(float(v) for v in gu.R_to_quat(gu.quat_to_R(quat) @ align.T))

has_frame_alignment

has_frame_alignment(child: EntityOptions) -> bool

Whether child's up/front rotate its asset at load, so a weld must cancel them.

Source code in eden/options/robots/composite/base.py
def has_frame_alignment(child: EntityOptions) -> bool:
    """Whether ``child``'s ``up``/``front`` rotate its asset at load, so a weld must cancel them."""
    return not np.allclose(align_up_and_front(child.up, child.front), np.eye(3), atol=1e-9)

Droid

Droid = Franka arm + Robotiq 2F85 (after mjlab's Droid).

Attributes

Classes

Droid

Droid(**data)

Bases: ManipulatorOptions

Franka arm + Robotiq 2F85, after mjlab's Droid.

The arm is the hand-less Panda (:func:_droid_arm), not the FR3 mjlab pairs the gripper with: the rig was built and tuned against that Panda, and keeping it avoids silently re-tuning every policy trained here. One keyword gets the faithful pairing: Droid(arm=FrankaResearch3()).

Construct directly like any other Options preset — Droid(base_pos=(0, 0, 0.4)) — and add it to a scene by name (en.scenes.Empty(robot=Droid(...))); the scene expands it into the robot (arm) and gripper entities welded at the Franka's attachment link.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_droid_arm)
gripper class-attribute instance-attribute
gripper: RobotOptions = Field(default_factory=_droid_gripper)
ee_link: str = 'attachment'
grasp_links: list[str] = ['left_pad', 'right_pad']

Panda + Franka Hand

Franka Emika Panda arm + Franka Hand, as a composite rig.

The same robot as :class:~eden.options.robots.FrankaEmikaPanda (one MJCF, arm and fingers in one entity) but assembled the way every other manipulator rig is: the arm-only :class:~eden.options.robots.FrankaEmikaPandaNoHand with the :class:~eden.options.robots.FrankaHand welded onto its attachment flange. That is what lets a task written against the composite contract — arm_name / gripper_name, ee_link, grasp_links, the gripper's tool point and stroke — swap this rig for a Droid or a UR + Robotiq by registry name alone, with no single-entity special case. The hand lands exactly where panda.xml puts it: the attachment flange both Eden Franka arms expose is 135 degrees about z, and franka_hand.xml's root carries the complementary 180 degrees.

Attributes

Classes

PandaFrankaHand

PandaFrankaHand(**data)

Bases: ManipulatorOptions

Panda arm + Franka Hand, welded at the arm's attachment flange.

Registered panda_franka_hand (<arm>_<end-effector>, like ur5e_2f85): panda alone is the single-entity :class:~eden.options.robots.FrankaEmikaPanda in ROBOT_REGISTRY.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_panda_arm)
gripper class-attribute instance-attribute
gripper: RobotOptions = Field(default_factory=_franka_hand)
ee_link: str = 'attachment'
grasp_links: list[str] = ['left_gripper_pad', 'right_gripper_pad']

UR5e / UR10e + Robotiq 2F-85

UR5e / UR10e + Robotiq 2F85 composite manipulators.

Universal Robots arms carrying the same pad-only Robotiq 2F85 as :class:Droid. The gripper welds at identity onto the UR URDF's fixed ee_link flange frame, so no offset is needed here; a rig that does need a tool-frame offset can set attach_offset / attach_quat on the composite (the Franka instead carries a dedicated attachment body). Registered under the same lowercase names the manipulation_lift_box --manipulator flag uses, so a task can swap the arm by name via :data:COMPOSITE_ROBOT_REGISTRY.

Attributes

Classes

UR5eRobotiq

UR5eRobotiq(**data)

Bases: ManipulatorOptions

UR5e + Robotiq 2F85, welded at the arm's ee_link flange.

Registered ur5e_2f85, not ur5e: the bare arm already owns that key in ROBOT_REGISTRY, and a rig named for its arm alone both collides with it and hides which end-effector is on. <arm>_<end-effector> is what the rest of this package already does (franka_x_hand, kuka_sharpa, xarm7_xhand1_r, oli_wuji).

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_ur5e_arm)
gripper class-attribute instance-attribute
gripper: RobotOptions = Field(default_factory=_droid_gripper)
ee_link: str = 'ee_link'
grasp_links: list[str] = ['left_pad', 'right_pad']

UR10eRobotiq

UR10eRobotiq(**data)

Bases: ManipulatorOptions

UR10e + Robotiq 2F85, welded at the arm's ee_link flange (see :class:UR5eRobotiq).

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_ur10e_arm)
gripper class-attribute instance-attribute
gripper: RobotOptions = Field(default_factory=_droid_gripper)
ee_link: str = 'ee_link'
grasp_links: list[str] = ['left_pad', 'right_pad']

Franka + XHand (LeFranX)

FrankaXHand = Franka arm + RobotEra XHand1 (left) on the LeFranX coupling, the dexterous Droid.

Attributes

Classes

FrankaXHand

FrankaXHand(**data)

Bases: DexManipulatorOptions

Franka arm + RobotEra XHand1 (left) on the LeFranX right-angle coupling, a 12-DOF dexterous manipulator.

The dexterous counterpart of :class:~eden.options.robots.composite.droid.Droid: same Franka arm and home pose, but the two-finger gripper is swapped for the XHand1 hand, welded to the Franka's attachment frame and driven in joint space. The grasp point is the fingertip centroid. Construct like any Options preset — FrankaXHand(base_pos=(0, 0, 0.4)) — and add it to a scene by name (en.scenes.Empty(robot=FrankaXHand(...))); the scene expands it into the robot (arm) and hand entities.

The hand hangs on the coupling (wengmister/xhand-coupling <https://github.com/wengmister/xhand-coupling>_, the mount of the LeFranX Franka + XHand teleoperation stack), a plate bolted to the flange plus a holder hanging off it. The hand's finger axis is perpendicular to the flange axis and its palm faces along it, so with the flange pointing down the fingers are horizontal and the palm faces down. Both CAD parts are exported in one assembly frame, which fixes the transform: the hand's Ø38 wrist bore slides over the holder's 13 mm boss and the wrist face rests on the seat around it, 46 mm out from the flange face and 37 mm off the flange axis; the fingers point back across that axis.

The clocking about the flange axis is the one measured on a lab robot: at the Franka ready pose (0, -pi/4, 0, -3pi/4, 0, pi/2, pi/4) the fingertips point along the base frame's -y, the palm faces down and the thumb is on the base's side. That is one 90-degree bolt step off the coupling's dowel-pin hole. A coupling mounted on its pin instead hangs the hand at attach_offset=(-0.037, 0.0, 0.046), attach_quat=(0.0, 0.7071068, 0.0, 0.7071068) (fingertips along -x at the ready pose). A hand on another mount takes that mount's attach_offset / attach_quat the same way.

Only the weld is modelled. The coupling itself (75 x 77 x 95 mm envelope) has no geometry here, so nothing collides with it.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_franka_arm)
hand class-attribute instance-attribute
hand: RobotOptions = Field(default_factory=_xhand_left)
ee_link: str = 'attachment'
grasp_links: list[str] = list(XHand1_L.metadata.target_hand_links)
attach_offset class-attribute instance-attribute
attach_offset: Vec3FType | None = _LEFRANX_MOUNT_OFFSET
attach_quat class-attribute instance-attribute
attach_quat: UnitVec4FType | None = _LEFRANX_MOUNT_QUAT

xArm7 + XHand

XArm7XHand = UFACTORY xArm7 + RobotEra XHand1 (left/right), ConTrack's rig.

ConTrack ships the arm and hand pre-welded in one URDF (assets/urdf/xarm_xhand_{left,right}.urdf). This composite reproduces that chain from Eden's own unit robots — :class:~eden.options.robots.ufactory_xarm7.Xarm7 plus :class:~eden.options.robots.robotera_xhand.XHand1_L/XHand1_R — welded at the arm flange, so the rig needs no ConTrack-specific asset. Genesis attach removes the hand's root joint and merges both bodies into a single kinematic tree, so this is a true fixed joint, not a constraint-based weld.

Equivalence to ConTrack's combined URDF (verified in tests/options/robots/composite/test_xarm_xhand.py):

  • The xArm7 chain (link_base..link7: joint origins, axes, limits, masses, inertias, dynamics) is bit-identical between Eden's arms/ufactory_xarm7/xarm.urdf and the combined file.
  • ConTrack welds the hand with joint_eef: link7 -> {side}_hand_link at xyz=(0, 0, 0.007) rpy=(3.14, 0, ±1.57). Eden's link_eef is a zero-offset fixed child of link7, and Eden's XHand1 assets carry that rpy rotation inside the asset (their base frame is the flange-aligned convention), so the mount reduces to :data:_MOUNT_OFFSET with an identity rotation.
  • rpy="3.14 0 ±1.57" truncates π and π/2, i.e. ConTrack's weld is 1.781 mrad off the exact rotation. Mount at ConTrack's own truncated value and all 38 link positions agree to 292 nm — the chains are the same geometry. This rig mounts at the exact rotation instead, which leaves a 0.37 mm systematic offset at the fingertips versus the released clips. That is deliberate: this is a general-purpose preset, so it describes the real robot rather than reproducing one dataset's typo, and 0.37 mm is ~80x below the tightest std any ConTrack reward uses (3 cm contact distance). Override attach_quat if you need the truncated frame bit-for-bit.
  • Every hand link frame is rotated by a constant 180° ((1, ±1, 0)/√2) relative to the combined URDF: the two XHand1 revisions use mirrored per-link frame conventions. Link positions, joint values, and dynamics are unaffected; only link quaternions differ, so terms that read a hand link's orientation are not frame-comparable with the source implementation.

Known asset-revision deltas (Eden's XHand1 is a later revision than ConTrack's copy): per-link masses differ by ≤ 8 g (largest: right thumb_rota_link2, 35 g vs 43 g), the *back*/*tip* massless stubs carry different placeholder inertias, and thumb_rota_joint1/2 cap at 1.570 rad instead of 1.745. The bundled clips peak at 1.505 rad on thumb_rota_joint1, so the reference stays reachable — but the residual policy's ±0.25 rad finger action clips ~0.07 rad earlier than in the source.

Attributes

Classes

XArm7XHand1_R

XArm7XHand1_R(**data)

Bases: _XArm7XHand1

xArm7 + XHand1 (right), the 19-DOF rig ConTrack's right-hand clips track.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

hand class-attribute instance-attribute
hand: RobotOptions = Field(default_factory=partial(_contrack_hand, XHand1_R))
grasp_links: list[str] = list(XHand1_R.metadata.target_hand_links)

XArm7XHand1_L

XArm7XHand1_L(**data)

Bases: _XArm7XHand1

xArm7 + XHand1 (left), the 19-DOF rig ConTrack's left-hand clips track.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

hand class-attribute instance-attribute
hand: RobotOptions = Field(default_factory=partial(_contrack_hand, XHand1_L))
grasp_links: list[str] = list(XHand1_L.metadata.target_hand_links)

KUKA iiwa 14 + SharPa Wave

KukaSharpa = KUKA LBR iiwa 14 + SharPa Wave (left), the 29-DOF rig SimToolReal trains on.

SimToolReal's merged 29-DOF URDF (iiwa14_left_sharpa_adjusted_restricted.urdf) reaches the hand through two intermediate frames::

iiwa14_link_7
  --[iiwa14_joint_ee     xyz=(0, 0, 0.045)  rpy=(0, 0, 0)      ]--> iiwa14_link_ee
  --[iiwa14_sharpa       xyz=(0, 0, 0)      rpy=(0, 0, +15 deg)]--> sharpa_mount
  --[sharpa_mount_joint  xyz=(0, 0, 0.05)   rpy=(0, 0, -90 deg)]--> left_hand_C_MC

Both are pure frames (sharpa_mount carries only a 50 g placeholder mass) and every rotation is about z, so the chain collapses to the one attach_offset / attach_quat weld below. Verified against that URDF: 36/36 links, forward kinematics <= 9e-7 m / 2.5e-4 deg, per-DOF dynamics exact, and a 200-step 29-DOF PD rollout agreeing to 0.002 deg. Three deliberate differences: the 0.05 kg sharpa_mount has no counterpart (leaving the arm's M(q) block 0.5-0.7% low), arm collision is 46 MJCF spheres vs 8 URDF meshes (hand geometry is identical), and :data:SIMTOOLREAL_HAND_DOFS_POS_LIMITS is opt-in.

That agreement also depends on the joint damping and effort limit the iiwa14 MJCF omits; those are hardware properties so they live on :class:~eden.options.robots.kuka_iiwa14.Iiwa14 and are inherited here.

Attributes

SHARPA_MOUNT_OFFSET module-attribute

SHARPA_MOUNT_OFFSET: tuple[float, float, float] = (0.0, 0.0, 0.095)

SHARPA_MOUNT_QUAT module-attribute

SHARPA_MOUNT_QUAT: tuple[float, float, float, float] = (0.7933533402912352, 0.0, 0.0, -0.6087614290087207)

SIMTOOLREAL_ARM_KP module-attribute

SIMTOOLREAL_ARM_KP: dict[str, float] = {'joint1': 600.0, 'joint2': 600.0, 'joint3': 500.0, 'joint4': 400.0, 'joint5': 200.0, 'joint6': 200.0, 'joint7': 200.0}

SIMTOOLREAL_ARM_KD module-attribute

SIMTOOLREAL_ARM_KD: dict[str, float] = {'joint1': 27.027026473513512, 'joint2': 27.027026473513512, 'joint3': 24.672186769721083, 'joint4': 22.067474708266914, 'joint5': 9.752538131173853, 'joint6': 9.147747263670984, 'joint7': 9.147747263670984}

SIMTOOLREAL_HAND_KP module-attribute

SIMTOOLREAL_HAND_KP: dict[str, float] = {'left_thumb_CMC_FE': 6.95, 'left_thumb_CMC_AA': 13.2, 'left_thumb_MCP_FE': 4.76, 'left_thumb_MCP_AA': 6.62, 'left_thumb_IP': 0.9, 'left_index_MCP_FE': 4.76, 'left_index_MCP_AA': 6.62, 'left_index_PIP': 0.9, 'left_index_DIP': 0.9, 'left_middle_MCP_FE': 4.76, 'left_middle_MCP_AA': 6.62, 'left_middle_PIP': 0.9, 'left_middle_DIP': 0.9, 'left_ring_MCP_FE': 4.76, 'left_ring_MCP_AA': 6.62, 'left_ring_PIP': 0.9, 'left_ring_DIP': 0.9, 'left_pinky_CMC': 1.38, 'left_pinky_MCP_FE': 4.76, 'left_pinky_MCP_AA': 6.62, 'left_pinky_PIP': 0.9, 'left_pinky_DIP': 0.9}

SIMTOOLREAL_HAND_KD module-attribute

SIMTOOLREAL_HAND_KD: dict[str, float] = {'left_thumb_CMC_FE': 0.28676845, 'left_thumb_CMC_AA': 0.40845109, 'left_thumb_MCP_FE': 0.20394083, 'left_thumb_MCP_AA': 0.24044435, 'left_thumb_IP': 0.04190723, 'left_index_MCP_FE': 0.20859232, 'left_index_MCP_AA': 0.24595532, 'left_index_PIP': 0.04243185, 'left_index_DIP': 0.03504461, 'left_middle_MCP_FE': 0.2085923, 'left_middle_MCP_AA': 0.24595532, 'left_middle_PIP': 0.04243185, 'left_middle_DIP': 0.03504461, 'left_ring_MCP_FE': 0.20859226, 'left_ring_MCP_AA': 0.24595528, 'left_ring_PIP': 0.04243183, 'left_ring_DIP': 0.0350446, 'left_pinky_CMC': 0.02782345, 'left_pinky_MCP_FE': 0.20859229, 'left_pinky_MCP_AA': 0.24595528, 'left_pinky_PIP': 0.04243183, 'left_pinky_DIP': 0.0350446}

SIMTOOLREAL_ARM_ARMATURE module-attribute

SIMTOOLREAL_ARM_ARMATURE: dict[str, float] = {f'joint{i}': 0.0 for i in range(1, 8)}

SIMTOOLREAL_HAND_ARMATURE module-attribute

SIMTOOLREAL_HAND_ARMATURE: dict[str, float] = {'left_thumb_CMC_FE': 0.0032, 'left_thumb_CMC_AA': 0.0032, 'left_thumb_MCP_FE': 0.00265, 'left_thumb_MCP_AA': 0.00265, 'left_thumb_IP': 0.0006, 'left_index_MCP_FE': 0.00265, 'left_index_MCP_AA': 0.00265, 'left_index_PIP': 0.0006, 'left_index_DIP': 0.00042, 'left_middle_MCP_FE': 0.00265, 'left_middle_MCP_AA': 0.00265, 'left_middle_PIP': 0.0006, 'left_middle_DIP': 0.00042, 'left_ring_MCP_FE': 0.00265, 'left_ring_MCP_AA': 0.00265, 'left_ring_PIP': 0.0006, 'left_ring_DIP': 0.00042, 'left_pinky_CMC': 0.0032, 'left_pinky_MCP_FE': 0.00265, 'left_pinky_MCP_AA': 0.00265, 'left_pinky_PIP': 0.0006, 'left_pinky_DIP': 0.00042}

SIMTOOLREAL_HAND_DOFS_POS_LIMITS module-attribute

SIMTOOLREAL_HAND_DOFS_POS_LIMITS: dict[str, tuple[float, float]] = {'left_thumb_CMC_AA': (-0.3491, 0.1309), 'left_index_MCP_AA': (-0.03491, 0.03491), 'left_middle_MCP_AA': (-0.03491, 0.03491), 'left_ring_MCP_AA': (-0.03491, 0.03491), 'left_pinky_MCP_AA': (-0.03491, 0.03491)}

Classes

KukaSharpa

KukaSharpa(**data)

Bases: DexManipulatorOptions

KUKA iiwa 14 + SharPa Wave (left), a 29-DOF dexterous manipulator (7 arm + 22 hand).

The SimToolReal rig as two unit robots welded at the arm flange (see the module docstring for the mount derivation), so arm and hand each stay swappable. The scene expands it into the robot (arm) and hand entities::

scene = en.scenes.Empty(robot=KukaSharpa(base_pos=(0.0, 0.8, 0.0)))

base_pos there is SimToolReal's training placement, 0.8 m behind a workspace at the world origin.

Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

arm class-attribute instance-attribute
arm: RobotOptions = Field(default_factory=_iiwa_arm)
hand class-attribute instance-attribute
hand: RobotOptions = Field(default_factory=_sharpa_left)
ee_link: str = 'link7'
attach_offset class-attribute instance-attribute
attach_offset: Vec3FType | None = SHARPA_MOUNT_OFFSET
attach_quat class-attribute instance-attribute
attach_quat: UnitVec4FType | None = SHARPA_MOUNT_QUAT
grasp_links: list[str] = list(_SHARPA_L_TIPS)

G1 + Inspire hands

G1Inspire = Unitree G1 29DoF body + an Inspire RH56DFQ hand welded to each wrist (Weave's G1-Inspire-HOI rig).

The composite form of Weave's merged g1_29dof_rev_1_0_with_inspire_hand_DFQ.urdf, cut at its two fixed hand joints so each hand is its own scene entity with its own DOFs while the assembled rig is the one the Weave reference motions were retargeted onto.

The body is :class:~eden.options.robots.unitree_g1.G1Dofs29NoHands on the bare-wrist URDF: the same G1 rev 1.0 joints, frames and masses, except that each *_wrist_yaw_link carries the bare wrist (0.0846 kg) rather than the no-hand MJCF's wrist-plus-rubber-hand (0.2546 kg), so the arms carry the welded hands' real mass and no phantom payload on top.

The mount is the fixed hand joint the split cut (*_base_link_joint): the hand base link sits 0.0415 m along the wrist, rotated rpy (0, 0, π/2) left and (π, 0, -π/2) right. Those two rotations are not mirror images, so the rig sets them per side. :class:~eden.datasets.weave_dataset.WeaveG1InspireHOIDataset rebuilds the hand base pose of the reference from the same constants.

The hands are not gravity-compensated: the G1 is a floating-base humanoid whose arms carry the 0.19 kg per hand.

Attributes

G1_BARE_WRIST_FILE module-attribute

G1_BARE_WRIST_FILE = 'humanoids/unitree_g1/g1_29dof_rev_1_0_bare_wrist.urdf'
G1_INSPIRE_WRIST_LINKS_NAME: tuple[str, str] = ('left_wrist_yaw_link', 'right_wrist_yaw_link')

G1_INSPIRE_MOUNT_OFFSET module-attribute

G1_INSPIRE_MOUNT_OFFSET: tuple[float, float, float] = (0.0415, 0.0, 0.0)

G1_INSPIRE_LEFT_MOUNT_QUAT module-attribute

G1_INSPIRE_LEFT_MOUNT_QUAT: tuple[float, float, float, float] = (_H, 0.0, 0.0, _H)

G1_INSPIRE_RIGHT_MOUNT_QUAT module-attribute

G1_INSPIRE_RIGHT_MOUNT_QUAT: tuple[float, float, float, float] = (0.0, _H, -_H, 0.0)

Classes

G1Inspire

G1Inspire(**data)

Bases: HumanoidOptions

Unitree G1 + two Inspire RH56DFQ hands, a 53-DOF bimanual humanoid (29 body + 2 x 12, 6 of each hand coupled).

Expands into three scene entities — the body under :attr:~eden.options.robots.composite.base.HumanoidOptions.robot_name plus a hand per wrist::

scene = en.scenes.Empty(robot=G1Inspire())
# -> entities "robot", "left_hand", "right_hand"

A hand's six mimic followers are driven in software, so a position controller commands only :attr:~eden.options.robots.composite.base.HumanoidOptions.left_hand_actuated_dofs and passes the couplings through::

ImplicitPDController.configure(entity_name=rig.left_hand_name, dofs_name=rig.left_hand_actuated_dofs,
                               mimic_dofs=rig.left_hand_mimic_dofs)
Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

robot class-attribute instance-attribute
robot: RobotOptions = Field(default_factory=lambda: G1Dofs29NoHands(file=G1_BARE_WRIST_FILE))
left_hand class-attribute instance-attribute
left_hand: RobotOptions = Field(default_factory=lambda: InspireRH56DFQ_L(is_fixed_base=False))
right_hand class-attribute instance-attribute
right_hand: RobotOptions = Field(default_factory=lambda: InspireRH56DFQ_R(is_fixed_base=False))
left_ee_link: str = G1_INSPIRE_WRIST_LINKS_NAME[0]
right_ee_link: str = G1_INSPIRE_WRIST_LINKS_NAME[1]
mount_offset class-attribute instance-attribute
mount_offset: Vec3FType = G1_INSPIRE_MOUNT_OFFSET
left_mount_quat class-attribute instance-attribute
left_mount_quat: UnitVec4FType | None = G1_INSPIRE_LEFT_MOUNT_QUAT
right_mount_quat class-attribute instance-attribute
right_mount_quat: UnitVec4FType | None = G1_INSPIRE_RIGHT_MOUNT_QUAT
left_grasp_links: list[str] = list(InspireRH56DFQ_L.metadata.target_hand_links)
right_grasp_links: list[str] = list(InspireRH56DFQ_R.metadata.target_hand_links)

G1 + Wuji hands

G1Wuji = Unitree G1 29-DOF (hands stripped) + a Wuji Hand2 beta2 bolted to each wrist.

The G1 counterpart of :class:~eden.options.robots.composite.oli_wuji.OliWuji. The flange is the *_ee_link frame the no-hand G1 MJCF declares 4 cm past each *_wrist_yaw_link, along the forearm (+x of the wrist frame).

The orientation. Unlike Oli, the G1 wrist frame runs its forearm along +x, while a Wuji hand's fingers run along its base link's -z. The mount is therefore -90 deg about the flange's y axis, which takes the hand's -z onto the flange's +x: fingers continue the forearm, palm toward the body midline and thumb on the wrist frame's +z (forward in the arms-down pose). One quaternion serves both sides, because the per-side chirality lives in the _L / _R hand assets.

Attributes

Classes

G1Wuji

G1Wuji(**data)

Bases: HumanoidOptions

Unitree G1 + two Wuji Hand2 beta2 hands, a 69-DOF bimanual humanoid (29 body + 2 x 20).

Expands into three scene entities — the humanoid under :attr:~eden.options.robots.composite.base.HumanoidOptions.robot_name plus a hand per wrist::

scene = en.scenes.Empty(robot=G1Wuji(base_pos=(0.0, 0.0, 0.793)))
# -> entities "robot", "left_hand", "right_hand"
Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

robot class-attribute instance-attribute
robot: RobotOptions = Field(default_factory=G1Dofs29NoHands)
left_hand class-attribute instance-attribute
left_hand: RobotOptions = Field(default_factory=lambda: WujiHand2Beta2WithMount_L(is_fixed_base=False))
right_hand class-attribute instance-attribute
right_hand: RobotOptions = Field(default_factory=lambda: WujiHand2Beta2WithMount_R(is_fixed_base=False))
left_ee_link: str = 'left_ee_link'
right_ee_link: str = 'right_ee_link'
mount_quat class-attribute instance-attribute
mount_quat: UnitVec4FType = (_HALF, 0.0, -_HALF, 0.0)
left_grasp_links: list[str] = list(WujiHand2Beta2WithMount_L.metadata.target_hand_links)
right_grasp_links: list[str] = list(WujiHand2Beta2WithMount_R.metadata.target_hand_links)

Oli + Wuji hands

OliWuji = LimX Oli (hands stripped) + a Wuji Hand2 beta2 bolted to each wrist flange.

A :class:~eden.options.robots.composite.base.HumanoidOptions rig: a humanoid root with two dexterous hands, rather than the one-arm-one-end-effector shape of :class:~eden.options.robots.composite.base._ArmCompositeOptions. Both welds are identities, so this module is entirely defaults — the entity names, the pelvis knob, the mount transform, the DOF shortcuts and the two welds all come from the base.

The flange. Carried by the asset, not by this file: :class:~eden.options.robots.limx_oli.OliNoHands declares a pure *_hand_mount frame on each exposed wrist flange, so the hands seat on it with no offset and nothing here re-derives one.

The orientation. Also the identity, because both assets already share the wrist convention: Oli's arm and its moulded hand run along -Z, which is the Wuji finger axis (up=(0, 0, -1)); and the moulded hand is wide across X (8.6 cm), thin across Y (5.5 cm), with its distal geometry drifting +Y right / -Y left — fingers curling toward the palm — which is the palm normal the Wuji hand declares per side. Palm inboard, thumb forward.

The catch. A Wuji hand's up/front rotate its asset 180 deg at load, and a weld has to cancel that or the hand ends up pointing back up the forearm — see :func:~eden.options.robots.composite.base.weld_quat, which :meth:~eden.options.robots.composite.base.HumanoidOptions._mount_spec applies to every humanoid weld.

The hands are deliberately not gravity-compensated (unlike the industrial-arm rigs here): Oli is a floating-base humanoid whose arms must carry the payload they are given.

Attributes

Classes

OliWuji

OliWuji(**data)

Bases: HumanoidOptions

LimX Oli + two Wuji Hand2 beta2 hands, a 71-DOF bimanual humanoid (31 body + 2 x 20).

Added to a scene by name like any other entity; it expands into three — the humanoid under :attr:~eden.options.robots.composite.base.HumanoidOptions.robot_name plus a hand per wrist, each welded to the asset's own *_hand_mount flange frame::

scene = en.scenes.Empty(robot=OliWuji(base_pos=(0.0, 0.0, 0.903)))
# -> entities "robot", "left_hand", "right_hand"

The rig exposes what a controller needs as shortcut properties over the sub-options, so the DOF lists and fingertip links cannot drift from the robot definitions::

JointPositionAction.configure(entity_name=rig.robot_name, dofs_name=rig.robot_dofs)
DifferentialIKController.configure(entity_name=rig.robot_name, ee_link_name=rig.right_ee_link)
Source code in eden/options/options.py
def __init__(self, **data):
    # Seed type metadata as construction data so BaseModel validates it into
    # __pydantic_extra__ (no-op for extra="forbid" classes, which use
    # the serialization envelope instead).
    data.update(option_metadata(self))
    # NOTE: all of the option modifications/validations should be done in the __init__ method
    try:
        BaseModel.__init__(self, **data)
    except ValidationError as exc:
        # A misspelled field on an extra="forbid" class should teach the field set, not just
        # name the typo. Only rewritten when every error is extra_forbidden.
        enriched = _enrich_extra_forbidden(type(self), exc)
        if enriched is None:
            raise
        raise enriched from None

Attributes

robot class-attribute instance-attribute
robot: RobotOptions = Field(default_factory=OliNoHands)
left_hand class-attribute instance-attribute
left_hand: RobotOptions = Field(default_factory=lambda: WujiHand2Beta2WithMount_L(is_fixed_base=False))
right_hand class-attribute instance-attribute
right_hand: RobotOptions = Field(default_factory=lambda: WujiHand2Beta2WithMount_R(is_fixed_base=False))
left_ee_link: str = 'left_hand_mount'
right_ee_link: str = 'right_hand_mount'
left_grasp_links: list[str] = list(WujiHand2Beta2WithMount_L.metadata.target_hand_links)
right_grasp_links: list[str] = list(WujiHand2Beta2WithMount_R.metadata.target_hand_links)

See also