Go Down

Topic: encoder rotativo recuperato da mouse (Read 3909 times) previous topic - next topic

niki77

buona sera a tutti.

Sto giocando con un encoder rotativo di un vecchio mouse (quelli con la pallina per intenderci).
Non è un modello meccanico, e di quelli con fotodiodi.
Se non ho contato male nel disco vi sono 44 aperture, ne ho dedotto che durante 1 rivoluzione  ogni canale genera 44 impulsi di salita, ma questo al momento non è rilevante.
Quello che ho fatto è collegare le uscite dei due canali ad arduino e  calcolare la velocitá incrementando un contatore  e azzerandolo dopo x millisecondi.
Veniamo al punto, il problema è che sembra che la velocità misurabile non sia molto alta, non arrivo a 300 rivoluzioni al minuto.
La mia domanda è la seguente, è possibile che siano cosi limitati?
Facendo un calcolo ho al massimo 300 giri x 44 implusi =  13200 implusi al minuto = 220hz.
Possibile che non con arduino non si riesca a misurare oltre? Mi pare impossibile.
Limite fisico dell'encoder? Lo avrei capito se fosse stato un modello meccanico, ma ottico... Mi sembra strano.
Qualcuno ha esperienze in merito?
Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

uwefed

#1
May 09, 2012, 01:08 am Last Edit: May 10, 2012, 12:00 am by uwefed Reason: 1
Arduino puó rivelare frequenze molto piú alte di 220Hz. Sicuramente il problema é lo sketch.
Puoi darcelo perché possiamo capire dove sta il problema?

In alternativa potresti procurami una sfera di cristallo o sei per caso bravo in telepatia?  ;) ;) ;)

Ciao Uwe

Janos

Prova a postare lo schetch, vediamo se riusciamo a darti una mano.

P.S. Uwe, come mai così acido? Hai mangiato jogurt scaduto per cena? Scusami, se è nuovo dell'ambiente magari non gli è venuto in mente che il problema potesse essere lo schetch...

niki77

Certo che può dipendere anche dallo sketch,  perchè no, non ho la presunzione di essere un asso,ma me la cavicchio.

Domani mattina appena mi collego dal portatile lo posto, adesso sul tab i sorgenti non mi sono ancora organizzato per averli.
Quello che più che mai chiedevo è se qualcuno li aveva già utilizzati per misurare rotazioni di una certa entità.
Ci aggiorniamo domani che adesso comincia ad essere tardino...
Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

Janos

Si, io... =) Ho letto un encoder rotativo incrementale a due canali su 4 fronti ad una frequenza dell'ordine del KHz, non mi ricondo di preciso...

niki77

#5
May 09, 2012, 10:29 am Last Edit: May 09, 2012, 10:31 am by niki77 Reason: 1
E' allora daje, che in qualche modo mi aiuti a farlo funzionare !

Breve introduzione, è uno sketch per controllare la velocità di un motore attraverso la lettura dell'encoder.
Sinceramente in questo momento non me ne frega niente che riesca a mantenere il motore alla velocità desiderata , mi accontenterei di vedere che riesce a leggere la velocità corrente anche a regimi di rotazione molto elevati.
Per scrivere questo codice mi sono basato su un codice trovato sulla rete non ricordo esattamente dove!

Code: [Select]



// controllo rotazione motore tramite lettura di un encoder rotativo ottico a 2 canali

#define motorCW             10                  // CW motor controller (basato su L298)
#define motorCCW            11                  // CCW motor controller (basato su L298)
#define motorPwm            6                   // PWM motor  controller (basato su L298)
#define encodPinA1          3                   // encoder A pin
#define encodPinB1          8                   // encoder B pin
#define sampleIntervall     100                 // intervallo di tempo per il calcolo della velocità

unsigned long lastMilli = 0;                    // loop timing
unsigned long lastMilliPrint = 0;               // loop timing
int targetSpeed = 300;                          // velocità desiderata
int currentSpeed = 0;                           // velocità attuale
int pwmValue = 0;                               // valore del controllo pwm per il motore
volatile long pulse = 0;                        // accumulo impulsi letti da encoder
float Kp =   .4;                                // PID proportional control Gain  (non è farina del mio sacco,serve per calcolare l'incremmento del pwm)
float Kd =    1;                                // PID Derivitave control gain (idem come sopra)


void setup()
{
 Serial.begin(115600);
 pinMode(motorCW, OUTPUT);
 pinMode(motorCCW, OUTPUT);
 pinMode(motorPwm, OUTPUT);
 pinMode(encodPinA1, INPUT);
 pinMode(encodPinB1, INPUT);
 attachInterrupt(1, interruptEncoder, CHANGE);
 analogWrite(motorPwm, pwmValue);
 digitalWrite(motorCW, HIGH);
 digitalWrite(motorCCW, LOW);
}

