3D visionTroubleshooting

Depth camera calibration reverts to factory: push the saved table

Why a depth camera's calibration reverts to factory after reconnects, how to catch it by hashing the calibration table, and how to push the saved table back.

LumaSpec Vision11 min read Share on LinkedIn

On this page 9 sections
  1. 01Why does depth accuracy return after recalibration, then fade days later?
  2. 02How do I tell a calibration revert from real lens movement?
  3. 03How do I read and hash the calibration table in C?
  4. 04Why does the table write fail with an invalid command?
  5. 05How do I make the restore automatic on every connect?
  6. 06Can my own application code corrupt the calibration?
  7. 07What makes one camera in a multi-camera setup keep dropping out?
  8. 08When is re-uploading the saved table the wrong fix?
  9. 09Questions

You calibrated a stereo depth camera against a target, depth looked right for a few days, and now the far end of the range reads short or long again. On rigs like this the usual cause is not your code and not the lenses: the camera's stored calibration table has reverted to its factory content during a reconnect or a power event. This guide shows you how to prove it in minutes — hash the table on connect, diff the fields that move — and how to push your saved table back with the raw calibration calls in C, then clean up the USB conditions that trigger the reset. It is written for anyone running multiple stereo cameras on robots or cells who cannot recalibrate mid-shift. The same discipline applies across our 3D sensors, and the fixes below need no calibration target.

Why does depth accuracy return after recalibration, then fade days later?

The camera's calibration table slipped back to factory content, so depth is still computed but with the wrong left-right geometry — a 0.4 mm baseline error on a 50 mm baseline is a 0.8% scale error, about 16 mm at 2 m and 32 mm at 4 m.

The camera has not forgotten how to match pixels. It recomputes depth from whatever the stored table says, and on stereo cameras that table lives in on-camera flash, loading when the device opens. When a reconnect or power event resets the extrinsics to factory content, near-range checks still pass and the far end reads off, because the error is a scale error: it grows in proportion to distance. No single frame looks wrong; only a flat-plate bias test or a table diff exposes it.

Z = f · B / d depth from disparity ΔZ_bias = Z · ΔB / B scale error from a wrong stored baseline ΔZ_step = Z² / (f · B) depth change per whole disparity pixel

Here f is focal length in pixels, B the stereo baseline, d disparity in pixels and Z depth, all in consistent units.

Worked example. Take a stereo pair with f = 700 px and a calibrated left-right translation of 50.2 mm. The factory table on the same unit holds 49.8 mm, so a revert swaps in ΔB = 0.4 mm. At a target 2,000 mm out, the bias is 2,000 × 0.4 / 50.2 = 15.9 mm, call it 16 mm. At 4,000 mm the same table error gives 4,000 × 0.4 / 50.2 = 31.9 mm — double the distance, double the error. For scale, one whole disparity pixel at 2 m is worth 2,000² / (700 × 50.2) = 114 mm, so a subpixel matcher still shows the 16 mm bias well inside a single disparity step.

The decision rule is simple: if a target calibration fixed depth and the far range drifted off again with no knock or disassembly in between, read the table and compare. Do not recalibrate — the geometry did not move, the stored numbers did. Baseline is also what sets far-range reach in the first place, which is why long-range stereo depth accuracy with a wide baseline is worth reading before you specify the camera.

The save-and-hash habit fits any stereo or structured-light deployment, from a MV-DLS300P-04 laser galvanometer stereo camera to a MV-DPS200P-02 projected structured-light camera: whatever calibration the SDK exposes, save it the day it is made.

3D sensorsHikrobot MV-DLS300P-04 Laser galvanometer stereo cameraMV-DLS300P-04
Type
Stereo / structured light
$16,579.00 · quotedView and quote
3D sensorsHikrobot MV-DPS200P-02 Projected structured-light 3D cameraMV-DPS200P-02
Type
Stereo / structured light
$8,829.00 · quotedView and quote

How do I tell a calibration revert from real lens movement?

Diff two saved tables: a revert changes only RotationLeftRight, RotationLeftRGB, TranslationLeftRight and TranslationLeftRGB, while every intrinsic and distortion coefficient stays byte-identical; mechanical lens movement shifts intrinsics as well, and a table re-upload would not fix it.

