quanto spazio faccio? gli encoder

Arianna parafrasando Totò e Peppino si chiede “per andare dove devo andare, quanto devo andare?”, a questa domanda gli encoder danno la risposta.
Con encoder, nel caso di trasduttori di posizione, intendiamo dei dispositivi in grado di indicarci lo spostamento rotatorio di un albero o lineare di un carrello. Questo valore può essere assoluto o incrementale. I trasduttori possono essere di vari tipi: ai link https://en.wikipedia.org/wiki/Linear_encoder e https://en.wikipedia.org/wiki/Rotary_encoder, sono rappresentati esempi di encoder realizzati con diverse tecnologie.

Gli encoder sono composti di diverse parti: meccanica, elettronica, conteggio etc. In funzione dell’applicazione vengono acquistati già completi (es nell’automazione industriale) oppure costruiti con parti separate, per esempio in una stampante. Arianna ne ha uno realizzato a d hoc che ci permette di vedere le varie parti che lo costituiscono.

Il trasduttore meccanico

20170306_113814

Nel caso di Arianna usiamo un trasduttore incrementale rotatorio, detto anche angolare. Questo è un disco con dei fori lungo la periferia posto sull’asse della ruota, un sensore opportuno rileva il passaggio di ogni fessura, il numero di fessure sul disco è detto ppr cioè pulses per revolution. Quando contiamo ppr impulsi abbiamo fatto un giro. È possibile avere ppr che variano da 1 a decine di migliaia. Nel nostro caso ne abbiamo 20, con una ruota intorno ai 35 mm di diametro un impulso equivale a circa 5.25 mm.
Gli encoder possono anche indicare il senso di rotazione, nel nostro caso non abbiamo questa informazione.

Sensore elettrico

La rilevazione degli impulsi encoder viene fatta con sensori a infrarossi, questi sono composti da un emettitore e un ricevitore. L’interruzione del fascio luminoso viene rilevata dal ricevitore e costituisce l’informazione. I modelli a forcella (fork)  comprendono Tx e Rx meccanicamente allineati rendendoli facili da usare. Un modello usato è lo Omron ee_sx4070

20170306_114329

Tx Rx a infrarossi per la lettura degli impulsi

Il Tx è composto da un diodo led a infrarossi, si decide la corrente di lavoro ponendo una resistenza in serie. Il ricevitore è un fototransistor, un transistor la cui base è alimentata dalla luce.
Per il fototransistor la radiazione luminosa ha lo stesso effetto di iniettare corrente in base. Facendola semplice lo fa commutare dandoci un livello alto o basso in uscita a seconda che il percorso Tx Rx sia libero o meno. Avendo un fototransistor va polarizzato per poter funzionare, può essere messo a collettore o emettitore comune e le correnti vanno viste dal data sheet.

fototransistor

fototransistor

Si consiglia di usare sensori con già uscita digitale. Si riconoscono dall’avere 5 terminali anziché quattro. Al loro interno hanno già la parte elettronica che squadra il segnale e gli applica una isteresi per evitare falsi conteggi. Per isteresi si intende che la tensione per dare un livello alto in uscita deve superare un valore Vh, per dare un livello basso non è sufficiente che scenda sotto Vh ma bensi sotto Vl, che è minore di Vh. Questa differenza di livelli, isteresi, fa si che in caso di segnali incerti nel commutare non si abbiano false commutazioni. Il simbolo nel triangolo è rappresenta l’isteresi.

uscita digitale

uscita digitale

Conteggio hw o sw debounce

Il segnale in uscita del foto sensore va ora collegato a un ingresso digitale e contato. Questa operazione può essere fatta via software o tramite un circuito hardware. In ambedue i casi lo scopo è contare il numero di impulsi che sono arrivati.
Nel caso software possiamo mettere nel loop una lettura dell’ingresso, questo metodo è detto polling.  Ovviamente non possiamo rimanere fermi ad attendere il fronte, faremmo solo quello. Dobbiamo ad ogni ciclo verificare un eventuale cambio dell’ingresso e a fronte di un cambio incrementare un contatore. La prossima verifica avverrà al prossimo passaggio. Il sistema è adatto ove il tempo di cambio del segnale sia molto maggiore del tempo di ciclo. Nel nostro caso vogliamo avere una velocità intorno ai 0.5 m/s, con una ruota di sviluppo 0.15 m abbiamo un giro ogni 0.333 s. Il sensore da 20 impulsi a giro e quindi un impulso ogni 16 ms, poiché un impulso è un ciclo 50% alto e 50% basso abbiamo 8 ms per riconoscerlo.
Voler utilizzare il sistema del polling costringe a scrivere un programma strozzato da questo vincolo, per esempio l’invio di dati con la seriale può causare perdita di impulsi encoder.

L’interrupt

