A camera that works on its own disappears as soon as a second one is plugged in, or it appears in the list and delivers black frames, or the wrist view shows up in the wrong column in some episodes.
With two or more USB cameras the usual cause is bandwidth rather than a broken camera: several cameras streaming uncompressed video on one USB controller ask for more than the controller can carry, and the one that loses the race never starts. Move a camera to a port on a different controller or onto a powered hub, and make sure each camera actually carries the label you pass to record, because a label that matches no camera is filled from scan order instead, and scan order moves. A camera that is listed but black is almost always held open by another process.
Three failures, one complaint
| What you see | Cause | First move |
|---|---|---|
| Works alone, vanishes when a second camera is plugged in | USB bandwidth on one shared controller | Move one camera to a different controller or a powered hub |
| Listed, but every frame is black | Another process holds the camera | Close whatever opened it, only one process gets a camera |
| The wrist view shows up in the wrong column in some episodes | The label matched no camera id or name, so it was assigned by scan order | Rename the camera to the label you pass to record, then record one test episode |
| Works for a few minutes, then drops out | Power on a passive hub, or a marginal cable | Powered hub, shorter cable, direct port |
| Never appears on any machine or port | The camera or its cable | Test it in any other application first |
| Appears, but only at a low resolution | The controller granted what was left | Free up bandwidth, then reconnect |
ay-robots devices
# SERIAL PORTS
# robot_1 unknown
# CAMERAS
# camera_1 USB 2.0 Camera
# camera_2 UGREEN Camera 2K (gripper)The FaceTime camera and any iPhone or iPad paired through Continuity are deliberately not listed and never opened, so that no camera on your Mac can switch itself on without you noticing. If a Mac user goes looking for the built-in webcam in this list and does not find it, nothing is broken. If you need it for a test, start the backend process with AYR_ALLOW_BUILTIN_CAMERAS=1 and it appears after the external cameras, never in front of them.
Bandwidth is the usual answer
A USB camera that streams uncompressed frames reserves bandwidth on its controller for as long as it is open, whether or not anything is looking at the frames. The reservation is made up front, and if the controller cannot satisfy it, the camera does not start. That is the mechanism behind the most common report on this page: two cameras that each work perfectly on their own and refuse to work together.
The trap is that USB sockets are not independent. Several physical ports on a machine usually hang off one controller and share its budget, and which ports share is not something you can tell by looking at the case. Two ports right next to each other may be one controller; a port on the opposite side of the laptop is often a different one. This is why "I moved it to another port and nothing changed" is such a common second sentence.
The levers, roughly in order of how well they work: put the second camera on a genuinely different controller, add a powered hub so the hub arbitrates and supplies its own power, lower the resolution of whichever camera matters least, and prefer a camera that can deliver a compressed stream, since compressed video needs a fraction of the budget that raw frames do. Two cameras at moderate resolution is an ordinary setup that works on ordinary hardware. The difficulty scales with pixels per second, not with the number of devices.
# Linux: which controller does each port hang off
lsusb -t
# Linux: what the kernel calls each camera, and its capabilities
v4l2-ctl --list-devices
# The platform view of the same hardware
ay-robots devicesLabels only bind if a camera carries the label
When you pass --cameras wrist,top, each name is looked up against the cameras the platform knows about: it matches if the name appears inside a camera’s id or inside its reported name, compared in lower case. A name that matches nothing is not an error. The recorder falls back to taking the next camera that is still free, in scan order, and carries on.
That fallback is where the quiet column swap comes from. Scan order follows device paths, and device paths move after a replug, after a reboot, or after waking with a hub attached. So as long as no camera is actually called wrist, the words wrist and top are only two column names in your dataset, filled in whatever order the scan produced that day. Nothing warns you, because from the system’s point of view nothing went wrong.
The consequence for recording is expensive and delayed. If the scan order flipped between Monday and Wednesday and both sessions went into the same dataset, then half your episodes have the wrist view in the scene column and the other half do not. Nothing crashes. Training runs. The policy is asked to make sense of two contradictory views of the world and does poorly, and the cause is three weeks upstream by the time you look for it.
The fix is to rename the camera to the label you intend to use. Renaming changes the id the platform keeps for that device, which is stored against its hardware id and therefore survives reboots, so afterward the device list shows wrist next to the camera model and the label binds to that specific camera instead of to a position in a queue. There is no CLI command for renaming, and there is no rename in the desktop client either: it is done on the Hardware page for your robot in the dashboard, in the device list at the top, where every camera and every arm carries a small edit control next to its id. The camera dropdown in the client’s settings is a different list, built from what the operating system hands the client’s own webview, so the entries you pick there are not the ids a label is matched against. Then record one throwaway episode and look at the column names before you commit a session to them.
ay-robots record --task "pick up the red cube and place it in the bowl" \
--dataset cube_pick \
--episodes 10 \
--cameras wrist,topGive each camera the name you intend to record with the first time you add it, and keep that name for the life of the project. A device list is fine for a quick look at what is plugged in. It is the rename that makes the label in your recording command mean one specific piece of hardware.
Only one process gets the camera
In practice a camera is exclusive: the first process to open it holds it, and everything after that is refused or handed nothing. The refusal is rarely reported as clearly as it should be, so the second consumer typically shows the camera in its list and then produces black frames, which reads as a broken camera.
The usual holders are a video call application minimized in the background, a browser tab that was granted camera permission and never released it, a preview window you opened yourself and forgot, and an earlier instance of the client that did not exit cleanly. A policy that is still running counts too, since the inference loop opens a camera and only gives it back when the loop is stopped. Rule all of that out before you touch the hardware.
# What the local backend is doing: hardware, simulation or demo, and
# whether it is idle, teleoperating or running a model. A backend that
# is running a model has a camera open.
ay-robots status
# After closing the other consumers, rescan
ay-robots devicesWork through it in this order
- 1Scan and note what is actually listed
A camera that is missing from the list and a camera that is listed but black are different problems. Do not skip this distinction, it halves the search.
bashay-robots devices - 2Unplug every camera but the failing one
If it works alone, the camera is fine and you have a bandwidth or contention problem. If it fails alone on a known-good port, the camera or its cable is the suspect.
- 3Close everything else that could hold a camera
Video calls, browser tabs with camera permission, any preview window, any second copy of the client. Then rescan before changing anything physical.
- 4Move the camera to a port on the other side of the machine
You are trying to land on a different controller. On Linux, lsusb -t tells you whether you succeeded instead of leaving you to guess.
- 5Put it on a powered hub
A powered hub supplies its own current and often resolves both the bandwidth contention and the slow dropouts that come from a laptop port supplying two cameras and an arm adapter.
- 6Shorten the cable, remove extensions
Long unpowered extensions are a leading cause of "worked for five minutes then stopped". Test with the shortest cable you own before concluding anything about the camera.
- 7Reduce the pixel rate
Lower the resolution of the camera that contributes least, or use one that delivers a compressed stream. This is the lever that makes an over-subscribed controller viable without buying anything.
- 8Rename the cameras, then record one throwaway episode
Give each camera the name you pass to record, then confirm that the right view landed in the right column before you commit a session to it. One test episode is cheap; discovering a swapped camera after fifty is not.
bashay-robots record --task "camera check" --episodes 1 --cameras wrist,top
Adding a powered hub puts another brick on your desk, and desks with several bricks are where the expensive mistake happens. Feetech STS3215 servos run at 7.4 V and 12 V destroys them in seconds. Label the hub supply and the arm supply so that a tired hand at the back of the desk cannot confuse them.
What a dropout does to an episode
The recorder does not leave a hole. It holds the newest complete camera snapshot and writes that same snapshot again whenever a fresh one is not ready, so a camera that stops delivering produces a run of byte-identical repeat frames, not a gap. The episode therefore looks intact: the check that runs when an episode is saved counts one JPEG per camera against the number of rows in the parquet file, and repeated frames satisfy that count exactly. A camera that falls out of the capture entirely does fail that count, and then the save is refused and the artifacts are moved to a quarantine folder rather than written into the dataset, so the case you have to catch yourself is the quiet one. The recorder does report it: dropped_camera_frames comes back with every saved episode and counts frames written with no image at all, the recording status carries a separate stale_camera_frames count for every tick where a snapshot had to be reused, and the web cockpit raises a warning while you are still recording once camera frames start going missing or arriving incomplete. Treat any of those as a reason to discard the session, because a policy trained on frozen frames learns that the scene holds still while the arm keeps moving. Review episodes before you keep them, and throw away anything recorded during a session where a camera was dropping. The reasoning behind the review habit is on /learn/record-your-first-dataset, and the composition of a usable dataset is on /so-100/data-collection.
Frequently asked questions
How many cameras can I use at once?▾
Two is the common setup and it works on ordinary hardware. Beyond two you are managing bandwidth on purpose: separate controllers, compressed streams, and resolutions chosen deliberately rather than left at whatever the camera offers.
The camera works in a video call but not here. Why?▾
Two reasons, and both can apply. Call applications typically request a lower resolution or a compressed format, which fits where a raw stream does not. And if the call application is still running, it is holding the camera, so nothing else can open it.
Does the camera id end up in my dataset?▾
No, the label you pass to record does, as the column name. That is why the two should be the same word: rename the camera to wrist on the Hardware page for your robot in the dashboard, and the column called wrist holds that camera. The rename does not travel, though. It is written into the device config of the machine whose backend answered, so a second machine starts with an empty mapping, hands the same camera an automatic camera_1 or camera_2 id, and falls back to scan order for your label until you open the Hardware page for that machine’s robot and rename there as well.
Can I move a camera to a better position halfway through a dataset?▾
Not without splitting the dataset. A policy learns the mapping from pixels to actions, so moving the camera invalidates everything recorded before the move in the same way that nudging a tripod does.
Do I need a wrist camera at all?▾
You can record with one scene camera and get a working pipeline. A wrist view earns its place at the moment of the grasp, when the gripper occludes the object from any camera that is not on the wrist.
My camera is fine but the arm disappeared when I added the second camera.▾
Then the serial adapter and the cameras are sharing a controller and the cameras took the budget. Move the arm adapter to a port on a different controller, or move the cameras onto a powered hub and leave the adapter directly on the machine.
Was this page helpful?
A symptom in one part of the chain is regularly produced by the part before it. These pages cover the neighboring areas.