Ardubottino

Da raspibo.

Ardubottino è stato creato soprattutto per testare tre componenti: l'Arduino, il sensore della distanza a ultrasuoni e il ponte ad H. Lo chassis e i motori sono Tamiya.

Ardubottino.jpg

Indice

Prima Versione

Componenti

Ardubottino ha questi componenti:

  • Arduino UNO [1]
  • Sensore di distanza a ultrasuoni SRF05 [2]
  • Il board con montato il ponte ad H L298 [3]
  • Kit Tamiya per lo chassis e i motori
  • Batteria da 9v
  • Ulteriori batterie per il motore (inizialmente 4,5v, poi 4,8v)

Il sensore di distanza è sprovvisto di pin, occorre quindi saldarne una strip prima di procedere.

Il sensore di distanza a ultrasuoni SRF05

102 1298.JPG

È piuttosto semplice da usare. Seguendo le istruzioni [ http://www.robot-electronics.co.uk/htm/srf05tech.htm trovate qui] si può notare che ha due modalità di comunicazioni, date dal pin Mode. Se questo pin è collegato alla 0v il sensore userà un solo pin per la comunicazione dei dati dell'eco (Echo) e la ricezione dei comandi (Trigger), se, al contrario, non è connesso a niente allora saranno usati due pin diversi. Nel caso dell'Ardubottino ho preferito usare la prima modalità, quella con un pin solo (soprattutto per pigrizia: perché è già così lo sketch di esempio "ping" nella libreria di Arduino!). Oltre al pin Mode e al pin Trigger/Echo occorre collegare il primo e l'ultimo pin rispettivamente a +5v e a terra. Nella foto si può vedere, da sinistra: +5v, non usato, Trigger/Echo (collegato al pin 13 di Arduino), Mode (terra), Terra (terra).

Il board ponte ad H L298

Ardubottino dettagliol298.jpg

Il ponte ad H L298 è utilissimo per manovrare due motori indipendenti. Può essere usato anche per muovere un motore passo-passo. Ha due ingressi per la corrente: tramite un ponticello può essere separata fra corrente per la logica e corrente per i motori. La logica dev'essere alimentata da +5v; se l'alimentazione è separata, i motori possono ricevere fino a +12v. Per comodità (e anche dopo diversi goffi tentativi per capire come funzionava) ho preferito alimentare i motori solo con i 5v.

Sui lati ci sono le connessioni per i motori mentre, a fianco dell'alimentazione, ci sono i pin che servono per comandarli. I pin sono sei, due sono gli enable per i motori e quattro servono per dare la direzione, due per motore. I pin dell'enable si possono chiudere con dei ponticelli, comandarli da Arduino/Raspberry Pi oppure gli si può mandare un impulso PWM per regolare la velocità.

Motori ad encoder, gioie (poche) e dolorer (tanto)

Il problema di Ardubottino è che non va dritto. Il motivo? Forse il ponte ad H non dà gli stessi voltaggi ai motori, forse i motori sono un po' diversi, forse si stanno usurando in modo diverso, forse i cingoli hanno qualche imperfezione, forse le ruote sono storte, forse è sbilanciato. Ad ogni modo ognuno di questi problemi (tranne le ruote storte e le imperfezioni dei cingoli) si sarebbero risolti scoprendo a che velocità vanno i motori. Per scoprire questo dato sono necessari gli encoder. Un encoder è una rotellina con dei fori e un sensore o due, composti da un LED e da un ricevitore a infrarossi, che sentono quando un foro è passato. Encoder di questo tipo si trovano ad esempio nelle rotelline dei mouse o in quelli che hanno ancora la pallina.

EncoderArdubottino.JPG

Ora: questi sarebbero degli encoder "fatti apposta" per il kit Tamiya. Uso il condizionale perché, se non sono stato io ad averli montati in modo approssimativo, il loro utilizzo è alquanto scomodo. Anzitutto la rotella non sta fissa sull'albero. Il foro esagonale è troppo grande e ha un notevole gioco. Il range del sensore è minimo e non tiene conto della struttura del kit Tamiya (la rotella va quasi a sfregare contro le viti che lo fissano allo chassis). Inoltre sono estremamente fragili: una delle rotelle si è spaccata subito in due. I sensori stessi, poi, non si incastrano nella sede e occorre incollarli. La lettura degli encoder andrebbe fatta sfruttando gli interrupt di Arduino. Gli interrupt sono dei pin che restano in ascolto e fanno scattare degli eventi quando cambiano valore[4]. Purtroppo uno dei due pin (sull'Arduino Uno sono il pin 2 e il pin 3) dava dei risultati estremamente inaffidabili. Dopo varie prove, il risultato migliore l'ho ottenuto usando la libreria Encoder [5] su due pin non di interrupt. Il risultato di tutta questa fatica è un robottino che grosso modo va dritto. Un risultato mediocre di molto inferiore alla calibrazione a occhio. Alla fine, infatti, ho preferito quest'ultima.

