Skip to content
advancedvideo

LeRobot - Robot Episode Annotation

Annotate teleoperated robot demonstrations: synchronized camera streams and per-frame arm signals on one timeline, with phase segmentation, outcome and failure cause, and a dense progress-reward curve.

About this dataset

LeRobot is an open-source library for end-to-end robot learning from Hugging Face, presented by Remi Cadene and colleagues in "LeRobot: An Open-Source Library for End-to-End Robot Learning". It spans the robot learning stack, from low-level middleware for motor control to large-scale dataset collection, storage and streaming, with a focus on real-world robotics and accessible hardware.

A LeRobot episode is a recorded demonstration: one or more camera streams alongside the robot's own signals, sampled per frame. What the library does not provide is the human judgement about those episodes, which is what a policy trained on them ultimately depends on: where one sub-goal ends and the next begins, whether the demonstration actually succeeded, and how close to done the robot was at each moment.

Potato puts N synchronized video streams and M time-series lanes on a single timeline and collects three layers over them. Phase segmentation marks where the robot's sub-goal changes. Outcome records success, partial or failure, with a failure-cause taxonomy. Dense progress reward is drawn as a curve across the episode. The layers cost very different amounts of annotator time, so most projects enable a subset.

Three outcomes rather than two is a deliberate choice. "Partial" is the modal result in real robot data, and collapsing it into success or failure destroys the signal that makes the dataset worth annotating in the first place. The config also collects a relabelled instruction, so an episode that failed at the task it was recorded for can be reused as a successful demonstration of the task it actually performed.

The time-series lanes are downsampled with a min/max-preserving reduction, so a one-frame force spike survives being drawn into a 300-pixel-wide lane rather than being averaged away. This matters in practice: a missed grasp is usually visible in the gripper and wrist-force traces several frames before it becomes obvious in the video, which is why the lanes sit beside the video rather than behind a tab.

Potato reads LeRobot v2 through pyarrow, RoboMimic and ALOHA HDF5 through h5py, and RLDS/TFDS through tensorflow_datasets, all via the same source field, and exports a per-frame JSONL sidecar so a read-only public dataset does not have to be rewritten. Each episode is reviewed twice. Phase-boundary agreement is reported as temporal IoU, outcome agreement as Krippendorff's alpha, and reward-curve agreement as ICC plus Pearson, each with coverage stated alongside, because a correlation computed over 5% of a timeline says nothing about the other 95%.

Episode layers
Phases, outcome, dense reward
Outcomes
success / partial / failure
Import formats
LeRobot v2, RLDS/TFDS, HDF5 (RoboMimic, ALOHA)
Agreement
Temporal IoU, alpha, ICC + Pearson
Library
Hugging Face LeRobot
00:03:42 / 00:12:15Action:walkingtalkingsittingMark temporal segments with action labels

Configuration Fileconfig.yaml

This Potato config reproduces the annotation task. Save it as config.yaml and run potato start config.yaml to try it.

yaml
# yaml-language-server: $schema=https://potatoannotator.readthedocs.io/en/latest/schemas/potato-config.schema.json
#
# LeRobot — Robot Episode Annotation
#
# Paper:   Cadene et al., "LeRobot: An Open-Source Library for End-to-End
#          Robot Learning", 2026 — https://arxiv.org/abs/2602.22818
# Library: https://github.com/huggingface/lerobot
#
# A LeRobot episode is a teleoperated demonstration: one or more camera streams
# recorded alongside the arm's own signals, sampled per frame. This config puts
# the streams and the signals on one timeline and asks a human what the robot
# was doing, when, and whether it worked.
#
# Reproduction notes
# ------------------
# `source_field` names the item field holding the episode. Potato reads
# LeRobot v2 (pyarrow), RoboMimic and ALOHA HDF5 (h5py), and RLDS/TFDS
# (tensorflow_datasets) through the same field, plus its own manifest format
# which needs no extra dependency.
#
# Three layers are collected here. They cost very different amounts of
# annotator time -- phases are seconds per episode, a dense reward curve is
# minutes -- so most projects want a subset rather than all of them.
#
# The phase taxonomy below is for manipulation. Navigation or locomotion needs
# an entirely different set, which is why it is a config value and not a fixed
# list.
#
# Three outcomes, not two. "Partial" is the modal result in real robot data and
# collapsing it into success or failure destroys the signal that makes the
# dataset worth annotating.
#
# The series lanes are downsampled with a min/max-preserving reduction, so a
# one-frame force spike survives the trip to a 300-pixel-wide lane instead of
# being averaged away. That matters: a failed grasp is usually visible in the
# gripper and force lanes well before it is obvious in the video.
#
# Two annotators per episode. Potato reports phase-boundary agreement as
# temporal IoU, outcome agreement as alpha, and reward-curve agreement as ICC
# plus Pearson -- each with coverage stated alongside, because a correlation
# computed over 5% of a timeline says nothing about the other 95%.

