Reachy 2023 Camera Focal Length: Why It’s Missing and How to Get It

Why the focal length isn’t on Kurokesu’s site

The Kurokesu C2 is a modular board-level camera with an interchangeable M12 (S-mount) lens mount. There is no single focal length to publish because the body accepts any M12 lens — 2.1 mm, 2.8 mm, 3.6 mm, and beyond. Pollen Robotics has not disclosed which specific M12 lens they fit to Reachy 2023’s head cameras in any public datasheet. That’s the dead end you hit on the product page.

Stop looking for a number. What stereo depth estimation actually needs is the full intrinsic matrix, which you obtain through calibration.

What the C2 sensor does tell you

The C2 is built around a Sony IMX290 sensor at 1920 × 1080 resolution. Sensor dimensions are 5.63 × 3.17 mm, pixel pitch is 2.9 µm × 2.9 µm. That pixel size is your bridge between the focal length that calibration gives you (in pixels) and the physical focal length of the mounted lens (in millimetres):

focal_length_mm = focal_length_px × 0.0029

If calibration returns fx ≈ 800 px, the physical lens is roughly 2.3 mm. If fx comes back around 1200 px, the lens is closer to 3.5 mm. You can always work backwards once calibration is done, but in practice you rarely need to — OpenCV operates entirely in pixel units.

Running Pollen’s multical calibration

Pollen Robotics maintains a fork of multical specifically for this workflow. The general steps:

Prepare

  • Print the ChArUco board and measure the square and marker sizes precisely in metres.
  • Edit example_boards/pollen_charuco.yaml with your measurements.
  • Unplug both cameras from Reachy’s onboard computer and connect them directly to your machine.
  • Collect image pairs — move the board through many positions, angles, and distances. Aim for at least 30–50 pairs, with board corners reaching into the frame edges.

Run the calibration

multical calibrate \
  --image_path <absolute_path_to_calib_images> \
  --boards example_boards/pollen_charuco.yaml \
  --isFisheye True

The --isFisheye True flag is not optional. It selects the equidistant projection model (Kannala-Brandt) instead of the standard pinhole model, and it is the right choice for the wide-angle M12 lenses on Reachy’s head. Using the wrong model produces distortion coefficients that look reasonable but will quietly wreck depth estimates at the frame edges.

Reading the output

Multical writes a calibration file with the intrinsic matrix for each camera. The key values look like this:

K (camera matrix):
  [[fx,  0, cx],
   [ 0, fy, cy],
   [ 0,  0,  1]]

distortion (fisheye, 4 coefficients): [k1, k2, k3, k4]

fx and fy are focal lengths in pixels. They should be very close to each other for a well-centred lens — a large gap is a sign something went wrong. cx and cy are the principal point; for a 1920 × 1080 sensor they should land near (960, 540). The four k values capture the radial distortion profile of the fisheye lens.

Checking quality

Multical reports an AVG SLOPE SCORE. Below 0.1% is acceptable; below 0.05% is good. If you land above 0.1%, add more images at sharper board angles — especially with the board tilted 30–45° toward and away from the camera — and rerun.

From intrinsics to stereo depth

Individual camera calibration gives you the intrinsic matrix and lens distortion. Stereo calibration adds the extrinsics: the rotation R and translation T between the two camera frames. In OpenCV’s fisheye module:

cv2.fisheye.stereoCalibrate(
    objectPoints, imgPoints1, imgPoints2,
    K1, D1, K2, D2,
    imageSize,
    R, T,
    flags=cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC
)

Then rectify with cv2.fisheye.stereoRectify(). After rectification, disparity maps to depth via:

depth = (fx_rectified × baseline) / disparity

where baseline is the distance between camera optical centres (taken from T in the stereo calibration output). fx_rectified comes from the rectified projection matrix P1 or P2, not your original K matrices.

One trap to avoid

OpenCV has two separate calibration paths: the standard cv2.calibrateCamera() using the Brown-Conrady distortion model, and the fisheye path under cv2.fisheye. The two are not interchangeable — distortion coefficients from one cannot be fed into functions from the other. Since Pollen’s multical command uses --isFisheye True, stay in cv2.fisheye for every subsequent step: undistortion, stereo calibration, and rectification.

Sources


Related Articles

Similar Posts