The AY-Robots recording tutorial showing how a LeRobot dataset is captured from a teleoperation session, one task string per recording run.
multi-task learninglanguage conditioninglerobot datasettask balanceSmolVLAACT

Multi-Task Datasets for One Policy: Language and Task Balance

AY-Robots ResearchAugust 23, 202618 min read

Training one policy on several tasks: how the LeRobot task field works, what task balance does to sampling, and the cases where a single-task ACT still beats a multi-task VLA.

What you need to know

  • A LeRobot dataset already carries a task string per episode: prompts in meta/tasks.parquet, a task_index on every data row.
  • Not every policy reads it. On lerobot main (23 August 2026) ACT has no tokenizer and no task field, so it cannot tell your tasks apart.
  • RT-1 measured the trade: cutting 25 percent of the tasks while keeping 97 percent of the data cost as much generalization as discarding 49 percent of the data.
  • SmolVLA on an SO-100 averaged 78.3 percent over three tasks trained multi-task, against 48.3 for ACT trained per task.
  • Balance is frames, not episodes. EpisodeAwareSampler shuffles one permutation over all frames, so longer episodes get more gradient updates.
  • lerobot-train still refuses a list of repo ids. Merge first with lerobot-edit-dataset.
  • Single-task still wins for one fast reactive motion: ACT runs at 20 ms per action step against SmolVLA's 245 ms.

Almost every LeRobot dataset you record is already multi-task in the format sense: it has a task field, a task index, and the trainer reads both. Whether that becomes one policy that does several things, or one policy that does the average of several things badly, comes down to which task string you write, how many frames each task contributes, and whether your model can read language at all.

This page covers all three: the file layout, the CLI flags on lerobot main and Isaac-GR00T main as of 23 August 2026, and the published numbers from BC-Z, RT-1 and SmolVLA. It also says where multi-task training makes things worse, and where ACT on one task still beats a vision-language-action model trained on five.

What a multi-task LeRobot dataset actually is

Here is the on-disk shape you are working with. Everything about tasks lives in two places: one metadata file and one column.

text
my_dataset/
  data/
    chunk-000/
      file-000.parquet        # one row per frame, includes task_index
      file-001.parquet
  meta/
    info.json                 # fps, features, total_tasks
    stats.json
    tasks.parquet             # the task strings, indexed by task_index
    episodes/
      chunk-000/file-000.parquet   # per-episode metadata: tasks, length, ...
  videos/
    observation.images.front/chunk-000/file-000.mp4
    observation.images.wrist/chunk-000/file-000.mp4
The LeRobotDataset v3.0 layout, from the class docstring in src/lerobot/datasets/lerobot_dataset.py on main.

The lerobot source describes that metadata in one sentence: tasks contains the prompts for each task of the dataset, which can be used for task-conditioned training. Nothing forces one task per dataset. 120 episodes over four task strings is a legal v3.0 dataset.

Format version matters before anything else

GR00T reads a v2 flavour of the format: meta/tasks.jsonl instead of a parquet, plus a meta/modality.json. Isaac-GR00T ships scripts/lerobot_conversion/convert_v3_to_v2.py for the conversion. On AY-Robots, GR00T N1.7 and N1.5 take LeRobot v2.0 or v2.1 while Pi0.5, SmolVLA and ACT take v3.0. See dataset rejected as v3.

Where the task string comes from

The recorder writes one task string per run. Recording a dataset with lerobot-record takes --dataset.single_task, singular, and every frame in that invocation gets it. The natural unit of collection is one task per run, so a multi-task dataset is something you assemble afterwards.

bash
# one run, one task string, 25 episodes
lerobot-record \
    --robot.type=so101_follower \
    --robot.port=/dev/tty.usbmodem585A0076841 \
    --robot.id=my_follower_arm \
    --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
    --teleop.type=so101_leader \
    --teleop.port=/dev/tty.usbmodem58760431551 \
    --teleop.id=my_leader_arm \
    --dataset.repo_id=${HF_USER}/cubes_pick \
    --dataset.num_episodes=25 \
    --dataset.single_task="Pick up the red cube and put it in the box" \
    --dataset.streaming_encoding=true \
    --dataset.encoder_threads=2
