The AY-Robots policies comparison table showing GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT with parameters, GPU tier, inference latency per action step and minimum episodes
Flow MatchingDiffusion PolicyPi0.5Action ChunkingInference LatencyVLA

Flow Matching vs Diffusion for Robot Policies

AY-Robots ResearchAugust 23, 202627 min read

Flow matching and diffusion train the same network with different targets. The objective, the samplers as lerobot ships them, the real step counts, and why fewer steps does not mean lower latency.

The short version

  • Diffusion and flow matching train the same shaped network on the same data. The difference is the regression target: diffusion predicts the noise that was added, flow matching predicts a velocity along a straight line between noise and action.
  • Step counts differ by an order of magnitude. lerobot's Diffusion Policy runs 100 reverse steps by default. Pi0.5 and SmolVLA run 10 forward-Euler steps. NVIDIA's published GR00T N1.7 timings use 4.
  • Fewer sampler steps does not mean lower latency. Pi0.5 is the slowest policy on AY-Robots at 485 ms per action step. ACT, which does no iterative sampling at all, is 20 ms.
  • Where the time sits depends on the model, and the two published decompositions disagree. On an RTX 4090 Pi0 spends 46 ms on the prefix and 27 ms on 10 flow steps. On an H100 in PyTorch eager, GR00T N1.7 spends 31.3 ms in the backbone and 48.2 ms in the action head at 4 steps.
  • What the latency buys is a chunk of 50 continuous actions, one second of motion at 50 Hz, produced in a single generation pass instead of token by token.
  • If you have a 24 GB card, your flow matching option is SmolVLA at 245 ms, not Pi0.5. Pi0.5 needs an 80 GB card and is cloud-only on this platform.

The difference is one line in the loss

Diffusion and flow matching get described as rival paradigms. In an action head they are close to the same code. Both take an observation, a noisy action chunk and a scalar that says how noisy that chunk is, and both regress one vector per action dimension. The architecture is interchangeable: a U-Net, a diffusion transformer or a 300 M transformer expert all work under either objective. What actually differs is what that regressed vector is supposed to be, and how many forward passes you need before you have an action you can send to a servo.

Diffusion asks the network for the noise that was added. Flow matching asks it for a velocity: given a point on a straight line between noise and data, which way is data. Everything downstream follows from that choice, including the time variable, the sampler, the step count, and how hard the whole thing is on your control loop. This article walks the objective, the sampler and the numbers that ship in lerobot today, then looks at what it costs when you actually run a policy on an arm. If you want the wider family tree first, the VLA overview covers it.

Diffusion (DDPM style)Flow matching (straight path)
Corruptionx_t = sqrt(alpha_bar_t) * x_0 + sqrt(1 - alpha_bar_t) * eps, shaped by a beta schedulea straight interpolation between noise and action, no schedule
What the network outputsthe noise eps (prediction_type = "epsilon" in lerobot)a velocity, whose sign depends on which end of the path is time zero
Time variablean integer timestep, 0 to num_train_timesteps - 1a continuous scalar in [0, 1]
Samplerthe scheduler's reverse step, scheduler.step(...).prev_sampleforward Euler, x <- x + dt * v
Steps at inference, lerobot default100 (num_inference_steps falls back to num_train_timesteps)10 (num_inference_steps for Pi0.5, num_steps for SmolVLA)
Tunables that can wreck itbeta_schedule, beta_start, beta_end, clip_sample, prediction_typethe timestep distribution and the number of Euler steps

Diffusion: predict the noise, then subtract it a hundred times

The denoising diffusion probabilistic model recipe (arXiv 2006.11239) corrupts data over T discrete timesteps according to a variance schedule, trains a network to predict the noise at each timestep, then walks the chain backwards. Diffusion Policy (arXiv 2303.04137, first posted 7 March 2023, revised to v5 on 14 March 2024) put that machinery behind a visuomotor imitation learning head and reported "an average improvement of 46.9%". Check which version you are quoting: the v5 PDF says 15 tasks across 4 manipulation benchmarks, while the abstract on the arXiv listing page still says 12. It is in lerobot as policy type diffusion, and it is not a vision-language-action model: no language conditioning, no web pretraining, no checkpoint to start fine-tuning from. You train it from scratch on your own data, the same way you train ACT.

