gbm
Che cos'è l'algoritmo PID
Tutorials -
Lunedì 05 Luglio 2010 01:42
Scritto da gbm

Ciao a tutti. In queste ultimi mesi mi sono dedicato principalmente ad algoritmi per la correzione di un errore. Sono partito dall'algoritmo PID. Questo costrutto matematico è uno strumento molto utile nel line following (inseguitori di linea) e in quasi tutti gli altri campi dell'automazione. E' un'ottima base matematica da capire ed utilizzare per gestire la reazione ad un dato errore. Chiaramente non è la scelta piu' indicata per qualsiasi applicazione ma risulta davvero versatile. Trovo che sia fondamentale per un appassionato conoscere il funzionamento di questo tipo di correzione. Per questo ho scritto questa guida. Allo stesso modo noto che è un ambito abbastanza discusso, ma soprattuto ho trovato varie "interpretazioni" di questo algoritmo. Esiste una libreria: PID library arduino l'ho testa insieme al suo ottimo visualizzatore grafico che consiglio: Graphical frontend PID arduino ma sinceramente non mi sono trovato come avrei voluto. Poi caricare una libreria e perdersi il concetto di base secondo me è sbagliato. Quindi l'ho riscitto e ci ho lavorato parecchio. Alla fine sono 5 righe e non credo serva realmente una libreria per questo.

 

Proporzionale


P - proporzionale PID

P = valore errore * kP (variabile utilizzata per la regolazione)

L'azione proporzionale varia proporzionalmente all'errore. Questa parte è importante per le micro-correzioni o comunque in tutto il range di utilizzo dove la considerazione del tempo è trascurabile. E' possibile correggere un errore utilizzando unicamente questa parte, solo che il comportamento del sistema sarà oscillante e difficilmente convergerà verso lo 0, ma piu' probabilmente avrà un'andamento instabile che spesso ricade in risonanza.

Ponendo kP 0 e aumentandola uniformemente otterremo miglioramento nel tempo di crescita della correzione, aumento dell'esagerazione nella correzione, una grande diminuzione dell'errore in stato statico e a valori alti un effettivo degrado della stabilità.

 

Integrale


I - integrale PID

I  = [valore errore * Δt (durata loop) ] * per kI (regolazione)

L'azione integrale è propriamente l'integrale dell'errore rispetto al tempo. In parole povere I è un valore in cui viene sommato l'errore, questo viene moltiplicato per l'intervallo di tempo tra questa acquisizione e quella precedente. Il risultato (supponendo si volesse correggere un errore negativo) è un valore che non si azzerera raggiunto lo 0 come fa P, ma necessita che l'errore diventi negativo per un tempo che varia in rapporto all'accumulazione dell'errore stesso. Questa parte è un'ottima base per la correzione dell'inerzia acquisita se l'errore si protrae nel tempo (o almeno io l'ho utilizzata in quest'ambito) e soprattutto se ben regolata aiuta l'algoritmo a raggiungere lo zero ammorbidendo la forma della curva di risposta.

Ponendo kI 0 e aumentandola uniformemente otterremo rallentamento nel tempo di crescita della correzione, aumento massiccio dell'esagerazione nella correzione, una diminuzione dell'errore in stato statico e a valori alti un effettivo degrado della stabilità.

 

Derivativa


D - derivativa PID

D = valore errore - valore errore loop precedente / Δt (durata loop)

L'azione derivativa è studiata per compensare rapidamente le variazioni del segnale di errore. Questa correzione varia in rapporto alla velocità di cambiamento dell'errore. In poche parole è necessario ricordare l'errore della scorsa acquisizione e sottrarlo all'errore attuale. Ho notato che molti sul web snobbano questa parte, in tantissimi programmi viene regolata al minimo. Secondo me questa parte è poco capita dalla comunità. Io stesso ho faccio fatica a trovare una regolazione corretta e spesso ricado nel disabilitarla. Consiglio vivamente di sperimentare.

Ponendo kD 0 e aumentandola uniformemente otterremo una diminuzione leggera nel tempo di crescita della correzione, una diminuizione dell'esagerazione nella correzione e se kD ha un valore piuttosto basso puo' migliorare la stabilità.

 

La somma di questi tre termini è la correzione prodotta dall'algoritmo. Guardando le formule (fonte wiki) per molti sembrerà roba da nerd inconcepibile (e questo è anche un po' colpa dei matematici), ma studiata e applicata su qualcosa di fisico e ricontrabile è facile farla propria.

La cosa importante su cui io all'inizio sono caduto è l'analisi del tempo. E' facile pensare che il loop gia scandisca di per se il tempo, ma la durata di ogni loop è differente, in rapporto a come vengono svolte le routine e cosa sta succedendo, per questo serve salvarsi in due variabili (nel codice di esempio time e previousTime, i millis() di prima e dopo l'acquisizione per conoscere l'intervallo di tempo intercorso (Δt). Chiaramente aggiungendo funzioni o routine, è possibile che Δt aumenti e modifichi il comportamento del PID in questo caso andrà ricalibrato (molto probabilmente basterà diminuire equamente I e D in rapporto a quanto è aumentato Δt ).

