Skip to main content

Quick reference

Dense, scannable lookup for the most common commands, topics, and parameters. The cheat sheet you print or pin to a second monitor. Every link points at the page with the full details.

All commands below assume you've entered the workspace env (e.g. cd humanoid_control_ws && pixi shell) so ros2, colcon, and the Humanoid Control console scripts are on PATH. Looking for the workspace tasks (pixi run sim, pixi run build, pixi run ping-bus, …)? See How-to → Workspace commands.

Launch invocations

Grouped by which machine they run on. See Concepts → Architecture → Deployment topology for the split rationale. Launches come from two repos:

  • Humanoid Control ships the Lite + Prime bringups (humanoid_bringup_lite, humanoid_bringup_prime), the description viewer (humanoid_bringup_lite), and the policy prepare-and-load launch (humanoid_control_policy).
  • pianist_ros2 ships the piano-task launches (pianist_bringup composes a Lite + piano MuJoCo scene; pianist_policy prepares the piano policy and runs the USB-MIDI driver).

Single-machine sim / dev (no robot, no tether)

# Drag joints in RViz — no controllers, no physics
ros2 launch humanoid_bringup_lite view_lite.launch.py

# MuJoCo sim — full controller stack, /clock from sim time
ros2 launch humanoid_bringup_lite mujoco.launch.py

# Lite + piano in MuJoCo (pianist_bringup composes the scene, spawns piano_state_bridge)
ros2 launch pianist_bringup mujoco.launch.py

# Calibrate the zero pose (writes ./calibration.yaml on Ctrl+C)
ros2 launch humanoid_bringup_lite calibrate.launch.py

Robot onboard computer (real bringup)

# Real Lite — both buses, two ros2_control blocks, gamepad + joy_teleop
ros2 launch humanoid_bringup_lite real.launch.py

# Real Lite, no gamepad attached (switch controllers via ros2 control switch_controllers)
ros2 launch humanoid_bringup_lite real.launch.py enable_gamepad:=false

# Gamepad enumerated as js1 (multiple controllers plugged into the Jetson)
ros2 launch humanoid_bringup_lite real.launch.py joy_dev:=/dev/input/js1

# Real Lite, no joy button switching (raw debug / calibration)
ros2 launch humanoid_bringup_lite real.launch.py enable_joy_teleop:=false

real.launch.py boots the real-time control plane only — visualisers live on the operator workstation; the in-process policy is prepared and loaded on the robot below.

Robot onboard computer (prepare + load a policy)

# Prepare + load the in-process tracking policy (Humanoid Control → humanoid_control_policy):
# runs `prepare` (ONNX → .mcap + overlay), then loads rl_policy_controller
# inactive. Activate it with R1+A (joy_teleop) or:
# ros2 control switch_controllers --activate rl_policy_controller --deactivate <current>

ros2 launch humanoid_control_policy lite_policy.launch.py \
wandb_run_path:=… wandb_checkpoint_name:=model.onnx

# Piano policy (pianist_ros2 → pianist_policy); task picked by ONNX task_type
ros2 launch pianist_policy piano_policy.launch.py \
wandb_run_path:=… motion_file:=/path/to/song

# USB-MIDI keyboard driver — publishes /piano/key_state (Float32MultiArray)
ros2 launch pianist_policy midi_keyboard_driver.launch.py

Operator workstation (host side of the tether)

# Live URDF + /lite/joint_states viewer (humanoid_bringup_lite)
ros2 launch humanoid_bringup_lite viz.launch.py # viser, http://0.0.0.0:8080
ros2 launch humanoid_bringup_lite viz.launch.py viewer:=rerun # native rerun window

Both machines must share ROS_DOMAIN_ID. Full surface: Launch args.

CAN bus setup

# One-time, after USB-to-CAN adapters plug in
sudo ip link set can0 down 2>/dev/null
sudo ip link set can0 up type can bitrate 1000000
sudo ip link set can1 down 2>/dev/null
sudo ip link set can1 up type can bitrate 1000000