Read the table the day you calibrate, and read it again after a bad-depth event. Compare field by field — the XML your calibration tool exports works as well as the raw bytes, since both hold the same table. Four extrinsic fields moving on their own is the signature of a revert. The confirmation costs nothing: push the saved table back, and if depth returns to target accuracy with no calibration pattern in front of the camera, the geometry never moved. A real lens shift survives the re-upload and drags intrinsics and distortion along with it.

The physical test is a flat plate. Mount a rigid matte plate at a measured 2,000 mm, average 30 frames and read the mean signed error. A constant offset of order 16 mm is calibration; a frame-to-frame spread of tens of millimetres is noise, because one disparity pixel is 114 mm at this range and subpixel jitter lands in that band. The test-before-you-trust habit is the same one behind hand-eye calibration accuracy verification. For the wider question of when factory numbers are good enough, see camera-IMU extrinsics: factory content versus your own calibration.

How do I read and hash the calibration table in C?

Open the device, call the raw get-calibration-table entry point, pull the bytes and their length from the returned raw-data buffer, and hash them — in C that is three calls and a ten-line loop, and it runs on every connect without starting a stream.

/* device: your already-open device handle. The rs2_* names are the
   SDK's documented calls; h and i are illustrative locals. */
rs2_error* e = 0;
rs2_raw_data_buffer* buff = rs2_get_calibration_table(device, &e);
const unsigned char* arr = rs2_get_raw_data(buff, &e);
int len = rs2_get_raw_data_size(buff, &e);

uint32_t h = 2166136261u;            /* FNV-1a 32-bit offset basis */
for (int i = 0; i < len; ++i) {
    h = (h ^ arr[i]) * 16777619u;    /* FNV-1a 32-bit prime */
}
printf("cal table: %d bytes, hash %08x\n", len, h);
/* then write arr[0 .. len) to a file named for the camera serial */

Prove the tooling before you trust it: run the read twice in a row on an untouched camera and both runs must print the same hash. Replug the camera and read again — same hash. The day the hash changes with no set-call in your log, you have caught a revert in the act. These raw calls are functionally equivalent to the vendor's separate calibration library and its XML round-trip, so nothing extra is needed on the host.

Why does the table write fail with an invalid command?

Because the camera refuses a calibration write while no stream is running: the call raises an error naming the failing function and reports hwmon command 0x51 failed, type Invalid Command (-1). Start any stream first, keep it alive, and the same call succeeds.

The working order is open device, start your normal stream, then write:

/* arr_saved, len_saved: bytes loaded from your per-serial backup file */
rs2_set_calibration_table(device, arr_saved, len_saved, &e);

After the write, read the table back and re-hash it; success means the readback hash equals the saved hash. If the write still fails while a stream runs, close any viewer or second process holding the device — exclusive access matters for writes, not reads.

How do I make the restore automatic on every connect?

Turn the check into a connect hook: hash the table, compare with the stored hash for that serial number, and only on mismatch start a stream and push the saved bytes back — no target, no recalibration, and the cell keeps running.

  1. On device open, read the calibration table and compute its hash.
  2. Look up the stored hash by serial number.
  3. If the hashes match, continue startup and log the hash with a timestamp.
  4. If they differ, start your normal stream profile.
  5. Push the saved bytes with the set-calibration call.
  6. Read the table back and re-hash; it must equal the stored hash.
  7. Log serial number, old hash and new hash to the line journal, then release the cell.

This is the same persistence discipline as saving camera settings across power cycles with a user set: store what the device should return to, and check it at every open. The restore hook is your insurance while you hunt the root cause; it is not the fix itself.

Can my own application code corrupt the calibration?

Not through normal use: the table changes only when something explicitly calls the set-calibration entry point, so if your log shows no such call, the revert happened on the device side — typically during a USB re-enumeration or a torn shutdown of a streaming process.

Log every calibration write your software issues; that log is the dividing line between "my code did it" and "the device did it". The pattern that reproduces the behaviour is violent: start a multi-camera streaming sample and kill it almost immediately, in a loop. Reverts also appear after ordinary exits and after the camera vanishes until it is replugged. On the rig that reproduced it reliably, the same camera reverted every time — the one enumerated last on the bus — and the behaviour followed several different cameras across firmware builds, so treat it as a system condition, not one bad unit. An explicit reset-to-factory call exists in self-calibration tooling; short of that, a normal flow is not supposed to rewrite the table.

