Radar con Arduino

Buongiorno, nel post di oggi andremo a creare un radar con Arduino, un servomotore e un sensore di distanza.

Ovviamente non sarà come nei film con i sottomarini e il razzo che si avvicina. No, nemmeno l’interfaccia sfondo nero, scritte verdi. Che diciamocelo, gli anni ‘80 sono passati da un po’.

 

Il radar che andremo a costruire ha due componenti principali: un motore e un sensore che misura la distanza.

Questa guida, quindi, si suddividerà in due parti. La prima riguarderà come utilizzare correttamente il servomotore, mentre la seconda lavorerà l’utilizzo del sensore di ottico di distanza.

 

LA LISTA DELLA SPESA

 

FACCIAMO GIRARE QUESTO SERVO MOTORE

Eccoci alla prima parte della guida. Il servomotore che abbiamo acquistato, (... che io ho acquistato, voi fate come vi pare), ha la capacità di girare di 180 gradi.

 

Il nostro obiettivo sarà quindi quello di far girare il servo di 1 grado alla volta, e poterlo fermare in quel punto, per permettere di fare altro, ossia di effettuare la misura della distanza.

 

Vero, esistono svariate guide in giro per il web che permettono di far girare un servo, ma dato che il nostro è un motore che dovrà essere integrato direttamente con un sensore, tanto vale fare una libreria unica.

 

In questo post non vi presenterò il lavoro finito e impacchettato a dovere, bensì vi farò apprezzare il metodo iterativo (e il sudore) con cui si creano i software in generale.

 

Per prima cosa creiamo la libreria, che chiameremo (con molta fantasia) Radar.

Andiamo nel path Arduino/libraries e creiamo una cartella e due file con:

mkdir Radar

cd Radar

touch Radar.h

touch Radar.cpp

 

Apriamo il file Radar.h e andiamo a inserire:

#ifndef Radar_h
#define Radar_h

class Radar {
  private:
    int digitalPin;
    int angle;
    long int pulse0;
    int min16;
    int max16;

  public:
    Radar(int);
    void move(int);
    int getAngle();
    void setMinimumPulse(long int);
    void setMaximumPulse(long int);
    void refresh();
};

#endif

Questa è la definizione della classe. I metodi sono pochi e quelli che ci interessano veramente sono:

  • Radar(int): il costruttore. Prende come parametro il pin di Arduino a cui è collegato il servo.
  • move(int): Permette di inserire il valore dell’angolo.

 

Ora dobbiamo implementare l’header, aprendo e inserendo all’interno del file Radar.cpp il seguente contenuto:

#include "Radar.h"
#include <Arduino.h>
#define NO_ANGLE (0xff)

Radar::Radar(int d_pin) {
    digitalPin = d_pin;
    angle = NO_ANGLE;
    min16 = 34;
    max16 = 150;
    pulse0 = 0;
    digitalWrite(digitalPin, 0);
    pinMode(digitalPin, OUTPUT);
}

void Radar::setMinimumPulse(long int t) {
    min16 = t/16;
}

void Radar::setMaximumPulse(long int t) {
    max16 = t/16;
}

void Radar::move(int angleArg) {
    if (angleArg < 0) {
        angle = 0;
    } else if( angleArg >= 180) {
        angle = 180;
    } else {
        angle = angleArg;
    }

    pulse0 = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/64L;
    refresh();
}

int Radar::getAngle() {
    return angle;
}

void Radar::refresh() {
    static unsigned long lastRefresh = 0;
    unsigned long m = millis();
    long int base = 0;

    if ( m >= lastRefresh && m < lastRefresh + 20) {
        return;
    }

    lastRefresh = m;

    if (!pulse0) {
        return;
    }

    digitalWrite(digitalPin, 1);

    int start = TCNT0;
    int now = start;
    int last = now;
    long int go = start + pulse0;

    for (;;) {
        now = TCNT0;
        if ( now < last) {
            base += 256;
        }
        last = now;

        if ( base+now > go) {
            digitalWrite( digitalPin, 0);
            break;
        }
    }
}

Ora, prestiamo un attimo attenzione a una cosa. Purtroppo il servomotore non fa effettivamente 180 gradi. Ne fa un po’ di meno. In più noi siamo interessati al fatto che parta e arrivi nello stesso punto, e magari anche che la posizione di partenza sia allineata al corpo del motore.

Per intenderci, ecco una foto della posizione corretta di partenza:

Posizione Corretta del Servo

 

ed ecco quello che non vogliamo:

Posizione Errata del Servo

 

Ok. Dobbiamo quindi fare una calibrazione. Come possiamo farlo?

Semplice: utilizziamo un potenziometro e il Serial Monitor, gentilmente offerto da Arduino.

Ecco lo schema elettronico:

 

Creiamo un file di arduino e incolliamo il seguente programma.

#include <Radar.h>

Radar r(9);
int angle = 0;
int poten = A0; 

void setup(){
    Serial.begin(9600);
}

void loop(){
    angle = analogRead(poten);
    angle = map(angle, 0, 1023, 0, 250);
    Serial.println(angle);
    r.move(angle);
    delay(8);
}

 

E annotiamo i valori limite con cui il nostro motore può lavorare. Per me sono 15 e 195. Il che’ ha senso. Notiamo che con la posizione 0 del motore (figura della posizione errata, vedi sopra) l’angolo può tranquillamente essere di circa 15 gradi dalla posizione corretta. 180 + 15 fa casualmente 195, quindi anche per il limite superiore la matematica ci dà sicurezza.

 

Ok ci siamo quasi. Ora dobbiamo far ruotare periodicamente il nostro servo. Lo facciamo cambiando al programma precedente la funzione di loop, incollando questa:

void loop() {
  for(int i=15;i<=195;i++){  
    r.move(i);
    delay(20);
  }

  for(int i=195;i>15;i--){  
    r.move(i); 
    delay(20);
  }
}

 

SENSORE SHARP: L’OCCHIO DEL RADAR

In questa seconda parte completeremo la libreria Radar che stiamo scrivendo, andando ad aggiungere il calcolo della distanza, momento per momento.

Iniziamo però a capire come funziona questo sensore. Iniziamo col dire che tutta l’elettronica (per Arduino) che misura delle distanza, funziona più o meno allo stesso modo.

  • un pin per l’alimentazione da 5 Volt.
  • un pin per il GND.
  • un pin per la restituzione del valore analogico della distanza.

Il sensore Sharp arriva in due parti: il sensore e il connettore con i cavi. Quest’ultimo ha tre fili:

  • rosso: da collegare a 5 Volt.
  • nero: da collegare al pin GND.
  • giallo: lo colleghiamo al pin analogico A0.

Creiamo un file di test di Arduino, e incolliamo il seguente script:

int sensorPin = A0;
int d;

void setup() {
  Serial.begin(9600);
  pinMode(sensorPin, INPUT);
}

void loop() {
    d = getDistance();
    Serial.println(d);
}

int getDistance() {
    float sensorValue = analogRead(sensorPin);
    float cm = 10650.08 * pow(sensorValue, -0.935) - 10;
    return roundf(cm);
}

 

Ottimo, arrivati a questo punto dobbiamo integrare questo script alla libreria Radar. Integriamo la funzione getDistance all’interno della funzione move, che a questo punto restituirà un intero, che corrisponde alla distanza in centimetri dall’ostacolo.

 

Per completezza ecco il file Radar.h

#ifndef Radar_h
#define Radar_h

class Radar {
  private:
    int digitalPin;
    int analogPin;
    int angle;
    long int pulse0;
    int min16;
    int max16;

  public:
    Radar(int a_Pin, int d_Pin);
    int move(int angle);
    int getAngle();
    void setMinimumPulse(long int);
    void setMaximumPulse(long int);
    void refresh();
    int getDistance();
};

#endif

e il file Radar.cpp che implementa l’header.

 

#include "Radar.h"
#include <Arduino.h>

#define NO_ANGLE (0xff)

Radar::Radar(int a_pin, int d_pin) {
    digitalPin = d_pin;
    analogPin = a_pin;
    angle = NO_ANGLE;
    min16 = 34;
    max16 = 150;
    pulse0 = 0;
    pinMode(analogPin, INPUT);
    digitalWrite(digitalPin,0);
    pinMode(digitalPin, OUTPUT);
}

void Radar::setMinimumPulse(long int t) {
    min16 = t/16;
}

void Radar::setMaximumPulse(long int t) {
    max16 = t/16;
}

int Radar::move(int angleArg) {
    if (angleArg < 0) {
        angle = 0;
    } else if( angleArg >= 180) {
        angle = 180;
    } else {
        angle = angleArg;
    }

    pulse0 = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/64L;

    refresh();
    return getDistance();
}

int Radar::getAngle() {
    return angle;
}

void Radar::refresh() {
    static unsigned long lastRefresh = 0;
    unsigned long m = millis();
    long int base = 0;

    if ( m >= lastRefresh && m < lastRefresh + 20) {
        return;
    }

    lastRefresh = m;

    if (!pulse0) {
        return;
    }

    digitalWrite(digitalPin, 1);

    int start = TCNT0;
    int now = start;
    int last = now;
    long int go = start + pulse0;

    for (;;) {
        now = TCNT0;
        if (now < last) {
            base += 256;
        }
        last = now;

        if (base+now > go) {
            digitalWrite( digitalPin,0);
            break;
        }
    }
}

int Radar::getDistance() {
    return roundf(10650.08 * pow(analogRead(analogPin),-0.935) - 10);
}

 

Ed ecco infine il codice per Arduino:

#include <Radar.h>

int sensorPin = A0;
int servoPin = 8;
Radar r(sensorPin, servoPin);

void setup() {
  Serial.begin(9600);
}

void loop() {
  for(int i=15;i<=200;i++){  
    Serial.println(r.move(i));
    delay(30);
  }

  for(int i=195;i>15;i--){  
    Serial.println(r.move(i));
    delay(30);
  }
}

 

CONCLUSIONI

Finalmente abbiamo finito. Non è stato facile, ma ci siamo riusciti. Ora non ci resta che sbizzarrirci con le sue applicazioni.

Dubbi? Suggerimenti? Non esitate a scriverci nei commenti!

 

A presto!

 

 

Dottore in Informatica. Appassionato di computer fin dal primo PC Olivetti con Windows 95 e la bellezza di 8 MB di RAM.
Utilizzatore Linux da 5 anni, credo profondamente nella filosofia open source.
Dopo tante distro provate sul mio fidato Toshiba, uso Fedora per lo studio e il divertimento.

2 risposte

  1. Bellissima spiegazione e argomento. Un po difficile per me che di programmazione sono a digiuno,ma un po per volta spero di comprendere. Per adesso lo metto in memoria nel mio PC. per quando capirò meglio le manovre da fare e provare questa splendida ralizzazione. Risultato, spero un bel giocattolo per il mio nipotino.
    Grazie per questa spiegazione. Mario Luosi

    • Emanuele Lovera - Blogger

      Bene! Sono contento che ti sia piaciuto l'articolo! Facci sapere poi come va l'implementazione!
      A presto

      Emanuele

Lascia un commento