encoder ottico lettura del movimento errato (risolto)

Ciao a tutti, chiedo aiuto per uscire da un problema di cui non solo non conosco la soluzione ma nemmeno la causa.
Sto costruendo un rover con raspberry e arduino nano , per adesso sono riuscito a farlo funzionare abbastanza bene via interfaccia web. il problema è che non va dritto, essendo mosso da due motori indipendenti, per risolvere il problema parzialmente ho pensato di misurare la velocità di rotazione dei due motori con questi affari http://www.dfrobot.com/wiki/index.php/Wheel_Encoders_for_DFRobot_3PA_and_4WD_Rovers_(SKU:SEN0038)
e regolare di conseguenza il segnale pwm su ogni motore per correggere gli sbandamenti.
Il problema è leggere i valori dei due encoder , ho collegato le masse dei due encoder al gnd di arduino , il + degli encoder al + di arduino e i due fili data ai pin 2 e 3.
Dopodiche con la gestione degli interrupt incremento una variabile (1 per motore) per leggere i numeri di giri.
Fatto questo farò la routine di aggiustamento, il problema è che uno dei due encoder (quello collegato al pin 3) riceve dei segnali spuri, anche con la ruota ferma e l’altra che gira mi aumenta il valore della variabile.
Ho letto che forse è un problema legato ai disturbi che creano i due motori anche se hanno l’alimentazione separata.
Forse si potrebbe risolvere con un filtro ma non saprei come farlo.

grazie per l’aiuto

Dacci lo sketch. Ciao Uwe

ecco il codice,

#include <Wire.h>
#define SLAVE_ADDRESS 0x04
   //variabili per gestione i2c
   String datiin;
   String stringa;
   char data[11];
   String serialsi;
   String stato;
   long timer1;
  //costanti per gestione motore
  const int motor11Pin = 9;    // H-bridge 
  const int motor12Pin = 10;    // H-bridge 
  const int motor21Pin = 11;    // H-bridge   
  const int motor22Pin = 12;    // H-bridge 
  const int enablePin1 = 5;    // H-bridge enable pin
  const int enablePin2= 6;    // H-bridge enable pin
  //const int intpin=2;
  //const int intpin=3;
  int velocita1;
  int velocita2;
  long movruota1;
  long movruota2;
void setup() {
    serialsi="s";
    riempidata("niente    ");
    if(serialsi=="s")
    {
      Serial.begin(9600); 
      Serial.println("Ready!");
    }
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData); 
    datiin="";
    //gestione motore
    pinMode(motor11Pin, OUTPUT);
    pinMode(motor12Pin, OUTPUT);
    pinMode(motor21Pin, OUTPUT);
    pinMode(motor22Pin, OUTPUT);
    pinMode(enablePin1, OUTPUT);
    pinMode(enablePin2, OUTPUT);
    fermamotore();
    velocita1=150;//sx
    velocita2=150;//dx
    attachInterrupt(0, movimento1, RISING);
    attachInterrupt(1, movimento2, RISING);
    //vaiavanti(velocita1,velocita2);
}

void loop() {
  delay(100);
  if(stato=="avanti")
  {
    if ((timer1+5000)<millis())
    {
      Serial.print("movruota1 ");
      Serial.println(movruota1);
      Serial.print("movruota2 ");
      Serial.println(movruota2);
      Serial.println(movruota2-movruota1);
      Serial.println("*************");
      timer1=millis();
    }
  }
}


int index = 0;

