Improving update rate on CANSparkMax motor controllers for PID controller

Hey all,
So our team is running a shooter on PID control, and have encountered errors symptomatic of not updating quickly enough. What is the best way to overcome or circumvent the limit of a 50ms update delay for the CANSparkMax motor controllers?

Thank you!

1 Like

It would probably help to describe the symptoms you are seeing and also provide some specifics on your shooter (how it’s powered, any gearing, approx. moment of inertia – just a description of what’s spinning).

Are you using onboard or external PID control? Trying to use set calls is a perfectly functional option with a roboRIO-side PID controller but you may also want to try tuning the onboard controller and using that instead, especially if only interacting with velocity control. The encoders being directly connected to the motor controller and factored into internal loops are going to be faster than CAN bus propagation 100% of the time.

We are using the CANPIDController’s .setReference() method, I’m assuming that’s what you mean by onboard control. Is the fact that we’re using this method and still finding the update time lethargic symptomatic of some other time-consuming code?

Oh, and answering nuttle’s question, our robot has a single flywheel, a colson wheel, being driven by two neo’s at a 24:10 gear ratio. It is firing Power Cells with moderate compression.

How is the lethargy of control response exhibited? Call timeouts in your robot code, slow to spinup / change target, or something else? setReference() shouldn’t cause significant lag but if you’re calling it every robot loop, depending on how the REV libraries work, may not cache identical values and actually try to set every loop, causing delays.

1 Like

What encoders are you using? The internal NEO encoders have an non-configurable filtering delay.

1 Like

If you are running velocity control on the SPARK MAX, you set the various parameters and then setReference() – at that point, the RIO is out of the loop, and you don’t need to call setReference() repeatedly, only when you are trying to vary the desired velocity. So, the only other code that’s relevant is on the controller.

Using an external encoder with higher resolution is generally helpful, but may not make much difference in some scenarios. For a given RPM, you can work out how many encoder transitions will happen in 50ms. There are 42 of these per revolution of the motor, and you have to account for the gearing.

Looking at: SPARK-MAX-Documentation/ at master · REVrobotics/SPARK-MAX-Documentation · GitHub, k[Alt]EncoderAverageDepth and k[Alt]EncoderSampleDelta are of interest. But the non-“Alt” forms of these seem to be for brushed motors (with an external encoder) and the “Alt” forms certainly require an external encoder (with a brushless motor). So this is another way an external encoder might help. I don’t know enough about the controller internals or the specific setup you are using to be sure here though; see the other comment on this (above).

It’s also possible that shooting slows things down enough that no matter what the controller does, it’s going to take time to recover. So it is helpful to know if the problem is variation in velocity while not shooting, or if you are trying to address something you see when you shoot. And, it can also make a difference how you are measuring the velocity. Are you reading data from the controller, using an external tachometer, or going by symptoms you see in the overall mechanisim?

These seem to roughly align if you ask me, based on a very similar configuration on our shooter wheel in 2020.

That being said, keep in mind… 50ms is 2.5 control loops on the RIO. While that’s not completely trivial, it’s also quite small - in the region where things like CAN bus delay, code order of operations, how you extract out and analyze the desired/actual speed timeseries signals… all this can start to stack up.

All that being said -

A couple concrete scenarios that fit this description:

  1. RIO has unexpected delay between executing a line of code, and sending a CAN bus message
  2. SparkMax has unexpected delay between receiving a CAN bus message and changing the motor output voltage
  3. SparkMax has unexpected delay between a change in mechanism motion, and reading it with some sensor (internal encoder? external encoder? limit switch?)
  4. SparkMax has unexpected delay between reading a change in the world in a sensor, and transmitting a corresponding CAN bus message
  5. RIO has unexpected delay between getting a CAN bus message, and changing the value in your software which you are reading

I’m not sure if all of these apply. Not all are easy to measure… for example, using something like the REV hardware client will take certain delays out of the loop, but add new ones.

The key though is that the correct “knobs” to adjust depend on which delay you’re attempting to attack.

For example, 1 and 5 are both dependent on the underlying hardware/software in the RIO which reads CAN bus electrical signals and queues messages back and forth in software. FWIW I doubt the issue is here, given others haven’t found major issues, but that’s not to discard the possibility you’ve uncovered something new.

Nuttle’s advice applies a lot to 3, but assumes external encoder (internal encoder has no corresponding knobs and, indeed, has a non-trivial amount of filtering on it).