encoder in quadratura

ok grazie

mi sono ricordato proprio adesso che con la famiglia AVR XMega (es: ATxmega128a1) avresti potuto farlo poichè possiede una parte hw dedicata chiamata Quadrature Decoder (QDEC)....
Io l'ho provato qualche mese fa e funziona alla grande anche con encoder molto risoluti.

cioè?
ps non so se ho scritto che ho inserito un moror shield

ciao ho cambiato motorino ed encoder ma il problema persiste non riesco ad acquisire il valore dell'encoder
http://www.micromotorssrl.com/electricaldata_1/e_data_1_pg.html

questo è ciò che sto utilizzando cosa ne pensate come mai non va

Nooooooooooooooooooooooooo !!!!!

ma che hai fatto! quell'encoder non va bene per la tua applicazione:
intanto hai solo 3 impulsi per giro, non bastano per realizzare un controllo di velocità e poi hai un solo canale e quindi non puoi risalire alla direzione di rotazione.

per la direzione uso il pin di direzione del motor shield mentre come mai dici che non si può fare con 3 impulsi?

max tu cosa devi fare, ancora non mi è chiaro.

a) un controllore di posizione
b) devi controllare solo la velocità dell'azionamento meccanico al variare del carico
c) devi controllare la posizione ove la velocità deve assumere profili diversi (inviluppo dinamico del profilo di velocità es: profilo trapezoidale)
d) devi controllare entrambe le cose

spiega meglio e dai più dettagli.....

devo fare un controllo di velocita, coppia e posizione, però per prima cosa devo fare il controllo in velocità e per far questo dovrei ricavare dall'encoder gli impulsi e ricavarmi la velocità il problema che invece di avere per ogni giro 0000011100000111000000 ho una combinazione casuale di giri ho provato una soluzione farlocca usando questo codice ma non è stabile e nemmeno preciso

"
#include <SoftwareSerial.h>

#define encoder 5
#define motore 3

int val;
int flag;
int cont;
int app;
int pwm;
int pwmfb;
float giro;
float durata;
float app2;
float app3;
float vel;
float pausa;
int posizione;
int pot=5;

void setup()
{
pinMode(encoder,INPUT);
pinMode(motore,INPUT);
Serial.begin(9600);
app2=0;
}

void loop()
{
// acquisizione del valore del pwm

posizione=analogRead(pot);
pwm=map(posizione,0,1023,100,255);
//pwm=pwm-pwmfb;

// fine acquisizione valore del pwm
analogWrite(motore,pwm);
// comincio la procedura per acquisire il dato dall'encoder

durata=millis();
//durata=durata-app2;

val= digitalRead(encoder); // acquisisco il valore (alto o basso del encoder)
// incremento il valore cont ognivolta che trovo una variazione di valore
if (val!=flag)
{ if (val==HIGH)
{flag=1;}
else
{flag=0;}
cont=flag+cont;
}
cont=cont;
//dividendo per 3 calcolo quanti giri ha fatto il rotore
giro=cont/3;
//Serial.print("impulsi ");
//Serial.println(cont);
//Serial.print("il motore ha fatto ");
//Serial.println(giro);
giro=giro*100; // trasformo in giri al secondo
vel=giro/durata;
pausa=map(pwm,100,255,350,20); // calcolo la pausa da calcolare per campionare l'impulso
//pwmfb=map(vel,0,0.75,0,255);
//Serial.print("la velocita ");
Serial.println(vel);
//Serial.print("il tempo ");
//Serial.println(durata);
Serial.flush(); // pulisco il buffer seriale
delay(pausa); // pausa per il campionamento

// fine acquisizione velocità

}
"

Risolto il controllo in velocità se vi interessa vi posto il codice.
Ora il problema è come aumentare la coppia

dopo i vari replay, sarei curioso di dare una sbirciatina al codice....

come aumentare la coppia?
chiaramente implementando algoritmo PID o controller fuzzy logic.... anche via firmware.

a te la scelta
ciao

eccovi il codice

#include <SoftwareSerial.h>

#define encoder 5
#define motore 3
#define pot 5

int pwm,pwmfb,posizione,err;
float vel,durata,durh,durl;

void setup()
{
pinMode(encoder,INPUT_PULLUP);
pinMode(motore,INPUT);
Serial.begin(19200);
err=0;
durata=0;
}

void loop()
{
// acquisizione del valore del pwm
posizione=analogRead(pot);
pwm=map(posizione,0,1023,100,255);
// fine acquisizione valore del pwm
// inizio del controllore
err=pwm-pwmfb;
pwm=pwm+err;
if (pwm>255) {pwm=255;}
// fine controllore

analogWrite(motore,pwm);

// comincio la procedura per acquisire il dato dall'encoder
durh = pulseIn (encoder, HIGH);
durl = pulseIn (encoder, LOW);
durata=durl+durh;
// fine acquisizione velocità

vel=10000/durata;
Serial.println(vel);
vel=vel*100;
pwmfb=map(vel,0,98,0,255);
}

cyclone:
dubito fortemente che riuscirai a leggere la sequenza in quadratura con il solo arduino alla max velocità.
la famiglia HEDS5xxx sono encoder con risoluzione molto elevata e in base alla sigla si può arrivare fino a 1024 CPR.

anche gestendo bene i segnali A e B utilizzando gli interrupt potrai stare dietro alla lettura solo se farai andare il motore molto ma molto lentamente.
per migliorare un pò la funzione di intercettazione e conteggio degli impulsi dovresti scrivere la routine in assembly (codice macchina).

