Mark#1

Vi presentiamo il Progetto Mark#1, un primo prototipo di robot car creato in casa e con tante funzionalità

Il Progetto Mark#1 è il nostro primo prototipo di robot car in gradi di spostarsi in autonomia, adoperando alcuni sensori che abbiamo già avuto modo di conoscere su questo sito. Il cervello a comandare questo robot sarà Arduino UNO, scelto per sfruttare una motor shield in nostro possesso e in gradi di gestire i due motori elettrici che faranno muovere il nostro robot.

Progetto

Abbiamo recensito molti robot ed abbiamo appreso molto da essi, spingendoci così a crearne uno ex novo ed a modo nostro. Premettiamo che si tratta solo di un primo modello, destinato a essere sostituito da una nuova versione già in fase di progettazione: da qui il nome Mark#1 in onore del primo prototipo di armatura di Iron Man. Di base la Mark#1 è molto simile a tanti altri robot visti sul canale, tuttavia la novità sta nella scelta dei cingoli, piuttosto che le ruote gommate che spesso abbiamo trovato,  poiché donano una maggiore aderenza su molte superfici e poi perché l’aspetto da cingolato è più aggressivo. Abbiamo utilizzato anche dei led che simulano l’accensione di fari e luci di posizione, nonché un display Oled che mostra i principali movimenti del robot. Mark#1 è anche previsto di un sensore Big Sound per rilevare rumori e spaventarsi: funzione del tutto inutile (ce ne rendiamo conto), ma simpatica. Per evitare, poi, che in fase di retromarcia possa urtare con degli ostacoli, abbiamo posizionato un sensore IR che comunicherà al nostro Arduino di fermarsi. Il controllo principale sul percorso del robot è affidato al sensore HC-SR04, fissato su un servo-motore SG90.

  • Mark#1

Componenti

Come anticipato, il cervello del Mark#1 è un Arduino UNO, su cui abbiamo collocato una motor shield con chip L298P, con la qualche gestiremo velocità e direzione dei motori da 12V collocati nella scocca del robot. La motor-shield che utilizziamo in questo progetto prevedeche i pin 8 e 9 vengano adoperati per ricevere un segnale che arresti i motori, ma questa funziona sarà già integrata nel codice, quindi adopereremo questi pin per altro. Per questo motivo, elimineremo i pinheader della shield per collegarci due led.

Vi alleghiamo lo schema della shield per maggior chiarezza.

Mark#1



La scocca è in metallo e fa parte di un set acquistabile online; tuttavia, non è stato facile trovare il manuale di montaggio che vi lascio però disponibile a questo LINK, con tutte le librerie, il codice e le parti in stampa 3D da realizzare.

Il Mark#1 esplorerà l’ambiente grazie al sensore ultrasuoni HC-SR04 alloggiato su un servo-motore SG90, il quale ruoterà il sensore prima a destra e poi a sinistra qualora venisse individuato un ostacolo. Per prevenire gli urti posteriori e individuare ostacoli, abbiamo montato un sensore KY-032, un sensore IR per rilevamento ostacoli.

  • Mark#1

Tutti i movimenti vengono stampati su un display Oled da 0,96″, che ha la sola funzione di conferma del movimento scelto.

Per il nostro Mark#1 abbiamo anche adoperato 3 led (2 bianchi ed uno rosso), a cui abbiamo saldato 3 resistenze da 220OHM, che si attiveranno quando il sensore di luminosità rileverà l’assenza di luce.

Il comparto di alimentazione è affidato a due batterie 18650 da 3,7V e 5800mAh, comunque insufficienti a fornire la corretta tensione per i due motori da 12V. Pertanto alle batterie abbiamo collegato un DC-DC da 2V-24V in ingresso e 5V-28V in uscita che aumenta la tensione a 12V. per calibrare l’uscita dovrete ruotare il trimmer posizionato sul dispositivo ed aiutarvi con un multimetro.

Lista componenti:



Collegamenti

Iniziamo a parlare dei collegamenti da effettuare per il nostro Progetto Mark#1 premettendo che la shield utilizzata nello schema è solo a scopo rappresentativo e non rappresenta quella realmente adoperata, quindi i collegamenti ai motori potrebbero essere differenti a seconda della motor-shield adoperata. Nulla togli il fatto che essa va applicata sul nostro Arduino UNO e che i pin per i successivi collegamenti sono i medesimi. Un’altra importante premessa da fare è l’alimentazione: tutti i sensori sono alimentati a 5V, tensione fornita tramite la shield.

Mark#1

