System designHow-to

Hardware trigger grab timeout: interrupt the wait cleanly

Set a finite grab_timeout on the acquisition handle so a grab waiting for a hardware trigger returns in one second and your stop key can interrupt the loop.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01Why the stop key never gets reached
  2. 02What you need
  3. 03Step by step: a wait that breaks
  4. 04Choosing the timeout: two constraints
  5. 05Verify the three behaviours
  6. 06Mistakes that cost an afternoon
  7. 07Threads versus timeouts: the decision
  8. 08Before you ship
  9. 09Questions

A camera set to external trigger delivers nothing until the trigger edge arrives, and the call that asks for the frame stays inside the SDK for as long as that wait lasts. Your stop key, your shutdown flag, your cleanup code all live after the grab call, so none of it runs while the camera waits. The usual workaround is to fire the sensor and then break, which works but leaves the program hostage to the hardware. This guide shows the fix: bound the wait with a finite grab timeout, treat the timeout error as an idle cycle, and check the stop condition once per cycle, so a key press interrupts the program within one timeout period. The snippets are generic machine-vision SDK pseudocode; every SDK has an equivalent parameter.

Why the stop key never gets reached

In external trigger mode the camera arms itself and waits for the edge on its trigger input, and the acquisition device does that waiting inside the grab call: the grab does not return until the edge arrives and the frame is exposed. While it waits, your loop is not spinning — it is parked one frame deep in the SDK. A key read or a flag check placed after the grab is not being skipped; execution has simply not reached it yet.

That is why the field workaround is to fire the sensor first and press stop second: the trigger lets the grab return, and only then does execution reach the stop check. It works, but it makes stopping depend on the hardware cooperating. The clean fix is a finite wait. A timeout turns one unbounded call into a polling cycle: no trigger inside the timeout, the call returns with an acquisition error, your catch branch runs, the stop check runs, and the loop re-arms for the next window.

The same timeout error also arrives when a trigger you expected never comes. There the missing frame is the fault, and the diagnosis is different — frame didn't arrive within timeout: one wait call per frame covers that case. Here the empty wait is normal operation, and the timeout is what makes it interruptible.

What you need

A camera whose acquisition mode can leave free run for external trigger, with the sensor wired to the input line you select as trigger source. The per-camera trigger configuration walkthrough covers the free-run default every camera ships with, and the ignored-trigger checklist is the first stop when no image ever arrives. Any area scan camera with a trigger input works; on the bench a global-shutter GigE model such as the MV-CU020-90GC is a typical stand-in:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
Up to 3 MP
$309.00View partBuild a system with it

An SDK that exposes a grab timeout on the handle — grab_timeout in milliseconds in most machine-vision interfaces. LabVIEW exposes the same wait as a timeout input on its grab node; a walkthrough of LabVIEW image grab wait timeouts covers the terminals involved.

A stop input that runs outside the loop: a key flag on the bench, an HMI bit or a socket message on the machine. Anything the operator can set while the grab call is parked will do, because after the fix the loop reads it at least once per timeout period.

On GigE, one network port per camera. A dual-port PoE NIC such as the MV-GE2002P keeps camera traffic off the shared office link, which is where trigger pulses get lost under load; the frame grabbers and network cards category holds the rest:

Frame grabbersHikrobot MV-GE2002P Dual-port PoE NIC, full height V2.0MV-GE2002P
Interface
GigE
Ports
2 ports
$249.00View partBuild a system with it

And the light: the same edge usually strobes it, so exposure and illumination share one timestamp. A 4-channel digital strobe controller such as the LS-CTL-0070 fires the light from the trigger, and the light controller finder lists the rest:

Light controllersDigital control box, 4 ch, 200 W, RS232 (LS-CTL-0070)LS-CTL-0070
Output
Constant voltage 24 V
Channels
4 channels
Control
Digital
Feature
Strobe
$549.00View partBuild a system with it

Step by step: a wait that breaks

  1. Set the trigger mode. Switch the camera out of free run, select external trigger, and point the trigger source at the line your sensor is wired to. Do this in the camera's feature list before the loop starts.

  2. Open the handle, then bound the wait before the first grab. The call below is the standard open_framegrabber form for a GigE Vision camera; pass your camera's device string where the example shows 'default' once more than one camera shares the link.

open_framegrabber('GigEVision', ..., AcqHandle)
set_framegrabber_param(AcqHandle, 'grab_timeout', 1000)

The 1000 is milliseconds: the grab call now returns one second after it starts if no edge arrives. The zeros and 'default' strings are the standard open_framegrabber arguments your interface documents; the two that matter here are the interface name and the timeout.

  1. Make the timeout an idle cycle, not a crash. Wrap the grab pair in try / catch. The catch branch is where the stop check finally lives:
StopRequested := false
while (true)
    try
        grab_image_start(AcqHandle, -1)
        grab_image(Image, AcqHandle)
        // Frame in hand: inspect it, write results, clear the cycle.
    catch (Exception)
        // No trigger inside 1000 ms: housekeeping runs here.
        if (StopRequested)
            break
        endif
    endtry
endwhile
close_framegrabber(AcqHandle)

StopRequested is your flag, set from anything that runs outside this loop — key handler, HMI bit, socket message. The grab_image_start / grab_image pair is the asynchronous pattern; the timeout governs the wait between the two calls exactly as it governs a single grab, and the same holds around the asynchronous grab variant. On timeout the acquisition raises an error, the catch takes it, and the loop re-arms.

  1. Close the handle on every way out. break lands after the while loop and close_framegrabber releases the device. If the stop can also arrive as an exception — window closed, script killed — run the close on that path too; the handle cleanup guide shows the pattern.

Choosing the timeout: two constraints

