The AY-Robots recording tutorial showing how a LeRobot dataset is captured from a teleoperation session with an SO-100 arm.
LeRobotDataset RecordingAction ChunkingACTSO-100

Episode Length and What It Does to Training

AY-Robots ResearchAugust 23, 202621 min read

Episode length only means something next to chunk size. What the LeRobot defaults do, why k/T predicts trouble, and how much supervision padding quietly removes.

What you need to know

  • lerobot-record ships with episode_time_s=60, reset_time_s=60, fps=30 and num_episodes=50. The 60 seconds is a timeout you hit by accident, not a length anybody recommends.
  • Real LeRobot reference datasets are much shorter. lerobot/svla_so101_pickplace averages 238.8 frames per episode, which is 8.0 seconds at 30 fps.
  • Episode length means nothing on its own. The number that predicts trouble is k/T: chunk size divided by episode length in frames.
  • ACT's default chunk_size of 100 covers 3.33 seconds at 30 fps. On an 8 second episode that is 42 percent of the entire task predicted from one camera frame.
  • The last chunk_size minus 1 frames of every episode produce a partly padded target. At k=100 on a 240 frame episode that is 41 percent of your frames.
  • Padded steps are masked out of the ACT loss, so they do not corrupt training. They quietly remove supervision instead: 21 percent of all action steps in that same case.
  • Longer is not automatically better. Long episodes add reset drift, operator fatigue and several valid solutions to the same task, which a single-observation policy cannot tell apart.

What an episode actually is, in frames

An episode is one uninterrupted attempt at the task: you press record, you drive the arm through the pick, you stop. In a LeRobot dataset it is not a file any more. Since the v3.0 format, many episodes live inside the same Parquet and MP4 shards, and the episode boundaries are resolved through meta/episodes/ records that carry per-episode lengths and byte offsets. What you get back when you index the dataset is a frame, not an episode. The dataset length is the total frame count, not the episode count.

That distinction is the whole article. Every training sample is one frame plus a window of future actions hanging off it. How long your episodes are decides how many of those windows fit inside an episode, and how many of them run off the end. For imitation learning that is the difference between a dataset that teaches a horizon and one that teaches a fragment.

bash
# Episode statistics of any LeRobot dataset on the Hub, without downloading it
curl -sL https://huggingface.co/datasets/lerobot/svla_so101_pickplace/resolve/main/meta/info.json \
  | python3 -c 'import json,sys; d=json.load(sys.stdin); \
      print(d["codebase_version"], d["robot_type"], d["fps"], "fps"); \
      print(d["total_episodes"], "episodes,", d["total_frames"], "frames"); \
      print(round(d["total_frames"]/d["total_episodes"],1), "frames/ep =", \
            round(d["total_frames"]/d["total_episodes"]/d["fps"],1), "s")'

# v3.0 so100_follower 30 fps
# 50 episodes, 11939 frames
# 238.8 frames/ep = 8.0 s
Run this against any repo id before you train on it. Checked 24 August 2026.

Three reference datasets, read from their meta/info.json on the same day, give a useful sense of the real range. All three carry exactly 50 episodes, which is also the ACT minimum on this platform and the lerobot recording default.

DatasetFormatEpisodesFramesfpsFrames / episodeSeconds / episode
lerobot/svla_so101_pickplacev3.05011,93930238.88.0
lerobot/svla_so100_pickplacev3.05019,63130392.613.1
lerobot/aloha_sim_insertion_humanv3.05025,00050500.010.0

The simulated ALOHA set is exactly 500 frames per episode because simulation episodes are cut at a fixed length. The two real SO-100 sets are averages, and they sit at 8 and 13 seconds. Nobody recorded for a minute.

The recording defaults are a ceiling, not a target

Here is what teleoperation recording gives you if you change nothing. These are the field defaults in src/lerobot/configs/dataset.py on the lerobot main branch, read on 24 August 2026 at version 0.6.2 in development. The last release on PyPI at that point was 0.6.1, published 3 August 2026.

FieldDefaultWhat it means in practice
fps30One frame and one action row every 33.3 ms
episode_time_s60Hard stop at 1800 frames per episode
reset_time_s60A minute of unrecorded time to put the scene back
num_episodes50Same as the platform minimum for ACT, GR00T and Pi0.5
videotrueCamera frames encoded to MP4 rather than kept as PNG
push_to_hubtrueUploads when you finish unless you turn it off
streaming_encodingfalseOff by default; turning it on makes save_episode near instant
num_image_writer_threads_per_camera4Four writer threads per camera
Episodes end when you say so, not when the timer does

