Sida 1 av 1

Kod för PID-reglering i C

Postat: 5 september 2010, 09:39:12
av Stene
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å?

Re: kod för pid i c

Postat: 5 september 2010, 09:54:55
av Icecap
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

Postat: 5 september 2010, 10:53:43
av bos
Hur ser din kod ut, och vad är det som inte fungerar?

Re: kod för pid i c

Postat: 6 september 2010, 19:24:15
av Stene
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.

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

Postat: 6 september 2010, 19:30:55
av sodjan
Komplett värdelöst kodexempel utan definitioner av variabler.
Vad vill du att vi ska göra med det ?

(signed/unsigned ?)

Re: Kod för PID-reglering i C

Postat: 6 september 2010, 19:39:10
av Stene

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

Postat: 6 september 2010, 19:46:32
av Icecap
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.

Re: Kod för PID-reglering i C

Postat: 7 september 2010, 08:16:21
av Agwan
Ä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.

Re: Kod för PID-reglering i C

Postat: 7 september 2010, 09:13:10
av Gimbal
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

Postat: 7 september 2010, 09:46:17
av Agwan
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

Postat: 7 september 2010, 09:59:42
av Gimbal
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

Postat: 7 september 2010, 10:08:22
av jesse
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 ... :roll:

Re: Kod för PID-reglering i C

Postat: 7 september 2010, 10:27:00
av Icecap

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);
  }
Detta är en jag skrev ihop för ett tag sedan, jag garanterar inget men den är en "finskrivning" av en väl fungerande rutin i ett projekt. Den ger enbart positivt värden ut då den är designat att köra mellan 0 och full patte, jag använder den att reglera effekt med och värmeenheten har svårt att alstra negativ värme varför negativa värden ut är ointressant.