The AY-Robots public dataset directory showing LeRobot-format robot manipulation datasets available for training.
lerobot datasethugging face hubdataset carddata licensingreproducibilityrobot data

Publishing a Robot Dataset on Hugging Face: Repo, Card, Licence

AY-Robots ResearchAugust 23, 202621 min read

How to publish a LeRobot dataset properly: what is really in the repo, the dataset card fields that decide reproducibility, licence choice, and the version tag that moves under you.

What you need to know

  • A published dataset is a Hugging Face repo with three things: the data, a meta/ folder the loader trusts, and a README.md whose YAML block carries the licence and tags.
  • lerobot-record uploads for you. push_to_hub() defaults to license="apache-2.0" and tags the commit with the codebase version, which in lerobot 0.6.2 is the string v3.0.
  • That version tag is not cosmetic. LeRobotDataset() resolves revision to CODEBASE_VERSION by default, so it fetches the v3.0 tag rather than main. Push again and the tag moves.
  • The auto-generated card is a stub: Homepage, Paper and Citation all read [More Information Needed]. Everything that makes a run reproducible is the part you write yourself.
  • Licence is a choice with consequences. apache-2.0 is the lerobot default, cc-by-4.0 is the common data choice, cc-by-nc-4.0 quietly excludes most companies from fine-tuning on your data.
  • A licence is not consent. If a face, a colleague or a stranger's living room is in frame, no SPDX identifier fixes that. Reframe or do not publish.
  • GR00T will not read a v3.0 dataset. It wants LeRobot v2 plus a meta/modality.json that the standard format does not contain.

Published is not the same as uploaded

Uploading a LeRobot dataset takes one flag. Publishing one takes an afternoon. The difference shows up three months later, when somebody else, or you, tries to reproduce the run and cannot answer basic questions: which arm, which cameras, which firmware, how many episodes were thrown away and why, and whether the number in the card counts the deleted ones.

Most public robot datasets are uploads. They have data, they load, and the card says This dataset was created using LeRobot and nothing else. That is enough to train on and not enough to trust. This article is about the gap: what the repo actually contains, what the card has to say for a stranger to repeat your run, and which licence choice you are making without noticing. It assumes you already have episodes on disk; if you do not, start with recording your first dataset and with what makes VLA training data good in the first place.

Which versions this describes

Checked on 23 August 2026 against lerobot at main, where pyproject.toml reads version = "0.6.2" and lerobot_dataset.py imports CODEBASE_VERSION, currently the string "v3.0", from the dataset metadata module. The newest PyPI release that day is 0.6.1, so main is a version ahead of pip install lerobot. Hub docs were read the same day. Check flag names against --help before pasting anything below.

What is actually inside the repo

A LeRobot dataset repo is not a folder of videos. It is a schema plus shards plus a metadata layer that reconstructs episode boundaries, as the dataset documentation describes. In v3.0 the episode is no longer a file; it is a range of rows and a byte offset recorded in metadata, which is why deleting a file by hand corrupts the dataset in a way that is hard to see.

PathWhat it holdsNotes
meta/info.jsonSchema: features, dtypes, shapes, fps, robot_type, total_episodes, total_frames, chunks_size, and the path templates for data and video shardsThe one file a reader should open first
meta/stats.jsonGlobal mean, std, min and max per feature, used for normalisationExposed in Python as dataset.meta.stats
meta/tasks.parquetNatural-language task strings mapped to integer idsv3.0 default. v2.1 used meta/tasks.jsonl, still referenced in lerobot as LEGACY_TASKS_PATH
meta/episodes/Per-episode records: length, task, byte and frame offsets, stored as chunked Parquetv2.1 kept this as meta/episodes.jsonl plus meta/episodes_stats.jsonl
data/chunk-XXX/file-YYY.parquetFrame-by-frame tabular data: state, action, timestamps, indicesMany episodes per file
videos/{video_key}/chunk-XXX/file-YYY.mp4Camera streams, one directory per camera keyMany episodes per file
README.mdThe dataset card: YAML metadata block plus proseThe only file most humans will read

