COLLECT

COLLECT records teleoperation episodes to a standard robot-learning dataset, with PASS/FAIL review in the same tab.

COLLECT: choose a dataset and task, unlock motion, record, review, then export the quality split.

Overview

COLLECT is the third of the console's seven tabs (DEBUG · MANUAL · COLLECT · REPLAY · RL · EVAL · RESULT). It runs no policy: each episode records the arm's state alongside the operator's commanded action, so the dataset is replayable and trainable in one pass.

Tab is gated by config. COLLECT is enabled only when the launched config carries a collection section (see below). No separate process.

Prerequisites

Use MANUAL first to confirm motion and camera streams.

Why COLLECT may be greyed out

Relaunch with a collection config; see below.

Workflow

#StepAction
1TASKPick the dataset and task/prompt from collection.tasks. The task is stored with the episode.
2RECORDFor EVA-gated control, move MOTION from LOCKED to enabled, then press START RECORD. Press it again to end and save; CANCEL discards the active take, and HOME requests the configured home action.
3CONVERT QUEUEWatch pending episodes become usable or rejected while the background saver and validators finish.
4QUALITY CHECKReplay and mark PASS/FAIL; add and save an optional note.
5QUALITY EXPORTSelect LeRobot v2.1/v3.0, HDF5, or MCAP, export both subsets, then optionally upload the current accepted export.
What gets recorded. Both halves of the teleop: observed state (joint angles + EEF) and commanded action (joint angles + EEF). Driven by the config, not the tab.

While recording

Status line: COLLECTING, SAVING, QUEUE_FULL, or IDLE, plus frame count, queue depth, and ETA.

Recordings append. Relaunching into the same folder appends new episodes after existing ones. See Data collection for the on-disk layout.
Motion gates depend on the input path. WebXR has a global ARM/MOTION gate plus per-hand grip authorization. Leader/Follower adapters follow their own configuration and hardware safety requirements. Read the VR guide before moving hardware with WebXR.

Success and common faults

SymptomCause / action
Frame count stuck at 0No data arriving. Verify drivers and that config topic names match the publishers.
MOTION remains LOCKEDThe teleop client is offline/stale or its safety state is not ready. Check the input node, heartbeat, and any device-side authorization before retrying.
Camera black / missingStream down or camera name mismatch. Confirm via MANUAL.
QUEUE_FULLSaver behind. Wait for IDLE; if persistent, lower fps or raise save_queue_max.
Episode red on reviewAn auto-check fired — see Quality control.

Quality control

On STOP, replay the episode and mark PASS/FAIL. In addition, every episode runs through automatic checks; any failure marks it red and persists the reasons alongside the data.

FlagRaised whenAction
episode_too_shortFewer frames than min_episode_frames.Re-record longer, or lower the threshold.
non_monotonic_timestampA frame's timestamp does not advance past the previous.Re-record. If recurring, check the publisher clock.
missing_camera · invalid_image_shapeConfigured camera absent, or image not H×W×3.Verify stream and config name match.
missing_configured_column · invalid_vector_dimConfigured field missing or wrong length.Verify topic publishing and arm count.
non_finite_valueNaN/Inf in a recorded value.Auto-zeroed; re-record if frequent.
frame_count_mismatchCamera video frame count differs from the data table.Re-record; indicates dropped/stalled camera.
Bad fields are zero-filled, not dropped. Schema stays consistent; the flag is persisted with the episode.

For deeper review and adding instruction text per episode, use REPLAY.

Quality export

Live capture always writes the LeRobot v2.1 raw dataset. EXPORT DATASET first derives accepted and rejected episode sets from QC, then converts both to the selected format. Progress is asynchronous and reports completed/total episodes.

For exact output paths, per-format structures, atomic replacement, and failure checks, see Data formats.

Writing a collection config

A config becomes a collection config the moment it declares columns to record — that is what enables the tab. A collection config inherits a per-robot deploy file via _base_ and adds a collection section (see Configuration).

Settings

SettingPurpose
storage.log_dirOutput root. Per-task subfolders. Empty → default folder named after the config.
storage.fpsTarget frame rate.
storage.save_queue_maxMax episodes queued for save. Full → QUEUE_FULL.
schema.robot_typeRobot id, e.g. agilex_piper.
schema.min_episode_framesThreshold for episode_too_short.
schema.armsArms to record, one entry each.
schema.camerasCameras to record. At least one required.
schema.columnsThe four required dataset fields: qpos, eef, action_qpos, action_eef.
teleop.control_sourcetransport for commands already present in the robot transport; client for an EVA input client such as WebXR.
tasksDataset-set keys mapped to prompt/target pairs offered in the TASK picker.

Copy the shipped collection config for your robot (e.g. configs/02_collection/dual_agilex_piper.py) and edit the marked values. Lines marked ← change… must match your robot.

configs/02_collection/my_robot.py
_base_ = ['<path to your robot's deploy _base.py>']  # ← point at your robot's deploy config

collection = dict(
    storage=dict(
        log_dir='',           # ← where to save (empty = a default folder named after this config).
        fps=30,               # sane default; change only if your robot runs at another rate.
        save_queue_max=15,     # sane default.
    ),
    schema=dict(
        robot_type='<your robot type>',  # ← e.g. 'agilex_piper'.
        min_episode_frames=10,  # sane default.
        arms=dict(left_arm='left', right_arm='right'),  # ← one entry per arm.
        cameras=dict(
            cam_high='observation.images.cam_high',  # ← list your cameras (name → dataset field).
        ),
        columns=dict(           # keep these four keys; values are standard dataset field names.
            qpos='observations.state.qpos',
            eef='observations.state.eef',
            action_qpos='action.qpos',
            action_eef='action.eef',
        ),
    ),
    teleop=dict(control_source='transport'),  # ← 'client' for VR; see the VR preset.
    tasks=dict(
        pick_up_the_apple=[('pick up the apple', -1)],  # ← dataset set → prompt/target pairs.
    ),
)

Save under configs/02_collection/ and launch. The loader enforces:

Field lengths are checked. Joint fields must match the robot's joint count; EEF fields are 8-wide per arm (x, y, z, orientation, gripper). Mismatches are flagged on review.

Launching

bash
eva --config configs/02_collection/my_robot.py

Open the console at http://localhost:8080 (override via --web-port, see CLI reference). Episodes are replayable through the dataset transport and reviewable in REPLAY.