Skip to main content

Diagnostics and utility commands

Standalone executables shipped by Humanoid Control, and the workspace pixi tasks that wrap the common ones. Every tool is reachable as ros2 run <package> <executable> — that canonical form is what the per-tool sections below document. The frequently-used ones also have a workspace task (pixi run ping-bus, pixi run scan-bus, …), each a one-line wrapper over the same canonical command.

The hc CLI was retired

Rev 1 of the workspace interface protocol shipped these tools behind a packaged hc CLI (humanoid_control_cli). Rev 2 dissolved it — the package is deleted. Its verbs became the workspace tasks in the table below, or, for rarely-used tools, plain documented ros2 run commands. See How-to → Workspace commands for the task interface itself.

Task ↔ canonical command mapping

Former hc verbTask (if any)Canonical command
hc bus pingpixi run ping-busros2 run humanoid_devices_robstride robstride_ping
hc bus discoverpixi run scan-busros2 run humanoid_devices_robstride robstride_discover
hc bus probepixi run profile-busros2 run humanoid_devices_robstride robstride_probe
hc bus probe-reportros2 run humanoid_devices_robstride robstride_probe_report
hc motor sliderros2 run humanoid_devices_robstride mit_slider_gui
hc vizpixi run vizros2 launch humanoid_bringup_lite viz.launch.py (viewer:=viser default, viewer:=rerun)
hc viz viserros2 run humanoid_bringup_lite viser_viz
hc viz rerunros2 run humanoid_bringup_lite rerun_viz
hc viz urdfros2 launch humanoid_bringup_lite view_lite.launch.py
hc calibratepixi run calibrateros2 launch humanoid_bringup_lite calibrate.launch.py

Tasks forward trailing arguments verbatim (pixi run ping-bus --iface can0 --id 11). Commands without a task are deliberate — rarely-used tools get no alias. Run them inside pixi shell as plain ros2 run …, or from any terminal as pixi run -- ros2 run ….

humanoid_control_policy and pianist_policy each ship a prepare console script (the launch-time policy-artifact prep step); pianist_policy also ships the piano_state_bridge and midi_keyboard_driver key-state nodes. These are normally driven by their launch files, but are reachable via ros2 run … too.

Index

ExecutablePackageRepoWhat it does
robstride_pinghumanoid_devices_robstrideHumanoid ControlSingle-actuator probe (GetDeviceId / OperationStatus). Read-only.
robstride_discoverhumanoid_devices_robstrideHumanoid ControlScan a CAN ID range, print every device that replies. Read-only.
robstride_probehumanoid_devices_robstrideHumanoid ControlLink RTT / jitter probe against one actuator.
robstride_probe_reporthumanoid_devices_robstrideHumanoid ControlReport companion for robstride_probe captures.
mit_slider_guihumanoid_devices_robstrideHumanoid ControlQt slider window publishing Float64MultiArray to a forward_command_controller.
joy_teleopjoy_teleop (teleop_tools)external (built from source)Stock gamepad node; maps buttons directly to /controller_manager/switch_controller. Normally launched by bringup.
calibrate_robothumanoid_bringup_liteHumanoid ControlSample (min, max) per joint; write calibration.yaml on Ctrl+C.
rerun_vizhumanoid_bringup_liteHumanoid ControlNative rerun viewer subscribed to /robot_description + /lite/joint_states.
viser_vizhumanoid_bringup_liteHumanoid ControlBrowser viewer (default port 8080). Same subscriptions.
preparehumanoid_control_policyHumanoid ControlLaunch-time prep: resolve the ONNX (local / W&B), convert the LeRobot motion → .mcap bag, emit the rl_policy_controller overlay (used by lite_policy.launch.py).
preparepianist_policypianist_ros2Piano counterpart of humanoid_control_policy prepare (song → key-state .mcap; used by piano_policy.launch.py).
piano_state_bridgepianist_policypianist_ros2Sim-side bridge — JointState piano keys → std_msgs/Float32MultiArray on /piano/key_state.
midi_keyboard_driverpianist_policypianist_ros2USB-MIDI input → /piano/key_state (std_msgs/Float32MultiArray, real-piano counterpart of the sim bridge).

Per-tool reference

robstride_ping

ros2 run humanoid_devices_robstride robstride_ping --iface can0 --id 11
ros2 run humanoid_devices_robstride robstride_ping --iface can0 --id 11 --read-status

# Equivalent one-line task wrapper:
pixi run ping-bus --iface can0 --id 11
ArgDefaultDescription
--ifacecan0SocketCAN interface
--id32Target Robstride device ID
--timeout-ms500How long to wait for the reply
--read-status(off)After GetDeviceId, also Enable → wait for OperationStatus → Disable, for a one-shot pose / fault read

Read-only when --read-status is omitted. With --read-status, the motor is briefly Enabled and Disabled — no MIT operation control, no commanded motion, but the actuator does transition Enable → Disable internally.

Used in: Tutorials → Drive one Robstride, How-to → Probe CAN bus.

robstride_discover

ros2 run humanoid_devices_robstride robstride_discover --iface can0
ros2 run humanoid_devices_robstride robstride_discover --iface can0 \
--scan-from 1 --scan-to 127 --per-id-wait-ms 8

# Equivalent one-line task wrapper:
pixi run scan-bus --iface can0
ArgDefaultDescription
--ifacecan0SocketCAN interface
--scan-from1Lowest ID to ping
--scan-to32Highest ID to ping (inclusive; clamped to 127)
--host-id253Host CAN ID used in the GetDeviceId frame
--per-id-wait-ms8Gap between successive ping sends
--drain-ms200Listen window after the last ping

