Posts Tagged ‘Arduino’

Improving the Beginner’s PID – Derivative Kick

Friday, April 15th, 2011

(This is Modification #2 in a larger series on writing a solid PID algorithm)

The Problem

This modification is going to tweak the derivative term a bit. The goal is to eliminate a phenomenon known as “Derivative Kick”.

The image above illustrates the problem. Since error=Setpoint-Input, any change in Setpoint causes an instantaneous change in error. The derivative of this change is infinity (in practice, since dt isn’t 0 it just winds up being a really big number.) This number gets fed into the pid equation, which results in an undesirable spike in the output. Luckily there is an easy way to get rid of this.

The Solution


It turns out that the derivative of the Error is equal to negative derivative of Input, EXCEPT when the Setpoint is changing. This winds up being a perfect solution. Instead of adding (Kd * derivative of Error), we subtract (Kd * derivative of Input). This is known as using “Derivative on Measurement”

The Code

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
void Compute()
{
   unsigned long now = millis();
   int timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
      double error = Setpoint - Input;
      errSum += error;
      double dInput = (Input - lastInput);

      /*Compute PID Output*/
      Output = kp * error + ki * errSum - kd * dInput;

      /*Remember some variables for next time*/
      lastInput = Input;
      lastTime = now;
   }
}

void SetTunings(double Kp, double Ki, double Kd)
{
  double SampleTimeInSec = ((double)SampleTime)/1000;
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
}

void SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}

The modifications here are pretty easy. We’re replacing +dError with -dInput. Instead of remembering the lastError, we now remember the lastInput

The Result

Here’s what those modifications get us. Notice that the input still looks about the same. So we get the same performance, but we don’t send out a huge Output spike every time the Setpoint changes.

This may or may not be a big deal. It all depends on how sensitive your application is to output spikes. The way I see it though, it doesn’t take any more work to do it without kicking so why not do things right?
Next >>

Creative Commons License

flattr this!

Improving the Beginner’s PID – Sample Time

Friday, April 15th, 2011

(This is Modification #1 in a larger series on writing a solid PID algorithm)

The Problem

The Beginner’s PID is designed to be called irregularly. This causes 2 issues:

  • You don’t get consistent behavior from the PID, since sometimes it’s called frequently and sometimes it’s not.
  • You need to do extra math computing the derivative and integral, since they’re both dependent on the change in time.

The Solution

Ensure that the PID is called at a regular interval. The way I’ve decided to do this is to specify that the compute function get called every cycle. based on a pre-determined Sample Time, the PID decides if it should compute or return immediately.

Once we know that the PID is being evaluated at a constant interval, the derivative and integral calculations can also be simplified. Bonus!

The Code

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastErr;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
void Compute()
{
   unsigned long now = millis();
   int timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*Compute all the working error variables*/
      double error = Setpoint - Input;
      errSum += error;
      double dErr = (error - lastErr);

      /*Compute PID Output*/
      Output = kp * error + ki * errSum + kd * dErr;

      /*Remember some variables for next time*/
      lastErr = error;
      lastTime = now;
   }
}

void SetTunings(double Kp, double Ki, double Kd)
{
  double SampleTimeInSec = ((double)SampleTime)/1000;
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
}

void SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}

On lines 10&11, the algorithm now decides for itself if it’s time to calculate. Also, because we now KNOW that it’s going to be the same time between samples, we don’t need to constantly multiply by time change. We can merely adjust the Ki and Kd appropriately (lines 31 & 32) and result is mathematically equivalent, but more efficient.

one little wrinkle with doing it this way though though. if the user decides to change the sample time during operation, the Ki and Kd will need to be re-tweaked to reflect this new change. that’s what lines 39-42 are all about.

Also Note that I convert the sample time to Seconds on line 29. Strictly speaking this isn’t necessary, but allows the user to enter Ki and Kd in units of 1/sec and s, rather than 1/mS and mS.

The Results