port: 8000
annotation_task_name: "LeRobot - Robot Episode Annotation"
task_dir: "."
media_directory: media
output_annotation_dir: "annotation_output/"
output_annotation_format: "json"

data_files:
  - sample-data.json

item_properties:
  id_key: "id"
  text_key: "instruction"

user_config:
  allow_all_users: true
  users: []

num_annotators_per_item:
  default: 2

annotation_schemes:
  - annotation_type: episode_annotation
    name: episode_review
    description: "Mark the phases, judge the outcome, and draw the progress."
    source_field: episode
    layers:
      - phases
      - outcome
      - reward
    phases:
      - name: reach
        color: "#4ECDC4"
        key_value: "1"
      - name: grasp
        color: "#FFD93D"
        key_value: "2"
      - name: transport
        color: "#6C8AE4"
        key_value: "3"
      - name: place
        color: "#95E1A3"
        key_value: "4"
      - name: retract
        color: "#C9A0DC"
        key_value: "5"
    outcomes:
      - success
      - partial
      - failure
    failure_causes:
      - missed grasp
      - object slipped
      - collision
      - wrong object
      - wrong placement
      - timeout / stalled
    reward_range: [0.0, 1.0]
    series_shown:
      - gripper
      - wrist_force
      - shoulder_lift
      - elbow

  - annotation_type: text
    name: relabelled_instruction
    description: "Write the instruction this episode actually demonstrates, if it differs from the one given."
    placeholder: "e.g. put the red block in the left bin"

  - annotation_type: text
    name: notes
    description: "Anything the phases and the outcome do not capture? (optional)"

annotation_instructions: |
  Each item is one robot demonstration: the camera views above, the arm's own
  signals as lanes below, and one timeline running through all of them.

  1. Segment the episode into phases. Drag a boundary where the robot's
     sub-goal changes, not where the video happens to cut. Number keys 1-5
     select phases directly.
  2. Judge the outcome. Use "partial" when the task was advanced but not
     completed; it is a real category, not a hedge.
  3. If it failed or only partly succeeded, pick the cause.
  4. Draw the dense progress reward: how close to done was the robot at each
     point? This is the slowest layer, so it may be disabled for your project.

  Watch the lanes, not only the video. A missed grasp is usually visible in the
  gripper and wrist force traces several frames before it becomes obvious on
  camera.

  If the episode demonstrates something other than the instruction it was
  recorded for, write what it actually shows. Hindsight relabelling turns a
  failed demonstration into a usable one for a different instruction.

Sample Datasample-data.json

json
[
  {
    "id": "lerobot_001",
    "episode": "episodes/pick_place_block_001.parquet",
    "instruction": "Put the red block in the left bin.",
    "task": "pick_place_block",
    "recorded_outcome": "success",
    "context": ""
  },
  {
    "id": "lerobot_002",
    "episode": "episodes/pick_place_block_002.parquet",
    "instruction": "Put the red block in the left bin.",
    "task": "pick_place_block",
    "recorded_outcome": "failure",
    "context": "gripper closes on nothing; visible in the force lane first"
  }
]

// ... and 8 more items

Get This Design

View on GitHub

Clone or download from the repository

Quick start:

git clone https://github.com/davidjurgens/potato-showcase.git
cd potato-showcase/multimodal/lerobot-episode-annotation
potato start config.yaml

Dataset & paper

Cadene et al., 2026

Citation (BibTeX)

bibtex
@article{cadene2026lerobot,
    title = {LeRobot: An Open-Source Library for End-to-End Robot Learning},
    author = {Cadene, Remi and Aliberts, Simon and Capuano, Francesco and Aractingi, Michel and Zouitine, Adil and Kooijmans, Pepijn and Choghari, Jade and Russi, Martino and Pascal, Caroline and Palma, Steven and Shukor, Mustafa and Moss, Jess and Soare, Alexander and Aubakirova, Dana and Lhoest, Quentin and Gallouédec, Quentin and Wolf, Thomas},
    journal = {arXiv:2602.22818},
    year = {2026}
}

Details

Annotation Types

episode_annotationtext

Domain

RoboticsMultimodal

Use Cases

Robot Episode AnnotationPhase SegmentationReward LabellingVideo Timeline Annotation

Tags

lerobotroboticsepisode-annotationimitation-learningteleoperationrldshdf5time-seriesembodied

Found an issue or want to improve this design?

Open an Issue