PID Regler Erfahrungen ?

Mahlzeit zusammen,

gibt es von Eurer Seite Verbesserungsvorschläge fuer meinen Regelkreis?
Versuchsaufbau:
Heizung extern 5V – 10Ohm 10W Widerstand – Emitter S8050(NPN) – Collector – 0V
Arduino PWM – 220Ohm – Basis S8050
Arduino 5V – 10kOhm – (A0) – 10kNTC – 0V

Welche Verstärkungsfaktoren verwendet Ihr für z.B. eine träge Heizung?
Wer Fehler entdeckt bitte melden.

Besten Dank
Grillgemuese :slight_smile:

/*PID Regler 18.03.2018   (Grillgemuese)
  Versuch mit PID Regler 0- 255   (8Bit)
  Sensor NTC 10k 25°C    0-1023  (10Bit)
  Heizung = ext.5V---(10Ohm)->-(S8050)---GND
                 PWM---(220Ohm)---|       
*/
#define serial_debug //auskommentieren wenn nicht in Gebrauch
#ifdef serial_debug
  #define Baudrate 19200
  //#define plotter_mode //Plotter kompatibel (ohne t)
#endif
#define lcd_i2c_mode //auskommentieren wenn nicht in Gebrauch
#ifdef lcd_i2c_mode
  #include <Wire.h>
  #include <LiquidCrystal_I2C.h> //Ver.1.1.2
  LiquidCrystal_I2C lcd(0x27, 16, 2);
#else
  #include <LiquidCrystal.h>
  /**** 4 Bit Mode  RS EN D4 D5 D6 D7 ****/
  LiquidCrystal lcd(10, 9, 8, 7, 6, 5);
#endif
/******** Ein-/Ausgaenge ********/
const uint8_t Sensor = A0;        //0-5V | 0-1023 (~5mV)
const uint8_t Heizung = 3;        //PWM Ausgang
const uint8_t Status_LED = 13;    //soll erreicht (+/-T_tol)
/******** globale_Konstanten ********/
const bool Regler_limit = false;   //Regler (-100|300)
const uint32_t lcd_zyklus = 3000; //lcd Seitenwechsel nach 3s
const uint32_t PID_zyklus = 500;  //(t) je XXms neue Berechnung
const float T_tol = 0.25;         //Toleranzwert
const float T_soll = 50.0;       //(w)
const float T_0 = 273.15;         //abs. Nullpunkt
const float B_NTC = 3977.0;       //Beta Materialkonstante
const float T_NTC = 25.0 + T_0;   //NTC Nenntemperatur
const float R_NTC = 10000.0;      //NTC Nennwiderstand
const float R_v = 9935.0;         //10k R (Spannungsteiler)
/******** PID Einstellungen ********/
const float Ta = 1.0;       //Ausregelzeit  |
const float Tn = 1.0;       //Nachstellzeit |>von Medium abhaengig
const float Tv = 1.0;       //Vorhaltezeit  |
const float Kp =  50.0;     //(50.0)Verstaerkungsfaktor P
const float Ki =  0.50;     //(0.50)Verstaerkungsfaktor I
const float Kd =  1.00;     //(1.00)Verstaerkungsfaktor D
/******** globale_Variablen ********/
/******** Prototypen ********/
/*float skal(const uint16_t r, const float min_r, const float max_r,
           const float min_f, const float max_f); */
float NTC_Temperatur(const float U_in);
void lcd_Ausgabe(const uint32_t *ms, const uint32_t *tx, const float *P, const float *I, const float *D,
                 const float *PID, const uint8_t *PWM, const float *T_ist, const float *T_min, const float *T_max);
#ifdef serial_debug
  void debug_Ausgabe(const uint32_t *ms, const float *ist, const float *P, const float *I,
                     const float *D, const float *PID, const uint8_t *PWM);
