I'm trying to implement a PID without floating point operations on a micro controller. I appreciate all feedback I can get.
Header file:
#ifndef BRUSHPID_H
#define BRUSHPID_H
#if defined(ARDUINO) && (ARDUINO >= 100) // Arduino Library
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define KU_CONSTANT 2
#define DT 100 // in milliseconds, descrete time step size chose to be 0.1[s]=100[ms]
#define PERIOD 6000 // in milliseconds, measured as 6[s]=6000[ms]
// Ziegler Algorithem Constants
#define ZIEGLER_P 0
#define ZIEGLER_PI 1
#define ZIEGLER_CLASSIC_PID 2
#define ZIEGLER_PESSEN_INTEGAL_RULE 3
#define ZIEGLER_SOME_OVERSHOOT 4
#define ZIEGLER_NO_OVERSHOOT 5
// Tuning Type
#define TUNING_TYPE ZIEGLER_P
// bit extension to avoid float operations
#define LONG_SHIFT 10
class BrushPID{
private:
unsigned long p;
unsigned long i;
unsigned long d;
signed long lCurrentError;
signed long lLastError;
signed long lTotalError;
signed long lMaxError;
signed long lMaxTotalError;
signed long caclulateNextOutput(void);
unsigned long ulLastTimeStamp;
void zieglerNicholasTuning(signed int iTuningMethod);
void resetTotalError(void);
public:
BrushPID(void);
~BrushPID(void);
signed long getNextOutput(signed long *newError);
};
#endif BRUSHPID_H
Source File:
#include "BrushPID.h"
#include "../BrushCommon/BrushCommon.h"
BrushPID::BrushPID(void) {
// init local object data
this->lCurrentError=0;
this->lLastError=0;
this->lTotalError=0;
this->ulLastTimeStamp=millis(); // timestamp since arduino boot in milliseconds
this->p=KU_CONSTANT;
this->i=0;
this->d=0;
//init PID paramaters according to chosen tuning type
this->zieglerNicholasTuning(TUNING_TYPE);
// calculate overflow boundry
this->lMaxTotalError=MAX_SIGNED_LONG-MAX_SIGNED_LONG/(max(this->i*DT,1)); // max to prevent division by 0
this->lMaxError=MAX_SIGNED_LONG-MAX_SIGNED_LONG/(max(max(this->p,this->d),1));
}
// destructor
BrushPID::~BrushPID(void) {
//nothing to do
}
// next step of discrete PID
long BrushPID::getNextOutput(long *newError){
//prevent overflows
if(lMaxTotalError<abs(this->lTotalError)) this->resetTotalError();
if(lMaxError<abs(*newError)) *newError=this->lLastError;
//check if DT has passed, by comparing current to last registered time stamp
while((millis()-this->ulLastTimeStamp)<DT);
//set new timestamp
this->ulLastTimeStamp=millis();
this->lLastError=this->lCurrentError;
this->lCurrentError=*newError;
this->lTotalError+=*newError*DT;
return caclulateNextOutput();
}
long BrushPID::caclulateNextOutput(void){
return (this->p*this->lCurrentError+this->i*this->lTotalError+this->d*(this->lCurrentError-this->lLastError)/DT)>>LONG_SHIFT;
}
/* ****
* Setting PID Parameter according to Ziegler-Nicholas tuning Method
* Source: http://en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method
****/
void BrushPID::zieglerNicholasTuning(int iTuningMethod=ZIEGLER_P){
unsigned long shifted_constant=KU_CONSTANT<<LONG_SHIFT;
switch(iTuningMethod){
case(ZIEGLER_P):
this->p=shifted_constant>>1;
this->i=0;
this->d=0;
break;
case(ZIEGLER_PI):
this->p=(shifted_constant*22)/10;
this->i=(this->p*10)/12/max(PERIOD,1);
this->d=0;
break;
case(ZIEGLER_CLASSIC_PID):
this->p=(shifted_constant*6)/10;
this->i=2*this->p/max(PERIOD,1);
this->d=(this->p*PERIOD)>>3;
break;
case(ZIEGLER_PESSEN_INTEGAL_RULE):
this->p=(shifted_constant*7)/10;
this->i=(shifted_constant<<1);
this->d=(shifted_constant*15)/100;
default:
this->p=shifted_constant;
this->i=0;
this->d=0;
}
}
void BrushPID::resetTotalError(){
this->lTotalError=0;
}
this->
business visually distracting. If you're coding in an IDE that can't highlight class members in a different color, I find that a simple leading underscore (naming convention) is enough to call out member data. I know this is a somewhat religious point, though. – Nate May 24 '13 at 3:15__
anywhere, and don't start anything with_
. – Yuushi May 24 '13 at 4:31__
), so there should be no confusion. If your code had a mixture of variables starting with__
, and_
, then there would be a source of confusion. But, you should only have ones starting with_
(single) in application layer code. Systems programmers would have different coding standards. – Nate May 24 '13 at 5:09