Controllo motore con encoder

Ciao a tutti, sto provando a controllare un motore DC con Arduino (e Motor Shield rev3), mentre voglio ottenere le informazioni sullo scostamento angolare mediante un encoder incrementale a 256 impulsi per rivoluzione.

Per esempio vorrei che il motore giri mezzo secondo in una direzione e mezzo secondo nell'altra, mentre mediante un interruttore ottengo le informazioni sullo scostamento angolare.

Il mio codice è il seguente:

define encoder0PinA 2

define encoder0PinB 4

int brake = 8; int dir = 13; int vel = 11;

volatile int encoder0Pos = 0; void setup() {

pinMode(encoder0PinA, INPUT); digitalWrite(encoder0PinA, HIGH); // pull-up resistor pinMode(encoder0PinB, INPUT); digitalWrite(encoder0PinB, HIGH); //pull-up resistor

attachInterrupt(0, doEncoder, CHANGE); // encoder pin on interrupt 0 - pin 2 pinMode(dir,OUTPUT); //pin 12 direzione motore pinMode(vel,OUTPUT); //pin 3 PWM motore pinMode(brake,OUTPUT); //pin 9 freno motore Serial.begin(9600); Serial.println("Start!"); }

void loop(){ digitalWrite(dir,LOW); analogWrite(vel,255); delay(500); digitalWrite(dir,HIGH); analogWrite(vel,255); delay(500);

}

void doEncoder() { if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) { encoder0Pos++; } else { encoder0Pos--; }

Serial.println (encoder0Pos, DEC); }

Il problema è che in questo modo il motore non cambia mai direzione (o comunque neanche lontanamente nei tempi previsti), poichè l'interruttore interrompe talmente spesso l'esecuzione del loop da non eseguire mai l'istruzione per la quale si inverte la polarità. Come posso risolvere?

Grazie in anticipo

Ah alcuni commenti sono sbagliati a causa di un cambio di pin che ho effettuato, non fateci caso

Mi ricommento da solo... ho commentato il Serial.print per vedere se era quello che rallentava troppo l'esecuzione ed è andato perfettamente, poi l'ho reinserito nel codice e va tutto perfettamente. Non ho idea del perchè.

Ti invitiamo a presentarti (dicci quali conoscenze hai di elettronica e di programmazione) qui: Presentazioni e a leggere il regolamento: Regolamento - qui una serie di schede by xxxPighi per i collegamenti elettronici vari: ABC - Arduino Basic Connections - qui le pinout delle varie schede by xxxPighi: Pinout - qui una serie di link [u]generali[/u] utili: Link Utili

Il codice devi racchiuderlo nei tag code, vedi sezione 7 del regolamento, spiega bene come fare. Altrimenti parte del codice può essere visualizzata male o mancare perchè interpretato come attributo del testo stesso.

Il problema non è l'interrupt (non chiamarlo interruttore, altrimenti sembra stai parlando di un tasto/pulsante) Il problema sono le delay() Vedi lo sketch BlinkWithout con esempio di Blink di un led senza l'uso di delay() ma di millis()

Ciao,

intanto grazie per la risposta. Ho effettivamente visto lo sketch che utilissa millis() anzichè delay() e ho implementato nel mio codice questa tecnica, ma comunque il problema si ripresenta oltre una certa velocità di rotazione del motore (attualmente non ho del tutto idea a quale velocità massima io debba andare, forse basta quanto riesco effettivamente a fare).

attachInterrupt(0, doEncoder, CHANGE); // encoder pin on interrupt 0 - pin 2

Questa istruzione passa alla routine doEncoder due volte per ogni transizione da HIGH a LOW: ti conviene usare il FALLING al posto del CHANGE.

cyberhs: Questa istruzione passa alla routine doEncoder due volte per ogni transizione da HIGH a LOW: ti conviene usare il FALLING al posto del CHANGE.

Grazie per la risposta, ma se pongo FALLING, qualora il canale A dell'encoder abbia una transizione da LOW ad HIGH non verrebbe rilevata, o sto sbagliando?

Giusto, ma devi rilevarlo?

Ciao Uwe

Non vorrei dire fesserie, ma poichè mi è necessario muovere il motore in ambo i versi sì, mi è necessario.

In ogni caso sto cercando ora di modificare questo sketch per poter rilevare la velocità angolare del motore a partire dalle informazioni sulla posizione, ma vorrei trovare una soluzione migliore delle differenze prime, alquanto affette da rumore

Giammy1288: Non vorrei dire fesserie, ma poichè mi è necessario muovere il motore in ambo i versi sì, mi è necessario.

Esatto, inoltre è prassi comune usare gli encoder in quadratura, ovvero si rilevano sia i fronti di salita e discesa di entrambi i canali in modo da aumentare la risoluzione di un fattore 4x oltre che per rilevare la direzione di marcia. Per contro Arduino non è molto adatto a gestire un encoder, dipende molto dalla risoluzione e dalla velocità di rotazione però se parliamo di un controllo velocità, e/o posizione, fatto come si deve Arduino non va bene.

