MicroDuck 61-Dimensional Observation Space Structure Explained

The 61-dimensional observation space in MicroDuck RL environments is a fixed-order concatenation of base angular velocity, projected gravity, joint positions and velocities, previous actions, and command inputs, enabling policy portability across walking, stand-up, and roller tasks.

The microduck_rl repository implements a standardized observation vector that every reinforcement learning task shares. This flat tensor—referred to as the "actor" observation—is constructed in src/mjlab_microduck/tasks/symmetry.py and consumed by all task configurations including velocity tracking, stand-up recovery, and roller skating.

61-Dimensional Observation Space Breakdown

The observation vector is assembled by stacking seven sensor and command groups in a strict index order. Deviating from this layout breaks policy compatibility.

Index Range Component Dimensions Description
0 – 2 base_ang_vel 3 Body-frame angular velocity (roll, pitch, yaw) from the IMU
3 – 5 projected_gravity 3 Gravity direction vector expressed in body coordinates
6 – 19 joint_pos_rel 14 Joint positions relative to default pose
20 – 33 joint_vel_rel 14 Joint velocities relative to default pose
34 – 47 last_action 14 Action vector applied in the previous simulation step
48 – 50 twist command 3 Desired body-frame velocity (linear x, linear y, angular z)
51 – 54 head command 4 Incremental head orientation deltas (neck pitch, head pitch, yaw, roll)
55 – 60 body command 6 Incremental whole-body pose deltas (position x/y/z, orientation roll/pitch/yaw)

The source code explicitly documents this ordering in symmetry.py lines 9-18:

[0:3]   base_ang_vel
[3:6]   projected_gravity
[6:20]  joint_pos_rel
[20:34] joint_vel_rel
[34:48] last_action
[48:51] twist command
[51:55] head command
[55:61] body command

Joint Ordering Convention

The 14 joints in joint_pos_rel (indices 6-19) and joint_vel_rel (indices 20-33) follow a consistent left-to-right pattern:

  • left_hip_yaw
  • left_hip_roll
  • left_hip_pitch
  • left_knee
  • left_ankle
  • neck_pitch
  • head_pitch
  • head_yaw
  • head_roll
  • right_hip_yaw
  • right_hip_roll
  • right_hip_pitch
  • right_knee
  • right_ankle

This ordering enables symmetry augmentation: the microduck_vel_symmetry utilities in symmetry.py permute and sign-flip specific indices when mirroring left and right limbs.

Why the Fixed Structure Matters

Policy portability — A model trained on one task (e.g., velocity tracking) can execute on another (e.g., roller skating) without retraining the observation encoder. The neural network input size and semantic meaning remain constant.

Symmetry operations — The permutation tables in src/mjlab_microduck/tasks/symmetry.py rely on predictable index positions to implement data augmentation for bilateral symmetry.

Curriculum safety — Reward terms in any *_env_cfg.py file can safely reference observation slots by index, knowing the layout is invariant across task variants.

Reading the Observation Shape in Code

import numpy as np
from mjlab_microduck.tasks.microduck_velocity_env_cfg import make_microduck_velocity_env_cfg

cfg = make_microduck_velocity_env_cfg(play=False, rough=False)
env = cfg.make()
obs = env.reset()                     # obs is a dict with "actor" and "critic"

print(obs["actor"].shape)             # → torch.Size([1, 61])

print(obs["actor"][0, :3])            # base angular velocity (roll, pitch, yaw)

print(obs["actor"][0, 6:20])          # relative joint positions

print(obs["actor"][0, 48:51])         # twist command (vx, vy, omega_z)

Key Source Files

  • src/mjlab_microduck/tasks/symmetry.py — Defines the 61-D actor observation layout and symmetry permutation/sign tables
  • src/mjlab_microduck/tasks/mdp.py — Registers observation groups (actor, critic) and constructs the flat tensors
  • src/mjlab_microduck/tasks/*_env_cfg.py — Task-specific configurations referencing the shared observation layout
  • tests/test_spin_cfg.py — Unit tests asserting the observation layout remains stable across task variants

Summary

  • The 61-dimensional observation space is a concatenated vector with fixed index ranges for each sensor group
  • Base IMU data occupies indices 0-5 (angular velocity + projected gravity)
  • Joint state spans indices 6-33 (14 positions + 14 velocities, relative to default pose)
  • Action history sits at indices 34-47 for policy stability
  • Command inputs fill indices 48-60 across twist, head, and body command groups
  • All task configurations in microduck_rl share this layout to guarantee policy interoperability

Frequently Asked Questions

How do I extract specific joint angles from the 61-dimensional observation?

Slice indices 6-19 for joint_pos_rel or 20-33 for joint_vel_rel. Use the documented joint ordering: left hip chain (5 joints), neck and head (4 joints), then right hip chain (5 joints). The neck_pitch at index 6 separates left and right limbs.

Can I modify the observation space size for custom tasks?

No—this breaks the policy portability contract. All task configurations in microduck_rl enforce the 61-D structure. Add derived features through wrapper observations or modify symmetry.py only if you retrain all policies from scratch.

What is the difference between "actor" and "critic" observations?

The "actor" observation (61-D) is the fixed policy input described here. The "critic" observation may contain additional privileged information for value estimation, such as contact forces or terrain height maps, depending on the task configuration in mdp.py.

Why are joint positions relative to a default pose rather than absolute?

Relative positions improve policy generalization across different initial configurations and reduce the input distribution shift when the robot starts from varied poses. The default pose is defined in the asset configuration and subtracted before populating indices 6-19.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →