A.R.T. (Automatic Route Tracer) – Robot automatico per risoluzione labirinti

Ad opera di: Dominique Balassi – Alberto Ciccarelli – Giovanni Saccone

Abstract

Nell’ottica di mettere alla prova le competenze acquisite e in fase di acquisizione durante il secondo anno del corso di studi di Ingegneria dell’Automazione si è scelto di dedicarsi alla concretizzazione di un’idea. Il desiderio di toccare con mano, oltre che sui libri, la materia oggetto del corso di studi ha costituito la Vision del progetto, la cui realizzazione non è stata priva di numerosi, sebbene stimolanti, ostacoli. L’ obiettivo del piano di lavoro è stato la creazione di un automa in grado di risolvere labirinti a pianta geometrica senza alcun intervento umano. Il conseguimento di tale traguardo è stato possibile grazie ad un lungo periodo di progettazione, volta al fine di consentire un’equilibrata sinergia tra il Software e le componenti Hardware. Al fine di avvalorare la tesi secondo cui l’elettronica, l’automazione e l’informatica sono e devono essere materie economicamente accessibili ed ecosostenibili si è tentato, ove possibile, l’utilizzo di parti riciclate o facilmente reperibili.

Mission

A seguito di un approfondita discussione, si è optato per la realizzazione di un drone terrestre in grado di risolvere problemi. Nell’ottica di individuare un compito sufficientemente complesso, ma allo stesso tempo non eccessivamente costoso in termini di risorse disponibili, si è scelto come obiettivo la risoluzione di labirinti a pianta geometrica. La natura di tale problema ha costituito un’ottima base per creare un sistema
autonomo in grado di interagire con il mondo fisico in maniera dinamica ed efficiente. Un’ulteriore motivazione a sostegno della scelta di questo obiettivo è stata la lunga lista di possibili applicazioni in diversi ambiti.

Componentistica

  • Compensato di pioppo 4mm
  • Motori stepper Nema 17 12V e 0.4A (17HS13-0404S) x2
  • Sensori ultrasuoni (HC-SR04) x3
  • Driver (A4988) x2
  • Converter (MT3608) 2A DC-DC Step Up
  • Cuscinetto sferico da 22mm x2
  • Power bank 30000 mAh con pannello solare
  • Arduino UNO R3
  • Cavetteria e guaine termorestringenti
  • Cerniera e angolari in acciaio
  • Pannelli in mdf (15x100x0.3) x9
  • Moquettes 225×125
  • Viti M3 x8
  • Ruota di un giocattolo x2

Trazione

La scelta della trazione è stata oggetto di attente riflessioni. Si è ritenuto necessario scegliere accuratamente la natura dei motori, il loro posizionamento e il sistema di trasmissione alle ruote.

Nella disamina delle varie tipologie di propulsori elettrici gli aspetti positivi dell’impiego di motori passo passo hanno prevalso su quelli negativi. Servendosi dell’alto livello di controllo degli stepper è stato possibile realizzare un drone in grado di effettuare svolte sempre uguali, minimizzando il numero di errori e variabili. La possibilità di bloccare i motori in una data posizione ha consentito di evitare l’elaborazione di protocolli di frenata, rendendo possibile il raggiungimento di velocità sostenute senza eccessivi calcoli inerziali. I motori adoperati (Nema 17HS13-0404S) sono stati recuperati da una stampante in disuso e ciò ha permesso l’abbattimento delle spese.

Si è reso necessario l’utilizzo di Driver Polou A4988, connessi al microcontrollore principale di Arduino Uno e opportunamente tarati sulle specifiche di funzionamento dei motori scelti. Il compito di tale componete è quello di gestire il flusso di corrente elettrica attraverso le due bobine presenti nello statore dei propulsori, al fine di generare un campo magnetico sufficientemente intenso da far muovere il rotore di un passo.

