Problem bei Temperaturregelung mit PID-Bibliothek

Hallo,

ich plane eine Temperaturregelung für einen Härteofen zu bauen. Aber die Regelung der Temperatur funktioniert noch nicht so, wie ich es gerne hätte. Vielleicht kann mir jemand weiterhelfen.

Ich verwende einen Arduino Micro als Herzstück. Die Ist-Temperatur des Ofens wird über ein Typ-K Thermoelement erfasst und das Signal mittels MAX31855 an den Arduino übertragen (Bibliothek von GitHub - adafruit/Adafruit-MAX31855-library: Library for the Adafruit Thermocouple breakout with MAX31855K). Die PID-Regler Bibliothek (http://playground.arduino.cc/Code/PIDLibrary) habe ich ebenfalls eingebunden. (Code folgt später im Post). Nun soll die einprogrammierte Solltemperatur aperiodisch erreicht werden.

Zu Beginn habe ich die Sprungantwort meiner Strecke ermittelt (siehe Bild im Anhang). Ergebnis Pt-2-Strecke. Mittels Wendetangenten-Methode komme ich auf Folgende Werte für die Strecke:

Ks = 950°C / 230V = 4,13K/V
Tu = ca. 18s
Tg = ca. 1875s

Für eine Simulation mit Scilab und der integrierten Toolbox XCOS habe ich die Strecke durch Pt1-Tt approximiert.

Mathematisch:

G(s) = Ks * 1/(Tgs +1) * e^(-sTu)

Ich habe nun schon mehrere Regelparameter nach unterschiedlichen Methoden ermittelt (Ziegler&Nichols , Chien&Hrones&Reswick , empirisch aus der Simulation) und getestet aber kein Ansatz hat bisher zur Realität gepasst. Ich vermute, dass der simulierte Regler nicht mit der PID-Regler-Bibliothek übereinstimmt aber da wäre ich über jede Hilfe froh.

Mein Reglermodell ist ebenfalls im Anhang

Hier noch der Code:

#include <SPI.h>
#include <Wire.h>
#include "Adafruit_MAX31855.h"
#include <LiquidCrystal.h>

#include <PID_v1.h>

// Example creating a thermocouple instance with software SPI on any three
// digital IO pins.
#define MAXDO   14
#define MAXCS   6
#define MAXCLK  15

#define RELAY_PIN 13

// Initialize the Thermocouple
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

//******************************
// Regelparameter
//******************************
double Setpoint, Input, Output;

double Kp = 2;
double Ki = 0.0005;
double Kd = 0;

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void setup() {
  Serial.begin(9600);

  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);
  
  lcd.begin(16, 2);
  
  lcd.clear();
  lcd.print("MAX31855 test");
  // wait for MAX chip to stabilize
  delay(500);
  
  //turn the PID on
  myPID.SetMode(AUTOMATIC);  
}

