Robot learning, mani in pasta
Quello che non ti dicono su come far afferrare le cose a un braccio robotico
Stefano Maestri
Software Engineer & Smanettone di Robotica

Quattro atti, un braccio robotico economico e tanto debugging
Hardware LeRobot + SO101, assemblaggio, telecamere e pipeline di registrazione.
Cinque bug reali: policy su CPU, action chunking, causal confounding, deriva di calibrazione, distribution shift.
Profiling, diagnostica, gli strumenti che abbiamo costruito e le metriche di training che contano davvero.
Cosa ha funzionato, cosa no, e la via più semplice con Cyberwave.
LeRobot, un braccio SO101 e un compito ingannevolmente semplice
Lo stack: una libreria open-source e un braccio da 300 €
"Afferra il blocco rosso e mettilo nella scatola."
Sembra semplice. Non lo era.
Cosa diceva internet che sarebbe stato il robot learning
YouTube: robot che piegano il bucato, cucinano uova, smistano scatole in magazzino.
Paper: "una pipeline di training lineare."
README di LeRobot: "semplice, accessibile, allo stato dell'arte."
Sorprese di assemblaggio a cui nessun tutorial ti prepara
Sorprese di assemblaggio a cui nessun tutorial ti prepara
Sorprese di assemblaggio a cui nessun tutorial ti prepara
Sorprese di assemblaggio a cui nessun tutorial ti prepara
Una staffa della spalla stampata in 3D aveva un difetto di adesione tra gli strati e si bloccava sotto carico. Ristampata con riempimento maggiore.
I servi sono arrivati con versioni di firmware diverse. Ogni motore ha dovuto essere riflashato alla stessa versione per funzionare insieme.
Lo strumento di debug FD per il firmware STS3215 gira solo su Windows. Utenti Linux: trovatevi una VM.
Il braccio leader si muove. Il follower segue.
Muovi il polso e una macchina a quindici centimetri ti rispecchia in tempo reale. È, davvero, magico.
Benvenuti nel Linux embedded.
Sembra lineare. Ci girerai dentro decine di volte.
Tre telecamere, un bus condiviso, banda insufficiente
| Telecamera | Posizione | Risoluzione |
|---|---|---|
| front | Vista dall'alto | 640×480 @ 30 Hz |
| right | Angolo laterale | 640×480 @ 30 Hz |
| wrist | Montata sul gripper | 640×480 @ 30 Hz |
Tutte e 3 le telecamere sul Bus USB 2.0 001 condividono 480 Mbps. Tre stream raw richiedono ~830 Mbps.
Quando Wayland rompe in silenzio i comandi da tastiera
Wayland blocca lo snooping globale della tastiera. Il listener di pynput fallisce in silenzio — niente comandi con le frecce durante la registrazione, e nessun errore.
# attiva la modalità interattiva lerobot-record \ --robot.type=so101_follower \ --dataset.reset_time_s=-1 \ --dataset.single_task="Grab the red block" # tra un episodio e l'altro [INTERACTIVE RESET] Episode 3 recorded. Keep scene and record next? [Y/n/q]: y # durante la registrazione # premi Ctrl+\ per terminare l'episodio [INTERACTIVE] Episode end requested via SIGQUIT
50 tentativi per afferrare un blocco rosso
…poi rifallo altre 49 volte.
Cinque bug che non hanno mai stampato un solo messaggio di errore
Nessun errore. Nessun warning. Solo un robot che non funziona.
Sintomo: GPU al 2%. Il training "funziona", la policy è spazzatura.
Soluzione: policy.to("cuda") esplicitamente; controlla il device dei parametri.
Sintomo: Perfetto in replay, fallisce sul robot reale.
Soluzione: Togli il braccio leader dall'inquadratura.
Sintomo: Funziona alle 10, fallisce alle 15. Stesso codice.
Soluzione: Registra con diverse condizioni di luce; abilita la color augmentation.
Loss 0.008. Sembra ottima. Manca il bersaglio ogni volta.
Policy ACT. 1 ora su GPU. Loss 0.008. Sembra ottima.
Il robot va verso il blocco… e manca. Non a caso — sistematicamente, sempre 6–7 cm a destra.
Forse il modello non è abbastanza grande? Provo ACT → manca. Provo Pi0.5 → manca. Provo SmolVLA → manca.
Scegliere il modello giusto per un setup economico da 16 GB
80M parametri · specialista
chunk_size=100, n_action_steps=100
500M parametri · condizionata dal linguaggio
Prompt testuale + encoder visivo.
3B parametri · foundation model
chunk_size=50, backbone pre-addestrata.
Bug #2
Quando "GPU inattiva il 90% del tempo" è in realtà corretto
chunk_size = 100, n_action_steps = 100
1 forward pass = 100 azioni = 3,3 s di movimento a 30 Hz. La GPU gira una volta, poi riproduce le azioni in cache.
I warning sporadici "running slower than requested fps" sono jitter temporale — non un bug.
Bug #3
La policy ha imparato a barare
Bug #3
La policy ha imparato a barare
Il braccio leader era visibile nell'inquadratura durante la registrazione. La policy ha imparato una scorciatoia: seguire il braccio leader, non il blocco.
Il braccio leader non c'è più. La policy vede una scena sconosciuta e produce movimenti casuali ed erratici.
Riposiziona le telecamere così il braccio leader non sia mai inquadrato. Poi ri-registra l'intero dataset.
Uno script di 40 righe ha trovato in 3 secondi ciò che non avevo trovato in 2 settimane
Bug #4 · Momento eureka
La causa radice di tutto, nascosta in un solo giunto
# compare_leader_follower.py Joint Mean|diff| Max|diff| ──────────────────────────────── shoulder_pan 1.2° 3.1° shoulder_lift 0.9° 2.4° elbow_flex 1.1° 2.8° wrist_flex 17.5° 22.3° wrist_roll 0.7° 1.9° gripper 2.3° 4.1° WARNING: wrist_flex exceeds 5° threshold!
| Metrica | Prima | Dopo |
|---|---|---|
| offset wrist_flex | 17,5° | 0,85° |
| Precisione del gripper | ±6–7 cm | ±0,3 cm |
| Successo della presa | 0% | 80% |
Ogni episodio ha insegnato al robot un mondo fisicamente sbagliato
Stessa policy. Stesso codice. Stessi iperparametri.
Bug #5
Training con la luce del mattino, deployment nel pomeriggio
Una policy addestrata con la luce del mattino fallisce nel pomeriggio. Le ombre si spostano, la temperatura colore cambia, il bilanciamento del bianco deriva.
# abilita le trasformazioni integrate lerobot-train \ --training.image_transforms.enable=true # augmentation di default brightness : (0.8, 1.2) contrast : (0.8, 1.2) saturation : (0.5, 1.5) hue : (-0.05, 0.05) # off per i compiti sul colore sharpness : jitter
Cosa significa davvero l1_loss per il tuo robot
Cosa significa davvero l1_loss per il tuo robot
l1_loss × servo_range (180°) = errore per giunto → si propaga lungo la catena cinematica → errore del gripper.
~600 righe di Python che hanno cambiato tutto
Statistiche di deriva per giunto tra i comandi del leader e le posizioni del follower, su ogni episodio.
Ha trovato la deriva di 17,5° su wrist_flex.
Segnala gli episodi anomali in base a fluidità della traiettoria, tempistica del gripper e metriche di completamento.
Ha individuato il 12% di episodi corrotti.
Controllo statico di calibrazione — legge le posizioni dei giunti dal vivo, leader vs follower, in tempo reale.
Verifica la calibrazione prima di registrare.
Successo e fallimento — perché contano entrambi
Blocco orizzontale, centrato, buona luce.
Blocco ruotato, angolo di 45°, prima della correzione sugli esempi difficili.
Cosa ha funzionato, cosa no, e la via più semplice
Ogni sintomo nascondeva la sua causa radice due livelli più in basso
La via più semplice
Cyberwave — scrivi Python una volta, eseguilo in simulazione e su hardware reale
| Sfida | Noi | Cyberwave |
|---|---|---|
| Calibrazione | 3 settimane | auto-rilevata |
| Setup telecamere | 2 giorni | pre-configurato |
| Infra di training | 1 giorno | GPU in cloud |
| Diagnostica | 1 settimana | dashboard |
Nessuno dei due è sbagliato — servono obiettivi diversi
Domande? Applausi liberi.
"Costruito con amore, frustrazione e una deriva di calibrazione di 17,5°."
maeste.it