Un tutorial sull’impiego di Micro:Rover con vari esempi e collegamenti con la Turtle Geometry, con LibreLogo e Turtle Python. I codici sono esportabili e subito utilizzabili per essere ulteriormente sviluppati.
Premessa
Quando nei laboratori impiego la tecnologia non dimentico mai di proporre agli studenti le esplorazioni del mondo fisico, o matematiche con mezzi fisici semplici. Un esempio per tutti quello di Emma Castelnuovo con L’officina matematica — ragionare con i materiali (La Meridiana, Bari, 2008) e altri suoi contributi analoghi. Esplorazioni che non vedo contrapposte a quelle che si possono fare con dispositivi come la scheda micro:bit, Arduino e similari, che un insegnante preparato e non troppo costretto in un ambito disciplinare dovrebbe invece integrare intelligentemente con quelle convenzionali.
In sei anni di insegnamento a Scienza della Formazione Primaria ho potuto scoprire le potenzialità didattiche delle tecnologie — insegnare è il miglior modo per imparare — ma ho anche dovuto constatare la distanza che c’è fra coloro che, giustamente, insistono su metodi e materiali del passato e coloro che tentano di proporre tecnologie più recenti, non in alternativa ma a complemento. Un complemento che trovo necessario perché ripensando al mio percorso di ricercatore nel campo scientifico, trovo che avrei tratto grande giovamento da esplorazioni di questo genere. Che non vuol dire abbandonare attività più tradizionali.
Dopo sei anni, dove ho avuto a disposizione un laboratorio da 3 crediti che dà un’idoneità, mi sentirei pronto ad immaginare un insegnamento da 12 crediti, magari suddivisi in due semestri, da svolgere in spazi con arredi modulari per integrare in modo ottimale teoria e pratica.
Ma siccome l’anno prossimo terminerà il mio servizio all’università, dovrò trovare altri contesti. O altre vite.
Questo è il tutorial che avevo promesso nel post di preparazione all’ultimo laboratorio di tecnologie didattiche per Scienze della Formazione Primaria che si è tenuto presso gli Orti Dipinti (Borgo Pinti 76, Firenze) il 10 novembre scorso. In realtà questo post lo dedico al robot che non abbiamo fatto in tempo ad utilizzare, fra quelli previsti:
Micro:Rover è l’unico fra questi ad avere le tipiche funzionalità di un robot. È un po’ più complesso da usare ma è caratterizzato da un eccezionale rapporto fra potenzialità didattiche e costo. Può essere assimilato ad uno dei robot Lego EV3 ma costa quasi dieci volte meno. Con un minimo di preparazione da parte degli insegnanti può essere inserito anche in attività alla scuola primaria. In passato ho collaborato in un’attività svolta in alcune classi di III primaria a Scuola-Città Pestalozzi, usando robot Lego della serie EV3 (oltre €500…) molto simili a Micro:Rover per quanto riguarda le funzionalità. È stata un’ottima esperienza (vediamo in seguito). Allo stesso tempo il robot si presta ad attività assai più complesse nella scuola secondaria.
Il Micro:Rover


