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:

Schema:

Circuit Scheme

Codice:

avoiding_obstacles_robot.ino
// 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.

Video dimostrazione:

Questo video non è attualmente disponibile, ci scusi per l'inconveniente.