Cameras & sensorsTroubleshooting

Repeated camera start-stop cycles trigger frame timeout: keep it open

Why start-stop camera cycles around every one-frame detection task raise a 15000 ms frame timeout, the tests that prove the cause, and the open-once fix.

LumaSpec Vision10 min read Share on LinkedIn

On this page 11 sections
  1. 01The symptom and the set-up that produces it
  2. 02Cause 1: every reopen re-pays the whole startup chain
  3. 03Cause 2: the previous close is still finishing
  4. 04Cause 3: the grab asks before the stream can answer
  5. 05The fix: open once, trigger per task
  6. 06Worked example: one frame per task at 30 fps against a 15000 ms wait
  7. 07Depth, ToF and profile cameras: same rule, one caveat
  8. 08Verify the fix in production
  9. 09When closing between tasks is the right call
  10. 10Checklist before you ship the loop
  11. 11Questions

A task that needs exactly one frame invites a tidy design: open the camera, grab, run the detector, close, release. Repeat that loop for every detection task and, soon enough, the grab fails with Frame didn't arrive within 15000 — the wait call expired before the first frame landed. This guide is for anyone driving single-frame inference (a YOLO-style detector, a presence check, a classifier) from a task loop. You will see why the reopen cycle itself starves the wait, a three-cause test order that finds yours, the open-once pattern that removes the failure, and the per-task arithmetic for both approaches.

The symptom and the set-up that produces it

The error appears on a working camera and a working program. Each detection task calls a wrapper function: start the camera, take one frame, hand it to the detector, release the resource, close. Early tasks succeed; as the cycles stack up, a grab raises the timeout, and the number in the message — 15000 — is the milliseconds the wait call waited.

Nothing in that list is broken. The camera enumerates, the detector returns, the close succeeds. What fails is the timing contract: the wait starts when the task asks for a frame, and under frequent cycling the startup chain no longer finishes inside the budget.

Cause 1: every reopen re-pays the whole startup chain

Opening a camera is not one call. Enumeration finds the device and binds a driver — descriptor reads and driver attach on a USB host, discovery plus IP negotiation on GigE. Configuration then sets format, exposure and ROI, buffers are allocated, and streaming is spun up before the first frame can move. A per-task open/close loop pays that whole chain on every task.

Frequent cycling makes it worse, not better: the device is often still settling from the previous close when the next open arrives, so chains lengthen. When the chain runs past the wait, you see exactly this error.

Test: run the same task loop with the close and open calls removed — one session for the whole run. If the error disappears, the cause is confirmed and the fix below is yours.

Cause 2: the previous close is still finishing

A release call returns before the driver has finished tearing down buffers and threads. The next open races that teardown, and the first frame of the new session stalls behind it. Short intervals between tasks make the race likelier.

Test: insert a pause of a few seconds between close and the next open. If failures thin out, the race is real — and the cure is still to stop closing. When a close is unavoidable, stop acquisition fully first, as the USB camera timeout on reopen pattern shows, and give a start room after a stop (single frame acquisition still busy). Repeated cycles add their own tax in leaked handles, covered in camera open close cycles leak handles.

Cause 3: the grab asks before the stream can answer

If the very first task after program start also fails, churn is not the whole story. Two grab-pattern faults produce the same message: a trigger fired the instant streaming starts, before any frame exists, and a polling loop instead of one blocking wait per frame. Both are fixed in frame didn't arrive within timeout: one wait call per frame, and one-shot capture has its own habit of dropping frames (software trigger image timeout).

Test: log a timestamp at trigger and at frame return for the first three tasks. A first-task failure on a freshly opened camera points here; failures that begin after several tasks point to causes 1 and 2.

The fix: open once, trigger per task

  1. At program start: enumerate once, open the camera once, and configure it — pixel format, exposure, ROI.
  2. Start acquisition once and leave it running.
  3. Per detection task: send the trigger, make exactly one blocking wait call for the frame, copy the frame out, run the detector, return the result.
  4. Change settings per task through the API, never by reopening — alternating exposures per trigger is a solved problem (two exposure times, one camera).
  5. Allocate frame buffers once and reuse them across tasks (reuse image buffers).
  6. Stop acquisition and close once, at shutdown, in that order.
# illustrative pseudocode; the function names are ours, not any SDK's
camera = open_camera()                                # once, at program start
configure(camera, exposure_us=10000, format="mono8")  # once
start_acquisition(camera)                             # once

def detect_one_frame():                # called per detection task
    trigger(camera)
    frame = wait_for_frame(camera, timeout_ms=500)    # one wait per frame
    return run_detector(frame)

stop_acquisition(camera)               # once, at shutdown
close_camera(camera)

The proof test for this pattern: run the task loop for a shift-length soak and count reopens in the log — the pass condition is exactly one open per program run.

Worked example: one frame per task at 30 fps against a 15000 ms wait

Inputs:

  • Grab pattern: one frame per detection task, camera closed after each — the failing set-up described above.
  • Wait budget: 15000 ms, the figure in the raised error.
  • Camera: the LS-CMR-0166, a 12 MP USB3 camera listed at 30 fps.

Rule in words: the wait must cover everything between asking for a frame and receiving it. On an open, streaming camera that is exposure plus readout — about one to two frame intervals.

frame_interval = 1000 ms / frame_rate slack = wait_budget / frame_interval

Arithmetic:

  • Frame interval: 1000 ms ÷ 30 fps = 33.3 ms, so 33 ms.
  • Triggered grab on an open camera: one to two intervals, 33-67 ms.
  • Slack: 15000 ms ÷ 33.3 ms ≈ 450 frame intervals.