Lo sketch

Lo sketch deriva per una parte dall'esempio Ping già presente nella libreria di Arduino. In pratica fa avanzare Ardubottino fino a che non incontra un ostacolo. Se lo incontra sterza in un senso e poi nell'altro e alla fine prosegue per la direzione dove ha trovato più campo libero. 4 7 6 5 sono i pin a cui ho collegato il ponte ad H. Sterzo è il tempo che deve impiegare per sterzare. pinping è il pin a cui è attaccato il trigger/echo del sensore a ultrasuoni.

long duration;
long dursx;
long durdx; 
const int en1 = 11; //pin enable
const int en2 = 9;
const int m1fd = 4; //controlli motore
const int m2fd = 7;
const int m1bk = 5;
const int m2bk = 6;
const int pinping = 13; //pin sensore distanza
const int sterzo = 600; //durata dello sterzo
const int distanza = 500; //distanza minima ostacoli
int motore1 = 67;//pwm motore dx
int motore2 = 74;//pwm motore sx

void setup() {
 pinMode(m1fd, OUTPUT);
 pinMode(m1bk, OUTPUT);
 pinMode(m2fd, OUTPUT);
 pinMode(m2bk, OUTPUT);
 pinMode(en1, OUTPUT);
 pinMode(en2, OUTPUT);
 analogWrite(en1,motore1);
 analogWrite(en2,motore2);
 dritto();
}

void loop() {
 if (echo() < distanza){ //trovato un ostacolo
  analogWrite(en1,170);
  analogWrite(en2,170);
  ferma();
  sinistra();
  delay(sterzo);
  ferma();
  dursx = echo(); //guarda a sinistra
  destra();
  delay(sterzo*2);
  ferma();
  durdx = echo(); //guarda a destra
  if (dursx < distanza && durdx < distanza){
    destra();
    delay(sterzo);
  } else if (dursx > durdx) {
    sinistra();
    delay(sterzo*2);
  } //sceglie dove c'è più spazio
  dritto();
  analogWrite(en1,motore1);
  analogWrite(en2,motore2);
 }
}

void dritto() {
 digitalWrite(m1bk,LOW);
 digitalWrite(m2bk,LOW);
 digitalWrite(m2fd,HIGH);
 digitalWrite(m1fd,HIGH);   
}

void rovescia() {
 digitalWrite(m1fd,LOW);
 digitalWrite(m2fd,LOW);
 digitalWrite(m1bk,HIGH);
 digitalWrite(m2bk,HIGH);
}

void destra() {
 digitalWrite(m1fd,LOW);
 digitalWrite(m2fd,HIGH);
 digitalWrite(m1bk,HIGH);
 digitalWrite(m2bk,LOW);
}

void sinistra() {
 digitalWrite(m1fd,HIGH);
 digitalWrite(m2fd,LOW);
 digitalWrite(m1bk,LOW);
 digitalWrite(m2bk,HIGH);
}

void ferma() {
 digitalWrite(m1fd,LOW);
 digitalWrite(m2fd,LOW);
 digitalWrite(m1bk,LOW);
 digitalWrite(m2bk,LOW);
 delay(50);
}

long echo() {
 pinMode(pinping, OUTPUT);
 digitalWrite(pinping, LOW);
 delayMicroseconds(2);
 digitalWrite(pinping, HIGH);
 delayMicroseconds(5);
 digitalWrite(pinping, LOW);
 pinMode(pinping, INPUT);
 duration = pulseIn(pinping,HIGH);
 return duration;
}

Seconda Versione

Ok, la modifica è un po' minima per essere una vera e propria seconda versione, ma dopo il fallimento degli encoder ero un po' giù di morale. Il progetto era di rendere Ardubottino telecomandabile con qualche mezzo estremamente low cost e nella maniera più semplice possibile. La mia scelta è caduta sull'infrarosso.

Il sensore a infrarossi

Arduir.jpg

