cumi
28.08.2006, 19:14
Hallo Zusammen
Ich möcht die Geschwindigkeit eines Modellautos mit einem AVR (konkret sehe ich den ATmega8 vor) regeln. Ich habe dazu einmal einen PID-Regler versucht zu implementieren.
Zuerst ein paar Bemerkungen zu meinen konzeptionellen Überlegungen:
Da auf dem AVR auch noch ein paar andere Prozesse am laufen sind, sollte der Regler möglichst schnell sein. Ich habe daher keine Gleitkommazahlen sondern nur signed und unsigned Integer verwendet (meine Typendefinition: IntX ist ein signed Typ, welcher X Bit lang ist, byte ist ein 8Bit langer, word 16bit und dword32 bit langer unsigned Typ). Daher auch die Gewichtungsangabe als zp/zn (zp: Zähler vom Proportionalteil). Der Eingang habe ich noch "skaliert". Ich messe ja eine Frequenz (Wegimpulsgeber). w ist nun die Sollfrequenz (nicht Geschwindigkeit!). Wenn diese grösser als wh (wird extern berechnet, zb. mit 1.2*w) dann gebe ich einfach Vollausschlag auf den Eingang. Ist die Frequenz dazwischen wird diese linear abgebildet.
Nun zu meinen Fragen:
1. Was hält ihr von den oben geschilderten Bemerkungen? Sind die schlau? Oder ist man mit Gleitkommazahlen ebensoschnell? Schneller?
2. Ist der PID-Regler so richtig implementiert?
3. An einigen Stellen habe ich das "überlaufen" der Integer noch nicht verhindert. Wie macht man das schön? Und was kommt eigentlich raus, wenn man einer Variable vom Type Byte den Wert 5*100 (also ein Produkt welches Grösser als 2^8 -1 ist) zuweist? (ich habe auf dieser Maschiene leider gleich keinen Compiler um es auszuprobieren). Wie verhindert man das überlaufen am schönsten?
4. sonstige Bemerkungen? Was würdet ihr noch ändern?
// includes --------------------------------------------------------------------
#include <normlib.h>
#include "global.h"
#include "freq.h"
#include "tim.h"
#include "svo.h"
// local definitions -----------------------------------------------------------
#define BYTE_MIN 0
#define BYTE_MAX 255
#define WORD_MIN 0
#define WORD_MAX 65535
#define DWORD_MIN 0
#define DWROD_MAX 4294967295
#define INT8_MIN -127
#define INT8_MAX 127
#define INT16_MIN -32767
#define INT16_MAX 32767
// global variables ------------------------------------------------------------
// local variables -------------------------------------------------------------
word w,wl,wh; // w:Sollwort, wl: unterer Skalierwert, w2: oberer Skalierwert
byte zp,np,zi,ni,zd,nd; //Gewichtung des p-,i-,d-Anteil
word iSat; //Max/Min Val of ie; must be < INT16_MAX
dword t0; //timestamp of last execution
int16 e0; //e of last execution
int16 ie; //integrated e
// local function declarations -------------------------------------------------
inline static int16 calcE(); //positiv: too fast, negativ: too slow
inline static byte pid(int16 e, dword dt); //0: full power, 255: no power
// global function implementation ----------------------------------------------
void regelInit(){
e0=timGet();
ie=0;
}
void regel(){
svoSet(pid(calcE(),timGetDif(t0)));
t0=timGet();
}
// local function implementation -----------------------------------------------
inline static int16 calcE(){
int16 fDif=freqGet()-w;
if(fDif>0){
if(fDif>=wh)
return INT16_MAX;
else
return fDiff*(INT16_MAX+1)/wh;
}else{
if(er<=-wl)
return INT16_MIN;
else
return fDiff*(INT16_MIN-1)/wl;
}
}
inline static byte pid(int16 e, dword dt){
int16 p,i,d;
//proportional part
p=(e+INT16_MAX+1)*zp/np;
//intergral part
{
int32 tmp=e*dt/F_CPU; //dt must be <1s
if(tmp>0){
if((INT16_MAX-ie)<=tmp)
ie=INT16_MAX;
else
ie+=tmp;
}else{
if((INT16_MIN-ie)<=tmp)
ie=INT16_MIN;
else
ie+=tmp;
}
}
i=ie*zi/ni;
//differential part
d=(e-e0)*dt/F_CPU*zd/nd;
e0=e;
{
int32 tmp=((p+i+d)/(BYTE_MAX+1))+127;
if(tmp>=BYTE_MAX)
return BYTE_MAX;
if(tmp<0)
return 0;
return tmp;
}
}
// EOF -------------------------------------------------------------------------
Vielen Dank für eure Hilfe!
Und ich hoffe, dass all diejenigen, welche in Zukunft einen PID Regler für einen AVR in C suchen hier fündig werden :)
Grüsse
cumi
Ich möcht die Geschwindigkeit eines Modellautos mit einem AVR (konkret sehe ich den ATmega8 vor) regeln. Ich habe dazu einmal einen PID-Regler versucht zu implementieren.
Zuerst ein paar Bemerkungen zu meinen konzeptionellen Überlegungen:
Da auf dem AVR auch noch ein paar andere Prozesse am laufen sind, sollte der Regler möglichst schnell sein. Ich habe daher keine Gleitkommazahlen sondern nur signed und unsigned Integer verwendet (meine Typendefinition: IntX ist ein signed Typ, welcher X Bit lang ist, byte ist ein 8Bit langer, word 16bit und dword32 bit langer unsigned Typ). Daher auch die Gewichtungsangabe als zp/zn (zp: Zähler vom Proportionalteil). Der Eingang habe ich noch "skaliert". Ich messe ja eine Frequenz (Wegimpulsgeber). w ist nun die Sollfrequenz (nicht Geschwindigkeit!). Wenn diese grösser als wh (wird extern berechnet, zb. mit 1.2*w) dann gebe ich einfach Vollausschlag auf den Eingang. Ist die Frequenz dazwischen wird diese linear abgebildet.
Nun zu meinen Fragen:
1. Was hält ihr von den oben geschilderten Bemerkungen? Sind die schlau? Oder ist man mit Gleitkommazahlen ebensoschnell? Schneller?
2. Ist der PID-Regler so richtig implementiert?
3. An einigen Stellen habe ich das "überlaufen" der Integer noch nicht verhindert. Wie macht man das schön? Und was kommt eigentlich raus, wenn man einer Variable vom Type Byte den Wert 5*100 (also ein Produkt welches Grösser als 2^8 -1 ist) zuweist? (ich habe auf dieser Maschiene leider gleich keinen Compiler um es auszuprobieren). Wie verhindert man das überlaufen am schönsten?
4. sonstige Bemerkungen? Was würdet ihr noch ändern?
// includes --------------------------------------------------------------------
#include <normlib.h>
#include "global.h"
#include "freq.h"
#include "tim.h"
#include "svo.h"
// local definitions -----------------------------------------------------------
#define BYTE_MIN 0
#define BYTE_MAX 255
#define WORD_MIN 0
#define WORD_MAX 65535
#define DWORD_MIN 0
#define DWROD_MAX 4294967295
#define INT8_MIN -127
#define INT8_MAX 127
#define INT16_MIN -32767
#define INT16_MAX 32767
// global variables ------------------------------------------------------------
// local variables -------------------------------------------------------------
word w,wl,wh; // w:Sollwort, wl: unterer Skalierwert, w2: oberer Skalierwert
byte zp,np,zi,ni,zd,nd; //Gewichtung des p-,i-,d-Anteil
word iSat; //Max/Min Val of ie; must be < INT16_MAX
dword t0; //timestamp of last execution
int16 e0; //e of last execution
int16 ie; //integrated e
// local function declarations -------------------------------------------------
inline static int16 calcE(); //positiv: too fast, negativ: too slow
inline static byte pid(int16 e, dword dt); //0: full power, 255: no power
// global function implementation ----------------------------------------------
void regelInit(){
e0=timGet();
ie=0;
}
void regel(){
svoSet(pid(calcE(),timGetDif(t0)));
t0=timGet();
}
// local function implementation -----------------------------------------------
inline static int16 calcE(){
int16 fDif=freqGet()-w;
if(fDif>0){
if(fDif>=wh)
return INT16_MAX;
else
return fDiff*(INT16_MAX+1)/wh;
}else{
if(er<=-wl)
return INT16_MIN;
else
return fDiff*(INT16_MIN-1)/wl;
}
}
inline static byte pid(int16 e, dword dt){
int16 p,i,d;
//proportional part
p=(e+INT16_MAX+1)*zp/np;
//intergral part
{
int32 tmp=e*dt/F_CPU; //dt must be <1s
if(tmp>0){
if((INT16_MAX-ie)<=tmp)
ie=INT16_MAX;
else
ie+=tmp;
}else{
if((INT16_MIN-ie)<=tmp)
ie=INT16_MIN;
else
ie+=tmp;
}
}
i=ie*zi/ni;
//differential part
d=(e-e0)*dt/F_CPU*zd/nd;
e0=e;
{
int32 tmp=((p+i+d)/(BYTE_MAX+1))+127;
if(tmp>=BYTE_MAX)
return BYTE_MAX;
if(tmp<0)
return 0;
return tmp;
}
}
// EOF -------------------------------------------------------------------------
Vielen Dank für eure Hilfe!
Und ich hoffe, dass all diejenigen, welche in Zukunft einen PID Regler für einen AVR in C suchen hier fündig werden :)
Grüsse
cumi