void loop() {

  lcd.clear();
  lcd.setCursor(0, 0);

  //*********************************************
  // Ausgabe der Zeit[s] und der Temperatur[°C]
  // auf dem seriellen Monitor
  //*********************************************
  int zeit = (millis()/1000);
  Input = thermocouple.readCelsius();
  Serial.print(zeit);
  Serial.print(";"); 
  Serial.print(Input);
  Serial.println(";");
  //******************************************
  
  if (isnan(Input)) 
    {
      lcd.print("T/C Problem");
    } 
  else 
  {
    lcd.print("Ist: ");
    lcd.print(Input);
  }
    
  myPID.Compute();
  // Display ausgabe des Regler-Ausgangs
  lcd.setCursor(0, 1);
  lcd.print("Out: ");
  lcd.print(Output);
  lcd.print(" ms");
  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  
  if (millis() - windowStartTime > WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if (Output < millis() - windowStartTime) digitalWrite(RELAY_PIN, LOW);
  else digitalWrite(RELAY_PIN, HIGH);
  delay(1000);
}

Für den Regler wurde das Beispiel des Relais-Ausgangs herangezogen (hier ein SSR verwendet). Dabei habe ich die Logik am Ende des Codes im gegensatz zum Beispiel invertiert. Bei mir soll eine “1” am Ausgangspin das Relais und folglich den Ofen anschalten bei “0” aus.

Ich hoffe ich habe alles genau genug beschrieben. Falls Rückfragen entstehen beantworte ich diese gern.

Im Vorfeld schonmal vielen Dank und viele Grüße

Elektrometaller

Hallo,

was du benötigst ist ein sogenannter "Schrittregler", ich kenne obige PID Bibliothek leider nicht, aber ich denke mit deinem Beispiel für den "Relais-Ausgang" ist selbiger gemeint. Nach meiner Erfahrung in Regelungstechnik (diskret aufgebaute) lassen sich Regelparameter, egal nach welcher Methode, nie genau berechnen, höchstens sind sie für eine Annäherung zu gebrauchen. Das wichtigste ist ausprobieren und beobachten. Bastel dir eine Ausgabe (kurvendiagramm z.b) wo du einen Soll /Ist Vergleich hast und die Stellimpulse siehst, zur Laufzeit.
Bei so einer trägen Temperaturegelung wie du sie benötigst ist ein PI-Regler besser geignet auf einen D-Anteil würde ich komplett verzichten. Was macht den dein Regler, schwingt er?
Grüße

Danke für die schnelle Rückmeldung.

Wenn ich die Bibliothek richtig verstehe wird bei dem verwendeten Beispiel ein PID-Regelalgorithmus berechnet und der Ausgang dann in eine "langsame" PWM umgerechnet mit einer Periodendauer von 5 Sekunden.

Bei den Parametern nach Ziegler/Nichols bekomme bei einem Sollwert von 100°C als ersten Überschwinger 148°C. Danach fällt die Temperatur auf etwa 98°C ab und beginnt dann wieder zu heizen. Danach schwingt der Regler etwa zwischen 120°C und 98°C.

In der Simulation habe ich den D-Anteil auch schon zu 0 gesetzt.
Bei den Parametern die ich empirisch aus der Simulation ermittelt habe kommt der Regler nicht wirklich in die Gänge. Bleibt etwa bei 40°C hängen.

Ich hätte gerne ein System in der Simulation, dass sich genauso wie das reale System verhällt. Damit könnte ich dann die Parameter schnell simulieren und verändern. Das reale System ist für die Beobachtung und das Einstellen der Parameter zur Laufzeit deutlich zu träge.

auf deiner reale Regelstrecke wirken aber viele verschiedene "Störgrößen" wie willst du die alle simulieren oder kennen?
Was ich bei der Sache noch nicht ganz verstanden habe, wie du deinen Ofen betreibst? Also, kannst du den in der Leistung dynamisch anfahren (0-100%), oder nur Ein und Aus schalten? Das ist für die "Betriebsart" deines PID-Regelalgorithmus wesentlich.

Der Ofen geht nur an oder aus. Aber durch das träge System und diese Puls-Pause-Betriebsart sollte er ja gut Regelbar sein. Dadurch kann ich die Eingangsspannung von 230V im Prinzip auf jede Spannung einstellen

Hallo

Weil du ein SSR als Stellglied einsetzt, hast du eigentlich ideale Voraussetzungen eine Schwingungspaketsteuerung zu nutzen.
Optimal wäre ein SSR mit Nulldurchgangsschalter.

Ich habe dein Programm dafür modifiziert.

#include <SPI.h>
#include <Wire.h>
#include "Adafruit_MAX31855.h"
#include <LiquidCrystal.h>

#include <PID_v1.h>

// Example creating a thermocouple instance with software SPI on any three
// digital IO pins.
#define MAXDO   14
#define MAXCS   6
#define MAXCLK  15

#define RELAY_PIN 13

// Initialize the Thermocouple
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

//******************************
// Regelparameter
//******************************
double Setpoint, Input, Output;

double Kp = 2.0;
double Ki = 0.2;
double Kd = 0.0;

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

unsigned long WindowSize = 5000;

void setup() {
  Serial.begin(9600);

  //initialize the variables we're linked to
  Setpoint = 100;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  lcd.begin(16, 2);

  lcd.clear();
  lcd.print("MAX31855 test");
  // wait for MAX chip to stabilize
  delay(500);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
}

void loop() {

  if (millis() % 1000 == 0) {
    lcd.clear();
    lcd.setCursor(0, 0);

    //*********************************************
    // Ausgabe der Zeit[s] und der Temperatur[°C]
    // auf dem seriellen Monitor
    //*********************************************
    unsigned long zeit = (millis() / 1000);
    Input = thermocouple.readCelsius();
    Serial.print(zeit);
    Serial.print("; ");
    Serial.print(Input);
    Serial.print("; ");
    Serial.print(Output);
    Serial.println(" ms");
    //******************************************

    if (isnan(Input))
    {
      lcd.print("T/C Problem");
    }
    else
    {
      lcd.print("Ist: ");
      lcd.print(Input);
    }

    // Display ausgabe des Regler-Ausgangs
    lcd.setCursor(0, 1);
    lcd.print("Out: ");
    lcd.print(Output);
    lcd.print(" ms");
  }
  // Input = thermocouple.readCelsius();
  myPID.Compute();
  sps(Output);
}

/************************************************
 * turn the output pin on/off based on pid output
 ************************************************/
void sps(double hz)
{
  //https://de.wikipedia.org/wiki/Schwingungspaketsteuerung

  static unsigned long szl;
  static unsigned long szh;
  unsigned long lzl = WindowSize - hz;
  unsigned long lzh = hz;

  if (!digitalRead(RELAY_PIN) && millis() - szl >= lzl ) {
    szh = millis();
    digitalWrite(RELAY_PIN, HIGH);
  }
  if (digitalRead(RELAY_PIN) && millis() - szh >= lzh ) {
    szl = millis();
    digitalWrite(RELAY_PIN, LOW);
  }
}

Der Parameter “WindowSize” gibt die Gesamtdauer eines Schwingungspaketes in ms an.
Der “Output” des Reglers gibt an für welche Zeit in ms das SSR eingeschaltet ist.
Das ist dann Quasi eine PWM-Ausgabe für träge Wechselstromverbraucher wie es Heizungen nun mal sind.
Je nach Trägheit der Regelstrecke kannst du “WindowSize” größer oder kleiner machen um bessere Regelergebnisse zu erzielen.
“Ki” habe ich gefühlsmäßig mal mal etwas schneller gemacht, 0.0005 erschien mir etwas zu träge. Aber ich kenne halt die echte Regelstrecke nicht.

Die Anzeige erfolgt nach wie vor im Sekundentakt, die eigentliche Regelung läuft jetzt aber mit voller Loopgeschwindigkeit.

Nach Möglichkeit kannst du noch das einlesen des Istwertes

Input = thermocouple.readCelsius();

aus dem Sekundentakt heraus nehmen und in jeden Loopdurchlauf vor den Regler packen. (Ist im Programm eingebaut aber auskommentiert)

Mit den ganzen theoretischen Berechnungsmethoden (Ziegler&Nichols ect.) für die Reglerparameter konnte ich bisher aus meiner Erfahrung nur 50% Trefferquote erreichen. Theorie und Praxis ist halt meist nicht das Gleiche. Mit etwas Erfahrung hat man “Kp” und “Ki” relativ schnell im Griff. “Kd” kannst du auf 0.0 stehen lassen, es sei den es treten größere Sprünge (Störungen) beim Istwert auf die schnell ausgeregelt werden müssen.
Bei einem Härteofen könnte das öffnen der Tür oder beschicken mit neuem Material solche Störungen verursachen. Dann hängt es aber auch wieder davon ab wie viel thermische Energie im Ofen gespeichert ist im Verhältnis zu der thermischen Trägheit des Härtematerials.

Gruß Peter

Ich habe bei meinen Regler auch immer erst mit (diversen) theoretischen Methoden die Parameter ermittelt. (mit Wendetangente die besten Ergebnisse erzielt).

Das reicht, für eine gute erste Einstellung.
Aber es muss trotzdem immer von Hand nachjustiert werden. Ich habe den PID-Regler aus dieser Bibliothek in zwei Anwendungen laufen: einen (Holzkohle-)Grillregler der auch sehr langsam ist, und einen Luftfeuchteregler, der ist noch viel langsamer (1 Woche zum einpendeln).

Ich binde zum Zwecke der Abstimmung immer noch die sd.h mit ein und logge alle relevanten Daten (soll, ist, error, p-term, i-therm, out etc) auf SD-Karte.
da kann man dann schön analysieren, und die Parameter nachstellen.

Hallo

vielen Dank schon mal für die Mühe die Ihr reingesteckt habt.

Von einer SP-Steuerung habe ich bisher noch nichts gehört; macht aber sinn. Das SSR hat leider keine Nulldurchgangserkennung (Ist schon etwas älter und ich hab auch nur noch die Nachfolgerdatenblätter gefunden).

Die Parameter die hier noch angegeben waren stammten aus meinem XCOS-Simulationsmodell. Dort hatte ich gute Ergebnisse damit erzielt...aber wie du schon richtig geschrieben hast passt die Theorie und die Praxis nicht immer zusammen. Ich bin auch auf die Störeinflüsse beim öffnen der Tür gespannt. Das habe ich bisher noch nicht berücksichtigt da ich erst das Führungsverhalten in den Griff bekommen wollte.

Die Parameter die ich aus der Wendetangentenmethode ermittelt habe ergaben wie bereits geschrieben einen großen Überschwinger und ein Schwingen in der Strecke.

Ich werden mir bei nächstmöglicher Gelegenheit den Code und die SPS mal genauer ansehen und nachvollziehen und auch auf meinem System ausprobieren. Komme aber vorraussichtlich erst nächste Woche dazu. Nochmals vielen Dank für die detailierte Beschreibung.

Eine Frage kam mir noch bei der if-Abfrage für die Anzeige auf:

Es könnte doch passieren, dass immer kurz bevor millis()%1000 == 00 abgefragt der Rest der Loop ausgeführt wird und so dieser Programmausschnitt niemals ausgeführt wird? Dann würde ja meine Anzeige nie aktualisiert oder?

Viele Grüße

Elektrometaller

Hallo Elektrometaller,

die Modulogeschichte für den Sekundentakt ist tatsächlich etwas unglücklich gewählt.
Aber da gibt es genügend andere Möglichkeiten und das bekommst du hin.

Wichtig war mir nur dass

  myPID.Compute();
  sps(Output);

außerhalb der langsamen Anzeigeroutine läuft.

Mit einem SSR ohne Nulldurchgangserkennung wird wohl öfter nicht im Nulldurchgang eingeschaltet werden was evtl. zu Netzstörungen führen kann. Besser wäre ein SSR mit Nulldurchgangserkennung.
Bei "WindowSize" = 5000 wird maximal alle 5 Sekunden eingeschaltet. Es hängt von den Gerätschaften in der Umgebung ab ob sich da etwas gestört fühlt. (Lampenflackern z.B.)

Mit der ursprünglichen Relaisausgangs-Variante hättest du aber das gleiche Problem.

Welche el. Leistung hat der Ofen denn?

Gruß Peter

Der Ofen hat eine Leistung von ca. 1,5kW.

Elektrometaller:
Der Ofen hat eine Leistung von ca. 1,5kW.

Das ist ja weniger als ein Bügeleisen und das hat auch keinen Nulldurchgangsschalter.

ein Bügeleisen und das hat auch keinen Nulldurchgangsschalter

Auch kein SSR und keinen PID Regler

Guten Abend,

ich habe nun den Code von peter_de ausprobiert und habe die Werte (Zeit, Ist-Temperatur und Reglerausgangsgröße) aufgezeichnet (siehe Bild im Anhang). Sollwert war 100°C. Die Reglerparameter so gewählt, wie vorgeschlagen: Kp = 2; Ki = 0.2; kd = 0.

Mich wundert, dass die Stellgröße zu Beginn so klein ist, sich dann steigert und dann wieder abfällt.
Ich hätte einen sofortigen Sprung in Richtung Reglermaximum erwartet, welches mit Näherung der Temperatur gegen den Sollwert langsam abnimmt.

Ich musste leider aus Zeitmangel den Versuch abbrechen weshalb ich nicht sagen kann, wo sich der Regler eingependelt hätte. Aber ein Überschwinger von mehr als 100°C über dem Sollwert ist für meine Zwecke auch nicht tolerierbar. Jemand noch eine Idee, wie ggf. die Parameter zu ändern sind oder ich den Regler und die Strecke simulieren kann?

Viele Grüße

Elektrometaller

Guten Abend,
Kp = 2 hatte ich unverändert aus deinem Eingangspost übernommen und angenommen dass das ein Wert aus Erfahrungen mit deine Regelstrecke ist.

So wie die Kurve jetzt aussieht ist Kp viel zu groß.
Da musst du dich rantasten. Ich würde mal einen Versuch mit Kp = 0.4; und Ki = 0.2; (unverändert) machen.
Die Schwingungspaketsteuerung scheint aber zu laufen?

Temperaturregelungen sind halt meist sehr träge und man muss deshalb viel Zeit in die Optimierung stecken.

Gruß Peter

Kp = 2 hatte ich empirisch mit der Reglersimulation in XCOS ermittelt. Hatte dort damit sehr gute Ergebnisse, die aber in der Praxis absolut nicht gepasst haben.

Ich werd morgen mal einen neuen Versuch starten und berichten. Ich denke aber, dass ich mit dem angepassten Kp besser hinkomme. Vielen Dank nochmals.

Sorry, dass ich widersprechen muss, aber der Kp ist nicht das Problem. Wenn die Stellgröße Kp dominiert wäre, würde die Stellgröße zu Null gegen, wenn die Temperatur 100°C erreicht.

Was hier den riesen Überschwinger verursacht ist der I-Anteil. Ki = 0.2 ist für thermisch träge Systeme viel zu gross! Fang mal mit 0,02 an. Selbst das kann noch zu hoch sein. Bei meinem Grillregler bin ich bei Ki = 0,005!

Wenn du aber schon unbedingt empirisch arbeiten willst, dann mach es anders:
Ki, Kd auf Null.
Kp langsam erhöhen, bis du den ersten Überschwinger bekommst, wieder zurück auf letzten Wert, gerade ohne Überschwinger. Das ist das Optimum.

Und jetzt erst anfangen mit Ki zu arbeiten. Der I-Anteil des Reglers ist nichts Schnelles, sondern ausgerichtet langfristige Abweichungen zu korrigieren.

edit: kannst du mal die Daten deiner Sprungantwort posten, also das Excel oder direkt die Messdaten?

Ich habe mal versucht auch deinem Bild was rauszulesen:
Annahme: Erreichte Temperatur 1100°C; Stellgrößensprung 0% auf 100%
Ks = 1060/100 = 10,6
Tu = 75 sek
Tg = 2200 sek

Kp = 0,6 * Tg/(tu*Ks) = 0,6 * 2200 / (75 * 10,6) = 1,66
Ki = Kp / Tg = 1,66 / 2200 = 0,00075

Mit Kp = 2 warst du also ganz gut gelegen, aber der Ki war viel, viel zu hoch!

Aber: das ist ein Faustformelverfahren. Das dient dazu, zumindest mal die richtige Größenordnung rauszufinden, mit der man dann weiterarbeiten kann.

Mich wundert, dass die Stellgröße zu Beginn so klein ist, sich dann steigert

Dann ist Kp zu klein. Das langsame Ansteigen ist der I Anteil.

Wenn du weisst, bei welcher Regelabweichung du auf jeden Fall den Regler am Anschlag haben willst, kannst du ja leicht Kp ausrechnen. Ob diese Vorgabe zu einem viel zu heftigen Regelverhalten führt, ist eine andere Frage :wink:

Eine Sprungantwort im offenen Regelkreis aufzeichnen, zumindest bis der Endwert absehbar ist oder die max. zulässige Temperatur erreicht ist, soviel Zeit musst du mal auf jeden Fall haben.

Hallo,

@gutherb:

Ich will eigentlich nicht empirisch arbeiten. Ich hätte lieber ein Simulationsmodell, dass auch mit der Realität übereinstimmt. Dann ginge das Feintuning der Parameter wesentlich schneller. Aber bisher blieb nur das empirische Ermitteln.

Ich wollte nun einen neuen Praxis-Test machen und habe jetz mehrere Vorschläge von euch erhalten, wie ich die Parameter einstellen soll. Was ist denn nun aber ein “guter” Wert oder welche Methode bringt mich schnellstmöglich zum Erfolg? Ziegler/Nichols und Chron/Reswick haben bisher leider nichts gebracht.

Die Messdaten befinden sich als gezipptes *.ods im Anhang.

Viele Grüße
Elektrometaller

Sprungantwort.zip (54.2 KB)

Ich habe deine Daten mal analysiert und bin auf folgende Werte gekommen:

Tu 91
Tg 2199

die genauen Kp, Ki kanst du dir nach obiger Formel ausrechnen, aber es nicht sehr weit von der grafischen Lösung entfernt.

Die Analyse ist etwas erschwert, weil die Messwerte recht störungsbehaftet sind, aber mit etwas Mittelwertbildung geht das schon. (die Datei mit der Analyse im Anhang)

Was ich aber nicht verstehe: wenn du das alles schon gerechnet hast, wie kommst du dann auf einen Ki = 0.2?

Zum Modell: Ich kenne das aus der Arbeit von unserer “Simulantentruppe”.
Ein Modell zu bauen, dass sich real verhält, ist viel Arbeit und erfordert einige Iterationen.
also Model bauen, simulieren, mit Realität vergleichen, Model anpassen, simulieren usw.

Mach lieber ein paar Messläufe. Das geht schneller.

Sprungantwort.zip (124 KB)

Ich hab nun etliche Versuche unternommen und dabei ermittelt, dass mit Kp = 30, Ki = 0.01 und Kd = 0 die Regelung bei 100°C Solltemperatur recht zügig und stationär genau ohne merklichen Überschwinger die Temperatur erreicht (siehe Bild im Anhang).

Allerdings bekomme ich nach einer gewissen Zeit x Probleme mit meiner Display-Ausgabe. Irgendwann zeigt diese nur noch kryptische Zeichen an. Woran könnte das liegen? Abhilfe schafft entweder ein erneutes Aufspielen des Programms oder Arduino ausschalten, kurz aus der Stiftleiste nehmen, wieder einstecken und anschalten.

Ich habe 3 Steckverbinder im Signalweg zwischen Arduino und LCD-Display. Schaltplan ist im Anhang. Bei Bedarf sende ich auch gerne noch ein Bild vom Aufbau.

Viele Grüße

Elektrometaller

Schaltplan Temp.-Regelung.pdf (16.7 KB)