python
n_obs_steps: int = 2
horizon: int = 64
n_action_steps: int = 32

noise_scheduler_type: str = "DDPM"
num_train_timesteps: int = 100
beta_schedule: str = "squaredcos_cap_v2"
beta_start: float = 0.0001
beta_end: float = 0.02
prediction_type: str = "epsilon"
clip_sample: bool = True
clip_sample_range: float = 1.0

# None means: fall back to num_train_timesteps
num_inference_steps: int | None = None
lerobot/policies/diffusion/configuration_diffusion.py, main branch, read 23 August 2026

That last line is the one that costs you. num_inference_steps is None out of the box, and modeling_diffusion.py resolves None to num_train_timesteps, so an untouched lerobot Diffusion Policy runs 100 U-Net calls to produce one action chunk. The camera encoders are not in that loop: _prepare_global_conditioning runs them once and the result is passed into every iteration as global_cond, which is the same trick Pi0 uses at a larger scale. The paper says so in as many words, that the policy "extracts the visual representation once regardless of the denoising iterations". So the 100 steps multiply the U-Net, not the whole model. That is still 100 transformer-sized forward passes per chunk.

python
if config.num_inference_steps is None:
    self.num_inference_steps = self.noise_scheduler.config.num_train_timesteps
else:
    self.num_inference_steps = config.num_inference_steps

# _prepare_global_conditioning() encodes the images ONCE, outside the loop.
# conditional_sample() then reuses that vector on every step:
self.noise_scheduler.set_timesteps(self.num_inference_steps)
for t in self.noise_scheduler.timesteps:
    model_output = self.unet(sample, t, global_cond=global_cond)
    sample = self.noise_scheduler.step(model_output, t, sample, generator=generator).prev_sample
lerobot/policies/diffusion/modeling_diffusion.py: the fallback, the conditioning, and the sampling loop
The paper is faster than the default

Diffusion Policy does not need 100 steps at inference. The paper states that "using DDIM with 100 training iterations and 10 inference iterations enables 0.1s inference latency on a Nvidia 3080 GPU", and its own implementation details say the real-world benchmarks ran DDIM at 16 inference iterations while the simulation benchmarks kept 100 for both training and inference. In lerobot that switch is --policy.noise_scheduler_type=DDIM --policy.num_inference_steps=10. If you benchmark Diffusion Policy against a flow matching model without setting those, you are benchmarking a default, not a method.

Flow matching: predict a velocity, then walk ten steps

Flow matching comes from Lipman et al., "Flow Matching for Generative Modeling" (arXiv 2210.02747, posted 6 October 2022). The idea is to regress a vector field directly instead of simulating an ODE during training, and to be free about which probability path you use. The paper argues that optimal transport paths are "more efficient than diffusion paths, provide faster training and sampling, and result in better generalization". Rectified flow (arXiv 2209.03003, Liu, Gong and Liu, 7 September 2022) reaches the same place from the straight-line side and reports "high quality results even with a single Euler discretization step". If you want the full derivation with code, the Flow Matching Guide and Code (arXiv 2412.06264, 9 December 2024) is the reference.

In Pi0 the path is literally a straight line. The Pi0 paper (arXiv 2410.24164, 31 October 2024) writes the interpolation as A_tau = tau * A + (1 - tau) * eps with eps drawn from a standard normal, and the target as u(A_tau | A) = A - eps, integrating from tau = 0 (noise) to tau = 1 (actions). That is the entire forward process. No alpha_bar, no beta schedule, no variance term to get wrong. Pi0.5 (arXiv 2504.16054, 22 April 2025) uses the same interpolation, calls tau "the flow matching time index", and then flips the sign: it trains the model "to predict the flow vector field" omega - a, noise minus action. Same straight line, mirrored axis.

python
dt = -1.0 / num_steps
x_t = noise
for step in range(num_steps):
    time = 1.0 + step * dt
    time_tensor = torch.tensor(time, dtype=torch.float32, device=device).expand(bsize)
    v_t = denoise_fn(x_t, time_tensor)
    x_t = x_t + dt * v_t
return x_t
lerobot/policies/common/flow_matching.py: the sampler shared by pi0, pi05, smolvla and eo1

