Calibrate Cameras
Calibrate camera intrinsics and stereo extrinsics on-board with voxl-calibrate-camera.
Table of contents
- Overview
- Before You Start
- Recommended Chessboard
- Command Line Options
- Monocular Calibration Process
- Stereo Calibration Process
- After Calibration
- Troubleshooting
- Legacy ROS Calibration
Overview
VOXL Camera Calibration is a package in voxl-suite >= 0.5.0 for calibrating cameras on-board on voxl. If you are on an older voxl-suite image, we recommend that you upgrade and use this tool, but you can find a link to the deprecated ROS calibration process at the bottom of this page.
Use of this tool requires the ability to view the overlay image coming off of voxl. We recommend using the VOXL Web Portal to view the overlay images.
Before You Start
Find the camera pipe name. The first argument to the calibrator is the name of the camera pipe published by voxl-camera-server, for example
tracking,tracking_front,tracking_down,stereo_front, orstereo_rear. List the pipes on your VOXL with:voxl-list-pipes- Use the right stream. Calibrate on the grayscale or raw pipe for a camera, not a color or encoded stream. The calibrator exits with
Received invalid camera formatif it is given the wrong one. - Know your board. The
-ssize is the count of inner corners, not squares, and-lis the edge length of one square in meters. Measure the printed board rather than trusting the file it was printed from. - Open the overlay before you start collecting. Run the command, open voxl-portal in a browser, and select the
camera_calibrator_overlaystream. The overlay shows the thresholded image by default, which makes lighting problems obvious. Add-rto see the raw image instead. - Tracking cameras use the fisheye model. Add
-ffor any tracking camera. If you leave it off for an ar0144 tracking camera, the tool warns and waits: press Ctrl-C and rerun with-f.
Recommended Chessboard
The size and spacing of the chessboard is configurable on the command line. Internally, ModalAI uses a 5x6 with 65.5mm spacing on a stiff, foam-backed board with matte finish to reduce glare. This is the default used by the calibrator tool unless a different size and spacing is set.
Note that the size is based on OpenCV’s definition of a chessboard and matches the ROS calibrator tool. That is to say, the size (4x7, 5x6, etc) is the count of the number of inner corners which is what is actually being detected by the algorithm (not number of squares). This is usually #squares-1 noting that partial outer squares should not be counted.

Here is an image for a 6x9: https://raw.githubusercontent.com/opencv/opencv/4.x/doc/pattern.png
Command Line Options
voxl-calibrate-camera <camera-pipe> [options]
| Short Option | Long Option | Effect |
|---|---|---|
-d | --en_debug | Enable debug prints |
-e | --extrinsics-only | For a stereo pair, load the existing intrinsics file and only calibrate extrinsics |
-f | --fisheye | Use the fisheye lens distortion model (required for tracking cameras) |
-g | --skip-extrinsics | For a stereo pair, calibrate intrinsics only and skip extrinsics |
-h | --help | Print the help message |
-l | --length | Edge length of a single square in meters (default 0.0655) |
-m | --mirror | Mirror the overlay output |
-n | --no-targets | Collect stereo extrinsics samples manually with the Enter key instead of guided targets |
-r | --no-threshold | Show the raw image in the overlay instead of the thresholded image (display only) |
-s | --size | Number of inner corners to look for, as NxM (default 5x6) |
The -t / --threshold option from earlier releases has been removed. The thresholded overlay is now the default.
Examples using a 6x8 checkerboard with 0.038 m squares:
voxl-calibrate-camera tracking -f -s 6x8 -l 0.038
voxl-calibrate-camera stereo_front -s 6x8 -l 0.038
voxl-calibrate-camera stereo_rear -s 6x8 -l 0.038
Monocular Calibration Process
To calibrate a monocular camera, such as the tracking camera on voxl, run the command below, open the web portal, and fill the rectangles like such:
voxl-calibrate-camera tracking --fisheye
![]()
Once you’ve filled all the rectangles, the calibration will run, which will take a few seconds, and then the calibration will be printed to the terminal window. If the calibration was successful, it will also write to /data/modalai/opencv_{camera-name}_intrinsics.yml
A rectangle accepts a sample only when every detected corner lies inside it and the chessboard covers roughly 40 to 50 percent of the rectangle’s area (the exact threshold depends on the lens type). Drawn corner markers alone do not count as a sample. The rectangle outline changes from red toward green as coverage increases. If collection stalls with corners visible, move the board closer to the camera until it fills most of the rectangle, keep the whole board inside the outline, and hold it still. Each rectangle needs 2 or 3 accepted samples before the next one appears.
These intrinsics files are also consumed by voxl-camera-server for Electronic Image Stabilization. Calibrating a camera used with EIS improves stabilization quality.
voxl2:/$ voxl-calibrate-camera tracking -f
Matrix
[277.7090664510777, 0, 290.6865358454301;
0, 277.5687125242331, 240.1597738541476;
0, 0, 1]
Distortion
[-0.006319990900085937;
0.00155893534296691;
0;
0]
distortion_model: fisheye
Re-projection error reported by calibrateCamera: 0.172684
Calibration Succeded!
Writing data to: /data/modalai/opencv_tracking_intrinsics.yml
Saved!
Exiting Cleanly
When calibrating the standard OV7251 Tracking Camera, make sure to use the -f or --fisheye flag to make sure that the calibrator uses the fisheye lens distortion model.
Stereo Calibration Process
To calibrate a stereo camera, run the command below, open the web portal, and fill the rectangles like such:
voxl-calibrate-camera stereo_front

