I’ve seen a bunch of threads on CD related to drivetrain PID tuning, but they all seem to be about positional control. We’re trying to run velocity controllers on each side of the drivetrain to do closed loop driving in teleop / cascaded control in auto.
FWIW, we’re running a two speed, 8 wheel differential drive with 4 775pros per side. Each motor is connected to a Talon SRX, and each side has a CTRE Mag Encoder connected directly to the wheels (on the same live axle). I’m only trying to tune the high gear PID currently, as we don’t find ourselves using low gear very much.
We’ve tried tuning using the steps in the Talon SRX software reference manual (PF controller), and while this seemed to give us promising results when just running one side of the drivetrain, when we applied the gains to the other side the robot didn’t track straight. Closing the loop exacerbated the tracking error that we observed when running the drivetrain completely open loop, which is not what I was expecting.
I’d be interested to hear what tuning processes other teams that run velocity controllers on their drivetrains use. Any advice?
I always say that a well tuned system starts with a good model. I haven’t spent much time with it myself, but 449’s drivetrain model seems like a good start. I would recommend characterizing and using their feedforward model, then adding closed-loop control.
It’s likely, especially if you have an inadequate feedforward (kV only in your case), that the differences in each side of the drivetrain produce different results when run with the same PID constants. Doing most of the work with feedforward will make this better, and/or tuning each side separately.
When desiring to drive straight or at a known curvature, it is often helpful to introduce some crosstalk into the system. That is, additional throttle will be added to the slower side, or some deducted from the faster side. Alternately, you can use a gyroscope and use that to correct the orientation.
As mentioned, you always want some sort of communication between the two transmissions in order to get proper drive control. The rationale for this is simple: let’s say one side of the drivetrain hits a small bump in the carpet, or one of the motor’s brushes have begun to wear down; then a PID controller with the same gains would give a slightly different output on one side as it adjusts, causing the whole robot to not go straight. In other words, any assumption that running both sides of the drivetrain at the same speed goal/same voltage will make it go straight should be discarded (if accurate heading goal following is the goal). Rather, we want each side to run at precisely the same speed (not just the same goal) for the drive to go straight. Of course, this can be achieved theough extremely aggressive PID gains, or effective feedforwards.
Howver, it is better to also modify the outputs of each side of the train based on heading sensor data. For example, if a gyro sensor detects the heading of the robot to he 0.1 radians off to the right, then the robot should speed up the right side of the drivetrain, and slow down the left. This can often be more accurate than just a PID loop running that detects one side is not quite getting to the goal velocty. Plus, by following a gyro you have both sides working together, so the dive will likely adjust faster. If you are familiar with “arcade driving,” think of heading error as a steering wheel; something you add/subtract from the right/left sides of the drivetrain respectively.
This might sound an awful lot like a secondary PID loop — that’s because it is! This system means you have one PID loop for each transmission and its encoder, and one for the gyro (or any other method to measure heading).
Of course, this is not a good idea to implement with just PID. As mentioned, feedforwards is a critical part of control. After all, raw voltage is infinitely more consistent and predictable than a closedloop controller. As such, it is important to hash out a basic physics model for your drivetrain. This would involve some basic electrical theory on how DC motors work, more specifically how to calculate voltage for a given velocity and torque/acceleration. From there, you should create a function that calculates voltage for each side of the drivetrain given an angular/linear accelefation/velocity goal. This calculated voltage would be what the PID loop “builds off of” making it so the PID loop has to do minimal adjustment. Team 443 has an excellent paper on motor characterization here.
Looks like I definitely need to take a look at the drivetrain characterization paper!
We are definitely planning to use a gyro for measuring heading error in auto, as we’ve found motion profiles to be a nightmare without one.
I’ll work on creating a feedforward gain from the characterization paper, but I’m still confused on how to tune the PID part of the velocity controllers. Any other advice specifically for velocity tuning would be much appreciated.
Having a way to visualize what the speeds on each side of the drivetrain were doing was key for us - generating plots or something like that.
I’m guessing the not-tracking-straight thing is likely due to steady-state error in the velocity (too small to cause a difference in control effort via P, but large enough to skew the direction of travel).
An I term is one way to correct for steady state error, but a gyro in this case is probably even better. We’ve had great success with only a P-gain controller to slightly offset the RPM commands going to the SRX’s.
Also, I wouldn’t necessarily expect the gains to be identical for both sides of the drivetrain - if the center of mass of the robot is not exactly in the center, or there’s manufacturing differences, etc. it’s likely they will be different.
Finally, general tuning technique in terms to of steps taken - we usually tune a single set of gains by applying them to both sides of the drivetrain first (F first, then P, then others), and get it “fairly close”. Then we tweak individual sides till we’re pretty straight. Then we add in gyro compensation.
If you’re doing velocity control on your drive, do not add any gyro correction until you have your drive control dialed-in. You should always tune cascaded PID controllers “inner-to-outer,” meaning that the loop closest to the actual process output (the “inner loop”) gets tuned first, and then the loop feeding into that loop, and so on.
The most important part of a good velocity control loop is an accurate feedforward. Running the characterization described here will provide you with the parameters needed to set up a “Kv + intercept” feedforward, which can easily be implemented on the Talons now that they support an arbitrary voltage add in closed-loop mode. Once you have this set up, you shouldn’t need any more than a little bit of P gain for accurate velocity control.
A differential drive with good PID velocity control should track very straight in all but two situations: near max speed, and during hard acceleration. Both of these cases will almost certainly not track straight due to “saturation” - that is, hitting the maximum possible voltage that can be provided to the motors, leaving no “headroom” for the control loop to correct for speed. For the first case (near max speed), the best solution is simply to put a software limit on the commanded speed of the robot that leaves sufficient “headroom.” The latter case (hard acceleration) requires gyro stabilization. However, if your robot is not tracking straight when driving at a moderate constant speed, then do not move on to gyro stabilization, because that means your velocity PID is not tuned acceptably.
Gyro stabilization works much as has been described in the thread - you simply run a PID controller closed on the difference between your actual heading (as measured by the gyro) and your desired heading, and send the output to a speed setpoint differential between the two sides (note that with a differential drive it really only makes sense to do this while not turning). We have found that if the loop is closed on the heading (as opposed to the raw gyro output, which is the rate of change of the heading), by far the most important part of such a stabilization loop is the derivative gain (equivalent to the proportional gain if you’ve closed the loop on the turn rate instead of the heading). A little bit of proportional gain (integral gain if you’ve closed the loop on turn rate) is helpful to make sure you do get pushed back to the desired heading if it is significantly perturbed, but derivative gain will prevent most perturbations from happening in the first place. As in most cases in FRC, integral gain (which is actually double integral gain if you’ve closed the loop on turn rate) is not useful, and should be avoided.
Oblarg, I don’t understand the mechanics of that. It’s my understanding that in a cascaded control loop, the output of the outer loop becomes the setpoint of the inner one, and the output of the inner loop is your “final” output.
So here, our velocity loop would be the outer loop, and the gyro loop would be the inner loop right? But don’t you have to tune both at the same time? If you haven’t tuned the Inner Loop at all (the FPID values are all zero) your output from the inner loop will be zero and your final output will be zero.
I guess I’m also struggling with the whole cascaded loop idea. Everything I’ve read says the output of the outer loop becomes the setpoint of the inner loop - but that doesn’t make any sense to me at all. Why would we make the output of the velocity loop the setpoint of the inner gyro loop?
The old style of doing auto makes sense to me: positional pid controls the distance and a seperate controller handles gyro corrections and the final outputs are added together - but that isn’t cascaded control right?
So here, our velocity loop would be the outer loop, and the gyro loop would be the inner loop right?
Nope. The velocity loop outputs to the motors, not to the gyro loop, and so is the inner loop.
The gyro loop outputs to the setpoint of the velocity loop (after being added to or subtracted from the commanded forward speed, of course), and so is the outer loop.
I guess I’m also struggling with the whole cascaded loop idea. Everything I’ve read says the output of the outer loop becomes the setpoint of the inner loop - but that doesn’t make any sense to me at all. Why would we make the output of the velocity loop the setpoint of the inner gyro loop?
We wouldn’t; you are correct that this would make little sense.
The old style of doing auto makes sense to me: positional pid controls the distance and a seperate controller handles gyro corrections and the final outputs are added together - but that isn’t cascaded control right?
A positional PID loop being used to control distance only really makes sense in the context of motion profile following (you wouldn’t want to do a raw position servo to a location), and in that context what you’ve described is an option and, as you noticed, is not a cascaded control system.
But this would not be usable in teleop, and is entirely different from what has been discussed in this thread thus far.
Now, one could analogously implement gyro correction in a non-cascaded manner by adding the output as a raw voltage to the output of the velocity PID - but this is likely to work significantly worse than a cascaded system, because the two control loops (gyro and velocity) can “fight” each other. It is much less likely for this to happen in a cascaded system.