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.