Two cameras fire together on paper and still every second pair goes missing. The trigger line is right: unplug it and the secondary stops dead, plug it back and it follows the primary. Yet a recording shows shots that pair up, then frames that run alone, and the effective synced rate sits near half of what each camera manages alone. At that point the wiring has proved itself, and the cause is almost always throughput: a primary/secondary trigger aligns the start of exposure, while each camera still has to fit exposure, readout and transfer inside every trigger period. This guide gives you the arithmetic behind that ceiling, the causes ranked by probability, the tests that separate them, and the fixes in order of payoff.
What the hardware sync guarantees — and what it does not
The scheme is simple. The primary runs free and pulses one of its GPIO lines each time it starts a frame; the secondary listens on an input line with its trigger mode on and frame start selected. On GenICam cameras that is a short list of settings: the output line's mode set to output, its 3.3 V staging enabled, its line source set to exposure-active or frame-active, and the secondary's trigger source pointed at that input on the rising edge. Exposure starts then align within microseconds, because a logic pulse crosses a cable in nanoseconds while readouts take milliseconds.
That guarantee covers timing only. Each camera still owns its readout clock and its own link to the host, and neither knows anything about the other's transfer queue. If the period between pulses is shorter than one camera's exposure-plus-readout cycle, or shorter than the time its link needs to carry the frame, that camera skips frames without raising a single error. The plug test — the secondary stops when the cable comes out — proves the trigger path, not the period. If you are still sorting out the wiring itself, Hardware sync two USB cameras: trigger both cameras together walks the line-by-line settings first.
The trigger hop is ordinary wiring. A power-and-I/O cable with an M8 8-pin plug on the camera end and open leads at the other lands the pulse on the second camera's input terminal without a custom loom.
- Type
- Power and I/O cables
- Length
- 6-8 m
The two limits that set each camera's real frame rate
A triggered camera delivers at the smaller of two ceilings. The first is the sensor cycle: exposure plus readout. The second is the link: frame bytes divided by sustained throughput.
Readout scales with row count; count on about 10 ms for a 16 MP global-shutter sensor, and proportionally less as you crop rows away. Link ceilings are set by the interface: USB 3.0 moves 5 Gbit/s on the wire, 8b/10b coding leaves 4 Gbit/s of payload capacity, and a steady imaging stream lands near 300–380 MB/s — take 350 MB/s as the working number. GigE holds about 110 MB/s, which is why full-resolution color over GigE is slower still.
Bytes per pixel dominate the second ceiling, and the pixel format is where most stereo rigs lose half their rate without noticing:
| Pixel format | Bytes per pixel | Frame at 16 MP |
|---|---|---|
| Mono8 or raw Bayer 8-bit | 1 | 16.1 MB |
| YUV 4:2:2 | 2 | 32.3 MB |
| RGB8 / BGR8 | 3 | 48.4 MB |
Trigger mode adds one constraint on top. With trigger overlap off, a pulse arriving while the previous frame is still in readout is discarded silently, so a marginal period becomes steady skipping. With overlap at readout, starts overlap, but a frame is still lost when the previous transfer has not finished. Overlap widens the acceptance window; it cannot create bandwidth. The same exposure-plus-readout sum governs the single-camera case, as Maximum frame rate on a hardware trigger works through.
Worked example: two 16 MP color cameras at full resolution on USB 3.0
Inputs: width 5320 px, height 3032 px, pixel format BGR8 — the full-resolution color mode of the two-camera USB3 rig behind this guide; sustained link throughput 350 MB/s, the USB 3.0 working number from the last section; exposure 10 ms, a locked indoor value; readout 10 ms, the 16 MP figure above.
The rule in words: a frame is delivered only when exposure and readout fit inside the trigger period and the link can move the bytes inside that same period.
The arithmetic:
- Frame size: 5320 px × 3032 px × 3 bytes = 48,390,720 bytes ≈ 48.4 MB.
- Transfer time: 48.4 MB ÷ 350 MB/s = 0.138 s, so the link caps one camera at 1 ÷ 0.138 ≈ 7.2 fps (at 380 MB/s it is 7.9 fps).
- Sensor cycle: 10 ms + 10 ms = 20 ms → 50 fps, so the sensor is not the binding limit.
- Deliverable rate: min(50 fps, 7.2 fps) = 7.2 fps per camera.
That 7.2–7.9 fps band is exactly where this rig's rate counters sat, near 7.5–8 Hz per camera: both cameras were already living on the link ceiling, not at any requested rate. The primary free-runs at that ceiling and pulses the secondary at the same pace, so every pulse landing while the link is still draining the previous 48.4 MB frame gets skipped. How many pairs survive follows from how far the period sits from that ceiling — about half in the first configuration, two thirds once the pacing changed. The wiring was never the problem.
Decision, with its threshold: required bandwidth = fps × frame_MB × 1.25 must stay under 350 MB/s per USB 3.0 controller. At a target of 8 fps, full-frame BGR8 needs 8 × 48.4 × 1.25 = 484 MB/s — impossible on this link. Switch to 8-bit Bayer and the same target needs 8 × 16.1 × 1.25 = 161 MB/s, well inside, with the link ceiling now 350 ÷ 16.1 ≈ 21.7 fps. The 1.25 factor pays for protocol overhead and host contention; without it you are tuning at the cliff edge.
Likely causes in order of probability
When pairs go missing after the plug test passes, work down this list. Each cause comes with a test that confirms or clears it in one recording.
1. The trigger period is shorter than the deliverable cycle
This is the cause whenever drops appear only at full resolution. The primary free-runs at its ceiling, and every pulse asks the secondary for another 48 MB frame; whenever a pulse arrives while the link is busy, the secondary skips it. Paired rates stuck at half or two thirds are the signature.
Test: cut bytes per frame — 8-bit Bayer or Mono8 instead of BGR8, or halve the ROI — and repeat the same recording. Drops that vanish were throughput.
Fix, in order of cost: change the pixel format, shrink the ROI, slow the trigger period until fps × frame_MB × 1.25 fits the link, or move both cameras to a link class with headroom. To set the pace from outside instead of letting the primary free-run, Camera hardware trigger frame rate with external pulses gives the counter arithmetic.
2. Trigger overlap set against the period
Test: enable the frame-ID chunk on both cameras and record. Gaps in the secondary's frame-ID sequence are skipped triggers; gapless sequences with unmatched pairs point at the matcher instead.
Fix: set the period to exposure plus readout with a 20% margin, after which either overlap setting holds. Keep overlap at readout only where the period has been checked.
3. Both cameras behind one host controller
Two cameras at 7 fps × 48.4 MB ask the host for about 678 MB/s in total — roughly twice what one USB 3.0 controller sustains. Desktop boards expose several controllers; compact hosts and laptops often route neighboring ports through one, and a hub makes sharing certain.
Test: record with one camera attached at a time and compare each camera's rate and drop counter with the two-camera run. A camera that slows only when the second is attached is sharing silicon. The bandwidth-first budget in USB hub drops camera frame rate applies line for line.
Fix: one camera per controller and never through a hub. On compact hardware, choose machines with independent USB 3.0 controllers — industrial mini PCs built around multi-camera capture are one route. On Linux, also raise the usbfs memory cap, which quietly truncates large transfers; USB3 camera low frame rate and latency covers that host-side fix.
4. Auto exposure stretching the cycle
With exposure in auto, exposure time drifts with scene brightness. A set-up that holds under bright light drops every time the scene darkens and exposure stretches toward the period; the two streams also drift apart in brightness, which wrecks stereo matching downstream.
Test: log the exposure-time chunk per frame and look for values where exposure plus 10 ms approaches the period.
Fix: lock exposure and gain at values measured under the light you will actually run.
5. The pairing stage never loaded
If both frame-ID sequences are gapless and both cameras hold their ceiling rates, the cameras have done their job. Read the launch log: a "Failed to load node" line means the sync node never started, so nothing assigns identical header stamps no matter how healthy the cameras are. The driver's primary/secondary launch file (primary_secondary.launch.py) pairs frames only when that node is present — install the missing package and re-run before touching camera settings.
Pair frames on header stamps, not arrival times
Once both cameras deliver every frame, pairing is a software question. The sync driver stamps paired frames with identical header timestamps; the recording tool stamps each message when it receives it, and those receipt times differ by milliseconds of transport jitter — per camera, which is the lag a live preview shows. Each camera's own timestamp chunk ticks on its internal clock, so the two raw streams never share a zero unless latched together.
Pair on the header stamps the driver assigns. Cross-check with an LED flickering in view: paired images must show it in the same state. If header stamps differ on frames the frame-ID sequences say were captured together, the matcher is reading arrival times — fix the matcher, not the cameras. Pair frames from two synced cameras by timestamp covers the matching methods, Camera frame timestamp vs system time explains why the clocks differ, and Chunk data on camera frames shows how to carry frame ID and exposure per image.
Prevention checklist
Run this list before any long recording:
- Compute fps × frame_MB × 1.25 and compare it with the link: under 350 MB/s per USB 3.0 controller, under 110 MB/s per GigE link.
- One camera per host controller, no hub in the path, shielded cable at the shortest length that reaches, drag-chain grade where it moves.
- Trigger period at least exposure plus readout plus 20% margin, with exposure and gain locked.
- Frame-ID, exposure-time and timestamp chunks enabled on both cameras from the first test recording.
- Plug test passed — the secondary stops when the cable is pulled — then a drop counter at 0% across a full recording before the real capture.
- Judge capture health by frame-ID continuity and drop counters, never by how smooth the preview feels.
The two cables carry all of it: a shielded, high-flex USB3.0 camera cable for the frame stream, and the M8 power-and-I/O lead for the trigger hop.
If the arithmetic says your link cannot carry the pair, shrink the bytes before buying bandwidth: pixel format, ROI and period cost nothing. When a link-class change is needed instead, the camera cables category and the cables step of the configurator match cable to interface, and the contact page is open for a second opinion on the numbers. More frame-rate work lives on the cameras topic page.
Questions
What does trigger overlap at readout actually change?
With overlap off, a pulse arriving while the previous frame is still in readout is discarded, so that exposure never happens. With overlap at readout, the next exposure can begin during the previous readout, which is how a camera reaches its highest rate. Overlap only helps while the link keeps up: if the exposure ends before the previous image has been transferred, the frame is lost anyway. Hold the period at exposure plus readout and keep bandwidth headroom.
Why does writing the frame rate fail in trigger mode?
On a triggered camera the effective rate is set by pulses at the input, so the AcquisitionFrameRate node is often locked read-only; the driver's write attempt fails with a not-writable warning and nothing breaks. You set the rate by choosing the trigger period: the primary's free-run pacing or an external generator. Read the achieved rate from the driver's rate and drop counters instead of the frame-rate node.
How much image quality do I lose moving from BGR8 to 8-bit Bayer?
Little to none where it matters. The camera sends one byte per pixel and debayering runs on the host, so final color depends on the host algorithm rather than the transport. A 16 MP frame falls from about 48 MB to about 16 MB, lifting the same USB 3.0 link from a 7.2 fps ceiling to roughly 21.7 fps. For stereo depth, matching typically runs on mono or debayered images regardless.
Can this scheme sync more than two cameras?
Yes. Fan the same pulse to every camera input, or daisy-chain it, and apply the arithmetic per camera: the sum of fps × frame_MB over all cameras must fit the host's aggregate controller bandwidth, each camera on its own link and controller. Four 16 MP BGR8 cameras at 8 fps ask 4 × 8 × 48.4 ≈ 1549 MB/s, which means one controller per camera or a move to 10 GigE or CoaXPress-class links.
The secondary stops when I unplug the cable — isn't the sync already proven?
That test proves the trigger line, its 3.3 V staging and the trigger mode are wired and enabled. It does not check throughput: the secondary can acknowledge every pulse and still skip frames whenever the period outruns exposure plus readout or the link. Treat the plug test as gate one, then run gate two: frame-ID continuity with no skipped triggers and a drop counter at 0% across a full recording.

