SO101-Nexus
API Reference

Configuration Classes

Reference for all configuration classes.

All configuration classes live in so101_nexus and are plain Python classes with default constructor arguments. They control rendering, robot parameters, reward shaping, observation composition, and environment behavior.

RenderConfig

Controls the visualization render camera (not observations): image resolution and which view render_mode shows. camera="side" selects an angled tabletop bystander view, useful for watching rollouts and recording presentation-quality videos of trained policies; it never enters the observation space. The camera selection applies to the MuJoCo backend only, since the Warp backend does not implement render().

from so101_nexus import RenderConfig

render = RenderConfig(width=640, height=480)

# Angled side view for render_mode="rgb_array" videos and the human viewer
render = RenderConfig(camera="side")

Constructor Parameters

ParameterTypeDefaultDescription
widthint640Render image width in pixels
heightint480Render image height in pixels
camera"overhead" | "side""overhead"View used by render_mode="rgb_array" and as the initial render_mode="human" viewpoint. "overhead" looks straight down; "side" is an angled tabletop bystander view
side_azimuth_degfloat160.0Azimuth of the side view in degrees
side_elevation_degfloat-30.0Elevation of the side view in degrees, in [-90, 0)

Pose

Defines a named robot arm configuration with fixed and free joints. See Customization for a conceptual overview.

from so101_nexus import Pose

pose = Pose(
    name="custom",
    shoulder_pan_deg=(-110.0, 110.0),  # free: sampled uniformly
    shoulder_lift_deg=-90.0,            # fixed
    elbow_flex_deg=90.0,                # fixed
    wrist_flex_deg=37.8,                # fixed
    wrist_roll_deg=(-157.0, 163.0),     # free
    gripper_deg=(-10.0, 100.0),         # free
)

Constructor Parameters

ParameterTypeDescription
namestrHuman-readable identifier
shoulder_pan_degfloat | tuple[float, float]Shoulder pan angle or range (degrees)
shoulder_lift_degfloat | tuple[float, float]Shoulder lift angle or range (degrees)
elbow_flex_degfloat | tuple[float, float]Elbow flex angle or range (degrees)
wrist_flex_degfloat | tuple[float, float]Wrist flex angle or range (degrees)
wrist_roll_degfloat | tuple[float, float]Wrist roll angle or range (degrees)
gripper_degfloat | tuple[float, float]Gripper angle or range (degrees)

Methods

MethodReturnsDescription
sample(rng)tuple[float, ...]Sample concrete joint angles in degrees
sample_rad(rng)tuple[float, ...]Sample concrete joint angles in radians

Built-in Poses

NameConstantDescription
"rest"REST_POSEArm curled in rest position, free shoulder_pan/wrist_roll/gripper
"extended"EXTENDED_POSEArm extended forward, free shoulder_pan/wrist_roll/gripper

Access via the POSES dict or by constant:

from so101_nexus import POSES, REST_POSE

pose = POSES["rest"]   # same as REST_POSE

RobotConfig

Controls the arm rest pose, grasping thresholds, and the end-effector solver.

from so101_nexus import RobotConfig

robot = RobotConfig(rest_qpos_deg=(0.0, -90.0, 90.0, 37.82, 0.0, -63.03))

Constructor Parameters

ParameterTypeDefaultDescription
rest_qpos_degtuple[float, ...](0.0, -90.0, 90.0, 37.8152144786, 0.0, -63.0253574644)Rest joint positions in degrees, one per joint
init_posestr | Pose | NoneNoneInitial pose for resets. String looks up from POSES, Pose instance used directly, None uses legacy rest + noise.
grasp_force_thresholdfloat0.5Minimum contact normal force (N) for a finger contact to count toward grasp detection
grasp_opposing_normal_thresholdfloat0.3How strongly the two finger sets must oppose for GraspState to fire: the force-weighted mean inward contact normals must satisfy dot(n_gripper, n_jaw) <= -threshold. Must be in [-1, 1]; -1.0 disables the test and accepts any two-sided contact
static_vel_thresholdfloat0.2Maximum velocity to consider the arm stationary
ee_orientation_weightfloat0.01Relative weight of the rotational error in the end-effector modes' inverse kinematics. Must be in (0, 1]. See Control Modes.
ee_delta_action_scaletuple[float, ...](0.02, 0.02, 0.02, 0.1, 0.1, 0.1, 0.2)Physical scale of a +/-1 pd_ee_delta_pose action: (x, y, z) in meters, (wx, wy, wz) in radians, gripper in radians. Seven positive entries.

