
Every open VLA policy you can fine-tune today takes RGB, state and language. What RGB-only policies learn about geometry, when a depth camera earns its price, and what it costs.
What you need to know
- •All five policies you can fine-tune here take RGB frames, a state vector and text. None has a depth input.
- •GR00T's modality.json has four slots: state, action, video, annotation. Pi0.5's openpi Observation carries images, image_masks and state, keys named base_0_rgb, left_wrist_0_rgb, right_wrist_0_rgb.
- •LeRobot got depth recording in v0.6.0, 6 July 2026, as 12-bit HEVC Main 12 in gray12le. Recording depth and training on it are still separate problems.
- •Depth's payoff is generalization, not peak success: 3D-CAVLA gains 1.0 point on seen LIBERO tasks and 8.8 on unseen. In DP3's ablation raw depth scored below plain RGB.
- •A D435i is blind closer than about 28 cm, where an SO-100 wrist camera lives. The D405 is the short-range part, and a second RGB view is usually cheaper than either.
The short answer, and why it keeps surprising people
Depth is almost certainly not what is holding your policy back. Every model you can fine-tune on AY-Robots takes the same three things: RGB frames, a proprioceptive state vector, a string of text. Bolt a depth camera onto your SO-100 tomorrow and none of them will read the stream. The tensor has nowhere to go.
That is an argument about sequencing, not against depth. The question is what an RGB-only policy already knows about your geometry, where that runs out, and what would have to change before a depth channel earns its calibration work. What follows reads the observation spaces out of the model cards and repositories, and dates each one.
| Policy | Image input | Other inputs | Depth? | Read from |
|---|---|---|---|---|
| GR00T N1.7 | uint8 RGB through SigLip2, Cosmos-Reason2-2B backbone | Proprioception, text, embodiment ID | No | nvidia/GR00T-N1.7-3B card |
| GR00T N1.5 | 224x224 uint8 RGB, SigLip2 | Proprioception, text | No | nvidia/GR00T-N1.5-3B card |
| Pi0.5 | images dict plus a per-key image_mask, 224x224 | state vector, tokenized prompt | No | openpi, class Observation |
| SmolVLA | Top plus wrist camera on SO-100, 64 visual tokens per frame | state, language | No | arXiv 2506.01844 |
| ACT | Four Logitech C922x webcams at 480x640 in the ALOHA rig | Joint positions, 7+7 DoF | No | arXiv 2304.13705 |
GR00T datasets carry a meta/modality.json mapping state, action, video and annotation. The data preparation guide never mentions depth. Isaac-GR00T issue #652, "Gr00t N1.5/6/7 are any able to handle depth data into the VLA", was opened 23 April 2026 and was still open with no maintainer reply on 24 August 2026, so the claim rests on the schema and the cards. Separately, GR00T N1.7 loads LeRobot v2, so a LeRobot dataset recorded as v3.0 must be converted to v2.1 first: dataset rejected as v3.
What an RGB-only policy actually learns about geometry
RGB policies are not geometry-blind, they are metric-blind, which is much less serious. A policy trained by imitation learning never answers "how far away is that cube", only "given this image and these joint angles, what did the human do next". Occlusion, shading, relative size and the gripper's apparent motion carry that, and parallax under the robot's own motion is a depth signal, free on every episode. The schema below is the whole surface a GR00T dataset exposes:
// Isaac-GR00T, getting_started/data_preparation.md, meta/modality.json schema
{
"state": {
"<state_key>": {
"start": <int>, // Starting index in the state array
"end": <int> // Ending index in the state array
}
},
"action": {
"<action_key>": {
"start": <int>,
"end": <int>
}
},
"video": {
"<new_key>": {
"original_key": "<original_video_key>"
}
},
"annotation": {
"<annotation_key>": {}
}
}The wrist camera does most of the geometry work
On an SO-100 the wrist camera sits centimetres from the object at grasp time. Parallax is large there, occlusion boundaries are crisp, and the gripper fingers are in frame as a known-size ruler next to the end effector. That two-view layout is what published SO-100 work assumes: SmolVLA uses top and wrist cameras for its SO-100 tasks, and normalises community datasets onto a top, wrist, side ordering because inconsistent naming hurt pretraining.
- A wrist camera buys relative depth near the gripper, contact timing, and a strong signal for when to close the fingers.
- It does not buy metric distance outside its frame, the scale of a novel object, or usable geometry on glass and chrome.
- Moving it breaks things. Even DP3, with real 3D input, manually transforms its point clouds and adjusts the crop box when the view changes, and warns that significant changes might be hard to handle. A 2D policy has no such repair step.
That is the honest weakness of RGB geometry: a policy that works at your desk and dies when you nudge the tripod, which has its own page, policy only works in one setup. Camera placement is decided at recording time and expensive to change later, covered at length in collecting high-quality VLA training data.

