PID settings on arduino based hlt

Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum

Help Support Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

SimBrew

Well-Known Member
Joined
Dec 19, 2010
Messages
86
Reaction score
0
Location
Montréal
I got a arduino controlled hlt with 1500w element heating like 5 gal.

I overshoot my setpoint by about 10 F.

The integral is too slow to shut the element off once it get to the setpoint

My process class are far away and I don't have a lot of time with the kid to test differents settings.

What are your PID settings on setup like mine ?
 
SimBrew said:
I will try for sure.
Would you happen to have your autotune code ?

I'm pretty sure I do. It's the same as what's right there on the site, though, with two minor exceptions.

- I added what I needed to get a Fahrenheit setpoint and readout in the serial monitor from a DS18B20

- I did a cut & paste swap of the part from the 'to control a relay' example, also located on that site.

I have *not* been successful getting the front end up in Processing, though. I just opened up a serial monitor in the Arduino IDE.
 
FOREVER! Or so it seems. Maybe 6 heat/cool cycles? In my crockpot, that must have been a couple of hours or so.

I watched Hudson Hawk while it ran.
 
Oh... if this is obvious, forgive me for bringing it up.

Watch that last line just before void setup(), it will run a simulation until you change that value to false.
 
FOREVER! Or so it seems. Maybe 6 heat/cool cycles? In my crockpot, that must have been a couple of hours or so.

I watched Hudson Hawk while it ran.

Thanks. I tried running it last night on my brewery and it about 2 hours for it to get to the setpoint once. My setpoint is at 154. The output would only go to 50. I will look at my code and try running it again tonight.

When I first ran i had it in simulation mode as you said and took me a couple of minutes to find the line to change it to tuning mode.
 
The integral is too slow to shut the element off once it get to the setpoint

This is a slow, integrating temperature loop. You don't want a lot of integral gain in this type of loop, because it will cause unnecessary controller windup.
Integral is not going to do anything at setpoint. More derivative can help to decrease overshoot. In fact, more proportional will help get your output high enough when you are far from setpoint but bring it down as you approach setpoint.

All of these suggestion are assuming your heating element is appropriately sized.
 
TO chuckjaxfl:

Do you mind sharing your code. I cant get the auto tune portion to work. I don't have a problem reading temperatures and even have the arduino send outputs to processing but cant seem to get the auto tune to work correctly.

Thanks in Advance.
 
Edited per the post below. Thanks for the tip!

Code:
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>

#include <OneWire.h>
int DS18S20_Pin = 8; //DS18S20 Signal pin on digital 8
//Temperature chip i/o
OneWire ds(DS18S20_Pin); // on digital pin 8

#define RelayPin 3
int relayPin = 3;
int WindowSize = 1000;
unsigned long windowStartTime;


byte ATuneModeRemember=2;
double input=80, output=50, setpoint=100;
double kp=2,ki=0.5,kd=2;

double kpmodel=1.5, taup=100, theta[50];
double outputStart=5;
double aTuneStep=500, aTuneNoise=1, aTuneStartValue=500;
unsigned int aTuneLookBack=200;

boolean tuning = true;
unsigned long  modelTime, serialTime;

PID myPID(&input, &output, &setpoint,kp,ki,kd, DIRECT);
PID_ATune aTune(&input, &output);

//set to false to connect to the real world
boolean useSimulation = false;

void setup()
{
  //Setup the pid 
  myPID.SetOutputLimits(0, WindowSize);
  myPID.SetMode(AUTOMATIC);


  if(useSimulation)
  {
    for(byte i=0;i<50;i++)
    {
      theta[i]=outputStart;
    }
    modelTime = 0;
  }

  if(tuning)
  {
    tuning=false;
    changeAutoTune();
    tuning=true;
  }
  
  serialTime = 0;
  Serial.begin(9600);
  
  windowStartTime = millis(); //from pid test Relay
  pinMode(RelayPin,OUTPUT);  //from pid test Relay

}