The stereo calibrator runs in two stages.
Stage 1, intrinsics. The calibrator calculates the intrinsics for each camera separately, so you will need to fill in all of the left rectangles, then all of the right rectangles. The same fill rule applies as for monocular calibration: the board must sit fully inside the rectangle and cover most of it before a sample is accepted.
Stage 2, extrinsics. By default the calibrator then guides you through a second set of targets that need the board visible in both cameras at once: two large close-range boxes (one per camera) followed by five small far-range boxes in the corners and center. Hold the board flat and still in each box until it is accepted. When the last box is done the extrinsics are calculated automatically.

If you prefer to choose the extrinsics frames yourself, run with -n / --no-targets. The calibrator then prints Sampling overlapping images now; press Enter in the terminal to capture a frame while the board is fully visible in both cameras, and type stop when you have enough. Take at least ten frames spread across the overlapping field of view at varied angles and distances. To redo only the extrinsics with existing intrinsics use -e, and to calibrate intrinsics only use -g.
At the end the tool prints whether it detected a horizontal or vertical pair and the distance between the cameras. Check that this baseline matches the physical spacing on your camera module.
If everything has been done properly, you will see output like this (this run used --no-targets; a target-guided run prints the same results without the sampling prompts):
voxl2:/$ voxl-calibrate-camera stereo
Sampling overlapping images now
Enter an empty line to take a sample or "stop" to finish sampling
Taking Frame
Taking Frame
stop
Stopping sampling
Calibrating Left Camera
Matrix
[493.7322756669245, 0, 306.7896643027532;
0, 493.1380517350153, 217.4680594645219;
0, 0, 1]
Distortion
[-0.1698366125016952;
0.06988854948207439;
-0.001138684086637882;
-0.0007932118450081079;
0]
distortion_model: plumb_bob
Re-projection error reported by calibrateCamera: 0.177678
Calibration Succeded!
Calibrating Right Camera
Matrix
[496.1483207581481, 0, 320.9542358074322;
0, 495.5756728109903, 223.7654477880258;
0, 0, 1]
Distortion
[-0.1725992238299667;
0.06664783196280108;
-0.00172072316840736;
-0.0004606934467252117;
0]
distortion_model: plumb_bob
Re-projection error reported by calibrateCamera: 0.179598
Calibration Succeded!
Calibrating Extrinsics
2 frames will be processed
R
[0.999777546053251, -0.0004080345076243199, 0.02108771954432934;
0.002055883177889161, 0.9969371281114418, -0.07818015053240687;
-0.02099123036171473, 0.07820611293723473, 0.9967161943839129]
T
[-0.08011754799309605;
-0.001690038045698758;
-0.0008435695774040179]
Re-projection error reported by stereoCalibrate: 0.259720
Extrinsics Calibration Succeded!
Saved intrinsics to: /data/modalai/opencv_stereo_intrinsics.yml
Saved extrinsics to: /data/modalai/opencv_stereo_extrinsics.yml
Exiting Cleanly
After Calibration
- The results are written to
/data/modalai/opencv_<pipe>_intrinsics.ymland, for stereo pairs,/data/modalai/opencv_<pipe>_extrinsics.yml. Each file records thereprojection_errorof the run. - Restart the services that consume the calibration so they pick up the new files, for example voxl-open-vins-server or voxl-qvio-server for VIO and voxl-camera-server for EIS. See Debugging Services for the restart commands.
Confirm every required file is present:
voxl-check-calibration- A calibration is only valid for the resolution it was captured at. If you change the camera resolution or swap the camera hardware, calibrate again. See Verifying Camera Calibration on the VIO page for how to read the files and the resolution check.
Troubleshooting
| Symptom or message | Cause | Fix |
|---|---|---|
| Corners are drawn but the rectangle never advances | The board does not fill enough of the rectangle, or some corners fall outside it | Move the board closer until it fills most of the rectangle and keep it entirely inside the outline |
| No corners are ever drawn | Wrong -s size, glare or poor lighting, a bent board, or the wrong pipe | Check the inner-corner count, use diffuse light and a matte board, and confirm the pipe name with voxl-list-pipes |
Waiting for valid pipe... | voxl-camera-server is not running or the pipe name is wrong | Start the camera server and check the pipe name |
ar0144 tracking cameras have fisheye lenses but the --fisheye argument was not given | Tracking camera run without -f | Press Ctrl-C and rerun with -f |
Received invalid camera format | Color or encoded stream given instead of the grayscale pipe | Use the camera’s grayscale or raw pipe |
Calibration Failed with Max reprojection error: 0.75 (pinhole) or 0.60 (fisheye) | Samples were blurred or the board moved while being captured | Hold the board still in each rectangle, improve lighting, and run again |
Calibration Failed with reprojection error near 1.0 px on a higher-resolution camera such as the 1280x800 OV9782 | The chessboard is not flat. Higher resolution amplifies small errors in the printed grid | Mount the pattern on a rigid, perfectly flat backing and repeat; a flat board typically brings the error to about 0.3 px |
Extrinsics Calibration Failed with exceeded allowable max reprojection error | Board was not flat or not still during the overlap samples, or the samples did not cover the shared field of view | Repeat the extrinsics stage with a flat board held still in every box, or use -e to redo only the extrinsics |
Extrinsics Calibration Failed with left/right cameras appear to be reversed | The left and right cameras are swapped in the voxl-camera-server configuration | Swap the two camera entries in /etc/modalai/voxl-camera-server.conf, restart voxl-camera-server, and calibrate again |
The allowed extrinsics reprojection error is 2.0 px at 640x480 and scales with the image area.
Legacy ROS Calibration
The deprecated ROS-based process is documented on the VOXL ROS Camera Calibration page for users on voxl-suite releases before 0.5.0.