Micro:Rover è basato sulla scheda BBC micro:bit, che può anche essere acquistata a parte (€25) e utilizzata per una miriade di altri progetti — la vedremo in un’altra occasione.
Quando si apre la scatola si trovano tre elementi: il carrello, la scheda di controllo e il sensore a ultrasuoni di distanza, più un cavo USB. La batteria agli ioni di litio da 3.7V tipo 18650 va acquistata a parte. Il montaggio è banale. La scheda di controllo inclusa, il cervello del robot, è la BBC micro:bit. Una scheda dotata di numerosi sensori che può essere programmata con linguaggio a blocchi, Javascript o Python per le esperienze più disparate. Può essere comprata a parte (€13).
Ma una volta montato che fare?
Come succede spesso oggi, nella scatola non ci sono delle vere istruzioni, eccetto per un libretto di poche pagine che mostra cosa fare quando si alimenta la scheda micro:bit (da sola) per la prima volta. Ma sul Micro:Rover nulla. C’è solo un’indicazione sulla scatola:
Get Tutorial:
http://freenove.com/fnk0037
Dal link si scarica un file zip che, una volta aperto, crea una cartella “Freenove_Micro_Rover” ma contenente alcuni tutorial in PDF e vari esempi caricabili nel robot suddivisi in due sottocartelle: quelli da caricare nel sistema a blocchi e quelli in Python. Le cose che si possono fare sono svariate e ci sono modi diversi per attuarle. Bisogna avere le idee chiare oppure essere disposti a cercare la propria strada. Completamente diverso dalla BeeBot e altri robot simili, con i quali il percorso è quasi subito chiaro e univoco.
Comunque ha abbastanza senso aprire semplicemente il primo file: tutorial.pdf. Dopo le semplici istruzioni per il montaggio la prima cosa che viene proposta è quella di scaricare un’app Android o IOS che consente di comandare il robot con i movimenti della mano (comunica via bluetooth). C’è anche la possibilità di attivare i sensori di prossimità, luminosità o tracciamento di linee. Siamo comunque nell’ambito della macchinina radiocomandata, che qui non ci interessa.
Ci interessa invece sfruttare il robot per sviluppare pensiero… computazionale, matematico, scientifico…
Il valore didattico lo troviamo in primo luogo nella scrittura del software per manovrare il robot, attivando un processo del tipo seguente.
- Definizione obiettivo
Voglio far compiere al robot una certa azione, o sequenza di azioni, ivi comprese eventuali reazioni ai segnali ricevuti dai sensori: prossimità, luce, inclinazione, orientazione, tracciamento sul suolo… - Costruzione teoria
Immagino traiettorie geometriche — segmenti retti, curve — e parametri fisici — tempo occorrente per superare un certo tratto, velocità… — che collegati opportunamente fra loro ipotizzo sortiscano l’effetto voluto - Scrittura codice
Scrivo il codice che interpreta le azioni suddette - Verifica teoria
Scarico il software nel robot e lo provo - Ripetizione ciclo
Torno al punto 2. se l’effetto ottenuto non corrisponde a quello immaginato — succede quasi sempre così.
In secondo luogo, se riusciamo a coinvolgere i ragazzi in tale processo, possiamo cercare di far emergere un’analogia dei movimenti del robot nel mondo reale con quelli della tartaruga sul foglio virtuale, sullo schermo. Se ci riesce è un passo importante perché quello di stabilire corrispondenze fra mondi diversi è uno dei fondamenti del pensiero matematico, o del pensiero scientifico tout court.
Come si programma il Micro:Rover?
Micro:Rover si può programmare in vari modi. La prima proposta del tutorial è quella di usare MakeCode che si presenta come una classica interfaccia di programmazione a blocchi ma offre anche un passaggio immediato a Python o JavaScript. Questo significa che in qualsiasi fase (entro certi limiti) di scrittura di un programma si può passare dalla versione a blocchi ad una delle due versioni testuali. Ciò in armonia con una tendenza attuale crescente a facilitare la transizione dal linguaggio a blocchi a quello testuale. Ciò essenzialmente perché gli studiosi si sono accorti che nelle versioni a blocchi i ragazzi è bene che ci stazionino poco e, del resto, sono proprio loro a voler passare alla “programmazione vera” appena vengono presi dal meccanismo.
L’alternativa in versione web è l’editore Python micro:bit.
Ambedue questi sistemi sono servizi web, che non necessitano di scaricare un software sul proprio computer. Vanno bene se si ha una connessione internet e se i progetti non sono troppo complessi. Più professionale, per così dire, è l’uso dell’editore Python Mu, che va invece scaricato sul computer. Questo si può usare anche se si è scollegati da internet ed io l’ho trovato molto più comodo se si deve editare il software a ripetizione, come sempre succede quando si inizia a lavorarci per davvero.
Le attività che ho citato all’inizio, svolte in alcune classi di III primaria a Scuola-Città Pestalozzi con robot Lego, riguardavano il conseguimento di una serie di obiettivi:
- raggiungere in linea retta un birillo posto ad una certa distanza. L’attività prevedeva la misura della distanza del birillo con un metro e la taratura della velocità del robot: per quanto tempo devo far girare i motori per coprire una certa distanza?
- raggiungere il solito birillo, girarci intorno e tornare alla base
- eseguire una gimcana intorno a una fila di birilli e ritorno
- immaginare e provare a conseguire un obiettivo a piacere
Molti diranno: troppo difficile! No, preparando perbene i bambini, assistendoli adeguatamente e dando loro tempo si sono dimostrati perfettamente all’altezza. Non sono mancate criticità, principalmente ascrivibili al fatto che avevamo fatto un programma troppo denso delle attività. Penso che con un minor numero di obiettivi, o più tempo dedicato alle attività, ne avrebbero tratto profitto un maggior numero di bambini.
Proviamo ora a rileggere quelle attività utilizzando il robot Micro:Rover.
Esempi
Riporto i codici in Python. Ho provato a realizzarne alcuni anche con i blocchi ma è disastrosamente più lento e io non ho tutto questo tempo a disposizione. I codici qui sotto funzionano sia nell’editore Python Mu in locale che con l’editore Python micro:bit da usare nel web.
Avanzamento in linea retta
Questo esempio può essere impiegato per riprodurre la succitata attività numero 1. che avevamo fatto presso Scuola-Città Pestalozzi:
1. raggiungere in linea retta un birillo posto ad una certa distanza. L’attività prevedeva la misura della distanza del birillo con un metro e la taratura della velocità del robot: per quanto tempo devo far girare i motori per coprire una certa distanza?
Con questo codice il robot è programmato in modo da avanzare di una distanza che può essere determinata mediante i tasti disponibili sulla scheda micro:bit. Questa ha due tasti, denominati A e B, che sono essere programmati per una varietà di azioni.