lerobot-record on main. The flag is single_task, and it applies to the entire run.
The AY-Robots recording tutorial page showing the steps to capture a LeRobot dataset from a teleoperation session.
The recording walkthrough at /learn/record-your-first-dataset. One session produces one task string.

What multi-task training actually buys you

Three papers give usable numbers, and they do not all point the same way.

BC-Z: pooling across tasks rescued a task that single-task training could not learn

BC-Z (Jang et al., CoRL 2021) collected 25,877 demonstrations across 100 tasks, 125 hours of robot time, 12 robots and 7 operators, plus 18,726 human videos. The policy is a ResNet-18 conditioned through FiLM layers on a 512-dimensional task embedding, running closed-loop at 10 Hz.

Its sharpest ablation is on place the bottle in the ceramic bowl, the task with the most data at 1000 demonstrations. A single-task policy on those 1000 demos reached 5 percent; the multi-task language-conditioned policy trained on all 25,877 reached 52. The paper's own reading: collecting over several robots and operators likely makes any one task harder to learn, and only pooling across tasks recovered it.

BC-Z settingConditioningSuccess
Table 4, single-task baseline, 1000 demosn/a5%
Table 4, multi-task, all 25,877 demosone-hot task id45%
Table 4, multi-task, all 25,877 demoslanguage embedding52%
Table 4, multi-task, no adaptive state-diffone-hot task id3%
Table 3, 21 training tasksone-hot task id42%
Table 3, 21 training taskslanguage embedding40%
Table 3, 21 training taskshuman video embedding24%
Table 3, 28 held-out taskslanguage embedding32%
Table 3, 28 held-out taskshuman video embedding4%

Three things fall out. Language matches a one-hot task id on the 21 training tasks, 42 against 40, so the embedding is not the bottleneck. Language is the only conditioning that transfers to unseen tasks, 32 percent against 4 for video. And the bottom row is the warning: an unrelated processing choice, the adaptive state-diff, dropped the same multi-task policy from 45 to 3 percent.

RT-1: task diversity beat task quantity

RT-1 (Brohan et al., 2022) trained a 35M-parameter transformer at 3 Hz on roughly 130k episodes covering over 700 instructions, gathered with 13 robots over 17 months. Its data ablation is the most useful result in this area because it separates how much data from how many tasks.

Dataset variant% tasks% dataSeen tasksGeneralization, allUnseen tasksDistractorsBackgrounds
Full dataset1001009773768359
Capped at 200 examples per task100517150523959
Capped at 100 examples per task100375546573547
Capped at 50 examples per task100225929143141
Narrower: drop the tasks with the least data75978654674253

Read the columns carefully, because the summary column is easy to misquote. Generalization, all is the mean of the three columns to its right, not one of them: 14, 31 and 41 average to 29 on the 22-percent row. Seen-task success is also not monotonic in dataset size, 55 at 37 percent of the data against 59 at 22 percent.

The number worth remembering

RT-1's own sentence: removing 25 percent of the tasks while keeping 97 percent of the data achieves an equivalent generalization performance to reducing the dataset size by as much as 49 percent. More episodes of a task you already have are worth less than the same number of episodes of a task you do not.

SmolVLA: multi-task on an SO-100, with the awkward row included

SmolVLA is the closest published result to what most readers will actually run: a 450M-parameter VLA, three real tasks, an SO-100 arm. It was pretrained on 481 community datasets totalling 22.9K episodes and 10.6M frames, then fine-tuned.

Policy and settingPick-PlaceStackingSortingAverage
ACT, trained per task (single-task), Table 370502548.3
Pi0 3.5B, multi-task, Table 3100404561.7
SmolVLA 0.45B, multi-task, community pretrained, Tables 3 and 575907078.3
SmolVLA 0.45B, single-task, no VLA pretraining, Table 555452040.0
SmolVLA 0.45B, multi-task, no VLA pretraining, Table 580403551.7

The averages give the quotable line: pretraining on the community mixture lifts the multi-task average from 51.7 to 78.3. The per-task columns give a second one. Without pretraining, going multi-task raised Pick-Place from 55 to 80 but lowered Stacking from 45 to 40. And single-task ACT scored 50 on Stacking against multi-task Pi0's 40, Pi0 at 3.5 B parameters against ACT's roughly 80 M.

Multi-task is an average, and averages hide regressions

