Wie mehrere PID Controller parametrieren?

Hallo,

ich habe es nun endlich geschafft, einen PID Controller auf dem Arduino laufen zu lassen. Sieht man von etwas Feintuning von Kp, Ki und Kd einmal ab.
Nun möchte ich gerne 2 PID's auf einem Arduino laufen lassen. Ich habe mir dazu das Beispiel angesehen, wie man 2 LCD Displays auf einem Controller anspricht

Nur wie bringe ich den beiden PID Instanzen bei, das sie unterschiedliche Kp, Ki und Kd Werte verwenden sollen? Die beiden Instanzen im Programm dann in verschiedenen Unterprogrammen in geschweiften Klammern zu verwenden, leuchtet mir auch ein.
Ich habe aber kein Beispiel finden können, wie ich 2 PIDs erst mal initialisiere.
Kann mir da bitte mal jemand auf die Sprünge helfen?
Sie sieht es mit einem PID aus:

/*
Arduino Tutorial 4.2 - Drehzahl mit Potisteuerung

by Scynd 2014

In diesem Tutorial geht es darum, die Lüftergeschwindigkeit mit Hilfe eines Drehpotenzometers einzustellen.
*/


//Konstanten
const int ledPin = 3;
const int FanPin = 9; // Lüfter an Pin 9 angeschlossen
const int PotiPin = A0 ; // Potenzometer am analogen Eingang Pin 0 angeschlossen
const int TachoPin = 2; // Pin des Tachosignals des Lüfters


// Variablen
int ledBright = 0;
int ledPercent = 0;
int Temp = 0;
#define KRITISCH 40.00
int FanSpeed = 0; // Variable für die Lüftergeschwindigkeit
int FanMin = 50; // Minimaler PWM Wert für den Lüfter. Kommt auf den Lüfter an
int PotiVar = 0 ; // Variable zum speichern des Potentiometereingangs
int AbfrZeit = 5000; // Zeitabstand für die Abfragen des Tachosignals
long TachoMillis = AbfrZeit; // Zeitabstand für Pulse Stretching Funktion
float RPS = 0; // Variable mit Kommastelle für die Berechnung der Umdrehungen pro Sekunde
int RPM = 0; // Variable für die gemittelte Drehzahl
float UmdrZeit = 0; // Variable mit Kommastelle für die Zeit pro Umdrehung des Lüfters
float FlankenZeit =0; // Variable mit Kommastelle für die Zeit pro Puls des Lüfters

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <PID_v1.h>;

#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

//PID SETUP
double Setpoint, Input, Output;                                          //I/O for PID
double aggKp=40, aggKi=2, aggKd=10;                                      //original: aggKp=40, aggKi=2, aggKd=10, Aggressive Turning,50,20,20
double consKp=10, consKi=0.5, consKd=2.5;                                    //original consKp=20, consKi=1, consKd=5, Conservative Turning,20,10,10
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);  //Initialize PID

LiquidCrystal_I2C lcd(0x20,16,4);  // set the LCD address to 0x20 for a 16 chars and 2 line display


void setup() {
  lcd.init();                      // initialize the lcd 
 // Print a message to the LCD.
  lcd.backlight();
  sensors.begin();
  
   Setpoint = 35.00;                      //Eingestellte Temperatur
     myPID.SetMode(AUTOMATIC);
  
TCCR1B = TCCR1B & 0b11111000 | 0x01; // Setzt Timer1 (Pin 9 und 10) auf 31300Hz
// Serial.begin(9600);
pinMode(ledPin, OUTPUT) ;
pinMode(FanPin, OUTPUT) ; //Setzt den Lüfter Pin als Ausgang
pinMode(PotiPin, INPUT) ; //Setzt den LEDPin als Ausgang
pinMode(TachoPin, INPUT); //Setzt den Tacho Pin als Eingang
}


