---
title: Configuration Classes
description: Reference for all configuration classes.
---

# 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 image resolution for the visualization render camera (not observations).

```python
from so101_nexus import RenderConfig

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

### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `width` | `int` | `640` | Render image width in pixels |
| `height` | `int` | `480` | Render image height in pixels |

---

## Pose

Defines a named robot arm configuration with fixed and free joints. See [Initial Poses](/docs/concepts/poses) for a conceptual overview.

```python
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

| Parameter | Type | Description |
|-----------|------|-------------|
| `name` | `str` | Human-readable identifier |
| `shoulder_pan_deg` | `float \| tuple[float, float]` | Shoulder pan angle or range (degrees) |
| `shoulder_lift_deg` | `float \| tuple[float, float]` | Shoulder lift angle or range (degrees) |
| `elbow_flex_deg` | `float \| tuple[float, float]` | Elbow flex angle or range (degrees) |
| `wrist_flex_deg` | `float \| tuple[float, float]` | Wrist flex angle or range (degrees) |
| `wrist_roll_deg` | `float \| tuple[float, float]` | Wrist roll angle or range (degrees) |
| `gripper_deg` | `float \| tuple[float, float]` | Gripper angle or range (degrees) |

### Methods

| Method | Returns | Description |
|--------|---------|-------------|
| `sample(rng)` | `tuple[float, ...]` | Sample concrete joint angles in degrees |
| `sample_rad(rng)` | `tuple[float, ...]` | Sample concrete joint angles in radians |

### Built-in Poses

| Name | Constant | Description |
|------|----------|-------------|
| `"rest"` | `REST_POSE` | Arm curled in rest position, free shoulder_pan/wrist_roll/gripper |
| `"extended"` | `EXTENDED_POSE` | Arm extended forward, free shoulder_pan/wrist_roll/gripper |

Access via the `POSES` dict or by constant:

```python
from so101_nexus import POSES, REST_POSE

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

---

## RobotConfig

Controls the arm rest pose and grasping thresholds.

```python
from so101_nexus import RobotConfig

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

### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `rest_qpos_deg` | `tuple[float, ...]` | `(0.0, -90.0, 90.0, 37.8152144786, 0.0, -63.0253574644)` | Rest joint positions in degrees, one per joint |
| `init_pose` | `str \| Pose \| None` | `None` | Initial pose for resets. String looks up from `POSES`, `Pose` instance used directly, `None` uses legacy rest + noise. |
| `grasp_force_threshold` | `float` | `0.5` | Minimum contact force to count as a grasp |
| `static_vel_threshold` | `float` | `0.2` | Maximum velocity to consider the arm stationary |

### Properties and methods

| Property | Type | Description |
|----------|------|-------------|
| `rest_qpos_rad` | `tuple[float, ...]` | `rest_qpos_deg` converted to radians |
| `rest_qpos` | `tuple[float, ...]` | Alias for `rest_qpos_rad` |
| `resolve_pose()` | `Pose \| None` | Returns 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.

| Parameter | Type | Description |
|-----------|------|-------------|
| `base_quat` | `tuple[float, float, float, float]` | Base orientation quaternion (w, x, y, z) |
| `sensor_cam_eye_pos` | `tuple[float, float, float]` | Sensor camera eye position |
| `sensor_cam_target_pos` | `tuple[float, float, float]` | Sensor camera target position |
| `human_cam_eye_pos` | `tuple[float, float, float]` | Human camera eye position |
| `human_cam_target_pos` | `tuple[float, float, float]` | Human camera target position |
| `wrist_camera_mount_link` | `str` | Link name for wrist camera mounting |
| `wrist_cam_pos_center` | `tuple[float, float, float]` | Center position for the wrist camera |
| `wrist_cam_pos_noise` | `tuple[float, float, float]` | Position noise applied to the wrist camera |
| `wrist_cam_euler_center_deg` | `tuple[float, float, float]` | Center wrist camera Euler angles in degrees |
| `wrist_cam_euler_noise_deg` | `tuple[float, float, float]` | Euler angle noise applied to the wrist camera |

---

## RewardConfig

Defines reward component weights and computes the shaped reward.

