Camera placement is the one data-collection decision you cannot revise after the fact. An RLBench evaluation published in April 2026 measured ACT at 0.83 success at the training viewpoint and 0.13 at 45 degrees of deviation, with pi0 falling from 0.84 to 0.24 over the same range, and a separate Franka study recorded four real tasks scoring 8/10, 8/10, 10/10 and 8/10 at the training viewpoint against 5/30, 1/30, 19/30 and 1/30 once the camera moved. The mechanism is a shortcut: policies without camera extrinsics infer pose from static background cues, so re-tooling a cell can break them even when nobody touches the camera. Start from the verified reference points, which are the ACT rig (four cameras at 480x640, 50 Hz, two of them wrist-mounted) and the LeRobot guidance of preferably two views at 480x640 or better, around 30 fps, at least 50 episodes with 10 per location, cameras fixed. Wrist views are not optional: removing them cost 9% and 43% relative success on the robomimic Square and Transport tasks, and dropped a real Can task from 73.3% to 43.3%. Conditioning on camera extrinsics lifted ACT on Lift from 33.6% to 60.6% and SmolVLA from 19.6% to 54.4%, but that is a research contribution with released code, not a LeRobot flag, and LeRobotDataset v3 has no field for camera pose at all. LeRobot's built-in image transforms are ColorJitter, SharpnessJitter and Identity, all photometric, so none of them buys viewpoint robustness. This week, photograph and measure your rig, write the extrinsics into a sidecar keyed by episode index, then shift one camera in known increments until the policy breaks so you know your own tolerance.
Where to put the cameras for robot imitation learning usually gets decided last, after the arm, the gripper and the teleoperation rig, and whatever bracket was already there settles it. That ordering is backwards. An RLBench evaluation published in April 2026 measured ACT falling from 0.83 success at the training viewpoint to 0.13 at 45 degrees of deviation. The policy did not get worse at the task. It stopped recognising the world.
A second group measured the same collapse on a real Franka Emika Panda, and a third found the mechanism. A policy given no camera extrinsics uses your static background as a coordinate frame, so re-tooling a cell can break it with nobody touching a mount. Camera placement is the one collection decision you cannot revise afterwards.
Moving the camera is what breaks the policy
Three independent groups, two simulators and two real arms, measuring the same thing.
The close-laptop row survives at 19/30 because the task is coarse: a rough estimate of where the lid is still produces a working push. The three tasks needing the gripper at a specific pose go to near zero. Task tolerance decides how much viewpoint error you can absorb, not policy architecture.
The mechanism paper is blunt: "policies without extrinsics often infer camera pose using visual cues from static backgrounds in fixed scenes; this shortcut collapses when workspace geometry or camera placement shifts." That is view-invariant policy learning with camera conditioning. The RLBench rows come from a view-synthesis system, the Franka rows from a sim-to-real image translation study trained on 35,294 real fixed-camera observations plus 59,520 simulated.
| Setup | Policy | At the training viewpoint | After the viewpoint changes |
|---|---|---|---|
| RLBench, deviation to 45 degrees | ACT | 0.83 | 0.13 |
| RLBench, deviation to 45 degrees | pi0 | 0.84 | 0.24 |
| Franka real, pick up coke, no augmentation | ACT | 8/10 | 5/30 |
| Franka real, stack cups, no augmentation | ACT | 8/10 | 1/30 |
| Franka real, close laptop, no augmentation | ACT | 10/10 | 19/30 |
| Franka real, stack blocks, no augmentation | ACT | 8/10 | 1/30 |
How many cameras and where
Two reference layouts are fully verifiable, and both are more conservative than the specs circulating on vendor pages.
The ACT and ALOHA rig used four Logitech C922x webcams at 480x640 RGB, two on the wrists of the follower robots and two at the front and the top, recording at 50 Hz with 50 demonstrations per task. It has no ablation over camera count or resolution, so treat it as a working reference, not an optimum.
The LeRobot maintainers publish the other: preferably two views, the leader arm out of frame, the only moving objects being the follower arm and the manipulated items, at least 480x640 or 720p, approximately 30 FPS. Their recording guidance is blunter. "We suggest recording at least 50 episodes, with 10 episodes per location. Keep the cameras fixed and maintain consistent grasping behavior throughout the recordings." And steal their placement test: you should be able to do the task yourself by only looking at the camera images.
The wrist figures come from the robomimic study of offline human demonstrations, whose authors called wrist images and image randomization together "the difference between a proficient and non-proficient real-world policy". Transport is the two-arm task.
The view-count figures come from a multi-camera view scaling study testing 1, 3 and 5 views, which reports equivalent performance with 3 to 5 times fewer expert trajectories than a single-view baseline. On a real FANUC CRX-10iA with two cameras and 50 demonstrations, dual-view training moved success from 0.45 to 0.75 at 25 trials. That is the evidence behind one line in how much data you need to train a robot policy: a wrist camera and an extra angle beat another thousand episodes.
| View | What it carries | Measured effect | What it costs |
|---|---|---|---|
| Third-person | Scene layout, object location before approach | Sole view in the collapse studies above, so it carries the shortcut | A bracket that must never move |
| Wrist | Gripper and object at contact, occlusion-free at the decision moment | Removing it cost 9% and 43% relative success on robomimic Square and Transport, and 73.3% to 43.3% on a real Can task | Strain relief, wrist mass, motion blur |
| Extra angles | Redundant geometry, recovers occluded configurations | 5 views versus 1 raised simulated success 0.14 to 0.18 on Square, 0.18 to 0.37 on Can, 0.69 to 0.85 on Lift | Linear VRAM and dataloader cost |
Resolution and frame rate against VRAM
Capture resolution is not a quality dial, it is a token budget. The ACT backbone is a ResNet18 that divides each spatial dimension by 32, so 480x640 becomes a 15x20x512 feature map, 300 tokens per camera, and four cameras give a 1200x512 sequence.
That is arithmetic on the divide-by-32 rule and the quadratic cost of attention, not a measured benchmark, and it explains the most common LeRobot support thread. A report opened in October 2025 describes an SO-101 arm on an RTX 3060 with 8GB of VRAM, 49 episodes and about 39,000 frames across two 1080p views, ACT at roughly 26 million parameters trained for 100k steps, asking whether those images can be downscaled to 480p for larger batches. No maintainer answered, so take it as evidence of the constraint, not advice: at 1080p across two views, batch size is set by the camera spec.
High-resolution capture therefore buys nothing at the model input while costing storage, bandwidth and dataloader time every epoch; the GPU cost of that checkpoint choice is in OpenVLA vs pi0 vs SmolVLA vs GR00T N1. The ACT row is the harder constraint, the ACTConfig docstring stating that "Right now we only support all images having the same shape", so resolution is a rig-wide decision taken once.
Frame rate carries a subtler trap. lerobot-record paces the loop at --dataset.fps and reports what it achieved per episode and per session, down to the share of loop time spent in the observe step. A frame's timestamp is derived from its index, so a session that ran at 25 Hz still produces a dataset claiming 30, the recorded motion simply faster than reality. Cameras are usually what pushes those ticks over budget.
| Capture resolution | ResNet18 feature grid | Tokens per view | 4-view sequence | Relative attention term |
|---|---|---|---|---|
| 480x640 | 15x20 | 300 | 1200 | 1x |
| 720x1280 | 23x40 | 920 | 3680 | roughly 9x |
| 1080x1920 | 34x60 | 2040 | 8160 | roughly 46x |
| Policy config | Image handling at the model input | chunk_size | n_action_steps |
|---|---|---|---|
| ACTConfig | No resize field; every view shares one shape | 100 | 100 |
| PI0Config | image_resolution=(224, 224) | 50 | 50 |
| SmolVLAConfig | resize_imgs_with_padding=(512, 512) | 50 | 50 |
Tell the policy where the camera is
The direct fix is to stop making the policy guess. Conditioning on camera extrinsics through Plucker embeddings of per-pixel rays improved all three tested architectures on all six tasks.
All figures are success rates in percent. Gains are largest where the base policy was weakest, the signature of removing a failure mode rather than adding capability.
The rig-design consequence is one sentence from that paper: "to achieve the same performance, training without camera pose conditioning requires several times more cameras compared to training with pose conditioning." The supporting figure has a logarithmic camera-count axis and no numeric table behind it, so the multiplier is not recoverable, but extrinsics are cheaper than cameras.
One implementation detail matters if you build this: random cropping was applied to both the images and the Plucker maps. Crop the image without the ray map and you have taught the policy a lie.
Conditioning also couples to the action space: Delta End-Effector Pose actions measured best, ahead of Absolute Joint Position and Absolute End-Effector Pose, and the view-scaling group independently ranked camera-space actions above base-space above end-effector-space. Specify a relative pose in the collection contract, not absolute joint targets. There is no LeRobot flag for any of this.
| Task | ACT without / with | Diffusion Policy without / with | SmolVLA without / with |
|---|---|---|---|
| Lift | 33.6 / 60.6 | 29.1 / 51.1 | 19.6 / 54.4 |
| Pick Place Can | 26.7 / 30.9 | 23.1 / 39.3 | 56.0 / 70.0 |
| Assembly Square | 10.8 / 18.7 | 2.0 / 2.4 | 22.0 / 26.4 |
| Push | 29.9 / 37.5 | 20.0 / 30.3 | 39.0 / 43.8 |
| Lift Upright | 22.9 / 34.6 | 9.5 / 20.7 | 23.6 / 33.4 |
| Roll Ball | 28.7 / 29.7 | 19.9 / 23.7 | 27.6 / 30.4 |
Recovering a rig you cannot re-mount
Sometimes the camera has already moved, or three lines each frame the workspace differently. View augmentation buys back part of the loss, as successes over trials aggregated across three shifted viewpoints:
Augmentation recovers a real fraction of the collapse and never all of it: stack cups goes from 1/30 to 13/30, a large relative gain and still a policy you would not ship. The ranking is not uniform either, with VISTA ahead on three of four tasks and MANGO winning only on stack cups. Benchmark on your own tasks.
A newer line of work drops the calibration requirement. A calibration-free view-robust VLA published in July 2026 states the plant engineer's version: "Real-world robot deployment rarely maintains the training-stage camera setup, where cameras often experience repositioning or remounting depending on actual scenarios." It predicts a camera-centric end-effector action plus a 6-DoF hand-eye matrix and composes them geometrically. That is where the field is heading, not a production option yet.
What is definitely not the answer is LeRobot's built-in augmentation. The transforms are ColorJitter, SharpnessJitter and Identity, configured through ImageTransformsConfig (defaults enable=False, max_num_transforms=3) and applied at training time only. All of it is photometric, so --dataset.image_transforms is a lighting measure, never a viewpoint one.
| Augmentation | pick up coke | stack cups | close laptop | stack blocks |
|---|---|---|---|---|
| None | 5/30 | 1/30 | 19/30 | 1/30 |
| Sim | 14/30 | 5/30 | 20/30 | 9/30 |
| Sim with domain randomization | 19/30 | 5/30 | 25/30 | 3/30 |
| VISTA | 23/30 | 8/30 | 29/30 | 18/30 |
| MANGO | 17/30 | 13/30 | 22/30 | 11/30 |
Locking the rig down
The geometry that was true during collection must still be true at inference, and you must be able to prove it. LeRobot gives you the capture half and none of the record-keeping half.
lerobot-find-cameras opencv # or: lerobot-find-cameras realsense
lerobot-record \
--robot.type=so101_follower \
--robot.port=/dev/tty.usbmodem585A0076841 \
--robot.id=cell_a_follower \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: /dev/video10, width: 640, height: 480, fps: 30}}" \
--teleop.type=so101_leader \
--teleop.port=/dev/tty.usbmodem58760431551 \
--teleop.id=cell_a_leader \
--display_data=true \
--dataset.repo_id=acme/cell-a-tray-load \
--dataset.single_task="Load the tray into the fixture" \
--dataset.num_episodes=50 \
--dataset.fps=30 \
--dataset.root=/srv/robotdata/cell-a-tray-load \
--dataset.push_to_hub=FalseTwo details are load-bearing. The camera keys become observation keys, so name them by location (front, top, left, right, wrist), never by device. And --dataset.push_to_hub=False with an explicit --dataset.root keeps the footage on the machine, which otherwise lands in ~/.cache/huggingface/lerobot/.
During recording, the left arrow or r re-records the current episode, right arrow or n ends it early, ESC or q stops and encodes. Use the re-record key whenever anyone bumps the rig. Afterwards, --dataset.episodes keeps a list of indices and --dataset.exclude_episodes drops more on top of it, documented as episodes to drop when corrupt or heterogeneous.
{
"dataset": "acme/cell-a-tray-load",
"rig_revision": "cellA-r3",
"cameras": {
"front": {
"mount": "kinematic base plate, cell frame column B, torqued fasteners",
"intrinsics_file": "calib/front_intrinsics_2026-08-10.yaml",
"extrinsics_base_to_cam": "calib/front_extrinsics_2026-08-10.yaml",
"verified_at_episodes": [0, 25, 49]
},
"wrist": {
"mount": "printed bracket, link 6, two M3",
"intrinsics_file": "calib/wrist_intrinsics_2026-08-10.yaml",
"extrinsics_ee_to_cam": "calib/wrist_handeye_2026-08-10.yaml",
"verified_at_episodes": [0, 25, 49]
}
}
}camera_spec: # extends the schema clauses in the format contract
views:
- {name: front, mount: fixed_frame, purpose: scene_layout}
- {name: wrist, mount: link6_bracket, purpose: contact_detail}
identical_across_views: true # ACT requires one shape for the whole stack
width: 640
height: 480
fps: 30
geometry_record:
intrinsics: per_camera_per_session
extrinsics: per_episode # nothing in LeRobotDataset v3 stores this
format: sidecar_json_keyed_by_episode_index
remount_tolerance:
method: measured # no published standard defines a number
procedure: shift_in_known_increments_until_acceptance_fails
recorded_in: acceptance_report
revalidation_trigger: # any of these voids the acceptance evidence
- camera_moved_or_replaced
- bracket_or_fixture_modified
- workspace_background_changed
- lens_or_focus_adjustedWhere the data comes from at all is a separate axis in teleoperation vs simulation vs human video. Camera geometry constrains all three the same way.
The record LeRobot will not keep for you
LeRobotDataset v3 metadata covers meta/info.json (schema, FPS, path templates), meta/stats.json (normalization statistics), meta/tasks.jsonl (task strings mapped to integer IDs) and meta/episodes/ (per-episode lengths, tasks and offsets), plus the Parquet and MP4 shards. No field holds camera intrinsics or extrinsics, there is no free-form per-episode slot, and calibration in the robot abstraction means motor calibration, a dict of MotorCalibration entries. Keep the geometry out of band, keyed by episode index: The check that populates verified_at_episodes is a two-minute routine: park the arm at a fixed home pose with a fiducial in view, capture one frame per camera, solve for the pose, compare against the reference. Run it every session and after any interruption.
Camera clauses for the collection contract
The seven schema and synchronisation lines every collection contract needs are in LeRobot vs RLDS vs HDF5. These five camera clauses extend that list. The tolerance clause is procedural on purpose. No dataset, paper or standard we could find defines a re-mount tolerance in millimetres or degrees for an imitation-learning rig, so any number a vendor quotes is invented. The buyer measures it and the integrator holds it. When a move forces re-validation, the trial arithmetic is in how many trials it takes to evaluate a robot policy.
| Situation | Views | Resolution and rate | The extra spend that pays |
|---|---|---|---|
| Fixed cell, one task, stable tooling | 1 third-person plus 1 wrist | 640x480 at 30 fps | None. Spend on episodes and diversity |
| Contact-rich or two-arm assembly | 1 to 2 third-person plus 1 wrist per arm | 480x640 at 30 to 50 fps | The second wrist camera |
| Cell re-tooled on a cadence | 3 or more third-person plus wrist | Identical across views | Per-episode extrinsics from day one |
| Several lines, same task, different framing | Several viewpoints, deliberately | Identical across all of them | View augmentation, benchmarked on your tasks |
| pi0 or SmolVLA fine-tune planned | 2 views | 640x480 already exceeds the model input | Nothing. The model resizes anyway |
| 8GB GPU, no upgrade budget | 2 views | 640x480, never 1080p | An honest batch-size test first |
Inside a regulated plant
In a German plant, Betriebsverfassungsgesetz Section 87 paragraph 1 number 6 gives the works council co-determination over the "Einführung und Anwendung von technischen Einrichtungen, die dazu bestimmt sind, das Verhalten oder die Leistung der Arbeitnehmer zu überwachen." It applies whether or not monitoring is the intent, so mount design, framing and retention get negotiated before collection starts, and re-aiming a camera can reopen the agreement. The EU anchor is the EDPB guidance on processing personal data through video devices, adopted in January 2020.
The rig can also end up inside the safety argument. Under the EU AI Act, Annex I Section A item 1 is Directive 2006/42/EC on machinery, so a vision policy acting as a safety component of a covered machine takes the Article 6(1) high-risk route, which Article 113 dates from 2 August 2027, and the rig and its geometry record become technical documentation. Under the 2025 revision of ISO 10218, a camera bracket inside the safeguarded space is a modification to the robot application that pulls the cell's risk assessment back into scope.
The footage also cannot leave the building. A camera aimed at a cell records part geometry, fixture design, process sequence, cycle time and sometimes the operator, which in pharma, defence supply and medical device manufacturing is among the most sensitive data on site. Everything above runs next to the cell: lerobot-record writes to --dataset.root, --dataset.push_to_hub=False keeps it there, and the sidecar never touches a network. The cost side is in cloud versus on-premise AI security and cost, the contractual side in data privacy restrictions on AI training, and the Physical AI and robotics hub covers the boundary.
What to do this week
Photograph your rig with a scale reference in frame, measure the extrinsics of every camera against the robot base, and write them into a JSON sidecar keyed by episode index. That is an afternoon, and it is what you will wish you had the first time somebody moves a bracket.
Then find your own breaking point. Take a policy you already trained, shift one third-person camera in known increments (2 cm, 5 cm, 10 cm, then 10 and 20 degrees of yaw) and run 20 rollouts at each step. The increment where success crosses your acceptance bar is the re-mount tolerance for your next contract.
FAQ
Quick answers to the questions this post tends to raise.



