Skip to main content

Introduction

Humanoid Control is the unified low-level control stack for the Berkeley Architecture Research (Humanoid Control) humanoid robots. It runs on ROS 2 Jazzy under PREEMPT_RT (shipped via the RoboStack conda channel and pixi, no system-wide ROS install required) and targets two robots with as much shared code as possible.

Task-specific packages (piano playing, etc.) ship from sibling repos that depend on Humanoid Control — see Packages reference for the split. Throughout the rest of the docs, runtime commands are shown in canonical ros2 launch … / ros2 run … form; if you prefer shorter aliases, the workspace also ships a pixi run … shortcut layer documented on How-to → Workspace shortcuts with pixi.

The two robots:

  • Lite — a bimanual humanoid with Robstride actuators on two SocketCAN buses (CAN-to-USB), one bus per arm.
  • Prime — a bimanual humanoid with eRob actuators over EtherCAT and Sito actuators over SocketCAN, running concurrently. The EtherCAT side is driven by the third-party ethercat_driver_ros2 (ICube Robotics), built on the IgH EtherLAB master.

The headline: identical controllers, message schemas, simulation tooling, and bringup launches across both robots. Robot differences live entirely in URDF and hardware-component selection.

What problem does this stack solve?

Humanoid control codebases historically split along a robot-by-robot, hardware-by-hardware axis: separate plugins for each actuator family, separate launch files per task, separate observation pipelines for sim vs. silicon. The result is N × M code paths, where N is robots and M is tasks.

Humanoid Control factors the axes orthogonally:

AxisWhere the variation lives
Robot (Lite / Prime)URDF + which <ros2_control> <plugin> is selected
Hardware tier (mock / sim / real)a single xacro arg (use_fake_hardware / use_sim)
Task (tracking / piano / locomotion)the .onnx + .mcap loaded into the one in-process RLPolicyController (the ONNX task_type selects observation terms)

Everything else is shared. Every learned policy is System 0: it runs in-process in C++ RLPolicyController inside the real-time ros2_control cycle — there is no separate policy process.

System at a glance

System at a glance

The two CAN buses on Lite are a physical split (one CAN-to-USB adapter per arm), surfaced as two <ros2_control> blocks on the real-hardware path: LiteLeftArm claims CAN ids 11..17 on can_interface_left, LiteRightArm claims 21..27 on can_interface_right. The controller_manager runs both plugin instances concurrently and exposes a single flat 14-joint list to controllers — they don't see the split. Prime adds a third path through ethercat_driver_ros2 for the eRob side.

How the project is organized

A single git repo at Berkeley-Humanoids/humanoid_control, a flat collection of ROS 2 packages (franka_ros2 / Universal_Robots_ROS2_Driver pattern):

Package organization

Notice that humanoid_controllers, humanoid_control_msgs, and humanoid_control_policy have no robot-specific code — everything robot-specific lives in humanoid_control_description_* or humanoid_bringup_*.

Design rationale (one-paragraph version)

ros2_control is the integration spine. Every joint exposes 3 state interfaces (position, velocity, effort) and 5 command interfaces (position, velocity, effort, stiffness, damping) — the MIT-mode hybrid command convention used by Cheetah, MIT Mini Cheetah, and the Berkeley Humanoids deployments. The actuator (or sim, or mock) computes torque as

tau = K_p (q_cmd - q) + K_d (q_dot_cmd - q_dot) + tau_ff

This formula is identical in our Robstride firmware, in mujoco_ros2_control::MujocoSystem, and in any controller we write — so the same update() body works against silicon, MuJoCo, and mock_components. See Architecture for the full ros2_control flow.

Reference materials

The architectural choices in this stack draw heavily on prior work. The project's AGENTS.md (kept project-local, not in the git repo) cites the full list; the most influential are:

  • rm_control — package decomposition pattern and industrial_ci workflow.
  • legged_control2 — two-tier hardware factoring (bus library / per-actuator-family plugin) that we mirror for humanoid_drivers_socketcan / humanoid_devices_robstride / humanoid_devices_sito.
  • mujoco_ros2_control — the MuJoCo ↔ ros2_control bridge whose MujocoSystem plugin we consume.
  • franka_ros2 — the flat package-collection layout this repo follows.
  • Universal_Robots_ROS2_Driver — gold-standard ros2_control hardware integration. The Universal_Robots_Client_Library / ur_robot_driver split mirrors our humanoid_drivers_socketcan / humanoid_devices_robstride split.

Next

  • Hardware specifications — joint counts, effort limits, transport details for Lite and Prime.
  • Architecture — ros2_control flow, the 5-mode FSM, and the in-process System 0 policy tier.
  • Installation — install the prebuilt packages from the berkeley-humanoids channel in ~2 minutes, or build from source.