Report per task, never only the mean. In SmolVLA's own ablation one of three tasks got worse when the other two were added. If that is the task you care about, the average tells you the opposite of what you need.

Which policies can even read a task string

This is where a lot of multi-task datasets quietly become pointless. Before you spend a week writing careful instructions, check whether your model has a text encoder at all.

PolicyReads the task string?Where the string goesLatency per action step
GR00T N1.7Yesmeta/tasks.jsonl, into the Cosmos-Reason2-2B backbone152 ms
GR00T N1.5Yesmeta/tasks.jsonl, into the VLM backbone165 ms
Pi0.5Yestask field, into the PaliGemma backbone485 ms
SmolVLAYestask field, tokenized at tokenizer_max_length = 48 into the SmolVLM2-500M-Video-Instruct backbone245 ms
ACTNoignored; the config declares no tokenizer and no task input20 ms

Latency figures are the ones AY-Robots publishes on the policy comparison page. The ACT row catches people: on lerobot main, configuration_act.py declares no tokenizer, no text encoder and no task field, and modeling_act.py never mentions a tokenizer. ACT is an imitation learning architecture mapping images and joint state to an action chunk, and nothing in it reads text.

A multi-task ACT dataset trains a confused single-task policy

Feed ACT four task strings and it trains without error, writes a healthy-looking loss curve, and produces a policy that learned the average of four behaviours conditioned only on pixels. If those tasks start from visually similar scenes, the result looks exactly like loss falls but the policy does nothing. Train ACT once per task, or move to SmolVLA.

The AY-Robots policies page comparing GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT by parameter count, GPU tier, inference latency and minimum episodes.
The five trainable policies at /policies. The latency column decides whether a language-conditioned VLA suits your motion.

Assembling several tasks into one dataset

Three runs, one task each. Now you need one dataset: merge, relabel, check.

  1. 1
    Record each task as its own run

    Keep camera positions, arm id and fps identical across runs. Merge requires identical features, and aggregate_datasets validates fps, robot type and features before concatenating anything.

    bash
    lerobot-record --dataset.repo_id=${HF_USER}/cubes_pick   --dataset.single_task="Pick up the red cube and put it in the box"   --dataset.num_episodes=25 ...
    lerobot-record --dataset.repo_id=${HF_USER}/cubes_stack  --dataset.single_task="Stack the red cube on the blue cube"       --dataset.num_episodes=25 ...
    lerobot-record --dataset.repo_id=${HF_USER}/cubes_sort   --dataset.single_task="Sort the cubes by colour into the two bins" --dataset.num_episodes=25 ...
  2. 2
    Merge them into a single repo id

    Episodes are concatenated in the order you list the repo ids. The task strings survive and become separate entries in meta/tasks.parquet.

    bash
    lerobot-edit-dataset \
        --repo_id ${HF_USER}/cubes_multitask \
        --operation.type merge \
        --operation.repo_ids "['${HF_USER}/cubes_pick', '${HF_USER}/cubes_stack', '${HF_USER}/cubes_sort']"
  3. 3
    Fix the task strings in place

    modify_tasks rewrites meta/tasks.parquet, the task_index column, the episode metadata and total_tasks in info.json. It edits in place, so --new_repo_id and --new_root are ignored. Back up first.

    bash
    # standardise wording across the merged dataset
    lerobot-edit-dataset \
        --repo_id ${HF_USER}/cubes_multitask \
        --operation.type modify_tasks \
        --operation.task_replacements '{"Sort the cubes by colour into the two bins": "Sort the cubes by color into the two bins"}'
    
    # or relabel specific episodes
    lerobot-edit-dataset \
        --repo_id ${HF_USER}/cubes_multitask \
        --operation.type modify_tasks \
        --operation.episode_tasks '{"0": "Pick up the red cube and put it in the box", "63": "Stack the red cube on the blue cube"}'
  4. 4
    Check the task counts before you rent a GPU

    Count frames per task, not episodes per task. Frames are what the sampler uses.

    python
    from lerobot.datasets.lerobot_dataset import LeRobotDataset
    
    ds = LeRobotDataset("your-user/cubes_multitask")
    print(ds.meta.total_tasks)
    print(ds.meta.tasks)          # task string -> task_index
    
    import collections
    frames = collections.Counter()
    eps = ds.meta.episodes
    for i in range(ds.num_episodes):
        task = eps["tasks"][i][0] if eps["tasks"][i] else ""
        frames[task] += eps["length"][i]
    for task, n in frames.most_common():
        print(f"{n:>8}  {task}")