Properties and methods

PropertyTypeDescription
rest_qpos_radtuple[float, ...]rest_qpos_deg converted to radians
rest_qpostuple[float, ...]Alias for rest_qpos_rad
resolve_pose()Pose | NoneReturns the resolved Pose object, or None

RobotCameraPreset

Robot-specific camera and mounting parameters for SO-100 and SO-101. All fields are required (no defaults); wrist_cam_euler_center_rad and wrist_cam_euler_noise_rad expose the Euler angles in radians.

ParameterTypeDescription
base_quattuple[float, float, float, float]Base orientation quaternion (w, x, y, z)
sensor_cam_eye_postuple[float, float, float]Sensor camera eye position
sensor_cam_target_postuple[float, float, float]Sensor camera target position
human_cam_eye_postuple[float, float, float]Human camera eye position
human_cam_target_postuple[float, float, float]Human camera target position
wrist_camera_mount_linkstrLink name for wrist camera mounting
wrist_cam_pos_centertuple[float, float, float]Center position for the wrist camera
wrist_cam_pos_noisetuple[float, float, float]Position noise applied to the wrist camera
wrist_cam_euler_center_degtuple[float, float, float]Center wrist camera Euler angles in degrees
wrist_cam_euler_noise_degtuple[float, float, float]Euler angle noise applied to the wrist camera

RewardConfig

Defines reward component weights and computes the shaped reward.

from so101_nexus import RewardConfig

reward = RewardConfig(reaching=0.25, grasping=0.25, task_objective=0.25, completion_bonus=0.25)

Constructor Parameters

