|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
|
|
Thread Tools | Rate Thread | Display Modes |
|
|
|
#1
|
||||
|
||||
|
PID vs Normal loops
One of my mentors assigned me a project for off season so we could get some better arm controllage going on, and he said to use a PID loop.
I'm still fairly new to C++ so bear with me. While I know little to nothing on Integrals and Derivatives, this is confusing to me. I understand that PID loops are used to narrow things from point A to point B without overshooting or gyrating while going as fast as possible. I found some old PID code for our drive system last year, but I can't really make heads or tails of it. And all the while, I don't really see a need for all that math, couldn't you do something such as Code:
#include <math.h>
#define p1 = pwm01; //Pot 1
#define p2 = pwm02; //Pot 2
#define dvr = pwm03; //Motor
int dist;
void Correct();
int Clamp(int var, int lBound, int mBound);
void Correct(){
if (!p1 == p2){
while (p1 > p2){
dist = Abs(p1 - p2);
dvr = 127 + Clamp(dist, 127, 255);
}
while (p1 < p2){
dist = Abs(p1 - p2);
dvr = 127 - Clamp(dist, 0, 127);
}
}
}
int Clamp(int var, int lBound, int mBound){
if (var < lBound)
return lBound;
if (var > mBound)
return mBound;
else
return var;
}
|
|
#2
|
||||
|
||||
|
Re: PID vs Normal loops
Quote:
This is where most people start when working on a control loop, and in some cases it will be good enough. What you will likely find, however, is that when p1 starts to get close to p2, your motor output will be so small that the motor may not actually move. Your instinct may then be to multiply "dist" by 2 to make the motor run faster, but if you keep doing this you'll eventually get to the point where your arm continuously overshoots it's target (this would be the equivalent of increasing the "P" constant in a PID loop). PID is all about trying to make a control loop that moves a motor to a desired position as quickly as possible but also have it stop when it's supposed to without overshooting. |
|
#3
|
|||
|
|||
|
Re: PID vs Normal loops
Read this. It helped me out A LOT! http://www.embedded.com/2000/0010/0010feat3.htm
In math, function line is whatever is on your graph in the present, the derivative is the slope of a line at any point in time, and the integral is how much area fits under a curve in a given time range. In robots, the present (P term) is where you are right now, the integral (I term) is where you have been, and the derivative (D term) is how fast you are getting to where you are going. If you know these three things, you can decrease the amount of time it takes to get to your set point, and decrease overshoot and settling time once you get there. Doing control without the I and D terms is like driving a car with no rear view mirror or speedometer. Sure you know where you are on the road, but you have no idea when to speed up or apply the brakes. Last edited by Tom Bottiglieri : 10-12-2007 at 11:20. |
|
#4
|
||||||
|
||||||
|
Re: PID vs Normal loops
Kevin Watson published a working example of PID for the FIRST controller in his 2005 code. http://www.kevin.org/frc/2005/ Download the navigation_frc2005_01_22.zip and looks at the pid.c and .h files.
|
|
#5
|
||||
|
||||
|
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);
}
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);
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); 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)); 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. |
|
#6
|
||||
|
||||
|
Re: PID vs Normal loops
Has anyone tried any other alternatives? I've read that PID is not necessarily the best but it's relative simplicity makes very useful. You can create a PID controller out of analog components.
|
|
#7
|
|||||
|
|||||
|
Re: PID vs Normal loops
You can try a Bang-bang_control as an alternative. It works like a thermostat on a furnace. What you pick as a controller really, really, really (did I say really?) depends on the application. In 2006, we used a bang-bang controller on the turret of our poof ball shooter for 2 competitions. Then we switched it to a PID for Nationals. In the end, they both worked, but we got better shooting accuracy with the PID. Eric |
|
#8
|
||||
|
||||
|
Re: PID vs Normal loops
Quote:
|
|
#9
|
||||
|
||||
|
Re: PID vs Normal loops
Quote:
Hence, an augmentation of the PID controller. I don't have the code with me, but let me explain to you how it works. First, I tuned up a really nice, very touchy PID. Touchy being it really goes nuts with a little bit of error (in the order of an inch). Then, here's where things get cool: I feed a stream of interim coordinates into the PID to generate trapezoidal velocity control while still arriving at the target. This is very useful for extreme long travels with large masses (like driving a robot from its starting position and arriving (smoothly) at the rack). I think you can kind of understand how this works: if a stream of coordinates is fed into a pid loop at a constant interval (each time the PID updates, in my case @100Hz) you feed a new, short range target into the PID. These targets are no more than a fraction of an inch apart in my case, remember they get fed in at a rate of 100Hz. That occurs during the acceleration phase: these ramps can be precalculated or calculated at acceleration execution time... all you need is a little 1/2*A*T^2 action. When acceleration or deceleration is complete, you switch to a constant position delta so as to keep the speed constant. Deceleration functions the same way as acceleration. Now, you may be thinking "hey, that's best suited for autonomous runs where distances can be pre calculated. what if my final target changes dynamically?" Well, I'm working on that one. My goal is to have a fully mathematical (no precalculation) formula set that handles acceleration, constant run speed, and deceleration. v2 that I'm working on now isn't ready yet, but it operates on a summation of three PID loops, one watches acceleration, one watches velocity, and one watches position simultaneously. To decide which should be listened to, a selection structure is set up which lets whatever PID routine has the lowest power output solution access to the motor. Everything but one part works beautifully in simulation: i'm still trying to figure out how to hande deceleration without the interim-position method explained above. Next on the list to be explored is state-space control which allows feedback from multiple sensors to factor into a single control loop and plant (motor or otherwise) action... but I might have to wait to have my curiosity satisfied till i'm in college a little while... I'm not sure how to do a Laplace Transform... Questions? Post! -q |
|
#10
|
|||
|
|||
|
Re: PID vs Normal loops
You may not, but Google does
![]() |
|
#11
|
|||
|
|||
|
Re: PID vs Normal loops
A cool idea would be to "learn" the (approximately) optimum PID constants over time...
You can simulate your robot arm pretty easily using a set of equations characterizing the mass distribution, and motor torques. Next create several test goal and end states within reachable bounds of the system (a state would be angle + angular momentum for each arm segment) Do a search through the set of all PID combinations to find the best one.To test optimality, simply use the PID constants and run them on your simulation, use a metric for how good the performance is (power usage, time to reach goal etc..) . It shouldn't take more than a few minutes on a modern computer. (of course you don't want to try every possible combination since it's infinte, but discretize the selection based on how large or small the term is probably going to end up being) If you need help programming this feel free to post, it isn't half as complicated as it seems. In fact maybe I will make something that does this over winter break lol. Last edited by Salik Syed : 12-12-2007 at 01:29. |
|
#12
|
||||
|
||||
|
Re: PID vs Normal loops
Or you could just use a rough-tuning method that's been used for decades: Ziegler-Nichols
Or, you could do something kind of like Salik said: If you have a decent physical sense about you, or just happen to have a physics book, a four-function calculator (or pad and paper) and rougly figure out what kind of numbers you need for the constants. For instance: set your P term based on how fast you want something to move for a maximum speed, with the biggest motion you might give it... aka if you are doing just a positional servo (for an arm or something) the biggest move you make you'll want to output the maximum control output for the motor... so you'd multiply (or divide) by whatever number you need to get your error to around 127 (or whatever max plant value you might have). D terms can be done similarly, but I terms are usually more of a gut feel if your doing "Tune by Feel" though they can be calculated relatively easily as well if you factor in your cycle times and characterists of your motor and the load it's moving. See previous post for more on augmentations of PID. -q |
|
#13
|
|||
|
|||
|
Re: PID vs Normal loops
Q,
The idea of using the preferred response to tune a PID is interesting. Once I'm done getting other components of our coprocessor up and running I will probably try that as a tuning method and see how well it works. Your idea detailed last page is also very interesting. I think it's important to decide what your goals are - lowest time to reach the target or reaching it smmothly. I don't know yet which will seem more important for this year's challenge, but until now I am tempted to say that the time it takes to go from position a to position b might be more important than the smoothness, particularly for a servo or autonomous position control. However, I am wondering about human controlled velocity PID - would you rather have a slight overshoot as you attempt to reach the target velocity, or would you prefer to reach it slower but more smoothly, perhaps taking a bit longer to get there. The idea of having a fully mathematical computation of to handle constant accceleration and constant velocity is interesting. Again, I might mess around with that a bit before or during the build season, depending on how much time I end up having. If I do, I'll be sure to let you know what results I get. I'm also currently reading about Laplace transforms and State-Space controls (amongst other control systems) to see maybe there's a reasonably easy alternative to PID. |
|
#14
|
||||
|
||||
|
Re: PID vs Normal loops
Quote:
On the human control side... has anyone actually implemented this with success in competition? I've found that drivers always say it 'feels wierd'... so usualy what ends up happening is I write code so that if the robot has neutral control input for a set period of time, a servo kicks in to hold it in that spot. Has anyone successfully implemented this? Wow I can't wait to see what the challenge is... I really hope it's more exciting that the game last year... -q |
|
#15
|
||||
|
||||
|
Re: PID vs Normal loops
There's another one which a former math/CS prof at Stanford is working on. I've read a draft of the paper, but it's not ready to be published yet. I'm hoping that it'll be ready for use this season, but it might not be.
I can't go into all the details, because I don't have them in my head (and don't yet fully grok them when on paper in front of me), but at a high level, the concept is this, assuming one dimensional control: - All motion is a sine wave. - First derivative of position is velocity; second derivative is acceleration; third derivative is jerkiness - The derivative of a sine wave is a sine wave - There are four variables that define a sine wave - frequency, amplitude, phase and offset (here's where the magic comes in: ) It's possible to map position, velocity, acceleration and jerkiness to the four variables defining the sine wave. You can then place various constraints on three of those variables, and then solve the corresponding differential equation to find a path that meets all those constraints. You re-solve this every clock tick and execute the plan, and it should give you smooth, damped motion control, without the need for the "tuning" that PID control requires. Unfortunately, I can't answer any questions about this or explain in more detail, because I simply don't know. But I'm hoping to learn, and once I get this new algorithm working in an FRC controller, I'll be sure to let everyone know. |
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Hobbies, normal and unusual | Jill1022 | Chit-Chat | 61 | 29-04-2005 08:33 |
| PID control loops - closed loop feedback | KenWittlief | Technical Discussion | 56 | 26-04-2004 21:27 |
| Well, is it normal for me not to be in FIRST Robotics and post here? | JKis6622 | Chit-Chat | 3 | 13-03-2004 15:46 |
| PID Control Loops | ttedrow | Programming | 7 | 05-12-2002 12:03 |
| Programming Loops | Mike o. | Programming | 5 | 26-03-2002 11:24 |