Buongiorno a tutti
dopo un paio di mesi di lavoro sono in dirittura di arrivo con la mia tesi, cioè il controllo con arduino di un pendolo inverso.
l'arduin deve prima con un algoritmo di swing ribaltare il pendolo e poi tenerlo in equilibrio...
dati gli scarsi mezzi che ho a disposizione (un binario per porte scorrevoli, una cinghia che slitta e un motorino smontato da una stampante) non è stato facile...ma qualcosa funziona...
il principio di funzionamento è fatto da 3 controllori in cascata
il pendolo è attaccato ad un perneo che ruota quasi senza attrito, ed un encoder incrementale a 2000 settori mi resitituisce l'angolo
con questo viene calcolato l'angolo e la velocità angolare
il controllore PD mi passa poi un riferimento al controllore PI in velocità, che mi manda un riferimeto al PWM
tutto funziona..MA..
l'encoder conta attraverso interrupt sui pin 2 e 3
finche oscilla piano e non va tanto avanti e indietro non c'è problemi...quando si inizia a parlare di rapidi cambiamenti di direzione, (intoro alla posiz di equilibrio) l'arduino perde qualche conteggio e così l'angolo del pendolo mi risulta sbagliato anche di una decina di settori (quasi 2 gradi) e quindi non funziona più niente
ho anche provato a far contare 1000 all'encoder e ridurre il tempo di campionamento (loop) a 20millisec, ma non è cambiato nulla!
quali sono le istruzioni che non vengono interrotte dall'interrupt? o come potrei fare per non perdere il conteggio di qualche settore?
se volete vi allego il codicie, ma è molto lungo!
Tommaso:
quali sono le istruzioni che non vengono interrotte dall’interrupt? o come potrei fare per non perdere il conteggio di qualche settore?
Non esiste nessuna istruzione che non viene interrotta, parlando di Assembly, quando si verifica un interrupt al termine dell’istruzione in esecuzione, da 1 a 4 cicli macchina, il controllo viene trasferito alla isr.
Sarebbe il caso di vedere il vero codice C generato da wiring per capire se qualcosa disattiva temporaneamente l’interrupt esterno a favore di altri processi.
Comunque c’è un limite di velocità gestibile dagli interrupt esterni, se lo superi è normale che ti perdi dei conteggi, tocca farsi due conti con la risoluzione dell’encoder, la velocità angolare e la durata della ISR per capire se la frequenza degli impulsi supera la velocità massima gestibile dall’ATmega 328.
Altra fonte di problemi può essere come gestisci gli impulsi all’interno della ISR, se ci metti molto tempo è normale che perdi impulsi quando questi diventano veloci.
Non è che stai usando dei float all’interno della ISR ?
//---- ENCODER FUNCTIONS
void doEncoderA(){
if (digitalRead(encoderPinA) == HIGH) { // low-to-high on channel A
if (digitalRead(encoderPinB) == LOW) { encoder_counter++; } else { encoder_counter--; } }
else { // high-to-low on channel A
if (digitalRead(encoderPinB) == HIGH) { encoder_counter++; } else { encoder_counter--; } }
}
void doEncoderB(){
if (digitalRead(encoderPinB) == HIGH) { // low-to-high on channel B
if (digitalRead(encoderPinA) == HIGH) { encoder_counter++; } else {encoder_counter--; } }
else { // high-to-low on channel B
if (digitalRead(encoderPinA) == LOW) { encoder_counter++; } else { encoder_counter--; } }
}
queste sono le 2 ISR e encoder_counter è un ,long INT che sta nella RAM
pensavo che ci fossero delle istruzioni privilegiate come in altri uC che non venivano interrotte anche in caso di interrupt, per questo pensavo che qualche settore non venisse contato
Tommaso:
queste sono le 2 ISR e encoder_counter è un ,long INT che sta nella RAM
Premesso che in realtà quello è solo del codice che wiring include nella vera ISR, che già contiene altro codice, è possibile ottimizzarle per ridurre i tempi di esecuzione in modo da poter aumentare la frequenza massima in ingresso.
Toccherebbe misurare in modo abbastanza preciso la durata complessiva della ISR quando gira l'encoder, in questo modo è possibile determinare la massima frequenza in ingresso.
Se hai a disposizione un DSO setta un pin qualunque a 1 logico quando entri nella "doEncoderA" e lo resetti quando esci.
Girando l'encoder, sempre nella stessa direzione, vedrai un'onda rettangolare sul pin controllato dalla funzione, la durata a ON ti dice quanto tempo ci mette il micro ad eseguire quella funzione, ma non la durata totale della ISR.
Aumentando la velocità di rotazione dell'encoder a un certo punto l'onda rettangolare diventerà irregolare, cioè non hai più impulsi positivi a distanza fissa, avrai dei salti nella sequenza, questa è la frequenza limite e, implicitamente, la durata complessiva della ISR inclusa la latenza d'ingresso e uscita.
Trovato questo valore puoi verificare sperimentalmente se è maggiore o minore della frequenza in uscita dall'encoder quando perdi i conteggi.
pensavo che ci fossero delle istruzioni privilegiate come in altri uC che non venivano interrotte anche in caso di interrupt, per questo pensavo che qualche settore non venisse contato
L'interrupt è un evento hardware e viene gestito dal micro, il linguaggio di programmazione non può interferire con questo processo.
Però è possibile attivare e disattivare a piacere gli interrupt pertanto il programmatore, e non il linguaggio, può decidere che una certa funzione del programma non deve essere interrotta, il prezzo da pagare è la possibile perdita di eventi legati agli interrupt.
In alcuni linguagggi di programmazione, p.e. wiring, gli interrupt sono filtrati dal linguaggio stesso perché sono già in uso da alcune funzioni di alto livello, quindi non è detto che un interrupt definito dall'utente venga eseguito sempre subito perché è possibile che quella tipologia di eventi sia momentaneamente disattivata da altre funzionalità del software.
Se parliamo di C ANSI non esiste nessuna funzione di libreria standard che disattiva gli interrupt, se parliamo di compilatore non standard che utilizzano librerie fuori specifiche, sopratutto quando l'interrupt viene gestito dal compilatore e non dal programmatore, è facile che determinati interrupt vengono disattivati temporaneamente e questo porta alla possibile perdita di eventi.
Intanto, perdona la mia ignoranza ma...cosa è un DSO?
Quello che volevo sapere era appunto se si sapeva che alcune funzioni dell'arduino, tipo che ne so, l'analogRead,le Serial.print o simili, disattivavano gli interrupt, così andavo ad ottimizzare il codice eliminando o riducento tali chiamate!
non so se mi sono spiegato
Tommaso:
Intanto, perdona la mia ignoranza ma...cosa è un DSO?
DSO = Digital Storage Oscilloscope, cioè un oscilloscopio digitale, puoi fare la stessa misura anche con un normale oscilloscopio analogico, ma sarà meno precisa.
Quello che volevo sapere era appunto se si sapeva che alcune funzioni dell'arduino, tipo che ne so, l'analogRead,le Serial.print o simili, disattivavano gli interrupt
Se parliamo di wiring quasi sicuramente si, però quali e come lo fanno non lo so, il solo modo per saperlo con sicurezza è prendere il sorgente C generato da wiring, puoi vedere dove viene messo compilando in modo verbose (tasto shift premuto), controllare cosa fa esattamente la ISR e se ci sono, oltre a dove sono, istruzioni che attivano/disattivano gli interrupt.
P.S. ho provato con uno sketch che stampa solamente il valore di encoder_counter e i settori sono contati correttamente, è quindi qualche cosa nel progamma completo che mi disattiva temporaneamente gli interrupt!
Una cosa non ho capito; come mai Ti servono 2 interrupt per controllare l'encoder.
Basta un interrupt su una fase. Nel momento che si verifica l'interupt controlli se l'altro canale è L o H e da tutto questo puoi capire che è stato un impulso e la direzione.
percui Ti basta come funzione interrupt:
presumo che hai attivato l'interrupt con il passaggio L-H della fase A del encoder:
Mi servono 2 canali perchè sarebbe un 500 Settori, con 2 led quindi riesco a moltiplicare per 4 la risoluzione! entrambe le funzioni si attivano con CHANGE
Dai un’occhiata a questo sito dove vengono eseguite delle misure sui tempi di risposta di Arduino per gli interrupt esterni e come bypassare wiring per accorciarli notevolmente.
Tommaso:
finche oscilla piano e non va tanto avanti e indietro non c'è problemi...quando si inizia a parlare di rapidi cambiamenti di direzione, (intoro alla posiz di equilibrio) l'arduino perde qualche conteggio e così l'angolo del pendolo mi risulta sbagliato anche di una decina di settori (quasi 2 gradi) e quindi non funziona più niente
Ma come fai ad essere certo di questa cosa ?
Non è che il problema, in realtà, sia il pid che non riesce a stare dietro i movimenti ?
Ovvero, come misuri la reale inclinazione del pendolo e la compari con la lettura dell'encoder eseguita da Arduino, sopratutto come fai a farlo in real time ?
Che ha problemi per alte velocità l'ho trovato in modo sperimentale.
ho preso il programma completo e con una Serial .print stampo a video solamente il valore encoder_counter, se a mano muovo il pendolo lentamente non c'è problema,se inizio a fare movimenti rapidi, quando il pendolo torna in posizione verticale di riposo non si è azzerato il valore encoder counter e neppure è un multiplo di 2000!
se invece tolgo tutte le altre funzioni, e lascio comunque la stampa a video dei settori, questo problema non c'è
A questo punto il modo più semplice per scoprire il problema è quello empirico.
Attiva le varie funzioni singolarmente, o a blocchi se tra loro concatenati, fino a che non trovi quella che ti fa sbagliare i conteggi, fatto questo si vede di capire perché succede e di trovare una soluzione.
Non mi pare che la analogRead usi gli interrupt, il sorgente è wiring_analog.c, si vede chiaramente che attende la fine conversione con un normale polling.
// start the conversion
sbi(ADCSRA, ADSC);
// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC));
Tommaso:
Mi servono 2 canali perchè sarebbe un 500 Settori, con 2 led quindi riesco a moltiplicare per 4 la risoluzione! entrambe le funzioni si attivano con CHANGE
Percui Tu vuoi triggerare sia sul fianco salente che su quello discendente di tutte due le fasi?
Tommaso:
Che ha problemi per alte velocità l'ho trovato in modo sperimentale.
ho preso il programma completo e con una Serial .print stampo a video solamente il valore encoder_counter, se a mano muovo il pendolo lentamente non c'è problema,se inizio a fare movimenti rapidi, quando il pendolo torna in posizione verticale di riposo non si è azzerato il valore encoder counter e neppure è un multiplo di 2000!
se invece tolgo tutte le altre funzioni, e lascio comunque la stampa a video dei settori, questo problema non c'è
La seriale a 9600 Baud blocca il programma per ogni carattere spedito ca 1mSecondo. Sappi questo che è un causa di esecuzione lenta di programmi. Alza la baudrate a 115200.
Ciao, scordati Serial.print e tutte le altre, salva i valori di test in array, disabilita le isr e spara i dati in seriale.
La seriale usa gli interrupt, di default quando entra in una isr tutti gli altri eventi sono disabilitati, cioè quando entra nella isr, viene eseguito un cli() clear Interrupt, quindi gli interruput 0 e 1 vengono disabilitati.
poi se vuoi velocizzare le isr, diciamo le funzioni doEncodrA e B, prova ad usare queste: