3D visionHow-to

Camera info full of zeros: calibrate intrinsics yourself

Your CameraInfo topic is all zeros. Calibrate the intrinsics on a checkerboard, check fx against focal length divided by pixel pitch, and publish the file.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01What all zeros in camera_info mean
  2. 02What you need on the bench
  3. 03Calibrate and fill the message, step by step
  4. 04Worked example: a 4 mm lens on 3.45 μm pixels
  5. 05Checks before you publish
  6. 06Mistakes that push fx off the sanity check
  7. 07Cameras you can check the same way
  8. 08The decision
  9. 09Questions

You subscribe to the camera's camera_info topic and read rows of zeros in K, D and P. Nothing that needs geometry works afterwards: no undistortion, no rectified stereo, no PnP pose, no depth deprojection. The pattern is common on embedded cameras and camera-on-module designs whose driver expects to read factory intrinsics from the module and finds nothing stored. The optics are not broken; the calibration data is missing. In about twenty minutes on the bench you can produce your own intrinsics, sanity-check them against focal length and pixel pitch, and publish a camera_info file that unblocks the whole pipeline.

What all zeros in camera_info mean

sensor_msgs/CameraInfo carries three payloads: the K matrix with fx, fy, cx and cy; the D vector of distortion coefficients; and the P projection matrix that rectified pipelines read. Publishing zeros is the driver's way of saying it had no calibration to load. Many camera modules store factory intrinsics in on-module EEPROM; when that storage is blank, when firmware and driver revisions disagree, or when the driver has no loader for your module at all, the message arrives empty.

Treat this as missing data, not a hardware fault. You have two legitimate paths: request the factory intrinsics from the vendor for your exact module and firmware revision, or calibrate the lens yourself. The bench job takes less time than that request usually takes to answer.

What you need on the bench

  • A checkerboard target — for example 9×6 inner corners with 25 mm squares — printed and glued flat onto glass or aluminium. Paper left free to curl bends fx.
  • The camera streaming at the exact resolution and focus you will deploy. Lock the focus ring once the image is sharp at working distance.
  • Even, diffuse light with no specular highlight on the target.
  • OpenCV: findChessboardCorners, cornerSubPix and cv2.calibrateCamera cover the whole job.
  • The lens's nominal focal length and the sensor's pixel pitch, for the sanity check in the worked example below.

Calibrate and fill the message, step by step

  1. Lock the configuration. Set the stream resolution and focus you will run in production and change nothing afterwards. Calibrating at one size and running at another forces scaling conversions on fx, fy, cx and cy while distortion does not scale at all, so calibrate at the size you use. The same principle applies to rectification: derive the new matrix at the deployed stream size so the ROI handling you choose matches production.
  2. Capture 15 to 25 frames. Push the target into every corner of the frame, tilt it between roughly 20° and 45°, keep a few shots near centre, and do not repeat poses.
  3. Detect and refine corners. Run findChessboardCorners on every frame and refine with cornerSubPix. Discard frames where detection fails or corners land visibly off grid; checkerboard calibration not detecting corners: what to check walks the usual causes.
  4. Solve. Feed object points in millimetres — your declared square size — and the refined image points to cv2.calibrateCamera. It returns K, five distortion coefficients in the order k1, k2, p1, p2, k3, and an RMS reprojection error. Accept a total RMS under about half a pixel; one pixel or more means bad corner detections or a warped target (checkerboard camera calibration error: fix the grid pitch).
  5. Fill the message. K is [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]. D holds the five coefficients as returned, and the distortion model name is plumb_bob for this fit. R is the identity matrix for a single camera. Set width and height to the stream size, and leave binning and ROI fields at zero unless you bin or crop. Fill P as the same projection in 3×4 form — fx, 0, cx, 0 on the first row — because rectified consumers read P. Toolboxes that print fc and cc in the classic calibration notation map across directly: fx is fc(1), fy is fc(2), cx is cc(1), cy is cc(2).
  6. Sanity-check fx against focal length divided by pixel pitch before publishing anything. The worked example below gives the arithmetic and the acceptance band.
  7. Load and publish. Most drivers accept a calibration file through a URL parameter and republish it on camera_info; the file path and name are the usual failure points (camera_info not published: fix the calibration file path and name). For an end-to-end check of the published message, ROS 2 camera calibration: validate and publish camera_info shows the validation flow.

Worked example: a 4 mm lens on 3.45 μm pixels

Inputs: f = 4 mm, the lens's nominal focal length from its data sheet or barrel marking; p = 3.45 μm = 0.00345 mm, the sensor's pixel pitch from its data sheet — depth camera pixel pitch: get it without a datasheet shows how to extract a pitch when no sheet exists.

The rule in words: with square pixels, a correct calibration's fx, expressed in pixels, must land close to the focal length in millimetres divided by the pixel pitch in millimetres.

fx ≈ f / p f_eff = fx × p

fx ≈ 4 mm / 0.00345 mm = 1159 pixels. fy comes out the same because the pixels are square.

Decision: accept and publish when the solver's fx lands between 1101 and 1217 pixels — ±5 %, which covers a typical lens's focal-length tolerance plus corner-fitting noise. Outside that band the inputs are wrong, not the physics, and the first suspect is the declared square size, because fx scales inversely with it: a target declared at 25 mm squares that really measures 23 mm pushes fx about 8 % low. Focus position and stream size come next.