the changes above do 3 things for us

  1. Regardless of how frequently Compute() is called, the PID algorithm will be evaluated at a regular interval [Line 11]
  2. Because of the time subtraction [Line 10] there will be no issues when millis() wraps back to 0. That only happens every 55 days, but we’re going for bulletproof remember?
  3. We don’t need to multiply and divide by the timechange anymore. Since it’s a constant we’re able to move it from the compute code [lines 15+16] and lump it in with the tuning constants [lines 31+32]. Mathematically it works out the same, but it saves a multiplication and a division every time the PID is evaluated

Side note about interrupts

If this PID is going into a microcontroller, a very good argument can be made for using an interrupt. SetSampleTime sets the interrupt frequency, then Compute gets called when it’s time. There would be no need, in that case, for lines 9-12, 23, and 24. If you plan on doing this with your PID implentation, go for it! Keep reading this series though. You’ll hopefully still get some benefit from the modifications that follow.
There are three reasons I didn’t use interrupts

  1. As far as this series is concerned, not everyone will be able to use interrupts.
  2. Things would get tricky if you wanted it implement many PID controllers at the same time.
  3. If I’m honest, it didn’t occur to me. Jimmie Rodgers suggested it while proof-reading the series for me. I may decide to use interrupts in future versions of the PID library.

Next >>

Creative Commons License

flattr this!

Improving the Beginner’s PID – Introduction

Friday, April 15th, 2011

In conjunction with the release of the new Arduino PID Library I’ve decided to release this series of posts. The last library, while solid, didn’t really come with any code explanation. This time around the plan is to explain in great detail why the code is the way it is. I’m hoping this will be of use to two groups of people:

  • People directly interested in what’s going on inside the Arduino PID library will get a detailed explanation.
  • Anyone writing their own PID algorithm can take a look at how I did things and borrow whatever they like.

It’s going to be a tough slog, but I think I found a not-too-painful way to explain my code.  I’m going to start with what I call “The Beginner’s PID.”  I’ll then improve it step-by-step until we’re left with an efficient, robust pid algorithm.

The Beginner’s PID

Here’s the PID equation as everyone first learns it:

This leads pretty much everyone to write the following PID controller:

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastErr;
double kp, ki, kd;
void Compute()
{
   /*How long since we last calculated*/
   unsigned long now = millis();
   double timeChange = (double)(now - lastTime);
 
   /*Compute all the working error variables*/
   double error = Setpoint - Input;
   errSum += (error * timeChange);
   double dErr = (error - lastErr) / timeChange;
 
   /*Compute PID Output*/
   Output = kp * error + ki * errSum + kd * dErr;
 
   /*Remember some variables for next time*/
   lastErr = error;
   lastTime = now;
}
 
void SetTunings(double Kp, double Ki, double Kd)
{
   kp = Kp;
   ki = Ki;
   kd = Kd;
}

Compute() is called either regularly or irregularly, and it works pretty well. This series isn’t about “works pretty well” though. If we’re going to turn this code into something on par with industrial PID controllers, we’ll have to address a few things:

  1. Sample Time – The PID algorithm functions best if it is evaluated at a regular interval. If the algorithm is aware of this interval, we can also simplify some of the internal math.
  2. Derivative Kick – Not the biggest deal, but easy to get rid of, so we’re going to do just that.
  3. On-The-Fly Tuning Changes – A good PID algorithm is one where tuning parameters can be changed without jolting the internal workings.
  4. Reset Windup Mitigation –We’ll go into what Reset Windup is, and implement a solution with side benefits
  5. On/Off (Auto/Manual) – In most applications, there is a desire to sometimes turn off the PID controller and adjust the output by hand, without the controller interfering
  6. Initialization – When the controller first turns on, we want a “bumpless transfer.” That is, we don’t want the output to suddenly jerk to some new value
  7. Controller Direction – This last one isn’t a change in the name of robustness per se. it’s designed to ensure that the user enters tuning parameters with the correct sign.
  8. NEW: Proportional on Measurement – Adding this feature makes it easier to control certain types of processes

