Questo tutorial è la prima bozza di parte delle lezioni di “Alfabeto Arduino con DotBot” per l’apprendimento dell’uso di Arduino mediante lo starter kit open source per l’insegnamento del coding e della robotica che abbiamo chiamato DotBot.
Sperimenterò questa prima parte durante il corso di cui sarò relatore presso l’IIS Cassato Gattapone (Gubbio): “Apprendimento attivo con Raspberry Pi e Arduino”.
Nel tutorial che segue illustro un schema estremamente semplice per la realizzazione del controllo di DotBot effettuata con Arduino con i relativi sketch di esempio è può essere considerato la base di partenza per ogni sperimentazione basata appunto su Arduino.
Per il pilotaggio dei motori ho utilizzato l’Arduino Motor Schield R3 di cui ho già effettuato un tutorial nei giorni scorsi (Utilizzo dell’Arduino Motor Shields R3) ed è propedeutico a questa lezione.
Più avanti aggiungerò esempi pratici con ulteriori sensori e inoltre mostrerò come far funzionare DotBot come segui linea e utilizzando soluzioni con motorini passo-passo. Vi saranno ulteriori lezioni basate su Raspberry Pi 3 e programmazione in Python e successive approfondimenti sull’uso di ROS (Robot Operating System).
L’intero tutorial sarà basato sulle indicazioni costruttive di DotBot che trovate descritte nell’articolo: DotBot: lo starter kit open source per l’insegnamento del coding e della robotica che ricordo potete stampare in 3D seguendo le indicazioni che trovate nell’articolo.
In ogni caso se non avete stampato DotBot potrete tranquillamente applicarlo al robot che disponete.
Ricordo che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: “tempoRotazione” che potrete poi tarare mediante la funzione calibrazioneRotazione() che trovate nell’ultimo sketch proposto. La soluzione ottimale per il controllo preciso del robot prevede l’uso di motorini con encoder, che mostrerò in lezioni successive.
Ovviamente anche la soluzione proposta in questo tutorial risulta approssimata (ed anche dettata da un risparmio economico) ricordo che lo scopo è comprenderne il funzionamento del movimento di DotBot, lascio a voi i successivi perfezionamenti.
Potrete sostituire alle batterie che io ho utilizzato, batterie ricarrabili, o pacchi batterie con autonomia maggiore che potrete fissare utilizzando gli appositi fori predisposti sullo chassis
Componenti utilizzati
- Arduino UNO R3
- Arduino Motor Shield R3
- Sensore ad ultrasuoni HC-SR04
Montaggio del circuito
Ribadisco, come già indicato nel tutorial: Utilizzo dell’Arduino Motor Shields R3 che sono state fornite due alimentazioni diverse una per la scheda Arduino ed una per i motori, per effettuare tale operazione è indispensabile effettuare il taglio del “Vi connect”
Come evidenziato i due motori sono stati collegati con polarità invertita in quanto per muovere in avanti il robot le due ruote dovranno muoversi in senso opposto.
Negli esempio che seguono verranno di volta aggiunte nuove funzioni, la lista completa è la seguente:
setupDotBot()
Imposta i pin dell’Arduino Motor Shield
driveDotBot([motore], [direzione], [velocità], [brake])
La funzione è da leggersi in questo modo:
driveDotBot pilota il [motore] (0 per A, 1 per B) in senso [direzione] (0 o 1 – orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore.
I motori vanno avanti fino a quando non gli verrà detto di fermarsi.
stopDotBot([motore])
ferma il motore [motore] (0 o 1).
rotazioneOraria()
gira il robot di 90° in senso orario
rotazioneAntioraria()
gira il robot di 90° in senso antiorario
distanzaOstacolo()
restituisce la distanza in cm dell’ostacolo rilevato
paragonaDistanze()
verifica la distanza dell’ostacolo che si trova a distanza maggiore dal robot
scegliDirezione();
sceglie la direzione da prendere in funzione della distanza a cui si trova l’ostacolo
La spiegazione del funzionamento d ogni parte del codice è inclusa all’interno dello sketch come commento.
Nota per lo studio:
i 6 sketch proposti variano solo nel loop per la realizzazione delle funzioni richieste, la variazione tra uno sketch e l’altro consiste nell’aggiunta di poche linee di codice, sarà quindi necessario effettuare uno studio preliminare di tutte le parti dello sketch 1.
Sketch 1
Rotazione di 90° in senso orario del robot sul proprio asse e ritorno alla posizione di partenza con intervallo di stop di 3 secondi.
/* Michele Maffucci * 25.03.2015 * DotBot Sketch 01 * Rotazione di 90° in senso orario del robot sul proprio asse * e ritorno alla posizione di partenza con intervallo * di stop di 3 secondi. Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoRotazione = 390; // tempo calcolato per una rotazione di 90° // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ rotazioneOraria(); delay(3000); rotazioneAntioraria(); delay(3000); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria() { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria() { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Descrizione delle singole sezioni dello sketch 1
... // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 ..
Le due variabili CW e CCW indicano: CC: ClockWise, rotazione oraria e CCW: Coulter ClockWise, rotazione anti oraria che verranno utilizzate all’interno delle funzioni per in controllo del senso di rotazione dei motori.
MOTORE_A e MOTORE_B che assumono rispettivamente il valore 0 e 1 per identificare il motore su cui effettuare il controllo.
... // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B
Pin utilizzati dall’Arduino Motor Shields R3, non possono essere utilizzati per altre funzioni.
Per la descrizione dell’utilizzo dello shield motori si consulti l’articolo: Utilizzo dell’Arduino Motor Shields R3
La tabella che segue specifica come usare la scheda per pilotare i motori:
Funzione
|
Canale A
|
Canale B
|
Direzione
|
pin digitale 12
|
pin digitale 13
|
Velocità (PWM)
|
pin digitale 3
|
pin digitale 11
|
Freno
|
pin digitale 9
|
pin digitale 8
|
Assorbimento Corrente
|
pin analogico A0
|
pin analogico A1
|
... // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoAngolo = 350; // tempo calcolato per una rotazione di 90°
E’ la sezione dedicata all’impostazione delle velocità avanti, indietro e alla velocità di rotazione, inoltre, poiché questa versione di DotBot non è dotata di encoder sui motori è necessario effettuare alcune prove per impostare il tempo di rotazione, con la variabile: tempoAngolo, per effettuare una rotazione di 90°.
// regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx
Come indicato nel commento, i motori possono avere velocità di rotazioni differenti, soprattutto perché sono motori DC estremamente economici, quindi potrebbe essere opportuno effettuare dei piccoli aggiustamenti sulla velocità di rotazione di entrambi, per default non il valore è fissato a 0, cioè nessuna correzione.
... void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } ...
Nel setup viene invocata la funzione setupArdumoto() in cui vengono di volta in volta tutte le impostazioni sui pin.
La funzione setupArdumoto() è stata creata solamente per comodità di lettura delegando ad essa l’impostazione dei pin delle schede utilizzate. Nel setup(), negli esercizi che aggiungerò in successive lezioni, verranno aggiunte funzioni di calibrazione che hanno la necessità di essere eseguite una sola volta.
... void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ rotazioneOraria(); delay(3000); rotazioneAntioraria(); delay(3000); } ...
Nello sketch 1 viene richiesto di effettuare una rotazione di 90° in senso orario del robot sul proprio asse e ritorno alla posizione di partenza con intervallo di stop di 3 secondi.
... // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } ...
la funzione driveDotBot() serve per impostare: il motore su cui vogliamo agire, il senso di rotazione, la sua velocità e stabilire se dobbiamo attivarlo o fermarlo (brake).
L’if consente la selezione del motore utilizzato su cui verranno impostati i pin dello shield motori, così come spiegato nel tutorial: Utilizzo dell’Arduino Motor Shields R3
... // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } ...
stopDotBot() è una semplice funzione che invoca la funzione driveDotBot() con i parametri impostati per fermare lo specifico motore. Questa funzione è stata creata solamente per rendere più leggibile il codice ed identificare nel nome della funzione l’azione che si va ad impostare nel loop()
... // Rotazione antioraria void rotazioneAntioraria() { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } ...
Si tenga conto che nel montaggio dei collegamenti del motore di sinistra l’impostazione delle connessioni è stata invertita.
Per effettuare una rotazione oraria di DotBot è necessario far girare in senso orario entrambi i motori alla velocità impostata in velRotazioneAnteriore con brake impostato a 0 che in altro modo vuol dire “freno a mano non tirato” 😃
La rotazione in senso orario dovrà essere effettuata per un tempo pari a tempoRotazione, necessario per effettuare una rotazione di 90° (circa).
... // Rotazione oraria void rotazioneOraria() { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a velRotazioneOraria driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a velRotazioneOraria delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } ...
Identica alla funzione descritta precedentemente ma con parametri impostati per far girare DotBot in senso antiorario.
... // impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); } ...
Impostazione di tutti i pin utilizzati sulle schede.
Sketch 2
Rotazione di 180° in senso orario del robot sul proprio asse e ritorno alla posizione di partenza con intervallo di stop di 3 secondi.
/* Michele Maffucci * 25.03.2015 * DotBot Sketch 02 * Rotazione di 180° in senso orario del robot * sul proprio asse e ritorno alla posizione di * partenza con intervallo di stop di 3 secondi. Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoRotazione = 350; // tempo calcolato per una rotazione di 90° // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ rotazioneOraria(); rotazioneOraria(); delay(3000); rotazioneAntioraria(); rotazioneAntioraria(); delay(3000); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria() { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria() { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Sketch 3
Rotazione di 360° in senso orario del robot sul proprio asse, rotazione di 360° in senso antiorario del robot sul proprio asse con intervallo di stop a pi/2 di 1 secondo e stop di 3 secondi a 360°.
/* Michele Maffucci 25.03.2015 DotBot Sketch 02 Rotazione di 360° in senso orario del robot sul proprio asse, rotazione di 360° in senso antiorario del robot sul proprio asse con intervallo di stop di 3 secondi. Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoRotazione = 350; // tempo calcolato per una rotazione di 90° // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ for (int i = 0; i < 4; i++) { rotazioneOraria(); delay(1000); } delay (3000); for (int i = 0; i < 4; i++) { rotazioneAntioraria(); delay(1000); } delay(3000); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria() { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria() { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Sketch 4
Avanti per 500 millisecondi, rotazione di 180° in senso orario del robot, avanti per 500 millisecondi, rotazione di 180° in senso movimento in avanti per 500 millisecondi.
/* Michele Maffucci 25.03.2015 DotBot Sketch 02 Rotazione di 360° in senso orario del robot sul proprio asse, rotazione di 360° in senso antiorario del robot sul proprio asse con intervallo di stop di 3 secondi. Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoRotazione = 350; // tempo calcolato per una rotazione di 90° // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ avantiSempre(); delay(500); rotazioneOraria(); rotazioneOraria(); avantiSempre(); delay(500); rotazioneOraria(); rotazioneOraria(); delay(500); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria() { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria() { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Avanti senza stop void avantiSempre() { int avantiDx = avantiveloce - correzioneDx; int avantiSx = avantiveloce - correzioneSx; driveDotBot(MOTORE_A, CCW, avantiDx, 0); // Imposta la rotazione del motore A in senso antiorario alla velocità massima driveDotBot(MOTORE_B, CW, avantiSx, 0); // Imposta la rotazione del motore B in senso orario alla velocità massima } // indietro senza stop void indietroSempre() { driveDotBot(MOTORE_A, CW, indietroveloce, 0); // Imposta la rotazione del motore A in senso orario alla velocità massima driveDotBot(MOTORE_B, CCW, indietroveloce, 0); // Imposta la rotazione del motore B in senso antiorario alla velocità massima } // impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Sketch 5
Parametrizzare il tempo necessario per effettuare una rotazione di una certa quantità di gradi
Far realizzare 3 movimenti di rotazione:
45° in senso antiorario del robot
dalla posizione raggiunta rotazione di 90° in senso orario
dalla posizione raggiunta, rotazione di 45° in senso antiorario
/* Michele Maffucci 25.03.2015 DotBot Sketch 02 Rotazione di 360° in senso orario del robot sul proprio asse, rotazione di 360° in senso antiorario del robot sul proprio asse con intervallo di stop di 3 secondi. Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario // int tempoRotazione = 350; // tempo calcolato per una rotazione di 90° // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" */ rotazioneAntioraria(175); delay(500); rotazioneOraria(350); delay(500); rotazioneAntioraria(175); delay(500); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria(int tempoRotazione) { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria(int tempoRotazione) { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Avanti senza stop void avantiSempre() { int avantiDx = avantiveloce - correzioneDx; int avantiSx = avantiveloce - correzioneSx; driveDotBot(MOTORE_A, CCW, avantiDx, 0); // Imposta la rotazione del motore A in senso antiorario alla velocità massima driveDotBot(MOTORE_B, CW, avantiSx, 0); // Imposta la rotazione del motore B in senso orario alla velocità massima } // indietro senza stop void indietroSempre() { driveDotBot(MOTORE_A, CW, indietroveloce, 0); // Imposta la rotazione del motore A in senso orario alla velocità massima driveDotBot(MOTORE_B, CCW, indietroveloce, 0); // Imposta la rotazione del motore B in senso antiorario alla velocità massima } // impostazione pin schede void setupDotBot() { // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Sketch 6
Utilizzando il sensore ad ultrasuoni rilevare gli ostacoli ed effettuare la scelta della direzione da intraprendere in funzione dell’ostacolo che si trova a distanza maggiore dal robot.
Per l’utilizzo del sensore ad ultrasuoni ne avevo già dato spiegazione in EduRobot UNO – Come costruire il vostro primo Arduino Robot – Lezione 2 ma per permetterne un’agevole studio duplico quanto già scritto in questo tutorial.
Per far evitare gli ostacoli al nostro robot utilizzeremo un sensore ad ultrasuoni, in altro modo useremo questo sensore per misurare la distanza dell’ostacolo, ma potremo anche pensare di usare questo sensore, ad esempio, in un sistema anti-intrusione che deve far suonare un allarme. In questa lezione viene utilizzato il sensore HC-SR04, dispositivo economico e con un buon range operativo, ha una sensibilità dichiarata nei datasheet che va da 2 centimetri a 4,5 metri circa, in realtà la massima distanza da esperimenti che ho effettuato arriva a circa 3,5 metri, molto comunque dipende dai materiali colpiti e dalla loro capacità di assorbimento sonoro.
Ma come funziona un sensore di questo tipo?
Ripeto brevementa la lezione che avevo pubblicato su scuola.arduino.cc la spiegazione si riferisce ad un’altra tipologia di sensore ad ultrasuoni, ma il funzionamento è analogo.
I sensori ad ultrasuoni non forniscono direttamente la misura della distanza dell’oggetto più vicino, ma misurano il tempo impiegato da un segnale sonoro a raggiungere l’oggetto e ritornare al sensore.
L’impulso ad ultrasuoni inviato all’HC-SR04 è di circa 40KHz il tempo viene misurato in microsecondi, la tensione di funzionamento è di 5V, quindi potremo alimentarlo direttamente utilizzando Arduino.
L’HC-SR04 è dotato di 4 piedini: GND, eco, trigger, +Vcc.
Per convertire l’intervallo di tempo misurato in una lunghezza, bisogna ricordare che la velocità del suono è di 331,5 m/s a 0 °C e di 343,4 m/s a 20 °C ed in generale varia secondo la relazione:
v = 331,4 + 0,62 T
dove la temperatura T è misurata in °C.
Per effettuare una misura di distanza di un ostacolo assumiamo di lavorare ad una temperatura ambiente di 20 °C e quindi la velocità del suono sarà di 343 m/s (approssimiamo) che vuol dire anche 0,0343 cm/microsecondi.
Quindi, ricordando che v=s/t (v: velocità, s: spazio, t: tempo) allora lo spazio percorso sarà:
s = v*t
da cui
s = 0,0343 *t
però, per calcolare lo spazio percorso, bisogna tener conto che il suono percorre due volte la distanza da misurare (giunge sull’oggetto e ritorna indietro al sensore) quindi il valore di t ottenuto deve essere diviso per 2. La formula corretta per la misura dello spazio percorso è:
s = 0,0343 * t/2
eseguendo la divisione di 0,0343/2 possiamo scrivere:
s = 0,01715 * t
oppure:
s = t/58,31
approssimando
s = t/58
formula più semplice da ricordare.
Per calcolare la distanza dell’oggetto dal sensore sarà sufficiente dividere il tempo t (tempo impiegato dal segnale per giungere sull’oggetto e tornare al sensore) per 58.
Per poter effettuare una misurazione viene mantenuto a livello basso il pin Trigger, dopo di che viene fornito un impulso a livello alto della durata minima di 10µs riportandolo poi a livello basso, dopo questo momento la capsula trasmittente emette un burst (sequenza di livelli alti/bassi) a circa 40KHz, l’onda ultrasonica generata (burst) colpisce l’ostacolo, torna indietro venendo rilevata dalla capsula ricevente. L’elettronica del sensore effettua un calcolo del tempo di andata e ritorno del segnale emettendo sul pin Echo, normalmente a livello basso, un segnale a livello alto direttamente proporzionale alla distanza dell’ostacolo:
/* Michele Maffucci 25.03.2015 DotBot 1.1 rilevare gli ostacoli ed effettuare la scelta della direzione da intraprendere in funzione dell’ostacolo che si trova a distanza maggiore dal robot Utilizzo Arduino UNO R3 Arduino Motor Shields R3 Funzioni principali usate: setupDotBot() Imposta i pin dell’Arduino Motor Shield driveDotBot([motore], [direzione], [velocità], [brake]) pilota [motore] (0 per A, 1 per B) in [direzione] (0 o 1 - orario o antiorario) a velocità [velocità] (tra 0 e 255), [brake] attiva o rilascia blocco motore. I motori vanno avanti fino a quando non gli verrà detto di fermarsi. stopDotBot([motore]) ferma il motore [motore] (0 o 1). rotazioneOraria() gira il robot di 90° in senso orario rotazioneAntioraria() gira il robot di 90° in senso antiorario distanzaOstacolo() restituisce la distanza in cm dell'ostacolo rilevato paragonaDistanze() verifica la distanza dell'ostacolo che si trova a distanza maggiore dal robot scegliDirezione(); sceglie la direzione da prendere in funzione della distanza a cui si trova l'ostacolo */ // Definizioni rotazione senso orario e antiorario. // Dipende da come colleghiamo i motori (eventualmente scambiare le connessioni) #define CW 0 #define CCW 1 // Definizione nomi dei motori: #define MOTORE_A 0 #define MOTORE_B 1 // assegnazione pin // // Da non cambiare perchè usati dall'Arduino Motor Shields R3 const byte PWMA = 3; // controllo PWM (velocità) per il motore A const byte PWMB = 11; // controllo PWM (velocità) per il motore B const byte DIRA = 12; // Controllo direzione per il motore A const byte DIRB = 13; // Controllo direzione per il motore B const byte BRAKEA = 9; // pin di stop per il motore A const byte BRAKEB = 8; // pin di stop per il motore B // Impostazione sensore ultrasuoni const int distanzaPericolo = 20; // distanza minima dell'ostacolo (in cm) int distanzaSinistra, distanzaDestra; // distanza sinistra e destra int misuraDistanza = 0; long durata; // durata dell'impulso long distanza; // distanza dell'oggetto int pin_segnale = 7; // pin Arduino a cui è collegato il sensore SR04 int pin_trig = 10; // pin Arduino a cui è collegato il sensore SR04 // Impostazione velocità avanti e indietro int avantiveloce = 127; // fermo = 0; medio = 127; massimo = 255 int indietroveloce = 127; // fermo = 0; medio = 127; massimo = 255 int velRotazioneOraria = 80; // velocità rotazione del robot in senso orario int velRotazioneAntioraria = 80; // velocità rotazione del robot in senso antiorario int tempoAngolo = 350; // tempo calcolato per una rotazione di 90° // per funzione calibrazione int scala = 350; // per funzione calibrazione - tempo calcolato per una rotazione di 90° int flagA = 0; // per funzione calibrazione int flagB = 0; // per funzione calibrazione // regolazione decellerzione motori // i motori possono avere velocità leggermente diverse, agire su questi parametri per // effettuare regolazioni int correzioneSx = 0; // decellerazione motore Sx int correzioneDx = 0; // decellerazione motore Dx void setup() { setupDotBot(); // Imposta tutti i pin della scheda delay(2000); // attesa di 2 secondi prima della partenza } void calibrazioneRotazione(int scala) { // impostazione di un angolo di rotazione fisso // decommentare le sezioni che si intendono utilizzare //Stabilito il tempo necessario per effettuare una rotazione di 90° //sistituire questo tempo nella variabile globale tempoAngolo // movimento singolo if (flagA == 0) { rotazioneOraria(scala); flagA++; } /* // movimento ripetuto avanti e indietro in un arco di 90° rotazioneOraria(scala); delay(1000); rotazioneAntioraria(scala); delay(1000); // movimento ripetuto avanti e indietro in un arco di 180° rotazioneOraria(scala); delay(1000); rotazioneAntioraria(scala); rotazioneAntioraria(scala); delay(1000); // movimento circolare in senso orario con stop finale if (flagB == 0) { rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); flagAB++ } */ } void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" utilizzare la funzione calibrazioneRotazione(int scala) per impostare un angolo di rotazione di circa 90° */ // calibrazione // decommentare calibrazioneRotazione() e commentare il testo del codice del loop() // calibrazioneRotazione(scala); misuraDistanza = distanzaOstacolo(); if (misuraDistanza > distanzaPericolo) // vai avanti { avantiSempre(); // se non ci sono ostacoli vai avanti } // valutazione dell'ostacolo che si trova a distanza maggiore // se l'ostacolo si trova a distanza minore di "distanzaPericolo" // bisogna scegliere la direzione else { stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); scegliDirezione(); } } void scegliDirezione() { rotazioneOraria(tempoAngolo); distanzaDestra = distanzaOstacolo(); // lettura distanza ostacolo di dx delay(500); rotazioneAntioraria(tempoAngolo); rotazioneAntioraria(tempoAngolo); distanzaSinistra = distanzaOstacolo(); // lettura distanza ostacolo di sx delay(500); rotazioneOraria(tempoAngolo); delay(100); paragonaDistanze(); } // driveDotBot // pilota il 'motore' in senso 'dir' (orario/antiorario) alla velocità 'vel' // con possibilità di azionare freno 'brake' void driveDotBot(byte motore, byte dir, byte vel, byte brake) { if (motore == MOTORE_A) { digitalWrite(DIRA, dir); digitalWrite(BRAKEA, brake); analogWrite(PWMA, vel); } else if (motore == MOTORE_B) { digitalWrite(DIRB, dir); digitalWrite(BRAKEB, brake); analogWrite(PWMB, vel); } } // rilevazione distanza ostacolo // misura la distanza dell'ostacolo long distanzaOstacolo() { digitalWrite(pin_trig, LOW); delayMicroseconds(2); digitalWrite(pin_trig, HIGH); delayMicroseconds(10); digitalWrite(pin_trig, LOW); durata = pulseIn(pin_segnale, HIGH); distanza = (durata / 2) / 29.1; delay(100); return distanza; } // paragona la distanza dell'ostacolo destro da quello sinistra ed effettua una scelta void paragonaDistanze() { if (distanzaSinistra > distanzaDestra) // vai a sinistra perchè ostacolo a sx più lontano { rotazioneAntioraria(tempoAngolo); delay(500); } else if (distanzaDestra > distanzaSinistra) // vai a destra perchè ostacolo a dx più lontano { rotazioneOraria(tempoAngolo); delay(500); } else // se le distanze degli ostacoli a { // sx e dx sono uguali gira di 180 gradi (circa) rotazioneOraria(tempoAngolo); rotazioneOraria(tempoAngolo); delay(1000); } } // Impostazione movimenti del robot // stopDotBot ferma i motori void stopDotBot(byte motore) { driveDotBot(motore, 0, 0, 1); } // Rotazione antioraria void rotazioneAntioraria(int tempoRotazione) { driveDotBot(MOTORE_A, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore A in senso orario a metà velocità max driveDotBot(MOTORE_B, CW, velRotazioneAntioraria, 0); // Imposta la rotazione del motore B in senso orario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Rotazione oraria void rotazioneOraria(int tempoRotazione) { driveDotBot(MOTORE_A, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore A in senso antiorario a metà velocità max driveDotBot(MOTORE_B, CCW, velRotazioneOraria, 0); // Imposta la rotazione del motore B in senso antiorario a metà velocità max delay(tempoRotazione); // Il motore girerà per un tempo pari a "tempoRotazione" stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); } // Avanti senza stop void avantiSempre() { int avantiDx = avantiveloce - correzioneDx; int avantiSx = avantiveloce - correzioneSx; driveDotBot(MOTORE_A, CCW, avantiDx, 0); // Imposta la rotazione del motore A in senso antiorario alla velocità massima driveDotBot(MOTORE_B, CW, avantiSx, 0); // Imposta la rotazione del motore B in senso orario alla velocità massima } // indietro senza stop void indietroSempre() { driveDotBot(MOTORE_A, CW, indietroveloce, 0); // Imposta la rotazione del motore A in senso orario alla velocità massima driveDotBot(MOTORE_B, CCW, indietroveloce, 0); // Imposta la rotazione del motore B in senso antiorario alla velocità massima } // impostazione pin schede void setupDotBot() { // impostazione pin per sensore ultrasuoni SR04 pinMode(pin_trig, OUTPUT); pinMode(pin_segnale, INPUT); // Tutti i pin devono essere impostati come output pinMode(PWMA, OUTPUT); // Velocità motore A pinMode(PWMB, OUTPUT); // Velocità motore B pinMode(DIRA, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale A pinMode(DIRB, OUTPUT); // Inizializzazione come OUTPUT del pin del motore sul canale B pinMode(BRAKEA, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale A pinMode(BRAKEB, OUTPUT); // Inizializzazione come OUTPUT del pin di fermata del motore sul canale B // inizializazione di tutti i pin a LOW digitalWrite(PWMA, LOW); digitalWrite(PWMB, LOW); digitalWrite(DIRA, LOW); digitalWrite(DIRB, LOW); digitalWrite(BRAKEA, LOW); digitalWrite(BRAKEB, LOW); }
Descrizione delle sezioni dello sketch 6
Nota: si descrivono solo le sezioni aggiunte rispetto agli sketch precedenti.
Variabili utilizzate per la calibrazione di DotBot
... // per funzione calibrazione int scala = 350; // per funzione calibrazione - tempo calcolato per una rotazione di 90° int flagA = 0; // per funzione calibrazione int flagB = 0; // per funzione calibrazione ...
Calibrazione della rotazione di DotBot
Per la calibrazione manuale decommentare la sezione che si intende utilizzare
... void calibrazioneRotazione(int scala) { // impostazione di un angolo di rotazione fisso // decommentare le sezioni che si intendono utilizzare //Stabilito il tempo necessario per effettuare una rotazione di 90° //sistituire questo tempo nella variabile globale tempoAngolo // movimento singolo if (flagA == 0) { rotazioneOraria(scala); flagA++; } /* // movimento ripetuto avanti e indietro in un arco di 90° rotazioneOraria(scala); delay(1000); rotazioneAntioraria(scala); delay(1000); // movimento ripetuto avanti e indietro in un arco di 180° rotazioneOraria(scala); delay(1000); rotazioneAntioraria(scala); rotazioneAntioraria(scala); delay(1000); // movimento circolare in senso orario con stop finale if (flagB == 0) { rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); rotazioneOraria(scala); delay(1000); flagAB++ } */ } ...
Impostazione del sensore ad ultrasuoni
... // Impostazione sensore ultrasuoni const int distanzaPericolo = 20; // distanza minima dell'ostacolo (in cm) int distanzaSinistra, distanzaDestra; // distanza sinistra e destra int misuraDistanza = 0; long durata; // durata dell'impulso long distanza; // distanza dell'oggetto int pin_segnale = 7; // pin Arduino a cui è collegato il sensore SR04 int pin_trig = 10; // pin Arduino a cui è collegato il sensore SR04
Questa sezione è dedicata all’impostazione del sensore ad ultrasuoni.
La variabile “durata” è il tempo impiegato dall’onda ultrasonica per giungere al rilevatore, questo valore verrà utilizzato per il calcolo della distanza dell’oggetto.
Le due variabili: pin_segnale e pin_trig sono quelle associate ai pin signal e trigger del sensore ad ultrasuoni.
Descrizione funzionamento loop()
... void loop() { /* Calibrazione rotazione Si ricordi che la rotazione, senza il controllo dei numeri di giri, dipende dalla carica delle batterie, quindi per ogni test effettuare un controllo ed eventualmente modificare la variabile: "tempoRotazione" utilizzare la funzione calibrazioneRotazione(int scala) per impostare un angolo di rotazione di circa 90° */ // calibrazione // decommentare calibrazioneRotazione() e commentare il testo del codice del loop() // calibrazioneRotazione(scala); misuraDistanza = distanzaOstacolo(); if (misuraDistanza > distanzaPericolo) // vai avanti { avantiSempre(); // se non ci sono ostacoli vai avanti } // valutazione dell'ostacolo che si trova a distanza maggiore // se l'ostacolo si trova a distanza minore di "distanzaPericolo" // bisogna scegliere la direzione else { stopDotBot(MOTORE_A); stopDotBot(MOTORE_B); scegliDirezione(); } } ...
Nel caso in cui si voglia calibrare DotBot togliere il commento a calibrazioneRotazione(scala) e commentare il resto del codice presente in loop().
Nel loop() come prima azione viene memorizzata nella variabile misuraDistanza il valore restituito dalla funzione che calcola la distanza dell’ostacolo: distanzaOstacolo() che riceve le informazioni dal sensore ad ultrasuoni.
Se la distanza misurata è maggiore della distanzaPericolo, la minima distanza dall’ostacolo, allora vuol dire che DotBot può continuare ad andare avanti, altrimenti se la misuraDistanza è inferiore alla distanza di pericolo bisogna fermare DotBot ed effettuare il cambio di direzione
... else { stopArdumoto(MOTORE_A); stopArdumoto(MOTORE_B); scegliDirezione(); } ...
Scelta della direzione da prendere
... void scegliDirezione() { rotazioneOraria(tempoAngolo); distanzaDestra = distanzaOstacolo(); // lettura distanza ostacolo di dx delay(500); rotazioneAntioraria(tempoAngolo); rotazioneAntioraria(tempoAngolo); distanzaSinistra = distanzaOstacolo(); // lettura distanza ostacolo di sx delay(500); rotazioneOraria(tempoAngolo); delay(100); paragonaDistanze(); } ...
DotBot effettua una rotazione di 90° a destra, effettua la lettura ed assegna il valore letto alla variabile distanzaDestra, si ferma nella posizione per mezzo secondo.
DotBot effettua una rotazione di 180° in senso antiorario rispetto alla precedente posizione effettua la lettura della distanza dell’ostacolo che viene memorizzata nella variabile distanzaSinistra, si ferma nella posizione per mezzo secondo.
Si pone nuovamente in posizione frontale ruotando in senso orario di 90°, mantiene la posizione ed invoca la funzione paragonaDistanze() che verifica quale delle due misure risulta maggiore.
Lettura della distanza dell’ostacolo
... // rilevazione distanza ostacolo // misura la distanza dell'ostacolo long distanzaOstacolo() { digitalWrite(pin_trig, LOW); delayMicroseconds(2); digitalWrite(pin_trig, HIGH); delayMicroseconds(10); digitalWrite(pin_trig, LOW); durata = pulseIn(pin_segnale, HIGH); distanza = (durata / 2) / 29.1; delay(100); return distanza; } ...
La modalità di funzionamento di questa funzione è stata descritta nella parte iniziale della sezione dedicata allo sketch 6.
Paragonare le distanze per la scelta del percorso
... // paragona la distanza dell'ostacolo destro da quello sinistra ed effettua una scelta void paragonaDistanze() { if (distanzaSinistra > distanzaDestra) // vai a sinistra perchè ostacolo a sx più lontano { rotazioneAntioraria(tempoAngolo); delay(500); } else if (distanzaDestra > distanzaSinistra) // vai a destra perchè ostacolo a dx più lontano { rotazioneOraria(tempoAngolo); delay(500); } else // se le distanze degli ostacoli a { // sx e dx sono uguali gira di 180 gradi (circa) rotazioneOraria(tempoAngolo); rotazioneOraria(tempoAngolo); delay(1000); } } ...
Nel caso in cui l’ostacolo che si trova a sinistra è ad una distanza maggiore da quello dell’ostacolo di destra, allora DotBot ruoterà in senso antiorario di 90° e manterrà la posizione raggiunta per mezzo secondo.
Se invece l’ostacolo che si trova a destra si trova a distanza maggiore di quello di sinistra, allora DotBot ruoterà in senso orario di 90° e manterrà la posizione raggiunta per mezzo secondo.
Se le distanze degli ostacoli a sinistra e a destra sono identiche, DotBot ruota di 180° in senso orario e mantiene la posizione per 1 secondo.
Rotazione antioraria e oraria
Le due funzioni sono state parametrizzate ed accettano come variabile di ingresso tempoRotazione che è quello che viene valutato in fase di calibrazione manuale.
... void rotazioneAntioraria(int tempoRotazione) void rotazioneOraria(int tempoRotazione) ...
Movimento in avanti
... // Avanti senza stop void avantiSempre() { int avantiDx = avantiveloce - correzioneDx; int avantiSx = avantiveloce - correzioneSx; driveDotBot(MOTORE_A, CCW, avantiDx, 0); // Imposta la rotazione del motore A in senso antiorario alla velocità massima driveDotBot(MOTORE_B, CW, avantiSx, 0); // Imposta la rotazione del motore B in senso orario alla velocità massima } ...
Per movimentare DotBot in avanti è necessario far girare la ruota sinistra in senso antiorario e lo destra in senso orario alla velocità massima.
Nella funzione sono state aggiunte le variabili avantiDx e avantiSx che permettono una regolazione fine della velocità di rotazione delle due ruote che potrebbe essere leggermente differente.
Movimento indietro
... // indietro senza stop void indietroSempre() int indietroDx = indietroveloce - correzioneDx; int indietroSx = indietroveloce - correzioneSx; { driveDotBot(MOTORE_A, CW, indietroDx, 0); // Imposta la rotazione del motore A in senso orario alla velocità massima driveDotBot(MOTORE_B, CCW, indietroSx, 0); // Imposta la rotazione del motore B in senso antiorario alla velocità massima } ...
Stessa modalità di funzionamento della precedente funziona ma in questo caso DotBot si muoverà in senso opposto.
Proposte esercizi di approfondimento
Esercizio 1
Ad ogni stop del robot si accendere un led rosso.
Esercizio 2
Ad ogni stop si accendere un led rosso e durante la scelta della direzione lampeggia un led giallo.
Esercizio 3
Ad ogni stop si accendere un led rosso e durante la scelta della direzione lampeggia un led giallo, mentre il movimento in avanti ed indietro è evidenziato da un led verde acceso che dovrà spegnersi durante la scelta della direzione.
Esercizio 4
Aggiungere al circuito dell’esercizio precedente un buzzer che all’avvio del robot per 2 secondi, vengano suonate alcune note musicali.
Esercizio 5
Aggiungere al circuito dell’esercizio precedente un buzzer che all’avvio del robot per 2 secondi,emetta un jingle musicale ed ogni volta che viene effettuata la rilevazione del percorso da intraprendere vengano suonate alcune note musicali.
Esercizio 6
Aggiungere una funzione di casualità che ad intervalli prestabiliti fa cambiare direzione al robot indipendentemente dalla scelta della direzione verso l’ostacolo più lontano.
Buona sperimentazione a tutti 🙂
Ottimo, ottimo articolo (ripetizione voluta!!)
Grazie per la costante immissione in rete degli articoli e demo e nws ; ma qualche volta riesci a dormire?? ;-))
da un affezionato lettore . Arrigo
Grazie Arrigo sei gentilissimo. In effetti preso dalle attività dormo poco andrò in letargo in estate.
Un caro saluto.