The defaults in src/lerobot/datasets/utils.py are worth knowing because they determine how your repo looks to a downloader: DEFAULT_CHUNK_SIZE = 1000 files per chunk directory, DEFAULT_DATA_FILE_SIZE_IN_MB = 100 and DEFAULT_VIDEO_FILE_SIZE_IN_MB = 200. Those are the values used when a dataset is created. They are not a contract: every dataset writes its own numbers into info.json, so a repo built under an older default keeps that older default forever.

Read the numbers off the dataset, not off the constants

lerobot/svla_so101_pickplace, one of the reference SO-arm datasets, has a meta/info.json reading total_episodes: 50, total_frames: 11939, fps: 30 and chunks_size: 1000. Its data_files_size_in_mb is 100, matching the constant, but its video_files_size_in_mb is 500 against a constant of 200. Same format version, different shard sizing: read shard sizes from info.json, never from the source.

text
my-user/so100-cube-to-bowl/
  README.md                          <- the card, YAML + prose
  meta/
    info.json                        <- codebase_version, fps, robot_type, features
    stats.json                       <- normalisation statistics
    tasks.parquet                    <- task strings -> ids  (v2.1: tasks.jsonl)
    episodes/chunk-000/file-000.parquet
  data/
    chunk-000/file-000.parquet       <- many episodes per file in v3.0
  videos/
    observation.images.front/chunk-000/file-000.mp4
    observation.images.wrist/chunk-000/file-000.mp4
The v3.0 layout. In v2.1 the same dataset would have one parquet and one mp4 per episode per camera.
Do not hand-edit the repo

Because episode boundaries live in meta/episodes/ and not in filenames, deleting an mp4 or a parquet through the Hub web UI leaves a dataset that still loads and quietly returns garbage for the affected indices. Use lerobot-edit-dataset --operation.type=delete_episodes --operation.episode_indices "[3, 17]", which writes a new dataset with the metadata rebuilt, and push that.

Getting it onto the Hub

