Skip to content
Copy as Markdown

Calibration Walkthrough

The problem

Manufacturing tolerances and assembly errors give every physical robot systematic geometric errors — joint zero-offsets, link length/orientation deviations from CAD, tool-center-point misalignment — typically 5-10mm at the end-effector. That's enough to make precision tasks (contact-rich manipulation, vision-guided grasping, multi-robot coordination) unreliable.

Kinematic calibration corrects this using external measurements (motion capture, a tracked marker, a camera) of the end-effector at a set of known joint configurations.

The model

P_measured = forward_kinematics(q, θ_nominal + Δθ) + ε
  • q — measured joint configurations (document any use of commanded substitutes)
  • θ_nominal — nominal kinematic parameters from the URDF
  • Δθ — parameter corrections FIGAROH solves for
  • P_measured — external sensor measurement of the end-effector (or another tracked frame) at each q
  • ε — measurement noise

FIGAROH minimizes the residual between P_measured and the predicted pose over Δθ using Levenberg-Marquardt, then reports which corrections are well- vs. poorly-observed given your measurement set.

Running it

Every robot in figaroh-examples provides a BaseCalibration subclass (e.g. TiagoCalibration) that supplies the robot-specific frame/marker configuration; the shape is the same for every robot:

from examples.tiago.utils.tiago_tools import TiagoCalibration
from figaroh.tools.robot import load_robot

robot = load_robot("path/to/robot.urdf", load_by_urdf=True)
calibrator = TiagoCalibration(robot, "config/tiago_unified_config.yaml")
calibrator.initialize()
result = calibrator.solve(plotting=False, html_report=True)

solve() always prints a terminal quality report (condition number, per-DOF residual RMSE, outlier count). html_report=True additionally writes a self-contained diagnostic page — see Reporting & Verification.

From the command line, the TIAGo example wraps this in one script:

cd examples/tiago
python calibration.py                 # full pipeline: calibrate → plot → save → export → viser viz
python calibration.py --calibrate-only  # just solve, skip URDF export
python calibration.py --update-model    # export a corrected URDF from a saved result

What you configure

The calibration: section of your unified config declares:

  • base_frame / tool_frame — the kinematic chain endpoints
  • markers — which frame(s) are measured, and which of their 6 DOF are observable (measure: [x, y, z, roll, pitch, yaw]) — a wrist-mounted 6-DOF tracker measures all six; a hand-mounted marker seen by a fixed camera might only give 3-DOF position
  • nb_sample — number of configurations in your measurement set (more isn't always better — see Optimal Experiment Design for how to pick the best set instead of the largest)
  • calibration_level and estimation — which corrections exist (joint_offset or full_params) and which of them are estimated, and how (structural by default; excitation, map, map_cv, cv_subset). With few postures this choice matters as much as the data: see Choosing what to estimate

Interpreting results

Compare nominal and calibrated prediction on unused configurations. Report translation and rotation components in explicit units; position-only or contact measurements cannot establish full pose accuracy. Analyze gauge, identifiable corrections and prior sensitivity before interpreting individual parameters.

Verification defaults are task checks, not guaranteed accuracy. Inspect which validation metrics were available, and reload any exported model to check FK against the selected calibrated stage. See Plan, Fit and Validate for acquisition, split design, residual interpretation and acceptance evidence.

Next steps