Hallo,
ich ärgere mich immernoch mit meiner Regelung meines mittels PWM
angesteuerten Ventils rum. Ich hatte dazu nochmal den Thread
"Algorithmus fuer PID (Heizungsregelung)" gelesen, und bin schon etwas
schlauer geworden. Mit meiner Problematik komme ich jedoch noch nicht
richtig vorwärts.
Ich habe zunächst einmal eine Sprungantwort meiner Regelstrecke
(bestehend aus Ventil und Durchflussmesser) aufgenommen, und mir so mit
der Ziegler Nichols Methode die Parameter k_p (Verstärkung) und t_i
(Verzugszeit) berechnet.
Nun habe ich folgende Regelroutine in meinem Programm:
// Regelabweichung e_k
e_k = v_soll - v_ist;
// Differenzengleichung zur Berechnung der Stellgröße:
u_k = u_k_alt + k_p*(e_k*(1+t_0/t_i)-e_k_alt);
u_k_alt = u_k;
e_k_alt = e_k;
zu den einzelnen Paramtern:
v_soll: Sollspannung die der Durchflussensor anzeigen soll (1-5V)
v_ist: Istspannung die der Durchflussensor anzeigt (1-5V)
t_0: Abtastzeit
Das größte Problem ist nun, daß ich nicht genau weiß, was ich mit der
Stellgröße u_k anfangen soll...
Normalerweise müßte das doch die Größe sein, die ich auf meine Strecke
(also auf mein Ventil) raufgebe, oder? Nun steuere ich mein Ventil ja
mittels PWM an, muß ich dann den u_k Wert in eine entsprechende
Pulsbreite übersetzen? D.h. wenn ich ein u_k von 5 Volt rausbekäme,
entspräche das einer Pulsbreite von 100 %? In welchem Rahmen bewegt sich
denn überhaupt u_k? Das ist mir nicht wirklich klar.
Die zweite Sache ist die Abtastzeit t_0. Was stellt die dar? Ist das die
Zeit, wie oft ich v_ist Werte mit meinem A/D Wandler einlese?
Über ein par Tipps wäre ich sehr dankbar,
Gruß,
Stefan
Hallo Stefan! u_k ist wie du schon sagtest die Stellgröße. Innerhalb deines Programmes ist dies natürlich nur irgendeine Zahl vom Typ BYTE oder int oder was auch immer. Mit diesem Wert musst du jetzt deine PWM ansteuern. In der Regel (hier: Atmel) bedeutet nun ein Wert von 0 0% PWM-Ausgang und 255 100% PWM-Ausgang. Wichtig ist, daß du die Stellgröße u_k begrenzt, so dass sie nicht kleiner als 0 und größer als 255 wird. Wenn dein Sollwert nicht erreicht werden aknn, so musst du zusätzlich die Integration unterbinden. Mit einem geeigneten Faktor und einem Offset kannst du natürlich dein u_k so skalieren, dass dein PWM-Ausgangssignal das mit dem Ventil macht, was du willst (Arbeitspunkt und Verstärkung sozusagen). Bezüglich t_0 hast du Recht: es ist die Zeit, mit der du deine Werte einliest. Damit deine DZGL stimmt, musst du dafür sorgen, das diese Abtastzeit konstant ist, d.h. am besten mit Timer-Interrupts arbeiten. So hast du die Möglichkeit, t_0 exakt zu bestimmen und in deine Gleichung einfließen zu lassen. Gruß, Sascha
Hallo Sascha, erstmal danke für die Antwort. Die Sache ist mir nun schon etwas klarer. >Mit einem geeigneten Faktor und einem Offset kannst du >natürlich dein u_k so skalieren, dass dein PWM->Ausgangssignal das mit dem Ventil macht, was du willst >(Arbeitspunkt und Verstärkung sozusagen). Was genau meinst du mit "Offset"? Gruß, Stefan
Mit dem Offset könntest du den Arbeitspunkt deines Ventils bestimmen, z.B. wenn du beim Reglerausgang=0 das Ventil trotzdem etwas geöffnet haben musst, addierst du einfach einen Offset, bevor du den Wert an deine PWM übergibst.
Alles klar, jetzt weiß ich was du mit Offset meintest. Ich habe das
Programm nun mal fertiggeschrieben (benutze übrigens CodevisionAVR und
ATmega323), jedoch klappt es noch nicht. Egal was für einen Sollwert ich
vorgebe, die Pulsbreite wird immer auf 100% gesetzt und ich bekomme
vollen Durchfluss (-> Ventil ganz auf -> Durchflussmesser zeigt 5 V an).
Ich poste hier mal das Programm mit einigen Erläuterungen (der
interessante Teil kommt ganz unten). Vielleicht findet jemand einen
Fehler:
#include <mega323.h>
// Standard Input/Output functions
#include <stdio.h>
#define ADC_VREF_TYPE 0x00
// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input|ADC_VREF_TYPE;
// Start the AD conversion
ADCSR|=0x40;
// Wait for the AD conversion to complete
while ((ADCSR & 0x10)==0);
ADCSR|=0x10;
return ADCW;
}
// Variablendeklaration für die Ist-Spannung:
float v_ist;
/* Timer 2 overflow interrupt service routine
Bei jedem Overflow des Timers wird das A/D Ergebnis //eingelesen und
v_ist berechnet (10 bit A/D Ergebnis auf 5 Volt "normiert"): */
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
v_ist = (5.0/1023)*read_adc(0);
}
void main(void)
{
// Declare your local variables here
// Die Regelparamter wurden von mir vorher mit Hilfe der
// Sprungantwort berechnet:
float e_k; // Regeldifferenz
float e_k_alt;
float k_p = 0.723; // Verstärkungsfaktor
float t_i = 7.343; // Verzugszeit
float t_0 = 8.85; // Abtastzeit
float u_k; // Stellgröße
float u_k_alt;
char v_soll;
int stell;
// Input/Output Ports initialization
// Port A initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=T State1=T State2=T State3=T State4=T State5=T State6=T
State7=T
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func0=In Func1=In Func2=In Func3=Out Func4=In Func5=In Func6=In
Func7=In
// State0=T State1=T State2=T State3=1 State4=T State5=T State6=T
State7=T
PORTB=0x08;
DDRB=0x08;
// Port C initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=T State1=T State2=T State3=T State4=T State5=T State6=T
State7=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=T State1=T State2=T State3=T State4=T State5=T State6=T
State7=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 460,800 kHz
// Mode: Phase correct PWM top=FFh
// OC0 output: Non-Inverted PWM
TCCR0=0x62;
TCNT0=0x00;
OCR0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: TCCR2=0x07: 3600 Hz
// TCCR2=0x06; 14400 Hz
// TCCR2=0x05; 28800 Hz -> t_0 = 1/(28800/255) = 8,85 ms
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x05;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
GICR|=0x00;
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x00;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 115200
UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x01;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC Clock frequency: 115,200 kHz
// ADC Voltage Reference: AREF pin
ADMUX=ADC_VREF_TYPE;
ADCSR=0x85;
// Global enable interrupts
#asm("sei")
// Sollspannung über Terminal einlesen (48 entspricht dabei dem Offset
zwischen ASCII und dezimal):
v_soll = (int) getchar()-48;
// Endlosschleife mit Regelroutine (was besseres ist mir nicht
// eingefallen):
while(1)
{
// Regelabweichung e_k berechnen:
e_k = v_soll - v_ist;
// Stellgröße u_k berechnen:
u_k = u_k_alt + k_p*(e_k*(1+t_0/t_i)-e_k_alt);
// Stellgrößenbegrenzung:
if (u_k > 5.0) u_k = 5.0;
else if (u_k < 0.0) u_k = 0.0;
// Stellgröße in entsprechenden PWM Wert umrechnen
// (falls die benötigte Stellgröße 5 Volt beträgt,
// wird OCR0 auf 255 gesetzt -> Ventil ganz auf):
stell = (int) (255/5.0)*u_k;
// OCR0 Register -> Pulsbreite setzen:
OCR0 = stell;
u_k_alt = u_k;
e_k_alt = e_k;
}
}
Ich hab jetzt dein Programm nicht durchgesehen, aber kann es sein, dass du keinen negativen Wirksinn hast? Wenn die Soll-Istwert-Abweichung positiv ist, muss natürlích dein Ventil weiter schließen.
Was mit auffällt: wenn t_0=8,85ms ist, dann musst du natürlich 0.00885 zuweisen und nicht 8.85!! die Regelroutine in der while(1) Schleife muss in die Interrupt-Routine timer2_ovf_isr, damit du einbe konstante Abtastzeit hast. So wird es nicht funktionieren! Gruß, Sascha
Hallo Sascha, die Sache mit den Millisekunden und der while (1) Schleife habe ich jetzt verbessert. Aber das mit dem Wirksinn sehe ich genau andersrum: Wenn ich eine positive Regelabweichung habe (mein Sollwert also höher ist als mein Istwert), dann fließt ja noch zuwenig Gas durchs Ventil, es muß sich also weiter öffnen statt schließen...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.