The guard on your side is shutdown hygiene: stop pipelines cleanly on exit, never kill a streaming process mid-frame in production, and supervise restarts so a crashed worker closes its streams before a new one opens the device.

What makes one camera in a multi-camera setup keep dropping out?

Shared power and link margin: three cameras on one host share USB bandwidth and hub power, and each re-enumeration is a window in which a revert lands — give cameras short high-flex cables and their own power path rather than the host rail, and the dropouts thin out.

Start with the physical layer, because the dropouts and the reverts travel together. Keep USB 3.0 runs as short as the machine allows and use cable built for the motion it sees; the specifics, including length limits, are in USB cable length limits for 3D cameras. A purpose-built part such as the LS-CBL-0219, a 3 m USB3.0 camera cable rated high-flex for drag-chain use, covers a moving mount. Power a hub from a supply sized for every camera plus margin instead of leaning on the host rail, and lock every connector. For the triage path when a camera has power but will not stream, follow USB link checks after a cable swap. The rest of the camera data cables range covers other lengths and lock types.

Cables3 m USB3.0 camera cable, high-flex (drag chain) (LS-CBL-0219)LS-CBL-0219$62.00View partBuild a system with it

When is re-uploading the saved table the wrong fix?

When the intrinsics or distortion coefficients changed too — that is physical lens or mount movement and needs a fresh target calibration — and when the flat-plate error jitters instead of biasing, which is noise, not geometry; a table write fixes neither.

What you observe What it means Do this
Hash differs; only the four extrinsic fields changed Table reverted Push the saved table while streaming, read back, log
Hash differs; intrinsics or distortion also changed Lens or mount moved Recalibrate on a target, save a new master
Hash matches; constant signed offset on the plate Reference distance or range handling, not the table Re-measure the plate distance first; if the offset stands, recalibrate
Hash matches; frame-to-frame jitter on the plate Matching noise, not calibration Improve texture and exposure, then filter the depth map
Camera not recognized until replug Power or link dropout Fix cables and power; keep the restore hook as cover

For the jitter branch, stereo depth jitter and IMU drift causes goes deeper than this guide. The filtering side is covered in smoothing a noisy stereo depth map. More depth-camera integration pieces live in the 3D vision topic.

When you add the next camera, size it in the 3D sensor finder or the system configurator, and wire the connect hook in on day one — a saved table plus a hash is ten minutes of work the afternoon you calibrate, and it is the difference between a logged repair and a lost shift. If you want a hand planning a multi-camera cell, contact us.

Cameras3D sensors

Laser profilers and stereo 3D cameras

170 models · from $699.00

Questions

Can I load a table saved from another camera of the same model?

No. Intrinsics and extrinsics are measured per unit, so another unit's table mis-scales depth on yours. Name each saved file with the camera's serial number and push a file only to the unit it came from. If a camera is replaced, calibrate the new unit and start a new master file for it.

Do I need the vendor's separate calibration library and XML files to restore a table?

No. The raw get and set calibration-table calls are functionally equivalent to that library and its XML round-trip: what the XML holds is the same table the raw calls read and write. Keep the XML if you already have one, but your own saved bytes plus a stored hash are all an automatic restore on connect needs.

How large is the depth error from a reverted baseline in percent?

It is a pure scale error: divide the baseline error by the calibrated baseline. A 0.4 mm shift on a 50 mm baseline is 0.8% of distance everywhere, which is 8 mm at 1 m, 16 mm at 2 m and 32 mm at 4 m. That is why far targets complain first and a near-range spot check can look clean.

Will restoring the table also calm jittery depth?

No. A reverted table reads as a constant signed offset on a flat plate at fixed range; jitter frame to frame is noise from texture, exposure or matching limits. Restore the table first if the hash differs, then treat what remains with depth filtering. Both problems can sit on the same camera at once.

How do I back up the table on calibration day so the restore file is trustworthy?

Read the table right after a successful target calibration, hash it, and store the bytes under the camera's serial number with the SDK and firmware versions noted alongside. Then re-read and re-hash after writing to the camera; the bytes you read back are the master copy you will restore from.