During recording the keyboard listener maps right arrow to end the current episode early, left arrow to end it and re-record it, and Esc to stop the session. That is why real datasets average 8 to 13 seconds while the default is 60: the operator finishes the pick and taps right arrow. If you never touch the arrow keys, every episode runs the full 1800 frames and the tail is the robot sitting still.

Left alone, the defaults also set your wall clock. Fifty episodes at 60 seconds of recording plus 60 seconds of reset is 100 minutes at the arm, and a large fraction of the frames you paid for with that time will be the arm holding a finished pose. Set the two timers deliberately instead.

bash
lerobot-record \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem58760431541 \
  --robot.id=my_follower \
  --robot.cameras="{front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
  --teleop.type=so100_leader \
  --teleop.port=/dev/tty.usbmodem58760431551 \
  --teleop.id=my_leader \
  --dataset.repo_id=${HF_USER}/pick-cube-short \
  --dataset.single_task="Pick up the red cube and drop it in the bin" \
  --dataset.num_episodes=50 \
  --dataset.fps=30 \
  --dataset.episode_time_s=15 \
  --dataset.reset_time_s=10 \
  --dataset.streaming_encoding=true \
  --dataset.encoder_threads=2 \
  --display_data=true
A 15 second ceiling on a task you can do in 8. The ceiling catches the attempts that go wrong.
The AY-Robots recording tutorial page showing the steps to capture a LeRobot dataset from a teleoperation session with an SO-100 arm.
The recording walkthrough at /learn/record-your-first-dataset. The same episode-length decisions apply whether you record from the desktop client or from lerobot-record on your own machine.

Action chunking is the other half of the equation

A chunked policy predicts a block of future actions from one observation. Action chunking came out of the ACT paper, which framed it as a way to cut the effective horizon of a task. The paper's phrasing is direct: predicting the next k timesteps instead of one reduces the effective horizon of the task by k-fold, which is what keeps compounding error under control. Their ablation is the number everyone quotes: success rises from 1 percent at k=1 to 44 percent at k=100, then tapers slightly at k=200 and k=400 as the policy approaches fully open-loop control.

Every policy on this platform chunks, but they chunk at very different scales. These are the config defaults in lerobot main, plus the action horizon in NVIDIA's own SO-100 example config for GR00T.

PolicyChunk sizeExecuted stepsObservation stepsSeconds of motion at 30 fps
ACT10010013.33
SmolVLA505011.67
Pi0.5505011.67
GR00T N1.7 / N1.5 (SO-100 example config)161610.53

Note the observation column. All four use n_obs_steps=1: one camera frame, one joint state, no history. The GR00T SO-100 config makes this explicit, with delta_indices=[0] for both video and state and range(0, 16) for the action. There is no context window in the language-model sense. The only temporal context these policies have is the chunk they are about to emit.

The learnable horizon is shorter than the episode

It is tempting to read episode length as how much of the task the policy learns. It is not. What a chunked policy learns is a mapping from one observation to the next k actions, and nothing longer. The episode contributes two things on top of that: it decides how many such mappings exist, and it decides how consistent they are with each other. A 60 second episode does not teach a 60 second behaviour. It teaches eighteen overlapping 3.33 second behaviours that happen to be stitched together by the fact that they came from one continuous attempt.

This is why effective horizon is the right phrase and context is the wrong one. There is no context window being filled. The policy re-reads the world every k steps and starts again from whatever it sees. Continuity across an episode is something you get by accident, because consecutive observations look similar and the demonstrations were consistent, not because the architecture carries state. When people say a policy lost track of the task, what usually happened is that two points in the episode looked alike from the camera and the policy picked the wrong continuation. Longer episodes make that collision more likely, not less.

The ratio that predicts trouble: k over T

Call the chunk size k and the episode length in frames T. Two derived numbers follow, and both of them are more useful than either input on its own. T/k is how many independent decision points the policy gets per episode. k/T is how much of the task it commits to from a single frame.

python
fps, episode_s, chunk = 30, 8, 100
T = int(episode_s * fps)
print(f"frames per episode   T        = {T}")
print(f"chunks per episode   T/k      = {T/chunk:.1f}")
print(f"ratio                k/T      = {chunk/T:.2f}")
print(f"frames with padding  (k-1)/T  = {(chunk-1)/T:.1%}")
print(f"action steps masked  (k-1)/2T = {(chunk-1)/(2*T):.1%}")

