Arduino PID Library

Neurasonic asked for a story on PID control. Here's my take.
PID stands for proportional, Integral, differential. What do THEY stand for? Start with comparing what you have with what you want with what you have. The difference is called the error signal. If you use only this error signal to drive the output, you have a proportional controller (if the error signal is linear or proportional). This can be OK, but there HAS to be an error to keep driving the output, so it's never exactly right.
OK, how about toting up the small difference, time after time, when you are as close as you can get with the proportional circuit, and driving the output with THAT too? That can work and hit the desired output on the nose.
Now what about if we next want a step change in output?
If we grow restless with a fast start, and a slower slower approach to the desired output, we can get bold. By monitoring the rate that the error signal changes we reduce the time to reach the target value, and (hopefully) minimize the over shoot and hunting chances.
Having said all that PID controllers can be a pig to tune. Some people find fuzzy logic controllers are much easier to understand and to set up.

Brian Whatcott
Altus OK

Having said all that PID controllers can be a pig to tune. Some people find fuzzy logic controllers are much easier to understand and to set up.

Having worked before retiring in a oil refinery I can agree that loop tuning a PID controller can sometimes be a challenge. It's not so much that it is difficult but rather it can be so time consuming that people lose patience.

One problem is that there can be such a process time delay, especially for temperature loops, that it takes some time to see if a specific tuning change made an improvement or made it worst. Some controller manufactures have added auto-loop tuning features into their PID controllers and a few I worked with worked pretty well.

Lefty

Hi Brett

Thanks for the great library! It's been flawless on my project.

I noticed in a few of your posts you mentioned you didn't think SetInputLimits would be used much. I just wanted to let you know that without it, I couldn't have used the library so easily. My process input is actually a value I'm reading on RS232 and converting to a weight from 0-0.5 grams. I was able to set in input limits to 0, 0.5 and it works perfectly.

My next project will probably use one of the Maxim thermocouple to digital chips, and I'll once again use the setinputlimits function. I vote to keep this in the code!

Thanks for your contributions,
Nigel

So many replies, so little time:

RodW - using 4 speeds is probably the safest way to go from an equipment standpoint. in that case, the easiest thing to try first might be an output divider. whatever the pid is asking for, divide by the fan speed(1-2-3-4) and send that to the temp setting. as the temp setting (not the pid output) gets close to the max, bump up your speed to the next level. if it gets close to the min, drop the speed down one. dividing the output will have a Feedforwardesque (totally a word) effect. the signal to the temp setting will instantly be changed when the speed changes.

Brian - I thought it stood for Proportional, Integral, Do not use. :wink:

Brian & Lefty - I find that the blessing and the curse of the PID is it's ease of configurability. you can get a control loop to "good 'nuf" pretty quickly. the problem is that sometimes that's a ways away from "good." I'd say, as far as arduino is concerned, simple fuzzy logic is the right place to start. that can get real complicated real quickly though, and at that point it's time for this library (IMO)

Nigel - Fear Not! I'm not getting rid of Input Limits by any means. I just separated them out into a separate function (SetInputLimits) to help the uninitiated. this is already complicated enough.

Brett

br3ttb,

You might want to include a default constructor in your next build. This might be my lack of experience with this platform but the following would not compile:

include <WProgram.h> 
include<PID_Beta6.h>
class foo{
public:
PID myPID;
}

Apparently it would not compile b/c it could not find a matching function call to PID();

I am new to this particular platform, so I could be missing something, however to get this to work in my class I had add the default constructor to your code and recompile.

Otherwise your package looks good.

I never considered that people would be embedding my class into another class. COOL!

The reason I didn't go with a default constructor is that for the pid to run correctly, it HAS to have the things specified in the current constructor (input, output, setpoint, tuning parameters.) I figured that would be a gentle way to get people to specify them.

maybe a middle of the road option would be to include a default constructor but have it commented out? if you're writing a new class you'll likely have the know-how to find/ uncomment. if you're the standard Arduino user you'll notice no change. thoughts?

Brett

[his example 1]


  • PID Simple Example
  • Reading analog input 0 to control analog PWM output 3
    ********************************************************/

#include <PID_Beta6.h>

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,5,1);

void setup()
{
//initialize the variables we're linked to
Input = analogRead(0);
Setpoint = 100;

//turn the PID on
myPID.SetMode(AUTO);
}

void loop()
{
Input = analogRead(0);
myPID.Compute();
analogWrite(3,Output);
}


[Your non-working example]
include <WProgram.h>
include<PID_Beta6.h>
class foo{
public:
PID myPID;
}


I guess I don't understand your critique - your example stuff doesn't look like his example stuff????

Can you 'splain the problem in words of at most two syllables? :slight_smile:

Brian W

Class? The only thing I recall about class is that if your late too often you could receive saturday detention. :wink:

Lefty

Brian:

psyber is using the library in a way I never even considered. Most people will take the pid library and use it right in their Arduino program, the way my example illustrates.

the way I understand it, psyber encountered a problem trying to embed a pid within a library of his own creation. On the library creation side, the rules are a little different. To make it work, he needed to -very lightly- massage the pid library code. His suggestion was aimed at saving the next guy some trouble.