Qui credo di scrivere qualcosa di leggermente discordante da questo interessante ebook creato da Sergio Camici per www.roboitalia.com. Come potete leggere la ritmica di funzionamento che usa Sergio è a tempo costante. Io ho scirtto una cosa leggermente diversa. Invece che avere un tempo costante e scartare il tempo avanzato aspettando il prossimo turno, semplicemente cronometro il tempo necessario per ogni loop e lo moltiplico. In questo caso, se un'acquisizione è durata piu' di un'altra avrà piu' peso nella correzione. Il codice sottostante sono decine di ore di sperimentazione e test empirici. Ma devo dire funziona piuttosto bene, è ancora leggermente affetto da Parkinson, ma spero di risolvero affinando le regolazioni.

 

Ecco un video che dimostra il funzionamento:

 

 

Come potete vedere la correzione è tutt'altro che perfetta. E' ancora oscillante e troppo determinata da P. Ma la cosa che davvero mi entusiasma è quanto la I sia in grado di correggere il momento inerziale acquisito da PICO in rapporto al tempo (e quindi quanto ha accelerato) in una direzione. Riesce effettivamente ad annullare il momento inerziale e spesso a riportare il robot quasi nella stessa posizione da cui è stato spinto via.

Ecco il codice:

//Giovanni Blu Mitolo - Gioblu Robotics - released under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License..
//use and enjoy free :-) - gioscarab[att]gmail.com
//Giovanni Blu Mitolo - Gioblu Robotics - materiale rilasciato sotto licenza Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
//usa pure e divertiti  :-) - gioscarab[att]gmail.com

#include  //Library used to interface servos - ibreria utilizzata per pilotare servi 
                    
Servo left; 
Servo right;

float time = 0;

#define servoLeftPin     9
#define servoRightPin   10
#define servoZero       81 //calibration 4 servos - calibrazione per servi
#define IRFront          2 
#define IRBack           3
#define ledPin          13 
#define leftIr           2 //proximity ir emitters - emettitori ir x sensori prossimità
#define rightIr          4


int frontSense = 0; //forward ir sensor - sensore ir anteriore
int backSense = 0;  //backward ir sensor - sensore ir posteriore
int orientation = 0;
int previousOrientation = 0;

float P = 0;
float I = 0;  //Il mesurement (empirism) - misurazione inerziale
float D = 0;
int fall = 0;       //value used to determine if the robot is fall
                    //variabile utilizzata per determinare se il robot è caduto
float kP = 12;      //used to tune
float kI = 430;
float kD = 20;

void setup() {
pinMode(ledPin, OUTPUT);
left.attach(servoLeftPin);
right.attach(servoRightPin);
left.write(servoZero);
right.write(servoZero);
Serial.begin(9600);
}


void getOrientation() {
  frontSense = 0;
  backSense = 0;
  orientation = 0;
 for (int i = 0; i < 10; i++) {
  frontSense = analogRead(IRFront) + frontSense;
  backSense = analogRead(IRBack) + backSense;
  if (i == 9) {
   frontSense = frontSense / 10;
   backSense = backSense / 10;
   orientation = frontSense - backSense;
  }
 }
}

/*Simplest thing ever
Stop wheels for 1/4 of a second
to get momentum to get up*/

/*La cosa piu' semplice del mondo
Stop alle ruote per 1/4 di secondo
per ottenere abbastanza momento per 
rialzarsi*/

void autoGetUp() {
if (fall == 25 || fall == - 25) {
 left.write(servoZero);
 right.write(servoZero);
 delay(250);
 fall = 0;
 I = 0;
}
}


void loop() {
autoGetUp();
getOrientation();
float previousTime = time;
time = millis();
float interval = time - previousTime;
      P = orientation / kP;
      I = I + (P * interval) / kI;
      D = (orientation - previousOrientation) / interval / kD;
      float PID = P + I + D;
if(P > 90) P = 90; //stop increase or decrease of the value
if(P < -90) P = -90; //costrizione del valore tra 90 e -90
if(orientation > 250) fall = fall + 1; 
if(orientation < -250) fall = fall - 1;
if(PID <= 1 && PID > 0) PID = 0; //cut off micro-corrections
if(PID >= -1 && PID < 0) PID = 0;
left.write(81 - PID);
right.write(81 + PID); 
previousOrientation = orientation;
Serial.print(P);
Serial.print("  ");
Serial.print(I);
Serial.print("  ");
Serial.print(D);
Serial.print("  ");
Serial.print(interval);
Serial.print("  ");
Serial.println(orientation);
}

Creative Commons License
PID self balancing by Giovanni Blu Mitolo is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
Based on a work at
www.gioblu.com.

 

Gioblu Robotics © 2010 - 2012 · Sitemap · privacy

gioscarab@gmail.com

Gioblu BOTServer è online dal 10 Aprile 2010 - 319.232 Visite - 1.027.175 Pagine visualizzate - 182.309 Visitatori unici - 536 utenti attivi