void loop()
{

 unsigned long now = millis();
 float temperature = getTemp();
 float tempF = temperature * 9/5 + 32;

  if(!useSimulation)
  { //pull the input in from the real world
    input = tempF;
  }
  
  if(tuning)
  {
    byte val = (aTune.Runtime());
    if (val!=0)
    {
      tuning = false;
    }
    if(!tuning)
    { //we're done, set the tuning parameters
      kp = aTune.GetKp();
      ki = aTune.GetKi();
      kd = aTune.GetKd();
      myPID.SetTunings(kp,ki,kd);
      AutoTuneHelper(false);
    }
  }
  else myPID.Compute();
  
  if(useSimulation)
  {
    theta[30]=output;
    if(now>=modelTime)
    {
      modelTime +=100; 
      DoModel();
    }
  }
  else
  {
 //    analogWrite(0,output); // commented out by me
   unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(output > now - windowStartTime) digitalWrite(RelayPin,HIGH);
  else digitalWrite(RelayPin,LOW);
  }
  
  //send-receive with processing if it's time
  if(millis()>serialTime)
  {
    SerialReceive();
    SerialSend();
    serialTime+=1000;
  }
}

void changeAutoTune()
{
 if(!tuning)
  {
    //Set the output to the desired starting frequency.
    output=aTuneStartValue;
    aTune.SetNoiseBand(aTuneNoise);
    aTune.SetOutputStep(aTuneStep);
    aTune.SetLookbackSec((int)aTuneLookBack);
    AutoTuneHelper(true);
    tuning = true;
  }
  else
  { //cancel autotune
    aTune.Cancel();
    tuning = false;
    AutoTuneHelper(false);
  }
}

void AutoTuneHelper(boolean start)
{
  if(start)
    ATuneModeRemember = myPID.GetMode();
  else
    myPID.SetMode(ATuneModeRemember);
}


void SerialSend()
{
Serial.print("setpoint: ");Serial.print(setpoint); Serial.print(" ");
Serial.print("input: ");Serial.print(input); Serial.print(" ");
Serial.print("output: ");Serial.print(output); Serial.print(" ");
  if(tuning){
Serial.println(" tuning mode ");
//    Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" ");
//    Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" ");
//    Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println();
  } else {
    Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" ");
    Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" ");
    Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println();
  }
}

void SerialReceive()
{
  if(Serial.available())
  {
   char b = Serial.read(); 
   Serial.flush(); 
   if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune();
  }
}

void DoModel()
{
  //cycle the dead time
  for(byte i=0;i<49;i++)
  {
    theta[i] = theta[i+1];
  }
  //compute the input
  input = (kpmodel / taup) *(theta[0]-outputStart) + input*(1-1/taup) + ((float)random(-10,10))/100;

}

float getTemp(){
 //returns the temperature from one DS18S20 in DEG Celsius

 byte data[12];
 byte addr[8];

 if ( !ds.search(addr)) {
   //no more sensors on chain, reset search
   ds.reset_search();
   return -1000;
 }

 if ( OneWire::crc8( addr, 7) != addr[7]) {
   Serial.println("CRC is not valid!");
   return -1000;
 }

 if ( addr[0] != 0x10 && addr[0] != 0x28) {
   Serial.print("Device is not recognized");
   return -1000;
 }

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

 
 for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
 }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];

 float tempRead = ((MSB << 8) | LSB); //using two's compliment
 float TemperatureSum = tempRead / 16;
 
 return TemperatureSum;
 
}
 
I hope it's helpful!

I'm not a programmer, so if it looks butchered it's because I'm pretty new to Aruidino. Like I said in another post, the last thing I wrote was in "Clipper" for dBase III.
 
I like that some code is starting to show up in this forum!

