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
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