HandUMI Setup and Calibration#
Complete this page before recording. No robot arm is required: these steps configure HandUMI, its tracking device, cameras, grippers, and workspace. Some calibrations are permanent for one physical assembly; the table/session alignment must be checked each session.
Calibration |
Repeat when |
|---|---|
Servo homing and opening width |
Servo, linkage, or gripper geometry changes |
Camera intrinsics |
Camera, resolution, or focus changes |
Controller-to-camera mount |
A controller or wrist camera mount moves |
Controller-to-TCP |
The controller/gripper mount or physical tool changes |
Table/session frame |
Each session, relocalization, or tracking reset |
1. Map HandUMI Hardware#
install.sh creates the ignored machine-local configs/rig.yaml. Inspect the
connected cameras and Feetech adapters:
handumi-setup-ports
Reconnect one physical device at a time and assign its port under cameras
or feetech in configs/rig.yaml. Robot-arm buses do not belong in this
recording setup; configure them only for real-robot teleoperation.
Set new Feetech IDs only when required:
handumi-set-servo-id --port /dev/ttyUSB0 --new-id 0
handumi-set-servo-id --port /dev/ttyUSB0 --new-id 1
Hardware mapping details
Two grippers may share one serial port only when they use different
servo_id values. With separate USB adapters, each side normally has its own
port.
A USB camera commonly exposes two /dev/video* nodes. Start with the first
node reported for each physical camera and confirm the stream. Map
left_wrist, right_wrist, and workspace explicitly in configs/rig.yaml.
Keep these machine-local paths in configs/rig.yaml; do not commit them as
portable project configuration.
2. Calibrate the Grippers#
First confirm that both encoders change smoothly while opening and closing:
handumi-calibrate-grippers monitor
Home each servo with the gripper held at mid-travel. This centers the encoder range and avoids crossing the 0/4095 wrap point:
handumi-home-servos
handumi-home-servos --side right # one side only
Then calibrate the physical opening width:
handumi-calibrate-grippers calibrate
handumi-calibrate-grippers calibrate --side right
For each side, enter the maximum opening in millimeters, place the gripper fully
open and press Enter, then fully close it and press Enter. The result is stored
in ~/.cache/handumi/calibration.yaml. Open and close each gripper again with
monitor and confirm that width increases toward fully open without flipping
or saturating.
3. Connect Tracking#
Meta Quest#
Enable Developer Mode, connect the headset over USB, authorize adb, and
install HandUMI Quest App:
wget https://github.com/robonet-ai/handumi-quest-app/releases/download/v0.2.1/handumi-quest-app-v0.2.1.apk
adb install -r handumi-quest-app-v0.2.1.apk
adb shell ip route # find the address after "src"
Set that address as meta_quest.connection.quest_ip in configs/rig.yaml.
Launch the app from Library → Unknown Sources and keep it in the foreground.
python -m handumi.tracking.meta_quest --config configs/rig.yaml
A healthy stream reports steady FPS and both controllers tracked.
PICO#
Install the XRoboToolkit PC Service and follow the current XR Robotics headset instructions. Start the PC service, then launch streaming:
bash /opt/apps/roboticsservice/runService.sh
Use 127.0.0.1:63901 for USB or the workstation IP with --pico-wifi.
Smoke-test a short capture before calibration:
handumi-record --device pico --skip-feetech \
--repo-id local/pico-smoke \
--output-dir outputs/datasets/pico-smoke \
--task "pico smoke" --num-episodes 1 --episode-time-s 10
Healthy output reports xrobotoolkit_sdk initialised without repeated
still waiting for PICO data messages.
4. Calibrate Cameras and Workspace#
Fix the 5 × 7 ChArUco board flat at its marked table position, with IDs 15 and 16 nearest the operator. Its center defines the table origin: +X right, +Y away, and +Z up.
Camera Intrinsics#
handumi-calibrate-spatial intrinsics --camera left_wrist
handumi-calibrate-spatial intrinsics --camera right_wrist
handumi-calibrate-spatial intrinsics --camera workspace
Move the board throughout each image and vary distance and inclination. The tool automatically accepts a distinct valid view every two seconds. Repeat after changing camera, resolution, or focus.
Controller-to-Camera Mounts#
Keep the board fixed. Move the complete HandUMI through varied roll, pitch, and yaw poses, pausing briefly for each automatic capture. Keep the controller tracking ring visible to the headset.
Choose the tracking device explicitly. Global options such as --device,
--pico-wifi, and --quest-ip come before the subcommand.
Meta Quest:
handumi-calibrate-spatial --device meta mount --side left
handumi-calibrate-spatial --device meta mount --side right
PICO:
handumi-calibrate-spatial --device pico --pico-mode mandos mount --side left
handumi-calibrate-spatial --device pico --pico-mode mandos mount --side right
PICO calibration relies on live XRoboToolkit snapshots, so hold the HandUMI
steady while each view is accepted. Use --pico-wifi for a wireless PICO setup.
Repeat only if a controller or wrist-camera mount moves.
Session/Table Frame#
With the board still at its marked position and the headset fixed as it will be during recording, solve the table frame for the same tracking device.
handumi-calibrate-spatial --device meta session --side left
handumi-calibrate-spatial --device meta visualize
For PICO:
handumi-calibrate-spatial --device pico --pico-mode mandos session --side left
handumi-calibrate-spatial --device pico --pico-mode mandos visualize
Inspect all cameras and both TCP trails in Rerun. The table surface must align
with z=0. If only the workspace-camera stage fails, retry it with:
handumi-calibrate-spatial workspace
Remove the board without moving the table, cameras, or headset. Repeat the
session calibration after relocalization or a tracking reset. The saved
outputs/calibration/session.yaml records tracking_device and
table_from_device; use it only with the same --device.
5. Calibrate the HandUMI Tool Tip#
Controller-to-TCP reconstructs the physical HandUMI tool-tip pose from each tracked controller. It is a property of the HandUMI gripper/tool and controller mount, not of a connected robot arm. Recalibrate only when that physical assembly changes. Fix the tip in a firm indentation. For 25 seconds, keep it fixed while rotating the tracked assembly through varied orientations. If the same calibrated HandUMI tool is used for another robot, validate and copy the result to that robot’s identity-bound calibration path; the wizard never silently assumes two physical tool assemblies are identical.
Capture and fit both sides
Select the tracking device:
TRACKING_DEVICE=meta # or pico
Capture the left side:
LEFT_RUN="outputs/tcp_pivot_left_$(date +%Y%m%d_%H%M%S)"
handumi-record --device "$TRACKING_DEVICE" --skip-feetech --only-left-camera \
--repo-id local/tcp_pivot_left --output-dir "$LEFT_RUN" \
--task "tcp pivot left" --num-episodes 1 --episode-time-s 25 \
--tracking-loss-timeout-s 3 --no-sounds
handumi-calibrate-tcp-offset pivot --device "$TRACKING_DEVICE" --side left \
--parquet "$LEFT_RUN/data/chunk-000/file-000.parquet" --episode 0 \
--output outputs/calibration/controller_tcp_candidate.yaml
Repeat for the right side:
RIGHT_RUN="outputs/tcp_pivot_right_$(date +%Y%m%d_%H%M%S)"
handumi-record --device "$TRACKING_DEVICE" --skip-feetech --only-right-camera \
--repo-id local/tcp_pivot_right --output-dir "$RIGHT_RUN" \
--task "tcp pivot right" --num-episodes 1 --episode-time-s 25 \
--tracking-loss-timeout-s 3 --no-sounds
handumi-calibrate-tcp-offset pivot --device "$TRACKING_DEVICE" --side right \
--parquet "$RIGHT_RUN/data/chunk-000/file-000.parquet" --episode 0 \
--output outputs/calibration/controller_tcp_candidate.yaml
Inspect the result:
handumi-calibrate-tcp-offset inspect \
outputs/calibration/controller_tcp_candidate.yaml
Accept a fit when RMS is below 0.50 cm, maximum error is below 1.00 cm, and condition is below 500. High RMS means the tip probably slipped; a high condition number means the capture lacked rotational variety.
Verify and promote the calibration
Before editing the project calibration, repeat a short pivot capture. Rotate
around a stationary tip and confirm the reported residual stays within the
acceptance limits. Touch the same point with both tips and confirm their
calibrated positions coincide; touching the table should place both tips near
z=0 after session calibration.
Pivot fitting calibrates translation, not orientation. Preserve the official quaternions and symmetrize only the measured positions:
x = (left.x + right.x) / 2
y = (left.y - right.y) / 2
z = (left.z + right.z) / 2
left.position = [x, y, z]
right.position = [x, -y, z]
Update only position in configs/calibration/${TRACKING_DEVICE}_controller_tcp.yaml
or the robot-specific calibration file declared in configs/robots/<robot>.yaml,
then run:
uv run pytest -q tests/tracking/test_transforms.py \
tests/scripts/test_replay_in_sim.py
Next: Record Demonstrations.