Path Paid on every task First frame against the 15000 ms wait
Open once, trigger per task one wait of 33-67 ms lands with about 450 intervals to spare
Reopen per task enumeration, configuration, streaming start — in full the reported failure: past the wait

Decision: keep the session open. At 33-67 ms of camera time per task, the reopen path spends roughly 200 to 450 times more, and in the failing set-up it spent the entire budget. Commit to the open-once pattern for any loop that grabs single frames more often than the program restarts.

If you are specifying the camera for a station like this, pick from area scan cameras a model whose frame rate sits comfortably above your task rate; the one behind the numbers above:

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

Filter further in the camera finder or build the whole station from the camera step of the configurator.

Depth, ToF and profile cameras: same rule, one caveat

The fix is identical in 3D. Laser profilers, structured-light and ToF cameras pay the same startup chain on every reopen, and their point clouds are the slowest frames to restart, so per-scan reopening hurts most here. Keep the session open and trigger per scan.

One caveat belongs to this family: a structured-light or ToF camera left streaming projects light the whole time, so the class label on its housing applies to the entire stream, not just to grabs — never bypass its interlocks.

Expect capture in your own API to time differently than in the vendor's viewer, and measure the API path, not the viewer: 3D camera capture time slower in the API than in the viewer. For hardware, start among 3D sensors — a ToF sensing camera suits volume-style tasks:

3D sensorsHikrobot MV-DT01WMU ToF sensing cameraMV-DT01WMU
Type
Depth / ToF camera
Price on requestView and quote

Verify the fix in production

  • Timestamp program start, first open and first frame once at startup; the whole chain should appear a single time in the log.
  • Per task, timestamp trigger and frame return; expect one to two frame intervals, 33-67 ms at 30 fps.
  • Count opens per program run; anything above one means a recovery path is reopening behind your back.
  • Soak the loop for a shift — steady intervals with zero unplanned reopens is the pass condition.

For the clock side of the measurement, measuring frame trigger wait time and polling resolution covers what your timestamps can and cannot resolve, and diagnose camera repeated capture timeout lays out the systematic pass when the error survives the fix.

When closing between tasks is the right call

Close deliberately in three cases: tasks spaced minutes apart across a shift, another tool needing exclusive access to the device, and hot-swap test benches. Then close in order — stop acquisition, drain the frame queue, close — because stop streaming hangs when the frame queue is left undrained, and budget the startup chain into the next task with a retry. The full trade-off is worked through in open camera close per grab, or keep the camera open, and restarting a stream on a host shared with paired sensors has its own order of operations (stop and start camera streaming without crashing paired sensors).

Two more cases change the usual answer. If several programs consume one camera, a single owner process holds the session open and serves frames — the pattern in keeping a camera open between MQTT-triggered grabs. And if the timeout fires with the camera already left open on a Camera Link line, stop chasing software timing and check the cable: shielding, grounding and routing next to drives.

Checklist before you ship the loop

  • One open per program run, one close per program run — count them in the log, because the count is the whole failure signature.
  • Acquisition started once; tasks trigger and wait, they never start and stop the stream.
  • Exactly one blocking wait call per requested frame; polling loops mask the stall they cause.
  • Settings changed per task through the API — reopening to reconfigure is the bug in another coat.
  • Frame buffers allocated once and reused; per-grab allocation adds jitter and fragmentation.
  • Error recovery retries the grab first; only a hard failure stops, drains and reopens.
  • Shutdown order fixed: stop acquisition, drain, close, so the next program start meets a settled device.
  • A shift-length soak with zero unplanned reopens is the release gate.

The decision, restated: a loop that grabs single frames keeps the camera open and pays 33-67 ms per task; reopen-per-task pays a startup chain that already cost a full 15000 ms wait in the field. If the loop still stalls after this change, capture the timestamps from the list above and bring them to the contact page — we will read the log with you.

Questions

How much longer does a reopen take than a triggered grab on an open camera?

On an open, streaming camera a triggered frame costs about one to two frame intervals — at 30 fps that is 33-67 ms from trigger to buffer. A reopen adds enumeration, configuration and streaming start on every task, and those chains run to seconds; in the reported case the first frame landed past the 15000 ms wait. That is a per-task difference of roughly two orders of magnitude.

Does keeping the camera open between tasks wear it out?

No. Area scan sensors are rated for continuous streaming — the frame rate on the data sheet assumes the camera runs at it for a full shift, and power and heat stay at their continuous values. On structured-light, ToF and laser-line cameras the projector or laser runs whenever the stream does, so the class label on the housing sets the exposure rules for the whole stream, not just for grabs.

What timeout should the per-frame wait use?

Size it from your frame interval, not from the default. Ten frame intervals is generous: at 30 fps that is about 330 ms, so round to 500 ms. Keep a 15000 ms default only during bring-up, because it hides a sick pipeline for fifteen seconds before telling you. A tight wait fails fast and points straight at the real stall.

Two programs need the same camera — who keeps it open?

One process owns the camera and serves frames; the others ask. A small broker that holds the session open and answers requests over a socket or an MQTT topic works well, and it removes every reopen from the consumers. The alternative — each program opening the device in turn — reintroduces the exact churn that causes the timeout, plus exclusive-access errors.

The timeout still fires with the camera left open — what next?

Check the grab pattern first: exactly one blocking wait per frame, buffers reused, trigger acknowledged. Then audit the recovery path, because any error handler that quietly closes and reopens the device rebuilds the churn you just removed; an open-count log exposes it instantly. On a Camera Link line with a healthy camera, move to the cable: shielding, grounding and routing next to drives.