Read the result back the same way: if the solver returns fx = 1180 pixels, the effective focal length is 1180 × 0.00345 = 4.07 mm, a few percent above nominal — an ordinary lens. If it returns 1500 pixels, a 4 mm lens is not what is mounted.

Checks before you publish

  • fx and fy agree within about half a percent on square-pixel sensors; a wider gap means the fit traded focal length against distortion.
  • cx and cy sit within a few percent of half the width and height — tens of pixels on a 1080p stream.
  • k1 comes out negative for most short wide C-mount and M12 lenses, which show barrel distortion; a strongly positive k1 on a wide lens deserves a re-run.
  • The geometry check beats them all. Aim at a ruler at a known distance Z and confirm the physical width the camera spans, about Z × image_width / fx, matches the ruler; the lens focal length calculator runs the same conversion from the focal-length side.
Lens focal length, field of view and working distance
Units
Solve for
Focal length
– mm
Field of view
– mm
Working distance
– mm
Magnification
– x
Angle of view
– °
Pixel on the part
– mm

Thin-lens model: m = sensor width / field of view, f = WD × m / (1 + m). Accurate to a few percent above about 150 mm; check the lens's minimum object distance.

Find lenses with this focal length Open the full calculator

Then run the downstream consumer once: solve PnP on the checkerboard's known corners and confirm the pose comes back stable. Which matrices PnP expects, K or the rectified set, is covered in rectified image pose estimation: choosing PnP intrinsics.

Mistakes that push fx off the sanity check

  • A wrong square size. fx shifts inversely with the size you declare, so it is the first suspect whenever fx misses the band.
  • A resolution mismatch. fx calibrated at 1920 columns is exactly twice the fx of the same camera at 960. Scale fx, fy, cx and cy by the width ratio if you must, and recalibrate rather than rescale distortion.
  • A moved focus ring. Recalibrate after any refocus.
  • A target that is not flat. Curvature distributes itself into the distortion coefficients.
  • Poses that are too parallel. fx and k1 trade off when every frame faces the camera head-on; keep the tilts from the capture step.

Cameras you can check the same way

If the module keeps refusing to yield usable factory data, standardise the next build on board-level cameras whose calibration inputs you control: a mount that takes ordinary machine-vision lenses, the shutter type the application needs, and a sensor whose pixel pitch is printed. Mount, shutter and interface sit on each card below; the LS-BCM-0056 also prints its pixel pitch, 1.6 μm×1.6 μm — the exact divisor the fx check divides by.

Board-level camerasGigE board camera / module, 12 MP, Color/Mono, 1.6 μm×1.6 μm pixels (LS-BCM-0056)LS-BCM-0056
Sensor format
1/2"
Lens mount
C-mount
$209.00View part
Board-level camerasHikrobot MV-CB016-10GM-M-W 1.6 MP GigE board-level camera, IMX296, Mono, M12 lens mount, WTB GigE portMV-CB016-10GM-M-W
Sensor format
1/2.9"
Lens mount
M12
$559.00View part
Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
$690.00View part

With a printed pitch, the sanity check for any nominal focal length is a one-line division, and a calibration that disagrees with it points at the target or the focus rather than the code. If you would rather have intrinsics delivered with the sensor, the factory-calibrated 3D sensors category is that route; the board-level cameras category is the build-it-in route.

The decision

Load the factory intrinsics when the vendor supplies them for your module and firmware revision; that is the zero-effort path. Otherwise calibrate today: 15 to 25 poses, RMS under about half a pixel, fx within ±5 % of f / p — publish the camera_info file and move on. When fx misses the band, fix the target or the focus before touching any code, because bad intrinsics poison every downstream measurement quietly. If empty EEPROM reads matter across a fleet of cameras, raise it with the vendor as a firmware question, and standardise the next build on hardware whose intrinsics story you control. The camera finder filters both routes, and the configurator below starts a camera-led build. Every order includes free expert support, and we are glad to sanity-check your calibration numbers with you.

Build the whole station

The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.

Start with the camera

More bring-up and calibration fixes for depth and stereo hardware sit on the 3D camera topic page.

Questions

My target is not a checkerboard — can I still calibrate?

Yes. Circle-grid targets and marker-augmented boards work with the same solvers, and many people find their corners easier to detect at wide angles and close range. Whatever pattern you choose, the printed spacing must be exact, because any sizing error moves the focal-length result one-for-one.

Can I copy one calibration to every identical camera on the rig?

No. Two units with the same sensor and the same nominal lens still differ by each barrel's focal-length tolerance, typically a few percent in fx. That spread is exactly what the sanity check allows, so calibrate each camera separately and store one file per unit. The rig code stays uniform even though the numbers differ.

How do I recover the pixel pitch when the sensor data sheet is gone?

Calibrate with a lens of known focal length and divide: pixel pitch in millimetres equals focal length in millimetres over fx in pixels. The result carries the lens's tolerance, so treat it as good to a few percent and cross-check it against the published pitch of any same-family sensor before relying on it.

How much does an fx error hurt depth accuracy?

Depth from disparity scales inversely with fx, so a five percent error in fx becomes a five percent bias in every computed distance. The ±5 % acceptance band suits detection, localisation and pick guidance; for gauging, tighten the target to one or two percent by adding frames and re-running until the RMS settles.

Which distortion model string matches an eight-coefficient fit?

The classic five-coefficient radial-tangential fit maps to the plumb_bob model name. When the solver returns eight coefficients with the higher-order radial terms added, use the rational_polynomial model name instead. Consumers read the string to decide how to parse D, so the coefficient count and the name must agree.