PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PID - Parameter on the fly ändern



hacker
04.02.2010, 15:37
Hallo zusammen,

ich habe für eine Hydraulik ein digitalen PID Regler gebaut. Differenzsignal geht auf einen AD, dann in den µC und am Ende wird die Stellgröße mit einem DAC ausgespuckt. Soweit so gut. Im Prinzip läuft das Ding auch recht gut.

Abtastfrequenz beträgt 10kHz. Mein Code sieht so aus (ein paar Dinge rausgemacht, die damit nichts zu tun haben):



#include <avr/io.h>
#include <avr/interrupt.h>
//#include <avr/eeprom.h>

#include <pid.h>
#include <adc.h>
#include <dac.h>

double Kp = 0;
double Ki = 0;
double Kd = 0;
double y = 0;
double e = 0;
double e_alt = 0;
double e_sum = 0;

#define Ta 0.0001

//unsigned char Kp_eeprom EEMEM;
//unsigned char Ki_eeprom EEMEM;
//unsigned char Kd_eeprom EEMEM;

void init_pid(void)
{
// die gespeicherten Parameter laden und Regler initialisieren
//Kp = (double) eeprom_read_byte(&Kp_eeprom);
//Ki = (double) eeprom_read_byte(&Ki_eeprom);
//Kd = (double) eeprom_read_byte(&Kd_eeprom);
// PID Timer
TCCR0 |= (1 << WGM01) | (1 << CS01) | (1 << CS00);
//OCR0 = 250 ergibt 1kHz
OCR0 = 25; // ergibt 10kHz
}

void pid_set_parameters(unsigned char p, unsigned char i, unsigned char d)
{
// Den aktuellen Variablen die neuen Parameter zuweisen
Kp = (double) p;
Ki = (double) i;
Kd = (double) d;
// Die neuen Parameter nicht-flüchtig ablegen, damit sie beim nächsten mal geladen werden können
//eeprom_write_byte(&Kp_eeprom, (unsigned char) Kp);
//eeprom_write_byte(&Ki_eeprom, (unsigned char) Ki);
//eeprom_write_byte(&Kd_eeprom, (unsigned char) Kd);
}

void pid_start(void)
{
// Regler starten
TIMSK |= (1 << OCIE0);
}

void pid_stop(void)
{
// Regler stoppen
TIMSK &= ~(1 << OCIE0);
}

void pid_calculate(void)
{
// e = w-x ; Differenz soll-ist
e = (double) adc_read();
// ADC Wert transformieren
if (e >= 2048)
{
e -= 4096;
}
// WindUp vermeiden
if (y < 4095)
{
e_sum += e;
}
// PID Stellgrößen Berechnung
y = -Kp*0.1*e;
y -= Ki*e_sum;
y -= Kd*(e-e_alt);

e_alt = e;
// Stellgröße beschränken
if (y > 2047){y = 2047;}
if (y < -2048){y = -2048;}
// Stellgröße für den DAC transformieren
y += 2048;
// Stellgröße für das Ventil ausgeben
dac_write((unsigned short)y);
}

ISR(TIMER0_COMP_vect)
{
// mit 10kHz Abtastrate PID Algorithmus aufrufen, also alle 100µs
pid_calculate();
}


So, nun zu dem Problem:

Ich möchte während der Regler seinen Dienst tut die Regelparameter on the fly ändern.
Angesprochen wird der Regler über Rx/Tx und der Fleury Lib für UART, interruptgesteuerter Empfang.

Wenn der Regler noch nicht gestartet wurde, kann ich wunderbar die Parameter ändern. Starte ich diesen jedoch einmal, kann ich ihn weder anhalten noch Parameter ändern.

Sprich er reagiert über UART nicht mehr.



Wo liegt hier das Problem? Ist die Abtastfrequenz zu hoch und die UART Interrupts kommen nicht "durch", weil ständig der Timer Interrupt zuschläge oder was ist hier los?

Abtastfrequenz muss bei 10kHz bleiben, da ich die Hydraulik sonst nicht stabil bekomme.



Viele Grüße,
hacker

Andun
04.02.2010, 16:28
Ich muss zugeben, dass ich nicht der Fachmann bin, aber zur Problemlösung ist sicherlich noch dein CPU-Takt interessant!

uwegw
04.02.2010, 16:51
Für so zeitkritische Sachen würde ich auf nem AVR nicht mit floats arbeiten, sondern auf Festkommaarithmetik umstellen. Wenn es an der Rechenleistung liegt, dann dürfte das schon helfen.

chientech
04.02.2010, 20:09
Hi,

also ganz wichtig wäre erst mal:
Controller ?
Taktrate ?

Wie schnell und wie oft wird der ADC Wert bestimmt?
Wird der ADC Wert gefiltert?
bzw. Was passiert in adc_read() ?

Wird der Algorithmus wirklich in double gerechnet oder macht der Compiler daraus in wirklichkeit ne Fload? (Bin ich mir nicht sicher?)
Eine Festkommarechnung ist hier aber sicher vorzuziehen.

Was benutzt du für einen DAC? (allgemeine Frage)


gruß ch