void loop() {

//lcd.clear();  
sensors.requestTemperatures(); // Send the command to get temperatures 

PotiVar = analogRead(PotiPin) ; // Liest das Potentiometer aus

Temp = (sensors.getTempCByIndex(0)) ;
if ((Temp) > -127.00) { Input = Temp;
}

//Input = Temp;
double gap = abs(Setpoint-Input); //entfernung zur eingestellten Temperatur
  if(gap < 1)
  
   {  
    //nah am Setpoint --> KONSERVATIVES REGELN
    myPID.SetTunings(consKp, consKi, consKd);
  }
  else
  {
     //weit weg vom Setpoint --> AGGRESSIVES REGELN
     myPID.SetTunings(aggKp, aggKi, aggKd);
  }
  myPID.Compute();
  
    if (Input < KRITISCH)
    analogWrite(FanPin,Output);
  else
    analogWrite(FanPin,255);


lcd.setCursor(0, 0);
//lcd.print("Poti");
lcd.print("Temp");
lcd.setCursor(5, 0);
//lcd.print(PotiVar);
lcd.print((Input),1);
lcd.write(0xD0 + 15); 
//lcd.setCursor(9, 0);
lcd.print("C  ");

lcd.setCursor(-4, 2);
lcd.print("Fan Out ");
//lcd.setCursor(1, 2);
lcd.print((Output),0);
//lcd.setCursor(6, 3);
lcd.print("    ");

//FanSpeed = map(PotiVar, 50, 1023, FanMin, 255); // Verteilt den PWM Wert über den Messbereich des Potis
ledBright = map(PotiVar, 0, 1023, 255, 0);
ledPercent = map(PotiVar, 0, 1023, 0, 100);

lcd.setCursor(-4, 3);
lcd.print("LED ");
//lcd.setCursor(1, 2);
lcd.print(ledPercent);
//lcd.setCursor(6, 3);
lcd.print(" %  ");

// Unterer Potenziometerbereichs (0-50) = Lüfter aus
//if(PotiVar < 50) {
//FanSpeed = 0;
//}

//analogWrite(FanPin, FanSpeed); // Gibt die Variable mit PWM aus
analogWrite(ledPin, ledBright);

// Alle 2000ms pulse_stretch starten um die Drehzal auszulesen
if((millis() - TachoMillis) >= AbfrZeit) {
pulse_stretch();
}
}


void pulse_stretch() {
// Nur wenn PotiVar größer als 50 ist, wird die RPM ausgelesen
if(Output == 255) {
//analogWrite(FanPin, 255); // Den Lüfter konstant mit Strom versorgen damit das Tachosignal funktioniert
FlankenZeit = pulseIn(TachoPin, HIGH); // Abfrage der Zeit pro Puls in Mikrosekunden
//analogWrite(FanPin, FanSpeed); // Setzt die Lüftergeschwindigkeit zurück
UmdrZeit = ((FlankenZeit * 4)/1000); // Berechnung der Zeit pro Umdrehung in Millisekunden
RPS = (1000/UmdrZeit); // Umrechnung auf Umdrehungen pro Sekunde
RPM = (RPS*60); // Umrechnung auf Umdrehungen pro Minute
lcd.setCursor(0, 1);
lcd.print("Fan ");
//lcd.setCursor(4, 1);
lcd.print(RPM);
//lcd.setCursor(8, 1);
lcd.print(" RPM  ");
}
// Wenn der Lüfter nicht angesteuert wird, schreibe Drehzahl 0
//else{
  
//lcd.setCursor(0, 1);
//lcd.print("Fan 0 RPM    ");
//lcd.setCursor(4, 1);
//lcd.print(RPM ,0);
//lcd.setCursor(8, 1);
//lcd.print("RPM");
  
//Serial.println("0");
//}
//lcd.setCursor(12, 1);
//lcd.print(FanSpeed);
TachoMillis = millis(); // Die TachoMillis werden aktualisiert um die nächsten 2000ms zählen zu können
//delay(1000);
}

Das ganze ist noch im Rohzustand. Es soll ein Temperaturmanagment für eine Power-LED werden. Ein DS18B20 klebt am Kühlkörper und misst die KK Temperatur. Erwärmt sich der KK, versucht der 1. PID den KK auf 40°C mittels Lüfter zu halten. Gelingt das nicht, soll der 2 PID dann ab 45°C zwangsweise die LED runterdimmen. Die KSQ hat einen 0-10V Eingang. Wenn das Ganze funktioniert, werde ich die Hardware hier vorstellen, denn dieses Projekt kann vielleicht noch für andere Leute interessant sein und läßt sich recht easy an andere Hardware anpassen.