FYI- You might want to wrap that code using the [ CODE ] [ /CODE ] tags (the # button under advanced). I believe it will make it easier to copy/paste.

Code:
Example of wrapped code
line 1
line 2
line 3
 
I hope it's helpful!

I'm not a programmer, so if it looks butchered it's because I'm pretty new to Aruidino. Like I said in another post, the last thing I wrote was in "Clipper" for dBase III.

Thanks Chuckjaxfl. No worries on the code. I am a hack when it comes to programming as well. I won't be able to try it out until Thursday.
 
Where did you get your ds18b20 probe, or did you build your own? Also, can you describe how you will mount it in your hlt or on the outlet of the hlt? For example, are you using a compression fitting and a T fitting?

-jbrew
 
Where did you get your ds18b20 probe, or did you build your own? Also, can you describe how you will mount it in your hlt or on the outlet of the hlt? For example, are you using a compression fitting and a T fitting?

-jbrew

I got my sensors at Sensors

I got my thermowell from Auberins ThermoWell I took connector off and pulled the thermocouple out then ran a drillbit through the it and the DS1820 probes fit great.
 
Where did you get your ds18b20 probe, or did you build your own?

Give www.brewershardware.com a look if you're looking for DS18B20 sensors (listed under "BrewTroller" sensors). His are pretty top notch.

I used his probe ends but put together my own sensors with a right-angle M12 connector on the end of them. I really like this setup better than any others I've had in the past.

M12_Probe1.jpg


M12_Probe2.jpg
 
Hopefully, I'll have better answers for you guys today. I'm working on it today.

The part that is pertinent to this thread is that I changed the Serial.print parts to work on the LCD (no biggie), and I changed a couple lines in the working code. Specifically, I changed the aTuneStep to 200, and the aTuneStartValue to 250. I'm hoping this will bring the rise & fall of the autotune to a reasonable rate, instead of the huge spike and the take-forever fall I experienced before.

I'll let you know how it goes.
 
chuckjaxfl said:
Hopefully, I'll have better answers for you guys today. I'm working on it today.

The part that is pertinent to this thread is that I changed the Serial.print parts to work on the LCD (no biggie), and I changed a couple lines in the working code. Specifically, I changed the aTuneStep to 200, and the aTuneStartValue to 250. I'm hoping this will bring the rise & fall of the autotune to a reasonable rate, instead of the huge spike and the take-forever fall I experienced before.

I'll let you know how it goes.

Okay, this went over like a fart in church.

I guess the numbers will work, eventually! I have hard time accepting that my PID should just be a P with no ID.

I'd try it, throw some more cold water in and see, but something just quit working in the can, so I'll have to fix that first.

Frustrating DIY day.

image-2676334983.jpg
 
It's official... I'm no help.

The autotune library continues to give me values similar to the above for P.. and zeros for I & D.

I spent some time last night reading about PID loop tuning. I'm going to post in the DIY section and see if I can get some help.
 
Hey Chuck!!!
Any results on the I and D numbers yet? You know I am watching to sneak a peak at where you end up too. I am using the REX C-100's and have been hard at assembling the rest of the circuit boards and relays to support this system. I decided to learn C and went with the PIC 16f877A 40 pin chip for a developement and prototype of the system. Keep us up with your results please..
Wheelchair Bob
 
No... I'm close to giving up the faith on the autotune. I'm really frustrated with it.

The only idea I have right now is to drag my the Arduino IDE, my sketches, etc, and everything over to my windows laptop so I can drag my laptop out to the garage. I'll have to tolerate using Windows, but that will at least allow me to make modifications on the fly using the Processing front end, if I can get that up and running. I'll have to rebuild my PCB on a breadboard, too, so I can use my Arduino Nano to get speak to the laptop. As it is, I'm pulling the chip from my homemade PCB, bringing it into the house and installing it into my UNO... writing a change to it, then carrying it back out and reinstalling it. It's a pain in the butt, and make me wish I'd installed a ZIF socket on my PCB because the pins are getting worn out.

Then I guess I'm going to have to use Processing to adjust the tuning manually. I'm reading a lot about it. So far, it looks pretty unachievable. There are a few methods. The one with the most data available (Ziegler-Nichols) appears to allow overshoot, and stabilizes in "a few cycles" which, for us, may be 1/2 through a brew session. There's another method (Labmda) that appears to work like my Auber pids (nice ramp up to temp with little-no overshoot), but the information that is available assumes you are a controls engineer and already understand all the terms.

I'm also considering "cheating", hooking up my Auber PID, running autotune with it, and copying the parameters over. But, I tried that when I was working with the crockpot, and settings for one controller don't equal the settings in another. For instance, the "I" may mean "resets per second" for one, and "seconds per reset" in the other. Same with "D".

It all makes me want to give up and just put the damn Auber PID in there.

In any case, I'm sorry to the OP for hijacking your thread. I think, though, that my solution will also be your solution.
 
Looks like the auto tune is trying to guestimate using the Ziegler-Nichols method (increase P till the system oscillates, then fix P,I,D based on some simple approximations). It might be easier just to do it yourselves.

http://en.wikipedia.org/wiki/PID_controller#Ziegler.E2.80.93Nichols_method

Or, just make your own PWM based control based on the temp differential and a certain K factor
 
Marc,
The Wiki on PID's was outstanding. That was a very timely link and an even better explanation. Sometimes WIKI goes over my head, but this one made enough sense that I could follow most of it and it really helped me understand the concept of wind-up and what the parameters were really for. Thanks a bunch, it really did help me a lot!!!
Wheelchair Bob
 
Or, just make your own PWM based control based on the temp differential and a certain K factor

Sounds like that's what he already has--a Proportional only controller.

Have you tried adjusting some of the parameters for the auto tune, like aTuneStep, aTuneNoise, or aTuneStartValue? Noise could definitely cause issues. Also, you want to try to perform the autotune in the middle of the operational range where your system probably has the most linearity. You are likely to experience non-linearity at limits.
 
I have had the same experience with the PID library, I don't think it is very suited for water heating because it lets I term ramp up to 100% of the output and the only thing that will reduce it is an overshoot of the setpoint. The I term is there to remove the constant error that will be there with a P drive only. We are also dealing with a system that can only heat so if it does overshoot we just have to let it cool down so the PID has no control over that. With such a slow system the derivative has no real effect because the rate of change is very small, especially with a fast sample rate.
What I have done is to have the heat output on until 5 degs c from the set point, I then hand it over to the PID to bring the temperature to the setpoint. Doing it this way stops the I term from winding up too much.

here is part of the code.

Code:
  myPID.SetOutputLimits(0, 100);
  myPID.SetSampleTime(5000);
  myPID.SetTunings(50,0.01,10);

void PID_HEAT (void){
  if((Setpoint - Input)>5){
    digitalWrite(Heat,HIGH);
    if ((Setpoint - Input)<6)
   {
    myPID.Compute();
    }
  }
  else{
  myPID.Compute();
  unsigned long now = millis();
  if(now - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if((Output*(WindowSize/100)) > now - windowStartTime) digitalWrite(Heat,HIGH);
  else digitalWrite(Heat,LOW);
  }
}

I have set the output to 100 to represent 100%
I have changed the sample time to 5 seconds, on my system I can set the window size from 500mS to 5000mS, so with a window size of 5000mS and an output of 50 the heat output will be on for 2500mS and off for 2500mS.
With P set to 50 and an output limit set to 100 that means the output is always on until 2 deg c error then it starts to ramp down.
I trialled it with 20l and a 2400w element and it ramped from 25 deg c to 52 deg c at 1.5 deg / min and came up to the set point and didn't overshoot
I work in deg c because thats what we use down here but I sure you guys can convert to deg F. I hope this helps a bit, here is the full code for my brauduino controller which is designed to mimic a braumeister controller

https://github.com/mathoaus/braumiser-controller/blob/master/brauduino2.ino

cheers steve
 
I have been using Arduino for about 2 years as my PID for my mash. I manual set my parameters by trial and error and help from the PID tuning wiki.

When I heard there was an auto tune library for the arduino I tried it but never got it to work. I am happy with the way my responds and reacts I was just wanting to see if autotune could improve it.

Here are my setting for PID maybe it helps some of you out.
P: 450,
I: 461.53
D: 0

Below is graph of my last batch I brewed.

The blue line on the bottomw is the PWM of the HERMS element. 255 is 100% and is on the y axis on the right. X axis is in minutes.

mash.jpg
 
I have had the same experience with the PID library, I don't think it is very suited for water heating because it lets I term ramp up to 100% of the output and the only thing that will reduce it is an overshoot of the setpoint. The I term is there to remove the constant error that will be there with a P drive only. We are also dealing with a system that can only heat so if it does overshoot we just have to let it cool down so the PID has no control over that.
The derivative term is what helps prevent a large amount of overshoot. A PID controller is certainly capable of controlling a integrating type temperature loop like this. This type of temperature loop shouldn't have a lot of integral gain it will just cause the controller to windup as you have pointed out. It should have a decent amount of P to get you moving toward the setpoint, only enough I to help you get to set point and a good amount of D to prevent significant overshoot.

With such a slow system the derivative has no real effect because the rate of change is very small, especially with a fast sample rate.
If the derivative term isn't doing anything it means that you don't have a large enough derivative term. You might be surprised how large a derivative gain may need to be to be effective.

PIDs are ubiquitous in industrial control systems. Their use in a heating applicationd similar to this is very common.
 
Hey fastTalker,
I never said that PID control isn't suited for heating water, I said I don't think the arduino PID library is very well suited for heating water.
here are my thoughts on the library, I could be wrong.

With the default sample time set to
Code:
    SampleTime = 100;

that would mean using a DS18B20 with a 12 bit resolution the conversion time is about 750mS the Iterm would have 7 increases before D could even get calculated, it gets worse when you put in the time it takes to ramp the temperature of the water. If the rate of rise is 2 deg c / min then that equates to 0.033 deg c / second, the finest resolution for a DS18B20 is 0.0625 degc so that means a measured temperature change every 2 seconds which would allow Iterm to increase 20 times before D would have a value

Code:
void PID::Compute()
{
   if(!inAuto) return;
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
	  double input = *myInput;
      double error = *mySetpoint - input;
      ITerm+= (ki * error);
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput;
      
	  if(output > outMax) output = outMax;
      else if(output < outMin) output = outMin;
	  *myOutput = output;
	  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
   }
}

If I term is there to remove a constant error that persists with a P drive only, which it is, why is it allowed to ramp to 100% of the output? If you had a constant error that required 100% output then your drive system is too small. I believe that for D to really be effective it should be subtracted from I term like this

Code:
ITerm-=(Kd*dInput);
double output = kp * error + ITerm;

that would allow D to stop Iterm from winding up if the rate of change is large enough.

Code:
void PID::SetTunings(double Kp, double Ki, double Kd)
{
   if (Kp<0 || Ki<0 || Kd<0) return;
 
   dispKp = Kp; dispKi = Ki; dispKd = Kd;
   
   double SampleTimeInSec = ((double)SampleTime)/1000;  
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
 
  if(controllerDirection ==REVERSE)
   {
      kp = (0 - kp);
      ki = (0 - ki);
      kd = (0 - kd);
   }
}

The last thing is if you increase the sample time the D gain is reduced and the I gain is increased.
I believe that the PID library would be well suited for fast responding symmetrical drive systems like motors but for heating water it isn't quite right but can be worked around.
Again, these are just my thoughts and I could be wrong as I'm not an expert on PID systems

cheers steve
 
Just caught this thread and wanted to chime in. I haven't yet messed with it but I did find this in line 26 the PID_v1.cpp file of the PID library

Code:
SampleTime = 100;	//default Controller Sample Time is 0.1 seconds

Since for heating large volumes of water there is massively small changes in the I and D calculation over 0.1 seconds of time the Ki and Kd terms need to be high in order to show any effect. For our situation, it might be advisable to change that to 10,000 or at least mess with other orders of magnitudes to get Ki and Kd in line with the process.

edit: and looks like i read through everything except for the last page and Matho was already on the same path.... but again, since the SampleTime term only plays with how Ki and Kd interact with the setpoint, a longer sample time could make the PID library much more effective for our application
 
Hey fastTalker,
I never said that PID control isn't suited for heating water, I said I don't think the arduino PID library is very well suited for heating water.
here are my thoughts on the library, I could be wrong.

With the default sample time set to
Code:
    SampleTime = 100;

Code:
void PID::Compute()
{
   if(!inAuto) return;
   unsigned long now = millis();
   unsigned long timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
	  double input = *myInput;
      double error = *mySetpoint - input;
      ITerm+= (ki * error);
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput;
      
	  if(output > outMax) output = outMax;
      else if(output < outMin) output = outMin;
	  *myOutput = output;
	  
      /*Remember some variables for next time*/
      lastInput = input;
      lastTime = now;
   }
}

If I term is there to remove a constant error that persists with a P drive only, which it is, why is it allowed to ramp to 100% of the output? If you had a constant error that required 100% output then your drive system is too small. I believe that for D to really be effective it should be subtracted from I term like this

Code:
ITerm-=(Kd*dInput);
double output = kp * error + ITerm;

cheers steve

Ok First im by no means an expert on PIDs as I recently just started learning, but there are a few things about your statements that are off.

First with the setpoint, that is the default setpoint, the library has a function to change it, which when libraries include functions like that, its because they can/should be changed to meet your needs. obviously this guy wasnt making this for brewing when he made this so his default isnt going to be what we need. To fix this call:
Code:
myPID.setSampleTime(5000)
and put in what you want for the delay, im going to initially test this with a 5 second delay as not much changes over 5 seconds with 5+gallons of water, you could probably do 10 and be fine.

Second your second point with the I input isnt correct, not the PID part but how he does it. If you compare your code

Code:
ITerm-=(Kd*dInput);
double output = kp * error + ITerm;

with the one provided

Code:
ITerm+= (ki * error);
      if(ITerm > outMax) ITerm= outMax;
      else if(ITerm < outMin) ITerm= outMin;
      double dInput = (input - lastInput);
 
      /*Compute PID Output*/
      double output = kp * error + ITerm- kd * dInput;

