Is putting PID loops in the area of the code that runs faster really that beneficial? If I recall, the ‘main’ portion of the loop updates at 20 hz, or every 50 milliseconds.
I took a single signals class in college and while we touched on oversampling, antialiasing, etc we didn’t really dig in. In this case, I look at our 150 pound robot and think to myself that 1/20 of a second response time should be more than good enough.
Is there something I’m missing? I’d love a whitepaper on this, since it seems to be one of the ‘black arts’ that we don’t really talk about here, even though almost every team utilizes PID loops at one point or another.
What language are you using. If you are Using Java then WPILibJ’s built in PIDController class has a constructor that you can set the update interval in, I assume with C++ there is a similar feature.
You are correct that a faster update rate (especially with our sloppy mechanical systems) won’t make too much of a difference. (At least in the present time, or ‘P’ portion of the calcs)
The real issue is the PID loop needs to be run at a constant interval, as you are taking the integral and differential of a signal. You’d like to shrink delta T to a very small value for the D, and really want that value to be constant. You could calculate your real differential by measuring dT, but why bother when you have the horsepower to do it in constant time in another thread?
All WPILib is doing for PID is making it easy for an end user to fork off a thread and repeatedly call a function at a constant rate. (They also throw in the math for free)
I’m not certain, but I think the actual sensors and motor outputs on the robot can be queried and updated much faster than 50hz. The 50hz timing is how often packets get sent to and from the classmate. It does you no good to query your joysticks or buttonboxes faster than 50hz, but the analog inputs can change much faster.
Tom, thanks for the explanation, and that does make sense. However, the integral and the derivative (at least in my experience) are not needed on our robots 90% of the time. Since my team has never really needed to use them, then it sounds like we’ve never really hurt ourselves keeping the P portion of the PID in the normal loop.
However, if we ever work into the I and D section (and we might a bit this year), I’ll start putting them in the “go fast” portion of the code.
Thanks!
Edit: On a side note, one of the really nice parts of the IFI system was that you had to program your own PID and understand how they worked. I made my team go through this year and use whitepapers to write their own PID code. It’s always good to know what’s under the hood of your robot!
Second note: This would be an Excellent EXCELLENT discussion to have at nationals and put a whitepaper here on chiefdelphi. Update rates of sensors, loops, and processing rate of code all tied together is something that simply doesn’t get addressed very often.
The rate that you run the PID loops depend on what you’re trying to control, how fast it is moving, and if you have any other logic that interacts with the PID loop. For example, if your robot is traveling at 10 ft/s you can travel 6 inches in one 50 ms time step. If you need to control to more accuracy than 6 inches (or you have some logic that looks for you to be in a 6 inch window) and you need to do it at a high robot speed, then you might want to move to the faster loop. If you are controlling motion that is slower or you don’t need that kind of accuracy while traveling that fast, then the faster sample rate won’t help you too much.
You can easily take the actual sample period into account in the PID code. For the integral, multiply the error by the time step before adding. For the derivative, divide the error by the time step before the subtraction. Just be sure to take this into account for your control gains - they will change by a factor of the average time step.
There are many competing factors at play when selecting a sampling rate/update rate for a PID controller. On the one hand, higher sampling rates minimize delays in the control loop and bring your system’s discrete-time controller “closest” to its ideal continuous-time form. On the other hand, higher sampling rates can also lead to increased noise susceptibility, especially when you are using the “derivative” term, and increasing the sample rate eventually reaches the point of diminishing returns anyhow.
Implementing control systems that trade off between these factors is somewhat of a black art - my college adviser passed down the wisdom that, in general, the sampling period that you should shoot for will be one or two orders of magnitude (10-100x) smaller than the system’s settling time (time from saturation to reaching its final destination). The popular article “PID without a Ph.D” agrees.
For example, consider a robot whose drive is commanded by a position PID loop. The setpoint is 30 feet away. For arguments’ sake, lets say that Kp = .1 (Percent of full throttle per foot of position error**). For most of its trip from x=0 to x=30, the PID controller will be telling the robot to floor it (since Kp*error >= 1.0 until error is less than 10 feet). The amount of time it takes between the last output of 1.0 and the time that the error enters a final X% bound around the setpoint (for example, the “1% settling time” in this case would be the time elapsed between the time at which error=10ft and error=1% of 30 ft, or 3.6 inches) is the settling time. Lets say it takes our robot 2 seconds to slow down, possibly oscillate one or more times, and come to a stop within 3.6 inches of the target - using the 10-100x rule of thumb, our sample period should be between 200 milliseconds and 20 milliseconds, or 5 to 50 Hz.
The idea is that at a, say, 3 Hz sampling rate we would have a really bad settling time (if the system is even stable at all). As we increase the sampling rate from this point, we expect the settling time/stability to improve until, at some point before 50Hz, we converge at around 2 seconds 1% settling time. Above that sampling rate, we are not likely to improve significantly (and in fact we might hurt our robustness to sensor noise if we have a D term).
The above is just a rule of thumb; given a good mathematical model of any process there are some analytical ways to figure out what the ideal sampling rate should be, but unless you are building a space shuttle, the math is sufficiently complicated that I would say just play with your system and see what works.
I will point out two other related factors to sampling rate frequency:
Sampling rate jitter - this can be a huge problem, especially once you start talking about 5% or more. Jitter is the variation between successive sample times. It can be insidious when dealing with integral and derivative terms, for fairly obvious reasons (differentiation in a discrete PID loop is usually approximately by finite differences, e.g. x(t) - x(t-sample_period) ). You can compensate for it somewhat if you measure the sampling period between updates, but if you can do this, why not just use a timer in the first place?
Remember that in our systems, the cRIO does not command the motors directly. Rather, it commands speed controllers, which have their own set of delays. Victors update at something like 100Hz (or is it 120?) - this means that a potential delay of on the order of 10ms gets added to every single output if you aren’t lucky. Jaguars have about half of this delay. So doing updates faster than 100Hz/200Hz is going to be fruitless.
** Note: I find that if I force myself to put units onto my PID controller gains, it makes life much, much more intuitive when it comes time to put in “initial guesses” for my parameter gains.
Tom,
This sounds like a great idea (my master’s is in control systems :)) In classes or in text books, it is easy to find the PID equations and to understand the concepts. However, implementing control systems (and signal processing, and image processing) in effective code is a skill that is sadly underrepresented in the majority of literature on controls.
Ha! I just emailed your reply to myself and I’m going to put it on the team server. That made a ton of sense and didn’t overcomplicate things, and gives a great rule of thumb to figure this stuff out.