The path most people take is that lerobot-record uploads at the end of the session. That is fine, and it is also why so many half-finished datasets exist: the upload happens before you have looked at the data. The sequence below separates recording from publishing on purpose.

  1. 1
    Authenticate with a write token

    Create the token at Settings, Access Tokens on huggingface.co with write access. Never paste it into a notebook you will commit.

    bash
    hf auth login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
    
    HF_USER=$(NO_COLOR=1 hf auth whoami | awk -F': *' 'NR==1 {print $2}')
    echo $HF_USER
  2. 2
    Record without pushing

    Add --dataset.push_to_hub=False so nothing leaves the machine yet. Defaults you are accepting silently: --dataset.num_episodes=50, --dataset.episode_time_s=60, --dataset.reset_time_s=60. The dataset lands in ~/.cache/huggingface/lerobot/{repo-id}.

    bash
    lerobot-record \
      --robot.type=so100_follower \
      --robot.port=/dev/ttyACM0 \
      --robot.id=my_follower_arm \
      --robot.cameras="{ front: {type: opencv, index_or_path: 1, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30} }" \
      --teleop.type=so100_leader \
      --teleop.port=/dev/ttyACM1 \
      --teleop.id=my_leader_arm \
      --dataset.repo_id=${HF_USER}/so100-cube-to-bowl \
      --dataset.num_episodes=50 \
      --dataset.single_task="Pick the yellow cube and put it in the bowl" \
      --dataset.push_to_hub=False \
      --display_data=true
  3. 3
    Look at every episode before you publish

    Load it locally and read the metadata back. If total_episodes does not match what you think you recorded, stop here.

    bash
    lerobot-edit-dataset \
      --repo_id=${HF_USER}/so100-cube-to-bowl \
      --operation.type=info \
      --operation.show_features=true
  4. 4
    Drop the bad episodes, then check what happened to the statistics

    delete_episodes writes a new dataset: it re-indexes episodes, rebuilds meta/episodes/, and re-aggregates meta/stats.json from the per-episode statistics of the episodes it kept. You do not need to recompute stats to get a consistent dataset. recompute_stats is the stronger, optional operation that rebuilds stats.json from scratch by iterating every frame - with the default output trap described below.

    bash
    lerobot-edit-dataset \
      --repo_id=${HF_USER}/so100-cube-to-bowl \
      --operation.type=delete_episodes \
      --operation.episode_indices "[7, 23, 41]" \
      --new_repo_id=${HF_USER}/so100-cube-to-bowl-clean
    
    # optional, and note the two flags: without them this writes a THIRD dataset
    lerobot-edit-dataset \
      --repo_id=${HF_USER}/so100-cube-to-bowl-clean \
      --new_repo_id=${HF_USER}/so100-cube-to-bowl-clean \
      --operation.type=recompute_stats \
      --operation.overwrite true
  5. 5
    Push with the card fields filled in

    This is the step that turns an upload into a publication. push_to_hub() forwards unknown keyword arguments into the card template, so dataset_description, url, paper and citation_bibtex all land in the README instead of staying as [More Information Needed].

    python
    from lerobot.datasets import LeRobotDataset
    
    ds = LeRobotDataset("my-user/so100-cube-to-bowl-clean")
    
    ds.push_to_hub(
        tags=["so100", "pick-and-place", "teleoperation"],
        license="cc-by-4.0",          # default is "apache-2.0"
        private=False,
        push_videos=True,
        # everything below is forwarded into card_template.md
        dataset_description=(
            "47 teleoperated episodes of an SO-100 follower arm picking a 25 mm "
            "yellow cube from a 30x40 cm mat and dropping it into a white bowl. "
            "Two cameras at 640x480/30 fps: a fixed front view and a wrist view."
        ),
        url="https://github.com/<your-name>/so100-cube-to-bowl",
        citation_bibtex=(
            "@misc{so100cube2026,\n"
            "  title  = {SO-100 cube-to-bowl teleoperation dataset},\n"
            "  author = {Your Name},\n"
            "  year   = {2026},\n"
            "  url    = {https://huggingface.co/datasets/<your-name>/so100-cube-to-bowl-clean}\n"
            "}"
        ),
    )
recompute_stats does not edit your dataset by default

With only --repo_id and --operation.type=recompute_stats, lerobot-edit-dataset writes to a new dataset called {repo_id}_recomputed_stats and leaves the original untouched. People run it, see no error, push the original, and ship stale statistics. For an in-place update pass --new_repo_id equal to --repo_id and --operation.overwrite true. reencode_videos has the same habit, landing in {repo_id}_reencoded.

The version tag moves under you

push_to_hub() ends with delete_tag then create_tag for CODEBASE_VERSION, currently the literal string v3.0. And LeRobotDataset.__init__ sets self.revision = revision if revision else CODEBASE_VERSION. So the loader pulls the v3.0 git tag, not main, and every push silently re-points that tag at the newest commit. Anyone who cited your dataset last month is now getting different data under the same name. If you want a citable snapshot, create your own immutable tag: hf repos tag create my-user/so100-cube-to-bowl-clean 2026-08-23 --repo-type dataset, and put that tag in the card.

The AY-Robots recording tutorial page, walking through capturing a LeRobot dataset from a teleoperation session with an SO-100 arm.
The recording tutorial on AY-Robots. The publishing decisions in this article all start here, because task string, camera keys and fps are frozen at record time.

The dataset card

The Hub renders README.md as the dataset card. It has two halves that do completely different jobs: a YAML front matter block the Hub parses for filtering, licence display and viewer configuration, and everything below it, which only humans read. LeRobot generates both from src/lerobot/datasets/card_template.md, and the generated prose half is close to empty.

The YAML block