Quello che potete vedere nella foto è un sensore a infrarossi come quelli che potreste recuperare in qualsivoglia elettrodomestico. I sensori IR hanno tre pin: +5v, terra e dati. Questo in particolare aveva le stesse specifiche di quest'altro[6] usato nel tutorial di Adafruit per leggere gli impulsi infrarossi con Arduino. Potrebbero non essere tutti uguali, quindi cautela.

La libreria IRremote

Gran parte del lavoro lo fa la libreria IRremote (che trovate qui [7]). Con questo sketch [8] si possono leggere, sotto forma di cifre, i codici del telecomando che volete usare. Tramite il monitor seriale noterete dapprincipio che ogni tasto ha assegnato due codici, questo per fare in modo che ci sia una differenza fra tenere premuto il tasto (dà lo stesso codice in uscita, in modo continuo) e dei clic ripetuti (ogni clic diverso il codice cambia). Con questo sketch sono riuscito a capire che il codice del tasto SU del telecomando era legato ai codici 2742014623 e 31889539. La libreria IRremote permette anche di trasmettere, ma per ora non sfrutteremo questa sua proprietà. Potete trovare più informazioni qui [9]

Lo sketch finale

Non ho voluto togliere all'Ardubottino la possibilità di andarsene in giro da solo, quindi ho assegnato al tasto OK del telecomando la capacità di passare dallo stato manuale a quello automatico. La libreria IRremote ci permette di creare la classe IRrecv che, tramite i suoi metodi, prepara (enableIrIN) , legge (decode), e aspetta il prossimo segnale (resume). I valori sono memorizzati nella struttura decode_results, il cui valore value è il codice del segnale del telecomando. La variabile booleana automat determina se Ardubottino è in modalità automatica o meno.

ATTENZIONE: La libreria IRremote.h non è compatibile con la libreria RobotIRRemote fornita con l'ultima versione della IDE di Arduino. Per compilare questo sketch occorre prima disabilitare RobotIRRemote. Per disabilitare una libreria occorre andare in arduino/libraries e spostare la cartella della libreria in un altro posto.

 #include <IRremote.h>
 
 int IRpin =2; //pin del sensore IR
 IRrecv irrecv(IRpin);
 long duration;
 long dursx;
 long durdx;
 
 const int en1 = 10; //pin enable
 const int en2 = 9;
 const int m1fd = 4; //controlli motore
 const int m2fd = 7;
 const int m1bk = 5;
 const int m2bk = 6;
 const int pinping = 13; //pin sensore distanza
 const int sterzo = 600; //durata dello sterzo
 const int distanza = 500; //distanza minima ostacoli
 int motore1 = 66;//pwm motore dx
 int motore2 = 72;//pwm motore sx
 boolean automat = false;
 
 decode_results results;
 
 void setup() {
  pinMode(m1fd, OUTPUT);
  pinMode(m1bk, OUTPUT);
  pinMode(m2fd, OUTPUT);
  pinMode(m2bk, OUTPUT);
  pinMode(en1, OUTPUT);
  pinMode(en2, OUTPUT);
  analogWrite(en1,motore1);
  analogWrite(en2,motore2);
  irrecv.enableIRIn();
 }
 
 void loop() {
   if(irrecv.decode(&results)){
     if (!automat) {
       if(results.value == 2742014623 || results.value == 31889539){
         dritto();
       } else if (results.value == 2383694249 || results.value == 2039764837 ) {
         sinistra();
       } else if (results.value == 15111918 || results.value == 2725237002) {
         rovescia();
       } else if (results.value == 1665824360 || results.value == 3250666572 ) {
         destra();
       } else  {
         ferma();
       }
     }
     if (results.value == 3969632309 || results.value == 18594425) {
       if (automat) {
         automat = false;
         ferma();
         delayMicroseconds(300000);  
       }else{
         automat = true;
         dritto();
         delayMicroseconds(300000);
       }
     }
     irrecv.resume();
   }
   if (automat && echo() < distanza){ //trovato un ostacolo
    analogWrite(en1,170);
    analogWrite(en2,170);
    ferma();
    sinistra();
    delay(sterzo);
    ferma();
    dursx = echo(); //guarda a sinistra
    destra();
    delay(sterzo*2);
    ferma();
    durdx = echo(); //guarda a destra
    if (dursx < distanza && durdx < distanza){
      destra();
      delay(sterzo);
    } else if (dursx > durdx) {
      sinistra();
      delay(sterzo*2);
    } //sceglie dove c'è più spazio
    dritto();
    analogWrite(en1,motore1);
    analogWrite(en2,motore2);
  }
 }
 
 
 void dritto() {
  digitalWrite(m1bk,LOW);
  digitalWrite(m2bk,LOW);
  digitalWrite(m2fd,HIGH);
  digitalWrite(m1fd,HIGH);   
 }
 
 void rovescia() {
  digitalWrite(m1fd,LOW);
  digitalWrite(m2fd,LOW);
  digitalWrite(m1bk,HIGH);
  digitalWrite(m2bk,HIGH);
 }
 
 void destra() {
  digitalWrite(m1fd,LOW);
  digitalWrite(m2fd,HIGH);
  digitalWrite(m1bk,HIGH);
  digitalWrite(m2bk,LOW);
 }
 
 void sinistra() {
  digitalWrite(m1fd,HIGH);
  digitalWrite(m2fd,LOW);
  digitalWrite(m1bk,LOW);
  digitalWrite(m2bk,HIGH);
 }
 
 void ferma() {
  digitalWrite(m1fd,LOW);
  digitalWrite(m2fd,LOW);
  digitalWrite(m1bk,LOW);
  digitalWrite(m2bk,LOW);
  delay(50);
 }
 
 long echo() {
  pinMode(pinping, OUTPUT);
  digitalWrite(pinping, LOW);
  delayMicroseconds(2);
  digitalWrite(pinping, HIGH);
  delayMicroseconds(5);
  digitalWrite(pinping, LOW);
  pinMode(pinping, INPUT);
  duration = pulseIn(pinping,HIGH);
  return duration;
 }