#endif
/****************************/
void setup()
{
#ifdef serial_debug
  Serial.begin(Baudrate);
  while (!Serial) {}
#endif
#ifdef lcd_i2c_mode
  lcd.init();
  lcd.backlight();
#else
  lcd.begin(16, 2);
#endif
  lcd.clear();
  lcd.print(F("PID-Regler  V0.1"));
  lcd.setCursor(0, 1);
  lcd.print(F("NTC 10k 25 B3977"));
  pinMode(Sensor, INPUT);
  pinMode(Status_LED, LED_BUILTIN);
  pinMode(Heizung, OUTPUT);
  while (millis() < lcd_zyklus + 1999);
}//void setup() ENDE

void loop()
{
  static float e[2];  //e[0] = aktuell | e[1] = alt
  static float esum;  //Differenzsumme fuer I-Anteil
  /******** Messungs_Takt ********/
  uint32_t akt_ms = millis();
  static uint32_t kal_ms;
  if (akt_ms - kal_ms >= PID_zyklus)
  {
    float d_t = ((float)akt_ms - (float)kal_ms) / 1000.0; //delta(t)
    kal_ms = akt_ms;
    float T_ist = NTC_Temperatur((float)analogRead(Sensor));
    /******** Differenz_Wert ********/
    e[1] = e[0];
    e[0] = T_soll - T_ist;
    /******** Status_LED ********/
    if (e[0] <= T_tol && e[0] >= 0.0 - T_tol) digitalWrite(Status_LED, HIGH);
    else digitalWrite(Status_LED, LOW);
    static uint32_t soll_ms = 0;
    static bool soll_erreicht = false;
    /******** Regler Traegheit ********/
    if (T_ist >= T_soll-0.1 && !soll_erreicht) {
      soll_ms = millis();
      soll_erreicht = true; }
    /******** T_min / T_max ********/
    static float T_min = 99.0, T_max = -99.0; 
    if (T_ist < T_min && soll_erreicht) T_min = T_ist;
    else if (T_ist > T_max) T_max = T_ist;
    /******** P_Wert ********/
    float P_Wert = 0;
    if (Kp != 0) P_Wert = Kp * e[0];
    if (Regler_limit) P_Wert = constrain(P_Wert, -100, 300);
    /******** I_Wert ********/
    float I_Wert = 0;
    if (Ki != 0) {
      esum += e[0];
      I_Wert = Ki * Ta / Tn * esum * d_t; }
    if (Regler_limit) I_Wert = constrain(I_Wert, -100, 300);
    /******** D_Wert ********/
    float D_Wert = 0;
    if (Kd != 0) D_Wert = Kd * Tv / Ta * (e[0] - e[1]) / d_t;
    if (Regler_limit) D_Wert = constrain(D_Wert, -100, 300);
    /******** PID_Wert ********/
    float PID_Wert = P_Wert + I_Wert + D_Wert;
    /******** Regler Ausgang ********/
    uint8_t PWM = constrain((long)PID_Wert, 0, 255);
    //uint8_t PWM = constrain(skal(PID_Wert, 0.0, 100.0, 0.0, 255.0), 0, 255);
    analogWrite(Heizung, PWM);
    lcd_Ausgabe(&akt_ms, &soll_ms, &P_Wert, &I_Wert, &D_Wert, &PID_Wert, &PWM, &T_ist, &T_min, &T_max);
#ifdef serial_debug
    debug_Ausgabe(&akt_ms, &T_ist, &P_Wert, &I_Wert, &D_Wert, &PID_Wert, &PWM);
#endif
  }
}//void loop() ENDE


/*Alternativfunktion zu map() fuer float
float skal(const float r, const float min_r, const float max_r, const float min_f,
           const float max_f) {
  return (r - min_r) * (max_f - min_f) / (max_r - min_r) + min_f;
}//float skal() ENDE */

float NTC_Temperatur(const float U_in)
{
  float pU = U_in / 1023.0;
  float R = R_v * pU / (1.0 - pU);
  float T_k = T_NTC * B_NTC / (B_NTC + T_NTC * log(R / R_NTC));
  return T_k - T_0;
}//float NTC_Temperatur() ENDE