What lerobot writes for you is: tags: [LeRobot] plus anything you passed, task_categories: [robotics], a configs entry pointing the viewer at data/*/*.parquet, and the licence. Everything else is yours to add. This is the block that makes your dataset findable, so it is worth ten minutes.

yaml
---
license: cc-by-4.0
pretty_name: SO-100 cube-to-bowl (47 episodes, 2 cameras)
task_categories:
  - robotics
size_categories:
  - 10K<n<100K
language:
  - en
tags:
  - LeRobot
  - so100
  - so101
  - pick-and-place
  - teleoperation
  - imitation-learning
  - video
configs:
  - config_name: default
    data_files: data/*/*.parquet
---
A YAML block worth writing by hand. size_categories counts frames, not episodes. The video tag forces the modality shown on the Hub.
YAML keyWhy it matters hereWritten by lerobot?
licenseDisplayed on the dataset page and used by every downstream filter. Defaults to apache-2.0 in push_to_hubyes
tagsLeRobot is added automatically. Search huggingface.co/datasets?other=LeRobot to see the whole community setpartly
task_categoriesAlways set to robotics by the lerobot card helperyes
configsPoints the Data Studio viewer at your parquet shardsyes
pretty_nameThe human title. Put the episode count and camera count in itno
size_categoriesCoarse bucket, e.g. 10Kno
license_name and license_linkRequired when license: other, for example a custom research licenceno
gated, extra_gated_fields, extra_gated_promptTurn the repo into an access-request dataset with a custom formno

The half that decides whether anyone can reproduce you

The prose half of card_template.md has six substitution points, and only two get filled for you: license from the push_to_hub argument, and dataset_structure with a JSON dump of info.json. The other four are yours. dataset_description defaults to an empty string; url (rendered as Homepage), paper and citation_bibtex each default to the literal text [More Information Needed]. That triple is the signature of an unedited card, and you can see it on a large share of public LeRobot datasets. Fill them through push_to_hub, then add the sections below by editing README.md directly. Nothing enforces any of it, which is precisely why it does not happen.

  • Hardware. Arm and variant (SO-100, SO-101, Koch v1.1, LeKiwi), servo model, supply voltage, and whether the leader and follower are the same model. A policy trained on your data will be deployed on hardware with different backlash. The SO-100 setup guide lists the variables that differ between two nominally identical builds.
  • Cameras. One line per camera: key name, physical placement, resolution, fps, and lens. "observation.images.front, tripod 45 cm in front of the mat, 640x480, 30 fps" beats any photo.
  • Calibration. Whether the arms were freshly calibrated and against which reference pose. Joint values from an uncalibrated arm are not comparable to yours.
  • The task string, verbatim. It is embedded in the data and language-conditioned policies read it. LeRobot's own guidance is 25 to 50 characters and no names like task1 or demo2.
  • Scene and variation. Object, its size, the surface, lighting, how many distinct start positions, how many episodes per position. This is what tells a reader whether your 50 episodes cover one pose or ten.
  • What you removed. Episode indices deleted, and why. If you deleted 3 of 50, say 47 everywhere and say where the other 3 went.
  • Known defects. The camera that dropped frames in episodes 12 to 15. The gripper that stuck. Nobody will hold this against you; discovering it after a training run, they will.
  • A version tag you control, plus the date, so a citation resolves to fixed bytes.

If that list feels arbitrary, it is a compressed robot-specific version of the datasheet proposed in Datasheets for Datasets by Gebru and colleagues (arXiv 1803.09010, first posted 23 March 2018, revised through December 2021), which organises dataset documentation into motivation, composition, collection process, preprocessing, uses, distribution and maintenance. The robotics-specific part is that composition includes physical geometry: where the camera was, is part of the data.

Choosing a licence, and what it does not cover

Hugging Face requires a licence identifier from a fixed list before it will display one on the page. lerobot picks apache-2.0 unless you say otherwise, which is a software licence being applied to a video dataset. It works in practice, and it is not what most data publishers actually intend.

