Hallo,
ich habe meine Wärmeschranksteuerung nun von 2-Punktregelung auf PID umstellen wollen. Die Implementation ging schneller, als vermutet, aber das Ding heizt, als gäbe es kein Morgen! ![]()
Ich habe schon zig mal drübergeschaut und auch Kp, Ki und Kd gedreht - erfolglos!
Habe die Initialisierung meiner Werte von float auf double umgestellt, weil die Werte im Beispielsketch der PID Lib so initialisiert wurden und ich die Temperatur übergeben muß. Falsche Werte auf Grund von unterschiedlichen Datentypen beim Rechnen damit hoffe ich damit ausgeschlossen zu haben.
/*
Example 39.3 - NXP SAA1064 I2C LED Driver IC Demo III
Displaying numbers on command
http://tronixstuff.com/tutorials > chapter 39
John Boxall July 2011 | CC by-sa-nc
*/
#include <PID_v1.h>
#include "Wire.h" // enable I2C bus
#include <LiquidCrystal_I2C.h>
#define sensorPin 0 // Verbunden mit LM35 Ausgang
#define DELAY1 10 // kurze Wartezeit beim Messen
#define DELAY2 500 // kurze Wartezeit beim Anzeigen
#define heaterPin 9 // Heizung-Pin
#define threshold 40 // Schalt-Temperatur für Lüfter (40 Grad Celsius)
#define hysterese 0.1 // Hysterese-Wert (0.1 Grad Celsius)
#define Kp 2.5
#define Ki 0.01
#define Kd 0
const int cycles = 50; // Anzahl der Messungen
LiquidCrystal_I2C lcd(0x20, 16, 2); // Adresse auf 0x27 für 16 Zeichen/2 Zeilen
byte saa1064 = 0x70 >> 1; // define the I2C bus address for our SAA1064 (pin 1 to GND)
int digits[12]={
63, 6, 91, 79, 102, 109, 125,7, 127, 111, 204, 0};
// these are the byte representations of pins required to display each digit 0~9, °C sign, and blank digit
//Define Variables we'll be connecting to
double Setpoint, Input, Output;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
int WindowSize = 5000;
unsigned long windowStartTime;
// right hand digit 180° flip - DP ist used as ° symbol :-) 204 is °c for big C use 143
void setup()
{
windowStartTime = millis();
//initialize the variables we're linked to
Setpoint = threshold;
//tell the PID to range between 0 and the full window size
myPID.SetOutputLimits(0, WindowSize);
//turn the PID on
myPID.SetMode(AUTOMATIC);
lcd.init(); // LCD initialisieren
lcd.backlight(); // Hintergrundbeleuchtung aktivieren
pinMode(heaterPin, OUTPUT);
Wire.begin(); // start up I2C bus
delay(500);
initDisplay();
}
void initDisplay()
// turns on dynamic mode and adjusts segment current to 12mA
{
Wire.beginTransmission(saa1064);
Wire.write(B00000000); // this is the instruction byte. Zero means the next byte is the control byte
Wire.write(B01110111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 21mA segment current
// left 111 is relevant for current left hand "1" 12mA, middle 6mA and right 3mA - so you can tewak it
Wire.endTransmission();
}
void clearDisplay()
{
Wire.beginTransmission(saa1064);
Wire.write(1); // start with digit 1 (right-hand side)
Wire.write(0); // blank digit 1
Wire.write(0); // blank digit 2
Wire.write(0); // blank digit 3
Wire.write(0); // blank digit 4
Wire.endTransmission();
}
void displayInteger(int num, int zero)
{
int hundred, ten, one;
// breakdown number into columns
hundred = num/100;
ten = (num-(hundred*100))/10;
one = num-((hundred*100)+(ten*10));
if (zero==1) // yes to leading zero
{
Wire.beginTransmission(saa1064);
Wire.write(1);
Wire.write(digits[hundred]);
Wire.write(digits[ten]+128); // 128 turns on DP
Wire.write(digits[one]);
Wire.write(digits[10]); // print position 10 from arry - °C
Wire.endTransmission();
delay(10);
}
else
if (zero==0) // no to leading zero
{
if (hundred==0) { hundred=11; }
if (hundred==0 && num<100) { hundred=16; }
if (ten==0 && num<10) { ten=16; }
Wire.beginTransmission(saa1064);
Wire.write(1);
Wire.write(digits[hundred]);
Wire.write(digits[ten]+128);
Wire.write(digits[one]);
Wire.write(digits[10]);
Wire.endTransmission();
delay(10);
}
}
void loop()
{
double resultTemp = 0.0;
for(int i = 0; i < cycles; i++){
int analogValue = analogRead(sensorPin);
double temperature = (5.07 * 100.0 * analogValue) / 1024; // reale Spannung Ub (theoretisch 5.00V)
resultTemp += temperature; // Aufsummieren der Messwerte
delay(DELAY1);
}
resultTemp /= cycles; // Berechnung des Durchschnitts
lcd.clear(); // clear-Methode löscht LCD Inhalt
lcd.print("Temp: "); // print-Methode schreibt LCD Inhalt
lcd.print(resultTemp);
Input = (resultTemp);
myPID.Compute();
lcd.setCursor(10, 0);
#if ARDUINO < 100
lcd.print(0xD0 + 15, BYTE); // Grad-Zeichen (Arduino 0022)
#else
lcd.write(0xD0 + 15); // Grad-Zeichen (Arduino 1.00)
#endif
lcd.print("C");
lcd.setCursor(0, 1); // setCorsor-Methode positioniert LCD-Cursor
lcd.print("Heizung: ");
{
delay(10);
}
clearDisplay();
{
/************************************************
* 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(heaterPin,HIGH);
else digitalWrite(heaterPin,LOW);
displayInteger(resultTemp * 10 ,0); //for LED-Display x10 - DP is hardwired
delay(5);
// if(resultTemp < (threshold - hysterese)) // Temperaturvergleich pos.
// digitalWrite(heaterPin, HIGH); // Heizung anschalten
// else if(resultTemp > (threshold + hysterese)) // Temperaturvergleich neg.
// digitalWrite(heaterPin, LOW); // Heizung abschalten
lcd.print(digitalRead(heaterPin) == HIGH?"an":"aus");
// delay(DELAY2);
}
}
Kompilieren tut er alles, aber irgendwo steckt ein logischer Fehler drin, den ich nicht finde :-/
Da der PID quasi eine Blackbox ist, weiß ich auch nicht, wo ich mit Auslesen von Zwischenergebnissen üner die serielle Schnittstelle ansetzen soll.
Gruß Gerald