You have three identical mono cameras on one multi-socket board and you want, per camera, the intrinsic matrix, the distortion coefficients and an extrinsic matrix referenced to the left camera. The failures arrive in a fixed order, and none of them is caused by the all-mono layout itself. They are: an SDK build that predates the feature, a board description that misses a socket field, and one over-long string that stops the EEPROM write. What follows is the board JSON that flashes, the measured-geometry example with its arithmetic, the calibration command with a real checkerboard, and the fix for the error string that halts most builds. Followed in order, the whole job is an afternoon's work.
Can a three-camera board calibrate all three sockets as mono?
Yes — all-mono is a supported layout once the board description names every socket. The calibration routine stops assuming a stereo pair plus a colour camera on builds that carry the feature, so move to such a build before you debug anything else.
On an older build the routine reports each socket's sensor model as unknown and repeats a warning that no calibration is loaded on the device. That warning is normal on a fresh custom build — the EEPROM stays empty until your first successful flash — so treat it as noise, not as the blocker. The genuine blocker is the build: once it knows a three-mono layout is legal, the same board JSON calibrates without complaint.
What must the board config contain for three mono cameras?
Each socket needs four entries: model (the sensor part string exactly as the SDK spells it), name, hfov (your mounted lens's horizontal field of view in degrees) and type. All three sockets go under board_config.cameras as CAM_A, CAM_B and CAM_C, and stereo_config is optional.
The four entries live in a JSON block whose shape the SDK's sample boards fix. The file below is an illustrative example for three sockets of the OV9282 sensor:
{
"board_config": {
"name": "FFC-3M",
"revision": "R1M0E1",
"cameras": {
"CAM_A": {
"model": "OV9282",
"name": "left",
"hfov": 71.9,
"type": "mono"
},
"CAM_B": {
"model": "OV9282",
"name": "center",
"hfov": 71.9,
"type": "mono",
"extrinsics": {
"to_cam": "CAM_A",
"specTranslation": { "x": 14.6, "y": -16.2, "z": 0 },
"rotation": { "r": 0, "p": 0, "y": 0 }
}
},
"CAM_C": {
"model": "OV9282",
"name": "right",
"hfov": 71.9,
"type": "mono",
"extrinsics": {
"to_cam": "CAM_A",
"specTranslation": { "x": 30.1, "y": 0, "z": 0 },
"rotation": { "r": 0, "p": 0, "y": 0 }
}
}
}
}
}
The hfov number is illustrative: sample boards list 71.86 for their own sensor-and-lens pairing, and yours must come from your lens's data sheet, in degrees. The extrinsics keys — to_cam, specTranslation, rotation — follow the same structure the sample boards use for a stereo pair, applied here to two extra mono sockets.
Which camera is the world frame, and how do you enter the measured offsets?
Pick the left socket as the world frame, point the other two at it with to_cam, and enter your tape measurements in specTranslation in centimetres. The geometry below puts the centre camera 21.8 cm from the reference and the outer one 30.1 cm away.
Measure sensor-centre to sensor-centre with the mount bolted down. With the centre camera 14.6 cm along x and 16.2 cm along y from the left socket, the translation magnitude the routine stores is:
Keep rotation at r = 0, p = 0, y = 0 for a parallel mount. The checkerboard solve refines orientation from images, but specTranslation is the geometry the EEPROM stores and the depth or multi-view math consumes, so a careful tape pass pays for itself.
Do you need a stereo pair before the calibration will flash?
No — stereo_config is optional. The three sockets calibrate and flash intrinsics, distortion and extrinsics with no pair declared, and you add the left_cam and right_cam keys only when you actually want disparity out of a socket pair.
Without stereo_config the flashed EEPROM still carries each camera's intrinsics and its extrinsics to the reference socket — exactly what a multi-view or pose pipeline reads. The multiview triangulation guide shows how one intrinsics vector then serves cameras of different models. Adding a pair later is one edit and one re-flash, and the stereo rectification fix for horizontal lines is worth a read before you spend that write, because a mis-ordered pair costs a second calibration.
Why does flashing fail with EEPROM_FIELD_TOO_LONG or a protected-fields error?
EEPROM_FIELD_TOO_LONG means the board_config.name string is longer than the EEPROM field reserved for it. Shorten the name — the sample board that flashes cleanly carries the ten-character name "Custom FFC" — and the identical JSON writes with no other change.
The messages map to distinct causes:
| Message | What it means | Fix |
|---|---|---|
| Repeated warning that no calibration is loaded | EEPROM empty, normal before the first flash | Continue; it clears after the write |
| Sensor model reported as unknown | Build predates all-mono support | Move to a build with the feature |
| "No PROTECTED permissions to override protected EEPROM fields" | Factory-protected unit refuses the overwrite | Flash the user area of a board sold for custom builds |
EEPROM_FIELD_TOO_LONG |
A board_config string overruns its field | Shorten the name to around ten characters |
The name is the usual offender: the camera model entries are short, while descriptive board names are not. "FFC-3M" in the example above fits; a sentence-length name does not.
What does a working calibration run look like from command to verification?
Run the calibration script with your board JSON and a checkerboard you have measured with a rule — 13 × 7 inner corners at 2.42 cm per square, a board 33.9 cm wide and 19.4 cm tall — then read the matrices back and hold each translation against the tape.
The script call declares the square size, the board file and the inner-corner grid (values shown are an illustrative measured target):
python3 calibrate.py -s 2.42 -brd ./ffc3m.json -nx 13 -ny 7
An n × m inner-corner grid carries (n + 1) × (m + 1) squares, so this target measures (13 + 1) × 2.42 cm = 33.9 cm across and (7 + 1) × 2.42 cm = 19.4 cm tall. Hold it square to each camera so it fills most of the frame. On a 4000-px-wide sensor viewing a 600 mm field, one 24.2 mm square spans (24.2 / 600) × 4000 = 161 px — comfortable above the roughly 10 px per square that corner detection wants.
For the capture itself, stream the target the way the calibration routine expects, mount a printed grid on rigid backing — paper is flat enough only when it cannot flex — and scale up with a large grid for a 1 m field of view when a socket sits far back. The measured-target OpenCV calibration walkthrough covers the same verification in image space.
After the flash, read the calibration back through the API: every camera should report an intrinsic matrix, distortion coefficients and extrinsics whose magnitudes match the tape — 21.8 cm to the centre camera, 30.1 cm to the outer one.
Which cameras suit a build like this?
Build the rig from one mono family across all three sockets so lenses and calibration maths stay uniform. Use USB3 parts for a bench rig, GigE when sockets sit metres from the host, and choose resolution by the smallest feature you must resolve — not by habit.
The mono area scan cameras below suit the three sockets of a custom rig, and the camera finder filters the rest by interface, resolution and shutter.
- Sensor format
- 1"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 16-25 MP
The MV-CS200-10UM is a 20 MP mono USB3 camera on a 1" sensor with a C-mount — the highest resolution of the three shown here, for the reference socket where corner placement matters most. The companion sockets can share the lower-cost tier: the LS-CMR-0166 is a 12 MP USB3 part and the LS-CMR-0053 a 12 MP GigE part, both rolling shutter — no obstacle for a static checkerboard, and the LS-CMR-0053's 9 fps is ample for hand-held captures.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
If the end goal is dense depth rather than per-camera poses, the 3D sensors category carries factory-calibrated stereo and structured-light units that skip the board build, and the camera configurator narrows candidates by interface and resolution. More multi-camera setup guides sit on the 3D topic page. Send us your socket layout through the contact page if you want the board JSON checked before you commit the write.
Questions
Can the three sockets use different sensor models?
Yes — each socket declares its own model, name, hfov and type, so mixing sensors is legal in one board_config. Matching sensors keep the lens choice uniform and let one checkerboard distance serve all sockets, but the calibration routine itself accepts mixed models as long as every entry is filled.
Why does the flash error point at the USER area?
EEPROM_FIELD_TOO_LONG reports the USER area because the board_config strings — the name among them — live there, not in a protected factory region. Shortening any over-long string in board_config clears it; the camera model entries are short and rarely the cause, so check the name first.
How far apart can the cameras in such a rig sit?
Yes, tens of centimetres is fine — the example geometry spans 30.1 cm between the outer sockets, and the stored translations are plain numbers. What changes is physics: a wider baseline improves depth precision at range but shrinks the overlapping view of two cameras and raises the closest usable distance.
Does a rolling shutter matter for this calibration?
Not for checkerboard captures: the target is static while each frame is taken, so rolling shutters introduce no skew here. The constraint appears later, when the same cameras film moving scenes — then trigger the sockets together, stagger the captures, or choose global-shutter parts for the sockets that see motion.