Terza Versione

Dopo l'ennesimo disastroso trasporto di Ardubottino, nel quale si è smontato tutto, ho deciso di dargli una sistemata definitiva. Saldature migliori, componenti fissati e meno fili volanti. Inoltre sono state fatte le seguenti modifiche.

Ardubottino 3ver.jpg

Passaggio dal Micro al Pro Mini

Per ragioni di spazio sono passato dall'Arduino Micro al Pro Mini della Deek Robots.

Newardbot2.JPG

C'è poco da dire su questa board. È sotto tutti gli aspetti un Arduino Pro Mini. Per essere programmato ha bisogno dell'adattatore USB. Purtroppo, come si può vedere nell'immagine, l'ho montato alla rovescia sulla breadboard e così non è possibile capire il numero dei pin. Capirci qualcosa è stato particolarmente ostico.

Abbastanza batterie

Avere l'alimentazione separata motori/logica è una soluzione che non mi soddisfa per niente. Come fare però per fare in modo che gli spikes dei motori non facciano riavviare l'Arduino? La questione è stata risolta in questa maniera.

Condensatori

Sono stati aggiunti dei condensatori ai motori. In questo modo lo spike dovrebbe essere assorbito in parte.

L'Arduino e i motori hanno una sorgente comune

Le batterie dei motori, 4 stilo da 2100mAh l'una, hanno amperaggio più che sufficiente per tenere acceso Arduino e motori. Dal + delle batterie partono due fili, uno va all'Arduino e l'altro al ponte ad H. Nei tentativi precedenti, davo al ponte ad H l'uscita a 5v dell'Arduino. Evidentemente la richiesta dei motori era eccessiva per il regolatore a 5v.

Turbo!

Aggiunta la modalità turbo, che fa andare il ns. robot più veloce!

Altri problemi

Avrei voluto mettere il sensore a ultrasuoni su un servo ma un bug della libreria Servo blocca il PWM in alcuni pin[10] per cui ho lasciato perdere.

Nuovo codice!

Inutile incollarlo qui. Ho aperto uno spazio su GitLab:

Link

Quarta versione

Era giunto il momento di sfruttare un po' di roba che rimaneva inutilizzata. Le modifiche sono state:

  • Encoder, stavolta funzionanti
  • Calibrazione automatica
  • Modalità programmazione
  • Blocco quando una ruota non gira
  • Sensore di caduta

Encoders

Nonostante abbia rotto le rondelle originali (che comunque erano fatte malissimo) sono riuscito a trovare alcuni modelli funzionanti. Il PLA azzurro, a differenza di quello bianco e di quello nero, sembra riflettere l'infrarosso dell'encoder. Tutto stava nel capire quale forma funzionasse meglio.

Arduencoder.JPG

