Calibrate
SUMMARY
calibrate() solves tcp_T_camera, the fixed transform between a robot's TCP (flange) and a wrist-mounted camera, from images of a static calibration board taken at a series of robot poses.
SUPPORTED TARGETS
Requires a CharucoTarget, per-corner ArUco ids are needed to recover a target pose per frame. See All Supported Targets.
UNITS
Input images are BGR ndarrays. robot_T_tcp_list and tcp_T_camera are (4, 4) SE(3) matrices with translation in meters. reprojection_error is in pixels.
The Skill
result = calibrator.calibrate(
robot_T_tcp_list,
image_list,
method="TSAI",
camera_T_target_list=[],
intrinsic_matrix=None,
distortion_coefficients=None,
output_path=None,
)There are three input modes, selected by which optional arguments are supplied:
| Mode | How to trigger | Behavior |
|---|---|---|
| Full pipeline (default) | Leave intrinsic_matrix and camera_T_target_list unset | Runs intrinsic calibration on image_list, estimates a per-frame target pose, then cv::calibrateHandEye. |
| Known intrinsics | Pass intrinsic_matrix (and optionally distortion_coefficients) | Skips the intrinsic-calibration stage; per-frame poses come from ChArUco detection + solvePnP against the given K, d. |
| Strict pass-through | Pass camera_T_target_list | Skips detection entirely. The caller is responsible for aligning robot_T_tcp_list and image_list to the supplied poses. |
The Code
Requires images of a calibration board alongside the robot's TCP pose (robot_T_tcp, as 4x4 homogeneous matrices) for each frame.
from telekinesis.axon import EyeInHandCalibrator
from telekinesis.axon.targets import CharucoTarget
target = CharucoTarget(squares_x=6, squares_y=9, square_length=0.012, marker_length=0.009)
calibrator = EyeInHandCalibrator(target)
result = calibrator.calibrate(
robot_T_tcp_list=robot_poses, # list of (4, 4) np.ndarray
image_list=images,
method="TSAI",
)
result.ok # bool
result.tcp_T_camera # (4, 4) np.ndarray
result.intrinsic_matrix
result.distortion_coefficients
result.reprojection_error
result.consistency # TargetConsistencyStats, when result.has_consistencyPass pre-computed per-frame poses
If you have camera_T_target transforms from an external source, pass them via camera_T_target_list to skip EyeInHandCalibrator's internal solvePnP (see compute_camera_T_target_list() for how to produce them):
result = calibrator.calibrate(
robot_T_tcp_list=robot_poses,
image_list=images,
camera_T_target_list=external_camera_T_target_list,
intrinsic_matrix=K,
distortion_coefficients=d,
)Hand-Eye Methods
HAND_EYE_METHOD_BY_NAME_MAP maps method names to cv::HandEyeCalibrationMethod values:
| Method | Notes |
|---|---|
"TSAI" | Default; robust general-purpose choice. |
"PARK" | |
"HORAUD" | |
"ANDREFF" | |
"DANIILIDIS" | Occasionally diverges to an outlier solution, see Compare Hand-Eye Methods for cross-method comparison. |
from telekinesis.axon.calibration import HAND_EYE_METHOD_BY_NAME_MAPHandEyeResult
| Field | Type | Description |
|---|---|---|
ok | bool | False when calibration failed; tcp_T_camera is then empty. |
tcp_T_camera | (4, 4) ndarray | Transform from TCP (flange) frame to camera frame. |
intrinsic_matrix | (3, 3) ndarray | Camera matrix, empty in the strict pass-through path when none was supplied. |
distortion_coefficients | ndarray | Distortion coefficients, may be empty, see above. |
reprojection_error | float | Intrinsic mean reprojection error (px); NaN when not computed (strict pass-through with no intrinsic stage run). |
valid_robot_T_tcp_list | list[ndarray] | Robot poses that survived detection and per-view filtering. |
valid_image_list | list[ndarray] | Images that survived detection and per-view filtering. |
camera_T_target_list | list[(4, 4) ndarray] | Per-frame target poses used in the solve. |
has_consistency | bool | True when the solve succeeded and consistency is populated. |
consistency | TargetConsistencyStats | See Eye-in-Hand State. |
See EyeInHandCalibrator for theory and best practices, and EyeInHandCalibrator State to read back the last result.