IdentifierWhat it allowsWho it fits
apache-2.0Commercial use, redistribution, modification, with attribution and a patent grantThe lerobot default. Fine if you want maximum reuse and do not mind a software licence on video
cc-by-4.0Any use including commercial, requires attributionThe usual choice for data you want cited
cc-by-sa-4.0Same, but derivatives must carry the same licenceUse when you want merged datasets to stay open
cc-by-nc-4.0Non-commercial onlyExcludes most companies from fine-tuning on your data. Choose deliberately, not by reflex
cc0-1.0Public domain dedication, no attribution requiredMaximum reuse, zero credit
odc-byOpen Data Commons Attribution, written for databases rather than creative worksLegally the cleanest fit for tabular robot logs
cdla-permissive-2.0Community Data License Agreement, permissiveCommon in industry data consortia
otherAnything else. Requires license_name and a LICENSE file or license_linkInstitutional or embargoed research releases
Public and permissive, or gated
Reasons to publish openly
  • Anyone can reproduce your training run, which is the only real check on a reported success rate
  • The LeRobot tag makes it discoverable to everyone browsing robot data on the Hub
  • Merging with other public datasets is legally trivial when the licences are compatible
  • Public storage on a free account is best-effort rather than capped, and the Hub's own condition for hosting a large dataset is that you are sharing it for community reuse
  • Someone else finding a defect in your data is a gift, not an embarrassment
Reasons to gate or restrict
  • Gating (gated plus extra_gated_fields) makes every downloader share username and email, and the dataset settings page gives you a user access report listing each request with its status and timestamp, which some institutions require
  • Private repos on a free account are capped at 100 GB, and the Data Studio viewer on private datasets needs PRO, Team or Enterprise
  • cc-by-nc-4.0 protects a commercialisation plan but also removes your data from most industrial benchmarking
  • Gating has two modes and both cost you something: automatic approval grants access as soon as someone hands over their details, manual approval means a human answers every request, and either way the extra click sits between your data and a casual reader
  • Restricting after the fact does not work: a public dataset has already been cloned
A licence is not consent

Robot manipulation footage is filmed in real rooms. Check every camera view for faces, name badges, screens with email open, whiteboards, letters on the desk, and windows showing a recognisable street. No licence identifier turns recorded people into licensed content, and in the EU the GDPR question is not answered by cc-by-4.0. Practical fix: point the fixed camera down at the workspace so the frame ends at the table edge, and check the wrist camera too, since it sweeps the whole room. If you find a face after publishing, delete the episode with lerobot-edit-dataset and republish; the old commit still exists in git history, so you also need to squash or delete the repo.

Two ways to get a dataset published

You own the arm, you install lerobot from source, and you drive the whole chain by hand. This is the path the commands above describe and it is the one that teaches you the format.

  1. Install lerobot from source. Dataset v3 landed in release 0.4.0 on 23 October 2025; the newest release is 0.6.1 and main is 0.6.2 as of 23 August 2026.
  2. Wire the leader and follower arms, find the ports with lerobot-find-port, find cameras with lerobot-find-cameras, calibrate with lerobot-calibrate.
  3. Record with --dataset.push_to_hub=False, review, delete bad episodes, recompute stats.
  4. Push with push_to_hub() and the card kwargs filled in, then edit README.md by hand for the hardware, camera and defect sections.
  5. Create your own immutable git tag so citations resolve to fixed bytes.
The part that eats the afternoon

Not the upload. Reviewing 50 episodes at 30 fps for a wrist camera that drifted or a gripper that failed to close is roughly an hour of watching video, and there is no shortcut that is honest.

Make it trainable, not just downloadable

A dataset that loads is not a dataset that trains. The five policies you are most likely to point at it disagree about which format version they accept and how many episodes they need before fine-tuning produces anything. ACT trains from scratch by imitation learning and SmolVLA needs the fewest episodes of the five. Say in the card which one you built for.

PolicyDataset formatMinimum episodesGPU tier
GR00T N1.7LeRobot v2.0 or v2.150A100 80 GB or H100 80 GB
GR00T N1.5LeRobot v2.0 or v2.150A100 80 GB or H100 80 GB
Pi0.5LeRobot v3.050A100 80 GB or H100 80 GB
SmolVLALeRobot v3.030RTX 4090 or any 24 GB card
ACTLeRobot v3.050RTX 4090 or any 24 GB card