ParameterTypeDefaultDescription
reachingfloat0.25Weight for the reaching component. Potential-shaped delta for pick-lift/pick-and-place; raw progress value for Touch/Move/LookAt.
graspingfloat0.25Weight for the grasping component. Potential-shaped delta for pick-lift/pick-and-place.
task_objectivefloat0.25Weight for the task-specific objective. Potential-shaped delta for pick-lift/pick-and-place.
completion_bonusfloat0.25Weight for the task completion bonus
action_delta_penaltyfloat0.0Penalty coefficient on L2 norm of consecutive action deltas
energy_penaltyfloat0.0Penalty coefficient on L2 norm of the action vector
tanh_shaping_scalefloat5.0Scale factor for tanh reward shaping
velocity_shaping_scalefloat15.0Scale factor for tanh velocity shaping (only read by PickAndPlaceConfig's task potential)

A "potential-shaped delta" pays the change in progress since the previous step rather than the raw value, so dwelling at a fixed state (e.g. holding an object without lifting it) earns no further reward. The implementation is so101_nexus.rewards.potential_shaping; see Rewards for which tasks use it.

Validation

The four base weights (reaching + grasping + task_objective + completion_bonus) must sum to 1.0. Construction raises an error if this constraint is violated. The penalty coefficients are separate additive terms and are not included in the sum constraint.

Methods

compute()

def compute(
    reach_progress: float,
    is_grasped: bool,
    task_progress: float,
    is_complete: bool,
    action_delta_norm: float = 0.0,
    energy_norm: float = 0.0,
) -> float

Computes the weighted reward from per-component progress values.

ParameterTypeDefaultDescription
reach_progressfloatrequiredProgress toward the target (0 to 1)
is_graspedboolrequiredWhether the object is currently grasped
task_progressfloatrequiredProgress on the task objective (0 to 1)
is_completeboolrequiredWhether the task is fully complete
action_delta_normfloat0.0Norm of the action delta for penalty
energy_normfloat0.0Norm of energy usage for penalty

Returns a single float reward value.


EnvironmentConfig

Base configuration for all environments. Task-specific configs extend this class.

from so101_nexus import EnvironmentConfig

config = EnvironmentConfig(obs_mode="state")

Constructor Parameters

ParameterTypeDefaultDescription
renderRenderConfig | NoneNoneRender camera settings (visualization only): resolution and overhead/side view selection. Uses RenderConfig() defaults when None.
rewardRewardConfig | NoneNoneReward weights. Uses RewardConfig() defaults when None.
robotRobotConfig | NoneNoneRobot settings. Uses RobotConfig() defaults when None.
ground_colorsColorConfig"gray"Ground plane color(s)
reset_settle_framesint5No-op environment frames advanced after reset before returning the first observation
goal_threshfloat0.025Distance threshold for goal completion (meters)
spawn_half_sizefloat0.05Half-size of object spawn region
spawn_centertuple[float, float](0.15, 0.0)Center of the spawn region (x, y)
spawn_min_radiusfloat0.10Minimum spawn distance from the robot base
spawn_max_radiusfloat0.30Maximum spawn distance from the robot base
spawn_angle_half_range_degfloat90.0Half-range of spawn angle in degrees
obs_modeObsMode"state"Observation mode: "state" or "visual"
robot_colorsColorConfig"yellow"Robot body color(s)
robot_init_qpos_noisefloat0.02Noise added to initial joint positions
observationslist[Observation] | NoneNoneObservation components to include. Task-specific configs provide defaults when None.

Validation

  • obs_mode must be "state" or "visual"
  • reset_settle_frames must be a nonnegative integer
  • When obs_mode="visual", observations must contain at least one camera component (WristCamera or OverheadCamera)
  • Duplicate camera component types are not allowed

Episode Length

max_episode_steps is owned by the Gymnasium registration, not the config. Set it per env at construction, the same way for every task:

import gymnasium as gym

# MuJoCo (single env): applied via the TimeLimit wrapper
env = gym.make("MuJoCoTouch-v1", config=config, max_episode_steps=256)

# Warp (batched): forwarded to the vector env, which truncates internally
envs = gym.make_vec("WarpTouch-v1", num_envs=4, device="cuda", config=config, max_episode_steps=256)

Registered defaults: PickLift, PickAndPlace, and StackCube 1024, Touch 512, LookAt and Move 256. They apply to both the MuJoCo*-v1 and Warp*-v1 ids.


Observation Components

Observation components are lightweight descriptor classes passed via the observations parameter. See Observations for a conceptual overview.

State Components

ClassSizeDescription
JointPositions()6Current joint angles
JointVelocities()6Current joint angular velocities (rad/s)
JointEfforts()6Actuator force on each joint (N*m)
GripperContactForce()3World-frame resultant contact force on the fingers (N)
EndEffectorPose()7TCP position + quaternion
TargetOffset()3Vector to the goal: goal minus object in manipulation tasks, goal minus TCP elsewhere
GazeDirection()3Unit vector from the wrist camera toward the target object
GazeState()1Binary in-frame flag (target object inside the wrist camera's field of view)
GraspState()1Binary grasp flag (two-sided, opposing-normal contact)
ObjectPose()7Object position + quaternion
ObjectVelocity()6Object linear (world frame) + angular (body frame) velocity
ObjectOffset()3Vector from gripper to object
TargetPosition()3Absolute goal position

Camera Components

WristCamera

from so101_nexus import WristCamera

cam = WristCamera(width=224, height=224)
ParameterTypeDefaultDescription
widthint640Image width in pixels
heightint480Image height in pixels
fov_deg_rangetuple[float, float](60.0, 90.0)FOV randomization range in degrees
pitch_deg_rangetuple[float, float](-34.4, 0.0)Pitch randomization range in degrees
pos_x_noisefloat0.005Position noise along x-axis
pos_y_centerfloat0.04Nominal y-offset from the wrist
pos_y_noisefloat0.01Position noise along y-axis
pos_z_centerfloat-0.04Nominal z-offset from the wrist
pos_z_noisefloat0.01Position noise along z-axis

Properties: fov_rad_range, pitch_rad_range (converted to radians).

OverheadCamera

from so101_nexus import OverheadCamera

cam = OverheadCamera(width=320, height=240, fov_deg=45.0)
ParameterTypeDefaultDescription
widthint640Image width in pixels
heightint480Image height in pixels
fov_degfloat45.0Vertical field-of-view in degrees

PickConfig

Extends EnvironmentConfig with parameters for pick and lift tasks.

from so101_nexus import PickConfig, CubeObject, YCBObject

config = PickConfig(
    objects=[CubeObject(color="blue"), YCBObject("011_banana")],
    n_distractors=2,
)

Additional Parameters

These are in addition to all EnvironmentConfig parameters.

ParameterTypeDefaultDescription
objectslist[SceneObject] | SceneObject | NoneNoneObjects to pick. A single object is auto-wrapped. Defaults to [CubeObject()] when None.
n_distractorsint0Number of distractor objects to spawn
lift_thresholdfloat0.05Height above the table to count as lifted (meters)
max_goal_heightfloat0.08Maximum target lift height (meters)
min_object_separationfloat0.04Minimum distance between spawned objects (meters)

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), GraspState(), GazeState(), ObjectPose(), ObjectOffset()] (31 dimensions).


