
Resolution, frame rate, exposure control and the USB bandwidth ceiling that breaks the third camera. What actually matters when picking webcams for a LeRobot recording rig.
What you need to know
- •Three USB 2.0 webcams at 640x480 and 30 fps in uncompressed YUYV need 442 Mbit/s. One controller may allocate 384 Mbit/s to all periodic transfers. The third camera fails on arithmetic, not a bad cable.
- •Set the pixel format to MJPG via the fourcc field on LeRobot's OpenCVCameraConfig. If the camera refuses, LeRobot warns and records in the old format anyway.
- •Resolution above 640x480 is mostly discarded: ACT turns a 480x640 frame into a 15x20 feature grid, one cell per 32x32 pixel block.
- •Exposure control matters more than megapixels: in dim light a camera on auto exposure lengthens exposure and drops its frame rate to pay for it.
- •Write auto_exposure=1 before exposure_time_absolute. uvcvideo makes exposure time a slave of auto exposure, so the other order does nothing.
- •LeRobot derives frame timestamps from the frame index, so a session that ran at 25 Hz still ships a dataset claiming 30.
Resolution: most of it never reaches the model
The instinct is to buy resolution. It is the number on the box, it is cheap, and it matters least, because almost none of it survives the trip into the network. Every policy you can train on an SO-100 resamples its camera input to a fixed grid first.
The ACT paper is explicit: each 480x640x3 image goes through a ResNet18 and comes back as a 15x20x512 feature map, one vector per 32x32 block of pixels. SmolVLA resizes to 512x512 and keeps 64 visual tokens per frame. BridgeData V2 recorded 640x480 and trained most baselines on 128x128.
| Rig or model | What it captured | What reached the network |
|---|---|---|
| ALOHA / ACT (2023) | 4x Logitech C922x, 480x640, 30 fps | 480x640 per camera into a ResNet18, 15x20x512 |
| BridgeData V2 (2023) | RealSense D435, 2x Logitech C920, a Pi camera module, 640x480 | 128x128 for most baselines, 320x256 for RT-1 |
| SmolVLA (2025) | community LeRobot datasets | resized to 512x512, 64 visual tokens per frame |
| Isaac-GR00T SO-100 example | 2 OpenCV cameras, 640x480, 30 fps | keys front and wrist, current frame only |
640x480 is where the reference rigs above converged, and none of them fed the network more than 640 pixels on the long side. What it reliably buys is four times the bus traffic and a rig that breaks on the third camera. Spend the savings on manual controls instead.
SmolVLA's authors hit this across community datasets: a key called images.laptop might be a top, side, or wrist view depending on who recorded it, and they found the inconsistency harmful in pretraining. NVIDIA's Isaac-GR00T SO-100 example expects exactly front and wrist, from observation.images.* in meta/modality.json. Pick those names on day one: what you type into the recorder becomes a key in the LeRobot dataset.
The bandwidth ceiling is arithmetic, not bad luck
This is the part that eats a day. Two cameras work. You mount the third, and either it refuses to open or all three drop frames. The cable is fine and the camera works alone. USB 2.0 allocates isochronous bandwidth up front, per endpoint, regardless of what the cameras send.
The uvcvideo FAQ, written by the driver's maintainers, states it plainly. High speed USB carries at most 480 Mb/s including protocol overhead, and the standard caps periodic transfers at 80 percent of that. That 384 Mbit/s is shared by every webcam, microphone, keyboard and mouse on the controller, and the cap applies to allocation, not usage: bandwidth reserved and never used is still gone.
| Stream | Bytes per second | Bus bandwidth | Fits in 384 Mbit/s? |
|---|---|---|---|
| 1x 640x480 YUYV 30 fps | 18.4 MB/s | 147.5 Mbit/s | yes |
| 2x 640x480 YUYV 30 fps | 36.9 MB/s | 294.9 Mbit/s | yes, 89 Mbit/s spare |
| 3x 640x480 YUYV 30 fps | 55.3 MB/s | 442.4 Mbit/s | no, over by 58 Mbit/s |
| 1x 1280x720 YUYV 30 fps | 55.3 MB/s | 442.4 Mbit/s | no, one camera blows it |
| 1x 1920x1080 YUYV 30 fps | 124.4 MB/s | 995.3 Mbit/s | no, over twice the bus |
That is width x height x 2 bytes x fps, what YUYV costs uncompressed. It is worse than the table suggests, because cameras over-report. Some always request the single-endpoint maximum of 198.608 Mb/s; two of those ask 397 Mbit/s and blow the cap before a frame moves. The Good Penguin measured one requesting 195 Mbit/s for a stream needing 46.
# Which controller is each camera on?
# Devices under the same root hub share one 384 Mbit/s budget.
lsusb -t
# What formats and frame rates does this camera really offer?
# The fps lists for MJPG and YUYV are usually different.
v4l2-ctl -d /dev/video0 --list-formats-ext
# What is it streaming right now?
v4l2-ctl -d /dev/video0 --get-fmt-videoNo space left on device (ENOSPC) on a camera open, with an empty disk, is the USB stack refusing to reserve isochronous bandwidth. If the stream opens but the kernel log fills with uvcvideo: USB isochronous frame lost, the allocation succeeded but data is not arriving. LeRobot makes both look identical: the reader logs Error reading frame in background thread, tolerates ten failures, then raises exceeded maximum consecutive read failures. Read dmesg first.
Four fixes, in order of effectiveness: compress on the camera, shrink the frame, unplug other periodic devices (USB audio reserves isochronous bandwidth too), and add a second controller. Only the last raises the ceiling. The cap is per controller, so extra hubs do not help, and a blue USB 3 port usually changes nothing either, since a USB 3 receptacle still routes USB 2.0 devices onto the USB 2.0 bus. The Raspberry Pi documentation is blunt about the Pi 4: all four ports connect to a single USB 2.0 hub inside the VL805, limiting total USB 2.0 bandwidth to that of one port. A cheap PCIe USB card is a real second controller; a hub is not. Hosting on a Pi? Read the client guide first.
MJPEG or YUYV: the setting that decides whether the rig works
Most UVC webcams offer the same resolutions twice, as uncompressed YUYV and as MJPEG. YUYV is the default on many systems and the one that will not scale. MJPEG compresses on the camera, before the data crosses the wire, the only place compression helps.
Arducam's documentation for its OV9281 UVC module lists MJPG at 640x480 running 50, 30, 15, 10 and 5 fps, while YUV2 on the same device appears only at 1600x1200, 1280x720 and 800x600, all capped at 5 fps. Same sensor, same cable. The pixel format decides which frame rates exist at all.
from lerobot.cameras.opencv import OpenCVCamera, OpenCVCameraConfig
from lerobot.cameras import ColorMode, Cv2Rotation
config = OpenCVCameraConfig(
index_or_path="/dev/v4l/by-id/usb-Acme_HD_Webcam_0001-video-index0",
fps=30,
width=640,
height=480,
fourcc="MJPG", # compress on the camera, not on the bus
color_mode=ColorMode.RGB,
rotation=Cv2Rotation.NO_ROTATION,
warmup_s=1,
)
with OpenCVCamera(config) as camera:
frame = camera.read()
print(frame.shape) # (480, 640, 3)First, fourcc must be a four character string, validated in __post_init__. Second, and this is the trap, setting it is best effort: LeRobot calls the OpenCV setter, reads the value back, and if the camera refused, logs 'failed to set fourcc' and 'Continuing with default format'. It does not raise. Your rig keeps running on YUYV and you find out when the third camera will not open. Resolution and frame rate do raise a RuntimeError.
Separately, index_or_path accepts a path, not just an integer, and LeRobot's docs warn that indices can change after a reboot or re-plug. On Linux, udev keeps stable symlinks under /dev/v4l/by-id/ keyed to vendor, product and serial. Passing one instead of 0 removes the 'wrist and front cameras swapped overnight' class of bugs. If they already swapped inside a recorded dataset, the symptom is a policy that only works in one setup.
- The only compression that happens before the USB bus, so the only one that fixes a bandwidth problem. It is what makes three cameras on one controller possible at all.
- Most webcams expose higher frame rates in MJPEG than in YUYV, sometimes by a factor of ten.
- The uvcvideo FAQ lists it as its second recommended workaround for the out-of-bandwidth error.
- Every frame is JPEG decoded on the host CPU, so on a small single-board computer the trade can go the wrong way.
- Lossy on the camera and again when LeRobot re-encodes to video. Thin wires and low-contrast edges take two hits.
- Bandwidth is variable and scene-dependent, so the driver cannot estimate it, and a cluttered scene produces larger frames than the clean bench you set up on.