That is the sampler, minus the real-time-chunking hook that wraps the velocity call. Eight lines, no scheduler object, no state. With num_steps = 10 the step size is 0.1 and the loop walks from time 1.0, which holds pure noise, down to time 0.0, which holds the action chunk. lerobot's docstring is blunt about the provenance: it calls this "the openpi sampling loop" and notes that pi0, pi05, smolvla and eo1 each used to carry their own copy. The training side of modeling_pi05.py matches it exactly, with x_t = time * noise + (1 - time) * actions and u_t = noise - actions.

The two sign conventions will cost you a day

There are two mirrored conventions in circulation and both are correct in their own frame. The Pi0 paper puts clean actions at tau = 1, noise at tau = 0, and regresses A - eps. openpi, Pi0.5 and lerobot put noise at time = 1, actions at time = 0, and regress noise - actions, with the negative dt absorbing the difference. Port a target or a sampler between the two and keep the sign, and you integrate away from the data. The output is still normalised, so it looks superficially plausible; the symptom is a policy that moves smoothly and confidently to the wrong place. That failure sits next to the ones on the failure-mode index.

The AY-Robots glossary entry for the LeRobot dataset format, showing the episode, camera stream and joint state structure that both objectives consume
Neither objective cares which one you picked at recording time. Both read the same LeRobot dataset: episodes, camera streams, joint states. The format version does matter, and differently per model.

Step counts across the five policies you can train here

Four of the five trainable policies on this platform use flow matching. That is not obvious from the labels, because the GR00T N1.7 model card names the objective and the architecture in the same breath. It says the model "uses a flow matching action transformer to model a chunk of actions conditioned on vision, language and proprioception", and then that "the flow matching transformer is implemented as a diffusion transformer (DiT), in which the diffusion step conditioning is implemented using adaptive layernorm (AdaLN)". DiT is the architecture. Flow matching is the objective. Reading "DiT" as "diffusion objective" is the mistake, and it is an easy one to make when the card's own timing table calls the integration steps "denoising steps". GR00T N1.7 and GR00T N1.5 use the same wording.

PolicyObjectiveSampler stepsInference on AY-RobotsGPU tier
GR00T N1.7flow matching in a DiT4 in NVIDIA's published timing table152 ms per action stepA100 80 GB or H100 80 GB
GR00T N1.5flow matching in a DiTnot stated; the card carries no timing table165 ms per action stepA100 80 GB or H100 80 GB
Pi0.5flow matching, 300 M action expert10 (num_inference_steps)485 ms per action stepA100 80 GB or H100 80 GB
SmolVLAflow matching, compact expert10 (num_steps)245 ms per action stepRTX 4090 or any 24 GB card
ACTconditional VAE, direct regressionnone, one forward pass20 ms per action stepRTX 4090 or any 24 GB card

Read that table twice, because it breaks the obvious story. If sampler steps drove inference latency, Pi0.5 at 10 steps would sit close to GR00T N1.7 at 4 steps, and both would be far behind a 100-step Diffusion Policy. Instead Pi0.5 is three times slower than GR00T N1.7 and roughly twenty-four times slower than ACT, which does no iterative sampling at all. The step count is a multiplier on the per-step cost, and the per-step cost is not the same across these models. The head-to-head page has the rest of the comparison, and Pi0.5 against SmolVLA is the more useful one if you are choosing between two flow matching models.

The AY-Robots policies comparison table listing GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT with parameter counts, GPU tier, inference latency and minimum episodes
The five trainable policies with their real inference numbers. Four use flow matching. The fastest one does not.

Where the time actually goes

Pi0 was designed so the sampler is the cheap part. The paper is explicit: "Note that inference can be implemented efficiently by caching the attention keys and values for the prefix o_t and only recomputing the suffix corresponding to the action tokens for each integration step." The images and the language prompt go through the PaliGemma backbone once. The 10 Euler steps then only touch the action expert, which was sized for that job: "To speed up inference (which requires multiple forward passes of the action expert), we downsize the action expert to {width=1024, mlp_dim=4096}", giving the 300 M figure both Pi0 and Pi0.5 quote. Whether the design goal is actually met is a measurement, and there are exactly two published ones.