Ich kenne diese Bibliothek nicht, aber normalerweise so:

PID myPID1();
PID myPID2();

Muss schon mehr sein:

PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);

Da brauchst du halt input, output und setpoint zweimal. Fertig. Und evtl. zwei Sets an Parametern.

Du kannst dir auch ein struct aus den ganzen Variablen anlegen. Dann erstellt du zwei Objekte davon. Und wenn du das PID Objekt erstellt übergibst du die Variablen die im struct stehen. Dann ist das etwas geordnet und die Variablen liegen nicht nur einfach wild im Sketch rum. Muss aber nicht sein

Serenifly:
Da brauchst du halt input, output und setpoint zweimal. Fertig. Und evtl. zwei Sets an Parametern.

Du kannst dir auch ein struct aus den ganzen Variablen anlegen. Dann erstellt du zwei Objekte davon. Und wenn du das PID Objekt erstellt übergibst du die Variablen die im struct stehen. Dann ist das etwas geordnet und die Variablen liegen nicht nur einfach wild im Sketch rum. Muss aber nicht sein

Puh, zuviel Input und Fachchinesisch :fearful:
Ich bin programmtechnisch Anfänger :wink:
Wenn ich ein Bespiel sehe, kann ich mir das für mich ableiten.
Mit den 2 Instanzen, wie agmue das vorschlug leuchtet mir ein. Ich hab's nur nicht gefunden, weil es unter den ganzen Parametern drunter "begraben" war.
Ich werde das mal versuchen und in den beiden Unterprogrammen innerhalb der geschweiften Klammern umprametrieren. Ist vermutlich nicht sehr elegant, aber es muß in erster Linie funktionieren. Schönheit kommt erst, wenn ich weiß, was ich tue :slight_smile:

Ich verstehe nicht wieso du da so riesige Probleme hast. Auch als Anfänger.

Relevant ist erst mal das:

double Setpoint, Input, Output;  
double consKp=10, consKi=0.5, consKd=2.5;
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);

Das verdoppelt man einfach da jeder Regler eigene Variablen braucht:

double Setpoint1, Input1, Output1; 
double Setpoint2, Input2, Output2; 

const double consKp1=10, consKi1=0.5, consKd1=2.5;    //Wenn du diese Werte nie veränderst sollten sie const sein
const double consKp2=20, consKi2=0.8, consKd2=2.7;
....

PID myPID1(&Input1, &Output1, &Setpoint1, consKp1, consKi1, consKd1, REVERSE);
PID myPID2(&Input2, &Output2, &Setpoint2, consKp2, consKi2, consKd2, REVERSE);

Danke, jetzt hab ich's verstanden. Ansatzweise war das schon durch die 2 unterschiedlichen Settings von Kp, Ki und Kd zusehen, da in der Vorlage, die ich genutzt habe von einem aggessiven Setting bei Annäherung auf den Sollwert auf ein konservatives Setting umgeschaltet wurde.
Mir war nur nicht klar, das ich bei der Deklaration an jede Variable nur eine Nummer mit dranhängen brauche. Ich habe das zu kompliziert gesehen, und dachte, ich muß die gleiche Variable nutzen, damit die als zugehörig erkannt werden und dann innerhalb des Unterprogrammes in den geschweiften Klammern erst die Werte zuweisen, da die sich dann bei gleichem Namen nicht mehr ins Gehege kommen.
Hätte ich den Einstieg nicht so lange vor mehr hergeschoben, wäre ich im Kopf vielleicht etwas beweglicher. Mit 12 hat mir die Digitaltechnik ja auch keinerlei Schwierigkeiten gemacht, die zu begreifen. Aber wenn man auf die 50 zugeht, dann dauert es schon etwas länger :wink:

nix_mehr_frei:
Mir war nur nicht klar, das ich bei der Deklaration an jede Variable nur eine Nummer mit dranhängen brauche.

Leider war ich zu faul, das zu schreiben, leckerer Essenduft zog in meine Nase, bitte um Nachsicht.

Spannend sind ja die "&" vor Variablen, womit man über Zeiger auf die zugreifen kann.