Frame rate: match the loop, not the box
A camera advertised at 60 fps is no better than one at 30 if you record at 30. What matters is whether it sustains your rate under your lighting with the other cameras attached. ALOHA records at 50 Hz while its appendix notes all four cameras stream 480x640 at 30 fps; BridgeData V2 ran its loop at 5 Hz.
LeRobot measures this for you. Recent versions of lerobot-record pace the loop at the configured dataset fps and print a one-line digest for every episode you keep, plus a summary when recording ends that breaks the loop down by step. In that documented summary the observe step, the camera read, averages 13.65 ms and peaks at 40.20 ms against a 33.3 ms budget, and is 74.4 percent of the loop's work. Pacing headroom near zero means one slow frame pushes you over.
Cadence (episode 0): 29.88 Hz vs 30 Hz target, 600 ticks, 20.0 s measured,
8/599 ticks over the 33.3 ms budget (work mean 18.4 ms, worst 45.1 ms)
Cadence summary, whole run, 2 episodes, target 30 Hz (33.3 ms budget per tick):
effective cadence: 29.88 Hz over 40.1 s measured
ticks over the 33.3 ms work budget: 16/1198 (1.3%), work mean 18.4 ms, worst 45.1 ms
loop-body steps (share of measured work):
observe mean 13.65 ms worst 40.20 ms 74.4% of work 1200 calls
process_obs mean 0.30 ms worst 0.30 ms 1.6% of work 1200 calls
teleop mean 1.80 ms worst 1.80 ms 9.8% of work 1200 calls
send mean 0.90 ms worst 0.90 ms 4.9% of work 1200 calls
record mean 1.70 ms worst 1.90 ms 9.3% of work 1200 calls
pacing headroom: 15.1 ms slept per tick on average (max 16.7 ms)A frame's timestamp is derived from its index, not from when the frame arrived. If your cameras only sustained 25 Hz but you configured 30, the dataset still claims 30, and nothing errors. The recorded motion is faster than what happened, and every velocity the policy learns is wrong by 20 percent. The fix is to lower the recording rate to what the rig can hold.
Exposure is the control nobody sets
Exposure time and frame rate are the same budget: a camera cannot expose for 40 ms and also deliver 30 frames per second. The uvcvideo FAQ answers 'why does my webcam only produce 15 fps when it advertises 30' with exactly this. In a dim room, auto exposure raises exposure and lowers the frame rate to pay.
A rig that held 30 Hz in the afternoon can drop to 15 Hz in the evening, no error anywhere, still writing timestamps that claim 30. V4L2 has a control for it: V4L2_CID_EXPOSURE_AUTO_PRIORITY, named 'Exposure, Dynamic Framerate' in the kernel and exposed by v4l2-ctl as exposure_dynamic_framerate. The kernel documentation scopes it: when auto exposure is set to Auto or Aperture Priority, this control decides whether the device may vary the frame rate, and by default it is disabled and the rate must stay constant. Cameras do not always honour that default. Set it to 0 explicitly whenever you leave auto exposure on.
- 1See what the camera exposes
List the video nodes, then dump every control with its range and current value. Controls marked
inactiveare held by an auto mode.bashv4l2-ctl --list-devices v4l2-ctl -d /dev/video0 --list-ctrls - 2Switch off dynamic frame rate first
It only bites while auto exposure is on: the kernel scopes it to the Auto and Aperture Priority modes. Set it to 0 before anything else, so the camera cannot trade frame rate for exposure without telling you. Harmless to set in manual mode too.
bashv4l2-ctl -d /dev/video0 --set-ctrl=exposure_dynamic_framerate=0 - 3Set manual mode, then the exposure time
Order matters. In uvcvideo,
V4L2_CID_EXPOSURE_ABSOLUTEis a slave ofV4L2_CID_EXPOSURE_AUTOwithmaster_manual = V4L2_EXPOSURE_MANUAL, so exposure time stays inactive until auto exposure is manual. Writing it first does nothing. Menu value 1 is Manual Mode, 3 is Aperture Priority Mode. White balance uses the same master/slave mechanism and needs the same two steps, but its manual value is 0: setwhite_balance_automatic=0first, then the temperature.bashv4l2-ctl -d /dev/video0 --set-ctrl=auto_exposure=1 v4l2-ctl -d /dev/video0 --set-ctrl=exposure_time_absolute=157 - 4Read the values back
Every one can fail silently. Read them back from the device while it is streaming, because some drivers reset controls when the stream starts.
bashv4l2-ctl -d /dev/video0 --get-ctrl=auto_exposure,exposure_time_absolute,exposure_dynamic_framerate
The unit for exposure_time_absolute is 100 microsecond steps, so 10000 is one second. 157 is roughly 15.7 ms, about as long as you can expose and still clear a 30 fps budget. If that looks dark, add light rather than exposure time. Light costs no frame rate.
It would be tidy to say 'turn every automatic off', but the reference rig for the whole ACT line did not. The ALOHA appendix states that for all cameras the focal length is fixed with auto-exposure on, to adjust for changing lighting. Auto exposure gives roughly constant brightness across varying light, while fixed exposure bakes your recording week's lighting into the dataset. Fixed focus is close to unarguable; fixed exposure is a trade. Either way, set exposure_dynamic_framerate=0.
The rest of the automatic stack
Autofocus is the automatic with no upside on a fixed rig. A wrist camera sitting close to a gripper hunts every time the arm moves, dropping blurred frames into the dataset as if they were sharp. Focus changes also alter apparent scale, so the same object looks different between episodes. ALOHA fixed focal length on all four cameras. Every control below resets on a re-plug, so keep them in a script beside your recording command.
| Control (v4l2-ctl name) | Set it to | Why |
|---|---|---|
| focus_automatic_continuous | 0, then set focus_absolute once | Blur bursts, and apparent object scale shifts. |
| exposure_dynamic_framerate | 0 | Stops the camera trading frame rate for exposure silently. Applies while auto exposure is on. |
| auto_exposure | 1 (Manual), or 3 for ALOHA's approach | Gate for exposure_time_absolute. Nothing works until it is set. |
| white_balance_automatic | 0, then set the temperature | Keeps scene colour constant across a multi-day session. |
| power_line_frequency | 1 for 50 Hz mains, 2 for 60 Hz | It is a menu, not a frequency: 0 disabled, 1 is 50 Hz, 2 is 60 Hz. The wrong one gives rolling brightness bands under fluorescent and LED light. |
Rolling shutter, global shutter, and whether you care
Almost every USB webcam has a rolling shutter: the sensor reads row by row, so the top of the frame is captured milliseconds before the bottom, and fast motion makes straight edges lean. NVIDIA's Isaac-GR00T SO-100 configuration feeds the current frame only, delta_indices [0], so no temporal context averages that away. Even so, at SO-100 speeds it is second-order next to motion blur, and the fix for both is the same: shorten the exposure, add light. Global shutter UVC modules exist, such as Arducam's OV9281, but cost more and are usually monochrome. Prefer a manual-focus lens over more pixels. The SO-100 setup guide covers the arm side, and the data collection page covers what a usable session looks like end to end.
Verify the rig in ten minutes, before you record 50 episodes
Every policy here needs 30 to 50 episodes before it is worth training on, so a fault found at episode 48 costs a whole session. This is the cheapest insurance in the pipeline. What makes those episodes worth keeping, beyond the cameras, is covered in the guide to collecting high-quality VLA training data.
- 1Enumerate what is attached
LeRobot ships a discovery command. It prints identifier, backend and default stream profile per camera, the fastest way to spot one defaulting to 15 fps at 1920x1080.
bashlerobot-find-cameras opencv - 2Confirm the bus topology
Cameras under the same root hub share one 384 Mbit/s allocation. If all three sit on one controller and you plan to run uncompressed, fix that first.
bashlsusb -t v4l2-ctl -d /dev/video0 --list-formats-ext | grep -A3 MJPG - 3Open every camera at once, not one at a time
The whole point: a camera that works alone tells you nothing. Start the recorder with the full camera set at the real resolution and frame rate, and watch for the fourcc warning as it connects.
bashlerobot-record \ --robot.type=so101_follower \ --robot.port=/dev/ttyACM0 \ --robot.id=my_follower \ --robot.cameras="{ front: {type: opencv, index_or_path: /dev/video0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30}}" \ --teleop.type=so101_leader \ --teleop.port=/dev/ttyACM1 \ --teleop.id=my_leader \ --dataset.repo_id=${HF_USER}/camera-check \ --dataset.num_episodes=2 \ --dataset.single_task="Pick up the cube" - 4Record two throwaway episodes, then read the cadence report
Two is enough. Check effective cadence against target, observe as a share of work, and pacing headroom. If headroom is near zero, lower the frame rate or resolution now, then check the kernel log for what the recorder swallowed.
bashdmesg | grep -i uvcvideo | tail -20 - 5Look at the frames, not only the numbers
With
--display_data=true, watch the wrist view while the arm moves fast. Blur bursts mean exposure is too long. Tilted vertical edges are rolling shutter. A brightness step between episodes means auto exposure is live.bashlerobot-record ... --display_data=true
Two ways to get a working camera rig
All of it works with open source tools on your own machine: LeRobot for the recorder, v4l-utils for the controls, your own eyes on the cadence report.
# 1. install the tools
pip install lerobot
sudo apt install v4l-utils # Linux camera controls
# 2. discover cameras and their default profiles
lerobot-find-cameras opencv
# 3. pin the controls, per camera, every session
for D in /dev/video0 /dev/video2; do
v4l2-ctl -d $D --set-ctrl=exposure_dynamic_framerate=0
v4l2-ctl -d $D --set-ctrl=auto_exposure=1
v4l2-ctl -d $D --set-ctrl=exposure_time_absolute=157
v4l2-ctl -d $D --set-ctrl=white_balance_automatic=0
v4l2-ctl -d $D --set-ctrl=focus_automatic_continuous=0
done
# 4. record, with MJPG and stable device paths
lerobot-record --robot.type=so101_follower ... --display_data=true- You control every knob, including ones no product UI exposes.
- v4l2-ctl is Linux only. macOS gives AVFoundation and far fewer controls; on Windows LeRobot sets the FOURCC after the resolution, because changing resolution can silently override it.
- You own the debugging when the third camera fails, and the failure is a kernel log line, not a dialog.
- Nothing here needs an account, a GPU, or a network connection.
The platform replaces none of that: the USB bus is on your desk, not in the cloud. It removes the surrounding work, so cameras are the only thing you debug.
- The desktop client records LeRobot-format datasets, episodes, camera streams and joint states, straight from a teleoperation session, so the recorder is not another thing to configure.
- A physical arm streams on /live, no signup, queue based, so you can see a working camera view before owning hardware.
- The public dataset directory lets you inspect other people's camera keys before fixing your own naming.
- Training rents a GPU by required VRAM on a spot market, so a bad rig costs 1 to 3 USD on the 24 GB tier, or 4 to 12 USD on the A100 and H100 tier, before you find out.
- The CLI and the MCP server expose the same operations to a terminal and to an agent, so a scripted pre-flight check is easy.
| Policy | Minimum episodes | Inference per action step | GPU tier |
|---|---|---|---|
| ACT | 50 | 20 ms | RTX 4090 or any 24 GB card |
| SmolVLA | 30 | 245 ms | RTX 4090 or any 24 GB card |
| GR00T N1.7 | 50 | 152 ms | A100 80 GB or H100 80 GB |
| GR00T N1.5 | 50 | 165 ms | A100 80 GB or H100 80 GB |
| Pi0.5 | 50 | 485 ms | A100 80 GB or H100 80 GB |
Those minimums are why verification pays for itself: 30 to 50 episodes is a real afternoon, and a camera fault contaminates all of them. Full comparison on the policies page, and the GR00T N1.7 on SO-100 guide covers training once the data is good.
Where this does not help
Three honest limits. First, no platform fixes your USB bus. If three cameras will not fit on one controller, that holds whether you record locally or through a hosted client.
Second, camera choice does not rescue remote inference latency. The control loop here runs 20 to 485 ms per action step depending on the model, and public-internet round trips turn a working policy into a hesitant one. Remote inference is viable for slow pick and place, not fast reactive motion. A better camera does not change that.
- Third: a decision made at recording time can bite at training time. Fine-tuning GR00T needs a LeRobot v2.0 or v2.1 dataset; a v3.0 dataset crashes the GR00T loader and must be converted down to v2.1.
- That is a dataset problem rather than a camera one, but it lands in the same place: a session to redo. The dataset-rejected-v3 page covers the conversion.