Published measurementEncode plus backboneSampler (action head)End to end
Pi0, RTX 4090, 10 flow steps (paper Table I)14 ms image encoders + 32 ms observation pass = 46 ms27 ms for 10 passes73 ms on-board, 86 ms off-board
GR00T N1.7, H100 80 GB, 4 steps, PyTorch eager (model card)6.2 ms data processing + 31.3 ms backbone = 37.5 ms48.2 ms85.8 ms, 11.7 Hz
GR00T N1.7, H100 80 GB, 4 steps, TensorRT full pipeline6.2 ms data processing + 8.8 ms backbone = 15.0 ms12.3 ms27.9 ms, 35.9 Hz

The first two rows disagree about where the money goes, and both come from the vendor. Pi0's prefix costs nearly twice its sampler on a 4090. GR00T N1.7's action head costs more than its backbone on an H100, at four steps, and it stays the larger half after TensorRT. So "the backbone dominates" is a Pi0 statement, not a law. The one thing that generalises is the third row: same model, same 4 steps, same card, 27.9 ms against 85.8 ms depending only on the serving stack. Before you attribute a latency difference to an objective, check that both numbers came off the same pipeline.

  1. Encode the camera frames and the prompt through the 3 B backbone. Once per chunk.
  2. Cache its attention keys and values as a prefix.
  3. Run the 300 M action expert against that cached prefix, 10 times, changing only the noisy action chunk and the time scalar.
  4. Emit 50 continuous actions, denormalise them, send them to the arm.
What the platform number does and does not mean

AY-Robots publishes 485 ms per action step for Pi0.5 and 20 ms for ACT. That is the loop budget your task has to fit into, measured on this platform's inference path. It is not a decomposition of backbone versus sampler, and it is not the number in anyone's paper. Treat every published per-model latency as belonging to the stack that measured it: Pi0's 73 ms is an RTX 4090 with 3 camera images, and NVIDIA's 27.9 ms is an H100 with one camera and a full TensorRT pipeline. Neither is a prediction about your robot.

Flow matching as an action head
What you get
  • One-line forward process. No beta schedule, no variance parameterisation, nothing to tune wrong.
  • Continuous actions with no discretisation floor, so gripper commands and joint targets keep their resolution.
  • Ten Euler steps instead of a hundred scheduler steps, which matters when each step is a transformer pass.
  • Straight paths are cheap to integrate coarsely, which is exactly the rectified flow argument.
  • The whole sampler is eight lines, so porting and debugging it is tractable.
What it costs
  • Still iterative. You cannot get an action from one forward pass, unlike a regression head.
  • The step count multiplies whatever your per-step cost is, and GR00T N1.7's published numbers show that head can be the larger half.
  • Two mirrored sign conventions are in circulation, and nothing errors when you mix them.
  • The timestep sampling distribution is a real hyperparameter and it is invisible in most training logs.
  • Attaching an expert to a pretrained VLM can hurt the VLM, which is a whole separate research problem.

What the latency actually buys

The trade Pi0 and Pi0.5 both make is chunk size against call frequency. Both use an action horizon of 50, and the Pi0.5 appendix pins it down: the action expert "takes in a sequence of noisy action tokens for an action horizon of 50, i.e. H = 49". Physical Intelligence's own writeup calls that a "50-step (1-second)" action chunk, which puts the control rate at 50 Hz. One generation pass yields a full second of motion, all of it continuous, all of it produced jointly rather than one step conditioned on the last. That is what action chunking is for, and it is why a 485 ms generation can still drive a smooth arm: you are not paying it per servo command. Pi0 reports running inference every 0.5 seconds, after executing 25 of the 50 actions, on its 50 Hz robots.

The alternative is autoregressive action tokens, and Pi0 is direct about why that does not work at this frequency. Flow matching, it says, "allows us to handle high-frequency action chunks (up to 50 Hz) and highly dexterous tasks, which we show pose a major challenge for prior autoregressive VLAs", and notes that "OpenVLA struggles on these tasks because its autoregressive discretization architecture does not support action chunks". FAST (arXiv 2501.09747, 16 January 2025) made discrete tokens competitive by compressing action sequences with a discrete cosine transform. Pi0.5 trains against both representations, the FAST tokens and the flow matching expert, and uses only the expert at inference, because discrete representations "are less well-suited for real-time inference, because they require expensive autoregressive decoding".

