protomotions.robot_configs module#

Robot configuration classes defining morphology and control parameters.

For model selection, asset requirements and motion preparation, see Robots.

Base Configuration#

class protomotions.robot_configs.base.RobotConfig(
asset,
common_naming_to_robot_body_names=<factory>,
default_root_height=1,
semantic_forward_axis_xy=None,
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset='all',
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=None,
simulation_params=None,
)[source]#

Bases: object

Configuration for robot morphology and control parameters.

Defines all robot-specific parameters including asset files, body names, control settings, and physical properties. Each robot (SMPL, G1, etc.) should subclass this and provide robot-specific values.

Key configuration areas:

  • Asset: Robot mesh/URDF files and physical properties

  • Body Names: Named body parts for tracking and observations

  • Control: PD gains, action scales, and control modes

  • Kinematic Info: Joint structure extracted from MJCF/URDF

  • Simulator Params: Simulator-specific overrides

Example:

config = RobotConfig(
    semantic_forward_axis_xy=(1.0, 0.0),
    asset=RobotAssetConfig(asset_file_name="robot.xml"),
    control=ControlConfig(control_type=ControlType.BUILT_IN_PD)
)
asset: RobotAssetConfig#
common_naming_to_robot_body_names: Dict[str, List[str]]#
default_root_height: float = 1#
semantic_forward_axis_xy: Tuple[float, float] | None = None#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_dof_pos: List[float] | MockTensor | Dict[str, float] | None = None#
contact_bodies: List[str] | str | None = None#
contact_partners: List[str] | str#
trackable_bodies_subset: List[str] | str = 'all'#
non_termination_contact_bodies: List[str] | str = 'all'#
init_state: InitState | None = None#
reset_noise: RobotNoiseConfig | None = None#
mimic_small_marker_bodies: List[str] | None = None#
anchor_body_name: str | None = None#
imu_body_name: str | None = None#
imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0)#

Unit xyzw rotation from sensor axes to the carrying body’s axes.

contact_pairs_multiplier: int = 16#
control: ControlConfig#
control_hz: int = None#
kinematic_info: KinematicInfo#
number_of_actions: int#
simulation_params: SimulatorParams | None = None#
property control_dt: float#

Seconds per policy control step (1 / control_hz).

__post_init__()[source]#

Compute derived fields after initialization.

property anchor_body_index: int#

Body index observations/rewards anchor on (0 = root), derived from the name.

There is one anchor slot, anchor_body_name. Assigning it re-derives the index on the next read, so the two cannot drift apart; there is no stored index to set (update_fields and the override funnel refuse it). The cache is rebuilt when it is absent too, so a config unpickled from a pre-property bundle (which has no cache) re-derives from the name rather than raising AttributeError on the first read.

property resolved_anchor_body_name: str#

The anchored body’s name, resolved through anchor_body_index.

body_indices(*names)[source]#

Indices for body names, ‘root’, ‘all’ or semantic groups, in declaration order.

Each name is ‘root’, ‘all’, a key of common_naming_to_robot_body_names, or a body name; the expansion is abstract_names_to_body_names’, with an unknown name and a body selected twice refused (both of which that function silently allows).

bodies_matching(pattern)[source]#

Indices of bodies whose name fully matches pattern (regex), in body order.

update_fields(**kwargs)[source]#

Update robot config fields and reprocess derived fields.

This is useful when you need to modify fields after initialization without manually calling __post_init__.

Parameters:

**kwargs – Fields to update on the robot config

__init__(
asset,
common_naming_to_robot_body_names=<factory>,
default_root_height=1,
semantic_forward_axis_xy=None,
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset='all',
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=None,
simulation_params=None,
)#

SMPL Humanoid#

class protomotions.robot_configs.smpl.SmplRobotConfig(asset: protomotions.robot_configs.base.RobotAssetConfig = <factory>, common_naming_to_robot_body_names: Dict[str, str] = <factory>, default_root_height: float = 0.95, semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0), default_dof_pos: Union[List[float], MockTensor, Dict[str, float], NoneType] = None, contact_bodies: Union[List[str], str, NoneType] = None, contact_partners: Union[List[str], str] = <factory>, trackable_bodies_subset: List[str] = <factory>, non_termination_contact_bodies: List[str] = <factory>, init_state: Optional[protomotions.robot_configs.base.InitState] = None, reset_noise: Optional[protomotions.simulator.base_simulator.config.RobotNoiseConfig] = None, mimic_small_marker_bodies: Optional[List[str]] = None, anchor_body_name: Optional[str] = None, imu_body_name: Optional[str] = None, imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), contact_pairs_multiplier: int = 16, control: protomotions.robot_configs.base.ControlConfig = <factory>, control_hz: int = 30, simulation_params: protomotions.robot_configs.base.SimulatorParams = <factory>)[source]#