void lcd_Ausgabe(const uint32_t *ms, const uint32_t *tx, const float *P, const float *I, const float *D,
                 const float *PID, const uint8_t *PWM, const float *T_ist, const float *T_min, const float *T_max)
{
  static int8_t Seite = 0;
  static uint32_t lcd_ms = 0;
  if (*ms - lcd_ms >= lcd_zyklus) lcd_ms = *ms;
  else return;
  switch (Seite)
  {
    case 0:
      lcd.clear();
      lcd.print(F("PWM= "));  lcd.print(*PWM);
      lcd.setCursor(12, 0);
      lcd.print(map(*PWM, 0, 255, 0, 100)); lcd.print(F("%"));
      lcd.setCursor(0, 1);
      lcd.print(F("s "));     lcd.print(T_soll, 2);
      lcd.setCursor(9, 1);
      lcd.print(F("i "));     lcd.print(*T_ist, 2);
      Seite++;
      break;
    case 1:
      lcd.clear();
      lcd.print(F("P="));     lcd.print(*P, 1);
      lcd.setCursor(8, 0);
      lcd.print(F("Kp="));    lcd.print(Kp, 2);
      lcd.setCursor(0, 1);
      lcd.print(F("I="));     lcd.print(*I, 1);
      lcd.setCursor(8, 1);
      lcd.print(F("Ki="));    lcd.print(Ki, 2);
      Seite++;
      break;
    case 2:
      lcd.clear();
      lcd.print(F("D="));     lcd.print(*D, 1);
      lcd.setCursor(8, 0);
      lcd.print(F("Kd="));    lcd.print(Kd, 2);
      lcd.setCursor(0, 1);
      lcd.print(F("PID= "));  lcd.print(*PID, 1);
      Seite++;
      break;
    case 3:
      lcd.clear();
      lcd.print(F("tx = "));  lcd.print(float(*ms) / 1000.0); lcd.print(F("s"));
      lcd.setCursor(0, 1);
      lcd.print(F("ts = "));  lcd.print(float(*tx) / 1000.0); lcd.print(F("s"));
      Seite++;
      break;
    case 4:
      lcd.clear();
      lcd.print(F("Tmin = "));  lcd.print(*T_min); lcd.print(F(" C"));
      lcd.setCursor(0, 1);
      lcd.print(F("Tmax = "));  lcd.print(*T_max); lcd.print(F(" C"));
      Seite = 0;
      break;
    default:
      lcd.clear();
      lcd.print(F("SEITEN FEHLER"));
      Seite = 0;
      break;
  }
  return;
}//void lcd_Ausgabe() ENDE

#ifdef serial_debug
void debug_Ausgabe(const uint32_t *ms, const float *ist, const float *P, const float *I,
                   const float *D, const float *PID, const uint8_t *PWM)
{
  #define K 2         //Nachkommastellen
  #define tab F("\t") //tab Makro
#ifndef plotter_mode
  Serial.print((float)*ms / 1000.0, K);
  Serial.print(F("\t"));
#endif
  Serial.print(*ist, K);    Serial.print(tab);
  Serial.print(T_soll, K);  Serial.print(tab);
  Serial.print(*P, K);      Serial.print(tab);
  Serial.print(*I, K);      Serial.print(tab);
  Serial.print(*D, K);      Serial.print(tab);
  Serial.print(*PID, K);    Serial.print(tab);
  Serial.println(*PWM);
  return;
}//void debug_Ausgabe() ENDE
#endif

Für eine einfache Temperaturregelung würde ich keine PID, sondern eine einfache Zweipunktregelung mit Hysterese benutzen.

Gruß Tommy

grillgemuese:
:slight_smile:
nanu... keiner meldet sich?

Mit meinen Parametern wirst du nichts anfangen können...

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

Kopiert aus meinem frischesten PID Projekt.
(ist erprobt, aber noch nicht der endgültige Stand, da nicht perfekt)
Recht träge und dazu noch Sensor weit von der Heizung weg.
PWM Bereich 0% bis 30% bei Puls+Pause=10sec
Heizung: 3000W 240V AC
Sensor: Thermistor, 10K

Und, welche Daten du bei dir brauchst, kann ich nicht mal erahnen.

Eigentlich sollte es im Internet schon ein paar Anleitungen geben, wie man brauchbare Start Parameter gewinnen kann. Das Feintunig muss dann sowieso von Hand gemacht werden.