
Behaviour cloning errors compound quadratically in the episode length. DAgger and HG-DAgger fix that by training on the states your policy actually visits. Here is the loop on an SO-100.
Your policy works for two seconds. The arm leaves home, tracks toward the cube, and the wrist ends up a centimetre off. From there everything it sees is new, the gripper closes on air, the run is over. Nothing in the training data showed the arm what to do from a centimetre off, so the policy has nothing to say.
That is not a bug in your training run. It is the defining failure of behaviour cloning, it has a published bound, and the fix predates every VLA: DAgger, short for Dataset Aggregation, from Ross, Gordon and Bagnell at AISTATS 2011. Below: the bound, the human-gated variant, and the loop on an SO-100.
What you need to know
- •Behaviour cloning on expert states carries a worst-case cost bound growing with T squared, the episode length. DAgger reduces it to linear in T.
- •The fix is not more demonstrations. It is expert labels on the states your own policy visits when it goes wrong.
- •A real arm cannot label states the learner is driving through, so robotics uses HG-DAgger: the human takes over when failure looks imminent, and only those segments become data.
- •LeRobot 0.6.1 ships this as lerobot-rollout --strategy.type=dagger, and needs a leader arm whose motors can be driven under software.
- •Budget from the literature: collect corrections until they are a third of your dataset in samples, retrain, repeat three times.
- •One action step costs 20 to 485 ms here. A takeover crossing the public internet arrives late.
Why behaviour cloning drifts, with the actual bound
Behaviour cloning is supervised learning with one twist that breaks supervised learning. You fit observation to action on pairs from a human driving the arm, then deploy. The deployed policy picks its own next state, so training happens on the expert's state distribution and testing on the policy's own. Those coincide only if it never errs.
Ross and Bagnell put a number on that gap in 2010, restated as Theorem 2.1 of the DAgger paper. With per-step error epsilon under the expert's state distribution and cost bounded in the unit interval, over an episode of length T the total cost can exceed the expert's by T squared times epsilon. The paper calls the bound tight.
Behaviour cloning: J(pi) ≤ J(pi*) + T2ε, with ε measured under the expert's state distribution.
DAgger: after Õ(uT) iterations some policy in the sequence satisfies J(pi) ≤ J(pi*) + uTεN + O(1), for εN the loss of the best policy in hindsight and u a bound on what one wrong action costs in cost-to-go. Linear in T, not quadratic.
| Episode length T | At 30 Hz | BC worst case, T squared times epsilon | DAgger worst case, u times T times epsilon |
|---|---|---|---|
| 60 steps | 2 s | 36 | 0.6 |
| 300 steps | 10 s | 900 | 3 |
| 900 steps | 30 s | 8100 | 9 |
| 1800 steps | 60 s | 32400 | 18 |
Published bounds at epsilon = 0.01 and u = 1, not a measurement of your arm, and the two epsilons are not the same quantity: the behaviour cloning one is measured under the expert's state distribution, the DAgger one under the distributions the learner itself induced. Cost is bounded in the unit interval, so the behaviour cloning column saturates at T; only its growth rate still says anything. Doubling the horizon quadruples the behaviour cloning worst case and doubles the DAgger one, which is why long-horizon tasks dominate pages like policy only works in one setup and policy freezes mid-motion.
DAgger in one page
- 1Start from demonstrations
Collect a normal dataset by teleoperation and train the first policy with plain behaviour cloning. This is the iteration where beta equals 1: the expert drives.
- 2Roll out the mixed policy
At iteration i the system executes pi_i = beta_i * expert + (1 - beta_i) * learner, beta_i being the probability of taking the expert's action.
python# DAgger, Ross, Gordon and Bagnell, AISTATS 2011, Algorithm 3.1 D = [] pi_hat = init_policy() for i in range(1, N + 1): beta = 1.0 if i == 1 else 0.0 # the parameter-free variant pi_i = lambda s: expert(s) if random() < beta else pi_hat(s) states = rollout(pi_i, horizon=T) # states the LEARNER visits D_i = [(s, expert(s)) for s in states] # labels the EXPERT gives D = D + D_i # aggregate, never replace pi_hat = train(D) return best_on_validation(pi_hat_history) - 3Label the visited states
Record the states the rollout passed through and the action the expert would have taken in each. That asymmetry is the whole idea.
- 4Aggregate, do not replace
Union the new set into the running dataset; every earlier round stays in. That is where the name comes from.
- 5Retrain from the aggregate
Train the next policy on all of D, then return to step 2. Keep the checkpoint that scores best on validation.
Any beta schedule works as long as its running average goes to zero, and the paper reports that the simplest choice, beta = 1 on the first iteration and 0 after, often performs best in practice. On Super Tux Kart the controller stopped falling off the track after 15 iterations, while the supervised baseline did not improve as more expert laps were added.
Step 3 asks the expert to label every state the rollout visits, including states the policy is driving through right now. In a racing game you pause and move a joystick. On an SO-100 you cannot: the arm is moving, the leader is not tracking the follower, and the label you record is what the human would have done a few hundred milliseconds ago.
HG-DAgger: the human holds the gate
HG-DAgger, from Kelly, Sidrane, Driggs-Campbell and Kochenderfer, keeps the aggregation and drops the mixing. HG stands for human-gated: the novice drives, the human watches, and when things go wrong the human takes over. Only the timesteps where the human has control are recorded.
Two things follow. Labels stay clean, because the human produces a continuous trajectory instead of annotating a state they do not control. And the data is recovery data: it starts where the policy got into trouble and ends where the human considers it salvaged. The same run yields a risk threshold learned from where the human actually intervened; rollouts started inside the resulting permissible set had a mean collision rate 12 times lower than those started outside.

