Almost all autopilots use PID algorythms in one way or another. Now there's a basic PID library in the standard Arduino software. Even better, here's a tutorial series from Brett Beuregard on how to use it!
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.
Comments
Jason if you have a minute please give some comments, weekend is coming to fly and test. Thank you!! ;)
By returned value I mean the value of Press variable. The returned value of the function Read() is 0 every 5 calls, and Press variable is not updated.
Jason you're right, the effect is present if the input is jumping but it's not derivative kick by definition.
I'm very curious about baro performance and how to get good alt hold from so noisy data, I couldn't alt hold well yet.
I did some tests of the baro on the table, getting values for some time and graphing histograms to see the distribution of the values. I get different results from 3 consecutive tests, not a consistent deviation.
I'm trying another filter aproach, but I'm not sure how much the overall performance is affected. What should I look to see cpu use?
I saw the filter on last versions is a moving average with size 6, reading baro at 10Hz. How did you came to best size? What do you think about reading at 40Hz (is it the max?) with filter of bigger size? What kind of filter are you trying now?
Maybe this is not affecting too much, but did you note that barometer.Read() returns a duplicated value every 5 calls? There is a state machine of 5 values, one is for temp and the returned pressure is the same of the last call for that state.
Sorry for so many questions, I got this things around my head from some time ago.
Derivative kick is from changing the set point and not the sensor data. last week I added a better Moving average filter to clean up those spike from the sensor values in the PID library. I'll post once I can thoroughly test, but the results are very good so far. They should really help Loiter as well as alt hold.
Jason
From my experience, using error in derivative term is acceptable only for PID's where SP is fixed.
If SP is changing, using error=SP-PV in derivative is not so good idea. I always used for derivative below solution. Is working well for both airplanes and multicopters.
Error=Pv-Pv_last
Pv_last=Pv
Deriv=Kd*Error
I was looking around to PID code some time ago and noted two things.
As the function get_pid doesn't know the input, only the error, there is no way to get rid of derivative kick effect. I'm wondering if this effect is what I see on my quad in alt hold, I'm getting sort of spikes of power on motors. My baro readings are noisy, even with the filter and testing on the table.
This is not important but proved to save some bytes of flash:
pid.cpp line 17:
output += error * _kp;
could be:
output = error * _kp;
because output is initialized as 0 each time the function is called.
Great addition and simple explanation to computer code.
I think in addition, a defintion of k_i,k_p,k_d (gains) are in order and a quick tuning relationship map associated to quadrotors would be nice. Like:
"tune k_i first (minimizing k_p=0/k_d=1),
then k_p, until you have stable/level flight,
then k_d to account for different environmental conditions,
and lastly adjust k_i more for reaction sensitivity".