Kilka słów o samym regulatorze
Regulator PID jest urządzeniem składającym się z trzech członów:
- P – proporcjonalnego, mnożącego wartość uchybu od wartości zadanej przez jakiś mnożnik zazwyczaj nazwany „kp”
- I – całkującego, w dużym uproszczeniu obliczającego pole pod wykresem sygnału wejściowego (jest to nie do końca poprawne wyjaśnienie ponieważ człon całkujący potrafi osiągać wartości ujemne, jednak pozwala zrozumieć zasadę działania)
- D – różniczkującego, z definicji obliczającego różnicę między dwoma położonymi nieskończenie blisko punktami na wykresie. Można tłumaczyć sobie to jako swego rodzaju ‚współczynnik zmian’
Regulatory PID mają za zadanie ustawić, a następnie utrzymać wartość wyjściową (np. temperaturę) na zadanym poziomie w jak najszybszy i stabilny sposób.
Przykładowy kod regulatora temperatury
Program wykorzystuje Arduino wyświetlacz 16×4, sterownik termopary typu K MAX31855
#include <LiquidCrystal.h> #include <SPI.h> #include "Adafruit_MAX31855.h" #define MAXDO 12 #define MAXCS 10 #define MAXCLK 13 #define rs 7 #define en 6 #define d4 5 #define d5 4 #define d6 3 #define d7 2 #define PWM_pin 11 LiquidCrystal lcd(rs, en, d4, d5, d6, d7); Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO); unsigned long aktualnyCzas = 0; unsigned long zapamietanyCzas = 0; unsigned long roznicaCzasu = 0; float temperature_read = 0.0; float set_temperature = 70; float PID_error = 0; float previous_error = 0; float elapsedTime, TimePID, timePrev; int PID_value = 0; int output = 0; float kp = 15; float ki = 0.15; float kd = 50; int PID_p = 0; int PID_i = 0; int PID_d = 0; void setup() { lcd.begin(16, 4); Serial.begin(9600); pinMode(PWM_pin,OUTPUT); TCCR2B = TCCR2B & B11111000 | 0x03; // pin 3 and 11 PWM frequency of 980.39 Hz TimePID = millis(); } void loop(){ temperature_read = thermocouple.readCelsius(); analogWrite(PWM_pin,output); aktualnyCzas = millis(); roznicaCzasu = aktualnyCzas - zapamietanyCzas; if (roznicaCzasu >= 1000UL) { zapamietanyCzas = aktualnyCzas; //Serial.println(aktualnyCzas); output = PID(temperature_read); lcd_display(); } } double PID(float inp){ PID_error = set_temperature - inp; PID_p = kp * PID_error; //Calculate the I value in a range on +-3 if(-3 < PID_error && PID_error < 3) { PID_i += PID_error * elapsedTime; } //For derivative we need real time to calculate speed change rate timePrev = TimePID; // the previous time is stored before the actual time read TimePID = millis(); // actual time read elapsedTime = (TimePID - timePrev) / 1000; //Now we can calculate the D calue PID_d = kd*((PID_error - previous_error)/elapsedTime); //Final total PID value is the sum of P + I + D PID_value = PID_p + PID_i + PID_d; if(PID_value < 0) { PID_value = 0; } if(PID_value > 255) { PID_value = 255; } previous_error = PID_error; //Remember to store the previous error for next loop. return PID_value; } void lcd_display(){ char buffer[16]; lcd.clear(); // lcd.setCursor(0,0); // lcd.print("Zadana: "); // lcd.setCursor(8,0); // lcd.print(set_temperature); // lcd.setCursor(14,0); // lcd.print("C"); // lcd.setCursor(0,1); // lcd.print("Odczyt: "); // lcd.setCursor(8,1); // lcd.print(temperature_read); // Wyświetlanie pierwszych trzech linijek lcd lcd.setCursor(14,1); // lcd.print("C"); // lcd.setCursor(0,2); // lcd.print("PWM: "); // lcd.setCursor(5,2); // lcd.print(PID_value); // lcd.setCursor(11,2); // lcd.print((100*PID_value)/255); // lcd.setCursor(14,2); // lcd.print("%"); // sprintf(buffer, "P:%d", PID_p); // lcd.setCursor(0,3); // lcd.print(buffer); // sprintf(buffer, "I:%d",PID_i); // lcd.setCursor(5,3); // Wyświetlanie ostatniej linijki lcd lcd.print(buffer); // w ten sposób nie wyświetlają się sprintf(buffer, "D:%d", PID_d); // "krzaki" na wyswietlaczu lcd.setCursor(10,3); // lcd.print(buffer); // }
Dlaczego człon I działa dopiero w zakresie błędu +/- 3?
Człon I ze względu na zasadę jego działania bardzo szybko osiąga bardzo wysokie wartości. Początkowa temperatura jest niska, więc pole pod wykresem z czasem bardzo szybko rośnie. Potrafi to doprowadzić do dużego przesterowania regulatora, a co za tym idzie – wzrostu temperatury wysoko ponad temperaturę zadaną. Najprościej można zapobiec temu na dwa sposoby:
- Wprowadzając limit wartości jaką może osiągnąć człon całkujący
- Ograniczając działanie członu całkującego do pewnego zakresu w pobliżu wartości zadanej
Prezentacja działania regulatora
Kilka słów o MAX31855 i jego chińskiej podróbce
Korzystając z najtańszego sterownika MAX31855 z Ebaya czy Aliexpress możemy napotkać spore problemy z uruchomieniem modułu. W moim przypadku obsłużyć go potrafiła jedynie biblioteka od Adafruit (https://github.com/adafruit/Adafruit_MAX31865). Niestety po uruchomieniu, Arduino odczytywało losowe wartości temperatury. Rozwiązaniem okazało się dolutowanie kondensatora 47nf pomiędzy zaciski termopary w module. Po tym zabiegu wszystko działa idealnie.
![](http://elektronika00.5v.pl/wp-content/uploads/2019/04/IMG_20190507_105854-1024x768.jpg)