void loop()
{
 if((millis()-lastMilli) >= sampleIntervall)  
 {                          
   lastMilli = millis();
   getMotorData();                                                          
   pwmValue= updatePid(pwmValue, targetSpeed, currentSpeed);                    
   analogWrite(motorPwm, pwmValue);                                          
 }
 sendDataToSerial();                                                          
}

void getMotorData()  
{                                                        
 static long pulseAnt = 0;                                                  
 currentSpeed = ((pulse - pulseAnt)*(60*(1000/sampleIntervall))) / 44;          // 44 implusi a rivoluzione letti solo sul fronte di salita
 pulseAnt = pulse;                                                  
}

// adegua il pwm del motore secondo uno specifico algoritmo (non è farina del mio sacco !)
int updatePid(int command, int targetValue, int currentValue)  
{  
 float pidTerm = 0;                                                          
 int error=0;                                  
 static int last_error=0;                            
 error = abs(targetValue) - abs(currentValue);
 pidTerm = (Kp * error) + (Kd * (error - last_error));                            
 last_error = error;
 return constrain(command + int(pidTerm), 0, 255);
}

// invia alla seriale i dati del motore ogni mezzo secondo
void sendDataToSerial()  
{                                                    
 if((millis()-lastMilliPrint) >= 500)  
 {                    
   lastMilliPrint = millis();
   Serial.print("SP:");            
   Serial.print(targetSpeed);  
   Serial.print("  RPM:");          
   Serial.print(currentSpeed);
   Serial.print("  PWM:");          
   Serial.print(pwmValue);              
 }
}

// routin di gestione interrupt su cambio stato del pin collegato al canale A dell'encoder
// al momento leggo solo i fronti di salita
void interruptEncoder()  
{                    
 // compara i valori rilevati dall'encoder , se sono entrambi a stato logico alto è un modivmento CW altrimenti CCW
 // potrebbe essere più efficiente non usare digitalRead ma comparare direttamente il registro della porta per capire la direzione  
 if(digitalRead(encodPinA1)==1)
 {
   if(digitalRead(encodPinA1)==digitalRead(encodPinB1))  
   {
     pulse ++;
   }
   else      
   {  
     pulse--;
   }
 }
}


Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

Janos

Esattamente, devi lavorare senza la digitalRead, è troppo lenta per un gestore di interrupt... Devi andare a vedere quei pin su quale porte del micro solo collegati e lavorare con quelle.

Leggiti sul datasheet cosa fanno i registri PORTx, PINx e DDRx. x sta per la porta in questione: i pin sono raggruppati in gruppi da 8 chiamati porte e ogniuna di queste porte hanno 3 registri per essere pilotati. Ad esempio per il primo gruppo avrai i registri PORTA, PINA e DDRA.

lesto

imagni che interrupt encoder sia attivato su uno dei due pin. Quindi, sapendo nella fase di setup se il pin è alto o basso, a questo punto puoi evitare una digitalRead sapendo che lo stato si inverte ogni interrupt.

la mia opinione è che il segnale alto/basso dall'encoder dura talmente poco da essere più veloce delle digitalRead. Una soluzione può essere quella di usare 2 interrupt, uno per pin, e poi dal loop verificare se sono avvenuti entrambi (se il loop è veloce), altrimenti c'è da rivedere il codice...

per test dovresti contare il numero di interrupt però solo su un pin alla volta, e darci così le frequenze (e magari pure le durate del segnale alto/basso) per capire da dove arriva sto casino

edit: ovvio che per contare gli interrupt niente digitalread! un semplice count++ basta e avanza
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

niki77


... i registri PORTx, PINx e DDRx. x sta per la porta in questione: i pin sono raggruppati in gruppi da 8 chiamati porte e ogniuna di queste porte hanno 3 registri per essere pilotati. Ad esempio per il primo gruppo avrai i registri PORTA, PINA e DDRA.


Si sapevo dell'esistenza e della possibilità di accedere direttamente ai registri ma non pensavo che digitalRead fosse stato veramente così lento (ammesso che dipenda da quello)

Potrei anche collegare l'interrupt invece che al CHANGE, al RISING (salita) e quindi andare come dice lesto a controllare solo il valore dell'altro canale per capire la direzione, che ne dite?

L'idea di collegare interrupt ad entrambi i canali non mi sembra delle migliori... però non si sà mai, ora vediamo.

Nessuno di voi pensa  che con questo harware (encoder del mouse a rotellina a fotodiodi) ci possano essere problemi fisico meccanici del sistema ad impedire letture di rotazioni molto elevate? (es. 2000 rpm)

Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

