I'd be happy to hear from readers with more experience on PID implementation and tuning to further improve this article.
So how do you set up the code? My robot uses a servo library for sending signals to the ESC. The values range from about 400 (neutral aka zero throttle) to ... well I've never tried more than 600 which is faster than the robot can deal with right now.
Here's the PID controller code.
int output = escZero
+
(Kp * error)
+ (Ki * integral)
+ (Kd * derivative);
setThrottle(output);
The variable output is an integer value that is intended to range from 400 to 600. The variable escZero is 400 (the ESC zero setting) so that the output is at zero throttle for a setpoint of 0 m/s. The variable error is the difference between the desired speed and the current speed. It's calculated prior to the output line thusly:
The integral is just a running sum of the error multiplied by the time since the last update. On the Arduino one might make use of millis() to measure the time period, on the mbed I use the Timer object.
And the derivative compares the previous error with the current error.
My simpleton approach involved solving for Kp given a desired output of 550 for a setpoint of 5m/s, then increasing Kp until the speed oscillated under no load, then I set Ki at a much lower value and incremented until the speed reached the desired set point. I haven't played with Kd yet.
So, my PID controller isn't optimized, but it is most likely stable, which is of much greater concern to me than fast rise time; the robot accelerates very quickly even under partial throttle.
And most importantly, I can now change the speed of the robot as it approaches a turn, and accelerate again after the turn.
I'm also investigating using the PID for braking, but don't hold your breath on that one.
float error = desiredSpeed - nowSpeed;
The integral is just a running sum of the error multiplied by the time since the last update. On the Arduino one might make use of millis() to measure the time period, on the mbed I use the Timer object.
integral += error * speedDt;
derivative = (lastError - error) / speedDt;
lastError = error;
So how the heck do you pick the gain values? It's best to read up on the various methods. The references above list a few options.
My simpleton approach involved solving for Kp given a desired output of 550 for a setpoint of 5m/s, then increasing Kp until the speed oscillated under no load, then I set Ki at a much lower value and incremented until the speed reached the desired set point. I haven't played with Kd yet.
So, my PID controller isn't optimized, but it is most likely stable, which is of much greater concern to me than fast rise time; the robot accelerates very quickly even under partial throttle.
And most importantly, I can now change the speed of the robot as it approaches a turn, and accelerate again after the turn.
I'm also investigating using the PID for braking, but don't hold your breath on that one.
PID controllers are very powerful tools. In your case, the PID loop is likely going to be dominated by the P term. Unless you happen to be going up and down hills, your speed is going to be very closely proportional to the ESC value. You might use a bit of the D term to adjust the final speed for battery and other non standard conditions.
ReplyDeleteThere are methods (Ziegler–Nichols) that will allow you to tune a PID for best results. I find the simplest is by gut feel looking at log data. Are you logging system data (speed, direction, ...) during test runs? If so, add the terms of the PID. Plot in Excel. adjust one term and re-run. Learn what each term does.
If it is stable then don't fiddle around with Kd (but by the links you gave you obviously already knew that). I have never used any other tuning method than the one you describe here but I can say this :
ReplyDeleteFrom experience this kind of tuning is not bad but you have to give some slack stability wise (which means poorer rise time performance). The reason is simply that you have little control on the environement parameters that might affect your loop (weight, wheels, adherence to the track, occurrence of a full moon...). So the best you can do is testing testing testing and choosing fairly "safe" values.
@skye and @simbilou Thanks for the tips! It looks like it is pretty stable going up and downhill outside the house, which is more than the slope at the SFE site. It should be working fairly well. I was really conservative with the settings. I can always change them in the config file if it goes unstable on me. No D term used.
ReplyDeleteMight I ask what you are using to sense speed? Do you have an encoder on the drive shaft or are you using the speed from the GPS?
ReplyDeleteOne issue to remember is the accuracy of the state variable (speed) measurement. If it has error or quantization the loop will have a hard time converging. I am re-learning this from the work I just started on my 2013 AVC entry. The optical encoder I have only gives me 60 counts per second at max speed. A simple off by one error equates out to something like 2 MPH!
@Skye: I'm using dual wheel encoders with speed measured as the average time between each of the 32 ticks per revolution at each wheel. I've not had much time to perfect the speed PID controller. It's only just barely good enough for what I need.
ReplyDelete