Paper · 2024-05-20 · cs.RO, cs.LG
Octo: An Open-Source Generalist Robot Policy
Octo is a transformer robot policy pretrained on about 800k Open X-Embodiment trajectories. It takes camera views and a language or goal-image task, and a diffusion head decodes action chunks. New sensors or action spaces are added at finetuning time. Author-reported: 72% average success over six finetuning setups versus 20% for training from scratch, the best baseline.
Vision-language-action modelImitation learningDiffusion modelsAction chunkingrobot-policygeneralist-policyfinetuningdiffusion-action-headopen-x-embodiment
arXivPDFCodeMarkdown15 min read
AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Teaching a robot a new skill usually means collecting data for that exact robot and task, and the result rarely works anywhere else. Octo is one network that is first trained on a large pile of robot demonstrations gathered from many robots. You give it camera views and a goal, either a written instruction or a picture of the finished scene. It answers with the motion the robot should make next.
The network is built from pieces that can be swapped. Each camera image, the instruction and each readout slot become tokens, and the same transformer reads all of them. A new camera or a different arm means adding a small new piece and keeping the rest. Then about 100 demonstrations are enough to finetune it. Its motion is produced the way image diffusion models make pictures: it starts from random noise and cleans it up over a short series of steps. The code and the released model weights are open, so the whole setup can be reproduced and reused.
TL;DR
Octo is an open transformer policy for robot manipulation, pretrained on about 800k trajectories from the Open X-Embodiment collection. Its inputs and outputs are tokens, so sensors and action spaces can change at finetuning time while the pretrained transformer is kept. Actions come from a small diffusion head over action chunks. The public code is MIT-licensed and matches most of the appendix hyperparameters, but its released batch size (512) differs from the paper (2048).
Problem
Robot policies are usually trained for one robot and one task, and they generalize narrowly §1. Earlier generalist robot policies fix the sensor set and the action format, do not finetune well to new domains, and the largest ones are not public §1. The paper aims at an open, pretrained policy that accepts different camera setups, different robots and both language and goal-image task specifications, and that can be adapted to new observations and action spaces with a small target dataset and a modest compute budget §1.
Method
Overview. Octo has three parts: input tokenizers, a transformer backbone and readout heads. The tokenizers turn the task (language or goal image) and the observation history into tokens. The backbone processes them as one sequence. Readout heads decode actions §3.1.
Tokenizers §3.1 app. D. Language is passed through a frozen T5-base encoder (111M parameters) that gives 16 language tokens. Images go through a shallow convolution stack and are cut into 16×16 patches, which gives 256 tokens for the third-person camera and 64 tokens for the wrist camera. Learned position embeddings are added before the tokens are arranged in sequence §3.1.
Backbone and attention §3.1. Tokens are arranged by time step. Observation tokens attend causally to the same and earlier time steps and to the task tokens. Missing inputs are masked out entirely, so a dataset without language still trains §3.1. Readout tokens are added per time step. A readout token attends to the task and earlier observations, and no observation or task token attends to it, so readouts only read the state §3.1 octo_module.py.
Action head §3.3 app. D. The action readout embedding is averaged and given to a diffusion head. The head is a 3-block residual MLP with hidden size 256, layer normalization and 20 diffusion steps on a cosine noise schedule. The transformer runs once per prediction. The denoising loop runs only inside the head. The paper reports that this beats MSE heads and discretized action heads in zero-shot and finetuning tests §3.3 tab. 2.
Training objective §3.3. The training objective is the standard DDPM objective. Noise is added to dataset actions and the denoiser learns to undo it. The sampler is the update below eq. 1. The paper says the coefficients come from the cosine schedule but gives no numeric values for them.
x^{k-1} = \alpha \big( x^k - \gamma\, \epsilon_\theta(x^k, e, k) + \mathcal{N}(0, \sigma^2 I) \big) eq. 1
# Training loss for the diffusion action head [§3.3]
# Input: embeddings (B, W, d) from one transformer pass per time step
# Input: actions (B, W, H, A), H = chunk length, A = action dim
# Output: scalar loss
def diffusion_loss(embeddings, actions, pad_mask):
# Flatten chunk and action dims into one vector per time step [app. D]
targets = flatten(actions) # (B, W, H * A)
# Clip the targets to [-5, 5] (code only, see Paper vs code)
targets = clip(targets, -5.0, 5.0)
# Draw one random diffusion step per sample [app. D]
steps = uniform_int(0, num_steps, shape=(B, W)) # num_steps = 20
# Draw Gaussian noise shaped like the targets [§3.3]
noise = normal(shape=targets.shape)
# Mix targets and noise according to the cosine schedule [§3.3]
noisy = sqrt(alpha_hat[steps]) * targets
noisy = noisy + sqrt(1 - alpha_hat[steps]) * noise
# Predict the noise from the embedding, noisy input and step [§3.3]
predicted = denoiser(embeddings, noisy, steps)
# Mean squared error on unpadded entries [§3.3]
loss = masked_mse(predicted, noise, pad_mask)
# Scale by the action dimension (code only)
return loss * action_dim# Sample one action chunk at inference [§3.3] [eq. 1] [app. D]
# Input: embeddings from one transformer pass
# Output: action chunk in normalized units, shape (H, A)
def sample_actions(embeddings):
# Start from pure Gaussian noise [§3.3]
noisy = normal(shape=(H * A,))
# Denoise from the last step to the first [app. D]
for step in reversed(range(num_steps)):
# Predict the noise for the current iterate [§3.3]
eps = denoiser(embeddings, noisy, step)
# Coefficients of eq. 1; values are in the code, not the paper
alpha = 1 / sqrt(alphas[step])
gamma = (1 - alphas[step]) / sqrt(1 - alpha_hats[step])
sigma = sqrt(betas[step])
noisy = alpha * (noisy - gamma * eps)
# Add fresh noise, but not at the last step (differs from eq. 1)
if step > 0:
noisy = noisy + sigma * normal(shape=noisy.shape)
# Clip the iterate to [-5, 5] (code only)
noisy = clip(noisy, -5.0, 5.0)
# Return normalized actions; un-normalize with dataset statistics
return noisyCode locations for the two blocks above: the loss is in action_heads.py, the sampler in action_heads.py, the denoiser MLP in diffusion.py and diffusion.py, and the cosine schedule in diffusion.py.
Finetuning §3.3 §4.2. The paper updates all parameters and reports that this beats freezing parts of the pretrained model. The recipe is the same for every setup: about 100 target trajectories, 50k steps, cosine decay with linear warmup. New observation inputs get a new lightweight encoder, new action spaces get a new head, and the transformer weights are kept §3.1 §3.3.
Training data §3.2 app. C tab. 3. The mixture is 25 datasets from Open X-Embodiment §3.2. The curation removes datasets without image streams and datasets that do not use delta end-effector control. It also removes datasets that are too repetitive, too low-resolution or too niche. Remaining datasets are split into "more diverse" and "less diverse", and the first group is weighted double. Missing camera channels are zero-padded. The gripper is aligned so that +1 means open and 0 means closed §3.2. The sampling fractions are listed in tab. 3. The pretraining data totals about 800k trajectories §1.
| Dataset | Share of training batches |
|---|---|
| Fractal | 17.0% |
| Kuka | 17.0% |
| Bridge | 17.0% |
| BC-Z | 9.1% |
| Stanford Hydra Dataset | 6.0% |
| Language Table | 5.9% |
| Taco Play | 3.6% |
| Furniture Bench Dataset | 3.3% |
| UTAustin Mutex | 3.0% |
| Austin Sailor Dataset | 2.9% |
| Roboturk | 2.8% |
| Toto | 2.4% |
| Austin Sirius Dataset | 2.3% |
| Berkeley Autolab UR5 | 1.5% |
| IAMLab CMU Pickup Insert | 1.2% |
| Viola | 1.2% |
| Berkeley Fanuc Manipulation | 1.0% |
| NYU Franka Play Dataset | 0.9% |
| UCSD Kitchen Dataset | <0.1% |
| Jaco Play | 0.6% |
| Berkeley Cable Routing | 0.3% |
| Austin Buds Dataset | 0.3% |
| CMU Stretch | 0.2% |
| NYU Door Opening | 0.1% |
| DLR EDAN Shared Control | 0.1% |
Source: tab. 3, which the paper introduces in app. C.
Training details §3.4 app. D. Two sizes are trained: Octo-Small, with a ViT-S sized backbone, and Octo-Base, with a ViT-B sized backbone. The optimizer is AdamW with weight decay 0.1 and the inverse square root learning rate schedule with linear warmup. Gradients are clipped at 1.0. Two frames of observation history are used, and goals are relabelled with a uniformly sampled future state from the same trajectory. Augmentation is applied, and per training example either the language instruction or the goal image is zeroed out. Datasets without language always use goal images §3.4. The ViT-B model trains for 300k steps on TPU v4-128 in about 14 hours §3.4.
Model sizes tab. 5.
| Model | Layers | Hidden size | MLP size | Heads | Parameters |
|---|---|---|---|---|---|
| Octo-Small | 12 | 384 | 1536 | 6 | 27M |
| Octo-Base | 12 | 768 | 3072 | 12 | 93M |
The paper also names Octo-Tiny at 10M parameters §4.3, but its architecture is not in the appendix table.
Code and checkpoints §3.5. The paper releases pretrained checkpoints, JAX finetuning scripts, the JAX pretraining pipeline and data loaders for Open X-Embodiment that work with JAX and PyTorch §3.5.
Hyperparameters
Values from the paper unless marked code. Where the paper is silent, the column says so.
| Name | Value | Where | Notes |
|---|---|---|---|
| Pretraining steps (ViT-B) | 300k | §3.4 octo_pretrain_config.py | |
| Batch size | 2048 | §3.4 | Released config says 512, see Paper vs code |
| Optimizer | AdamW | §3.4 app. D | |
| Peak learning rate | 3e-4 | tab. 4 config.py | |
| Warmup | 2000 steps | tab. 4 config.py | linear warmup |
| LR schedule | inverse square root | tab. 4 | timescale 10000 is code only config.py |
| Weight decay | 0.1 | §3.4 tab. 4 | applied to weight matrices only (code), see pitfalls |
| Gradient clip | 1.0 (global norm) | §3.4 tab. 4 | global-norm clip (code) train_utils.py |
| Observation history | 2 frames | §3.4 app. E octo_pretrain_config.py | |
| Goal relabelling | uniform future state | §3.4 config.py | |
| Steps sampled per trajectory | at most 100, random | app. E config.py | |
| Shuffle buffer | 500k | app. E octo_pretrain_config.py | shuffle before image decoding app. E |
| Primary camera | resize 256×256, 16×16 patches, 256 tokens | app. D octo_pretrain_config.py | |
| Wrist camera | resize 128×128, 64 tokens | app. D octo_pretrain_config.py | no crop augmentation (code) |
| Language encoder | T5-base, frozen, 111M | §3.1 app. E octo_pretrain_config.py | max 16 tokens app. D |
| Diffusion steps | 20 | app. D action_heads.py | cosine schedule §3.3 |
| Diffusion head | 3 blocks, hidden 256, layer norm | app. D action_heads.py | blocks expand 4× internally (code) diffusion.py |
| Action dimension | 7 | octo_pretrain_config.py | not stated in paper |
| Action chunk, pretraining | 4 steps | octo_pretrain_config.py | not stated in paper |
| Primary augmentation | crop scale 0.8–1.0, ratio 0.9–1.1; brightness ±0.1; contrast 0.9–1.1; saturation 0.9–1.1; hue ±0.05 | octo_pretrain_config.py | paper gives no numbers app. D |
| Language paraphrase | 50% of instructions rephrased | octo_pretrain_config.py | not in paper, see pitfalls |
| Finetuning steps | 50k | §3.3 finetune_config.py | |
| Finetuning LR | cosine, peak 3e-4, warmup 2000, decay to 0 | §3.3 finetune_config.py | warmup length not stated in paper |
| Finetuning weight decay | 0.01 | finetune_config.py | not stated in paper |
| Finetuning batch size | 256 | finetune_config.py | not stated in paper |
| Finetuning window | 1 by default | finetune_config.py | pretraining uses 2 |
| Finetuning chunk | 4 by default | finetune_config.py | Bimanual setup used 64 app. F |
| Finetuning demos | about 100 per setup | §3.3 app. F | per setup counts under Results |
| Finetuning hardware | one A5000 (24GB), about 5 h | §3.4 | |
| Pretraining hardware | TPU v4-128, about 14 h for ViT-B | §3.4 |
Results
Author-reported. Setup: six finetuning setups on real robots, each with about 100 target demonstrations, same finetuning recipe in every setup §4.2 app. F.
Finetuning tab. 1. Success rate averaged over 20 trials per setup tab. 1 (but see Pitfalls for Berkeley Bimanual). * means new observation input (force-torque). † means new action space (joint position control).
| Method | Berkeley Insertion* | Stanford Coffee | CMU Baking | Berkeley Pick-Up† | Berkeley Coke | Berkeley Bimanual† | Average |
|---|---|---|---|---|---|---|---|
| ResNet+Transformer, scratch | 10% | 45% | 25% | 0% | 20% | 20% | 20% |
| VC-1 (MLP head, MSE) | 5% | 0% | 30% | 0% | 10% | 50% | 15% |
| Octo (this paper) | 70% | 75% | 50% | 60% | 100% | 80% | 72% |
Source: tab. 1 §4.2.
Zero-shot control §4.1 fig. zero_shot. On several in-distribution tasks from the pretraining data, Octo averages 29% higher success than RT-1-X (35M parameters) with language instructions. Its results on the WidowX and RT-1 Robot tasks are similar to RT-2-X (55B parameters). With goal images on WidowX, success is 25% higher than with language. Ten trials per task app. F. Bar values are only in the figure, not in the text.
Design ablations tab. 2 tab. 6. All on Octo-Small, WidowX setup, averaged over two language-conditioned and two goal-conditioned tasks, 40 trials tab. 2.
| Variant | Aggregate success |
|---|---|
| Octo-Small (this paper) | 83% |
| Data: RT-X dataset mix (11 datasets) | 60% |
| Data: single robot dataset (Bridge) | 43% |
| Policy: discretized action prediction | 18% |
| Policy: continuous MSE action head | 35% |
| Architecture: ResNet-50 + Transformer | 70% |
Per-task breakdown tab. 6:
| Variant | Carrot on plate | Eggplant in pot | Bread on plate | Spoon on glove | Average |
|---|---|---|---|---|---|
| Octo-Small (this paper) | 80% | 90% | 70% | 90% | 83% |
| RT-X dataset mix | 80% | 80% | 40% | 40% | 60% |
| Single robot dataset (Bridge) | 20% | 70% | 60% | 20% | 43% |
| Discretized action prediction | 0% | 20% | 10% | 40% | 18% |
| Continuous MSE action head | 70% | 30% | 0% | 40% | 35% |
| ResNet-50 + Transformer | 80% | 60% | 100% | 40% | 70% |
Zero-shot generalization tab. 7. Octo-Small, WidowX. Each row averages two tasks, with 20 trials across them:
| Generalization type | Tasks | Average |
|---|---|---|
| In-distribution | carrot on plate 80%, eggplant in pot 90% | 85% |
| Novel objects | bread on plate 70%, spoon on glove 90% | 80% |
| Novel environment | mushroom in pot 20%, spoon on cloth 60% | 40% |
| Novel skill | flip cup on its side 10%, block in slot 0% | 5% |
Model scale fig. scaling. Zero-shot success rises with model size on UR5 and WidowX tasks. Base is more robust to initial scene changes than Small and is less prone to early grasp attempts §4.3. No numbers in the text.
Paper vs code
Code: github.com/octo-models/octo at
241fb3514b7c40957a86d869fecb7c7fc353f540, MIT. The link comes from the paper's
project page [octo-models.github.io]; no further search was needed. Files read:
scripts/configs/config.py, octo_pretrain_config.py, finetune_config.py,
octo/model/components/action_heads.py, diffusion.py, octo/model/octo_module.py,
octo/model/octo_model.py, octo/utils/train_utils.py, octo/data/utils/task_augmentation.py,
octo/data/oxe/oxe_dataset_mixes.py, octo/data/dataset.py, scripts/train.py,
and examples/.
- Batch size. Paper: 2048 §3.4. Released pretraining config: 512 octo_pretrain_config.py. The train script asserts divisibility by device count and divides by process count, so 512 is a global batch train.py train.py. The released config does not reproduce the paper's batch.
- Training target. Paper text: the denoiser learns to "reconstruct the original action" §3.3. Code predicts the added noise and takes MSE on it, the standard DDPM form action_heads.py. Read the text as loose wording for the same objective.
- Sampler noise. Eq. 1 adds noise at every step eq. 1. Code adds it only for steps above zero action_heads.py. The final step is noise-free.
- Clipping. Code clips training targets to [-5, 5] action_heads.py and clips every sampler iterate to the same range action_heads.py. The paper says nothing about clipping.
- Loss scale. Code multiplies the loss by the action dimension, which is 7 in pretraining action_heads.py. The paper says nothing. This matters for the global-norm gradient clip at 1.0, which fires at a different point.
- Learning-rate schedule. Paper: inverse square root with warmup §3.4 app. D. Code: peak divided by sqrt((step + 10000) / 10000) after warmup train_utils.py. The timescale 10000 is code only.
- Finetuning hyperparameters. The paper gives only the pretraining weight decay of 0.1. Finetuning code uses 0.01 finetune_config.py, batch size 256 finetune_config.py, window 1 finetune_config.py and chunk 4 finetune_config.py. The paper states two frames of history for training §3.4 and a chunk of 64 with 12 executed actions for the Bimanual setup app. F. That Bimanual config is not in the repository.
- Language and goal dropout. Paper: per example, the language or the goal image is zeroed out §3.4. Code: the mask is drawn per time step task_augmentation.py. The pretraining config switches to delete-and-rephrase with a 50% rephrase rate octo_pretrain_config.py. It does not set the image-keep probability, which the base config sets to 0.5 config.py. Whether the base value carries over depends on how ConfigDict merges nested dicts. I did not verify this.
- Paraphrases. Code paraphrases instructions with a dataset from an external repository (
rail-berkeley/OXE_paraphrases) octo_pretrain_config.py. The paper does not mention it. - Augmentation values. Paper: crop and colour jitter, no values app. D. Code values are listed in the Hyperparameters table octo_pretrain_config.py.
- Readout pooling. Paper: the readout token is the action embedding. Code averages the readout tokens octo_pretrain_config.py action_heads.py. With one readout token per step this is the same.
- Data mixture. Paper: 25 datasets with the fractions in tab. 3. Code: 25 datasets with per-dataset multipliers oxe_dataset_mixes.py, then scaled by dataset size when
balance_weightsis set and normalized dataset.py. Fractions depend on the dataset sizes, which the repo does not contain. - Model sizes. Paper: Small and Base match the code's ViT-S and ViT-B settings tab. 5 transformer.py. The code also defines a
vit_tsize (hidden 192, 12 layers, MLP 768, 3 heads) transformer.py. I did not confirm it is the paper's 10M Tiny model. - Inference example. The paper's appendix example calls
sample_actionswith no unnormalization statistics app. B. Then the output stays in normalized units. The repository's examples passmodel.dataset_statistics03_eval_finetuned.py.
Pitfalls
- "52%" is percentage points, not a ratio. The paper says Octo beats the next best baseline "by 52%" §4.2. Table 1 gives 72% against 20% for scratch, so that is +52 points. The relative gain is about 260%.
- VC-1 average. The six VC-1 values in tab. 1 average 15.8%, but the table reports 15%.
- Bimanual trial count. Table 1's caption says every setup uses 20 trials tab. 1. The appendix says Berkeley Bimanual used 10 trials app. F. The Berkeley Coke trial count is not stated.
- "29% higher" is not defined. The text does not say whether the gain is absolute or relative §4.1.
- Zero-shot is not generalization to new objects or skills. Zero-shot tasks come from the pretraining data, and the evaluation still changes object positions, lighting and backgrounds §4.1. Novel skills score 5% on average tab. 7, so do not expect zero-shot control of skills missing from the pretraining data.
- Data imbalance. Only 27% of pretraining data has wrist camera input, and 56% has language annotations. The paper blames these for the weak wrist-camera and goal-versus-language gaps §5.
- Wrist cameras hurt finetuning in the paper's tests. Third-person-only finetuning often did better §5.
- Finetune time. Section 3.4 gives about 5 hours on one A5000, while section 4 says under 5 hours §3.4 §4.2.
- Un-normalize actions. The action head outputs normalized values. Pass dataset statistics to
sample_actions(as in 03_eval_finetuned.py), or the output is in normalized units octo_model.py. - Clip bounds vs. normalization. The ±5 clip action_heads.py is applied in the space the head works in, so it interacts with your normalization. The paper does not discuss it.
- Preprocessing is not in the paper. The pretraining config reads a pre-resized copy from a GCS bucket (
gs://rail-orca-central2/resize_256_256) octo_pretrain_config.py. Only the 256×256 resize is in the paper. - Data loading. A small shuffle buffer (20k) hurt zero-shot results. The paper shuffles frames across trajectories before decoding and uses a 500k buffer app. E. Subsampling is capped at 100 random steps per trajectory app. E. Both are easy to skip and both matter.
- Gripper convention. The paper chose absolute gripper actions (+1 open, 0 closed) over a relative variant, because the relative one reduced retries app. E.
- Proprioception. The paper reports that adding proprioceptive inputs made pretrained policies worse app. E. The finetuning config still defines a proprioception key finetune_config.py.
- Ablations use Octo-Small. Compute limits kept the ablations on Small §4.3, so the Base results are not ablated.
- Figure and table numbers. Table numbers follow the source order. Figure numbers are not stated because the source adjusts the figure counter around the teaser [main.tex:174]. Figures are cited by their LaTeX labels. Appendix letters follow the order of the appendices environment. None of these were checked against the compiled PDF.
- Equation numbering. eq. 1 is the only numbered equation in the main text §3.3.
Lineage
- Builds on: Open X-Embodiment, the data source §1 and the RT-1-X and RT-2-X baselines §4.1.
- Compared against: RT-1-X (35M) and RT-2-X (55B) for zero-shot control §4.1, and training from scratch with a ResNet and FiLM architecture plus VC-1 for finetuning §4.
- Uses ideas from diffusion policies, action chunking and ViT scaling §2; those papers have no card here.
- Superseded by: not stated in this source.
Source and license
Adapted from “Octo: An Open-Source Generalist Robot Policy” by Octo Model Team, Dibya Ghosh, Homer Walke, Karl Pertsch, Kevin Black, Oier Mees, Sudeep Dasari, Joey Hejna, Tobias Kreiman, Ria Doshi, Charles Xu, Jianlan Luo, You Liang Tan, Lawrence Yunliang Chen, Pannag Sanketi, Quan Vuong, Ted Xiao, Dorsa Sadigh, Chelsea Finn, Sergey Levine (arXiv:2405.12213), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/octo-models/octo at commit 241fb3514b7c (MIT). Any quoted code is shown under that license.