[ODE] Servo to reach a target position

David Whittaker david at csworkbench.com
Thu Apr 10 14:51:01 2003


Noticed a bug.... see below (the fix:'s).  Unless I'm really confused, you
need to divide the Error by the timestep instead of a constant 10 in the
CalcI function.

Conceptually, it seems to me that the I calculation should only take into
account the integral of the error over the past (N) seconds.  As opposed
to the total error since the dawn of time...  But I could be wrong.

David

>>I'm be interested!  Not so much for ODE, but because I tried to write
>> one for an unrelated project and got nowhere.  Ended up coming up with
>> something that does work well, and fits what I thought PID meant, but
>> actually looks nothing like what a later learned PID was. :-)
>
> Here's the code, tho I haven't had a chance to test it very extensively.
> Be wary the I calculation may be wrong, but it works for me :)
>
> Also remember, 95% of applications only need P, and 99% only need P and
> D.
>
> // pid.h
>
> #ifndef PID_H
> #define PID_H
>
> #include <math.h>
>
> class PID
> {
> 	// Kp = Proportional gain
> 	// Ki = Integral gain
> 	// Kd = Derivative gain
> 	// Isum = accumulated error for I-term
> 	// E = saved Error from previous calculation
> 	// MaxE = maximum error signal seen
> 	// dT1 = saved dT from previous calculation
> 	// OV = Output Variable
> 	float Kp, Ki, Kd, Isum, E, MaxE, dT1, OV;
> public:
> 	PID ( float Kp_=0, float Ki_=0, float Kd_=0 )
> 	{
> 		Reset();
> 		SetGain ( Kp_, Ki_, Kd_ );
> 	}
>
> 	void Reset()
> 	{
> 		Isum = 0;
> 		E = 0;
> 		dT1 = 0;
> 		MaxE = 0;
> 		OV = 0;
> 	}
>
> 	void SetGain ( float Kp_, float Ki_=0, float Kd_=0 )
> 	{
> 		Kp = Kp_;
> 		Ki = Ki_;
> 		Kd = Kd_;
> 	}
>
> 	// SP = destination/target
> 	// PV = feedback
> 	// dT = time elapsed since last update
> 	// return value = command output
> 	float StepPosition ( float SP, float PV, float dT )
> 	{
> 		float E1 = E; // previous error
> 		E = SP - PV; // new error
>
> 		float Op=0, Oi=0, Od=0; // P, I, and D terms of calculation
>
> 		// P-Loop
> 		Op = CalcP ( E );
>
> 		// I-Loop
> 		Oi = CalcI ( E );
fix: 		Oi = CalcI ( E, dT );
>
> 		// D-Loop
> 		Od = CalcD ( E, E1 );
>
> 		// Final Summation
> 		OV = Op + Oi + Od;
>
> 		dT1 = dT; // save this dT to be previous dT for next time
>
> 		return OV;
> 	}
>
> 	// SP = destination/target
> 	// PV = feedback
> 	// dT = time elapsed since last update
> 	// return value = command output
> 	float StepSpeed ( float SP, float PV, float dT )
> 	{
> 		float E1 = E; // previous error
> 		E = SP - PV; // new error
>
> 		float Op=0, Oi=0, Od=0; // P, I, and D terms of calculation
>
> 		// P-Loop
> 		Op = CalcP ( E );
>
> 		// I-Loop
> 		Oi = CalcI ( E );
fix: 		Oi = CalcI ( E, dT );
>
> 		// D-Loop
> 		Od = CalcD ( E, E1 );
>
> 		// Final Summation
> 		OV += Op + Oi + Od;
>
> 		dT1 = dT; // save this dT to be previous dT for next time
>
> 		return OV;
> 	}
>
> private:
> 	float CalcP ( float E )
> 	{
> 		return E * Kp;
> 	}
>
> 	float CalcI ( float E )
fix: 	float CalcI ( float E, float dT )
> 	{
> 		if ( Ki > 0 )
> 		{
> 			Isum += E / 10;
fix: 			Isum += E / dT;
> 			if ( fabs(E) > MaxE )
> 				MaxE = (float)fabs(E);
> 			if ( Isum > MaxE )
> 				Isum = MaxE;
> 			else if ( Isum < -MaxE )
> 				Isum = -MaxE;
> 			return Ki * Isum;
> 		}
> 		else
> 			return 0;
> 	}
>
> 	float CalcD ( float E, float E1 )
> 	{
> 		if ( Kd > 0 )
> 		{
> 			float dE = E - E1; // change in error
> 			return Kd * E;
> 		}
> 		else
> 			return 0;
> 	}
> };
>
> #endif//PID_H
>
> // pidtest.cpp
>
> #include <stdio.h>
> #include "../pid.h"
>
> void main()
> {
> 	PID pid ( 0.1f );
> 	float position = 0;
> 	for ( int i = 0; i < 50; i++ )
> 	{
> 		position += pid.StepPosition ( 10, position, 0.1f );
> 		printf ( "%.02f ", position );
> 	}
> 	printf ( "\n\nfinal position: %.02f\n", position );
> }
>
> // end of pidtest.cpp
>
> _______________________________________________
> ODE mailing list
> ODE@q12.org
> http://q12.org/mailman/listinfo/ode