videosdk_teleop.export reads a recorded session and
builds one row per observation. The LeRobot dataset writer lives in the examples
repo, because dataset layouts change between LeRobot versions.
export_lerobot.py
Clone the examples repo and run it against a session directory.
--dry-run reports the row count, the measured frame rate and how many rows were
joined by id rather than by time. Add --explicit-only to drop the guessed rows
entirely.
Walking a session
build_rows() is pure. It touches no filesystem beyond checking that the images
a row references exist, and it does not import LeRobot, so you can test a join
without a training stack installed.
Correlation quality
Each row records how its action was matched to its observation.explicit is what you want. temporal happens when the leader never stamped an
observation id, and those rows are labelled rather than mixed in silently, so you
can filter, weight or reject them.
Even an
explicit row is an upper bound rather than proof. The id a command
carries is the newest observation the leader had received, and video
reaches the operator’s screen by its own path with its own delay, so what they
were actually looking at may be slightly older.The follower records both ids for exactly this reason: observation_id is
what the leader claimed, observation_id_local is what was current on the
robot at that moment. The gap between them in applied.jsonl is the size of
the doubt, so you can measure it rather than assume it away.summarize() also gives you rows, fps_effective, fps_jitter_ms,
camera_skew_ms_max, camera_skew_ms_mean, obs_to_applied_ms_mean and
rows_limited.
fps_effective is measured from the joint read times rather than declared, so
a recording that ran at 27 Hz reports 27 rather than the 30 you asked for.
fps_jitter_ms is the spread around it.What a row holds
action is the clamped value, what the arm was actually told to do. Training on
operator intent would teach a policy to command through the safety envelope and
rely on a clamp it will not have when deployed. target keeps the pre-clamp
intent on the same row, so the difference stays recoverable.
Per-camera capture times are kept rather than averaged away, so a wrist camera
running 60 ms behind the front camera is visible in the data.
Related
Recording format
The streams these functions read, and the drop counters to check first.
Recording
Turning recording on and bounding episodes.