Regelen met PID

Waarom zou je als thuisbrouwer een aparte pagina over PID regelaars nodig hebben? Als je een RIMS / HERMS installatie hebt en je koopt een kant en klare PID regelaar, die alles voor zijn rekening neemt, waarom zou je dan nog moeilijk doen over een PID regelaar? Ook als thuisbrouwer moet je echter een idee hebben hoe een PID regelaar werkt, omdat je zelfs met een kant-en-klaar apparaat makkelijk een verkeerde instelling kunt realiseren, wat kan leiden tot een flink temperatuur doorschot of een te langzame responsie. Je zou ook kunnen bedenken dat je geen regelaar nodig hebt, je zet gewoon de brander uit wanneer de temperatuur bereikt is. Wat je zult zien met zo'n "regeling" is dat er een behoorlijk temperatuur doorschot optreedt. De brander moet geleidelijk aan uitgezet gaan worden, lang voordat de gewenste temperatuur bereikt is. En dat is waarvoor je zo'n regelaar nodig hebt. En een PID regelaar is dan de meest gebruikte regelaar.

PID staat voor Proportioneel, Integrerend en Differentiërend. Als je in het binnenste kijkt van een PID regelaar, dan zie je dat het daadwerkelijk bestaat uit drie paden. Het was heel gebruikelijk om zo'n PID regelaar op te bouwen met discrete elektronica, maar tegenwoordig is iedere PID regelaar in software gerealiseerd, we spreken dan ook van een digitale PID regelaar. Zo'n softwarematige PID regelaar zit ook in de kant en klare kastjes die je kunt kopen. En als je deze pagina in zijn geheel bestudeerd hebt, dan moet je constateren dat je erg veel geld moet betalen voor die paar regels code (Zelfbouw kan dus lonend zijn)! Op deze pagina probeer ik de volgende zaken uit te leggen:

Wil je na het lezen van dit verhaal zelf je eigen PID regelaar maken, kijk dan met name op de pagina hardware. Hier wordt het complete ontwerp en bouw van een brouwsysteem uitgelegd.

1. Basiskennis Regelen met PID

De belangrijkste componenten van een PID regellus zijn weergegeven in onderstaande figuur.

Het Proportioneel-Integrerend-Differentiërend (PID) algoritme wordt zeer veel gebruikt in de meet- en regeltechniek en in de procesautomatisering. De PID methode is makkelijk te realiseren in elektronica, dat kan zowel in hardware als in software. Het brouwprogramma kent een aparte software routine, geschreven in de programmeertaal C++, die de PID regelaar implementeert. Er zijn twee soorten basisvormen voor PID regelaars:

De implementatie, zoals deze gekozen is voor het brouwprogramma, is die van het PID snelheids algoritme. Dit algoritme wordt ook in de meeste digitale PID regelaars gebruikt.

Een andere belangrijke eigenschap van een regellus is of deze inverterend of niet-inverterend werkt:

In bovenstaande figuur waren er drie parallelle paden te zien in de PID regelaar:

De P, I en D termen werken samen als een team. Om iedere term afzonderlijk te kunnen controleren, wordt iedere term uitgerust met een instelbare parameter:

Er is nog 1 aanvullende opmerking te maken en die betreft het type PID regelaar (voor details zie wederom het document PID controller Calculus with full C source):

Bij sommige industriële PID regelaars kun je het type instellen. Maar als er een type C PID regelaar aanwezig is, dan verdient het de voorkeur om deze te kiezen. Een systeemblokdiagram van zo'n type-C regelaar zie je in onderstaande figuur. Duidelijk zijn de P, I en D-acties te zien. Dit is ook de regelaar die in het brouwsysteem gebruikt wordt.

Terug naar boven

2. Implementatie van een PID regelaar

Er zijn vele verschillende manieren om een PID regelaar te implementeren. In feite kent iedere moderne PLC een of meerdere implementatievormen van een PID regelaar, die vaak onderling weer verschillend zijn. Dit is bij het bouwen van je eigen regelaar vaak verwarrend (welke implementatie moeten we kiezen?). De afleiding van een vorm, die geschikt is voor implementatie in software, is nogal wiskundig van aard (Z-transformaties). Die wiskundige afleiding heb ik niet op deze pagina staan, maar wel in een apart document gezet. In dit document staat direct ook de volledige C source code gegeven, zodat je hiermee direct je eigen regelaar kunt bouwen. Ben je hierin geïnteresseerd, lees dan dit document: PID Controller Calculus with full C source. Aan de andere kant, als de wiskundige kant je niet echt boeit en je meer geïnteresseerd bent hoe je zelf zo'n regelaar kunt maken, dan moet je de rest van dit verhaal zeker doornemen. Ik heb hier gekozen voor een PID regelaar die volgens het PID snelheids algoritme werkt, die een Type C regelaar is, die niet-inverterend werkt en zonder filtering van de D-actie. Dit is een zogeheten Takahashi regelaar en is de beste manier om een PID regelaar daadwerkelijk te implementeren. Onderstaande figuur laat de C code hiervan zien. Deze figuur maakt hopelijk duidelijk hoe je je eigen regelaar kunt maken in je favoriete programmeertaal (de grijze regels zijn commentaar).


	  
/*==================================================================
  Function name: pidInit(), pidCtrl()
  Author       : Emile
  ------------------------------------------------------------------
  Purpose : This file contains the main body of the PID controller.
            For design details, please read the Word document
            "PID Controller Calculus".

            In the GUI, the following parameters can be changed:
            Kc: The controller gain
            Ti: Time-constant for the Integral Gain
            Td: Time-constant for the Derivative Gain
            Ts: The sample period [seconds]
  ==================================================================*/
#include "pid_reg_web.h"
#include 

void pidInit(pidPars *p, double Kc, double Ti, double Td, double Ts)
/*------------------------------------------------------------------
  Purpose  : This function initialises the PID controller.
  Variables: p : pointer to struct containing PID parameters
             Kc: the controller gain [%/C]
             Ti: integral time-constant [sec.]
             Td: differential time-constant [sec.]
             Ts: sample-time period [sec.]

                   Kc.Ts
             ki =  -----   (for I-term)
                    Ti

                       Td
             kd = Kc . --  (for D-term)
                       Ts

  Returns  : No values are returned
  ------------------------------------------------------------------*/
{
    p->kp = Kc;
    if (Ti < 1e-3)
         p->ki = 0.0;
    else p->ki = Kc * Ts / Ti;
    if (Ts < 1e-3)
         p->kd = 0.0;
    else p->kd = Kc * Td / Ts;
    p->xk_2 = p->xk_1 = p->yk = 0.0;
} // pidInit()

double pidCtrl(double xk, double tset, pidPars *p, int vrg)
/*------------------------------------------------------------------
  Purpose  : This function implements the Takahashi Type C PID
             controller: the P and D term are no longer dependent
             on the set-point, only on PV (which is Thlt).
             This function should be called once every TS seconds.
  Variables:
        xk : The input variable x[k] (= measured temperature)
      tset : The setpoint value for the temperature
        *p : Pointer to struct containing PID parameters
        vrg: Release signal: 1 = Start control, 0 = disable PID controller
  Returns  : yk : PID controller output y[k] in % [0..100]
  ------------------------------------------------------------------*/
{
   if (vrg)
   {
      //--------------------------------------------------------------------------------
      // Takahashi Type C PID controller (NO filtering of D-action):
      //
      //                                    Kc.Ts        Kc.Td
      // y[k] = y[k-1] + Kc.(x[k-1]-x[k]) + -----.e[k] + -----.(2.x[k-1]-x[k]-x[k-2])
      //                                      Ti           Ts
      //
      //--------------------------------------------------------------------------------
      p->pp  = p->kp * (p->xk_1 - xk);                 // Kc.(x[k-1]-x[k])
      p->pi  = p->ki * (tset - xk);                    // (Kc.Ts/Ti).e[k]
      p->pd  = p->kd * (2.0 * p->xk_1 - xk - p->xk_2); // (Kc.Td/Ts).(2.x[k-1]-x[k]-x[k-2])
      p->yk += p->pp + p->pi + p->pd;                  // add y[k-1] + P, I & D actions to y[k]
   }
   else { p->yk = p->pp = p->pi = p->pd = 0.0; }

   p->xk_2  = p->xk_1;  // x[k-2] = x[k-1]
   p->xk_1  = xk;       // x[k-1] = x[k]

   // limit y[k] to a maximum and a minimum
   if (p->yk > PID_OUT_HLIM)
   {
      p->yk = PID_OUT_HLIM;
   }
   else if (p->yk < PID_OUT_LLIM)
   {
      p->yk = PID_OUT_LLIM;
   } // else
   return p->yk;
} // pidCtrl()




	  

Over de implementatie van zo'n PID regelaar zijn nog een paar opmerkingen te maken:

Ik hoor graag van je of je deze listing makkelijk of moeilijk te lezen vond!

Bij deze C code hoort ook een header file (.h file) die de belangrijkste definities en de functie-prototypes bevatten. Wil je deze functies gebruiken, dan volstaat het veelal om de header file te includen in je C project.


	  
#ifndef PID_REG_H
#define PID_REG_H
/*==================================================================
  File name    : pid_reg_web.h
  Author       : Emile
  ------------------------------------------------------------------
  Purpose : This is the header file for pid_reg_web.c
  ==================================================================*/

#define PID_OUT_HLIM (100.0) /* PID Controller Upper limit (%) */
#define PID_OUT_LLIM   (0.0) /* PID Controller Lower limit (%) */

//------------------------------------------------------------
// By using a struct for every PID controller, you can have
// more than 1 PID controller in your program.
//------------------------------------------------------------
typedef struct _pidPars
{
   double kp;   // Internal Kp value
   double ki;   // Internal Ki value
   double kd;   // Internal Kd value
   double pp;   // debug value for monitoring of P-action
   double pi;   // debug value for monitoring of I-action
   double pd;   // debug value for monitoring of D-action
   double yk;   // y[k]  , value of controller output (%)
   double xk_1; // x[k-1], previous value of the input variable x[k] (= measured temperature)
   double xk_2; // x[k-2], previous value of x[k-1] (= measured temperature)
} pidPars; // struct pidParams

//----------------------
// Function Prototypes
//----------------------
void   pidInit(pidPars *p, double Kc, double Ti, double Td, double Ts);
double pidCtrl(double xk, double tset, pidPars *p, int vrg);
#endif
	  

Als voorbeeld hoe deze routines aan te roepen zijn vanuit een standaard C-programma, wordt hier onderstaande code weergegeven. De routine pidInit() wordt als eerste aangeroepen met de juiste PID parameters en daarna wordt de regelaar zelf (de routine pidCtrl()) aangeroepen. De routine pidCtrl() heeft de actuele temperatuur nodig, de setpoint waarde, de struct met parameters en een vrijgave signaal. De regelaar zal pas echt gaan regelen, wanneer dit vrijgave signaal een 1 is geworden.


	     
#include 
#include 
#include "pid_reg_web.h"

int main()
{
    pidPars p;          // Create a struct for pidPars
    double out  = 0.0;  // PID controller output
    double temp = 30.0; // Actual temperature
    double sp   = 32.0; // Setpoint temperature

    // Call pidInit with the proper control parameters
    pidInit(&p, 80.0, 280.0, 45.0, 20.0); // p, Kc, Ti, Td, Ts

    // Call the actual controller every Ts seconds
    out = pidCtrl(temp, sp, &p, 0); // x[k], SP, p, vrg
    out = pidCtrl(temp, sp, &p, 0); // call this function every Ts seconds
    // The PID-controller is enabled by setting vrg to 1
    out = pidCtrl(temp, sp, &p, 1); // Controller is released
    out = pidCtrl(temp, sp, &p, 1); // 
    return 0;
} // main()
	     
Terug naar boven

3. Instellen van een PID regelaar

Het instellen van de PID regelaar omvat het vinden van zodanige waarden voor Kc, Ti en Td, dat de PID regelaar snel reageert op veranderingen in de referentie temperatuur en/of de gemeten temperatuur, waarbij de temperatuur doorschot wordt geminimaliseerd. Het instellen of tunen van een PID regelaar is altijd nodig, of je nu een kant en klare regelaar gekocht hebt, of dat je er zelf eentje gemaakt hebt. Het voordeel van zo'n kant en klare regelaar is dat ze vaak een auto-setup functie bevatten, die de optimale parameterwaarden automatisch voor je vindt (meestal dan...). Maar zelfs zo'n kant en klare regelaar gebruikt dezelfde algoritmen als die ik hier beschrijf. Deze informatie kan dus ook zinvol zijn als je regelaar niet zelf gemaakt hebt, maar gekocht hebt. Om de specifieke metingen te begrijpen, die we gaan doen, is het van belang om enkele grootheden uit te leggen / te definiëren:

3.1 Bepalen van de dode tijd en de relatieve helling van het brouwsysteem