The chunk boundary is where smoothness dies

Generating a chunk takes time, and during that time the robot is either finishing the previous chunk or standing still. Black, Galliker and Levine describe the symptom in Real-Time Execution of Action Chunking Flow Policies (arXiv 2506.07339, posted 9 June 2025): high model latency leads to "pauses or out-of-distribution jerky movements at chunk boundaries". Their fix, real-time chunking, generates the next chunk while executing the current one, "freezing" the actions guaranteed to execute and "inpainting" the rest. The paper says it applies to any diffusion- or flow-based VLA "out of the box with no re-training", and lerobot ships the RTC hook inside the same euler_integrate function shown above. If your policy stutters once per chunk rather than randomly, this is the cause, not your dataset. See policy freezes mid-motion.

Two step counts that get confused

"Steps" means two different things in this literature and mixing them up produces nonsense comparisons. Integration steps are how many times the sampler evaluates the velocity field to produce one chunk. Execution steps are how many actions of that chunk you run open-loop before you look at the cameras again. The SmolVLA paper ablates the second one on LIBERO, and the effect is much larger than anything the objective does.

Actions executed before re-observingSpatialObjectGoalLIBERO-10Average
18994855380.3
108994915782.8
307691744270.8
50 (the whole chunk)5470582551.8

Running the full 50-action chunk open-loop costs 31 points of average success against re-observing every 10. That is the SmolVLA paper's Table 13, and it is a bigger lever than the training objective, the sampler, or the step count. On this platform SmolVLA and Pi0.5 ship with n_action_steps equal to the full chunk size of 50, which is the bottom row of that ablation. lerobot's own LIBERO example overrides it to 10. Treat the shipped default as a thing to measure, not a recommendation.

The timestep distribution nobody mentions

Standard flow matching samples the training time uniformly. Pi0 does not, and Pi0.5 inherits the deviation. The Pi0.5 appendix is the clearer of the two: "we deviate from standard uniform sampling" and instead use p(tau) = Beta((s - tau) / s; alpha = 1.5, beta = 1), with timesteps above s excluded, "We use s = 0.999 in our experiments, which accommodates up to 1,000 integration steps". Pi0's Appendix B gives the same distribution and the same s. The shape puts more training mass on low tau, meaning the noisy end of the path, where the vector field is hardest to get right and where a coarse 10-step integration takes its first and largest jumps.

python
def sample_time_beta(bsize, device, *, alpha, beta, scale, offset):
    """Beta-distributed flow-matching timesteps: Beta(alpha, beta) * scale + offset."""
    time_beta = sample_beta(alpha, beta, bsize, device)
    return (time_beta * scale + offset).to(dtype=torch.float32, device=device)

# PI05Config defaults
num_inference_steps: int = 10
time_sampling_beta_alpha: float = 1.5
time_sampling_beta_beta: float = 1.0
time_sampling_scale: float = 0.999
time_sampling_offset: float = 0.001
chunk_size: int = 50
n_action_steps: int = 50
optimizer_lr: float = 2.5e-5
The schedule as lerobot ships it: the sampler in common/flow_matching.py and the defaults in pi05/configuration_pi05.py

Two things worth noticing there. First, this is a genuine hyperparameter that almost never appears in a training log, so if you compare a flow matching run against a diffusion run you may be comparing timestep distributions rather than objectives. Second, lerobot's own preset learning rate for Pi0.5 is 2.5e-5, while the AY-Robots trainer sends 5e-5 with a batch size of 1 and 30000 max training steps. Neither is wrong, but they are different starting points, and you should know which one produced a curve before you read anything into it. The training docs list what the form actually sends.

Does the objective actually win?

Usually, not always, and by less than the framing suggests. The cleanest published ablation is in the SmolVLA paper (arXiv 2506.01844, 2 June 2025), which trains the same action expert two ways, once with flow matching and once with a plain L1 regression loss on the predicted chunk, and evaluates on LIBERO. Table 10 of that paper:

Training objectiveSpatialObjectGoalLIBERO-10Average
Flow matching8994855380.25
Regression (L1)9285863875.25