Le caratteristiche tecniche dei Nema 17 richiedevano una tensione di funzionamento di 12V, mentre il generatore scelto per il progetto era da soli 5V. Per far fronte a tale problematica è stato implementato un converter KKmoon (MT3608) 2A DC-DC di tipo step up accuratamente tarato in funzione della necessità.
In merito alla scelta della disposizione dei motori, del sistema di trasmissione e dell’assetto che il drone dovesse avere, sono state vagliate differenti ipotesi.

A seguito di accurate riflessioni si è optato per la collocazione di due motori nella parte mediana del telaio con innesto diretto delle gomme sull’albero motore. Tale assetto si è rivelato essere il più congeniale ad una rotazione del drone precisa e poco invasiva in termini di raggio di sterzata. Al fine di garantire la massima stabilità sono stati installati cuscinetti sferici nella parte anteriore e posteriore del telaio.

Sensori

La scelta di sensori che potessero permettere al drone l’identificazione degli ostacoli e delle vie d’uscita è ricaduta sugli Elogoo HC-SR04. Tali sensori ad ultrasuoni si sono rivelati particolarmente efficaci grazie alle loro ristrette dimensioni, alla loro affidabilità e facilità di utilizzo. I sensori sono costituiti da un trasmettitore, in grado di produrre onde ultrasoniche, ed un ricevitore, in grado di leggerle. Dopo aver calcolato il tempo intercorso tra l’emissione del segnale e la sua ricezione, tenendo conto della propagazione delle onde attraverso l’aria e la densità della stessa a temperatura ambiente, è possibile risalire alla distanza di un ostacolo. Sono stati adoperati tre sensori: due ai lati ed uno nella parte anteriore.

Telaio

L’identificazione del materiale, della forma e della tecnica costruttiva del telaio ha rivelato una sorprendente complessità a causa delle numerose alternative e tecnologie disponibili. Sebbene la stampa 3D costituisse una valida opportunità per via della sua estrema versatilità nel creare forme complesse, i costi elevati e la scarsa reperibilità hanno indotto a trovare un’alternativa più efficace.

 

La scelta è pertanto ricaduta sull’utilizzo di compensato di pioppo intagliato a laser. Un’attenta fase progettuale in ambiente CAD 2D e 3D ha garantito la creazione di parti complementari e funzionali allo scopo.

Assemblaggio

La fase di assemblaggio ha richiesto una discreta manualità date le piccole dimensioni del progetto. In primis si è provveduto alla costruzione del telaio, ossia alla corretta disposizione delle parti precedentemente tagliate a laser e all’adesione delle stesse grazie ad uno specifico collante per il legno. A seguito dell’istallazione dei motori, assicurati grazie ad apposite viti, è stato possibile collocare i sensori, i driver ed il converter. La powerbank è stata prima disassemblata, al fine di ridurne le dimensioni, per poi essere
incollata ad un apposito supporto. La sistemazione di una cerniera ha consentito la realizzazione di un vano facilmente accessibile dove, successivamente, è stato collocato Arduino. Durante la fase di cable management si sono realizzati tutti i collegamenti per mezzo di saldature a stagno rivestite da apposite guaine termorestringenti, in modo da prevenire qualsiasi tipo di cortocircuito.

Algoritmo

Completata la realizzazione della parte Hardware, si è iniziata la progettazione ed implementazione del Software. L’ideazione di una procedura sufficientemente efficace e funzionale alla risoluzione di un labirinto a pianta geometrica ha costituito un’allettante sfida per la logica e l’ingegno. Si è deciso di rendere interamente fruibile il codice, in quanto si è convinti fautori del concetto di Open Source e dell’esigenza di collaborare e condividere il proprio slancio inventivo; anteponendo l’amore per il progresso al lucro
personale.

