Alvik: il nuovo robot educativo di Arduino
L’ecosistema Arduino è costantemente in evoluzione, come testimonia l’ultimo prodotto, oggetto di questo articolo, che rappresenta una vera e propria punta di diamante del Made In Italy per le discipline STEAM (Science Technology Engineering Art and Mathematics). Si tratta di qualcosa che rielabora in stile Arduino il concetto di Robotica Educativa rimanendo altamente compatibile con l’intero ecosistema al quale siamo abituati. In questo articolo vi illustrerò, in profondità, come è stato realizzato Alvik, quali idee, innovazioni e tecnologie ci sono alla base, il che ci consentirà di fare proprio questo oggetto. In un articolo successivo vedremo come programmarlo.
Arduino Alvik, oggetto dell’articolo, è stato gentilmente offerto da Arduino Le opinioni qui riportate sono del tutto personali e non influenzate in alcun modo dal fornitore del prodotto.
Indice dei contenuti
Alvik è Made In Italy
Mi piace sottolineare questa cosa perchè dopotutto è motivo di orgoglio. Lo stile prettamente italiano è riconosciuto all’estero soprattutto nel campo della moda, ma da anni, ormai, anche il nome di Arduino ha fatto in modo che la nostra nazione possa tenere la testa alta nella immensa folla di prodotti destinati alle persone che hanno la voglia di inventare, costruire ed innovare; prodotti che, a pensarci bene, sono cominciati a spuntare proprio dopo che Arduino ha lanciato l’idea in quell’ormai lontano 2005. Circuiti stampati e telaio sono interamente realizzati ed assemblati in Italia!
Come è fatto
Il telaio di Alvik è stato realizzato pensando all’utilizzo da parte dei bambini, quindi resistente ad urti, cadute e lanci (entro un certo limite ovviamente!). Il materiale utilizzato è un ABS fire-retardant ed è davvero ultra-resistente: ha il giusto grado di rigidità e le parti più sottili (come ad esempio i coperchi di chiusura dei motori) si piegano ma non si spezzano e ritornano alla loro forma originale senza deformazioni. Non è un risultato semplice da ottenere, questo, e denota una elevata bontà della mescola utilizzate. Abbiamo plastiche decisamente piacevoli al tatto e prive di ogni spigolosità o ruvidità: una scelta davvero ottima per i più piccoli considerando anche che, affianco alle ruote, sono stati previsti due fori compatibili con pin e stud di famosi mattoncini:
È scritto nella documentazione che i fori hanno compatibilità con i LEGO® Technic™, ma in reltà tutti i mattoncini LEGO® si agganciano tra loro, quindi se non avete elementi di connessione di questa serie, sottolineo che entrano, e si incastrano alla perfezione, anche i mattoncini piccoli 1×1 come potete vedere in questa foto:
Connettendo, quindi, determinati tipi di mattoncini ai fori si può, via via procedere a creare delle strutture più complesse che vanno a personalizzare il nostro robot: possiamo creare, ad esempio, delle strutture per alloggiare sensori.
Chi invece non vuole avere a che fare con i mattoncini ma preferisce invece un sistema più “massiccio” per aggiungere altri elementi al robot, può utilizzare viti M3 (mi raccomando: la lunghezza massima della parte che avvita non deve superare i 10mm altrimenti danneggiate il robot) sfruttando i due inserti presenti sopra e sotto ad ogni foro tipo LEGO®:
Ci tengo a sottolineare che gli inserti filettati, in fase di produzione dello chassis, non vengono inseriti dopo il processo di stampaggio (come faremmo noi, ad esempio, con la stampa 3D) ma sono proprio parte integrante della struttura! Ovvero gli inserti sono già inseriti nello stampo e la plastica viene colata intorno. Potrà sembrarvi, questa, una cosa banale ma vi assicuro che non lo è: la struttura realizzata in questo modo è molto più resistente, per contro richiede un processo di ingegnerizzazione più raffinato e che innalza non di poco i costi degli stampi. Purtroppo queste sono cose che i meno esperti non sempre riescono a cogliere e che giustificano i costi più elevati di un prodotto rispetto ad un altro a fronte di una migliore qualità e durabilità.
Altra cosa che mi è piaciuta molto, e che tengo ad evidenziare, è il sistema utilizzato per assicurare i motoriduttori alla struttura. I motoriduttori alloggiano in un apposito vano interno dello chassis, semplicemente “a pressione”, chiusi da un coperchio rimovibile che si aggancia a scatto dalla parte inferiore del telaio:
Questa cosa ci consentirebbe anche di fare modifiche e sostituire i motoriduttori con altri. Per riuscire a togliere il coperchio dei motoriduttori, però, consiglio di rimuovere prima la mainboard nella parte superiore (sono solo 4 viti) e poi allargare con delicatezza uno dei due ganci laterali del coperchio, questo per evitare di graffiare le plastiche e perchè, ad ogni modo, non riuscirete a sganciare il gancio del coperchio da sotto.
Il motoriduttore è davvero minuscolo: prodotto dalla TT motors, tipo GM12 (per gli amanti delle sigle, la sigla completa è GM12-N20VA-08255-150-EN), ingranaggi in metallo a vista con un rapporto di riduzione di 1:150, encoder in quadratura ad effetto Hall. Questo motoriduttore permette ad Alvik di raggiungere una velocità massima di 13cm/sec (circa 96rpm a vuoto con un assorbimento massimo di 90mA). I motoriduttori vengono alimentati a 5V dal boost converter e pilotati da un ponte H tipo MAX22211.
Anticipo che gli encoder, così come i motori e tutto il resto, sono gestiti in maniera indipendente da un altro microcontrollore pre-programmato sulla scheda madre (hackerabile dai più esperti), per cui non ci dovremo curare, nei nostri programmi, di andare, ad esempio, a leggere gli encoder ed eseguire il PID per fare in modo che il robot proceda in linea perfettamente retta.
Il sistema è alimentato da una cella LiPo in formato 18650, accessibile togliendo il coperchio con il logo di Arduino nella parte inferiore:
In realtà togliere la batteria da sotto può risultare un po’ difficoltoso senza avere attrezzi che possano permettere di fare leva sulla cella per estrarla, per cui se avete necessità, per qualche motivo, di sostituire la batteria e se non riuscite ad estrarla senza fare danni: togliete la mainboard per avere più libertà di movimento.
La Mainboard (in realtà si chiama, più propriamente, Alvik Carrier Board) fa da “coperchio” per il telaio e ha tutta la componentistica rivolta verso l’interno per proteggerla:
Adoro il sistema con cui hanno pensato di connettere l’Arduino Nano ESP32 alla mainboard: come vedete dalla foto superiore gli header si trovano nella parte inferiore della mainboard (quindi non sono visibili all’esterno del robot), e dall’altra parte della board ci sono i fori passanti:
La Arduino Nano ESP32 (che, se non si era capito, è già inclusa nel kit, preprogrammata con l’interprete micropython) è innestata proprio da questo lato, e quindi accessibile/rimovibile/swappabile dall’esterno senza smontare tutto:
Questa cosa, chiaramente, apre la possibilità ad innumerevoli altre personalizzazioni: nulla ci vieta di togliere l’Arduino Nano ESP32 e fare un adattatore per montare qualche altro tipo di scheda di sviluppo.
Al centro della mainboard, come anticipato, c’è un STM32F411RCT6 che svolge tutto il lavoro “pesante”: comunica con tutti i componenti onboard ed esterni collegati alle porte di espansione, prepara le risposte e le fornisce all’ESP32 su richiesta da nostro programma.
Il firmware precaricato su questo microcontrollore è aggiornabile via micropython tramite un’apposita procedura ed è anche disponibile il codice sorgente. Ho scritto un articolo apposta che spiega sia come fare ad aggiornare il firmware sia come aggiornare l’interprete micropython a bordo dell’Arduino Nano ESP32.
Sulla mainboard sono presenti, nella parte a vista, 7 tasti touch (4 frecce direzionali + tasto centrale e due tasti “ok” e “cancella” – questi sono controllati da un chip a parte che comunica con l’STM32 via I2C) e due LED RGB alle estremità (DL1 e DL2) celati sotto la piastra.
Nella parte inferiore è possibile accedere ai due tasti Boot e Reset dell’ESP32 al centro dei quali sono presenti la IMU e il sensore di Luce, di questi sensori ve ne parlo in dettaglio più in basso.
Alla mainboard è agganciata una seconda scheda che va a costituire la “faccia” di Alvik:
Ritengo interessante il fatto che questa board sia staccabile perchè apre ulteriori possibilità agli smanettoni. Al di sopra della “bocca” troviamo un piccolo concentrato di tecnologia: un sensore TOF (di cui vi parlo più in basso) e, al di sotto, 3 coppie trasmettitore/ricevitore IR che fungono da sensore di linea.
Come avete potuto notare, Alvik è modulare: ogni parte può essere tolta ed eventualmente sostituita. Personalmente è questo il tipo di design che preferisco perchè mi permette di fare in modo che il prodotto sia davvero mio in tutti i sensi: posso smontarlo e scoprire come è fatto o come funziona, posso sostituire pezzi o crearne di miei.
Connessioni esterne
Sono presenti, nella parte posteriore del robot, alle estremità due connettori tipo QWIIC (Sistema inventato da Sparkfun per connettere in parallelo componentistica che comunica in I2C). Affianco ai connettori QWIIC, verso l’interno ci sono due connettori Grove I2C (Sistema inventato da Seeed Studio non solo per I2C, ma che, in questo caso viene utilizzato solo per I2C) e infine, al centro, due connettori standard con passo da 2.54mm per 2 servocomandi:
Il pinout dei connettori è il seguente:
Tutti i sensori e tutti gli altri collegamenti, come già detto, passano sempre attraverso l’STM32 sulla main board, quindi nessun sensore collegato qui comunica direttamente con l’Arduino Nano ESP32.
Sulla destra della board è presente il tasto on/off per accendere il robot.
Sensori a bordo
Faccio una breve panoramica dei sensori montati a bordo di Alvik, con le relative funzioni da utilizzare nel codice giusto per farvi capire cosa saremo in grado di fare/come vengono utilizzati. I nomi delle funzioni sono gli stessi sia se programmate Alvik in Micropython, sia se lo programmate in Arduino IDE. Chiaramente alcune funzioni vanno utilizzate in maniera leggermente diversa se state programmando in uno piuttosto che in un altro linguaggio, questo perchè in Micropython una funzione è in grado di restituire più di un valore mentre in C una funzione restituisce al massimo un solo valore, per cui, per una funzione che prevede, ad esempio, l’output di 3 valori (tipo la lettura del giroscopio per dirne una), in Micropython avrete i 3 risultati come ritorno dalla funzione, mentre in C bisognerà passare alla funzione 3 variabili, come argomenti della funzione stessa, in cui volete che i dati siano salvati.
La funzione viene invece utilizzata in maniera uguale in tutti e due i linguaggi se restituisce un unico valore.
Per chiarire quello che ho appena scritto e non fare confusione faccio un esempio: nel caso della funzione che restituisce i valori del giroscopio, in Micropython riceveremo i 3 risultati direttamente dalla funzione senza curarci di dichiarare il tipo, per cui scriveremo:
gx, gy, gz = alvik.get_gyros() |
Mentre, se programmiamo in C, da Arduino IDE, la stessa cosa verrà scritta come:
float gx, gy, gz; alvik.get_gyros(gx, gy, gz); |
Pulsanti Touch
I 7 pulsanti touch posti sulla parte superiore del robot vengono gestiti da un controller touch capacitivo prodotto da Microchip: AT42QT2120 Si tratta di un controller molto efficiente e responsivo con consumi bassi. Le funzioni utilizzate per ottenere lo stato dei pulsanti sono:
- get_touch_any() – questo per rilevare se sia stato premuto un pulsante qualsiasi
- get_touch_ok(), get_touch_cancel(), get_touch_center(), get_touch_up(), get_touch_left(), get_touch_down(), get_touch_right()
Le funzioni restituiscono true se il relativo pulsante è stato premuto.
In più ci sono le funzioni che permettono di associare una callback ai pulsanti touch in modo da non controllarli continuamente e quindi renderli in qualche modo indipendenti dal resto del codice. Le funzioni hanno tutte la forma on_touch_[tasto]_pressed(callback, argomenti) dove sostituite [tasto] con ok, cancel, center, up, left, down, right – chiaramente callback è il nome della funzione che volete sia richiamata in automatico all’evento di pressione del tasto e argomenti è una lista di argomenti che eventualmente volete passare a suddetta funzione.
IMU
La IMU è prodotta da ST ed è una LSM6DSOX. Si tratta di una IMU a 6 assi (3 assi accelerometro, 3 assi giroscopio). Da codice è possibile utilizzare varie funzioni per ottenere i dati da un singolo sensore o in gruppo:
- get_imu() restituisce i valori ax, ay, az, gx, gy, gz ovvero valore di accelerazione (a), lungo i 3 assi e valore di accelerazione angolare (g) sui tre assi
- get_orientation() restituisce i valori degli angoli r, p, y ovvero roll (rollio – rotazione lungo l’asse longitudinale), pitch (beccheggio – rotazione lungo l’asse trasversale), yaw (imbardata – rotazione lungo l’asse normale)
- get_accelerations() restituisce i valori ax, ay, az ovvero i valori di accelerazione lungo i 3 assi
- get_gyros() restituisce i valori gx, gy, gz ovvero i valori di accelerazione angolare lungo i 3 assi
Se siete a digiuno di questi concetti relativi alle IMU, vi consiglio di leggere questo articolo che ho scritto anni fa per spiegare come funzionano gli accelerometri, all’interno del quale viene anche accennato in cosa differiscono rispetto ai giroscopi.
Sensore di luce
Il sensore di luce è prodotto dalla Broadcom ed è un ADPS-9960 che è capace di restituire letture per luce ambiente, luce RGB, fare da sensore di prossimità e riconoscimento gesture. In realtà di default, trovandosi il sensore al di sotto del robot (perchè è stato pensato per fargli riconoscere “etichette di colore” durante il movimento), la funzionalità di gesture non è attualmente prevista. Le funzioni da utilizzare sono:
- color_calibration(background) – dove background assume i valori “white” (default) o “black” – serve ad eseguire la calibrazione del sensore e fargli quindi riconoscere correttamente il colore bianco e il nero. Il sensore è già calibrato di fabbrica ma questa operazione la potete fare anche voi tranquillamente, qui è esposta la procedura.
- get_color(color_format) – dove color_format assume i valori “rgb” (default) o “hsv” – restituisce i valori r, g, b o h, s, v (float da 0 a 1) del colore letto
- set_illuminator(bool) – serve ad accendere uan coppia di led bianchi ultraluminosi posti affianco al sensore per illuminare la superficie di cui leggere il colore
L’illuminatore ha inoltre un comportamento davvero intelligente: internamente viene sfruttata la funzione di rilevamento di prossimità, per cui se l’illuminatore è acceso ma non vengono rilevati oggetti (ad esempio si prende il robot e lo si alza dalla superficie) i led vengono spenti automaticamente.
ci sono poi due funzioni non associate direttamente al sensore (nel senso che non operano sul sensore) e che sono:
- rgb2hsv(r,g,b) – che serve a convertire un valore r, g, b in h, s, v (float)
- hsv2label(h, s, v) – restituisce un nome comune (ovvero un’etichetta, esempio green , red, white, ecc oppure undefined) fornendo i valori h, s, v (float)
TOF
Questo è il sensore più interessante di tutti perchè si tratta di qualcosa di davvero diverso da ciò che troviamo normalmente sui robot in questa fascia di prezzo. Si tratta di un prodotto della ST: VL53L7Cx
Comunemente per il rilevamento ostacoli siamo abituati a trovare un classico sensore ad ultrasuoni, su Alvik abbiamo invece un più raffinato ed evoluto Sensore a tempo di volo (TOF = Time Of Flight) ovvero che misura il tempo di andata e ritorno di un raggio laser: il sensore annota il tempo di invio del raggio e registra il momento in cui rileva il riflesso, facendo calcoli a ritroso risale alla distanza. La tecnologia utilizzata da questo sensore si chiama FlightSense™ ed è una tecnologia brevettata da ST (a questo link trovate una presentazione).
Il sensore è costituito da due parti:
- Un emettitore laser a 940nm, chiamato nello specifico VCSEL (Vertical Cavity Surface Emitting Laser) che emette raggi disegnando un fascio quadrato da 60°x60° alla velocità massima di 60Hz
- Un ricevitore composto da un array di 64 elementi disposti in una matrice 8×8. I singoli elementi dell’array sono chiamati SPAD (Single Photon Avalance Diode)
Capirete che, essendo il ricevitore composto da una matrice, e scansionando il laser un’area quadrata è possibile analizzare più settori nel campo visivo e quindi, addirittura, generare immagini che riflettono le asperità rilevate (allo stesso modo di come fa un sensore Lidar). In realtà il sensore in questione ha si una matrice 8×8, ma può funzionare, per semplificare il codice, anche in modalità 4×4 (vengono raggruppate 4 celle, 2×2, per linea e viene restituita la media). Su Alvik il sensore viene proprio utilizzato in modalità 4×4 (questo si evince dal sorgente a bordo dell’STM32, nella funzione di inizializzazione del sensore).
Su Alvik questo sensore TOF viene quindi utilizzato in maniera più semplice, 4×4 appunto, per rilevare ostacoli posti frontalmente ma anche lateralmente, sopra e sotto. Alcuni però, facendo una modifica al codice sull’STM32 (che, ricordo, fa da interfaccia tra periferiche varie ed ESP32) sono riusciti ad utilizzarlo proprio come “sensore di immagine” in modalità 8×8 (anche se la risoluzione è molto bassa).
A questo link potete vedere che un utente ha installato MicroROS sull’Arduino Nano ESP32, ha modificato il firmware sull’STM32 della Carrier Board ed è in grado di ricevere il dato della matrice 8×8 dal sensore TOF. Purtroppo, a quanto pare, non ha condiviso ulteriori informazioni oltre al video.
Per ultimo: il sensore in questione riesce a rilevare oggetti fino a 3,5 metri di distanza. Prima di utilizzare il sensore dovete ricordarvi di rimuovere la pellicola protettiva che copre la superficie del sensore per proteggerlo dalla polvere:
Le funzioni utilizzate per leggere il TOF sono:
- get_distance(unit) – unit serve ad impostare in quale unità di misura devono essere restituiti i valori di distanza e può assumere i valori “cm” (default) o “mm”, “m”, “inch” = “in” – restituisce i valori distanza in questa sequenza: left, left_center, center, right_center, right ovvero rispettivamente a 45° a sinistra, 22° a sinistra, centro, 22° a destra, 45° a destra (per fare la lettura su 5 punti vengono utilizzate le due righe di SPADs centrali e spostati i gruppi di 2×2 sulla matrice da 8×8)
- get_distance_top(unit) – distanza dall’oggetto a 45° verso l’alto (in pratica viene letta tutta la prima riga di SPADs e restituita la media)
- get_distance_bottom(unit) – distanza dall’oggetto a 45° verso il basso (viene letta tutta l’ultima di SPADs e restituita la media)
Sensore di linea
E’ costituito, come abbiamo visto, da 3 coppie di emettitore/ricevitore IR da utilizzare per realizzare un’applicazione in cui Alvik segue una linea disegnata a terra. Questa sensor bar ha una sola funzione:
- get_line_sensors() – che restituisce i 3 valori left, center, right
La funzione restituisce numeri interi a 16bit: più riflettente è la superficie rilevata, più il numero restituito è basso, al contrario più ci si avvicina al nero, più il numero diventa grande. Per fare un line follower ho constatato che è necessario che la striscia nera da seguire sia larga almeno 2 cm, che è più o meno lo spazio occupato da due led infrarossi successivi: noterete infatti che ci sono 3 ricevitori e 4 led infrarossi, in pratica ogni ricevitore ha un led IR alla sua destra e alla sua sinistra.
Gestione alimentazione
Viene utilizzato un MAX17332X22 per gestire la carica e un LTC4360CSC8-2 come protezione da sovratensione e inversione di polarità.
Il robot viene ricaricato tramite la porta USB dell’Arduino Nano ESP32 mentre l’interruttore è in posizione OFF: in questo stato l’ESP32 comunque funziona (ovviamente) ma riceve un segnale che il robot è OFF ed è comunque in grado di restituire, via Micropython, il livello di carica della batteria: dal REPL di micropython, quando il robot è collegato in carica, potete vedere la percentuale di carica:
Via software possiamo ottenere la percentuale di carica mediante la funzione get_battery_charge()
Links
- Acquisto Arduino Alvik
- Manuale Alvik
- Tutta la documentazione su Alvik
- Corso online sulla programmazione di Alvik in MicroPython
- Github Libreria Alvik Micropython
- Github Arduino Lab for Micropython
- Github Alvik Carrier Board
- Download ultima release Arduino Lab for Micropython
- Github Libreria Alvik per Arduino IDE
- API Arduino Alvik
- Documentazione Arduino Nano ESP32
- Cheatsheet Arduino Nano ESP32
- Datasheet Sensore di Luce APDS9960
- Datasheet IMU LSM6DSOX
- Datasheet TOF VL53L7CX