Non è vero, per un'applicazione che ho sviluppato a lavoro sono riuscito a leggere un encoder in quadratura da 500 imp/giro a circa 25rpm dell'albero, quindi ad una frequenza di 12KHz per canale. Ovviamente c'è da scordarsi le digitalRead nel gestore di interrupt, troppo lente.

Inoltre, rispondendo ad un post successivo, i micromotors li utilizziamo anche a lavoro e sono con due fasi, non con una. Non so se quel micromotors che max ha acquistato ha solo una fase... Ad ogni modo considera che sono motoriduttori, quindi anche se hanno solo 3 imp/giro ma se hanno un rapporto di riduzione di 100 diventano 300 imp/giro sull'albero lento... :wink: Non so che rapporto di riduzione ha acquistato...

mi potresti dire come hai fatto e la logica cosi potrei riuscire a fare la retroazione in posizione

Janos:
Non è vero, per un'applicazione che ho sviluppato a lavoro sono riuscito a leggere un encoder in quadratura da 500 imp/giro a circa 25rpm dell'albero, quindi ad una frequenza di 12KHz per canale. Ovviamente c'è da scordarsi le digitalRead nel gestore di interrupt, troppo lente.

Si però per gestire 24000 interrupt al secondo (12000 * 2) con l'ATmega 328 non riesci a fare altro, per gli encoder con segnali che arrivano ad elevate frequenze tocca usare gli appositi IC oppure micro dotati dell'apposito modulo hardware.
Comunque 500 cpr in quadratura diventano 2000 ppr che a 25 rpm sono solo 833 Hz per canale, valore gestibile con Arduino anche se siamo al limite se vuoi fare pure altre cose.

Inoltre, rispondendo ad un post successivo, i micromotors li utilizziamo anche a lavoro e sono con due fasi, non con una.

Micromotors i motori te li fornisce come ti pare, ovvero senza encoder, con encoder a una fase o due fasi, tutti a bassa risoluzione, se non mi ricordo male il massimo che offrono loro sono 25 cpr, poi quelle che trovi in giro su i vari store è un altro paio di maniche.

max00:
mi potresti dire come hai fatto e la logica cosi potrei riuscire a fare la retroazione in posizione

Devi implementare un pid per il controllo della posizione.

@astro
perché solo 833Hz per canale? Comunque con tutti quegli interrupt, oltre al programma, ho un tempo ciclo di circa 150us, valore che acquisisco con la funzione micros() ad ogni inizio del loop.

@max

#define encInterrA 0
#define encInterrB 1
#define encA 2
#define encB 3

volatile unsigned long contEnc = 0; //Valore del conteggio dell'encoder

void encoderA() {
  unsigned char i = PINE;                   //Leggo il valore di PINE
  unsigned char a = i & (1 << PE4);         //Maschero gli altri bit per isolare quello corrisp. alla fase A
  unsigned char b = i & (1 << PE5);         //Maschero gli altri bit per isolare quello corrisp. alla fase B
  if (a == (1 << PE4)) {                    //Se A è alto
    if (b == 0) contEnc++;                  //e B è basso incrementa
    else contEnc--;                         //altrimenti decrementa
  }
  else {                                    //Se A è basso
    if (b == (1 << PE5)) contEnc++;         //e B è alto incrementa
    else contEnc--;                         //Altrimenti decrementa
  }
};

void encoderB() {
  unsigned char i = PINE;                   //Leggo il valore di PINE
  unsigned char a = i & (1 << PE4);         //Maschero gli altri bit per isolare quello corrisp. alla fase A
  unsigned char b = i & (1 << PE5);         //Maschero gli altri bit per isolare quello corrisp. alla fase B
  if (b == (1 << PE5)) {                    //Se B è alto
    if (a == (1 << PE4)) contEnc++;         //e A è alto incrementa
    else contEnc--;                         //altrimenti decrementa
  }
  else {                                    //Se B è basso
    if (a == 0) contEnc++;                  //e A è basso incrementa
    else contEnc--;                         //Altrimenti decrementa
  }
};

void setup() {
  digitalWrite(encA, HIGH);                           //Attivo i pullup degli ingressi dell'encoder
  digitalWrite(encB, HIGH);                           //Attivo i pullup degli ingressi dell'encoder
  attachInterrupt(encInterrA, encoderA, CHANGE);      //Attivo il gestore di interrupt per gli ingressi dell'encoder
  attachInterrupt(encInterrB, encoderB, CHANGE);      //Attivo il gestore di interrupt per gli ingressi dell'encoder
}

Considera che ho utilizzato un encoder con uscite npn, quindi ho attivato le resistenze di pullup interne. Queste, però, a 20KHz sono troppo grandi e quindi ne ho messe altre due esterne di valore più piccolo.
P.S. Questo codice è stato scritto per l'Arduino Mega 2560, quindi per il processore ATMega2560. Se usi l'Arduino Uno sicuramente non ti va bene perché i pin di interrupt non saranno sulla porta E. A tal riguardo ti consiglio di controllare il pin mapping dei relativi processori:

Janos:
@astro
perché solo 833Hz per canale?

500 impulsi per rotazione diventano 2000 in quadratura, 25 rpm sono 25/60 = 0.41666 Hz, 2000 * 0.41666 = 833 Hz

grazie per il codice ora cerco di capire la logica.
ps tu riesci a contare il num di impulsi?

@Astro
Hai ragione, ho sbagliato a dire io... :grin:
L'encoder, che ha una ruota di 65mm di diametro, quindi 204mm di perimetro, mi misura una velocità di scorrimento di 5mt/sec. Facendo i calcoli viene che l'encoder gira a 24.5Hz, non rpm... :wink: