A depth camera on an unattended machine has to survive one fault nobody schedules: the power cut while it is streaming. When the supply returns, the camera enumerates, answers every query you send it, and still delivers nothing but empty frames until its frame timeout fires. If the camera rides a crane or a gantry, nobody is there to pull the USB plug for hours. This guide gives you the recovery sequence that runs entirely in software: the documented device reset call, the re-enumeration wait, serial-number matching when two cameras share the bus, and a probe-first start-up ladder that puts first frames back within 15 s of the failed attempt.
Why does a depth camera list but stream nothing after power loss?
Because the power cut latched the camera's streaming engine while leaving its control endpoint alive: after the supply returns, the device enumerates and answers queries, but the endpoints that carry image data never restart, so the host receives empty frames until the frame timeout fires.
The signature is unmistakable once you have seen it. query_devices() finds the camera and get_info() reads its serial and firmware without error. Starting the stream prints a run of 'Empty frame has arrived.' warnings — 31 of them in the log that captured this fault — followed by 'Frames didn't arrived within 5 seconds', repeating for as long as the program runs. udevadm monitor stays silent throughout, so the host never sees the device leave or return.
A clean shutdown, a few minutes with the power off and a fresh boot do not clear it either. That last point is the diagnosis: rebooting the host cannot fix a state that lives in the camera's own power-on logic, so the camera itself has to be reset.
Note what it is not. A bandwidth or bus-power problem produces short, dark or corrupted frames and usually responds to a different port or a shorter cable; the USB bandwidth and power check covers that path. The post-cut state produces perfectly empty frames behind a perfectly healthy enumeration, which is why no amount of cable swapping fixes it. The same empty-frame signature has been reported on other depth camera lines, and even after a stop-then-restart of streaming mid-session, so the ladder below is generic depth-camera practice, not one model's quirk.
How do you tell a stuck camera from a cable or port fault?
Check three signatures: the device still enumerates and answers queries, the log repeats empty-frame warnings instead of showing corrupted or dark frames, and udevadm monitor prints nothing while streaming fails — together they point at the camera's latched state, not at wiring.
Rank the causes by how often they turn out to be the culprit:
- Camera latched by the cut. Enumeration is clean but every frame is empty. Test: the reset call in the next section brings streaming straight back. Fix: build the reset into the program.
- Marginal USB link. A flexed or damaged cable passes enumeration but not streaming. Test: swap the cable and try a port on another controller — the USB link checks after a cable swap list them in order. Fix: replace the cable, change the port.
- Bandwidth or bus power. Several streams share one controller, or a hub runs without its own supply. Test: run this camera alone on a root port. Fix: rebalance the ports or add a powered hub.
- Firmware state. The camera woke in update mode or with a mismatched driver. Test: read the USB ID before anything else, as reading the USB ID in firmware-update mode shows. Fix: the vendor's recovery procedure, firmware first.
| What you see | Likely cause | Deciding test |
|---|---|---|
| Enumerates, queries answer, frames empty, udevadm silent | Camera latched by the cut | Software reset restores streaming |
| Never enumerates | Cable, port or supply | Swap cable and port, watch udevadm |
| Enumerates with an unexpected USB ID | Firmware-update mode | Read the USB ID, run vendor recovery |
| Frames arrive dark or corrupted | Bandwidth or bus power | Run the camera alone on a root port |
Which software call recovers the camera without unplugging it?
The SDK's documented hardware reset: call hardware_reset() on the device object and the camera power-cycles its own internals, which the SDK documents as the same as unplug and replug — streaming came straight back in the reported case.
Two behaviours come with the call. Every camera setting returns to its default, so exposure, emitter and region settings must be applied again after the camera returns — reopen with your settings block, not a bare start. And the device handle goes stale the moment the camera drops off the bus, so do not touch the object after the call; re-enumerate and open a new one.
// Documented SDK calls: query_devices(), hardware_reset().
// Ours: the break after one device; a real program picks by serial (next section).
rs2::context ctx;
auto list = ctx.query_devices();
for (auto&& dev : list) {
dev.hardware_reset();
break;
}
// From here the old handle is dead: wait, then re-enumerate.
The test that proves the recovery on your bench: before the reset, the start code dies at the frame timeout behind empty-frame warnings; after the reset and the wait in the next section, the identical start code returns depth frames. Because settings revert, treat every reopened camera as fresh and start from a known settings state — the start-from-a-known-state pattern is built for exactly that.
How long should the program wait before reopening the stream?
Wait 5 s: the camera in the reported case re-enumerated 2-3 s after the reset call, so a 5 s wait covers it with margin, while a shorter one fails with a permission-denied error when the code tries to open the device before it finishes re-enumerating.
A blind sleep of that length works. A serial poll is better, because the program reopens the moment the camera is ready instead of always paying the full wait: poll query_devices() every 500 ms with a 10 s cap — up to 20 polls — and expect the camera by poll 5 to 7 (2.5-3.5 s). If the cap expires, log it as a failed recovery and move to the alarm path.
The first term is the frame timeout that told you the stream was dead, the second covers the measured 2-3 s re-enumeration with margin, and the third is the same timeout applied to the reopened pipeline; most reopen finishes well inside it, so live recoveries land near 12 s.
Put the 15 s in context. If the walk to a crane's power cabinet takes 4 h, that is 4 × 3,600 s = 14,400 s of lost observation, 960× the software recovery. At one unplanned cut per day, 15 s of downtime is 15 / 86,400 = 0.017% of runtime; at one per week it is 15 / 604,800 = 0.0025%.
How do you wait for one specific camera when two share the bus?
Match the serial number yourself: read RS2_CAMERA_INFO_SERIAL_NUMBER from each enumerated device and poll until yours returns, because the SDK's device_hub helper waits for any device and with two cameras connected it can hand you the wrong one.
The hub helper takes the context and hands back a device once one connects — rs2::device_hub hub(ctx); then hub.wait_for_device() — but in the SDK builds behind this fault it takes no serial argument, so the poll below is the application-side answer.
// Documented: query_devices(), get_info(), RS2_CAMERA_INFO_SERIAL_NUMBER.
// Ours: read_serial_from_config(), found, the 20 × 500 ms loop, sleep_ms().
rs2::context ctx;
std::string want = read_serial_from_config(); // the camera's serial, from its label
bool found = false;
for (int i = 0; i < 20; ++i) { // 20 polls × 500 ms = 10 s cap
for (auto&& dev : ctx.query_devices()) {
if (dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER) == want) { found = true; break; }
}
if (found) break;
sleep_ms(500);
}
If you would rather react than poll, register the devices-changed callback: set_devices_changed_callback() delivers an event whose get_new_devices() lists arrivals and whose was_removed() reports the departure, which is how multi-camera samples keep their device records straight through a reset. Read each camera's serial once from its label, store it with the cell's documentation, and match on it after every recovery.
Should you reset at every start-up or only after a failed stream?
Only after a failed stream: probe first with the standard frame timeout and reset only when it fires, because resetting a healthy camera at every boot costs about 10 s of re-enumeration and settings time and wipes the controls you would then have to set again.
Write the start-up as a ladder, in this order:
- Enumerate the bus, start the pipeline, and wait for frames with the standard frame timeout.
- On a timeout, call hardware_reset() and abandon the handle — no further reads on that object.
- Wait out the re-enumeration period, or poll for the camera's serial every 500 ms with a 10 s cap.
- Reopen the pipeline, apply the settings block, and read one control back to confirm the apply.
- If the reopened stream also times out, run one more reset cycle; after two failed cycles, raise an alarm and stop.
The arithmetic: one cycle costs about 15 s worst case, two cycles about 30 s, and the alarm keeps the loop bounded. An unconditional reset at boot also works — the program in the reported case simply called the reset at start — but it pays the re-enumeration on every clean boot and reverts settings, so probe first. The same ladder transfers to networked cameras through the link-level tools in watchdog and recovery patterns for PoE streams.
What stops the failure coming back?
Remove the three ingredients: cuts that arrive mid-stream, a marginal USB link, and outdated camera firmware — the reported fault was fixed outright in a later camera firmware and SDK build, so the first check on a recurring fault is whether your build is current.
- Update first. Move the camera firmware and the SDK to the vendor's current builds: the reported fault disappeared completely with newer builds, no code change needed.
- Keep cuts clean. Feed the controller and the camera from the same conditioned supply; a small UPS on the controller lets the program close the stream instead of dying mid-frame.
- Rate the cable for the motion. On a crane or gantry the USB cable flexes all day; use a drag-chain-rated USB 3 cable such as the LS-CBL-0219, 3 m, high-flex, rather than an office cable. The camera cable range filters by length and flex rating.
- No unpowered hubs. A hub without its own supply starves the camera at inrush and is the first thing to remove when a fault looks marginal; root ports only.
- Give the program native ports. A vision controller such as the MV-VC2140-128G40-1T carries two USB 3 ports plus four built-in light channels, so depth and illumination run from one box.
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
- Log every reset. Timestamp each reset call and each recovery. Within a week the log tells you whether cuts, cable or firmware is the ingredient still missing.
When is a site visit still the only fix?
When two reset cycles end with no device on the bus: if the camera never re-enumerates after the second attempt, or it comes back with an update-mode USB ID, the fault is below your program — port power, host controller or firmware — and hands at the machine finish it.
Hands then do three things, in order: reseat or replace the cable and try a root port; move the camera to a port on another controller and watch udevadm while it connects; reflash with the vendor's recovery tool if the device came back in update mode. The write-up for the last case — a device that drops and stays gone — is USB recovery after a capture crash.
So the decision, case by case: camera enumerates but streams empty — reset call, re-enumeration wait, reopen, two-cycle cap. Camera never enumerates — cable, port, then supply, at the machine. Camera enumerates with an unexpected ID — firmware recovery before anything else. Frames arrive dark or corrupted rather than empty — bandwidth and power, not the reset path.
If you are specifying hardware for a cell like this, pick the sensor for the volume first: the depth camera range runs from laser profilers to ToF models such as the MV-DT01WMU, and the depth camera finder filters them by type. Build the ladder above into the program before the cell ships, not after the first blackout.
- Type
- Depth / ToF camera
The system configurator pairs camera, controller and cabling in one pass, or contact the applications team with the crane's cycle and the site's cut history and we will lay the recovery logic out with you. The other depth-camera fault guides sit on the 3D topic page.
Questions
Does the hardware reset wipe my camera settings?
Yes. The call is documented as the same as unplug and replug, so user-set controls return to their defaults. Keep one settings block in your start-up code and apply it after the reopened pipeline starts, then read one control back — an exposure value, for instance — to confirm the apply landed before you trust the frames.
Can I just unbind and rebind the USB driver from the shell?
You can try a driver-level unbind and rebind or a per-port power toggle from the shell, but in the reported case those host-side tricks did not restore streaming: the stuck state lives in the camera's own stack, not in the host driver. Make the camera's documented reset call the first recovery and keep shell tricks as bench experiments, not production code.
How do I run the recovery automatically at boot?
Run the vision program as a supervised service that starts after the USB stack is up, and let it begin with the probe-first ladder: open with the frame timeout, reset on timeout, wait out the re-enumeration period, reopen. Resetting unconditionally at boot is harmless for a healthy camera but adds roughly 10 s to every start and wipes settings, so probe first.
Is one reset attempt enough, or should the code retry?
Budget two attempts. The first reset restored streaming on its own in the reported case; a second cycle costs another 15 s and covers a brownout that only half-reset the camera. If two cycles fail, stop and raise an alarm: an endless reset loop masks a failing cable, a dying port or a camera on its way out, and it can hide the power event that started it.
Does the same recovery apply to GigE depth cameras?
The latched state itself is a USB phenomenon, but the pattern maps across: detect the dead stream, command the device to restart, wait for it to reappear, reopen. On GigE you can also drop the link or the power from the switch port as the reset, and a watchdog on the acquisition loop catches the same silent failures between resets.