// callback for sending data
void sendData() { 
    Wire.write(data[index]);
    ++index;
    if (index >= 11) {
         index = 0;
    }
 }
 void receiveData(int byteCount){
   char ricevuto;
   while(Wire.available()) {
       ricevuto=char(Wire.read());
       if (ricevuto=='x')
           {
           eseguicmd(datiin);
           datiin="";
           }
       else
            {
             datiin+=ricevuto;
             }
           }
     }
    
 void eseguicmd(String commando){
   Serial.println(commando);
    
  if (commando=="avanti")
       {
       vaiavanti(velocita1,velocita2); 
       riempidata("vadoavanti");
       } 
    if (commando=="stop")
       {
       fermamotore();
       riempidata("mifermo   ");
       } 
    if (commando=="indietro")
       {
       vaiindietro(velocita1,velocita2);
       riempidata("indietro  ");
       } 
    if (commando=="sin")
       {
       vaisx(velocita1,velocita2);
       riempidata("sin       ");
       } 
     if (commando=="des")
       {
       vaidx(velocita1,velocita2);
       riempidata("des       ");
       }       
 }
 void riempidata(String testo){
   testo.toCharArray(data,11);
 }
 
 void fermamotore()
{
  stato="fermo";
  timer1=millis();
  movruota1=0;
  movruota2=0;
 analogWrite(enablePin1, 0); 
 analogWrite(enablePin2, 0);
  digitalWrite(motor11Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor12Pin, LOW);
  digitalWrite(motor22Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor21Pin, LOW); 
}
void settavelocita(int vel1,int vel2)
{
  analogWrite(enablePin1, vel1);
  analogWrite(enablePin2, vel2);
}
void vaiindietro(int vel1,int vel2)
{
  fermamotore();
  stato="indietro";
  digitalWrite(motor11Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor12Pin, HIGH);
  digitalWrite(motor22Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor21Pin, HIGH);
  settavelocita(vel1,vel2);
}
void vaiavanti(int vel1, int vel2)
{
  fermamotore();
  stato="avanti";
  digitalWrite(motor12Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor11Pin, HIGH);
  digitalWrite(motor21Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor22Pin, HIGH);
  settavelocita(vel1,vel2);
}
void vaisx(int vel1, int vel2)
{
  fermamotore();
  stato="sx";
  digitalWrite(motor12Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor11Pin, HIGH);
  digitalWrite(motor22Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor21Pin, HIGH);
  settavelocita(vel1,vel2);
}
void vaidx(int vel1, int vel2)
{
  fermamotore();
  stato="dx";
  digitalWrite(motor11Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor12Pin, HIGH);
  digitalWrite(motor21Pin, LOW);   // set leg 1 of the H-bridge low
  digitalWrite(motor22Pin, HIGH);
  settavelocita(vel1,vel2);
}
void movimento1()
{
   movruota1=movruota1+1;
 }
void movimento2()
{
   movruota2=movruota2+1;

}

mi viene un dubbio, potrebbe essere che le variabili per accumulare i valori non sono dichiarate come volatili?

nessuno ha qualche idea brillante? eventualmente anche idee alternative per bilanciare la velocità dei motori, quali sensori si potrebbero utilizzare?

raghi: ho pensato di misurare la velocità di rotazione dei due motori con questi affari http://www.dfrobot.com/wiki/index.php/Wheel_Encoders_for_DFRobot_3PA_and_4WD_Rovers_(SKU:SEN0038) e regolare di conseguenza il segnale pwm su ogni motore per correggere gli sbandamenti.

Quegli encoder sono dei giocattoli, non vanno bene per regolare la velocità dei motori sia perché hanno troppo poca risoluzione sia perché sono calettati sulla ruota e non sul motore stesso, ci puoi solo misurare, a spanne, lo spazio percorso. Per regolare la velocità dei motori ti serve un encoder da almeno 100 cpr calettato sull'albero motore e un controllo PID.

grazie per la risposta, in effetti sono "giocattolosi" ma alla fine a me serve solo per fare dei test sulla routine di bilanciamento, e in ogni caso la velocità prevista è bassissima (comandando il tutto da web devo fare tutto al rallenty :-) ) direi una decina di cm al secondo (un giro di ruota in pratica). Quindi secondo te il problema del conteggio errato è dovuto alla scarsa qualità di questi componenti?

raghi: Quindi secondo te il problema del conteggio errato è dovuto alla scarsa qualità di questi componenti?