You see that the kd*dInput is included further down.

Again Im not an expert on PIDs and know little on them, however, I do know code very well.

A library is a base for the general use of the feature. It is not all powerful and cant be designed for every case. PIDs are used for tons of different situations so it would be impossible to make a library that requires no changes to work for everything. Tons of people have been using this PID for sous vide applications and have gotten great results, Im pretty sure it works for mashing as well.
 
Hi

Be very carefull with the term PID. Around this froum it gets used to describe *any* digital process control. When you look into what is actually being done, well over 90% of the controlls are setpoint rather than PID.

To be very clear, A setpoint is a PID with no I =0 and D=0. To be properly called a PID both terms need to be non-zero. You can have PI controllers (pretty common), or PD controllers (pretty rare) as well as PID's and set points.

Bob
 
Hi

Be very carefull with the term PID. Around this froum it gets used to describe *any* digital process control. When you look into what is actually being done, well over 90% of the controlls are setpoint rather than PID.

To be very clear, A setpoint is a PID with no I =0 and D=0. To be properly called a PID both terms need to be non-zero. You can have PI controllers (pretty common), or PD controllers (pretty rare) as well as PID's and set points.

Bob
Be very careful reading threads. Most ones on the subject of a PID library, well over 90%, are actually talking about software. In fact, to be called a PID library, the code would actually have to be software.

