# What Are the 14 Joints Controlled by the Microduck Robot?

> Discover the 14 joints controlled by the Microduck robot, including leg, neck, and head degrees of freedom, all managed by reinforcement learning in the pollen-robotics/microduck_rl repository.

- Repository: [Pollen Robotics/microduck_rl](https://github.com/pollen-robotics/microduck_rl)
- Tags: getting-started
- Published: 2026-09-08

---

**The Microduck robot controls 14 servo-driven actuated joints—comprising bilateral leg joints (hip yaw/roll/pitch, knee, ankle), neck pitch, and three head degrees of freedom (pitch, yaw, roll)—that are directly commanded by reinforcement learning policies in the `pollen-robotics/microduck_rl` repository.**

The `pollen-robotics/microduck_rl` repository implements a physics-based reinforcement learning platform where precise control of **Microduck robot joints** is essential for locomotion and head orientation tasks. These 14 controllable degrees of freedom, referred to as the "I4" or servo joints, form the complete set of actuators used during training and inference, producing a stable 61-dimensional observation layout across all tasks.

## Complete Joint Index Reference

The 14 joints follow a strict ordering from 0 to 13, as defined in the MJCF model files. This index mapping remains consistent across all observation vectors and action commands.

| Index | Joint Name | Description |
|------:|------------|-------------|
| 0 | `left_hip_yaw` | Hip yaw on the left leg |
| 1 | `left_hip_roll` | Hip roll on the left leg |
| 2 | `left_hip_pitch` | Hip pitch on the left leg |
| 3 | `left_knee` | Knee joint on the left leg |
| 4 | `left_ankle` | Ankle joint on the left leg |
| 5 | `neck_pitch` | Pitch of the neck |
| 6 | `head_pitch` | Pitch of the head |
| 7 | `head_yaw` | Yaw of the head |
| 8 | `head_roll` | Roll of the head |
| 9 | `right_hip_yaw` | Hip yaw on the right leg |
| 10 | `right_hip_roll` | Hip roll on the right leg |
| 11 | `right_hip_pitch` | Hip pitch on the right leg |
| 12 | `right_knee` | Knee joint on the right leg |
| 13 | `right_ankle` | Ankle joint on the right leg |

This layout places all left leg joints (0-4) first, followed by the neck and head joints (5-8), then the right leg joints (9-13).

## Implementation in the Source Code

### MJCF Definition Files

The joint definitions reside in [`src/mjlab_microduck/robot/microduck/robot_walk.xml`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/robot/microduck/robot_walk.xml), where each of the 14 servo joints is declared with actuator properties. Complementary default actuator parameters, including damping and friction settings, are specified in [`src/mjlab_microduck/robot/microduck/joints_properties.xml`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/robot/microduck/joints_properties.xml).

### Actuator Targeting Logic

In [`src/mjlab_microduck/robot/microduck/microduck_constants.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/robot/microduck/microduck_constants.py), the system distinguishes servo joints from passive elements (such as wheels or backlash hinges) using the regular expression:

```python
target_names_expr = (r"^(?!passive_).*",)

```

This pattern ensures that only the 14 controllable joints are matched for policy actions, excluding any passive components.

### Joint Access Helpers

The file [`src/mjlab_microduck/tasks/mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/tasks/mdp.py) exposes the indices through dedicated helper functions:

- `_servo_joint_ids(env, asset)` – Returns the list of indices for the 14 servo joints
- `_servo_joint_pos(env, asset)` – Returns current joint positions as a `(num_envs, 14)` tensor
- `_servo_joint_vel(env, asset)` – Returns current joint velocities

## Practical Code Examples

To retrieve the current positions of all actuated joints:

```python

# Inside a task implementation or reward function

joint_pos = _servo_joint_pos(env, asset)          # shape: (num_envs, 14)

left_ankle_pos = joint_pos[:, 4]                  # index 4 → left_ankle

right_ankle_pos = joint_pos[:, 13]                # index 13 → right_ankle

```

To programmatically list all servo joint names:

```python
servo_ids = _servo_joint_ids(env, asset)
servo_names = [asset.joint_name[i] for i in servo_ids]
print(servo_names)

# → ['left_hip_yaw', 'left_hip_roll', ..., 'right_ankle']

```

When sending commands, the 13-dimensional command block uses the structure `[twist(3), head_pose(4), body_pose(6)]`. The `body_pose` segment contains targets for the leg joints. For example, to command the left knee (servo index 3):

```python
command = np.zeros(13)
command[6] = 0.2   # target for left_knee (offset 6 in command vector)

env.set_action(command)

```

## Observation and Control Integration

The 14 joints generate a stable 61-dimensional observation layout across all tasks in the repository. This consistent indexing allows reinforcement learning policies to maintain state continuity between environment resets and episode rollouts. All joint observations use the ordering defined in the MJCF files, ensuring that the physical simulation remains synchronized with the policy's action outputs.

## Summary

- The Microduck robot controls **14 servo-driven joints** organized as left leg (5), head/neck (4), and right leg (5).
- Joint indices 0-13 are defined in [`robot_walk.xml`](https://github.com/pollen-robotics/microduck_rl/blob/main/robot_walk.xml) and accessible via `_servo_joint_ids()` in [`mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/mdp.py).
- The regex `r"^(?!passive_).*"` in [`microduck_constants.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/microduck_constants.py) filters for actuated joints only.
- Joint states feed into a **61-dimensional observation vector** used by RL policies.
- Commands use a 13-dimensional vector where leg targets occupy the `body_pose(6)` segment.

## Frequently Asked Questions

### What is the difference between servo joints and passive joints in Microduck?

**Servo joints** are the 14 actuated degrees of freedom directly controlled by reinforcement learning policies, while **passive joints** include components like wheels or backlash hinges that are not actively commanded. The codebase uses the regular expression `r"^(?!passive_).*"` in [`microduck_constants.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/microduck_constants.py) to exclude passive elements from actuator targeting.

### How are the 14 joints ordered in the observation vector?

The observation vector follows the index order: left leg joints (indices 0-4), neck and head joints (5-8), and right leg joints (9-13). This ordering is hardcoded in the MJCF definition and exposed through the `_servo_joint_ids()` helper function in [`mdp.py`](https://github.com/pollen-robotics/microduck_rl/blob/main/mdp.py).

### Where can I find the physical limits for each joint?

Physical limits, damping coefficients, and friction parameters for the 14 joints are defined in [`src/mjlab_microduck/robot/microduck/joints_properties.xml`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/robot/microduck/joints_properties.xml). The complete kinematic structure and joint names are declared in [`src/mjlab_microduck/robot/microduck/robot_walk.xml`](https://github.com/pollen-robotics/microduck_rl/blob/main/src/mjlab_microduck/robot/microduck/robot_walk.xml).

### Which joints are included in the 13-dimensional command block?

The command block structure `[twist(3), head_pose(4), body_pose(6)]` maps to specific servo joints. The `body_pose` segment controls the leg joints (hips, knees, ankles), while `head_pose` commands the neck pitch and head orientation joints. The exact mapping offsets depend on the specific joint index within the servo list.