Arduino + Lego = Robot evita ostacoli – Obstacle avoidance robot

Come primo progetto per scoprire la potenza e la semplicità di Arduino ho deciso di costruire un piccolo robot in grado di muoversi ed evitare gli ostacoli in completa autonomia.
Fin dalle scuole superiori sono stato affascinato dal mondo dell’elettronica ed inoltre la mia passione (e anche mestiere) è programmare, sono riuscito a combinare entrambe le cose in questo progetto.
Come prima cosa mi sembra giusto dire che per sviluppare questo robot sono necessari un minimo di conoscenze di elettronica, nonchè un pò di manualità ed ingegno per risolvere i vari problemi che si possano presentare.

Volendo descrivere brevemente il mio progetto sono presenti quattro componenti principali:

  1. Arduino, il cervello del sistema
  2. il motore che fa muovere il robot e il motor driver per controllare il motore (verso e velocità)
  3. lo sterzo e il servo comando per cambiare direzione
  4. sensore di prossimità, per rilevare gli ostacoli


Elenco componenti e prezzi

Componente Qta Costo EUR (approx)
Arduino Uno 1 20,00
Hitec HS-422 1 9,00
SRF05 – Ultra-Sonic Ranger 1 23,00
Tamiya Twin Motor Gearbox (70097) 1 15,00
Tamiya Truck Tire Set 1 6,00
SN754410NE Motor driver 1 3,80
L7806 (regolatore tensione 6V) 1 0,50
LM1117 (regolatore tensione 3.3V) 1 1,50
Condensatore 100µF 1
Condensatore 10µF 1
Condensatore 0.33µF 1
Condensatore 0.1µF 1
Breadboard 1 5,00
Connettore coassiale 2,1 mm 1 1,00
TOTALE 78,80

Altro materiale necessario:

  • mattoncini Lego Technic
  • cavi di varie lunghezze e colori
  • batteria (consiglio una Ni-MH da 8,4V oppure una Li-Po 2S 7,4V)

Costruzione del prototipo

L’immagine seguente rappresenta lo schema elettrico del sistema:

Per chi ancora non lo conoscesse, per disegnarlo ho utilizzato Fritzing (http://fritzing.org), un software open source sviluppato per la progettazione elettronica.

Alcune note tecniche sul circuito:

  • entrambi i regolatori di tensione hanno bisogno di condensatori per smorzare i possibili sbalzi di corrente (come indicato nei relativi datasheet)
  • il gearbox utilizzato contiene due motori indipendenti, e ognuno è collegato ad un canale diverso del SN754410 in maniera da non sovraccaricare il circuito integrato. Ho collegato i pin di enable e input dei due canali assieme, in modo da far operare i due motori in parallelo.
  • il sensore SRF05 ha due modalità di funzionamento: pin separati per trigger ed echo oppure un unico pin per entrambi. Ho utilizzato la seconda modalità per utilizzare meno pin su Arduino.

Ho scelto di utilizzare i mattoncini Lego Technic perché ne possiedo in quantità industriale e sono molto versatili. Alla fine della costruzione però ho notato che rendono il robot più pesante di quanto mi sarei aspettato, comunque ritengo sia una valida scelta.

La prima pietra è stata la costruzione del telaio attorno al gearbox dei motori ed il posizionamento di Arduino.

Il secondo passo (nonché uno dei più complicati per me) è stata la costruzione dello sterzo con i pezzi che avevo a disposizione e l’innesto del servo comando con gli ingranaggi Lego. Il risultato non è stato dei migliori perché lo sterzo ha molto gioco, ma fortunatamente sono riuscito a correggere il difetto tarando via software il servo.

Successivamente ho collocato il sensore di distanza SRF05 sul fronte del robot.

Il quarto ed ultimo passo, per quanto riguarda la meccanica, è stato il posizionamento della breadboard e della batteria in cima al telaio.

Infine alcune foto di dettaglio del circuito assemblato sulla breadboard.

Codice sorgente

Di seguito il codice che permette il funzionamento del robot.

#include <Servo.h>

Servo myservo;  // create servo object to control a servo

const int servoPin  = 4;
const int motor1Pin = 7;    // H-bridge leg 1 (pin 2, 1A)
const int motor2Pin = 8;    // H-bridge leg 2 (pin 7, 2A)
const int enablePin = 11;   // H-bridge enable pin
const int signalPin = 12;   // pin Arduino a cui è collegato il sensore SRF05

// distanza dell'oggetto
int distance = 0;
const int numOfReadings = 3;                    // number of readings to take/ items in the array
int readings[numOfReadings];                    // stores the distance readings in an array
int arrayIndex = 0;                             // arrayIndex of the current item in the array
int total = 0;                                  // stores the cumlative total
int avgDistance = 0;                            // stores the average value


int sogliaStop;
int sogliaSter;
int sogliaRall;

boolean isAvanti;
int randNumber;
int tmp;

void setup()
{
  Serial.begin(9600); // open serial port to receive data
  myservo.attach(servoPin);  // attaches the servo on pin 9 to the servo object

  pinMode(motor1Pin, OUTPUT);
  pinMode(motor2Pin, OUTPUT);
  pinMode(enablePin, OUTPUT);

  // spengo i motori
  digitalWrite(enablePin, LOW);

  randomSeed(analogRead(0));

  randNumber = 0;
  isAvanti = true;

  sogliaStop = 10; // soglia di stop / retromarcia
  sogliaSter = 40; // distanza a cui muovere lo sterzo
  sogliaRall = 50; // distanza a cui moderare la velocità

  // create array loop to iterate over every item in the array
  for (int thisReading = 0; thisReading < numOfReadings; thisReading++) {
    readings[thisReading] = 0;
  }

  // metto lo sterzo in posizione centrale
  myservo.write(0);
  delay(100);
  raddrizza();
}


void loop()
{
  distance = getDistance();

  total = total - readings[arrayIndex];           // subtract the last distance
  readings[arrayIndex] = distance;                // add distance reading to array
  total = total + readings[arrayIndex];           // add the reading to the total
  arrayIndex++;                                   // go to the next item in the array

  // At the end of the array then start again
  if (arrayIndex >= numOfReadings){
    arrayIndex = 0;
  }

  avgDistance = total / numOfReadings;      // calculate the average distance

  Serial.print("MEDIA: ");
  Serial.println(avgDistance);


  if(isAvanti)
  {
    if(avgDistance<=sogliaStop)
    {
      // retromarcia al 100%
      raddrizza();
      indietro(100);
    }
    else if(avgDistance<=sogliaRall)
    {
      // regola la velocità in base alla distanza dell'ostacolo
      tmp = map(avgDistance, sogliaStop, sogliaRall, 0, 100);
      avanti(tmp);

      // se la distanza è inferiore ad una soglia, sterza a dx o sx in maniera casuale
      if(avgDistance<=sogliaSter)
      {
        if(randNumber==0) randNumber = random(1,100);

        if(randNumber%2)
        {
          // destra
          myservo.write(180);
		  
		  // sinistra
          //myservo.write(0);
        }
        else
        {
		  // destra
		  myservo.write(180);
		  
          // sinistra
          //myservo.write(0);
        }
      }
    }
    else
    {
      raddrizza();
      avanti(100);
    }
  }
  else
  {
    // se sta andando in retromarcia, continua finchè l'ostacolo non è a debita distanza
    if(avgDistance<=sogliaRall)
    {
      indietro(100);
    }
    else
    {
      avanti(100);
    }
  }

  // attende 100 millisecondi prima di incominciare una nuova misura
  delay(100);
}


long getDistance()
{
  // durata dell'impulso
  unsigned long pulseTime;
  unsigned long distance;

  pinMode(signalPin, OUTPUT);
  digitalWrite(signalPin, LOW);      // viene posto a LOW pin
  delayMicroseconds(2);              // per 2 microsecondi
  digitalWrite(signalPin, HIGH);     // invia un impulso di trigger
  delayMicroseconds(10);             // di 10 microsecondi
  digitalWrite(signalPin, LOW);      // pone il pin al LOW in attesa che l'impulso torni indietro

  pinMode(signalPin, INPUT);
  pulseTime = pulseIn(signalPin, HIGH); // legge l'eco dell'impulso emesso in microsecondi
  distance = pulseTime/58;              // divide la durata per 58 per ottenere la distanza in cm

  Serial.print("DISTANZA: ");
  Serial.println(distance);             // stampa sul Serial Monitor il valore della distanza

  return distance;
}

void avanti(int velocita)
{
  // quando si regola la velocità con PWM, al di sotto del valore 180 il motore non spinge il peso
  int vel = map(velocita, 0, 100, 180, 255);

//  vel = 255;
//  vel = 254;
//  vel = 0;

  digitalWrite(motor1Pin, LOW);
  digitalWrite(motor2Pin, HIGH);
  analogWrite(enablePin, vel);

  isAvanti = true;

  Serial.print("AVANTI - Input: ");
  Serial.print(velocita);
  Serial.print(" - Output: ");
  Serial.println(vel);
}

void indietro(int velocita)
{
  int vel = map(velocita, 0, 100, 100, 255);

  vel = 255;

  digitalWrite(motor1Pin, HIGH);
  digitalWrite(motor2Pin, LOW);
  analogWrite(enablePin, vel);

  isAvanti = false;

  Serial.print("INDIETRO - Input: ");
  Serial.print(velocita);
  Serial.print(" - Output: ");
  Serial.println(vel);
}

// mette lo sterzo in posizione centrale
void raddrizza()
{
  if(myservo.read()==180)
  {
      // da destra al centro
      myservo.write(50);
      randNumber = 0;
  }
  else if(myservo.read()==0)
  {
      // da sinistra al centro
      myservo.write(110);
      randNumber = 0;
  }
}

Datasheets

Reference

http://luckylarry.co.uk/arduino-projects/obstacle-avoidance-robot-build-your-own-larrybot/
http://scuola.arduino.cc/it/content/controlla-motore-dc-ponte-h-l293d
http://scuola.arduino.cc/it/content/misuratore-distanza-sensore-ad-ultrasuoni-srf05

24 thoughts to “Arduino + Lego = Robot evita ostacoli – Obstacle avoidance robot”

  1. Grazie mille e un’altra domanda, nello schema elettrico ho notato che hai collegato l’enable pin al pin 9 di arduino mentre nel programma dichiari cosi’ la variabile:
    const int enablePin = 11; // H-bridge enable pin
    Grazie, era solo una curiosità per capire il codice.
    E leggendo ho visto che il pin e’ attaccato ad un pin digitale. Grazie mille in anticipo 🙂

    1. in questo progetto i motori DC non sono collegati agli ingranaggi Lego, ho acquistato sia i motori che le ruote posteriori a parte. Invece per quanto riguarda lo sterzo, ho unito la testa circolare del servo-motore ad una puleggia Lego con uno spago: questo collegamento non è il massimo perché dà molto gioco allo sterzo, ma attualmente non ho elaborato una soluzione migliore.

      1. ciao andrea io ho provato a realizzarlo ma mi da un problema di caricamento come se non fosse collegato ti volevo chiedere un aiuto anche perche io sono alle primearmi con arduino grazie

        alessandro

  2. ciao, posso chiederti se ha avuto problemi a far camminare il robot perfettamente dritto (ho letto che non è banale perché i 2 motori delle ruote motrici devono girare esattamente alla stessa velocità, e inoltre ci possono essere slittamenti)? Quali sono i vantaggi di usare una Tamiya Twin Motor Gearbox piuttosto che comprare separatamente due motori dc e pilotarli?

    1. No, non ho avuto nessun problema di quel tipo e non ho mai letto niente a riguardo. Tra l’altro i pin 1 e 9 del SN754410 (ovvero quelli che regolano la potenza del motore) sono collegati sulla stessa linea, quindi lavorano esattamente in parallelo.
      Il vantaggio principale di usare un Tamiya Twin Motor Gearbox è quello di avere i due motori perfettamente allineati sullo stesso asse, cosa che richiede una certa abilità manuale se si volesse costruire da soli un telaio per i due motori.

  3. Salve.
    Volevo sapere se potevo farlo senza il motore servo e la programmazione come la modificherei? non sono molto bravo a programmare quindi chiedo il vostro aiuto? Grazie mille.

  4. Senza servo motore si elimina lo sterzo, quindi il funzionamento e la meccanica del robot sono differenti. Se hai intenzione di costruirne uno senza servo, ti consiglio di cercare altri progetti in rete. Grazie per l’interessamento.

  5. ciao, il tuo progetto mi ha veramente stupito. avevo una domanda da farti: ho visto che i motori che hai usato te, si possono comprare solo a pezzi, e poi montarli. per questo progetto è obbligatorio usare proprio questi, oppure possiamo usarne altri? e quali mi consiglieresti? tieni conto che sono alle prime armi e non me ne intendo molto di queste cose. grazie

  6. posso usare il sensore ultra sonico SRF04 però lo schema dovrebbe essere un po diversa, e il code per programmare l’arduino? e ti sto chiedendo aiuto, perché è la prima volta che uso arduino e non sono bravo..

  7. Lo schema elettrico che hai riportato causa un cortocircuito che ha rischiato di bruciare il connettore ,ne potresti caricare uno più completo per favore?

  8. ciao, posso chiederti quali modifiche andrebbero apportate in caso di utilizzo di un SRF-04 al posto del SRF-05? Grazie in anticipo

  9. Scusami ma io vedo 3 motori due normale è il terzo un servo mottore. Perché? È la prima volta che faccio una macchina in questo modo è vorrei un aiuto

Rispondi a Federico Annulla risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.