The trap that eats a day: you cannot pass a list of repo ids to lerobot-train

The DatasetConfig comment above repo_id still says you may provide a list of datasets that train.py concatenates, but make_dataset in src/lerobot/datasets/factory.py raises NotImplementedError("The MultiLeRobotDataset isn't supported for now.") before it constructs one. The comment is stale. Merge into a single repo id first. Checked on main, 23 August 2026.

The AY-Robots public dataset directory listing LeRobot datasets available for training.
The public directory at /directory. Check for a compatible dataset before recording a second task from scratch.

Task balance: what the sampler is really doing

The word balance does a lot of unexamined work here, so: lerobot trains with EpisodeAwareSampler, which its docstring calls a sampler over episode frames. It keeps only the per-episode frame boundaries, then shuffles each epoch with one torch.randperm over every frame, seeded from the run seed and the epoch number. No per-task weighting anywhere in it, and no per-task argument to give it.

A task's effective weight is therefore its share of the total frames. Twenty-five episodes of a 40-second stack and twenty-five of a 12-second pick contribute frames at roughly 10:3, so the stack gets about three times the gradient updates even though the episode counts match.

KnobWhereWhat it does
--dataset.episodeslerobot-trainTrain on an explicit list of episode indices
--dataset.exclude_episodeslerobot-trainDrop specific episodes on top of that, e.g. corrupt ones
--dataset.eval_splitlerobot-trainHold out the last ceil(n * split) episodes per task
episode_filterLeRobotDataset(...)Predicate over per-episode metadata, including task_index
--ds-weights-alphaIsaac-GR00T gr00t/experiment/launch_finetune.pySampling weight per dataset becomes len(dataset)^alpha
--dataset-pathIsaac-GR00T gr00t/experiment/launch_finetune.pyOne dataset root, or several separated by os.pathsep

Two deserve a note. lerobot's eval_split is stratified by task by design: make_train_eval_datasets groups episodes by task string and holds out the tail of each group, so eval_split 0.2 on five tasks gives an eval set containing all five.

GR00T's ds_weights_alpha is the only mixture control the fine-tune CLI exposes. Its docstring: each dataset's sampling weight is len(dataset)^alpha and per-dataset mix_ratio values are ignored. Alpha 1 is plain pooling by length, alpha 0 weights every dataset equally regardless of size. Either way launch_finetune.py pools every path you pass into a single entry at mix_ratio 1.0, so genuine per-dataset ratios have to come from a config file loaded by launch_train.py, not from a flag.

Uniform-over-tasks against proportional-to-frames
Reasons to force uniform
  • A rare task with 8 percent of the frames is otherwise learned as noise around the dominant one.
  • Octo's authors did a coarse version by hand: they doubled the weight of the more diverse datasets and down-weighted repetitive ones so they would not dominate the mixture.
  • Per-task scores become comparable, because each task saw a similar number of updates.
Reasons to leave it proportional
  • Upsampling a small task repeats the same frames, which is overfitting with extra steps, not more data.
  • If one task is the product and the others exist only for transfer, proportional is correct.
  • lerobot exposes no per-task weight, so forcing uniform means trimming episodes, which is destructive.

Writing task strings the model can use

SmolVLA's authors hit this at scale. Across 481 community datasets they found substantially noisy annotations: placeholders like task desc, commands as vague as Hold or Up, and datasets with no instruction at all. Their fix was to run Qwen2.5-VL-3B-Instruct over representative frames to write a short, action-oriented sentence, and to rename cameras by hand to OBS_IMAGE_1 through OBS_IMAGE_3.

  • Write the instruction the way a person would say it out loud: "Pick up the red cube and put it in the box", not "pick_red_cube_v2".
  • Keep it short. SmolVLA sets tokenizer_max_length = 48, so anything past roughly 48 tokens never reaches the model.
  • Separate the strings by content words, not punctuation or casing. "Pick the cube" and "pick the cube." are two different task_index values.
  • Name the object and the destination when they vary. If two tasks differ only in which bin, the string is the only signal the policy has.
  • Use exactly one string per task across all runs, standardised afterwards with --operation.type modify_tasks.
  • Do not encode the episode number, the date or the operator. Every unique string is a separate task as far as the format is concerned.