| Method | Who drives | What gets recorded | Where it costs you |
|---|---|---|---|
| DAgger (Ross et al., 2011) | Expert and policy mixed | Every visited state | Labels states nobody drives |
| SafeDAgger (Zhang and Cho, 2016) | Policy, safety net switches | Expert segments | A safety classifier |
| EnsembleDAgger (Menda et al., 2018) | Policy, gated by variance | Expert segments | Thresholds you pick |
| HG-DAgger (Kelly et al., 2019) | Policy, human takes over | Human segments only | Reacting in time |
| IWR (Mandlekar et al., 2020) | Policy, human takes over | Both, corrections upweighted | A balanced sampler |
| ThriftyDAgger (Hoque et al., 2021) | Policy, gated by novelty and risk | Expert segments | A risk estimate |
| HIL-SERL (Luo et al., 2024) | Policy, human interrupts RL | Transitions, learned reward | A reward classifier |
| RaC (Hu et al., 2025) | Policy, human takes over | Recovery, then correction | Operator discipline |
- Targets the states your policy actually fails in. More demonstrations of the happy path never visit them.
- Recovery is a skill. A policy that has seen a retry can retry; one trained on clean demos cannot.
- Cheap per round: you record only while the arm is in trouble.
- Mandlekar et al. report 87.3% on a simulated threading task against 76.7% for an equal budget of fresh demos.
- Model-agnostic: the same rounds feed ACT, SmolVLA, GR00T N1.7 or Pi0.5.
- Every round needs a human at the arm for the whole rollout, costlier per minute than clean teleoperation.
- You retrain after every round: 3 to 6 hours and 4 to 12 USD each on the A100 or H100 tier.
- Corrections from a barely working policy are near useless; the published base policies sat at 52 to 58 percent.
Running the loop on an SO-100 with LeRobot
LeRobot ships this as a rollout strategy. In 0.6.1 on PyPI, released 2026-08-03, lerobot-rollout takes --strategy.type with five registered values, base, sentry, highlight, episodic and dagger, and --inference.type with two, sync and rtc. Pin the version: 0.5.1 has no lerobot-rollout entry point, the command first appears in 0.6.0 of 2026-07-06.
pip install "lerobot==0.6.1"
# confirm the registered strategy, robot and teleoperator names on your own install
lerobot-rollout --help- 1Record the base dataset
An ordinary teleoperated recording. Defaults are 60 s per episode, 60 s reset, 50 episodes. The recording walkthrough covers the camera and calibration setup that must stay identical across rounds.
bashlerobot-record \ --robot.type=so100_follower \ --robot.port=/dev/ttyACM0 \ --robot.id=my_follower \ --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30} }" \ --teleop.type=so100_leader \ --teleop.port=/dev/ttyACM1 \ --teleop.id=my_leader \ --dataset.repo_id=me/cube-v0 \ --dataset.num_episodes=50 \ --dataset.single_task="Pick up the cube and put it in the box" - 2Train the base policy
ACT is the sensible start, for reasons below. The training walkthrough and the ACT on SO-100 guide have the hyperparameters.
bashlerobot-train \ --dataset.repo_id=me/cube-v0_20260823_101500 \ --policy.type=act \ --output_dir=outputs/train/act_cube_v0 \ --job_name=act_cube_v0 \ --policy.device=cuda \ --wandb.enable=true - 3Run the policy with a human on the gate
The policy drives; you pause when failure looks imminent; the leader powers up onto the follower's pose; you take over with torque released, teleoperate back to a familiar state, finish the sub-task, hand control back. Nothing is recorded during the pause. Space pauses, tab starts and stops a correction, escape ends the session. Use the same leader you recorded round zero with.
bashlerobot-rollout --strategy.type=dagger \ --robot.type=so100_follower \ --robot.port=/dev/ttyACM0 \ --robot.id=my_follower \ --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 2, width: 640, height: 480, fps: 30} }" \ --teleop.type=so100_leader \ --teleop.port=/dev/ttyACM1 \ --teleop.id=my_leader \ --policy.path=outputs/train/act_cube_v0/checkpoints/last/pretrained_model \ --dataset.repo_id=me/rollout_cube_hil_r1 \ --dataset.single_task="Pick up the cube and put it in the box" \ --dataset.fps=30 \ --strategy.record_autonomous=true \ --duration=1800 # for Pi0, Pi0.5 or SmolVLA add real-time chunking so the arm does not stutter: # --inference.type=rtc - 4Merge round zero with round one
A first-class merge operation exists, so no stitching parquet by hand. It refuses unless fps, robot_type and the feature set match, and that is where a dagger dataset trips: it carries an extra boolean intervention column your demonstrations do not have.
bash# 1. drop the column the demo dataset does not have lerobot-edit-dataset \ --operation.type=remove_feature \ --operation.feature_names='[intervention]' \ --repo_id=me/rollout_cube_hil_r1_20260823_143000 \ --new_repo_id=me/rollout_cube_hil_r1_flat # 2. aggregate round 0 and round 1 into one dataset lerobot-edit-dataset \ --operation.type=merge \ --operation.repo_ids='[me/cube-v0_20260823_101500, me/rollout_cube_hil_r1_flat]' \ --new_repo_id=me/cube-v1-aggregate - 5Fine-tune on the aggregate
Point the trainer at the merged dataset, not the corrections alone. The LeRobot page says to fine-tune on combined data, but its own example command points at the HIL dataset by itself.
bashlerobot-train \ --dataset.repo_id=me/cube-v1-aggregate \ --policy.type=act \ --policy.pretrained_path=outputs/train/act_cube_v0/checkpoints/last/pretrained_model \ --output_dir=outputs/train/act_cube_v1 \ --job_name=act_cube_v1 \ --policy.device=cuda - 6Read the intervention rate, not the loss
LeRobot's HIL-SERL guide calls a run successful when the human intervenes a lot at the start and less as it goes on. If interventions per episode stay flat across two rounds, loss falls but the policy does nothing is the better next stop.
Rollout datasets must be named rollout_something. lerobot-rollout raises Dataset names for rollout must start with 'rollout_' and exits, so --dataset.repo_id=me/cube-v0-hil-round1 never records a frame. lerobot-record refuses the mirror case, eval_.
Both then rename the dataset behind you: a date-time tag is appended at creation, so me/rollout_cube_hil_r1 lands on disk as me/rollout_cube_hil_r1_20260823_143000. Pass --dataset.no_stamp, or read the real name out of the log before writing the merge command.
--strategy.record_autonomous defaults to false. In that mode the dagger strategy keeps only the human correction windows, each saved as its own episode, and discards the autonomous frames. That is plain HG-DAgger, but it is not what IWR-style weighting needs: no non-intervention set is left to balance against. Set it to true and both segment types are recorded, corrections tagged intervention=true.
The flag also changes what ends the run. --strategy.num_episodes only bounds the corrections-only loop; with record_autonomous=true the session runs until you press escape, so give it --duration in seconds if you want it to stop on its own.
The dagger strategy needs a teleoperator whose motors can be driven under software: torque on to move the leader onto the follower's pose during the pause, torque off so you can drive. Both so100_leader and so101_leader qualify, but LeRobot's HIL-SERL guide notes the SO101 leader has reduced gears that let it track the follower, making takeover smoother than the gearless SO100. One trap sits in the source: if a rename_map renames action keys, the smooth handover silently does nothing. And STS3215 servos run at 7.4 V; 12 V destroys them.