# Check state (look for "UP" and "ERROR-ACTIVE")
ip -d link show can0
ip -d link show can1

Diagnostic CLIs

These are the workspace utility tasks — one-line wrappers over the ros2 run humanoid_devices_robstride … invocations listed in Diagnostics and utility commands.

# Scan an ID range; read-only, no Enable
pixi run scan-bus --iface can0 --scan-to 32
pixi run scan-bus --iface can1 --scan-to 32

# One-shot GetDeviceId / OperationStatus probe
pixi run ping-bus --iface can0 --id 11

# Per-joint slider window (forward_command_controller frontend; no task)
ros2 run humanoid_devices_robstride mit_slider_gui

# Live URDF + /lite/joint_states viewer — pixi run viz wraps this launch
ros2 launch humanoid_bringup_lite viz.launch.py # viser, browser at :8080
ros2 launch humanoid_bringup_lite viz.launch.py viewer:=rerun # native rerun window

# Single-machine sim/dev shortcuts for the raw viewer executables (no task)
ros2 run humanoid_bringup_lite viser_viz
ros2 run humanoid_bringup_lite rerun_viz

Mode-FSM gamepad bindings (Xbox layout)

The stock joy_teleop node (from teleop_tools) maps each button directly to /controller_manager/switch_controller — there is no FSM. Every binding activates one controller and deactivates its siblings (flat, BEST_EFFORT); any binding works from any state, with no gating and no ordering.

ButtonsActivates
Xdamping_controller
L1 + Astandby_controller_a
L1 + Bstandby_controller_b
L1 + Ystandby_controller_y
R1 + Arl_policy_controller (locomotion)
R1 + Bremote_policy_controller
BACKzero_torque_controller (STOP)

BACK selects zero_torque_controller — it does not shut the process down; CAN Disable still happens on Ctrl+C via the hardware on_deactivate. Pose and policy are independent, and every transition is allowed from every state — from any standby pose (or any other state) R1+A → locomotion or R1+B → REMOTE, and you can switch directly between poses. See Manual controller switching for the equivalent ros2 control calls.

Manual controller switching (no FSM)

These are interactive ros2 control calls:

# ZERO_TORQUE → DAMPING
ros2 control switch_controllers \
--deactivate zero_torque_controller \
--activate damping_controller

# any state → STANDBY Pose A (interpolates from the measured pose toward the
# Pose A target, constant PD from t=0, no stiffness ramp — safe from any state;
# standby_controller_b / _y are the other poses)
ros2 control switch_controllers \
--deactivate damping_controller \
--activate standby_controller_a

# Back to safe
ros2 control switch_controllers \
--deactivate <whatever>_controller \
--activate zero_torque_controller

Always end a session with zero_torque_controller active before Ctrl+C-ing the launch.

Topics you actually echo

TopicTypeRateWhen
/lite/joint_statessensor_msgs/JointState50 Hz real / 200 Hz simalways (joint_state_broadcaster, remapped at bringup)
/imu/datasensor_msgs/Imusensor-ratealways; RELIABLE
/safety_statushumanoid_control_msgs/SafetyStatuson-change, latchedTRANSIENT_LOCAL; source field per bus
/standby_controller_a/statehumanoid_control_msgs/StandbyStateactive-onlyTRANSIENT_LOCAL; Pose A
/standby_controller_b/statehumanoid_control_msgs/StandbyStateactive-onlyTRANSIENT_LOCAL; Pose B
/standby_controller_y/statehumanoid_control_msgs/StandbyStateactive-onlyTRANSIENT_LOCAL; Pose Y
/remote_policy_controller/commandhumanoid_control_msgs/MITCommandsource ratewhen a System 1/2 source (gravity-comp, VLA) feeds remote_policy_controller
/piano/key_statestd_msgs/Float32MultiArraysensor / sim ratepiano runs only (RELIABLE + KEEP_LAST(1)); live key state, in-process key_pressed term
/joysensor_msgs/Joysensor-ratewhen enable_gamepad:=true (default)