La forma che funziona meglio (per ora) è quella a destra. Potete trovare l'STL nel repository ardubottino su GitLab.

Il problema principale, nel creare questo tipo di oggetto in 3D, è stato il foro a forma di prisma esagonale. In Blender occorre creare un cilindro, ridurre i vertici a 6 e poi modificarne la dimensione solo con lo strumento proporzionale, in modo da non deformarlo.

Newardbot3.JPG

Gli encoder permettono di fare diverse cose interessanti. Ora Ardubottino sente quando una ruota non sta girando, ad esempio, inoltre sono state aggiunte due nuove modalità: la modalità "programmazione" (vai avanti di 10, poi gira a destra di 5, ecc.) e la modalità "calibrazione" (modifica la velocità di un motore fino a che le ruote non girano più o meno uguali).

Purtroppo gli encoder non sono tanto precisi, ogni tanto leggono male e segnano un passo in più o in meno. Questo però era così anche con le rotelle originali.

Il sensore di caduta

Posto sul davanti, sente quando ad Ardubottino manca la terra sotto i cingoli. Quando sente il vuoto ferma tutto.

Newardbot1.JPG

Il codice

Il codice sta diventando veramente troppo lungo per essere incollato qui, su questa pagina, per cui ne commenterò solo alcune parti. Lo sketch completo lo trovate su GitLab.

#include "ardubottino.h"

Sì, era ora che creassi un header con tutti i codici del telecomando. Per adattare Ardubottino a un altro telecomando basterà cambiare header.

volatile long right_interval = 0;
volatile long left_interval = 0;
volatile int right_count = 0;
volatile int left_count = 0;

Le variabili che sono modificate dagli encoder devono essere di tipo "volatile". Le variabili volatile possono essere cambiate da un evento esterno, nel nostro caso da un evento scatenato da un interrupt.

void right_encoder() {
 static unsigned long last_interrupt = 0;
 unsigned long time_interrupt = millis();
 if(time_interrupt - last_interrupt >70){
   right_interval = time_interrupt - last_interrupt;
   next_check_right = millis()+right_interval*2;
   right_count +=1;
   last_interrupt = time_interrupt;
 }
}

void left_encoder() {
 static unsigned long last_interrupt = 0;
 unsigned long time_interrupt = millis();
 if(time_interrupt - last_interrupt >70) {
   left_interval = millis() - last_interrupt;
   next_check_left = millis()+left_interval*2;
   left_count +=1;
   last_interrupt = millis();
 }
}

Ecco le brevi funzioni chiamate da

 attachInterrupt(0, right_encoder,RISING);
 attachInterrupt(1, left_encoder,RISING);

gli interrupt. La variabile x_count conta quanti giri (quanti quarti di giro) ha fatto la ruota; x_interval l'intervallo, in millisecondi, fra un passaggio e l'altro; next_check_x è una stima massima di quando dovrebbe essere il prossimo passaggio, serve per capire se una ruota s'è inceppata.

 if (irrecv.decode(&results)) {
  curr_result = results.value;
  if (curr_result == prev_result) {
    curr_result = 0;
  } else {
    prev_result = curr_result;
  }
(...)
}

Ricordate quando vi dissi che il telecomando per ogni tasto ha due codici? Serve per distinguere se state tenendo premuto il tasto o se lo state premendo più volte contemporaneamente. Con questo piccolo brandello di codice faccio in modo di ignorare i codici ripetuti, visto che ad Ardubottino non servono. Non tutti i telecomandi funzionano così.