How much correction data, and how to weight it
The most useful number here is not a bound, it is a budget. Mandlekar et al. started from 30 demonstrations, then ran three rounds, collecting in each until intervention samples reached roughly 33 percent of the initial dataset's sample count. That keeps human-labelled samples equal across methods regardless of base policy quality.
| Round | What you run | Human-controlled data to collect | What you train |
|---|---|---|---|
| 0 | Teleoperated demonstrations | 50 episodes (SmolVLA needs 30) | Base policy |
| 1 | Rollouts with takeover | A third of round 0 in frames | Fine-tune on rounds 0 and 1 |
| 2 and 3 | Rollouts with takeover | The same again, at whatever fails now | Fine-tune on everything so far |
Plain HG-DAgger discards the autonomous frames and trains on human segments plus the original demonstrations. IWR keeps both and weights them so the sampler draws from the intervention and non-intervention sets in equal proportion; on the same data it beat HG-DAgger on both tasks. Do not expect the trainer to do this for you: --dataset.repo_id is documented as accepting a list, but in 0.6.1 that path raises The MultiLeRobotDataset isn't supported for now. One merged directory means plain concatenation.
| Method | Threading task | Coffee machine task |
|---|---|---|
| Base policy (30 demonstrations) | 58.0 +/- 9.2 | 52.0 +/- 3.5 |
| Equal budget of fresh demonstrations | 76.7 +/- 2.3 | 64.9 +/- 8.3 |
| HG-DAgger, 3 rounds | 75.3 +/- 8.1 | 69.6 +/- 10.1 |
| IWR, 3 rounds | 87.3 +/- 5.0 | 87.5 +/- 9.4 |
Success rates in percent over 3 seeds, simulated Sawyer arm in robosuite, 2-layer LSTM policies; threading is one operator, coffee machine averages three. Not SO-100 numbers, so take the ordering, not the absolutes. Three rounds of corrections beat an equal budget of fresh demonstrations on both tasks. The data collection guide covers the case where the answer is still more demonstrations.
Two 2025 results say the loop scales. RaC runs this recovery-then-correction protocol on three real bimanual tasks and reports beating the prior state of the art with 10 times less data collection time and 10 times fewer samples. RECAP, behind Physical Intelligence's pi-star 0.6, folds teleoperated interventions from autonomous execution into an RL objective and reports more than double the throughput and roughly half the failure rate on its hardest tasks.
Two ways to run the loop
You need the arm, a leader arm with drivable motors, two cameras, a 24 GB card for ACT or SmolVLA, and uninterrupted time next to the robot. What costs you is the part nobody writes down.
- Round 0 is bench time: at 60 s per episode plus 60 s of reset, 50 episodes is about 100 minutes at the arm before a single correction exists.
- Camera positions and calibration must not move between rounds. A shifted camera turns correction data into a different task.
- Merging LeRobot datasets is one command, but strict: same fps, same robot_type, identical features.
- Appending has its own rule: --resume=true works, but --dataset.num_episodes then means additional episodes, and --dataset.root is required.
- You own the failures: camera not detected, dataset rejected as v3, out of memory.
The platform supplies four of the five pieces. The live arm streams a physical SO-100 in the browser with no signup, the desktop client records LeRobot-format datasets from a teleop session, training is a form that rents a GPU by required VRAM, and inference auto-provisions a pod with an idle watchdog.
The fifth is the splice. Nothing pauses a running policy mid-episode, hands you the leader arm and stitches your correction into the same trajectory. You run rounds, not interleaved segments.
- 1Serve the policy and run it
Provision the inference pod, point the local client at the endpoint, run the policy. The run your first policy walkthrough has the setup.
- 2Stop on drift, record the correction
Stop the policy where it leaves the trajectory and take it with teleoperation from there. Those recovery episodes start in states no reset produces.
- 3Retrain on the combined set
Add the corrections and launch the next run from the training form, fine-tuning from the previous checkpoint.
A retrain on the RTX 4090 tier (ACT, SmolVLA) runs 2 to 5 hours for about 1 to 3 USD. On the A100 or H100 tier (GR00T N1.7, GR00T N1.5, Pi0.5) it is 3 to 6 hours for 4 to 12 USD. Four rounds against ACT is 4 to 12 USD; against Pi0.5, 16 to 48 USD.
Where this does not help, and the latency wall
HG-DAgger only works if the human can spot a bad state and react before it is unrecoverable; the authors rule the method out where that is impossible. Add this platform's inference latency: 20 ms per action step with ACT, 152 ms with GR00T N1.7, 165 ms with GR00T N1.5, 245 ms with SmolVLA, 485 ms with Pi0.5. Put a public-internet round trip in front of that and remote takeover is viable for slow pick and place, not fast reactive motion.
One class of failure sits outside DAgger's reach. If the policy fails identically in every episode from the first frame, that is a broken input or a broken training run, not compounding error: check loss falls but the policy does nothing and policy only works in one setup. A gripper that never closes is mechanical first, and a joint stopping short is joint stops early.
The other limit is the dataset format. GR00T N1.7 and N1.5 take LeRobot v2.0 or v2.1, and a v3.0 dataset crashes the GR00T loader, so every correction round against GR00T has to come out in the accepted version. ACT, SmolVLA and Pi0.5 take v3.0; the dataset rejected as v3 page has the conversion.
Which policy to run the loop against
| Policy | Params | Per action step | Min episodes | Dataset format | Retrain per round |
|---|---|---|---|---|---|
| ACT | ~80 M | 20 ms | 50 | LeRobot v3.0 | 1 to 3 USD |
| SmolVLA | ~450 M | 245 ms | 30 | LeRobot v3.0 | 1 to 3 USD |
| GR00T N1.7 | ~3 B, ~40 M trained | 152 ms | 50 | LeRobot v2.0 or v2.1 | 4 to 12 USD |
| GR00T N1.5 | ~3 B | 165 ms | 50 | LeRobot v2.0 or v2.1 | 4 to 12 USD |
| Pi0.5 | ~3 B, PaliGemma backbone | 485 ms | 50 | LeRobot v3.0 | 4 to 12 USD |

