Skip to main content

Launch args

Every Humanoid Control bringup follows the same vocabulary. Each robot ships two parallel launchesreal.launch.py for silicon, mujoco.launch.py for MuJoCo physics. Operators pick the launch file rather than flipping a use_fake_hardware flag on a single one. The xacro still accepts use_fake_hardware:=true for direct ad-hoc visualization, but no bundled launch enables that path.

Launches live in two repos:

  • Humanoid Control ships the Lite + Prime control-plane bringups (humanoid_bringup_lite, humanoid_bringup_prime), the URDF inspector (humanoid_bringup_lite), and the policy prepare-and-load launches (humanoid_control_policy).
  • pianist_ros2 ships piano-task-specific launches: scene composition (pianist_bringup), the piano prepare-and-load launch, and the USB-MIDI driver (both pianist_policy).

humanoid_bringup_lite/launch/view_lite.launch.py

URDF inspector — no controller_manager, no physics.

ArgDefaultEffect
rviz_configbundled view_lite.rvizOverride with your own RViz layout.

Implicit: forces use_fake_hardware:=true on the xacro so the <ros2_control> block is harmless when robot_state_publisher parses the URDF.

humanoid_bringup_lite/launch/real.launch.py

Real-hardware Lite bringup. Loads two humanoid_devices_robstride/RobstrideSystem instances, one per physical SocketCAN bus (LiteLeftArm claims CAN ids 11..17 on the left bus, LiteRightArm claims 21..27 on the right bus).

ArgDefaultEffect
modearmsarms = 14 joints (default). arms_neck = 17 joints (requires neck silicon).
hardware_config<humanoid_bringup_lite share>/config/lite_hardware.yamlPer-machine bus + joint config. Maps the two <ros2_control> blocks to specific SocketCAN ifnames and joint IDs. Override to retarget a robot whose CAN ifnames differ.
calibration_file<humanoid_bringup_lite share>/config/calibration.yamlAbsolute path to the per-physical-robot zero-offset YAML. Pass '' for identity calibration (only the URDF direction sign flip applies, no offset). See Hardware specs → Bus-bring-up checklist for how to regenerate.
enable_mode_managertruefalse skips spawning the FSM orchestrator. Used by calibrate.launch.py and for raw-debug bringups where the operator drives controllers directly via ros2 control switch_controllers.
enable_gamepadtruetrue spawns joy_node so mode_manager can read /joy. The launch hard-fails on missing joy_dev. Pass false on a keyboardless lab box to drive the FSM via the /humanoid_control/mode/* std_srvs/Trigger services instead.
joy_dev/dev/input/js0Path passed verbatim to joy_node's dev parameter. Override when the onboard computer enumerates the gamepad as something other than js0 (multiple gamepads plugged in, udev rename). The pre-launch check fails fast when the specific path is missing and lists any other /dev/input/js* devices it can see, so the error message tells you which override to pass. Ignored when enable_gamepad:=false.

The active-policy target is picked by the START button, not a launch arg (convention: A = local policy, B = remote): R1+A (START_LOCOMOTION) activates rl_policy_controller (the in-process learned policy — tracking / piano / locomotion), R1+B (START_REMOTE) activates remote_policy_controller (the System 1/2 external-command ingress — gravity-comp today, VLA later; see Controllers).

Visualisers are not in this launch. real.launch.py is the onboard-computer entrypoint of the tethered deployment split — it publishes /robot_description and /lite/joint_states over DDS but spawns no viewers. Run ros2 launch humanoid_bringup_lite viz.launch.py on the operator workstation (see the viz.launch.py section below).

Implicit on the xacro: use_fake_hardware:=false use_sim:=false.

humanoid_bringup_lite/launch/viz.launch.py

Host-side live visualiser. Runs on the operator workstation of the tethered deployment split (not on the onboard computer). Subscribes to the /robot_description (latched) and joint-state topic that real.launch.py is publishing on the onboard side and renders the live pose locally — purely a DDS consumer, no hardware coupling.

ArgDefaultEffect
viewerviserviser (browser at http://localhost:8080, works headless, screen-records cleanly) or rerun (native window, better for time-cursor / multi-stream UX on a workstation with a display). Mirrors mjlab's --viewer flag. choices= rejects other values at parse time.
joint_state_topic/lite/joint_statesTopic the viewer subscribes to. Override for a multi-robot deployment or a non-Lite robot.

Only one viewer spawns per launch — pick at invocation. Run the launch twice in two terminals if you need both viewers simultaneously (same pattern as running two mjlab play processes upstream).

humanoid_bringup_lite/launch/calibrate.launch.py

Bundles real.launch.py with three overrides (calibration_file:='' enable_mode_manager:='false' enable_gamepad:='false') and adds the calibrate_robot observer node. The plugin runs with identity calibration so /lite/joint_states carries direction × raw_motor_pos, which is the frame the homing-offset formula expects.

ArgDefaultEffect
hardware_config<bundled lite_hardware.yaml>Forwarded to real.launch.py.
output$PWD/calibration.yamlPath the calibration observer writes on Ctrl+C. After verifying, mv it over humanoid_bringup_lite/config/calibration.yaml to make it the default for the next real.launch.py.
sweep_threshold0.5Minimum joint sweep (rad) below which the prior homing_offset is preserved instead of recomputed. Lets you re-calibrate one or two joints at a time without losing the others.

The observer reads per-joint static config (direction, lower_limit, upper_limit, can_id) from /robot_description — there's no parallel YAML config for it to drift against. The YAML schema keeps the same per-joint keys as T-K-233/Lite-Lowlevel-Python's JSON output, so values move between the two stacks unchanged.

humanoid_bringup_lite/launch/mujoco.launch.py

MuJoCo Lite bringup. Loads mujoco_ros2_control/MujocoSystem inside the mujoco_sim process (which hosts the controller_manager as a physics plugin).

ArgDefaultEffect
modearmsSame as the real bringup. arms (14 joints) or arms_neck (17 joints).
hardware_config<bundled lite_hardware.yaml>Same as the real bringup; the bus mapping is unused in MuJoCo but the joint list is read.
scenelite_dummyMJCF scene basename under lite_description/robots/lite_dummy/mjcf/. Default lite_dummy (robot only). Task packages from sibling repos (e.g. pianist_ros2's pianist_bringup) compose their own runtime scene XML and override this arg with their composed-file basename.
enable_gamepadtrueSame hard-fail-on-missing-/dev/input/js* behaviour as the real bringup.

Implicit:

  • The xacro is invoked with use_sim:=true. use_sim wins over use_fake_hardware in the xacro's <plugin> selector — see the decision tree in Packages.
  • Every node runs with use_sim_time:=true. Time advances at MuJoCo's pace via /clock.
  • humanoid_bringup_lite/config/sim_overrides.yaml is layered on top of humanoid_controllers/config/humanoid_control_lite_controllers.yaml so the real-hardware launch stays sim-time-free.

humanoid_bringup_prime/launch/real.launch.py

Stub real-hardware Prime bringup. Designed to load both ethercat_driver/EthercatDriver (eRob arms) and humanoid_devices_sito/SitoSystem (auxiliary) — two concurrent <ros2_control> blocks in the URDF.

The Prime URDF / MJCF is not yet imported from CAD — humanoid_bringup_prime is a stub today and this launch will not wire controllers until that import lands.

humanoid_bringup_prime/launch/mujoco.launch.py

Stub mirror of humanoid_bringup_lite/mujoco.launch.py for Prime, pending the Prime MJCF import.

humanoid_control_policy/launch/lite_policy.launch.py

Prepares and loads the in-process tracking-family policy. It runs humanoid_control_policy prepare synchronously (resolve the ONNX, convert the LeRobot motion to a .mcap bag, emit the rl_policy_controller overlay), then spawns rl_policy_controller inactive with that overlay into the running controller_manager. The operator's START_LOCOMOTION (R1+A / /humanoid_control/mode/start_locomotion) activates it. There is no separate runner process; the task is selected by the ONNX task_type metadata.

ArgDefaultEffect
checkpoint_file(empty)Absolute path to a local ONNX checkpoint. Mutually exclusive with wandb_run_path.
wandb_run_path(empty)W&B run path (entity/project/run_id); prepare downloads + caches the ONNX under ~/.cache/humanoid_control_policy/wandb/<run_id>/.
wandb_checkpoint_name(empty)Specific checkpoint filename inside the run (default: newest model_*).
motion_file(empty)Local LeRobot dataset directory; overrides registry_name and the ONNX dataset_repo_id.
registry_name(empty)HuggingFace LeRobot repo id override.
episode_index0LeRobot dataset episode index to convert.
out_dir~/.cache/humanoid_control_policy/launchWhere the prepared .onnx copy, .mcap bag, and rl_policy_params.yaml overlay are written (fixed so the spawner can find the overlay).

Each arg is forwarded verbatim to the prepare CLI (--checkpoint-file, --wandb-run-path, …). Sibling tracking-task families (piano, etc.) live in their own repos and register their own launch (see pianist_policy/piano_policy.launch.py below).

humanoid_control_policy/launch/lite_tracking.launch.py (alias)

Thin pass-through wrapper around lite_policy.launch.py, kept for the launch-policy-tracking pixi alias and existing scripts.

pianist_policy/launch/piano_policy.launch.py

Piano counterpart of lite_policy.launch.py: runs pianist_policy prepare (resolve the ONNX, convert the song to a key-state .mcap, emit the piano overlay), then spawns the same in-process rl_policy_controller inactive. The piano task is selected by the ONNX task_type='piano' metadata — there is no task: arg. The live key state is published separately by piano_state_bridge (sim) / midi_keyboard_driver (hardware) as std_msgs/Float32MultiArray on /piano/key_state.

Same args as lite_policy.launch.py, plus:

ArgDefaultEffect
skip_stride1Stride between lookahead frames (the training-time value).
key_state_topic/piano/key_stateLive key-state topic (Float32MultiArray) the controller subscribes to for the key_pressed extern term.

pianist_policy/launch/midi_keyboard_driver.launch.py

Starts the real-piano USB-MIDI driver standalone. Opens a MIDI input port and publishes std_msgs/Float32MultiArray (0.0/1.0 per key) on /piano/key_state — the same topic + type + QoS as the sim-side piano_state_bridge, so the in-process controller cannot tell them apart.

ArgDefaultEffect
port_name'' (auto-detect first available)MIDI input port name (or substring match). Use python -c 'import mido; print(mido.get_input_names())' to list available ports.
output_topic/piano/key_stateTopic to publish on.
publish_rate_hz50.0Publish rate for the latest pressed-state.

pianist_bringup/launch/mujoco.launch.py

MuJoCo bringup for the piano task. Composes the Lite robot and the piano MJCF into one scene file (_runtime_lite_piano.xml) inside lite_description's robots/lite_dummy/mjcf/ share dir, then delegates to humanoid_bringup_lite/mujoco.launch.py with scene:=_runtime_lite_piano. Also spawns the piano_state_bridge sim-side bridge so /piano/key_state exists on the sim path.

ArgDefaultEffect
enable_gamepadtrueForwarded to humanoid_bringup_lite/mujoco.launch.py.
modearmsForwarded.
hardware_config<humanoid_bringup_lite share>/config/lite_hardware.yamlForwarded.

scene:= is not exposed — pianist_bringup controls that internally.

xacro args (lite_description/robots/lite_dummy/xacro/lite_dummy.urdf.xacro)

The launch args ultimately feed these xacro args. Useful when driving xacro directly (e.g. in a sim2sim eval harness):

ArgDefaultEffect
use_fake_hardwaretrueSelect mock_components/GenericSystem — single combined <ros2_control> block. Only the xacro layer exposes this; no bundled launch uses it today.
use_simfalseWins over use_fake_hardware — select mujoco_ros2_control/MujocoSystem, also single combined block.
modearmsarms (14 joints) or arms_neck (17 joints). Selects which <ros2_control> block(s) the xacro emits.
hardware_config(empty)YAML the xacro reads to learn the per-machine bus + joint mapping. The launches set this; ad-hoc xacro calls usually leave it empty.
calibration_file""Passed verbatim as a <param name="calibration_file"> on both real-hardware blocks. Empty = identity calibration.

Common combinations

The launches are grouped by where they run. See Concepts → Architecture → Deployment topology for the rationale behind the split.

Single-machine dev path (sim, calibration, URDF inspection — no robot involved, no tether):

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

# MuJoCo physics, /clock from sim time.
ros2 launch humanoid_bringup_lite mujoco.launch.py

# Lite + piano in MuJoCo (pianist_bringup composes the scene).
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 (CM + hardware + FSM + gamepad — boots the real control plane, no visualisers, no policy runner):

# Real Lite, gamepad on by default. Press R1+B at STANDBY to start the remote policy.
ros2 launch humanoid_bringup_lite real.launch.py

# Same, but on a keyboardless lab box (drive the FSM via /humanoid_control/mode/* services).
ros2 launch humanoid_bringup_lite real.launch.py enable_gamepad:=false

# Gamepad enumerated as js1 instead of js0 (multiple controllers plugged in).
ros2 launch humanoid_bringup_lite real.launch.py joy_dev:=/dev/input/js1

Robot onboard computer — prepare + load a policy (the in-process inference now runs on the robot, so the prepare-time ML deps and the artifacts live here; the launches load rl_policy_controller into the local controller_manager):

# Prepare + load the tracking policy (humanoid_control_policy) from a W&B run.
ros2 launch humanoid_control_policy lite_policy.launch.py \
wandb_run_path:=user/proj/run_id \
wandb_checkpoint_name:=model_2000.onnx

# Prepare + load a piano policy (pianist_policy) against a song.
ros2 launch pianist_policy piano_policy.launch.py \
wandb_run_path:=user/proj/run_id

# USB-MIDI keyboard driver (publishes /piano/key_state locally —
# loopback to the in-process controller; does not cross the tether).
ros2 launch pianist_policy midi_keyboard_driver.launch.py

Operator workstation (host side of the tether — visualisers; talks to the robot strictly via DDS over a wired link):

# Live URDF + /lite/joint_states viewer (browser at :8080 by default).
ros2 launch humanoid_bringup_lite viz.launch.py # viser
ros2 launch humanoid_bringup_lite viz.launch.py viewer:=rerun # native rerun window

Both machines must share ROS_DOMAIN_ID and (recommended) RMW_IMPLEMENTATION.