Go Down

Topic: Arduino PID Library (Read 108536 times) previous topic - next topic


Hey All,

This is my first post on the Fourms, but I have been playing with the Arudino for about 3 months now.

I am trying to learn PID using this library and have set up a circuit with a photoresistor and an LED.  This circuit works per the AnalogReadWrite built in example (changed to output on digital pin 3 however).  

I am having trouble making the PID library work at all.  I have set up the serial monitor to check the Output from the library, and I do not get a response.  Any help would be appreciated!

Code: [Select]

* 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,2,1);

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

 //turn the PID on

void loop()
 Input = analogRead(0);


Hi br3ttb,

I appreciate the work you have done on this topic.

If you make improvements like the using interrupts idea ... please keep us informed.


So I have got the PID working for my research project and I've got to say it's really helped a lot! I'm glad I considered the Arduino and this PID library, br3ttb this thing is awesome and very useful thanks for putting in the time to make this!

One concern though... I have to control a very very precise water pump that uses an analog input to control the speed of the pump.
here is the pump... Do you think the PWM will be able to simulate the analog signal well enough for something like this? I guess I don't really know how closely the PWM simulates analog....



it's hard to know if you could "fake out" the pump. that is, it wants 0-? volts, while you'd be rapidly pulsing 5V.  it might be worth looking into a DAC (full disclosure, I'm a Chem-E, and my electrical advice should be viewed with suspicion.)

this may be a good place to start: http://www.arduino.cc/playground/Main/InterfacingWithHardware (scroll down to ADC/DAC)



Jul 07, 2010, 04:14 pm Last Edit: Jul 07, 2010, 04:17 pm by zer0vector Reason: 1

I am building a simple temperature controller and I'm attempting to use your PID library.  The arduino reads the temperature from an LM35 and then outputs a slow PWM pulse to a solid state relay which will switch a standard electrical output.  I figure I'll plug in either a water bath heater or an electric smoker to the outlet, it seems like it should work just as well for either application.  Anyhow, I don't have much of the hardware yet (it's all on order), but I put together the code and I wanted to make sure I was utilizing the PID library correctly.  As you can see, I'm using the Timer1 library to run a 1 second PWM pulse.  I wouldn't think a heating element would need anything faster than that.  The PID output adjusts the duty cycle of the pulse:

Code: [Select]
#include <TimerOne.h>      
#include <PID_Beta6.h>

double Input, Output;                                  //PID variables
double Setpoint = 140;                               //PID setpoint in Farenheit

int thermPin = 0;                                    //input read pin for LM35 is Analog Pin 0
int controlPin = 9;                                           //Output PWM pin to Solid State Relay
int val;

PID pid(&Input, &Output, &Setpoint, 3,4,1);            //Setup the PID calculation

void setup()

        pinMode(controlPin, OUTPUT);              //Setup the pin to control the relay

        pid.SetOutputLimits(0,1023);             //tell the PID to range the output from 0 to 1023
        Output = 1023;                                     //start the output at its max  and let the PID adjust it from there
        pid.SetMode(AUTO);                               //turn on the PID

     Timer1.initialize();                              //turn on the Timer, default 1 second frequency
     Timer1.pwm(controlPin, (int)Output);      //start PWM on pin 9 with a 100% duty cycle

void loop()
        val = analogRead(thermPin);                                                        //read the value of LM35
        Input = ((5.0 * val * 100.0 / 1023.0) * 9.0 / 5.0) + 32.0;       //convert voltage to temperature(F)

        pid.Compute();                                                      //give the PID the opportunity to compute if needed

     Timer1.setPwmDuty(controlPin, (int)Output);            //Set the duty cycle of the PWM according to the PID output


Any comments or tips would be very much appreciated.



first off, there's a timer library?!  why did I not know this?

your code looks good.  be advised that you will need to adjust the tuning parameters (3,4,1) depending on what you're connected to.  it's unlikely that the default values will give you the response you want, or that the same values will work for both the water bath heater and smoker.



Thanks for the quick reponse!