const int TRIG_PIN1 = 2;
const int ECHO_PIN1 = 5;
const int TRIG_PIN2 = 3;
const int ECHO_PIN2 = 6; //dichiarazione dei pin e delle variabili globali
const int TRIG_PIN3 = 4;
const int ECHO_PIN3 = 7;
const int enable1 = 12;
const int enable2 = 13;
const int pinDir1 = 8;
const int pinStep1 = 10;
const int pinDir2 = 9;
const int pinStep2 = 11;
const int numStepMotore = 200;
const long velocita = 2000;
struct incrocio {
bool avanti;
bool sinistra;
bool destra;
};
void setup () {
pinMode(TRIG_PIN1, OUTPUT); //definizione di tutte le modalità dei pin
pinMode(ECHO_PIN1, INPUT);
pinMode(TRIG_PIN2, OUTPUT);
pinMode(ECHO_PIN2, INPUT);
pinMode(TRIG_PIN3, OUTPUT);
pinMode(ECHO_PIN3, INPUT);
pinMode(pinStep1, OUTPUT);
pinMode(pinDir1, OUTPUT);
pinMode(pinStep2, OUTPUT);
pinMode(pinDir2, OUTPUT);
pinMode(enable1, OUTPUT);
pinMode(enable2, OUTPUT);
digitalWrite(enable1, LOW); //inizializzazione dei pin enable1 e enable2 a valori logici bassi (motori in tensione)
digitalWrite(enable2, LOW);
digitalWrite(pinStep1, LOW); //inizializzazione dei pin pinStep1 e pinStep2 a valori logici bassi
digitalWrite(pinStep2, LOW);
delay(7000); //timer necessario al corretto posizionamento del drone
struct incrocio svolta;
int i = 0;
int k = 0;
int del = 0;
for (int z = 0; z < 30; z++) { //ciclo di tutte le strade percorribili dal drone dalla sua accensione
error = false;
int curva = 2;
do {
del++; //contatore che permette l’avvio di un delay necessario alla correzione dell’assetto
if (error == false) //variabile che aumenta quando il drone è sul percorso giusto
i++;
if (error == true) //variabile che aumenta quando il drone è sul percorso errato
k++;
svolta.avanti = false ;
svolta.sinistra = false;
svolta.destra = false;
digitalWrite(pinDir1, LOW); //definizione dei valori logici dei pinDir (sono opposti per procedere in avanti)
digitalWrite(pinDir2, HIGH);
steps(290); //camminare in avanti fino al centro del prossimo incrocio
lettura(svolta); //lettura delle pareti circostanti
if (svolta.avanti == false && svolta.sinistra == false && svolta.destra == false) { //vicolo cieco
ruota180();
error = true; //percorrere la strada a ritroso
}
if (svolta.avanti == false && svolta.sinistra == true && svolta.destra == false) //svolta a sinistra
ruota_90();
if (svolta.avanti == false && svolta.sinistra == false && svolta.destra == true) //svolta a destra
ruota90();
if (svolta.avanti == false && svolta.sinistra == true && svolta.destra == true) //svolta a destra ad un incrocio a T
{ ruota90();
k = i;
}
if (svolta.avanti == true && svolta.sinistra == true && svolta.destra == false) { //va avanti ad un incrocio a T
k = i;
if (error == false)
curva = 0;
}
if (svolta.avanti == true && svolta.sinistra == false && svolta.destra == true) { //va avanti ad un incrocio a T
k = i;
if (error == false)
curva = 1;
}
}
if (del == 5) { //delay per riposizionare il drone se è leggermente disassato
del = 0;
delay(3000);
}
} while (error == false || k - i != 0);
if (curva == 0) //una volta all’incrocio in cui ha commesso l’errore cambia strada
ruota90();
if (curva == 1)
ruota_90();
}
}
void lettura(incrocio &svol) { //lettura delle pareti circostanti per 5 volte
float distanza1[5] = {0, 0, 0, 0, 0};
float distanza2[5] = {0, 0, 0, 0, 0};
float distanza3[5] = {0, 0, 0, 0, 0};
for (int i = 0; i < 5; i++) {
digitalWrite (TRIG_PIN1, HIGH);
delayMicroseconds (10);
digitalWrite(TRIG_PIN1, LOW);
unsigned long tempo1 = pulseIn(ECHO_PIN1, HIGH);
distanza1[i] = 0.03438 * tempo1 / 2;
digitalWrite (TRIG_PIN2, HIGH);
delayMicroseconds (10);
digitalWrite(TRIG_PIN2, LOW);
unsigned long tempo2 = pulseIn(ECHO_PIN2, HIGH);
distanza2[i] = 0.03438 * tempo2 / 2;
digitalWrite (TRIG_PIN3, HIGH);
delayMicroseconds (10);
digitalWrite(TRIG_PIN3, LOW);
unsigned long tempo3 = pulseIn(ECHO_PIN3, HIGH);
distanza3[i] = 0.03438 * tempo3 / 2;
}
float Distanza1 = 0;
float Distanza2 = 0;
float Distanza3 = 0;
Distanza1 = (distanza1[0] + distanza1[1] + distanza1[2] + distanza1[3] + distanza1[4]) / 5; //calcolo della media
Distanza2 = (distanza2[0] + distanza2[1] + distanza2[2] + distanza2[3] + distanza2[4]) / 5; // per eliminare
Distanza3 = (distanza3[0] + distanza3[1] + distanza3[2] + distanza3[3] + distanza3[4]) / 5; //eventuali errori
if (Distanza1 > 20)
svol.avanti = true;
if (Distanza2 > 20)
svol.sinistra = true;
if (Distanza3 > 20)
svol.destra = true;
}
void ruota90() {
digitalWrite(pinDir1, HIGH);
digitalWrite(pinDir2, HIGH);
Serial.println("Ruoto di 90");
steps(105);
}
void ruota_90() {
digitalWrite(pinDir1, LOW);
digitalWrite(pinDir2, LOW);
Serial.println("Ruoto di -90");
steps(105);
}
void ruota180() {
digitalWrite(pinDir1, HIGH);
digitalWrite(pinDir2, HIGH);
steps(211);
}
void steps(const int passi) { //funzione per la modifica del valore logico dei
for (int x = 0; x < passi ; x++) { //pinStep per il numero di passi necessari
digitalWrite(pinStep2, HIGH);
digitalWrite(pinStep1, HIGH);
delayMicroseconds(velocita);
digitalWrite(pinStep2, LOW);
digitalWrite(pinStep1, LOW);
delayMicroseconds(velocita);
}
delay(500);
}

