Kod för PID-reglering i C
Kod för PID-reglering i C
Jag har nu försökt länge med att skriva en egen kod för pid i c till en 16f887 men ej lyckats få den att fungera. Är det någon som har en sådan skriven eller en länk till en som man kan tjuvkika lite på?
Senast redigerad av blueint 6 september 2010, 19:32:20, redigerad totalt 1 gång.
Anledning: Rubrik "kod för pid i c"
Anledning: Rubrik "kod för pid i c"
Re: kod för pid i c
Jag antar att du menar en PID-reglering, detta finns det tidigare tråd om med kod och allt.
Re: kod för pid i c
Jag får ingen ordning på utgångsvärdet. Den bygger bara upp utgångsvärdet. Går alldrig nedåt.
P,I och D värdet justerar jag med vridputtar 0-255. Värdena har jag synliga i en lcd där jag även ser bör o är värdet.
P,I och D värdet justerar jag med vridputtar 0-255. Värdena har jag synliga i en lcd där jag även ser bör o är värdet.
Kod: Markera allt
/* breäkning PID */
fel = bor - ar; /* beräkning av felet i varvtals ökning */
pid_reg = P * fel; /* beräkning av p i pidregler */
sum_fel = sum_fel + fel ; /* beräkning av totala felet */
pid_reg += sum_fel * I ; /* beräkning av i i pidregler */
skil_fel = senast_fel - fel ; /* beräkning av skillnaden mellan fel värdena */
pid_reg += skil_fel * D ; /* beräkning av d i pidregler */
senast_fel = fel ; /* lägger värdet fel i senaste fel */
pid_reg = pid_reg / 255; /* gör om så talet kan användas för pwm */
Re: kod för pid i c
Komplett värdelöst kodexempel utan definitioner av variabler.
Vad vill du att vi ska göra med det ?
(signed/unsigned ?)
Vad vill du att vi ska göra med det ?
(signed/unsigned ?)
Re: Kod för PID-reglering i C
Kod: Markera allt
unsigned long P; /* char P i pidregleringen */
unsigned long I; /* char I i pidregleringen */
unsigned long D; /* char D i pidregleringen */
bank1 long ar; /* värdet varvtal är */
bank1 long bor; /* värdet varvtal bör */
bank1 long fel; /* värdet skillnaden varvtal är - bör int */
bank1 long sum_fel; /* värdet summa fel */
bank1 unsigned long pid_reg ; /* värdet för regleringen long */
bank1 long senast_fel; /* värdet senaste mätta felet */
bank1 long skil_fel ; /* värdet mellan felmätningarna */
Re: Kod för PID-reglering i C
Och då kommer standardfrågan: vad har du testat?
* Har du kollat att felet blir negativt? (alltså vid fel = bor - ar;)
* Har du kollat med fasta värden (t.ex. fel = -1) vad som händer?
Rent omedelbart skulle jag tro att det beror på att P, I & D-faktorerna är unsigned, det konstiga språket du använder kan mycket väl komma fram till att unsigned * signed = unsigned och då är du rökt.
* Har du kollat att felet blir negativt? (alltså vid fel = bor - ar;)
* Har du kollat med fasta värden (t.ex. fel = -1) vad som händer?
Rent omedelbart skulle jag tro att det beror på att P, I & D-faktorerna är unsigned, det konstiga språket du använder kan mycket väl komma fram till att unsigned * signed = unsigned och då är du rökt.
Re: Kod för PID-reglering i C
Är du säker på att det är din kod som inte fungerar? Det är inte trivialt att trimma in en PID-regulator. Vad har du för system? Måste du ha en PID, P räcker för väldigt många reglersystem.
Jag skulle börja med att trimma in P och se till att I och D är 0.
Öka P tills att systemet börjar självsvänga. Halvera P och se om du är nöjd med hur den reglerar. Om du får problem med att regulatorn inte når temperaturen/positionen/värdet du var ute efter, typ du vill reglera till 270 grader, men kommer bara till 240, då behöver du ta med integrerande del.
Öka P igen tills systemet självsvänger, notera nu både vad P är och vilken svängningstid du har. Minska P till 0.45 av värdet du hade nyss, och sätt I till 1.2*P(nuvarande)/svänningstiden. Det kan behövas trimmas lite upp och ner på parametrarna härifrån för att få systemet stabilt. Får du för mycket overshoot nu så kan du implementera D-delen i regulatorn.
Kom ihåg samma P och svängningstid som du hade när systemet precis började självsvänga innan. Välj nu nya P till 0.65 av det toppvärdet. Sätt I till 2*P(nuvarande)/svängningstiden och väl D till P(nuvarande)*svängningstiden/8. Känns systemet ryckigt, öka D lite eller minska P.
Kom ihåg att systemet inte kommer att bli perfekt, men gör du små trimmningar utifrån de värden jag gett dig så kommer det att bli ganska så bra.
Jag skulle börja med att trimma in P och se till att I och D är 0.
Öka P tills att systemet börjar självsvänga. Halvera P och se om du är nöjd med hur den reglerar. Om du får problem med att regulatorn inte når temperaturen/positionen/värdet du var ute efter, typ du vill reglera till 270 grader, men kommer bara till 240, då behöver du ta med integrerande del.
Öka P igen tills systemet självsvänger, notera nu både vad P är och vilken svängningstid du har. Minska P till 0.45 av värdet du hade nyss, och sätt I till 1.2*P(nuvarande)/svänningstiden. Det kan behövas trimmas lite upp och ner på parametrarna härifrån för att få systemet stabilt. Får du för mycket overshoot nu så kan du implementera D-delen i regulatorn.
Kom ihåg samma P och svängningstid som du hade när systemet precis började självsvänga innan. Välj nu nya P till 0.65 av det toppvärdet. Sätt I till 2*P(nuvarande)/svängningstiden och väl D till P(nuvarande)*svängningstiden/8. Känns systemet ryckigt, öka D lite eller minska P.
Kom ihåg att systemet inte kommer att bli perfekt, men gör du små trimmningar utifrån de värden jag gett dig så kommer det att bli ganska så bra.
Re: Kod för PID-reglering i C
Utvärdet läggs i pid_reg som är unsigned, dvs minusvärde blir svårt att få till.
Re: Kod för PID-reglering i C
Ja fast det är väl utparametern till värmaren eller motorn eller vad det nu är som regleras. Har man tex en PWM så kan 512 vara stilla, 0 fullt åt ena hållet och 1024 fullt åt andra.
Re: Kod för PID-reglering i C
Sant, man bör också ha lite begränsningar inlagda så att inga parametrar (och särskilt det integrerande värdet) växer sig alltför stort vilket är lätt hänt om bör och är värde skiljer sig mycket vid startup och det tar lång tid att korrigera. Är inte så lyckat om utsignalen bygger upp till 11 miljoner om max gas är 1024, det tar ju sin tid att komma ner från 11 miljoner igen.
Re: Kod för PID-reglering i C
Men du måste ha gränser så att du ser till att max och min-värdena inte överskrids. Om P skulle bli -1 så skulle den siffran omvandlas till ett enormt stort positivt tal. Eller om P bilr 2550. Vad händer då?
(Gimbal hann före)
Sen undrar jag hur du får in värden till dina P,I och D. Om du använder potar - kan du ställa in ett negativt värde?
till sist delar du pid med 255... det låter ojämnt. Är det säkert att det inte ska delas med 256?
Det verkar finnas en hel del matematiska problem inbakat i det här. Hade jag varit du så hade jag nog gjort samma program i en PC med flyttal och ritat upp de olika delresultaten i en graf (eller skrivit ut en lista). Då ser jag direkt om något värde blir 11 miljoner ...
(Gimbal hann före)
Sen undrar jag hur du får in värden till dina P,I och D. Om du använder potar - kan du ställa in ett negativt värde?
till sist delar du pid med 255... det låter ojämnt. Är det säkert att det inte ska delas med 256?
Det verkar finnas en hel del matematiska problem inbakat i det här. Hade jag varit du så hade jag nog gjort samma program i en PC med flyttal och ritat upp de olika delresultaten i en graf (eller skrivit ut en lista). Då ser jag direkt om något värde blir 11 miljoner ...

