proportional-integral-derivative controller (PID controller) help

Started by
7 comments, last by haphazardlynamed 17 years, 9 months ago
Hi, i'd like to ask if you can post some pseudo-code to show how a PID controller work. Thanks.
Advertisement
Oh those are fun!
I use them a lot in my games...
spaceship thruster controls and whatnot


heres mine:
class pidcontroller{	float integral;	float lasterror;	float P,I,D;public:	pidcontroller(float p,float i, float d){		P=p;I=i;D=d;		integral=0;		lasterror=0;	}	float checkpid(float input,float setpoint){			float error=setpoint-input;		integral+=error;		float derivative=error-lasterror;		lasterror=error;		//printf("e%f i%f d%f\n",error,integral,derivative);		return ((P*error)+(I*integral)+(D*derivative));	}};


so with mine, i have it contained as a class.
and the P,I and D parameters are inputted in the constructor
then the Setpoint is stored externally by your program (it changes a lot so i dont bother having it be inside the PID itself)
so every gameloop i call checkpid, sending it the Input and the Setpoint, and it returns a float for me to use in the game

actually calculating good P,I, and D values is something you need to do yourself of course... I just tweak them by hand
Thank you very much for your help ! I bet finding the most accurate P,I and D values must be quite time-consuming, right ?
What i need is to use a PID controller to command aircraft elevators so that altitude is mantained say at 10,000 feet.
What P,I,D values could i use just to start experimenting ?
Thanks again.
Google has a nearly infinite number of links on tuning PID controllers.
Quote:Original post by Alessandro
Thank you very much for your help ! I bet finding the most accurate P,I and D values must be quite time-consuming, right ?
What i need is to use a PID controller to command aircraft elevators so that altitude is mantained say at 10,000 feet.
What P,I,D values could i use just to start experimenting ?
Thanks again.


be careful here: a PID, while not limited to, is designed mostly for second order systems, and i quite doubt a transferfunction from rudders to height is anything like second order.
Just for comparison, here is my PID controller class:

class PIDController{public:	/// Constructor	PIDController( float kp, float ki, float kd );	/// Updates the controller.	void Update( float sv, float pv, float dt );	/// Returns the current control value.	float GetControlValue() const	{ return m_ControlValue; }private:	float	m_ControlValue;	float	m_P;	float	m_I;	float	m_D;	float	m_E0, m_E1, m_E2;}; /// @param	kp	P parameter./// @param	ki	I parameter./// @param	kd	D parameter.PIDController::PIDController( float kp, float ki, float kd )	: m_P( kp ), m_I( ki ), m_D( kd ){	m_E0 = 0.f;	m_E1 = 0.f;	m_E2 = 0.f;	m_ControlValue = 0.f;} /// @param	sv	Set value. This is the target value./// @param	pv	Process value. This is the measured (or current) value./// @param	dt	The time elapsed since the last update.void PIDController::Update( float sv, float pv, float dt ){	// If the elapsed time is zero (or less) ignore this update.	if ( dt <= 0.f )	{		return;	}	// Only the last 3 error values are needed.	m_E2 = m_E1;	m_E1 = m_E0;	m_E0 = sv - pv;	float const	e01	= m_E0 - m_E1;	float const	e12	= m_E1 - m_E2;	float const	p	= m_P * e01;	float const	i	= m_I * m_E0 * dt;	float const	d	= m_D * ( e01 - e12 ) / dt;	m_ControlValue += p + i + d; } 

It is based on the information in this page: sci.engr.* FAQ on PID Controller Tuning
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
*looks at JohnBolton's PID*
*remembers important point*

Alessandro:
My PID is designed under the assumption that it be updated every gameloop, and that the gameloop has a Constant update frequency.
If you are dependant on screen framerate for your game update (that's bad practice BTW) it will mess mine up.

if thats the case, notice how JohnBolton's takes time as an additional argument, so it can compensate for variable timings
I have to confess that I had never heard of PID controllers. However, it is fairly clear that this is just a particular subclass of linear filters, where the coefficients of datapoints in the past have all constant weight, except for the current one and the previous (well, I guess you could estimate the derivative using more than two points, but let's ignore that).

Wouldn't it be better for most situations to have some kind of downweighting of things that happened too long ago? In haphazardlynamed's code, that would mean replacing the line
integral+=error;
by
integral = integral*.99 + error;
or something of that style.
Yes, some PIDs do that,
another common approach is to just cap the min and max values for Integral to avoid having it oversaturate with time.

I typically have the 'I' parameter as 0 anyway though, since I'm usually using it as a filter for user input vs thruster controls and stuff, where things change a lot and having an Integral parameter that tries to zero it at some constant point is bad...

This topic is closed to new replies.

Advertisement