# frames per episode   T        = 240
# chunks per episode   T/k      = 2.4
# ratio                k/T      = 0.42
# frames with padding  (k-1)/T  = 41.2%
# action steps masked  (k-1)/2T = 20.6%
Run this with your own numbers before you launch a training job. It costs nothing and it has saved me runs.

Worked out across the episode lengths people actually record on an SO-100, at 30 fps:

EpisodeT (frames)ACT k=100: T/kACT k=100: k/TSmolVLA / Pi0.5 k=50: T/kk=50: k/TGR00T k=16: T/kk=16: k/T
8 s2402.40.424.80.2115.00.067
13 s3903.90.267.80.1324.40.041
20 s6006.00.1712.00.08337.50.027
30 s9009.00.1118.00.05656.30.018
60 s180018.00.05636.00.028112.50.0089
ACT's default chunk size is wrong for a short SO-100 episode

The ACT chunk ablation was run on the two simulated ALOHA tasks. The lerobot copy of one of them, lerobot/aloha_sim_insertion_human, is a fixed 500 frames per episode, so take T = 500 as the reference. There, k=100 sits at k/T = 0.20, which was the best point, and the dip started at k=200, or k/T = 0.40. An 8 second SO-100 episode at 30 fps is 240 frames, so lerobot's default k=100 lands at k/T = 0.42. In the paper's own terms that is past the peak and into the region where performance fell off. This mapping is arithmetic on their numbers, not a claim the paper makes, but it explains a lot of disappointing first ACT runs. If your episodes are 8 seconds, try chunk_size in the 30 to 50 range before you blame the data.

The platform exposes chunkSize and nActionSteps directly in the ACT on SO-100 training form, both defaulting to 100, so this is a field you change rather than a code edit. The other four policies do not expose it, which is worth knowing before you pick a model on the policies page.

What happens in the last k-1 frames of every episode

Ask a dataset for the frame 30 steps from the end with a chunk size of 100 and you are asking for 70 actions that do not exist. LeRobot does not error and it does not skip the sample. It clamps the query indices to the episode boundary and records a mask.

python
# src/lerobot/datasets/dataset_reader.py, DatasetReader._get_query_indices
ep_start = ep["dataset_from_index"]
ep_end = ep["dataset_to_index"]
query_indices = {
    key: [max(ep_start, min(ep_end - 1, abs_idx + delta)) for delta in delta_idx]
    for key, delta_idx in self.delta_indices.items()
}
padding = {
    f"{key}_is_pad": torch.BoolTensor(
        [(abs_idx + delta < ep_start) | (abs_idx + delta >= ep_end) for delta in delta_idx]
    )
    for key, delta_idx in self.delta_indices.items()
}
The clamp repeats the final action; the mask flags every repeated step as padding.

The mask is then honoured by the loss, so those repeats do not teach the policy to freeze at the end of a chunk. ACT divides by the count of valid steps rather than the full tensor size, which means a heavily padded batch is not down-weighted either. It simply carries less signal.

python
# src/lerobot/policies/act/modeling_act.py, ACTPolicy.forward
abs_err = F.l1_loss(batch[ACTION], actions_hat, reduction="none")
valid_mask = ~batch["action_is_pad"].unsqueeze(-1)
num_valid = valid_mask.sum() * abs_err.shape[-1]
l1_loss = (abs_err * valid_mask).sum() / num_valid.clamp_min(1)
Padded steps are excluded from the L1 term and from the denominator.

So the honest version of the story is not that short episodes poison the loss. It is that they spend a large share of your recording effort on targets that get thrown away. Exactly k-1 frames per episode produce a partly padded window, and the total share of masked action steps is (k-1)/2T.

Episode at 30 fpsPolicyFrames with a padded targetShare of all action steps masked out
8 s (240 frames)ACT, k=10099 of 240, 41.2 %20.6 %
8 s (240 frames)SmolVLA / Pi0.5, k=5049 of 240, 20.4 %10.2 %
8 s (240 frames)GR00T, k=1615 of 240, 6.2 %3.1 %
13 s (390 frames)ACT, k=10099 of 390, 25.4 %12.7 %
30 s (900 frames)ACT, k=10099 of 900, 11.0 %5.5 %
60 s (1800 frames)ACT, k=10099 of 1800, 5.5 %2.8 %
Why GR00T tolerates short episodes better