```python
from so101_nexus import RewardConfig

reward = RewardConfig(reaching=0.25, grasping=0.25, task_objective=0.40, completion_bonus=0.10)
```

### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `reaching` | `float` | `0.25` | Weight for the reaching component |
| `grasping` | `float` | `0.25` | Weight for the grasping component |
| `task_objective` | `float` | `0.40` | Weight for the task-specific objective |
| `completion_bonus` | `float` | `0.10` | Weight for the task completion bonus |
| `action_delta_penalty` | `float` | `0.0` | Penalty coefficient on L2 norm of consecutive action deltas |
| `energy_penalty` | `float` | `0.0` | Penalty coefficient on L2 norm of the action vector |
| `tanh_shaping_scale` | `float` | `5.0` | Scale factor for tanh reward shaping |

### 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()`

```python
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.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `reach_progress` | `float` | required | Progress toward the target (0 to 1) |
| `is_grasped` | `bool` | required | Whether the object is currently grasped |
| `task_progress` | `float` | required | Progress on the task objective (0 to 1) |
| `is_complete` | `bool` | required | Whether the task is fully complete |
| `action_delta_norm` | `float` | `0.0` | Norm of the action delta for penalty |
| `energy_norm` | `float` | `0.0` | Norm of energy usage for penalty |

Returns a single `float` reward value.

---

## EnvironmentConfig

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

```python
from so101_nexus import EnvironmentConfig

config = EnvironmentConfig(obs_mode="state")
```

### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `render` | `RenderConfig \| None` | `None` | Render camera resolution settings (visualization only). Uses `RenderConfig()` defaults when `None`. |
| `reward` | `RewardConfig \| None` | `None` | Reward weights. Uses `RewardConfig()` defaults when `None`. |
| `robot` | `RobotConfig \| None` | `None` | Robot settings. Uses `RobotConfig()` defaults when `None`. |
| `ground_colors` | `ColorConfig` | `"gray"` | Ground plane color(s) |
| `reset_settle_frames` | `int` | `5` | No-op environment frames advanced after reset before returning the first observation |
| `goal_thresh` | `float` | `0.025` | Distance threshold for goal completion (meters) |
| `spawn_half_size` | `float` | `0.05` | Half-size of object spawn region |
| `spawn_center` | `tuple[float, float]` | `(0.15, 0.0)` | Center of the spawn region (x, y) |
| `spawn_min_radius` | `float` | `0.10` | Minimum spawn distance from the robot base |
| `spawn_max_radius` | `float` | `0.30` | Maximum spawn distance from the robot base |
| `spawn_angle_half_range_deg` | `float` | `90.0` | Half-range of spawn angle in degrees |
| `obs_mode` | `ObsMode` | `"state"` | Observation mode: `"state"` or `"visual"` |
| `robot_colors` | `ColorConfig` | `"yellow"` | Robot body color(s) |
| `robot_init_qpos_noise` | `float` | `0.02` | Noise added to initial joint positions |
| `observations` | `list[Observation] \| None` | `None` | Observation 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:

```python
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 and PickAndPlace 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](/docs/concepts/observations-and-camera-modes) for a conceptual overview.

### State Components

| Class | Size | Description |
|-------|------|-------------|
| `JointPositions()` | 6 | Current joint angles |
| `EndEffectorPose()` | 7 | TCP position + quaternion |
| `TargetOffset()` | 3 | Vector from gripper to goal |
| `GazeDirection()` | 3 | Unit vector toward target |
| `GraspState()` | 1 | Binary grasp flag |
| `ObjectPose()` | 7 | Object position + quaternion |
| `ObjectOffset()` | 3 | Vector from gripper to object |
| `TargetPosition()` | 3 | Absolute goal position |

### Camera Components

#### `WristCamera`