Bases: RobotConfig

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_root_height: float = 0.95#
control_hz: int = 30#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies=<factory>,
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)#

SMPL-X Humanoid#

class protomotions.robot_configs.smplx.SMPLXRobotConfig(asset: protomotions.robot_configs.base.RobotAssetConfig = <factory>, common_naming_to_robot_body_names: Dict[str, List[str]] = <factory>, default_root_height: float = 0.95, semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0), default_dof_pos: Union[List[float], MockTensor, Dict[str, float], NoneType] = None, contact_bodies: List[str] = <factory>, contact_partners: Union[List[str], str] = <factory>, trackable_bodies_subset: Union[List[str], str] = 'all', non_termination_contact_bodies: List[str] = <factory>, init_state: Optional[protomotions.robot_configs.base.InitState] = None, reset_noise: Optional[protomotions.simulator.base_simulator.config.RobotNoiseConfig] = None, mimic_small_marker_bodies: List[str] = <factory>, anchor_body_name: Optional[str] = None, imu_body_name: Optional[str] = None, imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), contact_pairs_multiplier: int = 20, control: protomotions.robot_configs.base.ControlConfig = <factory>, control_hz: int = 30, simulation_params: protomotions.robot_configs.base.SimulatorParams = <factory>)[source]#

Bases: RobotConfig

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_root_height: float = 0.95#
contact_pairs_multiplier: int = 20#
control_hz: int = 30#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=<factory>,
contact_partners=<factory>,
trackable_bodies_subset='all',
non_termination_contact_bodies=<factory>,
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=<factory>,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=20,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)#

Unitree H1 (Version 2)#

class protomotions.robot_configs.h1_2.H1_2RobotConfig(asset: protomotions.robot_configs.base.RobotAssetConfig = <factory>, common_naming_to_robot_body_names: Dict[str, List[str]] = <factory>, default_root_height: float = 1.03, semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0), default_dof_pos: Union[List[float], MockTensor, Dict[str, float], NoneType] = None, contact_bodies: Union[List[str], str, NoneType] = None, contact_partners: Union[List[str], str] = <factory>, trackable_bodies_subset: List[str] = <factory>, non_termination_contact_bodies: Union[List[str], str] = 'all', init_state: Optional[protomotions.robot_configs.base.InitState] = None, reset_noise: Optional[protomotions.simulator.base_simulator.config.RobotNoiseConfig] = None, mimic_small_marker_bodies: Optional[List[str]] = None, anchor_body_name: Optional[str] = None, imu_body_name: str = 'torso_link', imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), contact_pairs_multiplier: int = 16, control: protomotions.robot_configs.base.ControlConfig = <factory>, control_hz: int = 50, simulation_params: Optional[protomotions.robot_configs.base.SimulatorParams] = None)[source]#

Bases: RobotConfig

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

imu_body_name: str = 'torso_link'#
default_root_height: float = 1.03#
control_hz: int = 50#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=1.03,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name='torso_link',
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=50,
simulation_params=None,
)#

Unitree G1#

class protomotions.robot_configs.g1.G1RobotConfig(asset: protomotions.robot_configs.base.RobotAssetConfig = <factory>, common_naming_to_robot_body_names: Dict[str, List[str]] = <factory>, default_root_height: float = 0.8, semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0), default_dof_pos: Dict[str, float] = <factory>, contact_bodies: Union[List[str], str, NoneType] = None, contact_partners: Union[List[str], str] = <factory>, trackable_bodies_subset: List[str] = <factory>, non_termination_contact_bodies: Union[List[str], str] = 'all', init_state: Optional[protomotions.robot_configs.base.InitState] = None, reset_noise: Optional[protomotions.simulator.base_simulator.config.RobotNoiseConfig] = None, mimic_small_marker_bodies: Optional[List[str]] = None, anchor_body_name: Optional[str] = 'torso_link', imu_body_name: str = 'pelvis', imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), contact_pairs_multiplier: int = 16, control: protomotions.robot_configs.base.ControlConfig = <factory>, control_hz: int = 50, simulation_params: Optional[protomotions.robot_configs.base.SimulatorParams] = None)[source]#

Bases: RobotConfig

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_root_height: float = 0.8#
anchor_body_name: str | None = 'torso_link'#
imu_body_name: str = 'pelvis'#
control_hz: int = 50#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.8,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=<factory>,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name='torso_link',
imu_body_name='pelvis',
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=50,
simulation_params=None,
)#

AMP Humanoid#