With a 16 step horizon, GR00T only loses 15 frames per episode to padding no matter how short the episode is. That is 6 percent of an 8 second episode against ACT's 41 percent. If you have already recorded short episodes and cannot re-record, that is a real argument for GR00T N1.7 over ACT, independent of model quality. It is also why the platform sets the GR00T minimum at 50 episodes and the SmolVLA minimum at 30.

Short and clean against long and messy

The angle people usually want settled: is it better to record thirty crisp eight-second picks or ten sixty-second sequences where you fumble twice and recover? Both produce a working dataset. They fail in different ways, and the failure modes are not symmetric. Short episodes fail quietly, by handing the optimiser less signal than you think you gave it. Long episodes fail loudly, by teaching contradictory actions for visually similar states.

There is also a bookkeeping difference that people underestimate. Training length is counted in gradient steps over frames, not over episodes. Fifty episodes of 8 seconds at 30 fps is about 12,000 frames; fifty of 60 seconds is 90,000. At the ACT defaults this platform sends, batch size 8 over 100,000 steps, the short dataset is seen roughly 67 times and the long one roughly 9 times. Neither number is automatically wrong, but they are very different regimes, and if you shorten your episodes without adjusting anything you have quietly changed how many epochs your training steps add up to.

Short, clean episodes
Advantages
  • More episodes per hour at the arm, so more independent starting configurations for the same effort.
  • Fewer chances for the scene to drift away from its reset state mid-episode.
  • Each episode shows one solution to one problem, which is easier for a single-observation policy to fit.
  • Failed attempts are cheap to discard: you lose 8 seconds, not a minute.
  • Matches what the reference datasets actually contain, so published hyperparameters transfer better.
Trade-offs
  • A large fraction of frames sit near the episode end, where the action window is padded and partly masked.
  • Chunk sizes tuned on longer episodes, including ACT's default 100, are too large relative to T.
  • The policy never sees recovery behaviour, because you re-record instead of recovering.
  • Total frame count drops fast: 50 episodes of 8 seconds is 12,000 frames, against 90,000 for 60 second episodes.
  • Tasks with a genuine multi-stage structure get cut into pieces that no longer make sense alone.
Long episodes with recoveries in them
Advantages
  • Padding becomes negligible: 5.5 percent of frames at ACT's default chunk size on a 60 second episode.
  • More frames per episode, so a smaller episode count still fills a training set.
  • Recovery from a bad grasp is in the data, which is the only way a chunked policy learns to recover.
  • Multi-stage tasks stay intact, so the language instruction still describes the whole episode.
Trade-offs
  • Long episodes contain several valid solutions to the same visual state, and a policy with n_obs_steps=1 has no way to tell which one it is in.
  • Operator fatigue shows up as inconsistent speed, which turns into inconsistent action magnitudes.
  • Scene drift accumulates: by second 45 the workspace no longer matches the reset state you calibrated for.
  • One bad episode costs a minute of recording plus a minute of reset.
  • Dead time at the end, where the arm holds a finished pose, teaches the policy to stop moving.

What the published datasets actually chose

It helps to see how much the accepted answer varies. These figures come from the papers and project pages, fetched and checked on 24 August 2026. The seconds-per-episode column is arithmetic on the totals the sources state, not a number the sources publish directly.

DatasetScaleControl rateSeconds per episode (derived)
ALOHA real tasks (ACT paper)50 demos per task, 100 for Thread Velcro50 Hz8 to 14, stated directly; 400 to 700 timesteps
BridgeData V260,096 trajectories, 24 environments, 13 skills5 Hz7.6 (38 timesteps / 5 Hz)
DROID76,000 trajectories, 350 hours, 564 scenes, 84 tasksnot stated on the project page16.6 (350 h / 76k)
SmolVLA community pretraining set481 datasets, 22.9K episodes, 10.6M framesmixed462.9 frames per episode; 15.4 at 30 fps

Two things stand out. The first is that the spread is narrow given how differently these datasets were built: a lab with two arms and a fixed protocol, a distributed collection effort across three continents, and a pile of hobbyist uploads all landed between about 8 and 17 seconds. The second is that control rate and episode length move together. BridgeData V2 runs at 5 Hz and averages 38 steps; ALOHA runs at 50 Hz and averages 400 to 700. In seconds those are close. In frames they differ by more than an order of magnitude, and it is the frame count that your chunk size has to live inside.

Nothing published sits at a minute. The DROID collection and BridgeData V2 went about it very differently and still landed in the same band. If you want the wider view on what makes a demonstration usable at all, the data collection guide covers the parts that are not about length.

The AY-Robots glossary entry for the LeRobot dataset format, explaining episodes, frames, camera streams and the meta directory.
The LeRobot dataset glossary entry. Episode boundaries, frame counts and the meta files are where every number in this article comes from.

Picking an episode length for your task

  1. 1
    Time the task by hand first

    Do the pick with the leader arm five times and note the wall clock. That median, plus about half again for the attempts that go badly, is your episode_time_s ceiling. Do not start from 60.

    bash
    # 8 s median, 15 s ceiling
    --dataset.episode_time_s=15 --dataset.reset_time_s=10
  2. 2
    Convert to frames and check k/T

    Multiply by fps. Then divide your policy's chunk size by that number. Aim for k/T at or below roughly 0.2, which is where the ACT ablation peaked, and for at least five decision points per episode.

    python
    T = 8 * 30            # 240 frames
    for k in (100, 50, 40, 30, 16):
        print(k, round(k/T, 3), round(T/k, 1))
    
    # 100 0.417 2.4
    # 50  0.208 4.8
    # 40  0.167 6.0
    # 30  0.125 8.0
    # 16  0.067 15.0
  3. 3
    Record a pilot of five episodes and read the metadata back

    Do not record fifty before you check. Five is enough to see whether your real average matches your plan.

    bash
    lerobot-record \
      --robot.type=so100_follower --robot.port=/dev/tty.usbmodem58760431541 --robot.id=my_follower \
      --teleop.type=so100_leader --teleop.port=/dev/tty.usbmodem58760431551 --teleop.id=my_leader \
      --dataset.repo_id=${HF_USER}/pilot --dataset.single_task="Pick up the red cube" \
      --dataset.num_episodes=5 --dataset.episode_time_s=15 --dataset.reset_time_s=10
    
    lerobot-edit-dataset --repo_id ${HF_USER}/pilot --operation.type info
  4. 4
    Throw away the episodes that ran long

    An episode that hit the ceiling almost always means the attempt went wrong. Delete those rather than keeping them for volume. This writes a new dataset and leaves the original alone.

    bash
    lerobot-edit-dataset \
      --repo_id ${HF_USER}/pick-cube-short \
      --new_repo_id ${HF_USER}/pick-cube-clean \
      --operation.type delete_episodes \
      --operation.episode_indices "[3, 11, 27]"
  5. 5
    Set chunk_size when you train, not after

    For ACT the chunk size is a policy config field, so it is fixed at training time. lerobot's own defaults are batch_size 8, steps 100000 and seed 1000, which match the ACT defaults this platform sends.

    bash
    lerobot-train \
      --dataset.repo_id=${HF_USER}/pick-cube-clean \
      --policy.type=act \
      --policy.chunk_size=40 \
      --policy.n_action_steps=20 \
      --batch_size=8 \
      --steps=100000 \
      --seed=1000 \
      --output_dir=outputs/train/act_pick_cube

Two ways to get this right

Install lerobot with the recording extras, wire up the leader and follower, and drive the whole loop from your own terminal. You control every flag, including the two timers, and you can inspect the dataset metadata directly. You also own the GPU problem.

bash
git clone https://github.com/huggingface/lerobot.git
cd lerobot
pip install -e ".[core_scripts]"

lerobot-find-port          # identify the two USB serial ports
lerobot-calibrate --robot.type=so100_follower --robot.port=/dev/tty.usbmodem... --robot.id=my_follower

# then lerobot-record with your own episode_time_s, then lerobot-train
  • Every DatasetRecordConfig field is a CLI flag, so episode_time_s and reset_time_s are one keystroke each.
  • chunk_size and n_action_steps are reachable for every policy, not just ACT.
  • You need a 24 GB card locally for ACT or SmolVLA, and an A100 or H100 class card for GR00T or Pi0.5.
  • Dataset format conversion is on you: a v3.0 dataset has to be converted down to v2.1 before GR00T will load it.

Where episode length stops being the problem

Two limits are worth stating plainly, because both get blamed on the data when they are not the data.

The first is inference latency. At 30 fps you need a new action every 33.3 ms. This platform lists ACT at 20 ms per action step, GR00T N1.7 at 152 ms, GR00T N1.5 at 165 ms, SmolVLA at 245 ms and Pi0.5 at 485 ms. Only ACT fits inside a 30 fps budget. Everything else has to run its chunk open loop while the next one computes, which is a large part of why chunking exists at all. No episode length fixes that, and putting the inference across a public internet hop makes it worse. Remote inference works for slow pick-and-place and not for fast reactive motion.

The trap that eats a day