Questo problema si pose si dall’inizio della programmazione. Trovò una soluzione con l’interrupt o interruzione. Questo consiste nell’interrompere il flusso di istruzioni corrente per saltare a una parte di codice appunto di interrupt, al termine di questa sezione il controllo ritorna all’istruzione dove era stato interrotto. Così facendo il nostro processore sembra fare più cose insieme, in realtà ne fa sempre una per volta. Dal punto di vista logico l’interrupt ci permette di ragionare separando le cose dal punto di vista logico, alcune operazioni vengono eseguite in polling nel loop principale, quando serve un codice in interrupt gestisce immediatamente l’encoder.

dichiarazione dell’interrupt



	/* odometro dx
	   pin 21 (atMega) int 2	
           la funzione attachInterrupt richiede:
            il numero dell'interrupt da collegare. La funzione digitalPinToInterrupt() passandogli il pin lo
            trova per noi;
            la funzione o callBack da chiamare quando accade l'evento;
            l'evento sul quale attivarsi, questo può essere fronte di salita (RISING), discesa (FALLING),
            qualunque transizione (CHANGE).  
        */
	attachInterrupt(digitalPinToInterrupt(GIRO_DX_PIN), odometroDxMisuraHW, CHANGE);
	// pin 20 (atMega) int 3	
	attachInterrupt(digitalPinToInterrupt(GIRO_SX_PIN), odometroSxMisuraHW, CHANGE);


routine di interrupt lato Destro



/*
	pin 21 (atMega) int 2
	sotto interrupt incremmento contatore interi
	solo se R != 0
*/
void odometroDxMisuraHW(void){
unsigned long pulseTime;
        /*
          questo test verifica il tempo tra due impulsi
          se sono troppo vicini significa che c'è un errore nella lettura
        */
	if ((millis() - pulseTime) < MIN_TIME_TRA_PULSE) return;
	pulseTime = millis();

	if (statoRun == 0) return;
	//Serial.println("dx");     // debug

	digitalWrite(ledPin, !digitalRead(ledPin));    // segnalazione visiva
	if (direzione == AVANTI)  odometroDxCnt ++;
	else                      odometroDxCnt --;
    
}


Il codice in interrupt deve spesso essere veloce, cioè durare poco. Evitiamo quindi ove possibile di utilizzare conti “difficili” e fatti in floating point.  Nella tabella sotto abbiamo una idea dei tempi di esecuzione con diversi formati. Vediamo che la divisione è un conto difficile, questo perché la moltiplicazione gode di una parte hardware dedicata alle moltiplicazioni 8  bit per 8 bit = 16 bit mentre la divisione no.

Nanosecond execution times on Arduino Mega2560 (16 MHz)
Operation  uint8          int16          int32          int64          float
+                  63               884           1763           8428          10943
*                  125            1449          4592          57038        10422
/                 15859         15969       41866       274809      31951

http://forum.arduino.cc/index.php?topic=196522.0

Traduzione in spazio

Dal numero degli impulsi alla misura fisica il passo è breve, ci basta una moltiplicazione per la costante meccanica. La costante meccanica è il numero di mm che Arianna percorre per impulso encoder arrivato. Questa traduzione in unita fisiche, i mm, viene fatta solo quando ci serve. In questo caso nel task di controllo che gira ogni 100 ms. Giunta prossima alla posizione richiesta Arianna rallenta per poi fermarsi arrivata nell’intorno della destinazione fissato.



if ( distanza > odometro )	direzione = AVANTI;
else						direzione = INDIETRO;
/* gestione velocità
   se sono ancora fuori dalla zona di approccio 
   tengo la velocita' normale
*/
	if ( abs(distanza - odometro) > E_APPROCCIO){
		motorSpeedRef = motorSpeedValue;
	}
	else
		// altrimenti attuo la velocita' di approccio
		if ( abs(distanza - odometro) > E_POSIZIONAMENTO){
			motorSpeedRef = APPROCCIO;
		}
		else{
			// arrivato all'interno dell'errore di posizionamento
			// mi fermo
			motorSpeedRef = FERMO;
			lastPosition  = odometro;
			statoRun      = 0;
		}
}// fine parte temporizzata TEMPO_CONTROLLO ms

// faccio le variazioni di velocita' con una rampa
if ((millis()-lastTimeFast) > 10){
	lastTimeFast = millis();

	if (statoRun == 0) motorSpeedRef = 0;

	// rampa sulla velocita'
	if (motorSpeedRef > motorSpeed)	motorSpeed += 5;
	if (motorSpeedRef < motorSpeed) motorSpeed -= 15; if (motorSpeed > 250) motorSpeed = 250;
	if (motorSpeed <   1) motorSpeed = 0;
}// fine temporizzata veloce



 

La prossima volta “come indico il libro, pan, tilt e puntatore”

Correre parallelo alla libreria

Annunci

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...