Getting to know PID

Update 4/10/2012

It amazes me how I thought I finally understood it and yet I run across another challenge which get me to become humble to how difficult this skill is to master. This year we attempted to have 4 closed loop systems, drive, turret, pitch, and power-wheel. The pitch was never created, and the turret had issues with using an encoder to maintain a reliable position. The drive worked pretty well, but then it hit me like a ton of bricks when we didn't have the ability to apply a parking brake. I finally get the difference between brake and coast mode as this can apply in handling the PID. So for the drive I found I can apply reverse force to make it stop, and I should have used this for both the drive and the arm. Fortunately the algorithm I used worked great with coast, so I could keep the power-wheel using this. The power-wheel was the real challenge as it had many problems to overcome. The first is due to how GetRate() starts to lose precision on the higher than 50rps speeds. It gets worse the faster it goes, which made it necesary to use the Kalman filter. Luckily, that helped smooth it out, but then the tuning was tricky. The biggest discovery on this was how much better it behaved when multiplying the error times the time slice. While I now have to use high coefficients, I've found it worth the change, so I've now enabled this for all the coefficients. I also discovered how to use 'D', as I thought before it was evil, but really this helps to dampen the oscillation artifacts. So with a high P and D and a low I, I was able to tune something that worked pretty well. In spite of the precision loss. I also address that, by using the distance instead of the rate method (that is a whole different discussion).

Update 9/1/2011

When running some of my encoder logs see here. I found a critical bug where the total error stopped to accumulate. I have put the bug fix in here. Actually I wouldn't call this a bug but more that it was not complete. What happened in my test is that I had a huge range in one direction and small in another so my test was as such where the robot goes in one direction and the momentum to slow down caused the PID to accumulate a lot of error in the scaler. Now one thing about this is that I need not have such a large range, and I have fixed this. Moving along the robot then goes in the reverse direction. With the error so high it takes a while to come back down and while that happened the actual present error started to grow, and I had the 'I' set to 8 (yes that is way too high). So then there is a line to check (error + total_error) * 'I'. This was overflowing and the total error stopped accumulating, yet it was stuck way too high to be able to apply any voltage. The solution is to allow the total error to still grow where it would equal the minimum or maximum output with the following equation. error = (max - (I * total_error)) / I. See source for more details.

Using PID and the Kalman filter 5/19/2011

I have had the opportunity to test an arm motor/potentiometer piece of code which has intense stress due to a poor gearing ratio and lack of a good counter-weight. My first attempt at this (new to me) I was hoping I could just calibrate my predicted speed to match the voltage, but then once I found out how fast the window motor turned (about 3 rps) and with only a 2:1 ratio (about 1.5 rps). I found this to be difficult to get right... I then researched PID (see below). After the regional competition I purchased my own cRIO since the robot was being shipped to st Louis, and had the opportunity to make a quick test kit that allowed me to see the PID work in action. Once I was happy with the kit results, we then procede to the championship and I test this code on the robot. I found the added stress of the poor gear ratio and bad counter weight was causing more error, but for the most part it did pretty well, but then on a few occasions the arm flew all the way backwards for no apparent reason. I found out from co-worker about the Kalman filter and I have included this in the source as well. It turns out that it is possible that the potentiometer may drop out with serious error, and this is where the Kalman filter can step up to save the day. It can weigh these errors with high uncertainty and help get the correct value needed. Once I added this filter I had much better results with this and no more bad surprises.

This is one rare window of opportunity for some robust software control, because we were rookies and had made it difficult for the arm to work properly. This window will close as we fix the arm to have better counter-weight and gearing.


The PID controller algorithm is one of the most exciting inventions that I have seen in a long time, so I want to put my spin on explaining it.

In the article in the link above it mentioned how this algorithm was invented to solve navigation of ships, so when I got to thinking about that it made sense to use that as an analogy. I apologize in advance if you are not a star blazers fan, but I am gong to use these characters to explain the algorithm:

  1. Captain Avatar - provides the final result
  2. Venture The navigation officer, who represents the P (proportional), or the *present* error
  3. Sandor the engineer, who represents the I (integral), or *past* errors
  4. Wildstar The tactical officer, who usually wants to fight and is aggressive, represents the D (Derivative), *future* error

So let me paint a picture, these characters are on a boat and up in the distance they want to steer the boat through a couple of buoys. They see their target (i.e. setpoint) in the distance, and want to keep the ship on a constant aligned trajectory to ensure their success. Now then this seems simple enough, but what happens if there is a current or random waves that knock the boat out of alignment?

Venture reports the current status of how far the ship is off from being in alignment; he only cares about its offset position in alignment with the original course
Wildstar is more concerned on where the nose of the ship is pointing and reports the amount of *error on its current heading (if it were to stay on this heading)
Sandor is keeping records of Ventures findings and wants to find the common error in an attempt to interpolate the stress of the water

Captain Avatar values each of their findings and finds a compromise by adding each of their inputs and submitting this to venture for the new heading. Usually, the present correction has the highest priority, and the past findings may also be as equally important if there was a trim setting for boats this value would go there. The future findings (to him) are lower priority but a little of this may be good to be more aggressive in getting on track more quickly.

When I tested out the PID against a potentiometer simulation, and I found the integral piece to be the real key I was after as it interpolated the difference between the speeds of the potentiometer vs. the speed of the motor. It was very cool to see how it gradually adjusted the calibration to the correct difference in speeds, and could simultaneously lock onto it so quickly. I tested using p(1),i(1),d(0.25). I wish I would have known about this back in 2004 when I attempted to write a dynamic audio resampler (and failed), or in 2008 when I tried to fix the oscillation from ships that supported engine ramping. Oh well. :)

I have attached some source explaining the algorithm this is what I have used for testing. It was originally written from the WPI library, but *heavily* refactored to what I needed. On a side note, the original source put the calculation on a separate thread imposing an equal notification period of time. I have changed it to be friendlier and run on the same thread and it can also handle erratic time slices by multiplying the error to the time slice delta.

*error = this analogy is slightly a bit over-simplified but accurate enough for illustration it really manages the derivative of the error itself.


Cache-control: no-store