Read-only — only GetDeviceId is sent. Background drain thread keeps the RX ring from filling during long scans.

Exit code: 0 if anything answered, 3 if scan completed cleanly with zero replies. Both are useful in CI.

Used in: How-to → Probe CAN bus, Hardware specs → Bus-bring-up checklist.

robstride_probe / robstride_probe_report

# Link RTT / jitter probe against one actuator:
ros2 run humanoid_devices_robstride robstride_probe --iface can0 --id 11
# Equivalent one-line task wrapper:
pixi run profile-bus --iface can0 --id 11

# Report companion — rarely used, no task:
ros2 run humanoid_devices_robstride robstride_probe_report

robstride_probe measures round-trip latency and jitter on the command → reply path for a single actuator; robstride_probe_report renders the captured data into a report. The report tool is rarely used, so it deliberately has no task wrapper.

mit_slider_gui

ros2 run humanoid_devices_robstride mit_slider_gui
ros2 run humanoid_devices_robstride mit_slider_gui \
--joint actuator_1 \
--command-topic /forward_mit_controller/commands \
--position-range -3.14 3.14 \
--kp-range 0 10

# Rarely used, no task — from outside `pixi shell`:
pixi run -- ros2 run humanoid_devices_robstride mit_slider_gui
ArgDefaultDescription
--jointactuator_1Joint name to read from /joint_states
--command-topic/forward_mit_controller/commandsFloat64MultiArray topic to publish to
--state-topic/lite/joint_statesFor the live readout
--position-range-3.14159 3.14159Slider range, rad
--velocity-range-1.0 1.0rad/s
--effort-range-1.0 1.0Nm
--kp-range0.0 10.0N·m/rad
--kd-range0.0 1.0N·m·s/rad
--default-kp2.0Initial slider value
--default-kd0.5Initial slider value

Requires python_qt_binding (installed alongside rqt_reconfigure).

Used in: Tutorials → Drive one Robstride, How-to → mit_slider_gui.

joy_teleop

The stock ROS teleop_tools gamepad node. There is no mode_manager executable and no FSM any more — joy_teleop maps gamepad buttons directly to /controller_manager/switch_controller, driven entirely by a YAML config (joy_teleop_lite.yaml / joy_teleop_biped.yaml / joy_teleop_prime.yaml). robostack-jazzy ships no joy_teleop binary, so it is built from source via humanoid_control.repos.

ros2 run joy_teleop joy_teleop --ros-args --params-file joy_teleop_lite.yaml

Each button activates one controller and deactivates its siblings (flat, BEST_EFFORT) — any transition from any state, no gating, no ordering. Default Lite-arm button map:

Button(s)Activates
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 no longer shuts the process down; CAN Disable still happens on Ctrl+C via the hardware on_deactivate. Normally launched by real.launch.py / mujoco.launch.py (when enable_joy_teleop:=true, the default). Without a gamepad, switch controllers directly with ros2 control switch_controllers --activate <name> --deactivate <name>.

Reference config pattern: qiayuanl/unitree_bringup config/g1/joy.yaml.

calibrate_robot

ros2 run humanoid_bringup_lite calibrate_robot --output ./calibration.yaml
ros2 run humanoid_bringup_lite calibrate_robot \
--output ./calibration.yaml --sweep-threshold 0.3
ArgDefaultDescription
--output(required)Path to write the resulting YAML
--sweep-threshold0.5Min sweep (rad) below which the prior homing_offset is preserved

Normally launched by calibrate.launch.py (which sets --output from a launch arg and brings up the rest of the stack) — that launch is what the pixi run calibrate task wraps. Standalone invocation is useful if you already have real.launch.py running with calibration_file:=''.

Used in: How-to → Calibrate the zero pose.

rerun_viz / viser_viz

The two live-viewer executables. On the tethered deployment they are spawned via ros2 launch humanoid_bringup_lite viz.launch.py on the operator workstation (viewer:=viser by default; viewer:=rerun for the native window) — that launch is what the pixi run viz task wraps. Direct invocation is the single-machine sim/dev shortcut (no task; run inside pixi shell or via pixi run --):

ros2 run humanoid_bringup_lite rerun_viz       # native rerun window
ros2 run humanoid_bringup_lite viser_viz # browser viewer at http://0.0.0.0:8080

Both read /robot_description (latched) once, subscribe to a --joint-state-topic (default /lite/joint_states), and render the live pose. rerun-sdk, viser, yourdfpy, and scipy ship in the workspace env.

Used in: How-to → Live viz, Concepts → Architecture → Deployment topology.

Adding a new CLI tool

For a tool that ships from one of the existing packages:

  1. Drop the source in <package>/scripts/<name>.py (Python) or <package>/src/<name>.cpp (C++).
  2. In the package's CMakeLists.txt, install it under lib/${PROJECT_NAME} without the .py extension so ros2 run finds it:
    install(
    PROGRAMS scripts/<name>.py
    DESTINATION lib/${PROJECT_NAME}
    RENAME <name>
    )
    For C++ add the executable target and install it normally — the install(TARGETS ... RUNTIME DESTINATION ...) lines.
  3. Rebuild with colcon build --symlink-install --packages-select <package>.
  4. Verify with ros2 pkg executables <package>.
  5. Only if the tool will be used often: add a one-line wrapper task to the reference block in How-to → Workspace commands so every workspace inherits the same alias. Rarely-used tools stay task-less by design.

The --symlink-install flag means Python scripts edit-loop without rebuilding — useful while iterating.