astrobeed: Esatto, inoltre è prassi comune usare gli encoder in quadratura, ovvero si rilevano sia i fronti di salita e discesa di entrambi i canali in modo da aumentare la risoluzione di un fattore 4x oltre che per rilevare la direzione di marcia. Per contro Arduino non è molto adatto a gestire un encoder, dipende molto dalla risoluzione e dalla velocità di rotazione però se parliamo di un controllo velocità, e/o posizione, fatto come si deve Arduino non va bene.

Per utilizzare gli encoder in quadratura però dovrei implementare due interrupt, caricando Arduino di ulteriore costo computazionale, se anche qui non vado errato.

Comunque sì, da test che ho effettuato, a quanto pare non mi è necessario rilevare la posizione angolare per velocità di rotazione particolarmente elevate, Arduino dovrebbe essere in grado di gestirlo. Il mio encoder ha 256 impulsi per rivoluzione, quindi non ha una risoluzione poi così elevata. Spero che Arduino Uno sia sufficiente.

Quello che però sto cercando di capire è come effettuare una buona rilevazione della velocità differenziando le posizioni angolari fornitemi dall'encoder, poichè ho bisogno di vedere come il motore di comporta nel dominio del tempo ad un gradino di tensione, per poi cercare (tramite Matlab) di stimare una funzione di trasferimento posizione angolare/tensione del motore, in modo da poterlo controllare in posizione angolare.

Giammy1288: Per utilizzare gli encoder in quadratura però dovrei implementare due interrupt, caricando Arduino di ulteriore costo computazionale, se anche qui non vado errato.

Mi sa tanto che non hai ben chiaro come funziona e come deve essere usato un encoder, se devi rilevare il senso di marcia è indispensabile un encoder a doppio canale, questo implica l'uso di due pin e dei relativi interrupt on change indipendentemente da come li usi. Un encoder da 256 step è considerato di media/alta risoluzione, il limite è dato dalla velocità di rotazione, di conseguenza il numero di impulsi da gestire. Esempio pratico, già a solo 3000 rpm, nella media per un motore DC, ottieni 512 cambi di stato sul singolo canale ad ogni rotazione, ovvero ottieni ben 3000/60*512 = 25600 impulsi (= interrupt per ogni canale), sei già ampiamente oltre le possibilità di Arduino.

astrobeed: Mi sa tanto che non hai ben chiaro come funziona e come deve essere usato un encoder, se devi rilevare il senso di marcia è indispensabile un encoder a doppio canale, questo implica l'uso di due pin e dei relativi interrupt on change indipendentemente da come li usi. Un encoder da 256 step è considerato di media/alta risoluzione, il limite è dato dalla velocità di rotazione, di conseguenza il numero di impulsi da gestire. Esempio pratico, già a solo 3000 rpm, nella media per un motore DC, ottieni 512 cambi di stato sul singolo canale ad ogni rotazione, ovvero ottieni ben 3000/60*512 = 25600 impulsi (= interrupt per ogni canale), sei già ampiamente oltre le possibilità di Arduino.

Ok mi sono sbagliato, semplicemente con il codice scritto all'inizio (e quindi con un solo interrupt per il canale "A") praticamente utilizzo solo metà della risoluzione dell'encoder. Per sfruttare tutti i 256 impulsi per rivoluzione devo usare un interrupt per ciascuno dei due canali dell'encoder, ora ho capito.

Il fatto è che io nel mio progetto non necessito che il motore sia nè particolarmente veloce nè (anzi non deve assolutamente) compiere uno scostamento angolare di 30° in entrambi i versi. Adesso sto semplicemente provando ad effettuare alcuni test in modo da ricavare una funzione di trasferimento del motore e per questo mi serve una risposta al gradino nel dominio del tempo del motore.

Giammy1288: Adesso sto semplicemente provando ad effettuare alcuni test in modo da ricavare una funzione di trasferimento del motore e per questo mi serve una risposta al gradino nel dominio del tempo del motore.

Ma la fisica non la insegnano più a scuola ? Con un impulso a gradino il motore accelera alla massima velocità molto rapidamente, pochi millisecondi, te lo scordi di misurare la durata degli impulsi, ovvero accelerazione e velocità istantanee, con un mezzo limitato come Arduino.

astrobeed: Ma la fisica non la insegnano più a scuola ? Con un impulso a gradino il motore accelera alla massima velocità molto rapidamente, pochi millisecondi, te lo scordi di misurare la durata degli impulsi, ovvero accelerazione e velocità istantanee, con un mezzo limitato come Arduino.

Non voglio un modello accuratissimo, se l'evoluzione dinamica del motore è così più veloce dei tempi di acquisizione di Arduino mi potrebbe bastare anche solo una buona misura della velocità a regime del motore in ambo i versi (probabilmente ha due caratteristiche differenti essendo un motoriduttore). In tal caso semplificherei il problema come se il motore avesse una funzione di trasferimento con un solo guadagno statico o al massimo con un solo polo.

L'unica cosa che mi preme è ottenere un segnale poco rumoroso di velocità, cosa che non ottengo effettuando delle differenze prime.

mbe metti dei stepper e gli fai fare quello che vuoi