Yeah, I understand about the tuning parameters, I guess I'll just have to eyeball things until I get them set up right.  Like I said, nothing is built just yet, it's still diagrams and connections floating around in my head.  I've heard it'll probably take a long time (hours? days?) to tune due to the slow response of my systems (I'm thinking the smoker will probably be faster than the water bath though).  

The Timer1 library is something I stumbled upon while trying to figure out how to slow down the arduino PWM.  I think I understood the software "window" method that you wrote into the second example on the PID library page, but it seemed like there must be a better way.  I don't claim to understand it completely, but it seems like just the ticket for what I'm trying to do:


Thanks again!



Question: I'm trying to put together a PID controller with a sample period of 1min or greater, because I'm controlling a heating element or fridge compressor that I don't want to switch on and off like crazy.

I'm running into problems when I try to set the sample time above 32767ms... which caused me to poke around and notice that there seems to be a discrepancy between SetSampleTime(int) and unsigned long tSample. Am I missing something, or should SetSampleTime() take an unsigned long?



Jul 10, 2010, 01:15 pm Last Edit: Jul 10, 2010, 01:19 pm by zer0vector Reason: 1
I'm not sure about the sampletime issue, the author is in probably a better position to answer that, but for a long PWM frequency like that, it's probably best you just write a window-like function similar to the one in the second example on the PID library page (he uses a 4 second frequency I think).  It doesn't matter how fast it samples, only how fast the output is switched.

Set outputlimits to 0 to 59 and declare a windowstarttime = millis()/60000 to get it in minutes.

Code: [Select]

if((millis()/60000)-windowstarttime > 60) windowstarttime += 60;

if(Output > (millis()/60000)-windowstarttime) digitalWrite(pin, HIGH);
else digitalWrite(pin, LOW);

I think that should do it.


Jul 10, 2010, 01:33 pm Last Edit: Jul 10, 2010, 01:36 pm by br3ttb Reason: 1
Am I missing something, or should SetSampleTime() take an unsigned long?

wow.  that is something I never thought would occur.  if I remeber correctly the backend variable (tSample) is an unsigned long because it's being compared to millis() and I wanted to make sure there were no casting issues.

in my experience, the only reason to make your sample time large is if you have to.  in plants with 20k pid loops there are legitimate processing issues, so they need to sacrifice on some loops and make the value bigger.  it didn't occur to me to allow it to be huge in the arduino setting.

instead of making the sample time really large, I'd recommend dealing with your concens on the Output side. you'll get your desired effect if you set up an output window as I did in Example 2 here: http://www.arduino.cc/playground/Code/PIDLibrary

you're free to set up a 10minute window, then have the pid tell you for how much of that window you should be on.  while the pid will be evaluating once a second (or every 30 seconds, whatever) your final control element will have a duty cycle of 10minutes.

and of course, the longer your duty cycle, the looser your control will be.  it's a balance you have to strike when you're using pid with an On/Off final control element.  that's why in industry it's generally connected to something that can range between 0-100%: valves, VFDs, gas flow to a burner, etc.

hope that helps,


Update: in the time it too me to craft my longwinded response, zer0vector jumped in with something much clearer.   ;)


Ah, ok, I haven't seen that before and should have read the example even though it didn't initially appear relevant.

So with that window logic, over the course of one window period, you're comparing the PID commanded control to an increasing ramp function, and turning the heat on or off depending on whether it's above or below. In a pathological scenario, couldn't your system respond in a way that made the control oscillate as often as every PID cycle? Granted, you would have to have horribly mis-tuned the PID controller, but it seems like it's generally inviting Murphy's Law if something else funny happens (the fridge door is left open?). Is it not worth adding some logic to enforce, say, only 2 control signal transitions during one window period?

And perhaps a better question for the chemical engineer is, are there specific rules of thumb on time separation of turning a fridge compressor on and off?

Thanks for the fast response!


first off,  I don't have any refrigerator-specific on/off style experience. the closest I've come is cryogenic distillation (which is AWESOME by the way.)  that isn't an on/off process though.  I can speculate with the best of 'em though.

In a pathological scenario, couldn't your system respond in a way that made the control oscillate as often as every PID cycle?

I have actually encountered this, and not just with a poorly tuned controller.  if the door is opened at just the right moment, the pid output could move in such a way that the compressor chatters.

this poses a tricky problem.   your transition enforcement fix would work, but if you're in minute 2 of your time window when the door opens, you want to respond NOW.  if you're restricted, you have to wait 8 minutes before the compressor kicks in to correct.  if that's fine, you're done.  if not...

maybe you could tie into the door open sensor; the one that turns on the light?  it seems that that's the major disturbance to the fridge process.  if you know when the door is open you can make more informed choices (e.g. chatter mitigation when the door opens, but allowing it to act as it wants as soon as the door closes)


I decided not to worry about minimum off or on time any further - I'm still leaving the thermostat my fridge came with intact, but cranked all the way up, and the the fridge cost less than my Arduino + OneWire temp sensor.

I agree that you'd want the compressor to turn on as soon as a disturbance is detected. At the same time, I'd like to insist on protecting against pathological disturbances. So, my solution allows a single "on" window during each PWM period, and that window is pushed to the end of the period. So, the compressor can turn on at any time if the PID suddenly increases the demanded control effort, and if the compressor is already on and the control saturates, the compressor will just stay on right into the next PWM period. It can also turn off ahead of original schedule if there's a negative disturbance. What do you think?

I'm currently cleaning up my code but I can post that if there's interest.


Jul 12, 2010, 07:03 pm Last Edit: Jul 12, 2010, 07:03 pm by br3ttb Reason: 1
my solution allows a single "on" window during each PWM period, and that window is pushed to the end of the period

I think that's an elegant solution for this process.  since your disturbance spikes will be increases in temperature, it's unlikely that you'll encounter a situation where the pid wants to suddenly back off.


By the way, why on earth is the I parameter equal to the reciprocal of the gain as it appears in the PID algorithm? This cost me a few hours to figure out, especially since there's a conditional that checks for 0, further complicating the behavior...  could this at least be very clearly documented/pointed out? Did I miss it somewhere?

Go Up