L’idea è questa: la distanza da percorrere viene determinata dal numero di pressioni del tasto A, ogni pressione corrisponde a 10 cm. Con il tasto B si fa partire il robot. Ovviamente il valore di 10 cm corrispondente a ciascuna pressione del tasto A può essere cambiato nel codice. Le righe che iniziano con il carattere # sono commenti che aiutano a capire il funzionamento del programma e che non vengono eseguite.
1 # distance.py
2 # quando si preme il tasto B il robot avanza
3 # di un tratto proporzionale al numero di
4 # precedenti pressioni del tasto A
5 # Ogni pressione aggiunge circa 10 cm
6
7 # Importo la libreria per gestire il robot
8
9 from Freenove_Micro_Rover import *
10
11 # Definisco un oggetto robot
12
13 Rover = Micro_Rover()
14
15 # Definisco il comando di avanzamento forward(st)
16 # dove d è la distanza da percorrere
17
18 def forward(d):
19 Rover.motor(50, 50)
20 sleep(105 * d)
21 Rover.motor(0, 0)
22 sleep(1000)
23
24 # Programma principale:
25
26 display.show("?")
27 while True:
28 if button_b.is_pressed():
29 count = button_a.get_presses()
30 d = count * 10
31 forward(d)
Descrivo nel dettaglio questo primo esempio. Nei prossimi daremo più cose per scontate. L’istruzione N.9 :
from Freenove_Micro_Rover import *
serve a caricare nel programma quello che serve per controllare il robot, inclusa la scheda micro:bit. L’istruzione N.13
Rover = Micro_Rover()
genera un nome per il robot, Rover, che viene usato per inviare i comandi.
Questi sono azioni preparatorie. Ora viene la sezione dove definiamo i comandi che ci servono, esattamente come facciamo con il costrutto TO...END
in LibreLogo (chi non sa cos’è LibreLogo può visitare il MOOC Coding a scuola con Software Libero oppure può scaricare il Piccolo manuale di LibreLogo). In questo esempio ne definiamo solo uno, quello che in LibreLogo è FORWARD 100
, che mandava la tartaruga avanti di 100 punti. Ma qui il robot non sa di poter essere una tartaruga, e noi glielo insegniamo così, con le istruzioni N.18-22:
18 def forward(d):
19 Rover.motor(50, 50)
20 sleep(105 * d)
21 Rover.motor(0, 0)
22 sleep(1000)
L’istruzione N.19
19 Rover.motor(50, 50)
aziona i due motori delle ruote, alla stessa velocità. I valori numerici dati nei comandi al robot devono essere determinati empriricamente con un po’ di tentativi. In questo caso 50 è risultato il valore appropriato per la rotazione delle ruote. Ovviamente, valori maggiori fanno andare il robot più velocemente e viceversa. Ma una volta messo in moto, come fare a fermarlo quando vogliamo noi? C’è un’istruzione apposta ma dobbiamo aspettare che sia passato il tempo giusto e questo lo facciamo così:
20 sleep(105 * d)
In questo modo diciamo alla scheda micro:bit di aspettare un certo tempo, senza interferire con il robot. Ovviamente, quanto maggiore è il percorso che vogliamo fare tanto maggiore dovrà essere il tempo di attesa. Ecco perché troviamo la distanza da percorrere, d, nell’argomento di sleep, con un fattore moltiplicativo, che qui abbiamo fissato empiricamente al valore 105, ma che è ovviamente collegato alla velocità di rotazione. Passato questo tempo, possiamo dire ai motori di fermarsi:
21 Rover.motor(0, 0)
Che è lo stesso comando dell’istruzione N.19 ma dove le velocità di rotrazione delle ruote sono fissate a 0. Infine, con l’istruzione N.21, attendiamo un secondo (1000 millisecondi) prima di fare altre cose. Questo perché i motori hanno un’inerzia e non si fermano proprio instantaneamente. È quindi bene aspettare un po’ prima di dare altri comandi.
Nell’ultima parte troviamo il programma principale, cosiddetto “main”, nel blocco di istruzioni 26-31:
26 display.show("?")
27 while True:
28 if button_b.is_pressed():
29 count = button_a.get_presses()
30 d = count * 10
31 forward(d)
Qui, come prima cosa mostriamo un punto interrogativo nel display a LED della micro:bit:
26 display.show("?")
Il resto realizza un ciclo infinito che inizia con l’istruzione
27 while True:
Questo è n modo buffo per dire di ripetere all’infinito tutte il istruzioni comprese nel blocco susseguente (N.29-31), contraddistinto per essere spostato a sinistra di una tabulazione, tecnica peculiare del linguaggio Python. Buffo perché sarebbe come dire: fai le cose qui sotto finché questa cosa vera è vera. Sì perché True
in Python è una costante logica che corrisponde a verità. L’altro possibile stato delle variabili logiche è False
, che corrisponde a falsità. Ovvio quindi che se dico di ripetere un ciclo finché True e vera questo andrà avanti all’infinito. E che succede in questo ciclo? Poco: si passa tutto il tempo a domandarsi se sia stato pigiato il tasto B e quindi, finchè questo non viene premuto, non succede nulla. Ciò equivale a dire che finché B non viene premuto le istruzioni 29-31 non vengono mai eseguite ma viene solo ripetuta la N.28. In realtà qualcosa potrebbe succedere, perché nel frattempo noi potremmo avere premuto, magari più volte, il tasto A ma, siccome il ciclo è appeso al valore del tasto B, nessuno se ne accorge. Ammettiamo ad un certo punto di avere premuto il tasto A dieci volte e poi, essendoci stufati, di avere premuto il tasto B. A quel punto il test dell’istruzione 28 ha esito positivo e si passa subito ad eseguire l’istruzione successiva:
29 count = button_a.get_presses()
Con la funzione button_a.get_presses()
si ottiene il numero di volte che nel frattempo è stato premuto il tasto A, numero che viene memorizzato nella variabile count
. A questo punto si ottiene la distanza da percorrere moltiplicando count
per 10 (valore che si può scegliere a seconda della convenienza) e si invoca il comando forward(d)
da noi testè definito.
Visto questo primo programma in dettaglio, non dovrebbe essere difficile farsi un’idea di cosa fanno gli altri, che elenchiamo commentandoli brevemente qui di seguito.
Per chi è nuovo a questo genere di attività può parere troppo complicato per bambini di 8-10 anni. Questo effetto è normale ma non si deve scambiare quella che deve essere la competenza dell’insegnante con quella che ci aspettiamo dai bambini. L’insegnante che affronta per la prima volta questi temi deve darsi il tempo di familiarizzare sperimentando da solo, o meglio insieme ad altri colleghi. Successivamente si tratterà di stabilire il genere di scaffolding da predisporre in modo da consentire ai bambini di esercitare il controllo su alcuni aspetti e parametri che saranno utili a sviluppare pensiero logico e ad esercitare il metodo sperimentale. Questo è quello che fu fatto nell’esperienza fatta presso Scuola-Città Pestalozzi a cui avevo accennato prima.
Andata e ritorno
Poniamoci ora il secondo obiettivo proposto fra le attività di Scuola-Città Pestalozzi:
2. raggiungere il solito birillo, girarci intorno e tornare alla base
Qui possiamo usare il codice precedente per l’andata e il ritorno ma non abbiamo quello per girare intorno alla meta.
Qui si pone la domanda: come fa a girare un robot che poggia su due ruote motrici e un ruotino passivo (di solito una sfereretta libera di girare)? La risposta può essere suggerita da una riflessione su come fanno a girare i trattori cingolati. Questi in fin dei conti non sono molto diversi dal nostro robot, se non per il fatto che poggiano unicamente su due cingoli i quali, essendo sufficientemente lunghi rendono inutile un terzo punto di appoggio. I trattori a cingoli sono molto stabili: se ne stanno perfettamente in equilibrio sui loro due cingoli — sono in effetti i veicoli più stabili che ci siano. Ebbene, come fanno a girare? Basta guardare i comandi di un trattore di questo genere: non ha uno sterzo ma due leve, che sono le frizioni dei due cingoli: se tiro una leva stacco il motore dal cingolo corripondente che rimane così fermo mentre l’altro continua a girare. L’effetto è quello di far girare il trattore dalla parte del cingolo fermo.
Possiamo fare esattamente la stessa cosa con il nostro robot, ad esempio con un comando di questo tipo…
Rover.motor(0, 50)
che si spiega da sé. In realtà noi possiamo fare molto di più: nulla ci impedisce di regolare a piacimento le velocità dei due cingoli, realizzando così ogni sorta di curve. Non ci vuole molto a questo punto per modificare il programma precedente in modo da aggirare una meta e tornare alla base:
1 # alle-retour.py
2 # quando si preme il tasto B il robot avanza
3 # di un tratto proporzionale al numero di
4 # precedenti pressioni del tasto A
5 # Ogni pressione aggiunge circa 10 cm
6
7 # Importo la libreria per gestire il robot
8 from Freenove_Micro_Rover import *
9 # Definisco un oggetto robot
10 Rover = Micro_Rover()
11
12 # Definisco il comando di avanzamento forward(st)
13 # dove d è la distanza da percorrere
14 def forward(d):
15 Rover.motor(50, 50)
16 sleep(105 * d)
17 Rover.motor(0, 0)
18
19 def steer(a): # Definisco il comando di rotazione
20 Rover.motor(35, 25)
21 sleep(4550 * a / 90)
22 Rover.motor(0, 0)
23
24
25 # Programma principale:
26 display.show("?")
27 while True:
28 if button_b.is_pressed():
29 count = button_a.get_presses()
30 display.scroll(str(count))
31 d = count * 50
32 display.scroll(d)
33 forward(d)
34 steer(180)
35 forward(d)
Qui abbiamo aggiunto la definizione della funzione steer(a)
che consente al robot di sterzare:
19 def steer(a):
20 Rover.motor(35, 25)
21 sleep(4550 * a / 90)
22 Rover.motor(0, 0)
Il parametro a
rappresentra l’angolo di rotazione in gradi anche se è difficile far coincidere tale valore con la rotazione reale del robot, che è influenzata da vari fattori, ad esempio lo stato dei pneumatici, la superficie del suolo, la precisione dei motori ecc. Il valore 4550
nel codice precedente è stato appunto aggiustato empiricamente nelle condizione in cui ho fatto le prove.
L’altra differenza rispetto al codice precedente si trova nel programma principale dove invece del solo comando di andata forward(d)
troviamo anche il comando di rotazione steer(180)
e di ritorno forward(d)
:
33 forward(d)
34 steer(180)
35 forward(d)
Questa sequenza di comandi ricorda quelle che abbiamo utilizzato in Logo. Ad esempio, qualcosa di simile potrebbe essere:
FORWARD D
RIGHT 180
FORWARD D
o nella versione in Turtle Python:
forward(d)
right(180)
forward(d)
Anche qui otteniamo che la tartaruga raggiunga un certo punto alla distanza D
, giri di 180° e poi torni al punto di partenza ma con una differenza: con il comando RIGHT 180
la tartaruga gira su se stessa e non ruota intorno ad un oggetto. Questa è una distinzione importante su cui riflettere. In effetti, quando ci siamo posti il problema di realizzare linee curve in Logo con i comandi base (non con comandi preconfezionati tipo CIRCLE
) siamo ricorsi allo stratagemma di creare curve a furia di piccoli passi e piccole rotazioni. Per esempio il cerchio lo avevamo realizzato così:
REPEAT 360 [ FORWARD 1 RIGHT 1]
Vale a dire che nella Turtle Geometry (che è quella espressa da Logo) le curve si realizzano attraverso delle approssimazioni e tutto l’edificio di questa teoria è basato su di esse.
Con i robot succede un po’ l’inverso: il modo più diretto di realizzare percorsi curvi è inerentemente continuo, attraverso le diverse velocità impartite alle ruote. Se vogliamo riprodurre il contesto della Turtle Geometry ci dobbiamo costruire un componente aggiuntivo: un comando di rotazione che riproduca il comando RIGHT
di Logo, ovvero un comando che faccia ruotare su stesso il robot. Un effetto molto facile da ottenere, con un comando del tipo Rover.motor(50, -50)
. La rotazione si ottiene facendo ruotare le ruote alla stessa velocità (non importa quale) ma in direzioni opposte. Per creare una situazione analoga a Logo dobbiamo incapsulare questo comando in un nuovo comando, chiamiamo right(a)
(identico quindi alla versione Turtle Python):
def right(a):
Rover.motor(35, -35)
sleep(1000 * a / 90)
Rover.motor(0, 0)
Si vede come il comando impieghi la capacità del robot di ruotare su se stesso mediante Rover.motor(35, -35)
. A questo punto possiamo far seguire una traiettoria curva al robot esattamente come facciamo con la tartraruga in Logo. Vediamo come cambia il codice dell’esercizio di andata e ritorno:
1 # alle-retour-turtle.py
2 # quando si preme il tasto B il robot avanza
3 # di un tratto proporzionale al numero di
4 # precedenti pressioni del tasto A
5 # Ogni pressione aggiunge circa 10 cm
6
7 # Importo la libreria per gestire il robot
8 from Freenove_Micro_Rover import *
9 # Definisco un oggetto robot
10 Rover = Micro_Rover()
11
12 # Definisco il comando di avanzamento forward(st)
13 # dove d è la distanza da percorrere
14 def forward(d):
15 Rover.motor(50, 50)
16 sleep(105 * d)
17 Rover.motor(0, 0)
18
19 def right(a): # definisco la rotazione del robot su se stesso
20 Rover.motor(35, -35)
21 sleep(1000 * a / 90)
22 Rover.motor(0, 0)
23
24 # Programma principale:
25 display.show("?")
26 while True:
27 if button_b.is_pressed():
28 count = button_a.get_presses()
29 display.scroll(str(count))
30 d = count * 50
31 display.scroll(d)
32 forward(d)
33 for i in range(180):
34 right(10)
35 forward(1.5)
36 forward(d)
Qui abbiamo sostituito la definizione del comando steer(a)
con quella di right(a)
e poi abbiamo codificato la traiettoria come avremmo fatto con la tartaruga:
32 forward(d) # andata
33 for i in range(18): # ciclo di 18 ripetizioni
34 right(10) # di una rotazione di 10°
35 forward(1.5) # e un passo di 1.5 cm
36 forward(d) # ritorno
Il fatto che gli argomenti d
di forward(d)
e a
di right(a)
si traducano rispettivamente in tragitti del robot di d
cm e rotazioni di a
gradi non si può dare per scontato: occorre sempre fare una taratura del proprio robot nelle reali condizioni di utilizzo, operazione che si risolve con l’aggiustamento dei fattori numerici all’interno delle funzioni forward(d)
, steer(a)
, right(a)
. L’approfondimento del ruolo di tali fattori e delle loro reciproche dipendenze possono costituire un’ottima palestra di riflessione e esplorazione scientifica.
Ovviamente, disegnando le traiettorie curve con il metodo della tartaruga, possiamo decidere di fare passi e rotazioni più piccoli per eseguire movimenti più morbidi. Nel codice che ho riportato questi non sono proprio piccoli, al fine di mettere in evidenza la differenza, come si può vedere anche dai video seguenti.
Lavorando con i robot viene spontaneo concentrarsi sull’aggiustamento dei vari parametri numerici per far fare loro quello che vogliamo, in codici del primo tipo che abbiamo visto. E quello di far simulare al robot il comportamento della tartaruga di Logo può parere una complicazione inutile. In realtà questa è una posizione superficiale perché l’assimilazione del robot alla tartaruga consente di stabilire un collegamento diretto fra geometria e realtà fisica. Lavorando in questo modo gli studenti hanno modo di toccare con mano come le astrazioni matematiche possano essere utili per descrivere e controllare apparecchiature ma anche come la trasposizione di concetti matematici nella realtà fisica possa essere problematica.
Lascio al lettore voglioso di sperimentare la creazione del codice necessario per impostare la terza delle attività proposte nel laboratorio a Scuola Città Pestalozzi — 3. eseguire una gimcana intorno a una fila di birilli e ritorno — e naturalmente anche la quarta — 4. immaginare e provare a conseguire un obiettivo a piacere. Non è difficile a partire dai codici che ho già descritto.
Inseguendo la tartaruga
Mettiamoci quindi sulle tracce della tartaruga, che sarà difficile da raggiungere perché ne ha fatta di strada! Sono centinaia gli esempi e le esercitazioni proposte nel MOOC Coding a scuola con Software Libero, nel Piccolo manuale di LibreLogo e nel libro Building knowledge with turtle geometry. Facciamo qui solo qualche esempio.
Il quadrato
Iniziamo dal classico quadrato, che in Logo avevamo codificato as esempio così:
REPEAT 4 [
FORWARD 100
RIGHT 90
]
E con Python Turtle (il Logo con Python):
for i in range(4):
forward(100)
right(90)
In ambedue i casi l’effetto è quello di disegnare un quadrato. Vediamo ora come appare in My Python (uso questa dizione per rifermi al codice creato per manovrare il robot Micro:Rover, anche se questo può essere programmato con altri sistemi):
for i in range(4):
forward(100)
right(90)
Sorpresa! È lo stesso di quello che disegna il quadrato virtuale con Python Turtle dove naturalmente i comandi forward(d)
e right(a)
sono quelli che abbiamo definito negli esempi precedenti. Questo codice è quello strettamente necessario per disegnare il quadrato. Come abbiamo visto, per azionare il robot dobbiamo aggiungere le istruzioni per così dire di servizio (librerie necessarie) e le nostre definizioni di forward(d)
e right(a)
. Vediamo:
1 # square.py
2 from Freenove_Micro_Rover import *
3
4 Rover = Micro_Rover()
5
6 def forward(d): # s in cm
7 Rover.motor(50,50)
8 sleep(100*d)
9 Rover.motor(0,0)
10 sleep(1000)
11
12 def right(a):
13 Rover.motor(35, -35) # a in °
14 sleep(1100*a/90)
15 Rover.motor(0,0)
16 sleep(1000)
17
18 while True:
19 display.show("OK")
20 for i in range(4):
21 forward(15)
22 right(90)
23 else:
24 display.scroll(":)")
Dopo avere visto gli esempi precedenti c’è poco da commentare, penso che sia facile capire come funziona.
La realtà fisica: il quadrato con la taratura dell’angolo di rotazione
Piuttosto è interessante soffermarsi su questo semplice esempio per mettere bene in luce i problemi inerenti alla taratura dei meccanismi dei robot a cui abbiamo già accennato. Il fatto che il robot percorra effettivamente 15 cm con il comando forward(15)
e ruoti di 90° con il comando right(90)
dipende dai valori dei fattori numerici contenuti nella definizione delle funzioni forward(d)
e right(a)
. Ad esempio il fattore 100 nel comando sleep(100*d)
all’interno della funzione forward(d)
ma anche i valori all’interno del comando Rover.motor(50,50)
. Tanto per fare un esempio, con il robot che sto usando per scrivere queste note il comando non fa andare il robot in linea retta, come si presumerebbe. Nel mio caso devo usare Rover.motor(50,52)
oppure Rover.motor(50,53)
, a seconda della superficie su cui lo faccio viaggiare. A prima vista potrebbe sembrare un difetto: due motori identici devono pur andare allo stesso modo! E invece no e la ragione è tanto semplice quanto importante da cogliere: i prodotti industriali sembrano tutti uguali ma non lo sono per niente. O meglio, lo sono fino ad un certo punto in dipendenza delle applicazioni cui sono destinati. Per commentare il caso specifico, i componenti di un robot didattico che viene venduto per circa €50 non possono costare più di tanto ma questo pone anche un limite alla qualità che essi possono avere. Non è che non se ne possano costruire di più precisi ma i costi salgono. Un conto è un robot didattico un altro un robot per operazioni chirurgiche: state pur certi che i motori che li azionano costano molto molto di più.
Tuttavia, in ambito didattico i difetti (entro certi limiti) non sono difetti ma pregi: comprenderli e tenerne conto fa crescere, come gli errori. Per esempio, nel nostro caso potremmo individuare un modo per poter aggiustare sul campo il fattore di scala che controlla gli angoli di rotazione, esattamente come avevamo visto nel programma iniziale, dove utilizzavamo i tasti A e B della scheda micro:bit, il “cervello” del robot Micro:Rover per determinare la distanza da percorrere. Ad esempio così:
# square-tuning.py
# Chiede un numero c di pressioni del tasto A per correggere
# l'angolo di rotazione: fattore s = 1000 + c * 10
from Freenove_Micro_Rover import *
Rover = Micro_Rover()
def forward(d): # s in cm
Rover.motor(50 * 0.90, 50 * 0.90)
sleep(100 * d / 0.90)
Rover.motor(0, 0)
sleep(1000)
def right(a, s):
Rover.motor(35, -35) # a in °
sleep(s * a / 90)
Rover.motor(0, 0)
sleep(1000)
while True:
display.show("?")
sleep(10000)
count = button_a.get_presses()
display.scroll(str(count))
c = count
s = 980 + c * 5
display.scroll(s)
for i in range(4):
forward(15)
right(90, s)
else:
display.scroll("?")
Confido ormai che il lettore si orienti facilmente in questi nuovi esempi che hanno tutti più o meno la stessa struttura. Qui c’è da notare che abbiamo usato il numero di pressioni del tasto A per determinare un fattore di scala con il quale aggiustare la taratura della rotazione. Più precisamente, il numero di pressioni del tasto vengono memorizzate nella variabile c
dalla quale calcoliamo il fattore di scala con la formula s = 980 + c * 5
, fattore che viene poi passato come secondo argomento al comando di rotazione right(90, s)
. Nella definizione di questo il fattore s
viene usato per modulare il tempo per cui la scheda deve “dormire” prima di fermare i motori che stanno facendo girare le ruote e quindi il robot.
Perfezionamento del robot per ridurre il range della correzione
È bene però considerare che non tutto si risolve in correzioni software. L’esempio che sto per fare si riferisce a un problema che emerse nelle attività presso Scuola-Città Pestalozzi, utilizzando i robot della serie Evo: non si riusciva a far girare i robot in modo preciso e ripetibile e non si capiva perché. Alla fine ci rendemmo conto che si trattava di un problema fisico…

