Ciao a tutti,
dopo qualche tentativo nell'utilizzo della libreria PID, ho scoperto che è una vera schifezza in quanto presenta svariate limitazioni (ad esempio manca la possibilità di inserire un'azione di anti-windup). Ho deciso quindi di scrivere il codice per conto mio (per adesso solo lo sketch che mi serve, poi forse in futuro faccio una libreria). Ora la mia domanda, principalmente rivolta ai "matematici", è: come approssimo la derivata e l'integrale dell'errore?
In pratica io ho per adesso fatto così:
Il mio dubbio non è tanto per la derivata (per la quale ho scritto praticamente il rapporto incrementale della funzione errore), quanto per l'integrale, per il quale ho invece sfruttato il teorema della media ipotizzando la funzione errore lineare.
Che ne pensate? C'è un modo migliore per approssimare integrali e derivate?
ciao e grazie della risposta,
non so se dt è costante (dipende se arduino ci impiega sempre lo stesso tempo per effettuare ogni ciclo e questo non lo so) ma nel dubbio così dovrei andare sul sicuro, no?
per quanto riguarda l'integrale, non lo so se devo moltiplicare tutto ogni volta per ki o solo " (((error + previousError)/2) * dt) "
io ho moltiplicato tutto perché comunque intuitivamente " integrativa + (((error + previousError)/2) * dt " rappresenta l'integrale e il kp è moltiplicato appunto per l'integrale:
comunque facendo una prova ho visto che impostando il coefficiente ki da 0,01 a 1 si ottiene un comportamento abbastanza anomalo nel senso che fino ad un valore di circa 0,5 non si apprezza per niente l'azione integrale (non si ha l'incremento) e ad un certo punto l'incremento si ha con una velocità molto brusca e va subito in saturazione, quasi come se non ci fossero vie di mezzo. Ma al di là del ki, tu come approssimeresti un integrale definito?
Grazie ancora per l'aiuto.
Ho controllato e il valore precedente dell'integrale non va moltiplicato per KI prima di essere memorizzato trovi delle belle slide qui http://wpage.unina.it/detommas/tsa/Lezione_6.pdf, vai direttamente alla slide 18 e scorri a partire da quella.
Devi tenere l'errore integrale in una variabile a parte, ogni volta ci aggiungi l'integrale dell'errore sul nuovo periodo ma questa variabile NON va moltiplicata per KI, deve contenere l'integrale dell'errore e basta.
In uscita moltiplichi il valore per KI, il risultato della moltiplicazione NON va memorizzato, il ciclo dopo non ti serve
esempio
/*Compute all the working error variables*/
double error = Setpoint - Input;
errSum += (error * timeChange);
/*Compute PID Output*/
Output = ki * errSum + ....;
Sicuro che vuoi "migliorare" le librerie di PID esistenti?
Bene, ho capito che il ki non va moltiplicato per il valore precedente dell'integrale (e questo è certamente un primo errore), ma perché dici che il risultato della moltiplicazione non va messo in memoria? Aprendo la libreria PID_v1 scopri che l'integrale è stato scritto così ITerm+= (ki * error) dove ki=Ki*dt e poi alla fine calcolaoutput = kp * error + ITerm- kd * dInputquindi come vedi lui usa la stessa variabile. Poi anche sulle slide che mi hai mandato (e per le quali ti ringrazio) c'è i = i+GI*e dove (GI contiene ki) quindi anche lì la moltiplicazione per il coefficiente viene messa in memoria. Mi sbaglio?
Rifletti sul problema e vedrai che troverai la soluzione, prova a lasciare perdere gli esempi di codice che potrebbero essere completamente sbagliati ma rifletti sul significato dell'azione integrale del PID, vuoi fare l'integrale sull'errore e poi moltiplicare per KI o vuoi fare l'integrale sull'errore moltiplicato per KI e poi moltiplicare tutto nuovamente per KI?
Io credo che tu abbia ragione. In ogni caso devo rifletterci bene. Una cosa è certa: se hai ragione tu, è una ulteriore conferma che quella libreria deve essere migliorata. Nei prossimi giorni ne parlerò con un mio professore. Ti tengo aggiornato. Grazie ancora per gli spunti di riflessione. A presto
Ciao a tutti,
non ho trovato una sezione di presentazione(forse sono impedito) quindi lo faccio qui,
Sono Marco Montagni, studentello di ingegneria di Firenze, triennale in elettronica, quinquennale in elettrica e automazione, alcuni esami di un master in robotica fatti a Madrid, nella politecnica, in erasmus e passati come esami a scelta. Per laboratorio di automatica ho realizzato un progettino ed anche io mi sono trovato un po' limitato con la PID library V1.. quindi l'ho modificata e ve la condivido volentieri dato che grazie a tutti voi(comunità open) ho realizzato un bel progetto in pochissimo tempo.
ho imparato a programmare ieri, quindi non offendetemi troppo se ho fatto qualche cacata.. si sono diretto e fatelo anche voi che non siamo a far finta di governare! ]
Punto uno:
L'integrale di una costante per una variabile temporale, è identico alla costante per l'integrale della variabile.
Quindi nella discussione avete ragione entrambi.
Punto 2
Non è fatta male quella libreria.. tiene in considerazione diverse cose, ad esempio il fatto di modificare i K tutti insieme ed evitare di fare calcoli con mezzi k vecchi mentre si modificano, quindi io l'avrei rifatta molto simile anche da zero.. in ogni caso è limitata.
Punto 3
L'anti windup è essenziale in fase di taratura se non sono state fatte simulazioni, evita di far andare all'infinito o in overflow la/Il termine integrale.
Punto 4
L'innerloop ovvero il meno davanti alla derivata non è sempre buona cosa quindi ho messo una variabile per definirlo..
Punto 5
L'approssimazione è migliore come ha fatto il collega rispetto a quella in avanti o indietro, ma dovrebbe essere fatta per tutti i termini e non solo su alcuni. Ai fini pratici con 3 filtri di kalman 2x2 una imu 9dof in lettura, un gps, 2 pid 50ms, xbee, il ciclo è di 6ms.. quindi a meno di lavorare alti di f tipo per un dcdc, e su sistemi meccanici/fisici è buona qualsiasi tipo di approssimazione, ma se non ricordo male il passaggio continuo discreto potrebbe mappare instabili alcuni poli stabili sul continuo con approssimazione in avanti o trapezio quindi c'è da stare attenti..
Sarebbe ganzo modificare ulteriormente la libreria, ma non farla da capo, e inserire vari tipi di approssimazione in modo da renderla ancora più completa. Sarebbe ancora più figo inserire una variabile che definisse il tempo di ciclo in modo da entrare in esecuzione con una stima più corretta della definizione di tempo di esecuzione del pid..
Fate riferimento agli esempi della Pid library ecc.. ho solo aggiunto le ultime 2 variabili quindi non riscrivo esempi
Allora definite la istanza con queste veriabili (le ultime due sono aggiunte) potete modificarle in qualsiasi momento senza eseguire alcun metodo.
PID(double*, double*, double*,double, double, double, int, float*,int*);
con la variabile float (positiva) che chiamerete come vi pare, ho indicato il valore assoluto dell'antiw, che sarà il limite della parte integrale superiore, da sola la libreria prenderà come limite inferiore la stessa variabile con il meno davanti.
Con l'ultimo intero che chiamerete come vi pare indico proprio il coefficiente di innerloop quindi dovrà necessariamente essere +1 se disattivato= PID classico o -1 se attivato=PID con innerloop.. se inserite altri valori andranno a moltiplicare il Kd, insomma fate un pasticcio!
Spero di essere stato chiaro
Un saluto a tutti!
tipo cosi:
PID motorePID(&VelGPS, &Automotore, &VelRif,kpMot,kiMot,kdMot, DIRECT,&AntiMot,&IL);
rispetto a prima : PID motorePID(&VelGPS, &Automotore, &VelRif,kpMot,kiMot,kdMot, DIRECT)
per il resto fate riferimento alla libreria pid classica..
Grande leo!!
Letto.. non fa l'antiwindup ma limita l'integrale al limite impostato come uscita del pid.. L'antiwindup deve avere un limite anche diverso del limite dell'uscita. Ed anche se fa l'inteegrale e la derivata con il più esatto tempo di esecuzione, non entra in ciclo esattamente al tempo impostato.. nel senso che potrebbe entrare ad eseguire il PID al più tempo impostato + tempo di ciclo e potrebbe essere migliorato almeno a metà ciclo inserendo una variabile aggiuntiva..
Ciao, benvenuto, approfitto dell'occasione per chiederti se è possibile fare a meno di numeri in virgola mobile, cioè si può fare il calcolo con interi grandi 32 bit anziché float. Dal punto di vista computazionale il micro impiega meno tempo cpu quando fa i calcoli su gli interi.
Mi rendo conto che una divisione tra interi comporta una perdita di precisione, ma se ancora prima di fornire i dati alla PID moltiplico x 100 o 1000 forse la perdita di precisione e influente e si potrebbe guadagnare in stabilità visto che il micro impiega meno tempo ad eseguire il calcolo.
Tuttavia questo vale per micro senza fpu come arduino (per Arduino DUE??? c'è FPU)
@leo
Lo chiama antiwindup ma per come lo conosco io, ovvero dagli appunti e qualche libro degli stessi professori, con diagrammi a blocchi ecc risulta una limitazione (con il classico blocco) a parametri differenti sul termine integrale e uscita sommata dei termini.. e a logica mi torna che per alcune applicazioni possa essere impostata diversa per ottenere un migliore controllo. @MauroTec
Non so dirti se in realtà non guadagni nulla o qualcosa oppure è cosi ottimizzato che in realtà ci perdi perché non sono cosi ferrato in assembler. Mi viene da pensare che alla fine se il micro non elabora numeri in virgola mobile li trasforma da solo in interi e poi ci fa' i calcoli proprio come faresti te usando numeri interi (moltiplicandoli in ingresso e ridividendoli in uscita successivamente) sarebbe la stessa cosa, o forse dato che è ottimizzato il suo codice per farlo lo fa' in minor tempo. In ogni caso si parla di microsecondi e solo su alcune stringenti applicazioni potrebbe essere giustificata una scelta cosi azzardata..
Come spesso accade però la soluzione è sempre provare e vedere!!!
Quindi ti ho modificato la libreria e ci metti tutti int32.. la trovi in allegato.. prova e facci sapere.. pero mi raccomando
usa per il pid classico
t1 = micros();
compute() //fai elaborare il pid
t2= micros();
T=t2-t1; // poi cerca il massimo di T dato che il pid verrà calcolato solo alcune volte
per il pid interi
t1 = micros();
//traforma le variabili
compute() //fai elaborare il pid
//ritrasfoma le variabili
t2= micros();
T=t2-t1; // poi cerca il massimo di T dato che il pid verrà calcolato solo alcune volte
Questo per ricordarti di trovare il tempo di elaborazione includendo la conversione
Non ho avuto tempo per analizzare in dettaglio il codice, però ho fatto dei test rapidi che mi hanno lasciato un poco sorpreso e devo indagare nelle internal code e avrlibc.
La dimensione del firmware aumenta quando questo opera delle operazioni sugli interi, rispetto a quando opera su virgola mobile. Tuttavia non ho indagato sulla velocità di esecuzione e mi aspetto maggiore velocità con gli interi, ma potrei essere smentito.
Se dichiari delle variabili in più per la trasformazione da float ad int aumenta la dimensione chiaramente.. ricordati di inserire i tempi che includano la trasformazione..
ciao
Si certo, ho scritto del codice di test (che ho perso) dove si compiono le stesse operazioni, e ciò che cambia è solo il tipo della variabile e ovviamente se l'operazione coinvolge float le variabili intere non ci sono proprio nel codice di test, stessa cosa quando ci sono le variabili int elimino tutte le variabili float e le operazioni su queste.
Non ho avuto tempo per indagare, ma rimane una cosa da fare prima o poi.
Una idea me la sono fatta, ma rimane aria fritta se non indago a fondo.
Metto questo post tra i preferiti, così prima o poi troverò il tempo per indagare a fondo.