Hallo allerseits,
Ich möchte Mal in die Runde fragen, ob jemand einen Einfall zum optimalen einstellen des PID Regler im folgendem Fall hat:
Ich habe zum solaren Eigenverbrauch eine 6 kW Heizpatrone eingebunden, die über ein Energie Messgerät ab ca -400W Überschuss angesteuert wird.
Das Messgerät (Siemens Sentron) liefert die Daten über MOD Bus an den Arduino. Ich nutze die PID_v1.h Funktionen.
Das Ganze läuft Recht gut, aber halt nicht sehr optimal, was das Regelverhalten an geht.
anbei die relevanten Sketch Auszüge...
Nun ist es so, dass es mir noch nicht gelungen ist , ein zügiges ohne Überschwingen, ruhiges Regelverhalten hin zu bekommen. ( habe durch diverse Workarounds, die Effekte minimiert)
Die momentan im Sketch eingestellten Werte, schwingen zwar nicht, sind mir allerdings auch zu reaktionsträge.
Situationen wie das aufziehen einer Wolke oder das zuschalten eines Verbrauchers, ändern schnell mal den PV Überschuß von zB 5 kW auf 1,5 kW und umgekehrt ,dann sollte möglichst schnell die Ansteuerung des Heizstabs zurück gefahren oder wieder zu geschalten werden. Im moment benötigt das Nachführen der Regelung weinige Minuten. Wenn es schneller gehen soll fängt es an über zu schwingen und es kommt zum Netzbezug, was natürlich unbedingt vermieden werden soll.
In dem Zusammenhang gleich noch die Frage nach der PID Autotune Funktion. Weiß jemand wie man die verwendet.
Ich stellte mir das so vor, dass ich die Funktion einbinden , einige Zeit laufen lasse und mir dann die optimalen PID Werte ausgegeben werden. Kann man das so verwenden? Wenn ja, wie?
Wer weiß dazu was.
Ich bin gespannt auf Ideen aus der Community...
#include "U8glib.h" //alles fürs Display
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); // I2C / TWI
//U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send ACK
#define WIZNET_W5100 1 // Ethernetshild bezeichnung
unsigned int param_value_int[7];
#include <Ethernet.h>
#include <PID_v1.h>
#include "ModbusTCP.h"
#include <avr/wdt.h>
//----------------------------------------------------------------------------
//#include <SPI.h> // Not needed if device is I2C
#include <Wire.h>
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
//------------------------------------------Thermometer------------------------------------------------------------------------------------------------------------------------------------------
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 11
#define TEMPERATURE_PRECISION 11
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
//DeviceAddress Anlegefuehler;
DeviceAddress Anlegefuehler = { 0x28, 0x4f, 0xea, 0xf6, 0x04, 0x00, 0x00, 0xce }; //284FEAF6040000CE 284FEAF6040000CE
float Anlegefuehler_temp, Anlegefuehler_temp_old = 0;
int Notabkuehlung = 0; // Wert wird mit 20C geladen wenn notaus durch überhitzung
//--------------Prototyp--------------------------------------------------------------------------------------------------------------------------------------------------------------
void Mbus();
void Display_();
void Leistungsstrg_PV();
void Ein_Ausgaenge();
void Temperatur();
void Pumpensteuerung();
void Timer(void);
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
IPAddress ModbusDeviceIP(192, 168, 178, 12); // Put IP Address of PLC here or slave , adresse von Modbus_modul
IPAddress moduleIPAddress(192, 168, 178, 11); // (11)Assign Anything other than the PLC IP Address, adresse von Ethernetshild arduino
//IPAddress ModbusDeviceIP(192, 168, 178, 38); // Put IP Address of PLC here or slave , adresse von Modbus_modul Wechselrichter MODBUS Addr1
byte mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // This is a generic MAC. In case your device has an specific MAC, please, use it; ggf. mac adresse eingeben
// The IP address for the shield
byte netmask[] = {
255, 255, 255, 0
};
byte gateway[] = {
192, 168, 178, 1
};
byte dns[] = {
192, 168, 178, 1
};
ModbusTCP node(1); // Unit Identifier.
unsigned int data1;
unsigned int data2;
float Watt;
double Watt_eigenv_Ausgabe;
unsigned long dataNew;
int counter = 0; // Seconds counter
unsigned long int currentTime, nextTime;
#define test_ausdruck 1 // zum aus ein schalten für testausdruck
#define REVERSE 1
#define DIRECT 0
#define P_ON_M 0
#define P_ON_E 1
double sollwert = -400;
//Define Variables we'll be connecting to
double Setpoint, Input, Output, Output_old;
//Define the aggressive and conservative Tuning Parameters
double aggKp = 0.5, aggKi = 0.08, aggKd = 0.001; //letzte einstellungen: aggKp=0,5, aggKi=0.07, aggKd=0.001;
double consKp = 0.1, consKi = 0.03, consKd = 0.0001; // werte ggf. 0.01;0.0005;0.0002 noch testen ??!
double downKp = 3.5, downKi = 0.05, downKd = 0.005; // werte zum schnellen zurückfahren des Verbrauchers wenn Watt pos wird
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);
double kW_Output_Heizung;
double Pump_Leist;
unsigned long WindowSize = 255;
const int HeizAnforderungEing = 7; // Eing. 7 zur Heizung
const int Hauptschuetz = 5; // Schaltet Leistung L1,2,3 auf Regler
const int Pumpe = 6; // für zusätzliche Zirkulationspumpe wird überanalog PIN & PWM angesteuert
double warten_Schuetz, warten_Temp, Abkuehlzeit, warten_Sollwert; // für Zeiten
double Pumpenleistung = 130; // für Analaogwert PWM
bool HeizAnforderung;
int display = 0;
int Watt_int;
int kW_Output_Heizung_int;
int Pump_Leist_int;
//bool Flag_Pumpe; // Merker für diverse..........
void setup() {
wdt_disable(); // WD vorsichtshalber ausschalten um zugriff aufrecht zu erhalten
Serial.begin(9600); // Monitorübertragung funktioniert nicht mehr bei höherer Baud Rate ---bei dearf nochmal austesten
delay(1000);
//------------------------------Ein/Ausgänge---------------------------------------------------------
pinMode(HeizAnforderungEing, INPUT); // Wenn Heizung Wärme benötigt und drei Wege auf "Nachheizung" steht; Puldown an Klemme
pinMode(Hauptschuetz, OUTPUT);
pinMode(Pumpe, OUTPUT);
TCCR3B = TCCR3B & 0b11111000 | 0x02; // PIN 3 PWM 3,9 kHz Takte für Analogausgänge geändert
TCCR4B = TCCR4B & 0b11111000 | 0x05; // PIN 6 auf 30 Hz zur PWM für Pumpe
/*//TCCR4B = TCCR4B & B11111000 | 0x01; // set timer 4 divisor to 1 for PWM frequency of 31372.55 Hz
//TCCR4B = TCCR4B & B11111000 | 0x02; // set timer 4 divisor to 8 for PWM frequency of 3921.16 Hz
TCCR4B = TCCR4B & B11111000 | 0x03; // set timer 4 divisor to 64 for PWM frequency of 490.20 Hz
//TCCR4B = TCCR4B & B11111000 | 0x04; // set timer 4 divisor to 256 for PWM frequency of 122.55 Hz
//TCCR4B = TCCR4B & B11111000 | 0x05; // set timer 4 divisor to 1024 for PWM frequency of 30.64 Hz*/
//-------------------------------Temperatursensor---------------------------------------------------------------------------------------------------------------------
sensors.begin();
Serial.print("Locating Temperatu_devices...");
Serial.print("Found -->");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" devices.");
if (!sensors.getAddress(Anlegefuehler, 0)) Serial.println("Unable to find address for Device -- Anlegefühler");
sensors.setResolution(Anlegefuehler, TEMPERATURE_PRECISION);
//------------------PID setup------------------------------------------------------------------------------------------------------------------------------
//initialize the variables we're linked to
myPID.SetSampleTime(50);
; // 50 ms hat sich im Test bewährt
myPID.SetOutputLimits(0, WindowSize);
//turn the PID on
myPID.SetMode(AUTOMATIC);
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
pinMode(4, OUTPUT);
digitalWrite(4, HIGH); // To disable slave select for SD card; depricated.
warten_Schuetz = millis(); // Zeit erstmalig laden
warten_Sollwert = (millis());
//Ethernet.begin(mac, moduleIPAddress); // Starts the Ethernet card
Ethernet.begin(mac, moduleIPAddress, dns, gateway, netmask);
node.setServerIPAddress(ModbusDeviceIP);
delay(1000); // To provide sufficient time to initialize.
//----------------------------------------------------------------------------------------------------------------------------------
HeizAnforderung = digitalRead(HeizAnforderungEing);
//----------Versionsanzeige-----------------------------
Serial.print("Vers_Eigenverbr_300823 ");
Serial.println("");
delay(5000);
wdt_enable(WDTO_8S); /* läd WD mit der Zeit 8s...weitere Zeiten 16 ms WDTO_15MS
32 ms WDTO_30MS
64 ms WDTO_60MS
0,125 s WDTO_120MS
0,25 s WDTO_250MS
0,5 s WDTO_500MS
1,0s WDTO_1S
2,0 s WDTO_2S
4,0 s WDTO_4S
8,0 s WDTO_8S*/
}
//----setup Ende-------------------------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------------------------------
void loop() {
Mbus(); //
Ein_Ausgaenge();
Temperatur();
if (millis() - warten_Sollwert >= 60000) {
Leistungsstrg_PV();
}
if (Watt > 100) {
warten_Sollwert = (millis()); // zu Timer neu stellen wenn Wert mal Positiv zB. 100 Watt war
myPID.SetMode(0); // PID einmal aus / ein schalten zur neu initialisierung
myPID.SetMode(AUTOMATIC);
}
Pumpensteuerung();
#if test_ausdruck
//Test_Prog();
Serial.print("Test_Prog-->");
Serial.println(Watt);
#endif
node.clearResponseBuffer();
//-------------------------------------------------- Display-------------
u8g.firstPage();
do {
Display_();
} while (u8g.nextPage());
//---------------------------------------------------
wdt_reset(); // Watchdog zurück setzen damit er nicht auslöst
}
#if test_ausdruck
Serial.print("Messung_Watt ");
Serial.println(Watt);
Serial.print(",");
#endif
Setpoint = sollwert;
#if test_ausdruck
Serial.print("Setpoint ");
Serial.println(Setpoint);
Serial.print(",");
#endif
double gap = abs(Watt - Setpoint); //Abweichung vom Einstellwert zB -400 W
#if test_ausdruck
Serial.print("gap--> ");
Serial.println(gap);
Serial.print(",");
#endif
if (gap < 200)
{ //we're close to setpoint, use conservative tuning parameters
myPID.SetTunings(consKp, consKi, consKd);
} else {
//we're far from setpoint, use aggressive tuning parameters
myPID.SetTunings(aggKp, aggKi, aggKd, P_ON_E); // P_ON_M soll das Überschwingen verhindern----testen; weitere Test mit P_ON_E...
}
if (Watt > 0) {
myPID.SetMode(0); // PID einmal aus / ein schalten zur neu initialisierung
myPID.SetMode(AUTOMATIC);
myPID.SetTunings(downKp, downKi, downKd);
}
/*#if test_ausdruck
Serial.print("PID_Input ");
Serial.println(Input);
Serial.print(",");
#endif */
Setpoint = map((Setpoint), 0, -6000, 0, 255); // scaliert 0-6000 watt auf 0-255 digits und //damit im PID Regler POS Werte verarbeitet werden
Input = map((Watt), 0, -6000, 0, 255);
myPID.Compute();
Watt_eigenv_Ausgabe = Output; //
//
kW_Output_Heizung = map(Output, 0, 255, 0, 6000); // scaliert 0-255 watt auf 0-6000 für die Anzeige;
/*#if test_ausdruck
Serial.print("PID_Output ");
Serial.println(Output);
Serial.print(",");
#endif */
#if test_ausdruck
Serial.print("kW_Output_Heizung 0-6000 ");
Serial.println(kW_Output_Heizung);
Serial.print(",");
#endif
/#if test_ausdruck
Serial.print("Watt_eigenv_Ausgabe 0-255 ");
Serial.println(Watt_eigenv_Ausgabe);
Serial.print(",");
#endif/
analogWrite(3, Watt_eigenv_Ausgabe); // hier wird der Ausgangswert zur Ansteuerung 0-10 V geschrieben
}