Spark MAX PID

Hey all, my team created the basic subsystems and commands required to drive the Spark MAXs, but now we are starting to get to the more difficult aspects - especially PID. For the last few years, we have used Motion Magic on the talons to control the speed of the motors. With the new Sparks, we have no idea how to use the CANPIDController or CANSparkMax classes to get an output of speed. The documentation seems to be lacking on their website, does anyone have any advice/code examples in Java?

So you have to create a CANPIDController from a CANSparkMax object, then you can set up a typical closed loop velocity control (which is similar to velocity control on the srx, a little less powerful than motion magic) by setting your PIDF gains in the controller, setting the output range, and then continuously calling the set reference command with your target velocity, velocity mode, gain set, and any arbitrary feed forward.

I postulate you could make something more similar to motion magic with a carefully tuned nested position and velocity loop (since the sparkmax is supposed to support nested loops on board) with a ramp rate, but we haven’t tried that out yet.

Thank you!

Actually, I have one more question:

What control type should we be using for going a distance? I don’t understand if we should use kVelocity or kPosition. Also, what inputs would I put in for the value of either? Ticks I want to travel, my error, or something else?

1 Like

Well that’s kind of up to you, you could monitor the distance traveled on your Rio and schedule your velocity to reach that distance, or you could just use distance mode.

The units on the velocity set are in RPM - I don’t have one of ours handy and I don’t see it in the API docs, but I imagine that distance set is in revolutions.

One trick that caught us is that you also have to use setoutputrange if you’re using controlled loop control since it defaults to 0, I would recommend starting with the full -1 to 1 range for starters and playing from there.

As to the cascading loop possibility, while the API hints at it it doesn’t -seem- like it’s usable yet, I left a question in the Rev API thread about it.

1 Like

My team used the WPI PIDController class last year. I understand what you are saying thus far but am confused how the CANPIDController class actually connects to setting speeds of the CANSparkMax class. In the PIDController class, there was a get() method to get the calculated error and adjust the motor controllers. There was also a method onTarget(), which returned if the PID had reached the desired reference, that was put in the isFinished() part of the command.

How do these commands translate to the CANPIDController and CANSparkMax class or is this somehow built in through other methods?

So the key difference between using the WPILib PID and either the talon srx or spark max ones is that in the on controller case the PID itself is run on the speed controller itself - saving you CPU time on your Rio and also running at much faster rates (1000 hz in the case of the SRX - I’m not actually sure on the spark max).

When you issue commands configuring the PID on motor controllers CAN commands are actually issued to the speed controller loading the gains and configuring the loop on that side .

When you make a PIDController in the Sparkmax library, you have to either provide it a sparkmax reference in it’s constructor or use a sparkmax object to get a pointer to it’s PID controller, after that using the member functions of the PID controller to set gains or using set reference to provide the setpoint causes commands to be issued straight to the motor controller.

1 Like

An update: we got our PID command based on distance to work. It took a lot of messing around, but eventually everything clicked.

Could you explain how you organized the CANPIDController and CANEncoder code? I have created a Command for driving a certain distance organized in the way stated above but I keep getting

ERROR 1 DifferentialDrive…Output not updated often enough

Also what are your configurations for the Spark Max on the Spark Max client?

The problem I am having is that the command doesn’t seem to reach the end(). I have the setReference(pos, ControlType.kPosition) in both the initialize() and execute(). Why would the motor not stop after the motor controller becomes on target?

I don’t have my code on me and I might be able to just send that latwr, but our settings have 40a current limit, the motors are on brake mode, and limit switches are turned off. Everything else is default.

What are your gains?

P = 1
The rest are default which I believe is 0
What do you recommend for I and D?

1 Like

When tuning a position loop you should always start with just P - 1 in my limited experience with these controllers so far though is really high, you should start with something like .00001 and move up from there until you see the behavior you desire (depending on the mechanics of your mechanism)

Ok thank you, I have got the motor rotating a certain amount. I think the problem I was running into was having a DifferentialDrive object being used in the default command.

Sorry for all the questions, but is there away to have this automated driveDistance command override the manual drive command?

Also how can I reset the encoder to zero before running the driveDistance? I know there isn’t a method for it so is there another way or do I always have to compute distance relative to the current encoder count?

I am asking because there may some times when we want to push a button and have automated commands help us align to specific game elements during teleop

They currently do not have a reset command. I saw a comment on their help thread that said it was work in progress, so I would expect it soon. For now, try to make the value you need relative to when you start.

1 Like