Five points of average, and the paper reads it as evidence that flow matching "provides better inductive bias for modeling complex, multimodal action distributions". But look at where the five points come from. Regression wins on Spatial and on Goal. The entire margin sits in LIBERO-10, the long-horizon suite, where the gap is 53 against 38. That is the shape you would expect if the benefit is about not averaging two valid strategies into one invalid one, which only bites when the task has genuine branch points. On short, single-mode tasks a regression head is competitive and much cheaper. ACT is a conditional VAE, trained with a reconstruction loss and a KL term at kl_weight 10.0, with no iterative sampling at all, and it is the fastest thing on this platform for exactly that reason.

For cross-model numbers rather than ablations, the arena lists 85 VLA models with 332 benchmark results and links every value to the paper or model card it came from, including Pi0.5, GR00T N1.5 and the transformer variant of Diffusion Policy. Benchmark numbers across papers are not directly comparable, which is the reason each one carries its source.

The AY-Robots arena leaderboard, a sortable table of 85 vision-language-action models with 332 benchmark results, each value linked to its source paper or model card
85 models, 332 results, every number linked back to where it was published. Useful for finding claims, not for settling arguments between papers that used different evaluation protocols.

Running the comparison

Both paths below fine-tune Pi0.5 on your own LeRobot dataset. The manual path is what the lerobot docs give you as of 23 August 2026, against lerobot 0.6.1, the current release on PyPI. The platform path is the same job with the GPU rental and the checkpoint storage handled.

The lerobot pi05 docs are the canonical version of this. Install lerobot with the Pi extra, accept the gated PaliGemma tokenizer licence on the Hub, and log in. The docs are explicit that Pi0.5 "uses the gated google/paligemma-3b-pt-224 tokenizer", so this step is not optional.

bash
pip install -e ".[pi]"        # from a lerobot checkout
# or, from PyPI:
pip install 'lerobot[pi]'

hf auth login
Install and authenticate

Pi0.5 normalises state and action with quantiles, so your dataset's meta/stats.json needs q01 and q99. Older datasets carry only min, max, mean and std and fail on the first batch with "QUANTILES normalization mode requires q01 and q99 stats".

bash
lerobot-edit-dataset \
    --repo_id your_dataset \
    --new_repo_id your_dataset \
    --operation.type recompute_stats \
    --operation.overwrite true
Recompute q01 and q99 into meta/stats.json
The recomputed stats are not where the trainer looks

The docs bury the trap in a footnote: the rewritten dataset lands in $HF_LEROBOT_HOME/your_dataset, not in the cache that --dataset.repo_id reads. Train without --dataset.root=$HF_LEROBOT_HOME/your_dataset (or without pushing to the Hub) and you get the same QUANTILES error again, from a command you just ran successfully. The other option is to skip quantiles entirely with --policy.normalization_mapping='{"ACTION": "MEAN_STD", "STATE": "MEAN_STD", "VISUAL": "IDENTITY"}', which is what the docs' own LIBERO example does.

bash
lerobot-train \
    --dataset.repo_id=your_dataset \
    --policy.type=pi05 \
    --policy.pretrained_path=lerobot/pi05_base \
    --policy.gradient_checkpointing=true \
    --policy.dtype=bfloat16 \
    --policy.device=cuda \
    --policy.freeze_vision_encoder=false \
    --policy.train_expert_only=false \
    --output_dir=./outputs/pi05_mine \
    --job_name=pi05_mine \
    --batch_size=64 \
    --steps=30000 \
    --save_freq=5000 \
    --seed=1000
The fine-tune itself, sized for a single 80 GB card
pretrained_path and path are not the same flag

--policy.path loads weights and the checkpoint's config.json, so stored settings like n_action_steps are inherited and --policy.type must be omitted. --policy.pretrained_path loads weights only, resets stored settings to the class defaults, and requires --policy.type. That reset is why the docs' LIBERO example passes --policy.n_action_steps=10 and --policy.empty_cameras=1 explicitly: without them they fall back to 50 and 0. Passing a --rename_map alongside --policy.pretrained_path renames the batch away from the expected keys and the first batch dies with "All image features are missing from the batch".

If you want the flow matching objective on a card you own rather than rent, SmolVLA is the same idea at 450 M parameters with the same 10-step sampler, and it trains on a 24 GB card. Physical Intelligence's own openpi repo states the requirement plainly: full fine-tuning needs "> 70 GB", LoRA fine-tuning "> 22.5 GB", inference alone "> 8 GB".