That first column is the trap. GR00T N1.7 will not read a v3.0 dataset at all, and NVIDIA says so plainly: GR00T uses a flavour of the LeRobot v2 format because upstream datasets like DROID and LIBERO are published in v2. Publishing v3.0 is correct and current; it also means every GR00T user has to convert first. Publishing both, as two repos or two branches, is the friendly move, and the GR00T N1.7 on SO-100 guide assumes you have done it.

bash
# v2.1 -> v3.0, run from a lerobot checkout against a dataset on the Hub.
# This one converts, pushes to main and moves the v3.0 tag for you.
python src/lerobot/scripts/convert_dataset_v21_to_v30.py \
  --repo-id=my-user/so100-cube-to-bowl

# convert a local copy instead, without touching the Hub
python src/lerobot/scripts/convert_dataset_v21_to_v30.py \
  --repo-id=my-user/so100-cube-to-bowl \
  --root=/path/to/local/dataset \
  --push-to-hub=false

# v3.0 -> v2, from the Isaac-GR00T repo, in its own venv
cd scripts/lerobot_conversion
uv venv && source .venv/bin/activate
uv pip install -e . --verbose
python convert_v3_to_v2.py --repo-id my-user/so100-cube-to-bowl
The two conversions. The v2.1 to v3.0 script takes --repo-id with hyphens; lerobot-edit-dataset takes --repo_id with an underscore. Re-check total_episodes on the output either way.
GR00T needs a file that LeRobot never writes

Converting to v2 is not enough. Isaac-GR00T requires meta/modality.json, which the standard LeRobot format does not contain. The schema is {"state": {"<key>": {"start": int, "end": int}}, "action": {...}, "video": {"<new_key>": {"original_key": "..."}}, "annotation": {...}}: it slices the concatenated state and action arrays into named fields, renames video keys, and declares annotation channels. For a 6-DoF SO-100 you would typically split the arm joints from the gripper. Indices are zero-based and follow Python slicing. If you publish an SO-100 dataset you want people to fine-tune GR00T on, ship this file. It is about fifteen lines and it saves every downstream user an hour of guessing. See also the dataset-rejected-v3 failure page.

The AY-Robots glossary entry for the LeRobot dataset format, describing episodes, camera streams and joint states.
The LeRobot dataset format glossary entry on AY-Robots. The v2 and v3 split described above is the single most common reason a published dataset fails to load for the person who downloads it.

The check before you make it public

Hugging Face publishes conditions for hosting a large dataset, and the list starts with the card. They require a dataset card, require that you are sharing for community reuse rather than as private backup, require you to stay inside the repository limits, and ask for formats the ecosystem reads, naming Parquet and WebDataset. A LeRobot v3.0 repo meets the format condition through its Parquet shards, with the camera streams alongside as MP4. The limits that matter for robot data: fewer than 100k files per repo, fewer than 10k entries per folder, files under 200 GB. v3.0 sharding keeps you inside all three; v2.1, with one parquet and one mp4 per episode per camera, does not once you pass a few thousand episodes. The CLI and MCP server expose the same dataset operations if you would rather script this check.

  1. Open meta/info.json and read total_episodes, total_frames, fps and robot_type out loud. Do they match the card?
  2. Load the dataset fresh in a clean environment: LeRobotDataset("my-user/name"). If it needed a local cache to work, it is broken.
  3. Open the dataset in the visualize_dataset Space and scrub through at least the first, middle and last episode.
  4. Check the Data Studio viewer renders on the Hub page. If it does not, your configs YAML entry is wrong.
  5. Scan every camera view for faces, screens, documents and windows.
  6. Confirm the licence identifier in the YAML matches what you actually intend, not the apache-2.0 default.
  7. Create your own dated git tag and reference it in the card.
  8. Have somebody who was not in the room read the card and tell you what they still cannot determine.