PickAndPlaceConfig

Extends EnvironmentConfig with parameters for pick-and-place tasks. The carried object is chosen per episode from an object pool: by default one cube per color in cube_colors, or pass objects to carry YCBObject / MeshObject instead.

from so101_nexus import PickAndPlaceConfig, YCBObject

# Default cube path (carries a colored cube onto the disc):
config = PickAndPlaceConfig(cube_colors="blue", target_colors="green")

# Object-pool path (carries a YCB object):
config = PickAndPlaceConfig(objects=[YCBObject("011_banana")], target_colors="green")

Additional Parameters

These are in addition to all EnvironmentConfig parameters.

ParameterTypeDefaultDescription
objectslist[SceneObject] | SceneObject | NoneNoneCarried-object pool. None derives a cube pool from the cube sugar below. Passing this together with any non-default cube sugar raises ValueError.
target_colorsColorConfig"blue"Color(s) for the target disc
target_disc_radiusfloat0.05Radius of the target disc (meters)
min_object_target_separationfloat | NoneNoneMinimum object/disc spawn separation (meters); None falls back to min_cube_target_separation
cube_colorsColorConfig"red"Color(s) for the default cube pool (compatibility sugar)
cube_half_sizefloat0.0125Half-size of the default cube(s) (meters)
cube_massfloat0.01Mass of the default cube(s) (kg)
min_cube_target_separationfloat0.0375Deprecated alias for min_object_target_separation
distractorslist[SceneObject] | SceneObject | NoneNonePool of non-target objects distractors are drawn from. Defaults to green, yellow, and purple cubes sharing cube_half_size / cube_mass
n_distractorsint0Number of distractor objects placed alongside the carried object each episode, sampled without replacement from distractors
min_object_separationfloat0.04Minimum spawn separation between the carried object and the distractors (meters), on top of their bounding radii

Distractor slots are only compiled when n_distractors > 0, so the default scene holds exactly one object. Distractors keep min_object_target_separation from the disc, so clutter never spawns on the goal. A distractor cube whose color is also a carried cube color warns, because the task description names the carried object by color alone.

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), GraspState(), GazeState(), TargetPosition(), ObjectPose(), ObjectVelocity(), ObjectOffset(), TargetOffset()] (43 dimensions).


StackCubeConfig

Extends EnvironmentConfig with parameters for the stack-cube task. Two cubes are spawned every episode: cube A (picked up and stacked) and cube B (the stationary base). Colors default to disjoint values so the two cubes are never the same color out of the box.

from so101_nexus import StackCubeConfig

config = StackCubeConfig(cube_a_colors="green", cube_b_colors="purple")

Additional Parameters

