Spark Max internal PID not working as expected [LabVIEW]

Hello, I am trying to tune a PIDF on the spark max for a flywheel using two neos on a 1:1 reduction and using the internal encoder to read speed. Currently I have a setup that works but still takes too long to spin up.

When I was trying to tune the PIDF to get a better spin up time, I found some things that make me believe the PID is not working as shown on this diagram.

I tried to set all values to zero and begin with only P, but it is not behaving like a P controller should. When I give it a setpoint, the output goes straight to 40% and does not change. The steady-state error is actually even negative, with the measured value overshooting the setpoint, yet the output still stays positive. Basically it is working as if it only had an F value.

A similar thing happens if I have all values to zero and a small I. The measured value starts working as you would expect, by ramping up very slowly and going up to the setpoint, but when it gets there, it keeps rising.

It is looking like the error is not actually being calculated as zero when the measured value reaches the setpoint, which is pretty odd.

I have already had a look at this thread and also this thread, but they don’t mention anything similar to what I have been seeing here. It is also complicated to debug the PIDF block as it only sends the value to the cam and the code is executed outside of the roborio.

Has anyone experienced similar issues with the spark max using LabVIEW?

It sounds like you’re trying to control a flywheel with feedback and no feedforward. This won’t work.

Get your feedforward control as close as you can without feedback, and then add proportional feedback on top of it. For flywheel control, you should not need anything other than proportional gain (integral gain is unstable, and derivative gain doesn’t gain you any additional control over the response at a constant setpoint).

Be aware that the NEOs have a 110ms measurement delay on their velocity readings that is hardcoded into the SparkMax. This renders the measured velocity fairly useless for feedback control.

1 Like

Here’s an example form our 2020 robot which was a two wheel shooter.
Your gains will differ as your flywheel and mechanical losses will be different.
Ours were P Gain=0.0004, F Gain =0.00017

This version just used the joystick throttles to adjust rpm. Another version just took top and bottom rpms typed by the driver at the Dashboard.

1 Like

Is there any specific reason for the logic around that true/false block to only set the constants when they change?

One thing we were speculating on was that maybe something is going wrong because we are setting the PID constants continuously.

Why are you doing this? Gain scheduling is usually a design smell.

The programmers that first coded this have told me it is done inside the loop so we can tune the PID on the fly and not have to reset everything whenever we change any values.

I guess they haven’t considered doing something like Mark did to only set it when there are actual changes.

Every time you call a pidf set gains command it sends a message out through the can bus, taking up cpu time and causing unnecessary congestion. Multiply that with doing that with multiple controllers and you quickly saturate your CAN bus and waste precious cpu cycles

Agreed. I am going to the lab right now and in around 30 min I’ll come back with a report on whether stopping the configuration spam helps it.

You shouldn’t change gains while mechanisms are in-motion in an ad-hoc manner. This is unsafe.

I doubt that will fix it, I was just commenting on why Marks code looked the way it did.

I have tried stopping the spam of configuration, and the weird behavior is still there.

This image represents well what is weird here:

The only gain that is not zero is P. The setpoint is at 1900 RPM and the actual flywheel speed is closer to 2000 RPM. Meanwhile the output is still positive. On a purely P controller, this should not be possible.

Is there something obvious that I am missing?

Edit: Just FYI, where it says current is actually the motor output, we just didn’t change the name while we were testing this.

Why is I Zone 100000?

According to the programmers, they set it this high to not limit the operation of the I gain. It seems a bit exaggerated, but it shouldn’t impact this P-only controller. Or do you think it might be?

With an I of 0.0 it probably doesn’t matter, but on general debugging principle it’s always clearer to have everything not being used set to zero.

Does the rpm chart change when you change your setpoint?
There may be a minor issue with the math in the code than gives you a slight increase over the value you expected. If changing the setpoint is followed by the chart output then it may just be a plotting issue.

Will zero the I to avoid any interactions with the rest of the PID.

The chart is working like you would expect, I can see the rpm ramping up to this value.

I will have a look to see if there is anything that could skew the chart. Some other things we will check is if there is any configuration on the spark that might be conflicting with what the robot is telling it to do and also improve the code to stop spamming the set output.

For that second bit I need to do a bit of refactoring. After that I will be able to share the code in a readable way too. I’ll be back later on with updates.

Thanks for the help so far :slight_smile:

Hello everyone,

Later happened to be quite a bit later. We had an international trip and a regional coming up, so there was not much time to write a decent report. Now that we are back I want to leave our results here for any time travelers from the future doing research.

On the Spark Max’s PID

For our shooter flywheel it has been pretty disappointing. Like I have shown on the post before, it still does some things that a PID should not. We haven’t been able to pinpoint why. At the moment we have been able to tune it with P and F to get a decent result. It can repeatedly get to a set speed (even though it is higher than our actual setpoint) and it can be very stable.

The main problems still present on our setup are that it never sends over 0.4 to the motors and that appears to be making the recovery a bit worse than it should.

Our Ugly Fix

The worst problem we had was underusing the neos we had, by sending a maximum of 40% voltage to them, the control loop was capping our power at 16%, which is pretty absurd. We have managed to fix that by starting with a bang bang control and moving to a PID.

We first started by setting the motor output to 1 as long as the measured speed was under 80% of the setpoint. This made sure that we could cut most of the time for speeding up and then move to a proper control loop to get a stable speed.

We found that the neos would make very unhappy noises when we sent 100% power to them. It looked like there was some heavy slipping going on at the motor’s rotor. The measured speed also went all over, which was probably unsettling our very advanced control loop. To fix that we also implemented a soft starter on our control loop. Basically when we start up the motors we send 0.6 to the motors for 100ms so they start moving and we decrease the impact created by the bang bang part of the controller.

All of this improved our shooter performance a lot, but I am still not very happy with it. We have a long list of improvements for worlds, but if we can get around to this, I would like to still improve the stability and recovery on this controller.

Thanks for all the help so far and I am open for suggestions so far on how to improve this system.

Are you using the internal NEO sensor? There’s ~110ms of lag due to the hardcoded firmware filter…

Just to be clear. If you use ONLY a feed forward term, you should get to your setpoint. If you put in a stupidly high setpoint, you should see the motor output go to 1.

Are you seeing that behavior?

We are using the internal NEO sensors for our flywheel controlled with a feedforward and a proportional feedback and we do not have this issue…

You definitely do have the issue; you likely have chosen a proportional gain small enough that it is not really doing anything and you are effectively running open-loop control.

There’s no escaping it, and the math is not pretty.