Il primo dispositivo da collegare è il sensore HC-SR04, connesso all’ECHO al pin digitale 4, mentre al TRIG al pin 5. Il servo-motore, connesso al pin PWM 6, andrà avvitato alla prima componente da realizzare in stampa 3D, la stessa su cui posizionare il sensore a ultrasuoni. Per fissare quest’ultimo sensore vanno bene anche due fascette.

Mark#1

Per i LED abbiamo realizzato dei supporti, anche essi da realizzare con una comune stampante 3D; le resistenze sono saldate ai LED e poste sotto una guaina protettiva. I LED bianchi sono collegati ai pin digitali 8 e 9, mentre il rosso al pin 10. Il sensore KY-032 è anch’esso connesso ai pin digitali, più precisamente al pin 7. Ultimo sensore connesso digitalmente è il sensore sonoro Big Sound, connesso al pin analogico A2 che possiamo programmare come digitale dichiarandolo come numero 16. La fotoresistenza sarà connessa al pin analogico A3, mentre il display Oled, che sfrutta l’interfaccia I2C lo collegheremo ai pin A4 e A5, interscambiabili coi pin SDA e SCL. Vi informiamo che servo-motore, fotoresistenza, display Oled, DC-DC e interruttore vanno posizionati su un piano da realizzare in stampa 3D.

Le batterie da 3,7V sono collegate ad uno switch e poi al DC-DC. Quest’ultimo porta la tensione a 12V alla scheda tramite la shield. Alcune shield possiedono già un interruttore integrato, ma abbiamo preferito porne uno tra le batterie ed il DC-DC. Vi informiamo che servo-motore, fotoresistenza, display Oled, DC-DC e interruttore vanno posizionati su un piano da realizzare in stampa 3D.

Codice

Pe il nostro Progetto Mark#1 avremo bisogno della libreria Servo.h per gestire il servo-motore SG90, la libreria Adafruit_GFX.h e Adafruit_SSD1306.h per il display Oled e  la libreria Wire.h per l’interfaccia I2C, mentre per il resto sarà semplice codice.


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Servo.h>

Creiamo l’oggetto servo, definiamo i pin che useremo per il sensore HC-SR04 e per la gestione dei motori, poi settiamo i parametri per il display Oled, creando l’oggetto display per la gestione dello stesso. I pin digitali 3 ed 11 gestiranno la velocità dei motori, mentre i pin 12 e 13 il senso di rotazione.


Servo myservo;

#define ECHO_PIN 4
#define TRIG_PIN 5

#define left_A 3 // definisce la velocità del motore sinistro al PIN 3
#define right_B 11 // definisce la velocità del motore sinistro al PIN 11
#define INT_A 12 // controlla la direzione del motore sinistro sul PIN 12
#define INT_B 13 // controlla la direzione del motore destro sul PIN 13

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 18
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &amp;Wire, OLED_RESET);

Creiamo alcune variabili che adopereremo per leggere informazioni dai sensori e misurare la distanza, impostiamo la distanza minima a cui il robot deve arrestarsi.


const int dist_min = 30;
int sound, lumen, sensorIR, rDistance, lDistance, mDistance;
double distanceCm;

Nel VOID SETUP definiamo la direzione dei pin adoperati, dichiariamo che il servo-motore è connesso al pin 6, avviamo monitor seriale, display e facciamo stampare su di esso il logo del canale con relativa scritta per poi attendere 3 secondi.


pinMode (ECHO_PIN, INPUT);
pinMode (TRIG_PIN, OUTPUT);
pinMode (left_A, OUTPUT);
pinMode (right_B, OUTPUT);
pinMode (INT_A, OUTPUT);
pinMode (INT_B, OUTPUT);
pinMode (7, INPUT);
pinMode (8, OUTPUT);
pinMode (9, OUTPUT);
pinMode (10, OUTPUT);
pinMode (16, INPUT);

myservo.attach(6);

Serial.begin(9600);

display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

display.clearDisplay();
display.drawBitmap (0, 0, logo, 128, 64, 1);
display.display();

delay(1000);

display.setTextSize(2);
display.setTextColor(1,0);
display.setCursor(10, 50);
display.println("RESHITOZE");
display.display();

delay(3000);

All’interno del VOID LOOP impostiamo il servo-motore nella posizione di 90°, settiamo che la distanza minima corrisponde alla distanza registrata, poi effettuiamo la ricerca di ostacoli. Verifichiamo pure se ci sono ostacoli sul retro tramite il sensore IR KY-032.


myservo.write(90);

mDistance = distance();

ultra();

sensorIR = digitalRead (7);