Lefty: This is all I know about class :wink: : Sidney Poitier - IMDb

Brett

Here is another vote for making it easier to embed the PID library in another class. I was in the process of creating a motor control class which would embed a PID controller to set the PWM signal to the motor when I saw psyber's post.

Edit:
It may not be necessary to add a default constructor. I haven't tested this yet but it does compile.

test.pde

#include<PID_Beta6.h>
#include "foo.h"

foo myFoo;

setup()
{
}

loop()
{
}

foo.h

class foo
{
public:
   foo();

   PID myPID;
   double Input;
   double Output;
   double Setpoint;
}

foo.cpp

#include "PID_Beta6.h"
#include "foo.h"

foo::foo() : myPID(&Input, &Output, &Setpoint,2,5,1)
{
   // initialize foo
}

I had to look up initialization lists, in my real life I've never used them. In another environment I'd include a pointer to a PID object in my class and use new to create it in the constructor.

I has use this libary to control angle of robot arm, it work as well on fixed setpoint.

if my setpoint is change it will not go to setpoint, it have offset. i try to increase D parameter, i would not help.

i guess my problem is my motor start moving at PWM 80%. when is start moving it's very fast and on next sampling it already overshoot.

anyone have some idea to slove this. :wink:

on next sampling it already overshoot. anyone have some idea to slove this.

you can use the SetSampleTime() function to make the pid sample more quickly. (Arduino Playground - PIDLibraryAdvancedMethods)

Brett

I want to make a motor controller that uses distance as the target but is able to accelerate until reaches a certain speed (sometimes is max speed), continue with that speed until almost at destination and decelerate until it reaches the target distance and stops. Sometimes I need to make small adjustments and speed never reaches the set speed and needs to decelerate because it reaches the destination.
Any advices on how to do that?
Thanks.

Can this PID library include a function to accelerate from 0 to SetSpeed and continue at that speed until the SetPoint is reached (because it will automatically decelerate before reaching the SetPoint)?

I have tried to do a function that accelerates until Speed = SetSpeed and only then activate the PID controller, but sometimes I need to make small corrections so the Speed never reaches SetSpeed. Can this be done in the PID controller?

I don't think so. the pid is a "simple" control element whose job is to make Input = Setpoint. once you want to do more complicated things, you'll need to make the pid part of a larger control strategy. it sounds like that's what you're trying to do anyway, so let's talk about that.

First, I'm not sure I completely understand your application. You use both SetSpeed and SetPoint in your question. I'm would assume that Speed is the output from the pid, and Setpoint is the value you want the PID Input to achieve. I'm not sure though.

I don't want to give you bad advice at this point. Could you give a description of what it is you're trying to do in the real world (i.e. "I've got this thermonuclear jetpack, and I'm tyring to control take-off velocity. my inputs and outputs are...") I'll be able to give better advice once I know what it is you're trying to do.

Brett

foo.cpp
Code:

#include "PID_Beta6.h"
#include "foo.h"

foo::foo() : myPID(&Input, &Output, &Setpoint,2,5,1)
{
// initialize foo
}

I had to look up initialization lists, in my real life I've never used them. In another environment I'd include a pointer to a PID object in my class and use new to create it in the constructor.

Nice! I figured there was some way to do it without monkeying around with the original library. I just wish there was a more intuitive method as my C/C++ is extremely rusty. I don't think this is covered in any of my old textbooks either. >:(

br3ttb:
As for the middle ground solution, I see no difference between you adding the code and then commenting it out, and having a developer simply add the code. In either case someone still has to edit the class and rebuild the library.

I hate to resurrect this old thread, but it is a good one, and the PID software looks really good.

Before finding this thread, I wrote my own. I was particularly interested to see how wind-up was handled. I saw that the Integral term was not incremented when the output was maxed.

This seems like a great idea, but doesn't it cause a bit of a glitch when the output drops down from max? Maybe the integral term is well below the value resulting in maximum output due to some other factor such as the differential.

Is this really going to make much of a glitch under these circumstances? I suspect that a "properly" tuned loop will not have such odd behaviour that the differential can push the output that far up/down.

I attempted to limit wind-up by not allowing the integral term to exceed the value that results in full output on its own. Is this not good enough?

-Tony

doesn't it cause a bit of a glitch when the output drops down from max?

I haven't noticed this. the inclusion of the (err>0) and (err<0) are supposed to (and seem to) keep this from happening

limit wind-up by not allowing the integral term to exceed the value that results in full output on its own. Is this not good enough?

the short answer is "yes." It's not as simple with the form of the pid I'm using, because I have a bias term in the mix. I'm in the process of migrating the library toward the method you describe.

Brett

Brett, nice library and easy to use and tidies up my code nicely. Any news on the 'lite'version for basic functionality?

Cheers,
Mike

Any news on the 'lite'version for basic functionality?

I've actually decided to ditch the idea of a lite version. Instead I'm working on reducing the footprint of the full version. The next revision of the library is almost done. it uses integer math throughout, so it's about half the size and 10X faster.

Brett

[edit]On second thought I may be able to add some preprocessor flags to remove a lot of the extra functionality, resulting in a lite library. I'll see what I can do[/edit]

Sounds brill! Look forward to using it.

Mike