These guys are just trying to determine how to hack a PID library to make it work for homebrewing.
 
Be very careful reading threads. Most ones on the subject of a PID library, well over 90%, are actually talking about software. In fact, to be called a PID library, the code would actually have to be software.

These guys are just trying to determine how to hack a PID library to make it work for homebrewing.

Hi

If one is trying to make sense out of a control system, it might be a good idea to understand the nomenclature involved before digging into it. By far the majority of people around here are *not* actually using a PID. That's not going to make things any easier to understand.

Bob
 
Hi

If one is trying to make sense out of a control system, it might be a good idea to understand the nomenclature involved before digging into it. By far the majority of people around here are *not* actually using a PID. That's not going to make things any easier to understand.

Bob

By far the majority of people around here are *not* actually using a PID.
Exactly- they are using a PID library. That is why offering a simple solution, like appropriate time constants or I and D gain values is preferable to confusing the issue with even more technical jargon. Your input would have been relevant if you had supplied P, PI, and PD libraries, since there probably aren't any given that the PID library is easily configured to revert to those modes using environment variables, parameters, constants, etc.

They just want it to work, and if they want to know more, it is better to give an appropriate link, as was done earlier in the thread, with more complete explanations.
 
Hey fastTalker,
I never said that PID control isn't suited for heating water, I said I don't think the arduino PID library is very well suited for heating water.
here are my thoughts on the library, I could be wrong.