An A/B you can actually run this week

The useful experiment is not diffusion against flow matching in the abstract. It is: does the iterative sampler earn its latency on your task. Train the cheapest flow matching model and the cheapest deterministic one on one recording and drive both.

  1. 1
    Record one dataset, use it twice

    Both policies need the same demonstrations. 50 episodes is the platform minimum for Pi0.5 and ACT, 30 for SmolVLA. Record with the desktop client straight from a teleop session so the joint states and camera streams line up: record your first dataset, the client download. Or skip recording and take something from the public dataset directory, which is the cheaper way to run this comparison first.

  2. 2
    Train the deterministic baseline first

    ACT is about 80 M parameters, has no base model at all (it only exists after training on your task), and the platform default is 100000 steps at a cost of 1 to 3 USD on the 24 GB tier. It is your floor. If ACT solves the task, no amount of flow matching will make it more solved, and you will have spent 20 ms per action step instead of 485.

    bash
    lerobot-train \
        --dataset.repo_id=your_dataset \
        --policy.type=act \
        --policy.chunk_size=100 \
        --policy.n_action_steps=100 \
        --batch_size=8 \
        --steps=100000 \
        --seed=1000
  3. 3
    Train the flow matching side

    SmolVLA if you are on your own 24 GB card, Pi0.5 if you are renting 80 GB. Both integrate 10 Euler steps by default. Keep the seed fixed so the run is repeatable; lerobot defaults to 1000, and GR00T's tyro CLI exposes no seed at all, which is worth remembering if GR00T is your third arm of the comparison.

    bash
    lerobot-train \
        --dataset.repo_id=your_dataset \
        --policy.type=smolvla \
        --policy.pretrained_path=lerobot/smolvla_base \
        --batch_size=2 \
        --steps=20000 \
        --seed=1000
  4. 4
    Cut the sampler steps and see if anything breaks

    This is the measurement that tells you whether the objective is doing work. Drop the integration steps and watch success rate against latency. If 4 steps performs like 10, you just bought back part of your loop budget, bounded by the action head's share of it. If it collapses, the multimodality is real and you are paying for something.

    python
    # integration steps, flow matching side (PI05Config / SmolVLAConfig)
    num_inference_steps = 10   # pi05; try 8, 6, 4, 2
    num_steps = 10             # smolvla, same idea, different field name
    
    # the fields to change, diffusion side (DiffusionConfig)
    noise_scheduler_type = "DDIM"
    num_inference_steps = 10   # instead of the None -> 100 fallback
    
    # then re-run the checkpoint
    # lerobot-eval --policy.path=./outputs/.../checkpoints/last/pretrained_model
  5. 5
    Then cut the execution steps, which is the bigger lever

    Separately from the sampler, shorten how much of the chunk you run before re-observing. SmolVLA's own ablation moves 31 points of average LIBERO success between executing 50 actions and executing 10. Change one at a time or you will not know which one moved the number.

    bash
    # generate a 50-action chunk, execute only the first 10
    lerobot-train \
        --dataset.repo_id=your_dataset \
        --policy.type=smolvla \
        --policy.chunk_size=50 \
        --policy.n_action_steps=10 \
        --seed=1000
  6. 6
    Compare on the arm, not on the loss curve

    Validation loss does not rank these. Run both on the same SO-100, same lighting, same object placement, 20 trials each, and count successes. Loss falls, policy does nothing exists because this step gets skipped.

Where flow matching does not help

Being honest about the limits is more useful than another ablation table. Flow matching is a way to sample from a multimodal action distribution cheaply. If your problem is not that, it fixes nothing.

  • Single-mode tasks. If there is one right way to do the motion, a regression head gets there with one forward pass. The SmolVLA ablation shows regression ahead on two of four LIBERO suites.
  • Latency. Pi0.5 runs 10 steps and is the slowest model here; ACT runs zero and is the fastest. Cutting steps helps in proportion to the action head's share of the loop, which is 37 percent for Pi0 on a 4090 and 56 percent for GR00T N1.7 on an H100 in eager mode.
  • Bad data. An expressive head models whatever distribution you gave it, including the inconsistencies. It will reproduce your hesitations faithfully. Data collection quality outranks the objective every time.
  • Memory. Flow matching does nothing for VRAM. Pi0.5 full fine-tuning needs the 80 GB tier and openpi says "> 70 GB". See out of memory during training.
  • The backbone problem. Bolting a continuous expert onto a pretrained VLM has its own cost: Knowledge Insulating Vision-Language-Action Models (arXiv 2505.23705, 29 May 2025) reports that "naively including such experts significantly harms both training speed and knowledge transfer". That is an architecture problem, not an objective problem.
