Tuning a PIDF follower on a robot with high gearing

One of the problems we have been encountering during the off-season is tuning a PIDF follower reliably on our 2017 robot. It is geared with a fairly high theoretical top speed (~17fps), and our autonomous trajectories are generated with a maximum velocity of about 5fps. This means there isn’t much torque at the wheels compared to resistive forces.

Without feedback terms, velocity does not track well. Generally a kV and kA value that work at faster speeds (4-6fps) are inadequate for slower speeds (2-3fps). We can add in feedback, but a few things happen:

  1. The Kp term does more corrective work than it should be doing
  2. Because of the large error, following is inconsistent between different trajectories

How do teams overcome this inconsistency at lower torque and get better tracking with feed-forward terms before moving on to adding feedback terms?

PID(F) assumes linear dynamics; the high voltage and low voltage operating points of DC motors attached to a substantial load behave quite differently.

You will have the best luck if you tune the feedforward terms near the setpoints you care about (or if you want, gain schedule so that no matter what the velocity setpoint, you have a suitable set of feedforward gains).

On 254 we typically tune around a “nominal” speed and accept that at very low speeds, the feedback part of the controller is doing more of the work (which for us usually only occurs briefly at the beginning or end of a motion profile). If we had to spend a large amount of time cruising at a different speed, we would probably gain schedule.

1 Like

We noticed this as well, after implementing acceleration feedforward on our practice bot (thanks a bunch for the Talon workaround you posted!). The Ka value that worked for our rather tame profile (max acceleration ~3.5 ft/sec^2) corresponded to a theoretical maximum robot acceleration of ~15 ft/sec^2, which is well below what we know the value to be.

Could you provide any insight into why motors so nonlinearly under these conditions? It doesn’t seem clear to me from the state equations.

It’s the friction that isn’t linear, not the motors. Rolling resistance, scrub forces, and static friction are a bear.

Yeah, I expected that friction would have some nonlinearity, but nowhere near the magnitude of the effect I observed.

This is one of the reasons I wish that the Talon’s configNominalOutputVoltage setting rescaled the output to be linear between the nominal output and the maximum output (the current functionality merely promotes the value to the nominal output when it is too low), which it seems to me would do at least a somewhat better job of properly accounting for the effect of friction. I suppose, upon reflection, that we could implement this via. workaround the same way we implement our acceleration feedforward, i.e. by adding the constant value to our velocity setpoints (we’d have to adjust our Kv to account for this, though).

I confess, though, that I’m pretty ignorant of the specific details of the frictional losses - what I suggest above would be akin to assuming that the frictional losses (in terms of extra torque required by the motor) are constant at all speeds. I have no idea how close (or far) this is from the truth. Given the relatively gigantic size of the Ka gain (something like a factor of 6.5 higher than the “theoretical” frictionless value) that we found to work at low speeds/accelerations, I suspect it’s pretty far - but I still don’t know why the discrepancy is so huge.

The biggest reason is the carpet. Almost any surface pair will have a larger coefficient of static friction than of dynamic friction due to developing bonds between the two surfaces. In the case of carpet, it deforms relatively slowly and rebounds inelastically, so the net friction rises as speeds decrease. I suspect that carpet fibers also work their way into any crevices in the wheels over time, furthering this effect.

Well, our current plan is to attempt to better-account for the effect of friction by, rather than computing Kv from a single data point (measured max speed), run the robot at a variety of voltages, measure steady-state speed at each, and perform a linear regression. The reciprocal of the slope will become Kv, and the negation of the intercept will become a constant “friction compensation term” that we will add to all of our velocity setpoints. This should, in theory, accurately account for any frictional effect that is itself linear with velocity (and a glance at the residuals from the regression should give us some insight into the degree to which it is nonlinear).

We will document how well this works, and probably write a short whitepaper on it. Hopefully, it will go some ways towards allowing us to have a single Ka gain work reasonably well over a wider range of accelerations.

I’m very interested in the answers to this, because it also applies to a differential swerve with too-high a turning gear ratio. Can someone post an example of gain scheduling or some other non-linear feedback control of turning for my non-coder brain to absorb?

Our current (post Chezy Champs) plan is to gather several kV values for speeds ranging from 0.5 fps to 10 fps and then write a class (or probably just grab 254’s InterpolatingTreeMap) to do linear interpolation for kV values given a velocity target.

Here’s a simple one.

  const double kP = ::std::abs(velocity) < 1.0 ? 5.0 : 4.0;
  voltage = kP * error;

If you want to hurt your head some, look at our 2016 drivetrain code. We gain schedule all 4 shifter combinations. ({left low, left high} x {right low, right high}).