Where depth genuinely pays off, according to published ablations
There is real evidence that 3D input helps, plus a pattern the headline numbers hide: raw depth is usually the worst 3D option, and gains concentrate in generalization rather than trained tasks. Four results, each read from the paper:
| Study | Depth source | Result |
|---|---|---|
| DP3, arXiv 2403.03954 | 84x84 depth from one simulated camera, converted to point clouds with intrinsics and extrinsics, cropped, downsampled to 512 or 1024 points | Six simulation ablation tasks (Adroit hammer, door, pen; MetaWorld assembly, disassemble, stick-push), 10 demos each: point cloud 78.3, oracle state 76.8, image 40.7, RGB-D 34.7, voxel 32.3, depth 32.0 |
| 3D-CAVLA, arXiv 2505.05800 | RGB-D back-projected to metric point clouds, encoded per view by a 1M-parameter PointNet-style encoder | LIBERO vs OpenVLA-OFT: 98.1 vs 97.1 on seen suites, 45.2 vs 36.4 on ten unseen tasks. Their w/o-Depth ablation drops to 97.0 and 41.0 |
| MolmoAct, arXiv 2508.07917 | No sensor. Depth tokens predicted from RGB by a VQVAE trained on 10 million Depth Anything V2 maps of RT-1, BridgeData V2 and BC-Z frames | 70.5 zero-shot on SimplerEnv visual matching, 86.6 average on LIBERO. 128-entry codebook, 100 tokens per 320x320 image |
| SpatialVLA, arXiv 2501.15830 | No sensor in pretraining. ZoeDepth monocular depth into an Ego3D position encoding | Four fine-grained tasks: no depth 45.4, sensor 70.5, predicted ZoeDepth 72.7. Needs no robot-camera extrinsic calibration |
Read the DP3 row again before you buy anything. Raw depth and RGB-D both scored below plain RGB; only as a cropped, downsampled point cloud did it reach 78.3. The authors are plain: RGB-D and depth images are "not comparable to point clouds, indicating that the proper usage of depth information is essential". Dropping only the crop step costs 33 points. Both sensorless entries are on the model arena: MolmoAct and SpatialVLA.
Depth helps most when the task needs geometry the camera cannot infer and when it is turned into a calibrated 3D representation first. It helps least stapled onto an RGB encoder as a raw channel. And it buys generalization more reliably than peak success: 3D-CAVLA's own depth ablation is worth 1.1 points seen against 4.2 unseen. If your policy already fails on its trained task, depth is not the bottleneck: loss falls but the policy does nothing.
The camera you would actually buy, and the number that decides it
If you do add depth, the spec that decides everything on a small arm is minimum depth distance, min-Z: the closest range at which the stereo pair can still triangulate. Below it you get holes, not numbers. On most models a wrist camera spends the interesting part of every episode inside that range, which is how people end up owning an expensive RGB camera. Hardware walkthrough in the SO-100 setup guide.
| Camera | Ideal range | Min-Z at max resolution | Depth accuracy | Depth FOV | Fit for an SO-100 wrist? |
|---|---|---|---|---|---|
| RealSense D405 | 7 cm to 50 cm | 7 cm at 480p | +/- 2% at 50 cm | 87 x 58 degrees | Yes, built for this distance |
| RealSense D435i | 0.3 m to 3 m | about 28 cm | under 2% at 2 m | 87 x 58 degrees | No. Blind at grasp distance, fine overhead |
| Plain USB webcam | n/a | n/a | none | varies | Yes, what every SO-100 example assumes |
The D435i is the part most people already own and most tutorials show, because it is the general-purpose one. Ideal range starts at 0.3 m, min-Z around 28 cm. Mount it on an SO-100 wrist and the depth frames go empty during approach and grasp, the only part of the episode where you wanted depth. The D405 is the short-range variant: 7 cm to 50 cm, min-Z 7 cm at 480p. Check the datasheet before ordering. Second time sink: datasheets moved to realsenseai.com after the spin-out from Intel, and old intelrealsense.com links did not resolve from our machine on 24 August 2026 (SERVFAIL, one network). If the device enumerates but never streams, start at camera not detected.
- Metric geometry that survives a lighting change, the one thing monocular cues cannot give you.
- A path to point-cloud policies such as DP3, where the published gap over image input is large, not marginal.
- Better behaviour on tall, thin or stacked objects where a third-person view has no useful parallax.
- Depth ignores texture. DP3 dropped colour deliberately and reported better appearance generalization.
- None of the five policies here reads it, so you fork a trainer before you see any benefit.
- Point clouds need intrinsics, extrinsics and a crop box you own forever. DP3 re-crops by hand when the view moves.
- Stereo fails on the objects people demo with: a glass jar returns the surface behind it, chrome its reflection.
- Extra USB bandwidth and frames per step, on a rig where the control loop is already the constraint.
- SpatialVLA scored lower with sensor depth than predicted depth, 70.5 against 72.7, calling the sensor noisy.
Recording depth with LeRobot today, and what still does not work
Depth recording landed through PR #3644, merged 27 June 2026 and shipped in LeRobot v0.6.0 on 6 July 2026. In 0.5.1 and earlier there is no depth module at all: src/lerobot/datasets/depth_utils.py returns 404 on the v0.5.1 tag and 200 on v0.6.0. That matters because the Pi0.5 trainer here runs lerobot 0.5.1, whose dataset path predates depth entirely.
In v0.6.x the SO follower checks each camera for a use_depth attribute. When set, the robot advertises an extra feature named after that camera, wrist_depth for a camera called wrist, of shape (height, width, 1), filled from read_latest_depth(). The writer quantizes those maps to 12-bit codes as HEVC Main 12 in gray12le, logarithmically by default (depth_min 0.01 m, depth_max 10.0 m, shift 3.5 m) so more quanta land near the camera. Lossless TIFF is available for raw maps. Format details in the dataset docs.
- 1Install LeRobot with the RealSense extra
pyrealsense2 sits behind one extra, the recording CLI behind another. You need both, and Python 3.12 or newer as of v0.6.1.
bashpip install 'lerobot[intelrealsense,core_scripts]' python -c "import lerobot; print(lerobot.__version__)" # expect 0.6.0 or newer; depth does not exist below that - 2Find the camera and note its serial number
RealSense devices also enumerate as generic OpenCV cameras, which is how people record only the colour stream and wonder where the depth went. The upstream example runs the recorder under sudo: access is a permissions problem first.
bashlerobot-find-cameras realsense - 3Record with use_depth enabled
use_depth defaults to False, and the config rejects a camera with neither use_rgb nor use_depth. Depth and RGB encoders are configured separately, which is why v0.6.0 renamed --dataset.vcodec to --dataset.rgb_encoder.vcodec.
bashlerobot-record \ --robot.type=so100_follower \ --robot.port=/dev/ttyACM0 \ --robot.id=my_so100 \ --robot.cameras='{ wrist: {type: intelrealsense, serial_number_or_name: "0123456789", width: 640, height: 480, fps: 30, use_depth: true}, front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30} }' \ --teleop.type=so100_leader \ --teleop.port=/dev/ttyACM1 \ --teleop.id=my_leader \ --dataset.repo_id=${HF_USER}/so100-depth-pick \ --dataset.num_episodes=50 \ --dataset.single_task="Pick up the cube and drop it in the bowl" \ --dataset.depth_encoder.vcodec=hevc \ --dataset.depth_encoder.depth_min=0.05 \ --dataset.depth_encoder.depth_max=1.0 \ --dataset.streaming_encoding=true - 4Verify the depth feature actually landed
Depth is flagged in the feature metadata. If the flag is missing, the stream went out as a plain single-channel video and the quantization never applied.
pythonimport json, os from pathlib import Path repo_id = "your-user/so100-depth-pick" root = Path(os.environ.get( "HF_LEROBOT_HOME", Path.home() / ".cache" / "huggingface" / "lerobot", )) / repo_id info = json.loads((root / "meta" / "info.json").read_text()) for key, ft in info["features"].items(): is_depth = ft.get("info", {}).get("is_depth_map", False) print(f"{key:44s} {str(ft.get('dtype')):8s} {ft.get('shape')} depth={is_depth}") - 5Decide what reads it
Here the manual path stops being a config change. No shipped policy reads that feature, so step five is writing code.
Three blockers as of LeRobot v0.6.1 (3 August 2026). One: a review item left open on the merged PR #3644, that prepare_observation_for_inference divides by 255 only when the dtype is uint8. Depth arrives as uint16 millimetres, skips that gate, and reaches the policy raw while RGB arrives scaled. The two agree only if the camera unit matches depth_output_unit, default mm. Get it wrong and nothing errors, the policy quietly learns nonsense. Two: LeRobot's ACT builds its backbone as resnet18 with ResNet18_Weights.IMAGENET1K_V1, a three-channel stem, and refuses non-resnet backbones. A one-channel feature is not a drop-in; read from the source, not a tested failure. Three: depth is inferred from channel count alone, is_depth_map = shape[2] == 1, with an upstream TODO against it, so a grayscale or IR camera is filed as depth. Capture also skips the LeKiwi and Unitree ZMQ cameras.
Do it yourself, or settle it empirically
Buy a D405, record on LeRobot 0.6.x with use_depth, then fork a trainer so something reads the channel. No published checkpoint has a depth input, so the fork is unavoidable. Three routes.
- Extend an existing policy: widen the first convolution of the ACT backbone to four channels, or add a second encoder branch. Cheap, but the new channel loses the ImageNet initialisation the other three keep.
- Move to a policy built for 3D. DP3 takes point clouds and has the strongest ablation here, at the cost of intrinsics, extrinsics, a crop box and 512 or 1024 points per frame.
- Colourise depth into a three-channel image and feed it as an extra camera. No code change, no guarantee the encoder does anything sensible with it. The null hypothesis to beat.
# start from the repo that has a depth ablation to reproduce
git clone https://github.com/YanjieZe/3D-Diffusion-Policy
# or extend the LeRobot trainer you already run
git clone https://github.com/huggingface/lerobot
cd lerobot && pip install -e '.[intelrealsense,core_scripts,training]'This is the real cost, not the camera. Every upstream release you want, you rebase. LeRobot shipped 0.5.0, 0.5.1, 0.6.0 and 0.6.1 between 9 March and 3 August 2026, including a breaking rename of the codec flags and a PyTorch bump to 2.7. Budget for that before hardware.
Straight about it: this platform does not help you record or train on depth. The desktop client records LeRobot-format datasets with episodes, camera streams and joint states from a teleoperation session, and the five trainers run the published policies as published, which means RGB.
It is good for settling the question that actually predicts your result, which is rarely depth versus RGB but one camera versus two, wrist versus overhead. Record both, fine-tune the same policy on each, compare. On the 24 GB tier a SmolVLA or ACT run is 2 to 5 hours at 0.30 to 0.60 USD per hour.
- 1Record both camera layouts
Same task, same operator, same episode count. Record your first dataset covers the mechanics, the desktop client captures from a teleop session.
- 2Train the same policy twice
Pick SmolVLA or ACT so both runs sit on the cheap tier and can also run locally. SmolVLA needs 30 episodes minimum, ACT 50. The training matrix has the guide for your arm.
- 3Compare on identical conditions
Same frame rate, same lighting. Runs at different frame rates differ for reasons unrelated to cameras.
- 4
If you do not own a depth camera, predict one
The notable development of the last two years is that the strongest 3D-aware vision-language-action models mostly do not use depth sensors. They estimate depth from RGB and use the estimate as structure. MolmoAct predicts 100 depth tokens per frame from RGB alone before any trajectory or action. SpatialVLA runs ZoeDepth over the frame and back-projects into an egocentric 3D position encoding, keeping that model frozen at 8.6% of parameters and 0.06 s per action. The broader arc is in how VLA models are developing.
- Neither needs a depth camera at inference, only a depth model at training time, on the GPU you already rented.
- Predicted depth is metrically wrong and geometrically plausible. SpatialVLA argue that captures relative layout and makes precise scale unnecessary: 72.7 with ZoeDepth against 70.5 with the sensor.
- It sidesteps the failure that kills stereo: a monocular model produces a confident surface for a glass jar, where a stereo pair produces a hole.
- It costs tokens. In MolmoAct the depth tokens sit ahead of everything else in the autoregressive path, so every action waits on them.
The weights are not uniformly permissive. Depth-Anything-V2-Small (24.8M parameters) is Apache-2.0. Base (97.5M), Large (335.3M) and Giant (1.3B, listed as coming soon) are CC-BY-NC-4.0, non-commercial. If you generate depth labels with the Large checkpoint and later ship the trained policy commercially, find that out now rather than in a due-diligence review.
# Depth Anything V2, README as of 24 Aug 2026
git clone https://github.com/DepthAnything/Depth-Anything-V2
cd Depth-Anything-V2
pip install -r requirements.txt
# then place depth_anything_v2_vits.pth (Apache-2.0) under checkpoints/
A decision rule that survives contact with a real task
| Your task | Is RGB enough? | Why |
|---|---|---|
| Pick and place of opaque objects, fixed camera | Yes | The setting every published SO-100 result uses |
| Insertion, plugs, sub-centimetre alignment | Usually, with a wrist camera | ACT's results are fine-grained bimanual tasks on plain webcams |
| Untextured objects on an untextured surface | Marginal | Monocular cues need gradients, stereo struggles without IR |
| Transparent or mirror-finish objects | RGB, not depth | Stereo reports the surface behind glass, worse than none |
| Stacking, bin picking, cluttered piles | No, geometry is the task | Where point-cloud policies earn their calibration overhead |
| Objects and layouts you never recorded | Depth helps most | 3D-CAVLA's 8.8 point gain was on unseen tasks, not seen |
| Wide scenes, moving base | Different article | openpi exposes three RGB keys, base_0_rgb and both wrists |
People underrate this: every extra stream is extra tokens and milliseconds per action step, and depth is not free. Here, inference latency runs from 20 ms per action step for ACT to 485 ms for Pi0.5, the difference between a smooth policy and a hesitant one; GR00T N1.7 against Pi0.5 lays those out. Experiments are cheap next to hardware, and the pricing page carries the full figures behind this table.
| Tier | Policies | Run time | Rate | Cost per run | Min episodes |
|---|---|---|---|---|---|
| A100 80 GB or H100 80 GB | GR00T N1.7, GR00T N1.5, Pi0.5 | 3 to 6 hours | 1.20 to 2.00 USD/h | 4 to 12 USD | 50 |
| RTX 4090 or any 24 GB card | SmolVLA, ACT | 2 to 5 hours | 0.30 to 0.60 USD/h | 1 to 3 USD | 30 SmolVLA, 50 ACT |
Two limits, plainly. First, depth: the recording client and all five trainers here are RGB, so a depth-conditioned policy is not something you can produce on this platform today, and nothing above implies otherwise. Second, latency: inference pods are auto-provisioned and carry an idle watchdog so they destroy themselves rather than billing silently, but the control loop still crosses the public internet. Viable for slow pick-and-place, not fast reactive motion, and more cameras only tighten the budget. See the training docs.
Record the dataset before you buy the camera
The desktop client records LeRobot-format datasets, episodes, camera streams and joint states, straight from a teleop session. Two RGB views recorded well beat one depth camera recorded badly, and you can settle that on your own objects for the price of two runs.
Get the desktop clientCan GR00T N1.7 or Pi0.5 take a depth image as input?▾
No. GR00T's modality.json has four keys, state, action, video and annotation, and the data preparation guide never mentions depth. Isaac-GR00T issue #652, opened 23 April 2026 asking exactly this, was still open with no maintainer reply on 24 August 2026. Pi0.5's openpi Observation carries images, image_masks and state, keys named base_0_rgb, left_wrist_0_rgb, right_wrist_0_rgb. Either needs a modified encoder, so a fork.
Does LeRobot support recording depth?▾
Yes, since v0.6.0 on 6 July 2026, through PR #3644. Set use_depth on a RealSense config and the robot advertises a second feature, wrist_depth for a camera called wrist, of shape (H, W, 1), quantized to 12-bit codes as HEVC Main 12 in gray12le, with lossless TIFF as an alternative. Recording is not the hard part: an open item on the merged PR notes policies get raw depth at inference without the scaling RGB gets, and no shipped policy reads it.
Is a second RGB camera better than one depth camera?▾
For most tabletop manipulation, yes, and it is the experiment to run first. Every published SO-100 result and the five-policy set here assume plain RGB; SmolVLA's SO-100 tasks use a top and a wrist camera. Depth's clearest win is generalization, not peak success.
Why did raw depth score worse than RGB in the DP3 ablation?▾
Because a depth image is a bad input format even when depth is the right information. Over six simulation tasks DP3 measured point clouds at 78.3, images at 40.7, RGB-D at 34.7 and raw depth at 32.0. The gain came from the cropped, downsampled point cloud, not from the channel. Removing only the crop step cost 33 points.
Can I get 3D awareness without any depth hardware?▾
Yes, and this is where the field is heading. MolmoAct predicts depth tokens from RGB, trained on 10 million maps labelled with Depth Anything V2. SpatialVLA runs ZoeDepth and back-projects into an egocentric position encoding, reporting 72.7 with predicted depth against 70.5 with a sensor. Both need a depth model at training time and a plain camera at run time. Licence check: Small is Apache-2.0, Base and Large are CC-BY-NC-4.0.
Sources
- NVIDIA GR00T N1.7 3B model card: input types (vision, state, language, embodiment ID), RGB-only vision parameters, Cosmos-Reason2-2B backbone and SigLip2 encoder
- NVIDIA GR00T N1.5 3B model card: 224x224 uint8 RGB frames, vision plus state plus language input list
- Isaac-GR00T data preparation: the meta/modality.json schema (state, action, video, annotation) and the LeRobot v2 requirement with a v3-to-v2 conversion script
- openpi models/model.py: the Observation dataclass (images, image_masks, state, tokenized prompt), IMAGE_KEYS base_0_rgb / left_wrist_0_rgb / right_wrist_0_rgb and IMAGE_RESOLUTION 224x224
- SmolVLA: A Vision-Language-Action Model for Affordable and Efficient Robotics (64 visual tokens per frame, top and wrist cameras on SO-100)
- Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware (ACT / ALOHA): four Logitech C922x webcams at 480x640, 14 DoF joint positions
- 3D Diffusion Policy: Generalizable Visuomotor Policy Learning via Simple 3D Representations (Table IV representation ablation, cropping ablation, 84x84 depth to point clouds)
- 3D CAVLA: Leveraging Depth and 3D Context to Generalize Vision Language Action Models for Unseen Tasks (LIBERO seen and unseen results, w/o-Depth ablation)
- MolmoAct: Action Reasoning Models that can Reason in Space (depth perception tokens, VQVAE on 10 million Depth Anything V2 maps, SimplerEnv and LIBERO scores)
- SpatialVLA: Exploring Spatial Representations for Visual-Language-Action Model (Ego3D position encoding with ZoeDepth, sensor-depth versus predicted-depth ablation)
- LeRobot PR #3644, feat(depth maps): adding support for depth in LeRobot, merged 27 June 2026: gray12le quantization, is_depth_map metadata, the inference normalization gap and the LeKiwi / Unitree ZMQ exclusion
- LeRobot v0.6.0 release notes, 6 July 2026: depth support shipped and --dataset.vcodec renamed to --dataset.rgb_encoder.vcodec
- RealSense D405 tech specs: ideal range 7 cm to 50 cm, min-Z 7 cm at 480p, +/- 2% accuracy at 50 cm, 87 x 58 degree depth FOV
- RealSense D435i tech specs: ideal range 0.3 m to 3 m, min-Z about 28 cm, under 2% accuracy at 2 m
- Depth Anything V2 repository: checkpoint sizes (24.8M / 97.5M / 335.3M / 1.3B) and the Apache-2.0 versus CC-BY-NC-4.0 licence split
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started