You started two depth cameras on one host and the second pipeline refuses to start — or both pipelines show the same scene. It is the classic multi-camera depth failure, and the camera itself is almost never the faulty part. The cause sits in how your code selects devices and how the host exposes them. This guide gives you the binding rule (serial, never index), the code pattern that starts every pipeline cleanly, the USB power and bandwidth numbers that decide whether two cameras can share a bus, and a ten-second test that proves each pipeline is streaming from the physical camera you meant. It applies to stereo, structured-light, ToF and laser-profile depth cameras across the 3D sensors range.
Why does only the first depth camera start when two are connected?
A pipeline that starts with no device selection binds to the first camera the host enumerates; start a second pipeline the same way and it either claims the same camera again or fails outright, because a streaming device cannot be claimed twice.
An unbound config hands the choice to the SDK. With one camera that is harmless; with two it is the bug. The other common variant is an index loop: the code enumerates devices, reads the serial at each index and binds it — but every index returns the same SERIAL_NUMBER string because the host has only exposed one device, or the index order changed between the enumeration pass and the start pass. The config code then looks correct and still fails.
| Symptom | What it actually means |
|---|---|
queryDevices() reports one device with two cameras plugged in |
host side: power, bandwidth or USB permission — fix the host before touching code |
every loop index returns the same SERIAL_NUMBER
|
the host exposed only one device; same fix as above |
the second pipeline throws at start()
|
both configs bound the same serial |
| both pipelines show the same scene | no per-pipeline selection was applied, so both sit on camera one |
Every one of these traces back to one missing step: a distinct serial bound to each config before start. The fix is the subject of the section after next; first, why the index cannot be trusted at all.
Why does the camera index change between runs?
Index order comes from USB enumeration timing — port wiring, power-up sequence, hub delays — not from the camera itself, so a replug can swap camera 0 and camera 1, while the serial number etched into each unit never changes.
Enumeration is a race. Two cameras power up within microseconds of each other, and the host controller reports them in the order their responses land, so the same rig can present a different order after every reboot or replug. An index is a valid loop counter only inside the enumeration pass you just ran; it is not an address. A serial is. It is fixed per unit, printed on the housing, and — the property that matters — a serial binding survives moving a camera to another port or hub, while an index binding does not. Station files and start-up code should therefore never record 'camera 0 is the left unit'; they should record the left unit's serial.
How do I bind each pipeline to its own camera serial?
Build one fresh config per pipeline and call its enable_device(serial) with a distinct serial before any start, reading the serials from queryDevices() device info in the same pass — never share a config between pipelines and never select by index.
- Plug both cameras in and run a probe: call
queryDevices()on your context and readgetInfo(CameraInfo.SERIAL_NUMBER)from each device. Expect one distinct string per camera; if two come back identical, stop and fix the host side first (the section below on one-device enumeration). - Create one pipeline and one fresh config per camera, sized from the device count.
- On each config, call
enable_device(serial)with a distinct serial before any stream call. - Enable the streams on that same config, for example
enableStream(StreamType.DEPTH, 640, 480)plus the matching color stream. - Start each pipeline with its own config; never start two pipelines from one shared config object.
- Once both pipelines run, do the physical check in the confirmation section below, then store the serial-to-station mapping in your station file.
// Illustrative pseudocode; the call names follow the multi-device pattern your depth SDK documents.
int deviceCount = ctx.queryDevices().getDeviceCount(); // probe: expect 2
String[] serials = new String[deviceCount];
Pipeline[] pipelines = new Pipeline[deviceCount];
for (int i = 0; i < deviceCount; i++) {
serials[i] = ctx.queryDevices().createDevice(i)
.getInfo(CameraInfo.SERIAL_NUMBER); // read it, never guess it
}
if (new HashSet<>(Arrays.asList(serials)).size() != deviceCount) {
throw new IllegalStateException("host exposed one device; fix power or permission first");
}
for (int i = 0; i < deviceCount; i++) {
Config cfg = new Config(); // one fresh config per pipeline
cfg.enable_device(serials[i]); // bind by serial, before start
cfg.enableStream(StreamType.DEPTH, 640, 480);
cfg.enableStream(StreamType.COLOR, 640, 480);
pipelines[i] = new Pipeline();
pipelines[i].start(cfg);
}
The distinct-serial check is the test that proves the set-up: it fails in milliseconds at start-up instead of letting the second pipeline die later, and it catches the one-device case that no amount of config code can repair. After start, keep each pipeline's frames in its own buffers; sharing one buffer across two pipelines produces the wrong depth-to-color pairing covered in mixing depth frames from two cameras: find the buffer bug.
Why does queryDevices() return one device when two cameras are plugged in?
The SDK can only list what the host exposes: with both cameras behind one bus-powered hub, the second unit never gets enough power to enumerate, and on mobile hosts it may stay hidden until you grant the USB permission for that specific device.
Work down this list; the first two causes cover most rigs.
- Hub power budget. A bus-powered hub shares one upstream 500 mA USB budget across its downstream ports — about 100 mA each at four ports — while a depth camera with an active projector typically needs a full 500 mA port to enumerate and stream. The second camera never powers up. Fix: a powered hub, or straight into the host.
- Shared bus bandwidth. Two cameras behind the same USB 2 hub share one 480 Mbps bus, and two VGA depth-plus-color streams need about 590 Mbps — the arithmetic is in the next section. One of the two drops off or refuses to stream. Fix: one camera per USB 3 port.
- Per-device USB permission. Mobile and embedded hosts gate every USB device behind its own approval; granting the first camera does nothing for the second. Approve each unit, then re-enumerate.
- Cable, port or boot state. Swap the second camera's cable and port, then try it alone in the host. A unit held in a recovery or update mode also refuses to enumerate, and the depth camera stuck in firmware update mode: read the USB ID first diagnostic identifies that state from the USB ID before anything gets reflashed.
The clean test: plug one camera in at a time, straight into the host. If each enumerates alone but they fail together, the cause is power or bandwidth — a host problem, not a code problem.
How much bandwidth do two depth cameras actually need?
A 640 × 480 depth stream at 16 bit and 30 fps carries about 147 Mbps, and the matching color stream adds another 147 Mbps, so one camera draws roughly 295 Mbps and two cameras 590 Mbps — more than a USB 2 bus can carry.
A depth stream is a pixel rate, so compute it once and the wiring decision falls out:
- Depth, 640 × 480 px at 16 bit, 30 fps: 640 × 480 × 2 B × 8 = 4,915,200 bit per frame; × 30 fps = 147,456,000 bit/s ≈ 147 Mbps.
- Color at the same size in YUY2, 2 bytes per pixel: another ≈ 147 Mbps.
- One camera, depth plus color: ≈ 295 Mbps. Two cameras: ≈ 590 Mbps.
USB 2.0 high-speed offers 480 Mbps raw, and practical throughput lands nearer 320-360 Mbps once protocol overhead is paid, so a single 640 × 480 × 30 fps depth-plus-color camera already saturates a USB 2 port and two can never share one bus. The decisions follow: run one camera per USB 3 port — roughly 4 Gbps usable leaves room for larger frames or higher frame rates — or run GigE depth cameras at one per NIC port, where 147 Mbps sits inside a 1 Gbps link with room to spare. Depth-only VGA at 30 fps fits a USB 2 port on its own but leaves nothing for color; dropping to 15 fps brings one camera to about 147 Mbps total, still too tight to share a bus with a second unit. The same arithmetic, scaled to more cameras and including power budgets, is worked through in multiple USB cameras on one host: bandwidth and power budget; the NIC-side equivalent for GigE units is two GigE cameras on one host: NIC ports and bandwidth budget.
How do I confirm each pipeline is streaming from the right physical camera?
Cover one lens completely with your hand: exactly one pipeline's depth frame must collapse to invalid, and the serial that pipeline was bound to is the serial on that unit's label — repeat for every camera and write the mapping into your station file.
Two identical cameras on a bench look the same to software, so the mapping has to be earned once and recorded. Multi-camera set-up guides make the same point: identify each unit by serial and record its position, because the serial is the only anchor that survives a re-enumeration. Placement itself — mounting height, tilt, the working-distance band a given model needs — is laid out in the 3D camera working distance and positioning guide.
Two failure signatures to know. If a pipeline stays valid while you cover its lens, it is bound to the other unit, or both pipelines sit on one camera — go back to the binding steps above. If a pipeline is correctly bound but its depth image stays black, the fault is sensor-side or stream-side, and the blank frame from one sensor on a multi-camera depth rig: isolate the cause checklist separates those two in a few swaps.
Do not expect the two pipelines to share a clock either. In their default mode depth cameras stream asynchronously, each on its own oscillator, so a timestamp comparison across cameras is not evidence of simultaneity. If you fuse frames by time and see scrambled ordering, the image timestamps out of order: frames and IMU data scramble article traces where the ordering breaks.
What host hardware runs two or more depth cameras without these problems?
Give every camera its own host port: a vision controller with 5+ LAN ports, PoE camera ports, eight built-in light channels and 32 GB of memory, such as the MV-VC5525-256G70-NN, keeps each unit off every other unit's bus.
For a two-camera bench rig, the entry side of the same family is enough. The MV-VC3301H-128G66 carries 8 GB of memory, 5+ LAN ports and 3-4 USB 3 ports; step up the memory and port count as point-cloud processing grows. Both are vision controllers — industrial computers with camera ports and built-in light channels rather than general desktops, so camera count is a wiring decision, not an expansion-card puzzle.
GigE depth cameras make the wiring simplest: one PoE cable per camera carries data and power, one port per camera, no hubs to budget. For wide-area presence and volume zones, a ToF camera such as the MV-DT01WMU slots into the same serial-bound pattern. Giving every device its own host connection is not vision-specific either; serial PLC networks solve the same one-host-many-devices problem, as connecting multiple PLCs to one PC over an RS232 serial bus shows from the control side.
Typical multi-camera depth workloads and the cameras that run them:
- Type
- Laser profiler
- Type
- Stereo / structured light
- Type
- Stereo / structured light
For a rig of several cameras over PoE, this is the controller that removes the shared-bus failure outright:
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- PoE
- PoE camera ports
- Light outputs
- 8 built-in channels
Commit to these decisions on every build: one camera per host port, one serial-bound config per pipeline, recipes keyed by serial rather than index, and USB 3 or GigE whenever a camera's stream exceeds roughly 147 Mbps. For unit shortlists, the camera finder filters depth and area-scan models by interface and resolution; the 3D topic page collects the diagnostics for everything that misbehaves after start-up; and for a second opinion on a wiring plan, send the camera list and stream settings through the contact page.
The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.
Questions
Can I hard-code the camera serial numbers into my program?
Yes. Serials are fixed per unit and survive reboots, replugs and port changes, so a hard-coded binding is stable. Put the list in a config file rather than in compiled code, with a comment mapping each serial to its station, so a replaced camera is a one-line edit instead of a rebuild and re-release.
Why do both pipelines show the same scene even though both started?
Both pipelines bound to the same camera, the first one enumerated, because no per-pipeline device selection was applied before start. Confirm it in seconds by covering one lens: if neither depth image changes, both pipelines sit on one unit. Rebuild each pipeline with its own config and a distinct enable_device(serial) call before start.
When does a serial binding need to be re-verified?
After a firmware update, a unit swap or a move between hosts. Some SDKs re-enumerate the device list during a firmware flash, and a few units report a different serial string across major firmware revisions, so re-run the queryDevices() probe and diff the serial list against your station file before starting production. A mismatch of even one character silently binds a pipeline to the wrong unit.
How many depth cameras can one host actually run?
Port count and decode load decide it, not a fixed camera number. Give each camera its own port, one USB 3 port or one GigE NIC port per unit. A 640 × 480 × 30 fps depth-plus-color camera draws about 295 Mbps, so a shared USB 2 bus carries one at most, while a controller with 5+ LAN ports runs several GigE units side by side.
Do multiple depth cameras need hardware synchronization?
Not to start and stream: in their default mode the cameras run asynchronously and each pipeline delivers frames on its own clock. Synchronization matters only when you fuse or compare frames captured at the same instant, and then you add a hardware sync line or align timestamps in software before matching.