These are in addition to all EnvironmentConfig parameters.

ParameterTypeDefaultDescription
cube_a_colorsColorConfig"red"Color(s) for cube A, the cube that gets picked up and stacked
cube_b_colorsColorConfig"blue"Color(s) for cube B, the stationary stacking base
cube_half_sizefloat0.0125Half-extent of each cube (meters); both cubes share the same size
cube_massfloat0.01Mass of each cube (kg); both cubes share the same mass
min_cube_separationfloat0.04Minimum spawn separation between the two cubes (meters), on top of their combined bounding radii
stack_alignment_marginfloat0.005Alignment tolerance (meters) for the stacked success check
cube_static_lin_thresholdfloat0.01Maximum linear speed (m/s) at which cube A still counts as static for success
cube_static_ang_thresholdfloat0.5Maximum angular speed (rad/s) at which cube A still counts as static for success
distractorslist[SceneObject] | SceneObject | NoneNonePool of non-target objects distractors are drawn from. Defaults to green, yellow, and purple cubes sharing cube_half_size / cube_mass
n_distractorsint0Number of distractor objects placed alongside cubes A and B each episode, sampled without replacement from distractors

Passing overlapping cube_a_colors/cube_b_colors pools warns (the two cubes may be the same color in some episodes).

Distractor slots are only compiled when n_distractors > 0, so the default scene holds exactly two cubes. A distractor cube whose color is also a cube A/B color warns, because the task description names cubes by color alone.

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), GraspState(), GazeState(), ObjectPose(), ObjectVelocity(), ObjectOffset(), TargetPosition(), TargetOffset()] (43 dimensions).


TouchConfig

Extends PickConfig with the touch task. The touch target can be any cube, YCB object, or mesh from the object pool.

from so101_nexus import TouchConfig

config = TouchConfig(touch_margin=0.03)

Additional Parameters

This is in addition to all PickConfig object-pool parameters (objects, n_distractors, min_object_separation) and the inherited EnvironmentConfig parameters.

ParameterTypeDefaultDescription
touch_marginfloat0.03Clearance added to the target object bounding radius; success fires when the TCP is within bounding_radius + touch_margin of the object center

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), GraspState(), GazeState(), ObjectPose(), ObjectOffset()] (31 dimensions).


LookAtConfig

Extends EnvironmentConfig with parameters for the look-at primitive task.

from so101_nexus import LookAtConfig, CubeObject

config = LookAtConfig(
    objects=CubeObject(color="blue"),
    fov_deg=60.0,
)

Additional Parameters

ParameterTypeDefaultDescription
objectslist[SceneObject] | SceneObject | NoneNoneTarget object(s). Only CubeObject is supported. Defaults to [CubeObject()].
fov_degfloat | NoneNoneWrist-camera vertical FOV in degrees; success = target within fov_deg / 2 of the optical axis. None reads the live camera FOV.

The MuJoCoLookAt-v1 and WarpLookAt-v1 registrations default max_episode_steps to 256; override it at construction via gym.make(..., max_episode_steps=N) (MuJoCo) or gym.make_vec(..., max_episode_steps=N) (Warp).

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), GazeDirection(), GazeState()] (23 dimensions).


MoveConfig

Extends EnvironmentConfig with parameters for the directional move primitive task.

from so101_nexus import MoveConfig

config = MoveConfig(direction="up", target_distance=0.10)

Additional Parameters

ParameterTypeDefaultDescription
directionMoveDirection"up"Cardinal direction: "up", "down", "left", "right", "forward", "backward"
target_distancefloat0.10Distance in meters to travel from the initial TCP position
success_thresholdfloat0.01Max residual distance (m) to count as success

The MuJoCoMove-v1 and WarpMove-v1 registrations default max_episode_steps to 256; override it at construction via gym.make(..., max_episode_steps=N) (MuJoCo) or gym.make_vec(..., max_episode_steps=N) (Warp).

Default Observations

When observations is not specified: [JointPositions(), JointVelocities(), EndEffectorPose(), TargetOffset()] (22 dimensions).

On this page