if (sensorIR == 0){
Stop();
delay (500);
ultra();

}



Andiamo ora ad esaminare alcune sotto-funzioni richiamate nel VOID LOOP o da altre funzioni. Partiamo dalla funzione distance() con la quale verifichiamo se sono presenti ostacoli entro la distanza minima di 30cm. Essa dovrà restituire un valore intero da confrontare nel codice.


int distance(){
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
unsigned long impulso = pulseIn(ECHO_PIN,HIGH);
distanceCm = impulso / 2.0 * 0.0343;
return (int)distanceCm;

}

Un altra funzione da esaminare è la ultra(), con la quale il robot sceglie il percorso migliore da effettuare ed evita gli ostacoli che si pongono davanti.


void ultra(){

if (mDistance &lt; dist_min) {
Stop();
delay(500);
myservo.write(30);
delay(1000);
rDistance = distance();

delay(500);
myservo.write(90);
delay(1000);
myservo.write(150);
delay(1000);
lDistance = distance();

delay(500);
myservo.write(90);
delay(1000);
if(rDistance &gt; lDistance) {
Right();
delay(360);
}
else if(rDistance &lt; lDistance) {
Left();
delay(360);
}
else if((rDistance &lt;= dist_min) || (lDistance &lt;= dist_min)) {
Back();
delay(180);
}
else {
Go();

}
}
else {
Go();

}

}

Due funzioni minori sono la light() per le luci


void light(){
lumen = analogRead(A3);
if (lumen &gt; 400) {
digitalWrite(10, HIGH);
digitalWrite(8, HIGH);
digitalWrite(9, HIGH);
}
else {
digitalWrite(10, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
}

Serial.print("Luce ");
Serial.println(lumen);
}

e la funzione clap() che si attiva con un rumore, tramite il sensore suono, facendo “spaventare” il nostro Mark#1 che farà una lunga retromarcia e stampa un messaggio su display Oled.


void clap(){
sound = digitalRead(16);
if (sound == 1) {
digitalWrite(10, HIGH);
digitalWrite(8, HIGH);
digitalWrite(9, HIGH);

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("PAURA");
display.display();

delay(2000);
Stop();
Back();
delay (3000);
}
else {
}

Serial.print("Clap ");
Serial.println(sound);
}

Le ultime funzioni che esaminiamo sono quelle di movimento, ossia i movimenti AVANTI, INDIETRO, STOP, DESTRA e SINISTRA. Tali funzioni si basano tutte sulla stessa programmazioni. Con le funzioni digitalWrite() impostano il senso di rotazione con HIGH e LOW, quindi i motori ruoteranno in senso orario con LOW e antiorario con HIGH, mentre la loro velocità di rotazione si stabilisce con la funzione analogWrite() con la quale moduliamo il segnala digitale in un range compreso tra 0 e 255, dove 255 è la velocità massima e 0 è l’assenza di rotazione.

// funzione per fermarsi
void Stop()
{
digitalWrite (INT_A, LOW);
digitalWrite (INT_B, LOW);
analogWrite (left_A, 0);
analogWrite (right_B, 0);

light();
clap();

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("STOP");
display.display();

}
// funzione per girare a destra
void Right()
{
digitalWrite(INT_A,LOW);
digitalWrite(INT_B,HIGH);
analogWrite(left_A,255);
analogWrite(right_B,255);

light();
clap();

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("DESTRA");
display.display();

}

// funzione per girare a sinistra
void Left()
{
digitalWrite(INT_A,HIGH);
digitalWrite(INT_B,LOW);
analogWrite(left_A,255);
analogWrite(right_B,255);

light();
clap();

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("SINISTRA");
display.display();

}

// funzione per retromarcia
void Back()
{
digitalWrite(INT_A,LOW);
digitalWrite(INT_B,LOW);
analogWrite(left_A,255);
analogWrite(right_B,255);

light();
clap();

sensorIR = digitalRead (7);
if (sensorIR == 0){
Stop();
delay (500);
ultra();

}

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("INDIETRO");
display.display();

}

// funzione per andare avanti
void Go()
{
digitalWrite(INT_A,HIGH);
digitalWrite(INT_B,HIGH);
analogWrite(left_A,255);
analogWrite(right_B,255);

light();
clap();

display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1);
display.setCursor(0, 5);
display.println("Tank:");
display.setTextSize(3);
display.setTextColor(1);
display.setCursor(0, 20);
display.println("AVANTI");
display.display();

}

Non ci resta che collegare il nostro Arduino UNO al computer, scegliere la porta e caricare il nostro sketch.