Detailed content
🎓 Learning by demonstration (imitation)
In module 5.3, you made the follower copy the leader in real time. Now the idea is to take it one step further: record these demonstrations and train a neural network that learns the connection “what I see and where my joints are → what the next movement should be.” Once trained, it controls the follower without anyone at the leader. This is called imitation learning.
🌱 New here?
A policy is the program that decides the next action based on what the robot observes. Here, it’s a neural network, not code with if. Imitation means the network learns by copying examples from a person, instead of figuring things out through trial and error (that would be reinforcement learning, which is almost always done in simulation and is covered in 5.5).
How to read this map: the green arrows show one cycle; the glowing block (training) is the only part that needs a GPU. The cyan arrow marks the difference between those who succeed and those who give up: evaluation shows where the policy fails, and the fix is to record more examples of exactly that situation.
✓ Good first task
- ✓Pick up a foam cube and put it in a shallow box.
- ✓A lightweight, colorful object that contrasts with the table.
- ✓Easy to judge success: is it in the box or not?
✗ Save for later
- ✗Folding fabric, opening a screw-top lid, tight-fitting parts.
- ✗A transparent, shiny, or heavier object than the gripper can handle.
- ✗Several different tasks in the same dataset right from the start.
Why learn this
Programming the arm to “pick and place” works until the cube moves 3 cm from its spot. The learned policy looks at the camera at every moment and adjusts its path. That’s how labs train arms today, and the SO-101 with LeRobot brings this method to your desk.
Key concepts
⏺️ Record episodes with lerobot-record
Each recorded demonstration is an episode. The command lerobot-record connects the leader, follower, and cameras, and saves three things at each frame: the images, the follower’s current joint positions (the state) and the position the leader commanded (the action). Between episodes, there’s a reset period so you can set up the table.
🌱 New here?
Frame is a recorded moment (at 30 frames per second, one every 33 ms). State is where the arm is; action is where it was commanded to go. The network learns to predict the action from the image and the state. The 6 numbers in each are the SO-101’s joints: base, shoulder, elbow, wrist bend and rotation, gripper.
How to read this map: each column is a frame. The top row shows what the camera saw (the cube gets closer, is picked up, and appears in the box). The rows below show the numbers the network will learn to connect: given the state and image, predict the action. The text instruction applies to the entire episode.
Step-by-step: your first recording
Check ports, calibration, and camera
Use the same ports and the same id calibration files as in 5.3. Run lerobot-find-cameras and note the index of the front-facing camera.
Keep the scene fixed
Secure the camera (with tape or a mount), keep the lighting constant, and always put the box in the same place. Only the cube’s position changes between episodes.
Record 2 test episodes
Run the command below with num_episodes=2, without uploading to the cloud. If everything works, delete the test folder and record for real (50 episodes is a good start).
Goal: record episodes for the task “put the cube in the box” with a front-facing camera. Replace the ports, the id and your Hugging Face username (without the < > symbols).
# Record demonstrations with the SO-101 pair (Linux; on Windows the port is COM3, COM4...)
HF_USER=<seu_usuario_hf> # replace with your Hugging Face username
DATASET=cubo-na-caixa-teste # dataset name
lerobot-record \
--robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=seguidor \
--robot.cameras="{ frente: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--teleop.type=so101_leader --teleop.port=/dev/ttyACM1 --teleop.id=lider \
--dataset.repo_id=${HF_USER}/${DATASET} \
--dataset.single_task="Put the red cube in the gray box." \
--dataset.num_episodes=2 \
--dataset.episode_time_s=20 \
--dataset.reset_time_s=10 \
--dataset.push_to_hub=false \
--display_data=true
How to check
- ✓A window opens with the camera image and joint graphs that change as you move the leader.
- ✓The terminal announces the start of each episode and the reset; at the end, it shows the folder in ~/.cache/huggingface/lerobot/.
- ✗Port or calibration error? Go back to 5.3. Flag names change between versions: check with lerobot-record --help and in the current docs.
✓ Good demonstration
- ✓Calm movement, done the same way every time.
- ✓Cube in varied positions, covering the entire area.
- ✓Starts and ends with the arm at rest.
✗ Ruins the dataset
- ✗Hesitating, backtracking, trying two different ways.
- ✗Your hand or arm appearing in front of the camera.
- ✗Moving the camera or changing the lighting during recording.
Why learn this
The policy can never be better than the demonstrations. If you record calmly and consistently, you train once; if you record in a hurry, you train five times and still don’t understand why it doesn’t work.
Key concepts
🗂️ Dataset on the Hugging Face Hub
LeRobot saves each dataset in a standard format: tables with state and action, camera videos, and a metadata file. This same format can be uploaded to the Hugging Face Hub, where you can train on a machine with a GPU (or in Colab) without copying files by hand, and where other people can use your data if you want.
🌱 New here?
O Hugging Face Hub is a free site that hosts models and datasets, similar to a GitHub for AI. The repo_id is the dataset address: username/name. To upload, create an account, generate a write token in settings, and log in once in the terminal.
How to read this map: the name at the top is the repo_id. The tables (middle) are lightweight; the videos (right, in cyan) take up almost all the space. If the dataset is too large, reduce the resolution or number of cameras, not the number of episodes.
Goal: log in, check a recorded episode, and upload the dataset to the Hub as private. Run this in the same Python environment where you installed LeRobot.
hf auth login # paste the write token (once per machine)
lerobot-dataset-viz --repo-id <seu_usuario_hf>/<dataset> --episode-index 0
# Open the local dataset, show its size, and upload it to the Hub as private.
import sys
from lerobot.datasets.lerobot_dataset import LeRobotDataset
repo_id = sys.argv[1] # e.g.: <your_hf_username>/<dataset>
ds = LeRobotDataset(repo_id) # reads from ~/.cache/huggingface/lerobot/
print("episodes:", ds.num_episodes, "| quadros:", ds.num_frames, "| fps:", ds.fps)
print("colunas:", list(ds.features))
ds.push_to_hub(private=True) # private: only you can see it
print("uploaded: https://huggingface.co/datasets/" + repo_id)
How to check
- ✓The viewer shows the video for episode 0 with the curves for all 6 joints synchronized.
- ✓python subir_dataset.py <your_hf_username>/<dataset> prints the number of episodes (what you recorded) and the columns, including state, action, and the "front" camera.
- ✓The dataset page opens in your logged-in browser and appears as private.
💡 Privacy
The camera records whatever is in the background: faces, your home, papers on the table. In a school setting, keep the dataset private and frame only the workbench. Make it public only after reviewing it episode by episode.
Why learn this
Separating "where you record" from "where you train" solves the problem for people without a GPU at home: record on the laptop next to the arm, then train in Colab or in the lab. A well-documented dataset also becomes lesson material for the whole class.
Key concepts
🏋️ Train ACT with lerobot-train
A ACT (Action Chunking with Transformers) is the recommended policy to start with: it is small, trains quickly, and works well with 50 episodes of one task. The trick is in the name: instead of predicting one movement at a time, it predicts a chunk of several consecutive movements, which makes the arm smoother and less hesitant.
How to read this map: on the left is everything the network sees at a given moment. On the right, the bars represent future movements: the solid ones are executed, while the outlined ones are discarded because by then the camera will have new information. The cyan arrow closes the loop, as in the sense-think-act cycle in 1.1.
🌱 New here?
Training means adjusting millions of network parameters until it makes few errors when predicting the recorded actions. Each adjustment uses a batch of frames (batch) and counts as one step (step). From time to time, training saves a checkpoint: a copy of the network at that point, which you can test or resume training from.
Goal: train an ACT on your dataset on a machine with an NVIDIA GPU. On a Mac with an Apple chip, replace cuda with mps; without a GPU, use Colab (there’s a ready-to-use notebook in the LeRobot documentation).
# Train the ACT policy (check the flags with: lerobot-train --help)
HF_USER=<seu_usuario_hf>
DATASET=<dataset>
lerobot-train \
--dataset.repo_id=${HF_USER}/${DATASET} \
--policy.type=act \
--policy.device=cuda \
--output_dir=outputs/train/act-${DATASET} \
--job_name=act-${DATASET} \
--batch_size=8 \
--steps=20000 \
--wandb.enable=false \
--policy.push_to_hub=false
How to check
- ✓The terminal shows lines with the current step and the loss (the error), which should drop quickly at first, then more slowly.
- ✓Folders appear in outputs/train/…/checkpoints/; the latest one is in last/pretrained_model.
- ✗"CUDA out of memory"? Reduce --batch_size (4 or 2). Training can take anywhere from minutes to many hours, depending on the GPU and the number of steps.
💡 A low loss doesn’t mean success
Loss measures how well the network copies the recorded demonstrations, not how well it performs the task in the real world. A network can memorize the 50 episodes and fail when the cube appears in a new location. The only number that matters is the success rate on the arm, which you’ll measure in the next topic.
Why learn this
Knowing how to read training results helps you avoid wasting GPU time: you learn when to stop after the loss levels off, test intermediate checkpoints, and tell the difference between "not enough data" and "not enough training."
Key concepts
🧪 Evaluate the policy on the real arm
Evaluation means letting the policy control the follower, without the leader, and counting how many times it succeeds. In the current version of LeRobot, you do this with lerobot-rollout; earlier versions used lerobot-record itself with the specified policy. Check which one applies to your installation with --help.
Safety before the first test
A newly trained policy can make sudden, unexpected movements. The SO-101 servos are small, but they can trap a finger or knock objects over. Keep your hand on the follower’s power switch, clear the table, keep everyone out of the arm’s range, and make the first test short.
Goal: run the trained policy on the follower for 30 seconds, using the same camera and the same instruction as in the recording.
# Run the policy on the real arm (check the flags with: lerobot-rollout --help)
POLITICA=outputs/train/act-<dataset>/checkpoints/last/pretrained_model
lerobot-rollout \
--strategy.type=base \
--policy.path=${POLITICA} \
--robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=seguidor \
--robot.cameras="{ frente: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--task="Put the red cube in the gray box." \
--duration=30
How to check
- ✓The follower moves on its own toward the cube; the leader can remain turned off.
- ✓After 30 seconds, the command ends and the arm stops.
- ✗Arm shaking or moving to a fixed corner? Did the camera move since recording, or does the camera name not match the dataset name ("front")?
Evaluation spreadsheet (20 attempts)
| Cube position | Attempts | Successful attempts | How it failed |
|---|---|---|---|
| Near the center (as seen in the recording) | 10 | record | missed the grasp, released outside… |
| At the edges of the recorded area | 5 | record | record |
| Outside the recorded area | 5 | record | record |
✓ Honest evaluation
- ✓Define "success" before you start, and don’t change it afterward.
- ✓Randomize the cube positions; don’t choose the easy ones.
- ✓Record video and note the type of failure for each mistake.
✗ Fooling yourself
- ✗Counting only the attempts you filmed.
- ✗Pushing the cube "just a little" to help.
- ✗Drawing a conclusion from 3 attempts.
When a failure pattern appears (for example, "it always misses when the cube is on the left"), record 10 to 20 new episodes in that area and train again. LeRobot also has a mode where you take control with the leader when the policy makes a mistake, and that correction becomes a new episode (--strategy.type=dagger); it’s the most efficient way to improve after the first version.
Why learn this
Without a numerical evaluation, every AI project "almost works." The spreadsheet shows you exactly where you stand, lets you compare versions, and gives you something concrete to present in your final project.
Key concepts
🗣️ SmolVLA and pi0: language-based policies
ACT learns a task and ignores text. A VLA (Vision-Language-Action model, introduced in 5.1) also receives the written instruction and can perform different tasks depending on the request: "put the cube in the box" or "put the ball in the box." These models are already pretrained on many robots; you just fine-tune them fine-tune with your episodes.
How to read this map: the difference between a VLA and ACT is the cyan box. With the text, the same model chooses which object to pick up. The text doesn’t become code or a list of steps: it directly conditions the joint movements.
| Policy | Understands text | Training compute | When to use |
|---|---|---|---|
| ACT | isn’t | lightweight | one task, first project |
| SmolVLA | yes | medium (GPU with plenty of memory) | a few tasks with the same arm |
| pi0 / pi0.5 | yes | heavy (large GPU or cloud) | research, varied tasks |
Goal: fine-tune SmolVLA from the base model lerobot/smolvla_base using your dataset. First, record episodes with different instructions (for example, half with "cube" and half with "ball").
# Install the SmolVLA extra in the same LeRobot environment
pip install "lerobot[smolvla]"
# Fine-tune from the base model (check the current docs for the flags)
lerobot-train \
--policy.path=lerobot/smolvla_base \
--dataset.repo_id=<seu_usuario_hf>/<dataset> \
--policy.device=cuda \
--output_dir=outputs/train/smolvla-<dataset> \
--batch_size=16 \
--steps=20000 \
--wandb.enable=false \
--policy.push_to_hub=false
How to check
- ✓At the start, training downloads the base model weights from the Hub (the first time only), then shows the loss going down, as with ACT.
- ✓For the evaluation, change only the text for --task and see whether the arm switches objects.
- ✗Running out of GPU memory? Reduce the batch size or train on Colab. The pi0 / pi0.5 family needs even more; start with ACT or SmolVLA.
💡 See also: simulation data
The INEMA project robotica-real2sim generates episodes in a simulation and exports them in LeRobot format (in the test: 50 episodes with cameras, 4,361 frames, 9.7 MB). Mixing a few real episodes with many simulated ones is the topic of the next module.
Why learn this
The final project (5.6) needs an arm that follows different requests. With ACT, you train one policy per task and the language model chooses which one to call; with a VLA, a single policy receives the request as text. Knowing both approaches lets you choose based on the hardware you have.
Key concepts
✅ Module summary
To go further
- LeRobot documentation — commands and flags for the current version
- SmolVLA base model — starting point for fine-tuning
- LeRobot on GitHub — code, examples, and discussions
- robotica-real2sim (INEMA) — step-by-step guide for anyone with an SO-101 and simulated data
Next module
5.5 · Simulate before going real: the SO-101 twin in MuJoCo, real2sim, and the gap between simulation and reality.