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.

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: init_pid4(), pid_reg4()
  Author       : E. vd Logt
  File name    : $Id: pid_reg.c,v 1.11 2013/07/23 09:42:46 Emile Exp $
  ------------------------------------------------------------------
  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]
  ------------------------------------------------------------------
  $Log: pid_reg.c,v $
  ==================================================================
*/
#include "pid_reg_web.h"
#include 

static double xk_1;  // PV[k-1] = Thlt[k-1]
static double xk_2;  // PV[k-2] = Thlt[k-1]

void init_pid4(pid_params *p)
/*------------------------------------------------------------------
  Purpose  : This function initialises the Type A PID controller.
             It also utilizes filtering of the D-action.
  Variables: p: pointer to struct containing all PID parameters

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

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

  Returns  : No values are returned
  ------------------------------------------------------------------*/
{
   p->ts_ticks = (int)((p->ts * 1000.0) / T_50MSEC);
   if (p->ts_ticks > SIXTY_SECONDS)
   {
      p->ts_ticks = SIXTY_SECONDS;
   } // if
   if (p->ti == 0.0)
   {
      p->k0 = 0.0;
   }
   else
   {
      p->k0 = p->kc * p->ts / p->ti;
   } // else
   p->k1   = p->kc * p->td / p->ts;
} // init_pid4()

void pid_reg4(double xk, double *yk, double tset, pid_params *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).
             The D term is NOT low-pass filtered.
             This function should be called once every TS seconds.
  Variables:
        xk : The input variable x[k] (= measured temperature)
       *yk : The output variable y[k] (= gamma value for power electronics)
      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  : No values are returned
  ------------------------------------------------------------------*/
{
   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->kc * (xk_1 - xk);              // Kc.(x[k-1]-x[k])
      p->pi = p->k0 * (tset - xk);              // (Kc.Ts/Ti).e[k]
      p->pd = p->k1 * (2.0 * xk_1 - xk - xk_2); // (Kc.Td/Ts).(2.x[k-1]-x[k]-x[k-2])
      *yk  += p->pp + p->pi + p->pd;            // add y[k-1] + P, I & D actions to y[k]
   }
   else { *yk = p->pp = p->pi = p->pd = 0.0; }

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

   // limit y[k] to GMA_HLIM and GMA_LLIM
   if (*yk > GMA_HLIM)
   {
      *yk = GMA_HLIM;
   }
   else if (*yk < GMA_LLIM)
   {
      *yk = GMA_LLIM;
   } // else
} // pid_reg4()




	  

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.


	  /*==================================================================
  File name    : $Id: pid_reg.h,v 1.13 2013/07/24 14:00:00 Emile Exp $
  Author       : E. vd Logt
  ------------------------------------------------------------------
  Purpose : This file contains the defines for the PID controller.
  ------------------------------------------------------------------
  $Log: pid_reg.h,v $
  ==================================================================
*/
#ifndef PID_REG_H
#define PID_REG_H

#ifdef __cplusplus
extern "C" {
#endif

// These defines are needed for loop timing and PID controller timing
#define SIXTY_SECONDS (1200)
#define TEN_SECONDS    (200)
#define FIVE_SECONDS   (100)
#define ONE_SECOND      (20)
// Period time of TTimer in msec.
#define T_50MSEC        (50)
#define PI              (3.141592654)
#define NODF            (1)

// PID controller upper & lower limit [%]
#define GMA_HLIM (100.0)
#define GMA_LLIM   (0.0)

typedef struct _pid_params
{
   double kc; // Controller gain from Dialog Box
   double ti; // Time-constant for I action from Dialog Box
   double td; // Time-constant for D action from Dialog Box
   double ts; // Sample time [sec.] from Dialog Box
   double k_lpf; // Time constant [sec.] for LPF filter
   double k0; // k0 value for PID controller
   double k1; // k1 value for PID controller
   double k2; // k2 value for PID controller
   double k3; // k3 value for PID controller
   int    ts_ticks;  // ticks for timer
   int    pid_model; // PID Controller type [0..3]
   double pp; // debug
   double pi; // debug
   double pd; // debug
} pid_params; // struct pid_params

//--------------------
// Function Prototypes
//--------------------
void init_pid4(pid_params *p);        // Type C Takahashi PID, no filtering of D-action
void pid_reg4(double xk, double *yk, double tset, pid_params *p, int vrg);

#ifdef __cplusplus
};
#endif
#endif
	  
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