Once we’ve addressed all these issues, we’ll have a solid PID algorithm. We’ll also, not coincidentally, have the code that’s being used in the lastest version of the Arduino PID Library. So whether you’re trying to write your own algorithm, or trying to understand what’s going on inside the PID library, I hope this helps you out. Let’s get started.
Next >>

UPDATE: In all the code examples I’m using doubles. On the Arduino, a double is the same as a float (single precision.) True double precision is WAY overkill for PID. If the language you’re using does true double precision, I’d recommend changing all doubles to floats.

Creative Commons License

flattr this!

Arduino Uno Keeps Header Offset

Monday, September 27th, 2010

Uno Spacing

The newest version of the arduino was announced last week. At first glance it seemed as though they got rid of the offset header spacing, but a closer look at the spec showed that it is still 0.16 inches. It would have been a bold move to change that spacing; there are a lot of 0.16-spaced shields out there. Looks like Sparkfun will be selling offset headers for some time to come.

See also: Offset Header Explained

flattr this!

Success!

Saturday, October 10th, 2009

chugging along

Constant Air + Constant Heat = Constant Smoke. Who knew?

Constant Air

pre-patch JBpost-patch

Holes. There were tons of holes. To have complete control over the air I had to patch them all. Most of them were fairly easy. A little JBWeld and aluminum foil and I had rigid, (fairly) high temp patches. All the seams got a bead of JB for good measure as well.

repositioned ramp

The biggest hole of all was at the front, where the toaster door used to be. The box was essentially open. What I did there was rotate the entire box 90 degrees, making that opening the top. The ramp needed to be repositioned, but that wasn’t too hard. Now the gaping hole was on the exit side of the chamber, where leaks aren’t as important.

The cover for this hole was also upgraded. Where before it was covered using a big piece of foil, I finally used something better: a nice metal sheet with a 3″ outlet pipe.

Constant Heat

thermistorThermistor Location

In the previous attempt I had scrapped the stock themostat and switched to SSR control of the heating elements. I set it to a fixed value, walked away, and the whole thing promptly caught on fire.

This time around, I added a thermistor to the mix. It’s amazing what a little feedback can do. As far the control algorithm, I didn’t bother using the PID library (*gasp*.) For a process this simple all it took was a back-of-the-envelope P-only controller. It held the temperature and SSR output constant, and more importantly, things didn’t catch fire.

Vibration

As before, I used a blow-drier fan for vibration. This time, however, it was mounted on the OUTSIDE of the box. It vibrated well -10 seconds every 15 minutes- for the duration of the test. It also didn’t melt into a pile of goo, which was a definite plus.

Results

this is why we're here

I’m ecstatic.   All my success criteria have been met! The Smoke was consistent, and by restricting the air inlet I was able to adjust smoke density.

I put in a pound of wood (3 large chunks), and this thing ran for 5 hours straight before the smoke started to die down. There’s room for 3-4 Times as much wood in there, so an 8-16 hour run time is attainable.

Next Steps

As far as proving out the concept, I’m pretty much done. All that’s left for this phase of the project is to smoke some meat.

Beyond that I’d like to improve the design from a DIY standpoint; making it as easy to copy as possible.

flattr this!

DIY BBQ Smoke Generator – 2nd and 3rd Attempts

Friday, September 25th, 2009

I’ve had a few more gos at making smoke and, well, there’s still work to be done.

2nd Attempt

not bad for 3 bucks

There were two main issues with the first attempt. The smoke was intermittent, and I needed to manually shake the ramp. In the second attempt I tried to take care of both problems with one stroke.  I mounted a fan to the underside of the ramp. This would induce air circulation, and the spinning would vibrate the ramp. I settled on the fan out of a a hair-drier. The idea was appealing for several reasons:

  • An old hair drier is $3 at the thrift store.
  • A hair drier uses 120V AC.  I wouldn’t need a separate source to power it
  • It’s a fan.

Fan mounted to ramp

So I got the fan out and mounted it to the ramp.  It turns out that this idea wasn’t all that good.

  1. Mounting the fan in the middle of the ramp like that did little to improve circulation.
  2. The fan didn’t vibrate all that much
  3. It turns out the motor was actually DC.  They use the heater coil as a resistor to get the voltage they need, then rectify it with a diode.  It’s a brilliant, low cost solution for them.  It was a pain in the butt for me.
  4. The fan was LOUD.  You know, like a hair drier.