You record beautiful 8 second episodes, train ACT at the default chunk_size of 100, and the arm moves to roughly the right place and then stops. That looks like a broken checkpoint. It usually is not. With k/T at 0.42 the policy is committing to 3.33 seconds of a task that takes 8, from one frame, and 41 percent of your training frames had padded targets. Before you re-record anything, retrain with chunk_size=40 and n_action_steps=20 and compare. If it still stalls, then work through loss falls but the policy does nothing and policy freezes mid-motion.

The second is that episode length cannot buy you memory. Every policy here uses n_obs_steps=1. If your task genuinely requires knowing what happened ten seconds ago, a longer episode does not give the policy that information, it just gives it more ambiguous frames. That is an architecture question, and the VLA overview and the model arena are better places to start on it than a recording flag. For the hardware side of a first dataset, the SO-100 setup guide covers calibration and camera placement, which matter more than episode length does.

The AY-Robots training matrix at /train with five policies as rows and four robot arms as columns, each cell linking to a specific training guide.
The training matrix at /train. Each cell is a guide for one model and one arm, with the defaults that model is actually launched with.

Record episodes at the length you meant to

The AY-Robots desktop client records LeRobot-format datasets straight from a teleoperation session, with the episode and reset timers in front of you instead of buried in a CLI flag.

Get the desktop client

A short checklist before you record fifty episodes

  1. Time the task by hand. Set episode_time_s to about 1.5x the median, not to 60.
  2. Set reset_time_s to what the reset actually takes. It is unrecorded time and it doubles your session length.
  3. Compute k/T for the policy you intend to train. Aim at or below 0.2. See the dataset docs for the format side.
  4. Record five, read the metadata back, then commit to fifty.
  5. Tap right arrow the moment the task is done. Dead frames at the end teach the arm to stop.
  6. Delete the episodes that hit the ceiling before training. The training docs cover what happens after that.
What is the default episode length in LeRobot?

episode_time_s defaults to 60 seconds and fps defaults to 30, so an episode caps at 1800 frames. reset_time_s is also 60 and num_episodes is 50. These are the defaults in src/lerobot/configs/dataset.py on the main branch as of 24 August 2026 (version 0.6.2 in development; 0.6.1 was the last PyPI release, 3 August 2026). Treat 60 seconds as a timeout rather than a target: real reference datasets average 8 to 13 seconds per episode.

How long should an SO-100 episode be?

Long enough to complete the task once plus a margin for attempts that go wrong. For a single pick and place that is usually 8 to 15 seconds at 30 fps, which is 240 to 450 frames. The LeRobot reference sets lerobot/svla_so101_pickplace and lerobot/svla_so100_pickplace average 8.0 and 13.1 seconds respectively. Published datasets cluster in the same band: BridgeData V2 at 7.6 seconds and DROID at about 16.6 seconds derived from 350 hours over 76,000 trajectories.

Does episode length change the chunk size I should use?

Yes, and this is the interaction that matters most. What predicts trouble is k/T, chunk size over episode length in frames. The ACT ablation peaked around k=100 on roughly 500 frame ALOHA episodes, which is k/T = 0.2, and fell off at k=200 and k=400. lerobot's default chunk_size of 100 on a 240 frame SO-100 episode is k/T = 0.42, past that peak. On short episodes, try 30 to 50 instead.

What happens when an action chunk runs past the end of an episode?

LeRobot clamps the query indices to the episode boundary, which repeats the final frame, and sets an action_is_pad mask for every repeated step. ACT then excludes those steps from the L1 loss and from its denominator, so they do not teach the policy to freeze. The cost is lost supervision rather than corrupted supervision: exactly k-1 frames per episode carry a partly padded target, and (k-1)/2T of all action steps are masked out.

Can I split a long episode into shorter ones after recording?

No, not with the shipped tooling. lerobot-edit-dataset supports delete_episodes, split, merge, remove_feature, modify_tasks, convert_image_to_video, reencode_videos, recompute_stats and info. The split operation divides a dataset into train and validation sets by episode, not within an episode. There is no trim or cut. Get the length right at recording time.

Are more short episodes better than fewer long ones?

For a single-stage task on this class of arm, yes, up to a point. More episodes means more independent starting configurations, and each one is cheap to discard when it goes wrong. The cost is padding: a short episode spends a larger share of its frames near the boundary where the action window is masked, and that share scales with your chunk size. If you must keep short episodes and cannot lower chunk_size, a policy with a 16 step horizon like GR00T loses far less to padding than ACT at 100.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started