Fixeer de output van de PID regelaar op een bepaalde waarde (bijv. 20 %). Bij een grote ketel met veel water erin, is 100 % beter (nauwkeuriger), maar als je de capaciteit van het systeem niet kent, is het beter om met een lagere waarde te beginnen. Nadat de output van de regelaar op een vaste waarde is gezet, zal de temperatuur gaan stijgen en ongeveer de volgende curve volgen:

Na het uitvoeren van dit experiment met mijn warmwaterketel (HLT, 90 L water, geen deksel op de pan, PID regelaar output op 100 %, alleen elektrisch verwarmingselement), heb ik berekend dat de dode tijd voor mijn HLT systeem gelijk is aan 115 seconden (zie het document PID Controller Calculus voor details). Tijdens dit experiment, was de temperatuurverandering ongeveer 0.4 °C per minuut (PID regelaar output op 100 %). De relatieve helling is dan gelijk aan 0.004 °C / (%.minuten) of 6.68E-5 °C/(%.s). MAAR... omdat ik recentelijk overgeschakeld ben op nieuwe gasbranders met veel meer vermogen, zal ik dit experiment nog eens moeten herhalen.

3.2 Bepalen van de tijdconstante van het brouwsysteem

Vanwege de grote tijdconstante die meestal in zo'n HLT systeem zit, is het voorgaande experiment niet erg geschikt om de ook de tijdconstante van het systeem te bepalen (niet nauwkeurig genoeg). Dit is dan ook de belangrijkste reden om twee experimenten uit te voeren (theoretisch gesproken geeft het meten van de stapresponsie uit het vorige experiment alle benodigde informatie om de parameters te kunnen bepalen). In dit experiment moet de output van de PID regelaar met de hand op een bepaalde waarde gezet worden (bijv. 20 %). De temperatuur zal nu wederom gaan stijgen en wel volgens deze curve:

Het verschil met het vorige experiment is dat we nu het HLT systeem naar zijn eindwaarde zullen laten convergeren (in mijn experiment duurde dit het grootste deel van de dag, omdat ik een zo nauwkeurig mogelijke meting wilde hebben). Na het uitvoeren van dit experiment (90 L water, geen deksel op de pan, PID regelaar output op 20 % gezet, alleen elektrisch verwarmingselement), heb ik de volgende data verzameld:

3.3 Berekenen van de optimale parameters voor de PID regelaar (Kc, Ti en Td)

Om te beginnen, een samenvatting van de gevonden waarden uit de twee experimenten voor mijn HLT systeem:

Er zijn een aantal bekende algoritmen, die de optimale waarden berekenen voor een PI en een PID regelaar. Deze algoritmen berekenen een waarde voor Kc, Ti en Td, die gebaseerd is op de gevonden waarden voor TD, a, K en tau. Deze algoritmen zijn:

De specifieke formules voor ieder algoritme zijn als volgt:

Wanneer deze formules toegepast worden op de gevonden waarden voor mijn HLT systeem, dan worden de volgende resultaten verkregen:

  Numerieke waarden
Algoritme Kc [%/(°C)] Ti [sec.] Td [sec.]
Ziegler-Nichols Open Loop 156.2 230.0 57.5
117.2 383.0  
Ziegler-Nichols Closed Loop 92.4 230.0 57.5
69.3 383.0  
Cohen-Coon 102.8 282.3 41.8
69.4 377.2  
ITAE-Load 80.8 489.0 44.9
59.2 810.2  

Al deze oplossingen zijn uitgeprobeerd voor mijn HLT systeem en eigenlijk gaven ze allemaal goede resultaten. Wat wel geconstateerd werd, was dat de D-term nogal gevoelig ingesteld stond, speciaal met de ITAE-Load waarden. Daarom is de D-term nog wat verkleind en heb ik meer filtering toegepast (de constante voor het laagdoorlaat filter heeft nu de waarde 10). Bij Hoe werkt het? kun je een tweetal grafieken zien, die de performance laten zien van de PID regelaar (vooral de onderste grafiek, die is met bovenstaande waarden gemaakt). Na al dit werk, begin ik redelijk te begrijpen hoe de PID regelaar werkt in combinatie met mijn HLT systeem. De werking van de PID regelaar is zodanig goed, dat ik erg blij ben met het resultaat hiervan. Het is grappig om te zien dat het bedenken van dit alles mij maanden gekost heeft, maar dat de uiteindelijke implementatie met slechts een paar regels code te realiseren valt.

Voor de volledigheid nog een aantal bronvermeldingen (een deel van bovenstaande informatie is hiervan afkomstig):

Terug naar boven