So other than learning how a hair drier is wiried, attempt 2 was pretty much a bust. I got the same smoke performance from a louder unit that needed a 12V power supply.

3rd Attempt

The fan didn’t really do much in the second attempt. It didn’t vibrate, and didn’t improve airflow. In the 3rd attempt I tried to fix both problems.

To increase the fan’s vibration, I decided to give it a little off-center weight. Inserting a screw into one of the blades shifted the center of gravity, and made it vibrate like a champ.  This didn’t do anything to help the noise / circulation issues though.  I try to be nice to my neighbors whenever I can, and 4-5 hours of vibrating hair drier noise just isn’t nice.

So I shifted gears. Instead of a constantly vibrating fan with on-off heat, I decided on an intermittent vibrating fan with more consistent heat. Since the initial smoke issues were caused by the On-Off heat control, maybe a more constant heat would lead to constant airflow / smoke. The Fan was relegated to vibration duty, turning on periodically to help the wood settle.

Shield

I built an arduino sheild (yes I used an offset header) to:

  • Send a 25% output to an SSR controlling the heaters. 
  • Vibrate the fan for 5sec every 10 min (more than enough to help the wood settle)

Great plan right?  I got everything set up, saw the smoke start, and walked away for 30 min.  Anyone know what happened next?  Anyone?  Yes.  You in the back.  The heaters added more and more heat to the chamber?  More than the air could remove,  even though the airflow was increasing with temperature?  You’re right!  I had suspected that this might happen, just not so quickly. 

…I came back to a fire.  It was contained within the box, and the box was alone on an a stone patio.  There wasn’t any danger, but the fan was cooked.  damn. Here’s some carnage photos where you can see the vibration screw:

Aftermath

Screw

So What Now?

I was hoping to be able to macgyver my way through this thing using aluminum foil and tape.  I’m going to have to up my game a bit.  In future attempts I’m going to start from overkill and work my way down, rather than the bottom up method I’ve been using.   So that means:

  • Really sealing the chamber.
  • Using an external fan
  • Using dampers to control airflow in and out.
  • Mounting the vibration mechanism OUTSIDE the fire box so it doesn’t get smoke/fire damage
  • More precise temperature control.  That’s right.  PID control.  I said overkill.  I meant it.

flattr this!

Sparkfun Now Carrying Offset Headers

Friday, August 14th, 2009

SparkfunHeader
The title says it all. Their picture is much nicer than mine too. That quarter or theirs really gets around.

Here’s a direct link to the product page.

flattr this!

Arduino Offset Header

Thursday, July 16th, 2009

The Arduino has a problem.
dimensions
Not a big problem by any means, but still annoying under certain circumstances. As the story goes, an 11th hour design mistake has left the Arduino community with a header that doesn’t follow standard 0.1″ (2.54mm) spacing.

So what?

misalignment

For the most part, this flaw is completely transparent to the user.  Either they plug wires directly into the header, or use shields that have been designed to mate nicely with it. The problem occurs when trying to create your own shield. When you try to line up a standard perfboard with the Arduino, the header doesn’t match up. This has left the community either buying protoshields or resorting to various other DIY techniques. (here’s two.)

An Easier Solution

Offset Header
offset header - standard shield
Thanks to my hackerspace, I’ve been able to machine a jig to make Offset Headers. In my opinion it’s a great solution to the problem. Slide one through the perfboard (or whatever other 0.1″ spaced board you’re using,) solder in place, and you have an Arduino-spaced shield using a standard-spaced board.

But wait… There’s more

offset header - arduino shield

So that takes care of the cheap-shield issue, but there’s more that this header can do. There are TONS of Arduino-spaced sheilds out there. In my opinion, that’s one of the main things keeping people from developing and buying standard-spaced Arduino clones. Currently, if you make your clone standard-spaced, you’re going to alienate all the existing Arduino shields.  Having these offset headers lets users buy a clone without fear of shield compatibility.