Re: Kod för PID-reglering i C
Kod: Markera allt
#define PID_MAX_I -1
static int PID_Do_Once; /* Local to this routine */
void PID_Initiate(void)
{
PID_Do_Once = 1; /* Make it disable D-part on first regulation */
}
unsigned short PID_Regulate(short Current, short Target, unsigned short P_Factor, unsigned short I_Factor, unsigned short D_factor, unsigned short Factor_Divisor)
{
static unsigned short Previous, I_Value; /* Holds previous "Current"-value, used by D-part */
long Work_S, Work_P, Work_I, Work_D;
short Diff; /* Could be positive or negative */
Diff = Target - Current; /* Positive = the need for speed */
/* Now calculate the P-part */
Work_P = Diff * P_Factor;
Work_P /= Factor_Divisor;
if(Work_P > (unsigned)PID_MAX_I) Work_P = (unsigned)PID_MAX_I; /* Top limit */
if(Work_P < 0) Work_P = 0; /* Bottom limit */
/* Now calculate the I-part */
Work_I = Diff * I_Factor;
Work_I /= Factor_Divisor;
if(Work_I > (unsigned)PID_MAX_I) Work_I = (unsigned)PID_MAX_I; /* Top limit */
if(Work_I < 0) Work_I = 0; /* Bottom limit */
I_Value += Work_I;
/* And lastly the D-part */
Diff = Current - Previous; /* Negative = hold back a little */
Work_D = Diff * D_Factor;
Work_D /= Factor_Divisor;
Previous = Current; /* remember to next time */
/* Now put it all together */
if(PID_Do_Once)
{
PID_Do_Once = 0; /* OK, register that it's done */
Work_D = 0; /* Set to known state as no Prevoius exists */
I_Value = 0; /* Set to known state as it's the starting point */
}
Work_S = Work_P; /* Start putting it together */
Work_S += I_Value;
Work_S += Work_D;
if(Work_S > (unsigned short)PID_MAX_I) Work_S = (unsigned short)PID_MAX_I;
if(Work_S < 0) Work_S = 0;
return((unsigned short)Work_S);
}