View Single Post
  #5   Spotlight this post!  
Unread 10-12-2007, 13:43
lukevanoort lukevanoort is offline
in between teams
AKA: Luke Van Oort
no team
 
Join Date: Oct 2005
Rookie Year: 2005
Location: Waterloo, ON, Canada
Posts: 1,873
lukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond reputelukevanoort has a reputation beyond repute
Send a message via AIM to lukevanoort
Re: PID vs Normal loops

I'm surprised nobody has mentioned it yet, but another good PID resource is Matt Krass's whitepaper on the topic.

Once you understand PID and start to code with it, I would reccommend writing a generalized function for PID, and then use a typedef structure to store the data for each application of the PID code. That was probably a bit confusingly worded, so here's an example. This is the PID code that ran on our 2007 robot in the offseason (you really don't want to see the code during the season... it was really bad and never worked right)
pid.c:
Code:
/*******************************************************************************
* FILE NAME: pid.c
*
* DESCRIPTION:
*  This file contains a generic PID function, and functions necessary to 
*  make the PID work.  
*
* USAGE:
*  You can either modify this file to fit your needs, or remove it from your 
*  project and replace it with a modified copy. 
*
*******************************************************************************/
#include "ifi_aliases.h"
#include "ifi_default.h"
#include "ifi_utilities.h"
#include "pid.h"
#include "user_routines.h"



void PID_Initialize (PID_STRUCT* pid_info, int Kp_value, int Ki_value, int Kd_value, int imax_value)
{
//intialize ze values of ze pid structair
pid_info->Kp = Kp_value;
pid_info->Ki = Ki_value;
pid_info->Kd = Kd_value;
pid_info->imax = imax_value;
}


unsigned char PID (PID_STRUCT* pid_info, int error)
{
int P;
int I;
int D;

P = (((long)error * (pid_info->Kp))/ 1000);
I = (((long)(pid_info->error_sum) * (pid_info->Ki)) / 10000);
D = (((long)(error - (pid_info->last_error)) * (pid_info->Kd)) / 10);

pid_info->last_error = error;

if(!disabled_mode)
pid_info->error_sum += error;

if (I > pid_info->imax)
	pid_info->error_sum = pid_info->imax;
else if (I < -pid_info->imax)
	pid_info->error_sum = -pid_info->imax;


return Limit_Mix(2000 + 132 + P + I - D);
}
pid.h
Code:
/*******************************************************************************
* FILE NAME: pid.h
*
* DESCRIPTION:
*  This file contains a generic PID function, and functions necessary to 
*  make the PID work.  
*
* USAGE:
*  You can either modify this file to fit your needs, or remove it from your 
*  project and replace it with a modified copy. 
*
*******************************************************************************/

typedef struct {
	int Kp; // In tenths
	int Ki; // In thousandths
	int Kd; // In tenths
	int last_error;
	int error_sum;
	int imax;
} PID_STRUCT;


void PID_Initialize (PID_STRUCT* pid_info, int Kp_value, int Ki_value, int Kd_value, int imax_value);
unsigned char PID (PID_STRUCT* pid_info, int error);
Now what this code does is let you just write PID code once to save time. So, say I wanted to use this code to make a PID loop for both sides of the drivesystem as well as a lift. I would then add
Code:
PID_Initialize(&auton_lift, Kp_lift, Ki_lift, Kd_lift, imax_lift);
PID_Initialize(&auton_left_drive, Kp_l_dr, Ki_l_dr, Kd_l_dr, imax_l_dr);
PID_Initialize(&auton_right_drive, Kp_r_dr, Ki_r_dr, Kd_r_dr, imax_r_dr);
to initialize the PID for those robot parts. (The initialization would probably be put in the User_Initialization() function in user_routines.c. Then to run PID on these functions, all that is needed is to type
Code:
		LIFT_MOTOR = PID(&auton_lift, (Get_ADC_Result(LIFT_POT) - LIFT_UNF));
LEFT_DRIVE = PID(&auton_left_drive, (Get_Encoder_1_Count(LEFT_ENCODER) - LEFT_GOAL));
RIGHT_DRIVE = PID(&auton_right_drive, (Get_Encoder_1_Count(RIGHT_ENCODER) - RIGHT_GOAL));
which is much easier than writing three seperate PID functions. If you are controlling one thing with PID, it probably would make sense to just write your PID function specific to that thing, but if you are controlling more (I believe seven things were intended to be controlled by PID on our 2007 robot... only one actually ended up using PID though; most other uses of PID got nixed due to sensor failure or lack of debug time) then this is a much cleaner, more efficient, and faster way to code.

If you don't understand the above, don't worry. I definitely wouldn't have when I first started programming FIRST robots. So, if it doesn't make sense now, come back and read it again after you learn more about C and PID; it should make sense then. (If not, PM me)

PS: If you are familiar with an object-oriented language, the above is basically an attempt to code in C using OOP principles.
__________________
Team 1219: 2009 - Mentor
Team 587: 2005 - Animator, 2006-2008 - Team Captain