Avoiding obstacles robot
Descrizione
Questo progetto è un robot autonomo evitante ostacoli costruito con un Arduino, due motori DC, un sensore di distanza a ultrasuoni e un servomotore. Il sensore a ultrasuoni è montato sul servo in modo che possa ispezionare il fronte, il lato destro e il lato sinistro del robot. Mentre il percorso è libero, il robot si muove automaticamente in avanti. Quando rileva un ostacolo entro 20 cm, si ferma, torna indietro, esegue la scansione di entrambi i lati e gira verso la direzione con più spazio disponibile. Questo progetto è utile per imparare la misurazione della distanza a ultrasuoni, la scansione controllata dal servo, il controllo della direzione dei motori, gli algoritmi di decisione e la navigazione robotica autonoma.
Componenti necessari:
- 1x Arduino UNO
- 1x sensore a ultrasuoni HC-SR04
- 1 servomotore da 5V
- 1x driver motore L298N
- 2x Motoriduttore da 5/12V
- Cavi jumper (breadboard opzionale)
Schema:
Codice:
// https://nemiatools.com
#include <NewPing.h> // Include la libreria NewPing
#include <Servo.h> // Include la libreria Servo
// Pin dei motori
const int leftMotorForwardPin = 2; // Pin avanti del motore sinistro
const int leftMotorBackwardPin = 3; // Pin indietro del motore sinistro
const int rightMotorForwardPin = 4; // Pin avanti del motore destro
const int rightMotorBackwardPin = 5; // Pin indietro del motore destro
// Pin del sensore e del servo
#define ultrasonicTrigPin 9 // Pin trigger dell'ultrasonico
#define ultrasonicEchoPin 8 // Pin echo dell'ultrasonico
#define scannerServoPin 10 // Pin del servo scanner
#define maxSensorDistance 200 // Distanza massima del sensore
bool movingForward = false; // Tiene traccia del movimento in avanti
int frontDistance = 100; // Memorizza la distanza frontale
NewPing ultrasonicSensor(ultrasonicTrigPin, ultrasonicEchoPin, maxSensorDistance); // Crea il sensore ultrasonico
Servo scannerServo; // Crea l'oggetto servo
void setup() {
pinMode(rightMotorForwardPin, OUTPUT); // Imposta il pin come uscita
pinMode(leftMotorForwardPin, OUTPUT); // Imposta il pin come uscita
pinMode(leftMotorBackwardPin, OUTPUT); // Imposta il pin come uscita
pinMode(rightMotorBackwardPin, OUTPUT); // Imposta il pin come uscita
scannerServo.attach(scannerServoPin); // Collega il pin del servo
scannerServo.write(115); // Centra il servo
delay(2000); // Attendi due secondi
frontDistance = readDistance(); // Legge la distanza frontale
delay(100); // Attendi brevemente
frontDistance = readDistance(); // Legge la distanza frontale
delay(100); // Attendi brevemente
frontDistance = readDistance(); // Legge la distanza frontale
delay(100); // Attendi brevemente
frontDistance = readDistance(); // Legge la distanza frontale
delay(100); // Attendi brevemente
} // La funzione setup termina
int scanRight() { // Scansiona il lato destro
scannerServo.write(50); // Gira il servo a destra
delay(500); // Attendi il movimento
int rightDistance = readDistance(); // Memorizza la distanza destra
delay(100); // Attendi brevemente
scannerServo.write(115); // Centra il servo
return rightDistance; // Restituisce la distanza destra
}
int scanLeft() { // Scansiona il lato sinistro
scannerServo.write(170); // Gira il servo a sinistra
delay(500); // Attendi il movimento
int leftDistance = readDistance(); // Memorizza la distanza sinistra
delay(100); // Attendi brevemente
scannerServo.write(115); // Centra il servo
return leftDistance; // Restituisce la distanza sinistra
}
int readDistance() { // Legge la distanza del sensore
delay(70); // Stabilizza la lettura del sensore
int distanceInCm = ultrasonicSensor.ping_cm(); // Misura la distanza in centimetri
if (distanceInCm == 0) { // Controlla una lettura non valida
distanceInCm = 250; // Imposta la distanza di riserva
}
return distanceInCm; // Restituisce la distanza misurata
}
void stopMotors() { // Ferma tutti i motori
digitalWrite(rightMotorForwardPin, LOW); // Ferma l'avanzamento destro
digitalWrite(leftMotorForwardPin, LOW); // Ferma l'avanzamento sinistro
digitalWrite(rightMotorBackwardPin, LOW); // Ferma la retromarcia destra
digitalWrite(leftMotorBackwardPin, LOW); // Ferma la retromarcia sinistra
}
void driveForward() { // Muove il robot in avanti
if (!movingForward) { // Controlla che non si stia già muovendo in avanti
movingForward = true; // Segna il movimento in avanti
digitalWrite(leftMotorForwardPin, HIGH); // Abilita l'avanzamento sinistro
digitalWrite(rightMotorForwardPin, HIGH); // Abilita l'avanzamento destro
digitalWrite(leftMotorBackwardPin, LOW); // Disabilita la retromarcia sinistra
digitalWrite(rightMotorBackwardPin, LOW); // Disabilita la retromarcia destra
}
}
void driveBackward() { // Muove il robot all'indietro
movingForward = false; // Cancella lo stato di avanzamento
digitalWrite(leftMotorBackwardPin, HIGH); // Abilita la retromarcia sinistra
digitalWrite(rightMotorBackwardPin, HIGH); // Abilita la retromarcia destra
digitalWrite(leftMotorForwardPin, LOW); // Disabilita l'avanzamento sinistro
digitalWrite(rightMotorForwardPin, LOW); // Disabilita l'avanzamento destro
}
void turnRobotRight() { // Gira il robot a destra
digitalWrite(leftMotorForwardPin, HIGH); // Abilita l'avanzamento sinistro
digitalWrite(rightMotorBackwardPin, HIGH); // Abilita la retromarcia destra
digitalWrite(leftMotorBackwardPin, LOW); // Disabilita la retromarcia sinistra
digitalWrite(rightMotorForwardPin, LOW); // Disabilita l'avanzamento destro
delay(500); // Gira per la durata indicata
stopMotors(); // Ferma dopo la svolta
}
void turnRobotLeft() { // Gira il robot a sinistra
digitalWrite(leftMotorBackwardPin, HIGH); // Abilita la retromarcia sinistra
digitalWrite(rightMotorForwardPin, HIGH); // Abilita l'avanzamento destro
digitalWrite(leftMotorForwardPin, LOW); // Disabilita l'avanzamento sinistro
digitalWrite(rightMotorBackwardPin, LOW); // Disabilita la retromarcia destra
delay(500); // Gira per la durata indicata
stopMotors(); // Ferma dopo la svolta
}
void loop() {
int rightScanDistance = 0; // Inizializza la distanza destra
int leftScanDistance = 0; // Inizializza la distanza sinistra
delay(50); // Attendi brevemente
if (frontDistance <= 20) { // Rileva un ostacolo vicino
stopMotors(); // Ferma il robot
delay(300); // Attendi brevemente
driveBackward(); // Muove all'indietro
delay(400); // Va in retromarcia per la durata indicata
stopMotors(); // Ferma il robot
delay(300); // Attendi brevemente
rightScanDistance = scanRight(); // Scansiona la distanza destra
delay(300); // Attendi brevemente
leftScanDistance = scanLeft(); // Scansiona la distanza sinistra
delay(300); // Attendi brevemente
if (rightScanDistance >= leftScanDistance) { // Confronta le distanze laterali
turnRobotRight(); // Gira a destra
} else { // Altrimenti gira a sinistra
turnRobotLeft(); // Gira a sinistra
}
stopMotors(); // Ferma il robot
} else { // Il percorso è libero
driveForward(); // Muove in avanti
}
frontDistance = readDistance(); // Aggiorna la distanza frontale
}
Come funziona:
Questo progetto controlla un robot autonomo che si muove in avanti e cambia direzione ogni volta che rileva un ostacolo. Il sensore a ultrasuoni misura lo spazio libero davanti, mentre il servo ruota il sensore per confrontare i lati destro e sinistro prima che il robot scelga dove svoltare.
La riga #include <NewPing.h> aggiunge la libreria NewPing. Questa libreria semplifica l'uso dei sensori a ultrasuoni generando automaticamente l'impulso di trigger, misurando l'eco di ritorno e convertendo il risultato in distanza.
La riga #include <Servo.h> aggiunge la libreria Servo di Arduino. Essa genera gli impulsi di controllo necessari per posizionare il servomotore a un angolo selezionato.
I quattro pin dei motori controllano i due motori DC tramite un driver per motori o un ponte H. Le righe leftMotorForwardPin e leftMotorBackwardPin controllano la direzione del motore sinistro, mentre rightMotorForwardPin e rightMotorBackwardPin controllano il motore destro.
Ogni motore ha un ingresso per la rotazione in avanti e uno per la rotazione all'indietro. Impostare l'ingresso forward su HIGH e l'ingresso backward su LOW fa ruotare quel motore in avanti. Invertendo questi stati si cambia direzione. Impostare entrambi gli ingressi su LOW arresta il motore.
I motori non devono essere collegati direttamente ai pin Arduino perché richiedono più corrente di quanta il microcontrollore possa fornire. Questi pin sono destinati a controllare un driver per motori adatto, come un modulo L293D, L298N, TB6612FNG o un altro modulo ponte H.
Il sensore a ultrasuoni usa il pin 9 come collegamento di trigger e il pin 8 come collegamento di echo. Il servo che ruota il sensore è collegato a il pin 10.
La riga #define maxSensorDistance 200 imposta la distanza massima di misura del sensore a ultrasuoni a 200 cm. Limitare la portata riduce il tempo speso ad attendere echi provenienti da oggetti molto lontani.
L'oggetto NewPing ultrasonicSensor(ultrasonicTrigPin, ultrasonicEchoPin, maxSensorDistance); associa il sensore a ultrasuoni ai suoi pin e alla distanza massima. L'oggetto Servo scannerServo; rappresenta il servomotore usato dallo scanner.
La variabile frontDistance memorizza l'ultima distanza misurata davanti al robot. La variabile movingForward registra se il comando di avanzamento è già stato applicato, evitando la ripetizione inutile dello stesso comando del motore.
All'interno di setup(), i quattro pin di controllo dei motori sono configurati come uscite. La riga scannerServo.attach(scannerServoPin); collega l'oggetto Servo al pin 10.
La riga scannerServo.write(115); porta il sensore a ultrasuoni nella posizione centrale. Il centro è impostato a 115 gradi invece che esattamente a 90 perché l'angolo corretto può dipendere dall'installazione meccanica del servo e della staffa del sensore.
Il programma attende due secondi per permettere al servo di raggiungere la sua posizione. Poi esegue quattro misurazioni frontali. Queste letture iniziali aiutano il sensore a ultrasuoni e lo scanner meccanico a stabilizzarsi prima dell'inizio della navigazione normale.
La funzione readDistance() esegue una misura a ultrasuoni. Il delay(70); iniziale lascia abbastanza tempo perché gli echi ultrasonici precedenti scompaiano, riducendo misurazioni instabili o sovrapposte.
La riga int distanceInCm = ultrasonicSensor.ping_cm(); invia un impulso a ultrasuoni e restituisce la distanza misurata in centimetri. Il sensore emette un'onda sonora ad alta frequenza e attende il suo riflesso da un oggetto. La libreria calcola la distanza dal tempo di andata e ritorno di quell'onda sonora.
Se non viene ricevuto alcun eco valido entro la portata configurata, ping_cm() restituisce zero. La condizione if (distanceInCm == 0) sostituisce questo valore con 250, così un eco mancante viene trattato come un oggetto più lontano del limite di misura di 200 cm.
La funzione scanRight() ruota il sensore a 50 gradi, attende 500 millisecondi che il servo raggiunga la posizione e misura lo spazio disponibile sulla destra. Poi comanda al servo di tornare alla posizione centrale.
La funzione scanLeft() esegue lo stesso processo a 170 gradi per misurare lo spazio libero sulla sinistra. Gli angoli esatti possono essere regolati in base all'orientamento e ai limiti meccanici del servo.
La funzione stopMotors() imposta tutti e quattro i segnali di controllo dei motori su LOW. Questo rimuove ogni comando di avanzamento e retromarcia e arresta entrambi i motori.
La funzione driveForward() attiva l'ingresso forward di entrambi i motori e disabilita entrambi gli ingressi backward. Poiché le due ruote ruotano insieme in avanti, il robot si muove in linea retta.
La condizione if (!movingForward) applica questi segnali solo quando il robot non si stava già muovendo in avanti. La variabile viene quindi impostata su true per registrare il nuovo stato di movimento.
La funzione driveBackward() attiva l'ingresso backward di entrambi i motori e disabilita i loro ingressi forward. Entrambe le ruote invertono il verso insieme, allontanando il robot dall'ostacolo rilevato.
La funzione turnRobotRight() fa avanzare il motore sinistro e arretrare il motore destro. Poiché le ruote ruotano in direzioni opposte, il robot gira approssimativamente attorno al proprio centro invece di seguire una curva ampia.
La funzione turnRobotLeft() esegue l'azione opposta: la ruota sinistra si muove all'indietro mentre la ruota destra si muove in avanti. Ogni svolta dura 500 millisecondi, dopo i quali i motori vengono fermati.
All'interno di loop(), il programma controlla prima l'ultimo valore memorizzato in frontDistance. La condizione if (frontDistance <= 20) significa che un ostacolo a 20 cm o più vicino è considerato pericoloso.
Quando viene rilevato un ostacolo, il robot si ferma per 300 millisecondi e poi fa retromarcia per 400 millisecondi. Questo crea ulteriore spazio tra il robot e l'ostacolo prima che inizi la scansione laterale.
Il robot si ferma di nuovo e misura il lato destro con scanRight(). Poi misura il lato sinistro con scanLeft(). I due risultati rappresentano la quantità di spazio libero disponibile in ciascuna direzione.
La condizione if (rightScanDistance >= leftScanDistance) fa sì che il robot svolti a destra quando il lato destro è almeno altrettanto libero quanto il sinistro. Altrimenti, svolta a sinistra.
Dopo la svolta, il robot si ferma brevemente. Nei cicli successivi, se il percorso frontale è libero, driveForward() lo fa continuare a muoversi.
Quando frontDistance è maggiore di 20 cm, la sequenza di evitamento dell'ostacolo viene saltata e il robot continua a viaggiare in avanti.
Alla fine di ogni ciclo, frontDistance = readDistance(); aggiorna la misurazione frontale. Questo nuovo valore viene utilizzato durante l'esecuzione successiva del ciclo per decidere se il robot deve continuare o iniziare una manovra di evitamento.
La sequenza completa di navigazione è quindi: avanzare, monitorare la distanza frontale, fermarsi quando un ostacolo è vicino, fare retromarcia, scansionare entrambi i lati, confrontare lo spazio disponibile, girare verso la direzione più libera e continuare a muoversi.
Per un funzionamento affidabile, utilizzare un driver per motori adatto e un'alimentazione separata in grado di fornire la corrente richiesta dai motori. Arduino, driver dei motori, sensore, servo e alimentazione dei motori devono condividere una massa comune. Poiché un eco ultrasonico mancante viene interpretato come percorso libero, questo robot deve essere considerato un prototipo didattico piuttosto che un sistema di navigazione critico per la sicurezza. Inoltre, nel diagramma dei collegamenti attuale, l'alimentazione è affidata a una singola batteria standard da 9 V. Nella pratica, questo tipo di batteria non è in grado di erogare in modo continuativo le correnti dell'ordine di 1–2 A richieste dai motori, con conseguente rischio di cali di tensione e prestazioni insufficienti del sistema. Si raccomanda pertanto l'utilizzo di una batteria LiPo ad alta capacità, abbinata a un convertitore DC-DC step-up opportunamente dimensionato, in grado di garantire la tensione e la corrente necessarie al corretto funzionamento del circuito.