With the default sample time set to
Code:
    SampleTime = 100;

that would mean using a DS18B20 with a 12 bit resolution the conversion time is about 750mS the Iterm would have 7 increases before D could even get calculated, it gets worse when you put in the time it takes to ramp the temperature of the water. If the rate of rise is 2 deg c / min then that equates to 0.033 deg c / second, the finest resolution for a DS18B20 is 0.0625 degc so that means a measured temperature change every 2 seconds which would allow Iterm to increase 20 times before D would have a value
If I understand what you are saying, then I have to disagree. The integration logic is taking the sample time into account. The smaller the sample time the, the smaller the increase of the I term of the equation every update of the PID. You will integrate more accurately the faster you update the PID output. If you are familiar with calculus then this should be very fundamental.

The I term and the D term calculations use the update time to correctly calculate the rate of change. Notice in the SetTunings() function these lines:
Code:
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
It is calculating instantaneous derivative. Technically, it would be more accurate to use the ACTUAL update time rather than the target update time. However, it will probably be close enough. You should make sure that your Arduino scan time is significantly faster than your PID update time though.

If I term is there to remove a constant error that persists with a P drive only, which it is, why is it allowed to ramp to 100% of the output? If you had a constant error that required 100% output then your drive system is too small. I believe that for D to really be effective it should be subtracted from I term like this

Code:
ITerm-=(Kd*dInput);
double output = kp * error + ITerm;

that would allow D to stop Iterm from winding up if the rate of change is large enough.
In the PIDs most basic form there is no limit to what each of the three terms can contribute to the output. So you limit the I term to keep the PID from winding up so high that the P and D have no effect on the output. This would allow you to have an I only controller if you wanted. The constant error you are referring to is the STEADY-STATE error you get with a P only controller.
And, unless I'm missing something here, isn't that what it is already doing.

There is no reason to update the controller slower. If you are capable of updating the controller faster, you should.

It seems you are over complicating this. A PID updates the output based on:
P - your error now
I - how long you have had error
D - how fast your error is changing

If it's not working change your gains, not the controller.

On another note, I don't agree with how the auto-tune functionality works. Closed-loop tuning is hard to do automatically. It would probably produce better results if it attempted to do an open-loop step change test to measure the first-order lag and dead-time.
 
..... It would probably produce better results if it attempted to do an open-loop step change test to measure the first-order lag and dead-time.

Hi

...and possibly consider how to improve (reduce) the lag and dead time before you spend a lot of effort on tuning things. In some systems it's easier to generate a known impulse (short duration power on) rather than a step. Either an impulse or a step can be used to get the basic lag and gain information you need.

Lag = how long before it got moving.
Gain = how much did you make it move with that input

Bob
 

Latest posts

Back
Top