Remote inference is the constraint the objective cannot fix

Inference has to sit next to the servos for fast tasks. The control loop on this platform is 20 to 485 ms per action step depending on the model, and adding public-internet round trips on top turns a working policy into a hesitant one. Cloud inference is viable for slow pick-and-place and not for fast reactive motion. Ten Euler steps instead of a hundred does not change that arithmetic, because the round trip is not part of the sampler. Pi0's own table prices its off-board hop at 13 ms on a local Wi-Fi link; the public internet is not that. If you want to feel the difference before committing hardware, the live arm streams with no signup.

Five policies, compared with real numbers

Parameter counts, GPU tier, inference latency per action step, minimum episodes and dataset format for GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT. Four of the five use flow matching. Only one of them is fast.

Compare the policies
Is flow matching just diffusion with a different name?

No, but they are closer than the framing suggests. Flow matching is the broader framework: the Lipman et al. paper (arXiv 2210.02747) presents it as compatible with a general family of Gaussian probability paths "which subsumes existing diffusion paths as specific instances", and finds that using flow matching with diffusion paths gives "a more robust and stable alternative for training diffusion models". The paper then argues for straight optimal-transport paths instead. In practice the visible differences are the corruption process (a straight interpolation with no beta schedule), the regression target (a velocity rather than the noise), and the sampler (forward Euler rather than a scheduler's reverse step).

Why is Pi0.5 slower than GR00T N1.7 if both use flow matching?

Not because of step counts. NVIDIA's published GR00T N1.7 timings use 4 denoising steps and Pi0.5 defaults to 10 in lerobot, but the step count is a multiplier on a per-step cost that differs between the two models, and the serving stack matters as much as either. The GR00T N1.7 model card lists 85.8 ms end to end in PyTorch eager on an H100 and 27.9 ms for the same model on the same card with a full TensorRT pipeline. On AY-Robots the published figures are 152 ms for GR00T N1.7 and 485 ms for Pi0.5 per action step, both measured on this platform's own inference path.

How many integration steps should I use?

Start at the shipped default, 10 for Pi0.5 and SmolVLA, then measure downward. The Pi0.5 appendix says s = 0.999 "accommodates up to 1,000 integration steps", so the training side is not the limit. The empirical question is where success rate falls off on your task, and the answer is task-dependent. Long-horizon multi-branch tasks tend to need more steps than a single reach and grasp. Measure the execution horizon separately: that one moved 31 points in SmolVLA's ablation.

Which velocity sign is correct, A - eps or eps - A?

Both, in their own frame. The Pi0 paper puts clean actions at tau = 1 and integrates from tau = 0, with target A - eps. openpi, Pi0.5 and lerobot put noise at time = 1 and integrate down to time = 0, with target noise - actions and a negative dt. lerobot's modeling_pi05.py is the concrete reference: x_t = time * noise + (1 - time) * actions, u_t = noise - actions. Mixing the two produces a policy that runs without error and moves confidently to the wrong place.

Can I train Diffusion Policy on AY-Robots?

No. The five trainable policies here are GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT. Diffusion Policy ships in lerobot as policy type diffusion and you can train it yourself from a checkout; it just is not one of the options in this platform's training form. If you want a flow matching model at the small end instead, that is SmolVLA, which needs 30 episodes and about 1 to 3 USD per run.

Does the choice of objective matter more than the dataset?

No, and it is not close. The SmolVLA ablation puts flow matching five points of average success ahead of an L1 regression head on LIBERO. Data problems routinely cost more than that, and so does the execution horizon, which moved 31 points in the same paper. Camera swaps, stale frames, a joint parked at its limit and an inconsistent demonstration strategy all move success rate further than the objective does. Get 50 clean episodes first.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started