if (program) {
     if (curr_result == OKA || curr_result == OKB) {
       blinkOK();
       executeProgram();
       clearMoves();
       noMotion();
     } else if (curr_result == NUM1A || curr_result == NUM1B) {
       moves[pointer][0] = (moves[pointer][0]*10) +1;
       blinkOK();
     } else if (curr_result == NUM2A || curr_result == NUM2A) {
       moves[pointer][0] = (moves[pointer][0]*10) +2;
       blinkOK();
     } else if (curr_result == NUM3A || curr_result == NUM3B) {
       moves[pointer][0] = (moves[pointer][0]*10) +3;
       blinkOK();
     } else if (curr_result == NUM4A || curr_result == NUM4B) {
       moves[pointer][0] = (moves[pointer][0]*10) +4;
       blinkOK();
     } else if (curr_result == NUM5A || curr_result == NUM5B) {
       moves[pointer][0] = (moves[pointer][0]*10) +5;
       blinkOK();
     } else if (curr_result == NUM6A || curr_result == NUM6B) {
       moves[pointer][0] = (moves[pointer][0]*10) +6;
       blinkOK();
     } else if (curr_result == NUM7A || curr_result == NUM7B) {
       moves[pointer][0] = (moves[pointer][0]*10) +7;
       blinkOK();
     } else if (curr_result == NUM8A || curr_result == NUM8B) {
       moves[pointer][0] = (moves[pointer][0]*10) +8;
       blinkOK();
     } else if (curr_result == NUM9A || curr_result == NUM9B) {
       moves[pointer][0] = (moves[pointer][0]*10) +9;
       blinkOK();
     } else if (curr_result == NUM0A || curr_result == NUM0B) {
       moves[pointer][0] = (moves[pointer][0]*10);   
       blinkOK();
     } else if (curr_result == FORWARDA || curr_result == FORWARDB) {
       moves[pointer][1] = 1;
       pointer ++;
       blinkOK();
     } else if (curr_result == BACKA || curr_result == BACKB) {
       moves[pointer][1] = 2;
       pointer ++;
       blinkOK();
     } else if (curr_result == LEFTA || curr_result == LEFTB) {
       moves[pointer][1] = 3;
       pointer ++;
       blinkOK();
     } else if (curr_result == RIGHTA || curr_result == RIGHTB) {
       moves[pointer][1] = 4;
       pointer ++;
       blinkOK();
     } else if (curr_result == WINA || curr_result == WINB) {
       clearMoves();
       blinkOK();
     } else {
       blinkNO();
     }
     if(moves[pointer][0] > 99) {
      moves[pointer][0] = moves[pointer][0]-((moves[pointer][0]/100)*100);
     }
     if (pointer >49) {
       executeProgram();
     }
}

Et voilà: 50 mosse programmabili per il nostro robottino. Si scrive un numero di massimo 2 cifre e una direzione per massimo 50 istruzioni. Premendo OK si dà inizio al programma. Ho anche aggiunto un lampeggiamento del LED di bordo in modo da dare un feedback visivo. Come ben saprete, i telecomandi a volte non trasmettono bene.

void executeProgram() {
 int right_count_program = right_count;
 
 for (int x=0;x<=49;x++){
   if (moves[x][1] == 0) {
     break;
   } else if (moves[x][1] == 1){
     forward();
   } else if (moves[x][1] == 2){
     backward();
   } else if (moves[x][1] == 3){
     left();
   } else if (moves[x][1] == 4){
     right();
   }
   while(moves[x][0] > 0) {
     if (right_count_program != right_count ) {
       moves[x][0] --;
       right_count_program = right_count;
       
     }
     if (digitalRead(fall)) {      
       noMotion();
       program = false;
       return;
     }
   }
   moves[x][1] = 0;
 }
 program = false;
 noMotion();
}

Come potete vedere a metà c'è anche un check (c'è anche per le modalità automatica e manuale) sul sensore di caduta.

if (curr_result == BLUEA|| curr_result == BLUEB) { //calibration
     blinkOK();
     program = false;
     automat = false;
     noMotion();
     delay(500);
     forward();
     int loops = 0;
     int last_left = left_count;
     int last_right = right_count;
     bool left_pass = false;
     bool right_pass = false;
     while (loops < 12) {
       if (last_left != left_count) {
         left_pass = true;
       }
       if (last_right != right_count) {
         right_pass = true;
       }
       if (left_pass && right_pass) {
         left_pass = false;
         right_pass = false;
         last_left = left_count;
         last_right = right_count;
         if (abs(right_interval - left_interval) < 80) {
           loops +=1;
         } else if (right_interval > left_interval) {
           motor1PWM += 1;
           analogWrite(en1,motor1PWM);
           loops = 0;
         } else {
           motor1PWM -= 1;
           analogWrite(en1,motor1PWM);
           loops = 0;
         }
         if(debug) {
           Serial.print(motor1PWM);
           Serial.print(" ");
           Serial.println(motor2PWM);
         }
       }
     }
     noMotion();
   }

La calibrazione: modifica la potenza di uno dei due motori fino a che gli intervalli degli encoder delle due ruote non sono quasi uguali. Occorre staccare i cingoli o mettere Ardubottino in un posto sopraelevato perché per funzionare ha bisogno di andare forward() per qualche minuto.

Strumenti personali
Namespace

Varianti
Azioni
Navigazione
Strumenti