class protomotions.robot_configs.amp.AMPRobotConfig(asset: protomotions.robot_configs.base.RobotAssetConfig = <factory>, common_naming_to_robot_body_names: Dict[str, List[str]] = <factory>, default_root_height: float = 0.8, semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0), default_dof_pos: Union[List[float], MockTensor, Dict[str, float], NoneType] = None, contact_bodies: Union[List[str], str, NoneType] = None, contact_partners: Union[List[str], str] = <factory>, trackable_bodies_subset: List[str] = <factory>, non_termination_contact_bodies: Union[List[str], str] = 'all', init_state: Optional[protomotions.robot_configs.base.InitState] = None, reset_noise: Optional[protomotions.simulator.base_simulator.config.RobotNoiseConfig] = None, mimic_small_marker_bodies: Optional[List[str]] = None, anchor_body_name: Optional[str] = None, imu_body_name: Optional[str] = None, imu_mount_quat: Tuple[float, float, float, float] = (0.0, 0.0, 0.0, 1.0), contact_pairs_multiplier: int = 16, control: protomotions.robot_configs.base.ControlConfig = <factory>, control_hz: int = 30, simulation_params: protomotions.robot_configs.base.SimulatorParams = <factory>)[source]#

Bases: RobotConfig

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_root_height: float = 0.8#
control_hz: int = 30#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.8,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)#

SOMA23#

class protomotions.robot_configs.soma23.Soma23RobotConfig(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(0.0,
-1.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)[source]#

Bases: RobotConfig

23-body humanoid derived from SOMASkeleton30.

Drops seven unactuated leaf end-effectors:

Jaw, LeftEye, RightEye,
LeftHandThumbEnd, LeftHandMiddleEnd,
RightHandThumbEnd, RightHandMiddleEnd

Remaining 23 bodies (22 joints × 3-DOF hinge = 66 DOFs):

Hips (root), Spine1, Spine2, Chest,
Neck1, Neck2, Head,
RightShoulder, RightArm, RightForeArm, RightHand,
LeftShoulder, LeftArm, LeftForeArm, LeftHand,
RightLeg, RightShin, RightFoot, RightToeBase,
LeftLeg, LeftShin, LeftFoot, LeftToeBase

See mjcf/soma23_humanoid.xml for definition.

semantic_forward_axis_xy: Tuple[float, float] = (0.0, -1.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

default_root_height: float = 0.95#
control_hz: int = 30#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(0.0,
-1.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name=None,
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)#

Anatomical SOMA#

class protomotions.robot_configs.anatomic_soma.AnatomicSomaRobotConfig(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name='Chest',
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)[source]#

Bases: RobotConfig

52 serial hinges, 23 semantic parts and 39 primitive colliders.

The SOMA23 skeleton with its joints expanded into serial hinges on their own carrier links: 15 of the 22 joints get three hinges (pitch, roll and twist), the knees, elbows and toes one hinge each, and Neck2 a single yaw (15 * 3 + 7 = 52). That gives 53 bodies, of which the 30 intermediate links are non-colliding hinge carriers sitting on their semantic body. Joint ranges are engineering limits, not a medically validated human range-of-motion model. Motion files must be retargeted to this skeleton; SOMA23 packs (66 DOFs, 23 bodies) do not load.

See mjcf/anatomic_soma.xml for the definition.

semantic_forward_axis_xy: Tuple[float, float] = (1.0, 0.0)#

Robot-local xy axis that points in the anatomical facing direction.

This is intentionally 2D, not 3D. ProtoMotions assumes Z-up humanoid assets, so heading should only choose the horizontal anatomical forward direction. Keeping the field in xy avoids using a forward vector with a vertical component that would require re-orienting the robot to stand upright before heading logic can be applied.

Every robot config declares this explicitly: __post_init__ refuses None, and a saved robot subtree without the field fails to load, since from_tree rebuilds every dataclass through its constructor. To identify the axis for a new robot, run python scripts/identify_robot_facing_axis.py --robots <robot-name> --view.

anchor_body_name: str = 'Chest'#
default_root_height: float = 0.95#
control_hz: int = 30#
__init__(
asset=<factory>,
common_naming_to_robot_body_names=<factory>,
default_root_height=0.95,
semantic_forward_axis_xy=(1.0,
0.0),
default_dof_pos=None,
contact_bodies=None,
contact_partners=<factory>,
trackable_bodies_subset=<factory>,
non_termination_contact_bodies='all',
init_state=None,
reset_noise=None,
mimic_small_marker_bodies=None,
anchor_body_name='Chest',
imu_body_name=None,
imu_mount_quat=(0.0,
0.0,
0.0,
1.0),
contact_pairs_multiplier=16,
control=<factory>,
control_hz=30,
simulation_params=<factory>,
)#