L’interesse dell’episodio risiede nella commistione di pensiero algoritmico astratto — il codice che manovra il robot — analisi del problema fisico e soluzione laboratoriale. Ovvero nella dialettica teoria-pratica, pressoché assente nella vita scolastica.
Il robot riproduce la tartaruga che fa la tartaruga…
Riprendo qui l’esperimento software dove si simula una tartaruga che vaga senza meta (Building knowledge with turtle geometry, p. 89; MOOC Coding a scuola con il software libero, lezione 7). Ecco il codice:
1 # random-walk-obst-avoidance.py
2
3 from Freenove_Micro_Rover import *
4 import random
5
6 Rover = Micro_Rover()
7
8 def forward(s): # s in cm
9 Rover.motor(50 * 0.90, 50 * 0.90)
10 sleep(100 * s / 0.90)
11 Rover.motor(0, 0)
12
13 def right(a):
14 if a > 0:
15 display.show("+")
16 Rover.motor(35, -35) # a in °
17 sleep(1000 * a / 90)
18 Rover.motor(0, 0)
19 elif a < 0:
20 display.show("-")
21 Rover.motor(-35, 35) # a in °
22 sleep(1000 * (-a) / 90)
23 Rover.motor(0, 0)
24 else:
25 Rover.motor(0, 0)
26
27
28 display.show(Image.HAPPY)
29 for i in range(100):
30 forward(random.random() + 1)
31 r = random.randint(-45, 45)
32 right(r)
33
Qui la definizione della funzione right(a)
(13—25) è un po’ più complicata: volendo scegliere casualmente l’angolo rotazione, occorre poter selezionare anche angoli negativi, altrimenti il robot ruoterebbe sempre a destra. Di conseguenza nella funzione usiamo due diversi comandi, con le rotazioni delle ruote invertite, a seconda che l’angolo sia positivo o negativo. Poi il programma principale ricalca le istruzioni date alla tartaruga negli esercizi succitati.
Sempre vagando a caso ma evitando gli ostacoli
Con il robot in molti casi è ancora più facile arricchire le simulazioni, poiché è già dotato degli opportuni sensori. Si tratta semplicemente di leggerne gli output e adeguare il codice. Per esempio se vogliamo che vaghi sì a caso ma evitando di sbattere su eventuali ostacoli, il codice precedente cambia nel modo seguente:
1 # random-walk-obst-avoidance.py
2
3 from Freenove_Micro_Rover import *
4 import random
5
6 Rover = Micro_Rover()
7
8 def forward(s): # s in cm
9 Rover.motor(50 * 0.90, 50 * 0.90)
10 sleep(100 * s / 0.90)
11 Rover.motor(0, 0)
12
13 def right(a):
14 if a > 0:
15 display.show("+")
16 Rover.motor(35, -35) # a in °
17 sleep(1000 * a / 90)
18 Rover.motor(0, 0)
19 elif a < 0:
20 display.show("-")
21 Rover.motor(-35, 35) # a in °
22 sleep(1000 * (-a) / 90)
23 Rover.motor(0, 0)
24 else:
25 Rover.motor(0, 0)
26
27
28 display.show(Image.HAPPY)
29 for i in range(100):
30 distance = Rover.get_distance()
31 if distance >= 15:
32 forward(random.random() + 1)
33 r = random.randint(-45, 45)
34 right(r)
35 else:
36 right(180)
37
La sezione nuova è compresa fra le istruzioni N. 30 e N. 36. Con l’istruzione distance = Rover.get_distance()
si memorizza nella variabile distance
la distanza rilevata dal sensore a ultrasuoni, poi con il blocco che inizia con if distance >= 15:
si valuta cosa fare in dipendenza del valore ottenuto: se inferiore a 15 cm il robot ruota di 180° altrimenti procede con un altro passo.
Da qui in poi non ci sono limiti, si possono riprodurre, migliorare, variare le numerose esplorazioni con la tartaruga discusse nei riferimenti citati.