The inference-time string has to match the training-time string

lerobot-rollout takes --task="...", and the lerobot SmolVLA guide annotates that exact line in its evaluation command: use the same task description you used in your dataset recording. A policy that scores well offline and fails on the robot is often one being asked for a string it never saw. See policy only works in one setup.

bash
lerobot-rollout \
  --strategy.type=base \
  --policy.path=${HF_USER}/my_smolvla_multitask \
  --robot.type=so100_follower \
  --robot.port=/dev/ttyACM1 \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
  --task="Stack the red cube on the blue cube" \
  --duration=60
One trained policy, three different --task values, three behaviours. This is the whole point of language conditioning.

Two routes to the same multi-task policy

  1. 1
    Install lerobot with the SmolVLA extra

    Use a fresh environment; the VLA extras pull in a lot.

    bash
    git clone https://github.com/huggingface/lerobot
    cd lerobot
    pip install -e ".[smolvla]"
  2. 2
    Record one run per task, then merge

    Same cameras, same fps, same robot id across every run.

    bash
    lerobot-edit-dataset \
        --repo_id ${HF_USER}/cubes_multitask \
        --operation.type merge \
        --operation.repo_ids "['${HF_USER}/cubes_pick', '${HF_USER}/cubes_stack', '${HF_USER}/cubes_sort']"
  3. 3
    Fine-tune with a per-task holdout

    eval_split holds out the last fraction of episodes per task, so every task appears in the eval set. The lerobot docs put a 20k-step SmolVLA run at roughly four hours on one A100.

    bash
    lerobot-train \
      --policy.path=lerobot/smolvla_base \
      --dataset.repo_id=${HF_USER}/cubes_multitask \
      --dataset.eval_split=0.2 \
      --batch_size=64 \
      --steps=20000 \
      --output_dir=outputs/train/smolvla_multitask \
      --job_name=smolvla_multitask \
      --policy.device=cuda \
      --wandb.enable=true
  4. 4
    Roll out each task separately

    Same checkpoint, three --task strings, three scores.

    bash
    for T in "Pick up the red cube and put it in the box" \
             "Stack the red cube on the blue cube" \
             "Sort the cubes by color into the two bins"; do
      lerobot-rollout --strategy.type=base \
        --policy.path=outputs/train/smolvla_multitask/checkpoints/last/pretrained_model \
        --robot.type=so100_follower --robot.port=/dev/ttyACM1 \
        --task="$T" --duration=60
    done
What this route costs you

A 24 GB card of your own or a rented one you configure yourself. In exchange you control the sampler, the mixture and the eval split completely.

The AY-Robots download page for the desktop client that records LeRobot datasets from a teleoperation session.
The desktop client at /download. It writes LeRobot episodes with camera streams and joint states, one session at a time.

When a single-task ACT still beats a multi-task VLA

The honest version is not "VLAs win". VLAs win on the axes a VLA is built for and lose on the one a small from-scratch model is built for. Latency is the axis nobody argues with: on this platform ACT runs at 20 ms per action step, GR00T N1.7 at 152 ms, SmolVLA at 245 ms and Pi0.5 at 485 ms.

SituationPickWhy
One task, fast reactive motionACT, single-task20 ms per action step; language buys nothing with one task
Three or more tasks, one rigSmolVLA or GR00T N1.7, multi-taskOne checkpoint instead of three; SmolVLA averaged 78.3 against ACT's 48.3
You want tasks you did not recordA language-conditioned VLABC-Z reached 32 percent on held-out tasks; a one-hot id transfers to nothing
Tasks look identical at the start frameA language-conditioned VLA, or separate policiesACT sees the same pixels and cannot disambiguate them
One task matters, the rest are decorationACT single-task on the one that mattersMulti-task can lower a per-task score while raising the mean

Language conditioning also has a measured cost. Octo, evaluated zero-shot on the WidowX tasks, reached a 25 percent higher success rate under goal-image conditioning than under language; the authors say this is likely because a goal image carries more information about how to achieve the task. Two tasks that differ only by a fine spatial detail may not be separable by a sentence.