lesto

allora, i tempi di risposta dei fotodiodi (quindi anche dei led) sono nell'ordine dei nanosecondi, quindi più veloci fino a 1000 volte di un arduino :)
magari dopo c'è un minimo di circuiteria per incrementare la durata del segnale alto, ma dubito che superi i 100microsecondi

Code: [Select]
Si sapevo dell'esistenza e della possibilità di accedere direttamente ai registri ma non pensavo che digitalRead fosse stato veramente così lento (ammesso che dipenda da quello)

se non erro una digitalread dura un centinaio di microsecondi, che scendono a 10/20 se usi i registri

fai questo conteggio di interrupt rising, prima su un pin e poi sull'alto. In base a questo capiamo dovè il problema
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

niki77


allora, i tempi di risposta dei fotodiodi (quindi anche dei led) sono nell'ordine dei nanosecondi, quindi più veloci fino a 1000 volte di un arduino :)


ALT, piccola precisazione.

L'encoder che sto usando è formato da un fotodiodo emettitore (anodo e catodo) e dal ricevitore che è nella versione a 3 pin, quindi è un dual foto transistor.
Il tutto è cosi collegato:

Fotodiodo emettitore collegato al +5 tramite resistenza da 1k che dovrebbe limitare la corrente a 50mA.
Il dual fototransistor collegato col comune al +5 e i due terminali del canale A e del canale B collegati ai due pin di arduino (senza pull-up) con due resistenze da 100k verso massa.
Così penso che il segnale non sia condizionato in alcun modo, ma come dicevo prima essendo ottico dovrebbe essere esente da disturbi e pertanto non credo vada trattato, ma si accettano obiezioni !
Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

lesto

ahhhhhh allora tu non hai a che fare con un segnale digitale, ma analogico.
I fototransistor sono molto più lenti di un fotodiodo.
se metti il fototransistor sul buco, avrai sì un valore quasi a 5V, ma quando l'encoder gira in fretta, probabilmente il passaggio luce/non luce è abbastanza veloce per non accendere completamente il fototransistor, e quindi crea una specie di PWM. E' facile immaginare che il voltaggio non supera i 3V e quindi non viene riconosciuto come 1 logico.

soluzione: baudrate al massimo, fai un loop con una analogRead sui 2 pin a cui collegherai i sensori, e stampa i valori a video. poichè la seriale è un collo di bottiglia, fai la media di 1000 letture analogiche (una lettura analogica dura 200micros, a te i calcoli :) )
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

niki77

Ho pensato di provare a gestire l'interrupt con solo questo codice

Code: [Select]


attachInterrupt(1, interruptEncoder, RISING);



e il gestore

Code: [Select]


void interruptEncoder() 
{                     
  if(PIND&3)
    pulse++;
  else
    pulse--;
}



Praticamente legge solo i fronti di salita del canale A ( evento RISING su PD2) e verifico lo stato del canale B (PD3)
Se lo stato del canale B è alto, quindi uguale al canale A incremento,  altrimenti decrementa.
La teoria ci dovrebbe essere tutta, ora c'è da provare !

Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

lesto

scusa ma quì c'è un errore di base.
Quando fai

attachInterrupt(1, interruptEncoder, RISING);

stai attaccando un interrupt sul pin di interrupt 1 (che non corrisponde al digitale 1), qundi
1: è inutile il controllo if(PIND&3) perchè il controllo è già fatta a priori.
2: sicuro che il pin interrupt 2 sia il PIND&3? altrimenti fin'ora stai misurando solo rumore
sei nuovo? non sai da dove partire? leggi qui: http://playground.arduino.cc/Italiano/Newbie

niki77

#14
May 09, 2012, 02:54 pm Last Edit: May 09, 2012, 02:57 pm by niki77 Reason: 1
Attenzione !!!!

Hai ragione, ho sempre fatto confusione , interrupt 0 è su PD2, ed interrupt 1 è su PD3 !

Quindi devo o spostare la condizione in  'PIND&2'

oppure cambiare l'interrupt gestito in  'attachInterrupt(0, interruptEncoder, RISING);'

supppongo però che questo sia intercorso dopo l'ultima modifica inquanto non era possibile che con le prove precedenti riuscissi ad avere risultati abbastanza coerenti solo col rumore.... ma poi che rumore??

P.S. possibile che di 15 (dico quindici non come numero a caso ma come 15) mouse a rotellina che ho smontato non ne ho trovato uno col detector digitale a 4 pin????????



Vi è una spiegazione scientifica a tutto.
La fede è solo quell'anello che si porta al dito dopo il matrimonio.

Go Up