The AY-Robots download page for the desktop client that records LeRobot-format datasets from a teleoperation session.
The desktop client on AY-Robots records episodes, camera streams and joint states into LeRobot format directly, which removes the schema-assembly step but not the documentation step.

One honest limit on all of this: a well-documented dataset does not make a policy work. The dataset card is upstream of the parts that break. A vision-language-action model fine-tuned on 50 clean episodes still fails when the camera moves 5 cm, and inference latency between 20 and 485 ms per action step decides whether the policy runs smoothly on the arm regardless of how good the data was. Publishing well is a courtesy to the next person, and a debugging aid for yourself. It is not a performance improvement.

Is the extra afternoon worth it

Writing a real card instead of accepting the generated one
What you get
  • You can reproduce your own run in six months, which is the case that actually happens
  • Failures become diagnosable: the card tells you whether the camera geometry changed
  • Other people's fine-tunes are comparable to yours, because the setup is written down
  • Filling dataset_description, url and citation_bibtex is one push_to_hub call, not manual editing
What it costs
  • Reviewing every episode is real time, roughly an hour per 50 episodes at 30 fps
  • Nothing enforces any of it, so the incentive is entirely internal
  • The v2 and v3 split means a genuinely useful publication is often two repos, not one
  • Republishing moves the v3.0 tag, so anyone pinned to it silently gets the new data

Record the dataset before you worry about publishing it

The AY-Robots desktop client records LeRobot-format datasets straight from a teleoperation session: episodes, camera streams and joint states in the right schema, so the only work left is the card, the licence and the push.

Get the desktop client

Questions people actually ask

Which licence should I put on a robot dataset?

If you want citations and broad reuse, cc-by-4.0. If you want the cleanest legal fit for tabular logs, odc-by. lerobot's push_to_hub defaults to apache-2.0, which is a software licence and works in practice but is probably not what you meant. Avoid cc-by-nc-4.0 unless you have a specific commercialisation reason, because it removes your data from most industrial use.

How many episodes should I record before publishing?

LeRobot's own recording guide suggests at least 50 episodes with 10 per object location. On AY-Robots the minimum episode counts per policy are 30 for SmolVLA and 50 for ACT, Pi0.5, GR00T N1.5 and GR00T N1.7. Publishing fewer is still useful if the card says clearly what the dataset is for; publishing 20 episodes described as a training set is not.

Do I have to convert my dataset to v2.1 for GR00T?

Yes. GR00T uses a flavour of the LeRobot v2 format and a v3.0 dataset will not load. Convert with the convert_v3_to_v2.py script in the Isaac-GR00T repo, then add meta/modality.json, which the standard LeRobot format does not write. If you expect GR00T users, publishing both versions saves everyone the round trip.

Why does my dataset load different data than it did last week?

Because LeRobotDataset resolves its revision to CODEBASE_VERSION when you do not pass one, so it fetches the v3.0 git tag rather than main, and push_to_hub deletes and recreates that tag on every push. Any republish moves it. Create your own dated tag and pass revision= explicitly if you need stable bytes.

Can I delete an episode after publishing?

You can, but do it with lerobot-edit-dataset --operation.type=delete_episodes rather than through the Hub file browser, because episode boundaries live in meta/episodes/ and not in filenames. That operation writes a new dataset and re-aggregates stats.json for the episodes it kept, so you do not have to recompute statistics separately; recompute_stats is only for a from-scratch rebuild, and it writes to {repo_id}_recomputed_stats unless you pass new_repo_id and operation.overwrite. Note that the old commit remains in git history, so for a privacy problem you need to squash the history or delete the repo, not just push a fix.

Does the Hub dataset viewer work for LeRobot datasets?

Yes, as long as the configs entry in your YAML points at data/*/*.parquet, which the lerobot card helper writes for you. Data Studio auto-converts the first 5 GB to Parquet, and for larger Parquet datasets sorting, filtering and search stay limited to those first 5 GB. On private datasets the viewer requires PRO, Team or Enterprise.

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started