Two constraints set the number. First, the timeout must be at least as long as your trigger period. If it is shorter, the loop spends part of every period outside the armed wait, and a short trigger pulse landing in that window is lost unless the camera's trigger pre-processing stretches the pulse. Second, the timeout bounds stop latency: the worst case is one full timeout plus one processing cycle.

t_period = 1 / trigger rate t_timeout ≥ t_period t_stop_max = t_timeout + t_cycle

Worked example: a feeder presents a part every 300 ms — a trigger period of 300 ms, about 3.3 parts/s — and one inspection cycle takes 30 ms. A timeout of 1000 ms clears the first constraint with margin (1000 ≥ 300) and stops the program in at most 1000 + 30 = 1030 ms, about one second. Tighten it to 350 ms and the stop bound drops to 350 + 30 = 380 ms while still clearing the 300 ms period. Below roughly one period you start gambling with pulses; 10-20% above the period is the practical floor.

If the station mostly idles — job mode, manual load, operator pacing — the period constraint vanishes and the timeout becomes a pure poll interval. 100-200 ms gives a stop response under a quarter second (200 + 30 = 230 ms) at the cost of empty cycles. Raise it back above the period when production starts.

Set grab_timeout explicitly on every handle; interface defaults differ, and a long default makes the stop feel dead even though the pattern is correct. And keep the direction of the parameter straight: it is a ceiling on the empty wait, not a delay on a filled one — the grab still returns the moment the edge arrives.

Verify the three behaviours

  1. Idle. Run the loop with no trigger. It should cycle once per timeout period: with 1000 ms and 30 ms of housekeeping, one catch entry every 1.03 s. Log the catch branch so you can watch the rhythm.

  2. Stop. Press the stop key while idle. The program should exit inside one timeout period — 1030 ms worst case in the worked example. If it takes two periods, something in the catch branch is blocking; find it now, not at the customer.

  3. Trigger. Fire the sensor. Images should arrive with the same trigger-to-image latency you measured before adding the timeout. To put a number on it, measure over a few hundred frames using the trigger frame-rate method.

  4. Dead link. Pull the network plug on a GigE camera. Your fault handling should escalate to a reopen instead of the loop spinning silently forever.

Mistakes that cost an afternoon

One catch for everything. "No trigger" and "camera gone" look identical if the catch swallows all exceptions. Look at the error: a timeout means idle; link and protocol errors mean the camera is unreachable, and the loop should escalate to a reopen path instead of continuing.

Timeout below the trigger period on a running line. Every cycle then leaves the armed wait for the length of your housekeeping, and pulses only last as long as your sensor drives them.

Setting the timeout on the first handle only. In a multi-camera rig each handle carries its own parameter state. Set grab_timeout after each open, before that camera's first grab; the port assignment guide keeps the handles themselves straight.

Testing the stop only with parts flowing. With parts arriving, every grab returns quickly and the blocked-wait bug never shows. The idle test above is the one that finds it.

Threads versus timeouts: the decision

One camera and one loop: take the timeout pattern. It is about six lines of change, there is no thread lifecycle to manage, and the stop latency is bounded by the number you chose. Interface with no timeout parameter, or an operator interface that must react faster than one timeout period: run acquisition in a worker thread that blocks on the grab and pushes frames into a queue, while the main thread reads the queue with its own short timeout and never touches the hardware. Give the worker a finite grab_timeout too, so it notices a dead trigger instead of blocking forever. The Python trigger loop shows the single-loop version, and the two-camera latency split shows the queue version.

Before you ship

  • grab_timeout set explicitly on every handle — defaults differ, and a long default makes the stop feel dead.
  • Timeout at or above the longest normal trigger period with 10-20% margin — shorter eats pulses.
  • The catch branch checks the stop flag every cycle — that is the whole point of the pattern.
  • Fault handling distinguishes timeout from link error and escalates to a reopen — idle and broken are not the same event.
  • Idle stop test exits inside one timeout period, and trigger-to-image latency is unchanged from the pre-timeout baseline.
  • The handle closes on every exit path, exceptions included.

That is the whole pattern: a bounded wait, a catch that runs your stop logic, and a value derived from your trigger period. To spec the camera, NIC and strobe controller for a trigger-fed station in one pass, start at the camera configurator, browse the systems topic page for the rest of the trigger guides, or send a layout for review through the contact page.

Questions

What grab_timeout value should I use for a hardware triggered camera?

Start at 1000 ms. It must stay above your longest normal gap between triggers — at one part every 300 ms, 1000 ms leaves ample margin — and it sets your worst-case stop latency, since the loop can only react between waits. Idle stations can poll at 100-200 ms for a snappier stop. Some SDK interfaces default to a long timeout, so always set the value explicitly.

Does the grab timeout add latency to images that do arrive?

No. The timeout is a ceiling on an empty wait, not a delay on a filled one. A trigger that fires 40 ms into the wait returns its frame at 40 ms, exactly as before. The extra second only appears when no trigger comes, which is precisely when you want the loop back so the stop check can run.

My SDK has no grab timeout parameter — what then?

Check the wait-on-buffer call first: many SDKs take a timeout there instead, and the pattern is identical. If neither exists, run acquisition in a worker thread that blocks on the grab and pushes frames into a queue; the main thread reads the queue with its own short timeout and never blocks on hardware. Stopping the program then means closing the worker from outside.

How do I tell an idle timeout from a camera that fell off the link?

Count consecutive empty cycles and watch the link state. Idle gives one timeout, then the next trigger recovers normally. A dead camera gives timeouts on every cycle plus GigE Vision link or heartbeat errors alongside. After two or three consecutive timeouts with those errors, stop grabbing, close the handle, re-enumerate and reopen.