Record LeRobot datasets without building the recorder
The AY-Robots desktop client records episodes, camera streams and joint states in LeRobot format straight from a teleoperation session, so the only thing left to get right is the cameras themselves.
Get the desktop clientQuestions people actually ask
How many USB cameras can I actually run at once?▾
Two at 640x480 and 30 fps in uncompressed YUYV fit, using 294.9 of the 384 Mbit/s allowed for periodic transfers. Three need 442.4 Mbit/s and do not. In MJPEG three is normally fine, because the camera compresses before the bus. Beyond that it depends on how truthfully your cameras report: some always request the single-endpoint maximum of 198.608 Mb/s, and two of those exceed the cap.
Does a USB 3 hub solve the bandwidth problem?▾
Usually not. A USB 2.0 webcam in a USB 3 port still runs on the USB 2.0 bus, and hubs on one controller share one budget. On the Raspberry Pi 4 all four ports connect to a single USB 2.0 hub inside the VL805, so all four share the bandwidth of one. What helps is a separate host controller, such as a cheap PCIe USB card, which the uvcvideo FAQ recommends explicitly.
Is 1080p worth it for robot training data?▾
On the evidence here, no: the reference rigs all recorded 640x480, and the models discard most of what they are given. ACT turns a 480x640 frame into a 15x20 feature grid; SmolVLA resizes to 512x512 and keeps 64 visual tokens per frame; BridgeData V2 trained most baselines at 128x128. One 1080p YUYV stream at 30 fps needs 995.3 Mbit/s, over twice the USB 2.0 bus.
Why did my frame rate drop when the sun went down?▾
Exposure time and frame rate are the same budget. In auto exposure a camera in a dim room lengthens exposure and pays for it with fewer frames per second. The uvcvideo FAQ gives this as the standard explanation for a 30 fps camera producing 15. Set exposure_dynamic_framerate to 0, and add light rather than exposure time.
My camera ignores the MJPG setting. How would I know?▾
You would not, unless you look. LeRobot sets the FOURCC, reads it back, and on a mismatch logs 'failed to set fourcc' and 'Continuing with default format', then carries on. Resolution and frame rate raise a RuntimeError when rejected; the pixel format does not. Check the connect-time log, or confirm with v4l2-ctl --get-fmt-video while streaming.
Sources
- Linux UVC driver and tools FAQ (uvcvideo maintainers)
- LeRobot documentation: Cameras
- LeRobot documentation: imitation learning on real robots, recording and cadence reporting
- LeRobot source: OpenCVCameraConfig (fourcc, warmup_s, backend)
- huggingface/lerobot
- Linux kernel: V4L2 Camera Control Reference
- Multiple UVC cameras on Linux: an unexpected challenge
- Raspberry Pi documentation: the USB bus on Raspberry Pi
- raspberrypi/linux issue 3406: cannot use multiple USB webcams at higher resolutions
- Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware (ALOHA and ACT)
- BridgeData V2: A Dataset for Robot Learning at Scale
- SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics
- NVIDIA Isaac-GR00T: SO-100 example, modality.json and camera keys
- Arducam OV9281 global shutter UVC camera: supported formats and frame rates
Sources
- Linux UVC driver and tools FAQ (uvcvideo maintainers)
- LeRobot documentation: Cameras
- LeRobot documentation: imitation learning on real robots, recording and cadence reporting
- LeRobot source: OpenCVCameraConfig (fourcc, warmup_s, backend)
- huggingface/lerobot
- Linux kernel: V4L2 Camera Control Reference
- Multiple UVC cameras on Linux: an unexpected challenge
- Raspberry Pi documentation: the USB bus on Raspberry Pi
- raspberrypi/linux issue 3406: cannot use multiple USB webcams at higher resolutions
- Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware (ALOHA and ACT)
- BridgeData V2: A Dataset for Robot Learning at Scale
- SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics
- NVIDIA Isaac-GR00T: SO-100 example, modality.json and camera keys
- Arducam OV9281 global shutter UVC camera: supported formats and frame rates
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started