SYSID Feedforward for Velocity RPM control

We are using SysId for the first time and really need some help understanding how to get values that are in the same units we work with.

We are trying to tune a Feedforward controller to assist our shooter, a BangBang velocity controller. Our setpoints are measured in RPM. We are using a Rev Through Bore controller on the shaft.

Our system:
To get to RPM we took the rate from the Rev Through Bore, divided it by 2048 and then multiplied it by 60 to get to RPM. Sanity check: with a NEO (5880 RPM free speed) and a 2:1 gear reduction (max 2940 RPM after reduction) we are printing out values that make sense.

On to SysId:
In sysid I’m unclear on whether we need to do the above conversions. There is a CPR section and the docs say to use 8192 for a Rev Through Bore encoder. Again, we applied 2048 to the returned rate, not 8192. Is that right?

Also, do we have to do some conversion to account for seconds vs minutes? Will that affect the ks, kv, ka calculations? I tried putting 60 in the units per rotation but the software wouldn’t let me change the value from 1.000

Any help would be appreciated. Thank you.

1 Like

Rev through bore CPR is definitely 8192 not 2048. How are you connecting it? Through the RIO? Directly to the Spark Max motor controller?

Rev SparkMax universe is all about rotations and RPM. Position is reported in rotations and velocity in RPM. There’s a unit conversion option, but I’m still not clear if that effects the input units for set position methods. My recommendations is to leave the conversion factor alone.

We are using the wpilib Encoder class. The encoder is plugged into two dio ports as a quadrature encoder and read on the rio.

We call getRate() here:

If you’re using the Rev API everything is in rotations (position) and RPM (velocity) if you’re using the RIO and WPILib for reading the encoder I’m pretty sure that will be returning in ticks per second, but I haven’t read the docs on that. So I think you’re on the right track.

First thing I do when I get confused about encoder ticks <-> speed/position is to put all those values on SmartDashboard in periodic() and then turn the mechanism manually and note the values. Always make a hypothesis first “I am going to turn the drive shaft 1 full rotation clockwise, I expect the output shaft to turn 2 full rations CCW and the encoder to change (2048 * -2) = -4092.” Then when you’re surprised by the output figure out where you are making a false assumption.

Exactly! That is what we did to come up with this conversation. So my question is what do I put in sysid to get ks, ka and kv values that will work with rpm set points?

I can’t help you there. I managed to get sensible numbers out of SysID for our drivetrain, but only got nonsense numbers out of SysId on our Arm using NEO motors and a through bore encoder on the output shaft. (Our through bore encoder is directly attached to the Sparkmax.) We ended up tuning PID for the arm by trial and error. I couldn’t find any step-by-step documentation for using SysId with anything other than a drivetrain (and for swerve you have to augment that with reading CD). I spent some time looking at the source code trying to figure out what it was doing, but ran out of time.

SysID expects you to know that the time lag is on measuring the encoder. Sometimes these numbers are provided in the tool tips. I’m at a loss for looking up or measuring these values when they are not.


This thread might help with the sysid stuff. Unfortunately still no step by step tutorial but we were able to get useful results in 2020. Only the threshold and error tolerance parameters are unit based so you can probably try the sysid routine a couple of times before you get the desired results.

Okay, so I ran the sysid with the attached outcome. My ks ended up negative. very strange!

No idea if this is rpm or rps.

These are the feedforward gains returned from sysid shown in the screenshot above.

kS: -14.2
kV: 2.2184
kA: 37.9

  1. why would ks be negative?
  2. I’m guessing this is rps, would we just use these numbers and then calculate the feedforward setpoint with our rpm/60?

Your data graphs are all wrong. Check out the WPI docs image example.

Without further insight, I would say you have an encoder issue (everything properly installed? Encoder loose?)

The rev through bore is working on the bot with the a and b channels feeding into the rio dio ports 0 and 1. We’ve been working with it in our shooter for weeks. I see the illustration you linked and yeah, my graphs look pretty messed up compared to those.

I’m not sure if this is a settings issue or not. I’ve left what I can to defaults.

Here’s a better attempt! I put a check next to “Reduce Encoding” and set the samples per average to 5.

kS: 0.043173
kV: 1.0649
kA: 0.1734

I entered these values into the feedforward controller and ran a feedforward calculation. I fed it a velocity setpoint of 33 rotations/second and the feedforward.calculate() method returned a value of 35. I was expecting a value in the range of 0-1, so there’s something up still.

Anyone have any ideas? Since I checked “reduce encoding” and used a value of 5 with samples per average, should I be feeding the feedforward something different than rps?

You need to further increase the samplesPerAverage setting for the encoder. The noise you’re seeing is probably from discretization error on the clock.

If you nab the most recent SysId from github it’ll estimate your sensor delay, too.

The feedforward constants are always going to be in units of volts; if you want unit scale you’ll have to divide. 35 volts is still unreasonable, though; look at your graph axes and you’ll see your max steady state speed is nowhere near 33 RPS (it caps out at around 7, looks like?).

Thanks, i’ll have to dive into that.

In meantime I scaled the feedforward output and it’s giving me a consistent value where multiplied by 0.9 is holding the motors at 90% of the setpoint. So that’s great.

Problem is, the bang bang takes over and keeps oscilating the power to full. I understand this is the expected behavior but if I target 1000 rpm, i get a range of 900-1300rpm depending on where it is in the oscilation cycle. We are getting very inconsistent shots and I think it’s because of this.

Any ideas on how to better control the a shooter?

With feed forward keeping the power so close to the setpoint would a PID controller be enough to bring it the rest of the way? I’ve read that bang bang is better for a shooter, but the RPMs range wildly! Any thoughts would be appreciated. Thank you.

Depends on a lot of things. If the oscillation is too large you probably need to increase the flywheel mass or decrease the measurement delay.

A proportional controller will also work.

Thanks Oblarg, thinking through it, a decreased measurement delay would mean less time that the bangbang is sending a full power signal to the motor controllers. Any suggestions on how to decrease that delay? Is that something in the encoder that I even have control over? I can do the research if you could be so kind as to point me to where in the system that delay is configured or occuring.

Depends on your choice of motor controller and encoder; you’ll have to consult their documentation. Note that the NEO integrated sensors have a hardcoded filter on their velocity measurements that adds about 110ms of signal delay.

I’m using a rev through bore plugged into dio ports on the rio with the Encoder class in wpilib. I read that this is faster than using the spark max calculations with the neo integrated encoders. Was that wrong?

That is correct; you might be seeing discretization noise from the rio clock in that case; try increasing the samplesToAverage setting on the Encoder class.

1 Like