Two policies is a legitimate answer

Nothing forces one checkpoint. With one fast task and two slow ones, a single-task ACT for the fast one plus a multi-task SmolVLA for the rest is often the highest-scoring setup, and both fit the same 24 GB tier. Head to head: ACT vs SmolVLA.

Evaluating a policy that does several things

A single average across tasks is the wrong summary statistic. Report a per-task table before and after, and hold out episodes per task rather than off the end of the dataset. The training docs cover the run mechanics this section assumes.

bash
# per-task holdout: the last 20% of episodes of EACH task go to eval
lerobot-train \
  --policy.path=lerobot/smolvla_base \
  --dataset.repo_id=${HF_USER}/cubes_multitask \
  --dataset.eval_split=0.2 \
  --steps=20000 \
  --output_dir=outputs/train/smolvla_multitask \
  --policy.device=cuda
eval_split is documented as the fraction of episodes held out per task. make_train_eval_datasets groups by task string first, then splits each group.

Check the noise floor before concluding a mixture change helped. NVIDIA's GR00T README notes users may observe 5-6 percent variance between runs from non-deterministic image augmentations alone, and the Isaac-GR00T fine-tune entry point exposes no seed at all, so GR00T runs are not bit-for-bit reproducible (lerobot's default seed is 1000). A 3-point per-task difference between two checkpoints tells you nothing.

How many episodes per task do I need for a multi-task dataset?

AY-Robots lists a minimum of 30 episodes for SmolVLA and 50 for ACT, Pi0.5, GR00T N1.5 and GR00T N1.7. Those are dataset minimums, not per-task minimums, so five tasks at the bare minimum leaves each with about ten episodes, which is not enough for any of them. Treat the published figure as the floor for one task. The lerobot SmolVLA guide recommends around 50 episodes for a single task, recorded as 10 episodes across each of 5 variations, and reports that a comparable 25-episode dataset performed badly.

Can I train ACT on a multi-task dataset?

It runs and it will not warn you, but ACT has no language input. On lerobot main configuration_act.py declares no tokenizer, no text encoder and no task field, so the string is ignored and you get one policy that averaged your behaviours. If the tasks start from visually distinct scenes it may still work; if they start from similar scenes it will not.

Do I have to balance the tasks equally?

No, and often you should not. What matters is that the balance matches your priorities, because EpisodeAwareSampler weights every task by its share of frames. If all your tasks matter equally, aim for equal frame counts, not equal episode counts. If one task is the product and the rest exist for transfer, leaving it dominant is correct.

How do I fix task strings after recording?

Use lerobot-edit-dataset with --operation.type modify_tasks: --operation.new_task for a blanket default, --operation.episode_tasks for a mapping from episode index to string, --operation.task_replacements for search and replace, resolved in that precedence. It edits in place, updating meta/tasks.parquet, the task_index column, the episode metadata and total_tasks, so take a copy first.

Can I train on several datasets at once instead of merging them?

On lerobot main, no: make_dataset raises NotImplementedError("The MultiLeRobotDataset isn't supported for now.") before constructing one, so merge into a single repo id first. Isaac-GR00T is different: gr00t/experiment/launch_finetune.py accepts several dataset roots separated by os.pathsep in --dataset-path, and --ds-weights-alpha weights them by len(dataset)^alpha.

Five policies, real numbers, no marketing

Parameter counts, GPU tier, inference latency per action step and minimum episodes for GR00T N1.7, GR00T N1.5, Pi0.5, SmolVLA and ACT, with the language-conditioning column that decides whether a multi-task dataset is worth recording.

Compare the policies

What to do next

One fast task: train an ACT and skip everything above. Three tasks on the same rig: record separately, merge, count frames per task rather than episodes, and fine-tune SmolVLA with eval_split set so the holdout contains all three. Then score each task on its own against the single-task baselines you already have. That comparison is cheap on the 24 GB tier and it is the only thing that tells you whether multi-task is better for your tasks.

Related reading: the BC-Z breakdown, how to collect high-quality VLA training data on the recording side, and the VLA overview. Benchmark data for 85 models sits in the arena, and the format details this page skipped are in the dataset docs.

Sources

Ready for high-quality robotics data?

AY-Robots connects your robots to skilled operators worldwide.

Get Started