```python
from so101_nexus import WristCamera

cam = WristCamera(width=224, height=224)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `width` | `int` | `640` | Image width in pixels |
| `height` | `int` | `480` | Image height in pixels |
| `fov_deg_range` | `tuple[float, float]` | `(60.0, 90.0)` | FOV randomization range in degrees |
| `pitch_deg_range` | `tuple[float, float]` | `(-34.4, 0.0)` | Pitch randomization range in degrees |
| `pos_x_noise` | `float` | `0.005` | Position noise along x-axis |
| `pos_y_center` | `float` | `0.04` | Nominal y-offset from the wrist |
| `pos_y_noise` | `float` | `0.01` | Position noise along y-axis |
| `pos_z_center` | `float` | `-0.04` | Nominal z-offset from the wrist |
| `pos_z_noise` | `float` | `0.01` | Position noise along z-axis |

Properties: `fov_rad_range`, `pitch_rad_range` (converted to radians).

#### `OverheadCamera`

```python
from so101_nexus import OverheadCamera

cam = OverheadCamera(width=320, height=240, fov_deg=45.0)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `width` | `int` | `640` | Image width in pixels |
| `height` | `int` | `480` | Image height in pixels |
| `fov_deg` | `float` | `45.0` | Vertical field-of-view in degrees |

---

## PickConfig

Extends `EnvironmentConfig` with parameters for pick and lift tasks.

```python
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.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `objects` | `list[SceneObject] \| SceneObject \| None` | `None` | Objects to pick. A single object is auto-wrapped. Defaults to `[CubeObject()]` when `None`. |
| `n_distractors` | `int` | `0` | Number of distractor objects to spawn |
| `lift_threshold` | `float` | `0.05` | Height above the table to count as lifted (meters) |
| `max_goal_height` | `float` | `0.08` | Maximum target lift height (meters) |
| `min_object_separation` | `float` | `0.04` | Minimum distance between spawned objects (meters) |

### Default Observations

When `observations` is not specified: `[JointPositions(), EndEffectorPose(), GraspState(), ObjectPose(), ObjectOffset()]` (24 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 colour
in `cube_colors`, or pass `objects` to carry `YCBObject` / `MeshObject` instead.

```python
from so101_nexus import PickAndPlaceConfig, YCBObject

# Default cube path (carries a coloured 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.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `objects` | `list[SceneObject] \| SceneObject \| None` | `None` | Carried-object pool. `None` derives a cube pool from the cube sugar below. Passing this together with any non-default cube sugar raises `ValueError`. |
| `target_colors` | `ColorConfig` | `"blue"` | Color(s) for the target disc |
| `target_disc_radius` | `float` | `0.05` | Radius of the target disc (meters) |
| `min_object_target_separation` | `float \| None` | `None` | Minimum object/disc spawn separation (meters); `None` falls back to `min_cube_target_separation` |
| `cube_colors` | `ColorConfig` | `"red"` | Color(s) for the default cube pool (compatibility sugar) |
| `cube_half_size` | `float` | `0.0125` | Half-size of the default cube(s) (meters) |
| `cube_mass` | `float` | `0.01` | Mass of the default cube(s) (kg) |
| `min_cube_target_separation` | `float` | `0.0375` | Deprecated alias for `min_object_target_separation` |

### Default Observations

When `observations` is not specified: `[JointPositions(), EndEffectorPose(), GraspState(), TargetPosition(), ObjectPose(), ObjectOffset(), TargetOffset()]` (30 dimensions).

---

## TouchConfig

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

```python
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.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `touch_margin` | `float` | `0.03` | Clearance 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(), EndEffectorPose(), GraspState(), ObjectPose(), ObjectOffset()]` (24 dimensions).

---

## LookAtConfig

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

```python
from so101_nexus import LookAtConfig, CubeObject

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

### Additional Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `objects` | `list[SceneObject] \| SceneObject \| None` | `None` | Target object(s). Only `CubeObject` is supported. Defaults to `[CubeObject()]`. |
| `fov_deg` | `float \| None` | `None` | Wrist-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(), EndEffectorPose(), GazeDirection()]` (16 dimensions).

---

## MoveConfig

Extends `EnvironmentConfig` with parameters for the directional move primitive task.

```python
from so101_nexus import MoveConfig

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

### Additional Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `direction` | `MoveDirection` | `"up"` | Cardinal direction: `"up"`, `"down"`, `"left"`, `"right"`, `"forward"`, `"backward"` |
| `target_distance` | `float` | `0.10` | Distance in metres to travel from the initial TCP position |
| `success_threshold` | `float` | `0.01` | Max 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(), EndEffectorPose(), TargetOffset()]` (16 dimensions).
