Frigate and Coral TPU: How Many Cameras One Accelerator Can Handle

Contents
The question asked before every Frigate installation is how many cameras one Coral can carry, and the answer is always a range because the honest form of the question is different. A TPU does not process cameras. It processes inferences, and it has exactly one thousand milliseconds per second to do it in.
Everything else follows from that: how many detections a second the setup produces, how long one detection takes on the chosen accelerator, and how much of the second is left over when several cameras are busy at the same time. Three numbers, one multiplication.

What One Detection Actually Costs
Frigate does not send every frame to the detector. A cheap CPU pass finds motion first and cuts out the areas where something changed; only those regions go to the TPU, each as its own inference. A still scene therefore costs nothing at all, and a scene with two separate moving areas costs two inferences per frame rather than one.
This is the detail that turns camera counts into guesswork. The load is not cameras times frame rate – it is cameras times frame rate times the number of motion regions, and the last factor is a property of the view, not of the hardware. A camera pointing at a hedge in the wind produces regions all day; one pointing at a closed gate produces none for hours.
The Calculation in One Line
inferences/s = cameras × detect fps × regions per frame
occupancy = inferences/s × inference time
headroom = 1000 ms/s − occupancy
8 cameras × 5 fps × 1.5 regions = 60 inferences/s
60/s × 8 ms = 480 ms/s → 48 % of one Coral
A factor of 1.5 regions per frame is a reasonable working figure for mixed outdoor views; a busy street needs two or three, a quiet corridor barely one. The figure is worth measuring rather than assuming, because it is the only one in the line that cannot be read off a data sheet.
Why the Average Is the Wrong Number
Sizing against the average produces a system that works beautifully for months. The average is low because most cameras see nothing most of the time. A budget built on it has no reserve for the afternoon when a delivery arrives, the dog is in the garden and the wind picks up – three cameras busy in the same second, in the moment that matters most.
The useful sizing case is the simultaneous one: every camera that could plausibly be busy at once, all busy, at the configured frame rate. If that case fits inside one second of TPU time, the installation has no bad afternoons. If it does not, the failure is invisible in the logs and obvious in the missing clip.
What Each Detector Delivers
| Detector | Inference time | Inferences/s | Cameras at 5 fps |
|---|---|---|---|
| Coral M.2 / PCIe | ~7 ms | 143 | 19 |
| Coral USB on a USB 3.0 port | ~8 ms | 125 | 16 |
| Coral USB on a USB 2.0 port | ~22 ms | 45 | 6 |
| OpenVINO on an Intel iGPU | ~15 ms | 66 | 8 |
| CPU only, four cores | ~90 ms | 11 | 1 |
The third row is the most common self-inflicted wound: the same stick, the same model, the same code, plugged into a black port instead of a blue one and running at a third of its speed. The last row explains why the CPU detector exists as a proof of concept rather than as an option – one camera is not an installation.
Two accelerators roughly double the ceiling, because Frigate hands work to whichever detector is free. What they do not halve is everything described in the next section.
The Bottleneck Is Usually Not the TPU
Before a frame reaches the detector it has to be decoded, and decoding is pure CPU work that no accelerator in this budget touches. Decoding a 1280×720 substream at 5 fps is small change. Decoding a 4K main stream at 20 fps costs roughly a core per camera, which is why an eight-camera setup can flatten a machine while the TPU sits at eleven per cent.
The arrangement that avoids this is the standard one and worth stating plainly: detection runs on the substream, at the resolution the camera already produces, at five frames per second. Recording runs on the main stream and is copied, not re-encoded – the full resolution lands on disk without a single frame being decoded for it. The detector never sees 4K, and it never needs to: a person is found from a few hundred pixels of outline, and the clip that gets watched afterwards is the untouched main stream.
How Saturation Announces Itself
Frigate reports two numbers that settle the question. Inference speed is the milliseconds one detection takes; if it climbs above the value from the table, the accelerator is throttling – a USB Coral warm enough to slow down is a familiar sight in a closed cabinet. Detection fps is the rate actually achieved; if it sits below the configured detect fps, frames are being queued and dropped.
Both symptoms present the same way from the outside, and it is the least helpful presentation imaginable: the recording exists, the timeline is complete, and the event simply has no object attached to it. Nothing failed loudly. The budget was overdrawn for four seconds, and those four seconds were the ones worth having.
Configuring the Detectors and Trimming the Region Count
In the Frigate configuration, a Coral is a detector of type edgetpu, and the device value tells Frigate which one to open: usb for a single USB stick, pci for a single M.2 or PCIe card. A second accelerator is a second entry with an index:
detectors:
coral1:
type: edgetpu
device: pci:0
coral2:
type: edgetpu
device: pci:1
The USB equivalents are usb:0 and usb:1. Each detector runs in its own process, and all of them pull from one common queue of detection requests fed by every camera. There is no fixed assignment of cameras to accelerators, which is why a second Coral raises the ceiling of the whole installation rather than that of half the cameras. In Docker, a PCIe Coral appears on the host as /dev/apex_0 once its driver is installed, and that device node is mapped into the container.
The factor in the calculation that configuration can lower without giving up detection chances is the number of regions per frame. Two Frigate features act on it directly.
- Motion masks cover areas where motion never means an object of interest: the camera’s own timestamp, the sky, treetops in the wind. Motion inside a mask no longer starts detection, so those areas stop producing regions. An object inside the masked area is still detected when motion elsewhere in the frame triggers detection. Masking too much has a price: an object that moves into a fully masked area can be lost and is tracked as a new object when it reappears.
- Stationary objects are not sent to the detector on every frame. Once a tracked object has stayed in almost the same position for the stationary threshold, by default ten times the detect frame rate (50 frames at 5 fps), Frigate verifies it only on every 50th frame, the default stationary interval. A car parked in view is then checked about every ten seconds instead of five times a second, until motion within the object restarts regular detection.
Both effects belong in the measured region factor from the calculation above: the figure worth using is the one observed with the masks in place, not the one before.
Questions and answers
How much load does lowering the detect frame rate from 5 to 3 fps take off the Coral?
In direct proportion: in the example with eight cameras and 1.5 regions per frame, 60 inferences per second become 36, which at 8 ms means 288 instead of 480 ms/s, or 29 instead of 48 per cent of one Coral. The price is fewer chances, because a person crossing the frame in one second is checked three times instead of five, and an object that is only briefly in view is more likely to be missed.
How can it be confirmed that a USB Coral is actually running at USB 3.0 speed?
A blue port alone proves nothing. A USB 2.0 hub in between, an extension cable without the USB 3.0 wires, or a plug that is not fully seated and only makes the USB 2.0 contacts will all hold the stick to 480 Mbit/s. On Linux, lsusb -t shows the negotiated speed per device: 5000M means USB 3.0, 480M means USB 2.0.
The decisive check is the inference speed that Frigate reports: around 8 ms means the stick is running fast, a value around 22 ms points to the slow connection. Frigate in Docker adds a quirk: after loading its firmware, the stick re-registers on the bus with a different ID. That is why the whole USB bus is usually passed into the container rather than a single device.
Can the decoding load that no Coral takes on be moved to the GPU?
Yes, and with several cameras that often helps more than a second accelerator. Frigate decodes through ffmpeg, and ffmpeg can hand that work to hardware: VAAPI or Quick Sync on the integrated graphics of Intel processors, NVDEC on Nvidia cards, and their own decoders on some single-board computers. In the configuration this is done through hwaccel_args, for which Frigate ships ready-made presets.
Two points belong in the calculation. First, it only relieves the stream that is actually decoded, which is the substream used for detection; the main stream is only copied anyway. Feeding detection the 4K main stream moves the expensive work instead of avoiding it. Second, decoding and detection share the same GPU when OpenVINO also runs there as the detector. The roughly 15 ms from the table can then no longer be taken for granted; what counts is the inference speed Frigate reports under load.