ACT is the obvious start: 20 ms per action step means your takeover lands roughly when you decide it, and four rounds cost 4 to 12 USD. It has no base model, so round zero must be real demonstrations. SmolVLA is the compromise at 30 minimum episodes, compared under ACT against SmolVLA. GR00T N1.7 and Pi0.5 are cloud-only. See the policy overview.
Action chunking means the model commits to a block of future actions at once, so a takeover cannot land mid-chunk without something absorbing the discontinuity; that is what --inference.type=rtc is for. For the counterweight on data scale see the BC-Z write-up, for the hardware end the SO-100 complete guide, and for the loop on your own arm imitation learning on the SO-100.
Your policy drifts. Find out which failure it actually is.
The failure-mode pages take the drift signatures one at a time, each with the check that separates it from the others, so you know whether you need correction rounds or a different fix entirely.
Open the fix indexIs DAgger the same thing as fine-tuning on failure cases?▾
No. Fine-tuning on failures means fresh demonstrations of the failing cases, each from a clean reset. DAgger records from the state the policy actually reached, mid-episode, which no reset produces.
How many correction rounds before it stops helping?▾
The published experiments use three rounds, and the first does most of the work: on the threading task IWR went from 58.0 percent at the base policy to 84.0 after round one, then 90.7 and 87.3, moves of the same order as the reported standard deviations of 3 to 5 points.
Does LeRobot implement DAgger or HG-DAgger?▾
The dagger rollout strategy in 0.6.1 is the human-gated loop, and what it records depends on one flag. With the default --strategy.record_autonomous=false you get plain HG-DAgger, only the correction windows, each its own episode. Set it to true and you get both segment types, corrections tagged intervention=true. Its docs cite Ross et al. 2011, Kelly et al. 2019, RaC and RECAP.
Why does my rollout exit before recording anything?▾
Most likely the dataset name: lerobot-rollout requires the repo name to start with rollout_ and exits otherwise. The dagger strategy also refuses to start without --teleop.type and --dataset.repo_id.
Can I run this with the AY-Robots browser teleoperation?▾
You can run the coarse version: drive the policy from an inference pod, stop it when it drifts, record correction episodes from the state the arm reached. What it cannot give you is a low-latency mid-episode takeover, and a late takeover produces a late label.
Sources
- Ross, Gordon, Bagnell: A Reduction of Imitation Learning and Structured Prediction to No-Regret Online Learning (DAgger), AISTATS 2011
- Ross and Bagnell: Efficient Reductions for Imitation Learning, AISTATS 2010 (source of the T squared epsilon bound)
- Kelly, Sidrane, Driggs-Campbell, Kochenderfer: HG-DAgger, Interactive Imitation Learning with Human Experts
- Zhang and Cho: Query-Efficient Imitation Learning for End-to-End Autonomous Driving (SafeDAgger)
- Menda, Driggs-Campbell, Kochenderfer: EnsembleDAgger, A Bayesian Approach to Safe Imitation Learning
- Hoque et al.: ThriftyDAgger, Budget-Aware Novelty and Risk Gating for Interactive Imitation Learning
- Mandlekar et al.: Human-in-the-Loop Imitation Learning using Remote Teleoperation (IWR)
- Hu et al.: RaC, Robot Learning for Long-Horizon Tasks by Scaling Recovery and Correction
- Luo, Xu, Wu, Levine: Precise and Dexterous Robotic Manipulation via Human-in-the-Loop Reinforcement Learning (HIL-SERL)
- Physical Intelligence: pi-star 0.6, a VLA That Learns From Experience (RECAP)
- LeRobot docs: Human-In-the-Loop Data Collection (lerobot-rollout --strategy.type=dagger)
- LeRobot docs: HIL-SERL Real Robot Training Workflow Guide
- LeRobot docs: Imitation Learning on Real-World Robots (record, train, resume)
- huggingface/lerobot on GitHub
- lerobot on PyPI (0.6.1 released 2026-08-03, 0.6.0 on 2026-07-06, no lerobot-rollout in 0.5.1)
Sources
- Ross, Gordon, Bagnell: A Reduction of Imitation Learning and Structured Prediction to No-Regret Online Learning (DAgger), AISTATS 2011
- Ross and Bagnell: Efficient Reductions for Imitation Learning, AISTATS 2010 (source of the T squared epsilon bound)
- Kelly, Sidrane, Driggs-Campbell, Kochenderfer: HG-DAgger, Interactive Imitation Learning with Human Experts
- Zhang and Cho: Query-Efficient Imitation Learning for End-to-End Autonomous Driving (SafeDAgger)
- Menda, Driggs-Campbell, Kochenderfer: EnsembleDAgger, A Bayesian Approach to Safe Imitation Learning
- Hoque et al.: ThriftyDAgger, Budget-Aware Novelty and Risk Gating for Interactive Imitation Learning
- Mandlekar et al.: Human-in-the-Loop Imitation Learning using Remote Teleoperation (IWR)
- Hu et al.: RaC, Robot Learning for Long-Horizon Tasks by Scaling Recovery and Correction
- Luo, Xu, Wu, Levine: Precise and Dexterous Robotic Manipulation via Human-in-the-Loop Reinforcement Learning (HIL-SERL)
- Physical Intelligence: pi-star 0.6, a VLA That Learns From Experience (RECAP)
- LeRobot docs: Human-In-the-Loop Data Collection (lerobot-rollout --strategy.type=dagger)
- LeRobot docs: HIL-SERL Real Robot Training Workflow Guide
- LeRobot docs: Imitation Learning on Real-World Robots (record, train, resume)
- huggingface/lerobot on GitHub
- lerobot on PyPI (0.6.1 released 2026-08-03, 0.6.0 on 2026-07-06, no lerobot-rollout in 0.5.1)
Ready for high-quality robotics data?
AY-Robots connects your robots to skilled operators worldwide.
Get Started