Practically speaking, gain scheduling means picking a different set of gains at different points in the state space. It’s harder to guarantee stability mathematically, so I like to avoid it if possible, or constrain its use such that it’s driven by something like gear ratio that doesn’t have as much interaction with the loop.

Sorry to semi-necro the thread, but we just ran the velocity-versus-voltage tests and it turns out that the behavior is linear as heck:

So, our proposed fix should work - I will report back next weekend when we’ve finished testing it.

This also suggests that our feedforwards, in general, should include an intercept to help account for friction. It would be nice if both WPILib and CTRE would support this - I have sent an email to the latter requesting it.

Edit: If anyone is wondering, the intercept there is approximately -1.55 ft/sec, which corresponds to an output voltage of ~1.24 volts, which in turn corresponds to a torque of about .75 Nm per side of the drive. It would be interesting to see how closely this matches the actual “torque required to move the wheels of the robot at rest” as measured directly with a torque wrench or whatever. Something to put on our list, I suppose.

Interesting. We will try something similar on our bot and report results.

So, I did a bit more investigation today, and it appears that the net friction does indeed decrease as velocity increases, however this decrease appears to be very nearly linear (as can be inferred from the near-perfect linearity of the earlier plot).

To be more specific, assuming the CIM specs used in the WCP drivetrain calculator are correct, the voltage required to overcome frictional losses on our practice bot drops from ~1.2V at velocities near 0, to ~.8V at velocities near max speed.

I will report back on how well our calculated “1.2V” figure corresponds to the actual torque required to move the wheels of the robot at rest once I get my hands on a torque wrench. Unfortunately, I can’t think of any way to directly measure this value at speed, so I’m not sure how to test if this decrease is actually a decrease in the friction forces, or rather is simply a result of the CIM specs not being quite right. If anyone has a clever idea for this, let me know. The good news is, well, it really doesn’t matter, pragmatically, which it is - since everything appears to be linear, we can easily just roll everything into the feedforward regardless of where it comes from.

It will be interesting to test this on other robots, and see if we obtain similar results.

Velocity is very linear except very close to the zero point (with the current generation of speed controllers). Acceleration vs. voltage is often not as nice due to friction, battery voltage sag, inductance, etc.

Assuming that you’re tracking your profiles well enough that actual speed is sufficiently close to desired speed at any given point in time, then wouldn’t a feedforward structure of “intercept + Kvvelocity setpoint + Kaacceleration setpoint” properly account for this? Unless I fundamentally misunderstand brushed DC motors, torque depends only on the difference between applied voltage and back-EMF. The “intercept + Kvvel" portion of the FF should account for back-EMF plus the voltage required to overcome frictional forces, leaving the "Kaacc” term to account (hopefully correctly) for your desired acceleration on top of that.

Yeah it should, though the nuisance factors I mentioned above still apply (I edited my post while you were responding)

We’re hoping that the Talon SRX’s setNominalClosedLoopVoltage() can account for battery voltage sag, though we haven’t at all considered inductance.

BTW I recently came across a procedure for tuning acceleration gain that I thought I would share. Haven’t tried this myself yet, but it is pretty intuitive and the math checks out.

  1. Tune velocity feedforward gain using standard methods (e.g. measure actual speed and divide by applied voltage).

  2. Using (slowing ramping) triangular or trapezoidal motion profiles, tune the velocity integral gain (Ki) to achieve zero steady state error tracking during the ramp up and down. Leave all other feedback gains set to zero. It doesn’t have to be perfect, you just want to achieve a ~zero error steady state without a ton of oscillation. You will probably lag behind the velocity profile at first, then integral will kick in and you’ll converge with the velocity ramp, then you’ll overshoot the end of the ramp up.

  3. Plot the effect of integral gain (integrator * Ki) during the ramp up. Measure the peak control contribution of the integrator term during the ramp up, divide by the acceleration of the ramp, and you have your acceleration feedforward gain (Ka).

  4. You can now remove the integral gain if you want (and you should try to if you can…integrators suck)

That is really, really clever. We’ll have to try it.

I’m still hoping that, now that we’ve got a better handle on how to account for frictional losses, there’s a chance that the “theoretical” value of Ka (i.e. calculated from motor stall torque, max voltage, gearing, robot mass, and wheel size) will work.

Inductance only really matters for very low inertia applications (it had a small but measurable effect on our 2015 can grabbers, but in a drivetrain or arm or something, you don’t notice it). Battery sag and nonlinear forms of friction are far bigger culprits most of the time.

setNominalClosedLoopVoltage() does work wonders for battery voltage sag, though it still can’t help you when you saturate.