Lastly

Many Headers

So that’s it. Offset headers by the boatload.  Hopefully someone somewhere finds them useful. I’ve sent a preliminary batch over to Adafruit, so they should be available there shortly. 

(UPDATE:  they are now available here)
(UPDATE: SparkFun has them now too.)

flattr this!

PID Front-End v0.2

Saturday, July 4th, 2009

PID_FrontEnd_v02

Some people spend the 4th of July relaxing with friends, drinking beer, enjoying some good weather. I… updated the pid front-end. I got several requests to add grid lines and axes, and now was the time. For those of you with an image of me in a dank basement coding away, fear not. There was a Hawaiian shirt and a hammock involved.

The picture says it all. The trend lines are a little thicker, and there are now axis labels and grid lines. It’s also worth noting, since it was a pain to code, that the time gridlines and labels scroll, and can be displayed in milliseconds, seconds or minutes.

It can be downloaded here.

flattr this!

Arduino Reflow Toaster

Sunday, June 14th, 2009

Reflow
Pic 1: Arduino Controlled Toaster-Oven. No, I have not tried making toast with it

The Mark 1 reflow toaster is complete! (“Mark 1” is another way of saying there’s a lot of work to be done.)

It can hold +/- 1 °F, which should be sufficient for surface mount electronics. I say “should be” because I haven’t soldered anything with it yet.

Construction:

Reflow-Mast
Pic 2: Termistor support mast

Temperature sensing is done by a thermistor. Simple solder connections and 20ga wire get the signal back to the Arduino. (UPDATE: the thermistor is wired at the Arduino using this method.) The wires are run through a copper tube, and everything is held in place with JB weld. This setup seems to work, even at temperatures that melt solder. I don’t know if that’s because the JB keeps the solder connections from melting, or if it holds the molten solder in place during run-time. Either way, I haven’t had any problems getting temperature readings.

On the heat side, an SSR is spliced directly into the 120V plug wire. This is not safe. The SSR is fine, but having exposed 120V dangling near the toaster is a recipe for bad times. In the mark 2 that will all be neatly enclosed.

The toaster’s intrinsic temperature control was left in place, turned all the way up to 500°F.  If everything is working correctly, it will stay out of the way.  If something goes wrong, the temperature will be prevented from rising without bound.

Temperature Control:

I decided to use the Arduino PID Library to control the oven temperature. I think it works really well, but I’m a little biased.

Reflow_OnOff_vs_PID
Pic 3: Temperature control: On-Off vs PID. On-Off is easier, while PID provides tighter control with a more stable output

The easiest way to control temperature would be to use on-off control: “If I’m too cold, turn on. if I’m too hot, turn off.” I tried that first. As you can see in the picture above, it does a decent job. The oven temperature (red line) stays within 5 °F of where we want to be (green line.) This is probably fine in this application. The one questionable thing is the output. Since it’s cycling between full on and full off, there might be some extra thermal shock to the system.

On-Off control is probably fine, but as I said, I’m biased. I decided to implement pid control. As you can see, it does a better job of staying where we want to be. Instead of 5 degrees, now the temperature stays within 1 degree. Also, since the output is essentially constant, there’s a better chance that there will be a uniform temperature gradient in the oven.

The extra effort probably wasn’t necessary, but I still think it’s pretty cool.

Next Steps

  • I think the termistor may be in a bad location.  I wanted it to be really close to the workpiece, so I put it directly underneath where the workpiece would go.  it seems that when there’s a piece in place, it actually changes how heat flows in that area, and the temperature reading is almost 20 degrees higher.  I’ll need to see if this adversely impacts reflow…  once I actually reflow something.
  • I may need to upgrade to a convection toaster to get more uniform temperature distribution
  • currently I’m adjusting setpoints and watching progress using the PID frontend.  it would be nice to move that functionality onto the arduino. maybe an LCD and some buttons…
  • It might be cool to have a pre-programmed setpoint profile that the oven can cycle through

flattr this!