Labirinto

Il labirinto è stato realizzato grazie all’impiego di utensili rotativi multifunzione e squadratrici a sega circolare per il legno. Dopo aver provveduto al taglio di strisce di mdf da 15x30x0.3 e 15x60x0.3, si è passati al loro assemblaggio. Per creare gli angoli tra i pannelli ci si è serviti di angolari in acciaio fatti aderire con colla cianoacrilica. La progettazione del labirinto ha portato alla creazione di parti facilmente trasportabili ed intercambiabili, massimizzando il numero di configurazioni possibili.

Applicazione

Le applicazioni per un drone in grado di leggere l’ambiente circostante, scegliere in modo autonomo la strada da percorrere ed eventualmente modificare il proprio percorso, sono molteplici. Potrebbe essere impiegato nell’identificazione di vittime a seguito di crolli di edifici, frane, incendi o qualsiasi altro tipo di calamità naturale o emergenza. A differenza dei droni attualmente utilizzati a tale scopo, il progetto ART potrebbe essere opportunamente sviluppato per muoversi autonomamente, eliminando parzialmente o totalmente la necessità di intervento umano. È plausibile immaginare l’impiego di più esemplari contemporaneamente attraverso un opportuno sistema di gestione dello sciame. L’utilizzo simultaneo di più droni, se opportunamente gestito, potrebbe consentire la condivisione di informazioni tra gli stessi, al fine di un celere raggiungimento degli obiettivi. Una possibile evoluzione del progetto ART potrebbe inoltre essere commercializzata nel settore della Domotica e della robotica educativa.