Common one-liners

# Live controller state
ros2 control list_controllers

# Rate of /lite/joint_states (50 Hz real, 200 Hz MuJoCo)
ros2 topic hz /lite/joint_states

# Read the current pose (post-calibration, joint frame)
ros2 topic echo --once /lite/joint_states

# Safety status of every active bus
ros2 topic echo --once /safety_status

# Switch controllers without a gamepad (any transition, from any state)
ros2 control switch_controllers --activate damping_controller --deactivate zero_torque_controller
ros2 control switch_controllers --activate standby_controller_a --deactivate damping_controller

# Fake a System 1/2 MITCommand publish (when remote_policy_controller is active in MuJoCo)
ros2 topic pub --once /remote_policy_controller/command \
humanoid_control_msgs/msg/MITCommand "{header: {stamp: now}, joint_names: [...], ...}"

Services for FSM transitions

The /humanoid_control/mode/* std_srvs/Trigger services were removed along with the FSM. Switch controllers directly instead — every transition is allowed from every state (activate one controller, deactivate the current one):

Former intentCommand
DAMPros2 control switch_controllers --activate damping_controller --deactivate <current>
STANDBY Aros2 control switch_controllers --activate standby_controller_a --deactivate <current>
STANDBY Bros2 control switch_controllers --activate standby_controller_b --deactivate <current>
STANDBY Yros2 control switch_controllers --activate standby_controller_y --deactivate <current>
LOCOMOTIONros2 control switch_controllers --activate rl_policy_controller --deactivate <current>
REMOTEros2 control switch_controllers --activate remote_policy_controller --deactivate <current>
STOPros2 control switch_controllers --activate zero_torque_controller --deactivate <current>

The gamepad does exactly this via joy_teleop.

The Lite joint table

14 actuated DOFs across two buses. Order is canonical (see Concepts → Frozen schemas).

IdxJointCAN idBusModel
0left_shoulder_pitch11can0rs-02
1left_shoulder_roll12can0rs-00
2left_shoulder_yaw13can0rs-00
3left_elbow_pitch14can0rs-00
4left_wrist_yaw15can0rs-05
5left_wrist_roll16can0rs-05
6left_wrist_pitch17can0rs-05
7right_shoulder_pitch21can1rs-02
8right_shoulder_roll22can1rs-00
9right_shoulder_yaw23can1rs-00
10right_elbow_pitch24can1rs-00
11right_wrist_yaw25can1rs-05
12right_wrist_roll26can1rs-05
13right_wrist_pitch27can1rs-05

Full table (effort / current limits / per-joint K/D) in Hardware specs → Joint table.

MIT-mode command convention

Every plugin (Robstride, Sito, MujocoSystem) computes torque as:

τ = K_p · (q_cmd − q) + K_d · (q̇_cmd − q̇) + τ_ff

Five command interfaces per joint: position, velocity, effort, stiffness, damping. Three state interfaces: position, velocity, effort. See Concepts → MIT command surface.

Frequent failure modes (one-liners)

SymptomFirst thing to check
ENOBUFS / Network is down warningsMotor power off → frames don't ACK → qdisc fills. Power the motors.
/lite/joint_states shows exactly 0.0 for every jointMotors un-Enabled (no power, or Enable frame dropped). Check /safety_status flags.
Launch dies with "joy_dev:=/dev/input/jsN does not exist"enable_gamepad:=true is the default and the bringup hard-fails when the resolved joystick path is missing. Plug a gamepad in, pass joy_dev:=<actual path> (the error message lists any other /dev/input/js* it found), or pass enable_gamepad:=false.
A gamepad button doesn't switch the controllerCheck joy_teleop is running and the target controller is loaded; read the active one with ros2 control list_controllers.
ros2 topic echo /safety_status reports flags ≠ 0Check Concepts → Safety pipeline for the bit definitions.

Full guidance: Troubleshooting.