Non ho parlato di scarsa qualità, ho detto che non sono adatti al controllo velocità, ancora peggio se parliamo di basse velocità dove fa molto comodo usare encoder da 256-300 cpr, ovviamente del tipo a doppio canale con decodifica in quadratura. Non ultimo il fatto che Arduino non può gestire encoder in quadratura ad alta risoluzione, non possiede ne le necessarie risorse hardware ne la velocità per farlo in softmode. Un modo alternativo per far andare quasi dritto il tuo rover è usare una imu dotata di magnetometro in modo da ottenere una bussola compensata sugli assi X e Y, grazie alla sua lettura puoi correggere i motori in modo da far andare abbastanza in linea retta, volendo anche sterzare di un preciso angolo, il tuo rover, comunque serve sempre molta matematica e un controllo pid :) Tieni presente che la soluzione basata sulla bussola funziona bene in esterno, in interno può essere facilmente influenzata da masse ferrose e campi magnetici locali.

ok, avevo interpretato "giocattoli" come indice di scarsa qualità. Comunque mi riferivo allo strano comportamento per cui ho degli interrupt sul pin 3 anche a ruota completamente ferma (l'altra in movimento) mentre il fenomeno non si presenta sul pin 2 (cn movimento delle ruote invertito chiaramente ) e nemmeno in caso di movimento manuale delle ruote.

Ci sono 2 metodi per leggere encoder ad alta velocità con arduino:

http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino

ti consiglio di leggere tutti i link della pagina , soprattutto il primo link indicato da oleg il 9 novembre 2012

Comunque il tuo problema di disturbi è perchè non usi un encoder a 2 canali A e B che ti consente di avere sempre una posizione precisa ma usi un solo canale così delle semplici vibrazioni falsano la lettura, puoi migliorare , ma non correggere del tutto mettendo una rete RC con resistenza da 2K2 e 22NF

per fare le prove ho fatto girare i motori a bassissima velocità e a vuoto (le ruote non toccavano terra e il tutto era fermo quindi le vibrazioni di fatto quasi inesistenti e il problema mi si pone solo sull'encoder posto sul pin 3. sul pin due funziona (chiaramente in queste condizioni ideali) piuttosto bene. Ho ripreso la rotazione e contato il numero delle rotazioni in rapporto al valore del contatore facendo andare il video a velocità ridotta e l'errore è nell'intorno del 3%. comunque provero a filtrare il rumore proveniente dal motore e vediamo se il problema è quello, per sicurezza userò un'altra scheda (arduino uno in questo caso ) per escludere problemi all'arduino nano grazie mille

ho fatto la prova di sdoppiare il circuito lasciando l'arduino nano a comandare i motori e un arduino uno a leggere gli encoder riporto una parte dei risultati:


movruota1 520 movruota2 526


movruota1 562 movruota2 576


movruota1 603 movruota2 625


movruota1 643 movruota2 674


movruota1 684 movruota2 724


movruota1 725 movruota2 774


movruota1 767 movruota2 825


movruota1 808 movruota2 875


movruota1 850 movruota2 926


cosi il comportamento è corretto (un motore in effetti un motore è leggermente piu lento) ma adesso veramente non capisco piu nulla , non puo essere nemmeno il disturbo inserito dai motori allora......scheda nano rotta?

alla fine mi sono arreso alla mia ignoranza, due bei filtri rc e risultato (arduino nano) e sembra meglio movruota1 4476 movruota2 4544


movruota1 4812 movruota2 4853


movruota1 5233 movruota2 5193


movruota1 5554 movruota2 5543


Infatti ho scritto: "puoi migliorare , ma non correggere del tutto" se vuoi la perfezione DEVI usare un encoder a 2 canali

ciao alla fine ho capito il problema, cioè io, ho usato il raspberry per gestire gli interrupt e facendo un po di prove mi sono reso conto che il problema era nel montaggio del sensore, in pratica toccava lo chassis di metallo e quindi leggeva dati sbagliati. Grazie a tutti per le risposte ora devo modificare il telaio per evitare il problema e poi mi concentrerò sugli altri sensori ciao