RP6conrad
04.02.2010, 22:35
Was steht eigentlich in "Main" ? Scheinbar lauft nur noch der Timer Interrupt, und wird auch nur "PID calculate" ausgefuhrt.

hacker
05.02.2010, 15:57
Ok, Festkommaarithmetik werde ich mal versuchen.

Controller ist Mega32 und läuft mit 16MHz. Der ADC wird einmal in der PID Berechnungs-Funktion geholt, also alle 100µs. Die reine Wandlung des externen ADCs beträgt ca. 1,5µs + die paar Befehle zum Daten abrufen (die Daten kommen parallel).
Nein, der ADC - Wert wird nicht gefiltert. In adc_read() passiert also nichts zeitaufwendiges. Es wird rein der ADC ausgelesen, mehr nicht.

Als DAC benutzte ich einen AD767.

In der Endlosschleife Main läuft rein gar nichts.

Kann ich mittels AVR Studio irgendwie rausbekommen, wie lange die PID Berechnung dauert?


Was passiert eigentlich, wenn eine ISR momentan ausgeführt wird und während dessen trifft ein neuer Interrupt ein? Verpufft dieser, oder wird der gemerkt, bis die akutelle ISR vorbei ist?



/*schluck*

Laut Simulator soll die Berechnung 350µs dauern...kommt mir arg lang vor.

/*holla*

Mit Shorts brauchts nur ganze 7,6µs!

uwegw
05.02.2010, 16:38
Ich hab grad keine Zahlen für den AVR, aber bei einem XC161 (16bit, 40MHz) braucht eine Float-Multiplikation etwa 6,4µs. Ganz grob geschätzt würde ich beim AVR mit dem fünffachen rechnen.
Du machst fünf Multiplikationen, macht also etwa 160µs. Daher dürfte der AVR die ganze Zeit im Timer-Interrupt hängen.

Wenn der Timer-Int aktiv ist und der serielle Interrupt tritt ein, wird dieser erst mal gespeichert. Wenn der Timer nun fertig ist, führt der AVR einen Befehl aus main aus. Dann bearbeitet er den nächsten anstehenden Interrupt. Inzwischen ist aber auch der Timer_interrupt nochmal aufgetreten, und wurde ebenfalls gespeichert. Nun wird nach der Priorität entschieden, wer zuerst drankommt. Und das ist der Timer...

RP6conrad
05.02.2010, 19:16
Ich siehe keinen Grund warum etwas anderes als die function "PID_calculate" ausgefuhrt werden soll. Da stehts nicht in main, und nur diese ISR(TIMER0_COMP_vect) ist programmiert. Du muss auch einen function für diese serielle comm. programmieren.

Besserwessi
05.02.2010, 21:54
Wenn die Ausführungszeit der Timer ISR länger ist als der Abstand der ISRs, dann kommt eine ISR von der USRT nie zur ausführung, denn die Timer interrupts haben die größere Priorität.
Da hilft also nichts als die ISR zu beschleunigen, indem man von den Floats weg kommt.

Die oben genannten 350 µs sind für Floats nicht besonders lang.

sternst
05.02.2010, 22:49
Man könnte ja auch einfach sagen "ich will die Regelfunktion mit maximaler Frequenz ausgeführt haben", und einfach die Funktion in die (im Moment leere) Main-Schleife packen (und auf den Timer-Interrupt verzichten). Dann hast du auch keine Probleme mehr mit Interrupt gesteuertem UART-Empfang nebenher.

chientech
06.02.2010, 10:20
Hi,

mal eine ganz andere Frage.

Das Stellglied welches du für deine Hydraulik benutzt kann das überhaupt innerhalb von 100µs eine neue Stellung anfahren?

Wenn nicht macht es auch keinen Sinn den Regler so schnell laufen zu lassen.

gruß ch

hacker
06.02.2010, 11:28
Du muss auch einen function für diese serielle comm. programmieren.

Die Funktion ist in einer anderen *.c Datei implementiert.

Jetzt ist mir auch alles klar. Ich hätte aber nie gedacht, dass die Flots so lange brauchen. Ich werde alles mit Shorts lösen. Dann komm ich sogar auf noch schnellere Zeiten.

Den Regler will ich nicht in die Main packen, da er schon zu definierten Zeiten aufgerufen werden soll, und nicht von Interrupts mal kurzzeitig verzögert werden soll.

Das Stellglied hat mit den hohen Frequenzen keine Probleme.

Vielen Dank für alle. Ich denke das Problem ist gelöst.

Ich werde erst nächste Woche dazu kommen es an der Maschine selbst auszuprobieren. Ich werde dann berichten.

Besserwessi
06.02.2010, 12:17
Wenn man die regelschleife möglicht wenig stören will, sollte man dann die UART routine ins Hauptprogramm schreiben. Jede weitere ISR kann zu verzögerungen führen.

Wenn man nicht gerade Strom sparen muß, macht es ja nichts wenn man eine Aufgabe per Polling erledigt.

Eine anderer Punkt der nicht besonders gut für die Geschwindigkeit ist, ist es in der ISR eine Funktion aufzurufen. GCC kann deutlich besser optimieren wenn die ISR ohne Funktionsaufrufe auskommt.