-
Notifications
You must be signed in to change notification settings - Fork 16
New and better PID framework for using SmartMotion PID with a CanSparkMax #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
fcb0386
81dd9c7
db7eb64
1837647
b41a5b9
b589b22
ac72e46
6ebaf64
e3965f4
48dd06a
fa1d671
21b05d0
7ce2fba
14db653
c6f6d36
5bbdf08
4de9cf4
7a2d5b6
ddbb139
5bbebca
1f7c1bc
9b8bdbb
761553a
1a4285e
28e426e
01d079e
d8dcc62
e08dfda
dd4ca19
e82f043
fc78f8f
ed5d4d0
65ee961
f5b38a9
1ea1b55
98dd22b
9bdb154
277e4bf
89c5df7
2a26fce
a016497
65f3aa5
cb3c948
885b37c
6456782
4724223
ad12708
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.team766.controllers; | ||
|
|
||
| import com.ctre.phoenix.motorcontrol.NeutralMode; | ||
|
|
||
| public class CanSparkMaxSmartMotionLinearPIDController extends ExtendableCanSparkMaxSmartMotionPIDController { | ||
|
|
||
| private final double constantAntigravPower; | ||
|
|
||
| public CanSparkMaxSmartMotionLinearPIDController(final String configName, final double absEncoderOffset, final double absEncoderOffsetForZeroEncoderUnits, final OffsetPoint first, final OffsetPoint second, final double antigravityPower, final NeutralMode defaultNeutralMode) { | ||
| super(configName, absEncoderOffset, absEncoderOffsetForZeroEncoderUnits, first, second, defaultNeutralMode); | ||
| constantAntigravPower = antigravityPower; | ||
| } | ||
|
|
||
| public void runPIDs(){ | ||
| updateAntigrav(constantAntigravPower); | ||
| run(); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.team766.controllers; | ||
|
|
||
| import java.util.*; | ||
| import com.ctre.phoenix.motorcontrol.NeutralMode; | ||
|
|
||
| public class CanSparkMaxSmartMotionRotationalPIDController extends ExtendableCanSparkMaxSmartMotionPIDController { | ||
|
|
||
| private final double degreesToEncoderUnitsRatio; | ||
| private final double antiGravK; | ||
|
|
||
| public CanSparkMaxSmartMotionRotationalPIDController(final String configName, final double absEncoderOffset, final double absEncoderOffsetForZeroEncoderUnits, final OffsetPoint first, final OffsetPoint second, double degToEncoderUnitsRatio, double antigravityCoefficent, NeutralMode defaultNeutralMode) { | ||
| super(configName, absEncoderOffset, absEncoderOffsetForZeroEncoderUnits, first, second, defaultNeutralMode); | ||
| antiGravK = antigravityCoefficent; | ||
| degreesToEncoderUnitsRatio = degToEncoderUnitsRatio; | ||
| } | ||
|
|
||
|
|
||
| private double euToDegrees(final double eu) { | ||
| return eu * degreesToEncoderUnitsRatio; | ||
| } | ||
|
|
||
| public void runPIDs(){ | ||
| updateAntigrav(antiGravK * Math.sin(euToDegrees(getHallSensorPosition()))); | ||
| run(); | ||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| package com.team766.controllers; | ||
|
|
||
| import com.team766.config.ConfigFileReader; | ||
| import com.team766.hal.RobotProvider; | ||
| import edu.wpi.first.math.MathUtil; | ||
| import com.revrobotics.SparkMaxAbsoluteEncoder; | ||
| import com.revrobotics.SparkMaxPIDController; | ||
| import com.revrobotics.CANSparkMax.ControlType; | ||
| import com.revrobotics.CANSparkMax.IdleMode; | ||
| import com.revrobotics.SparkMaxAbsoluteEncoder.Type; | ||
| import com.ctre.phoenix.motorcontrol.NeutralMode; | ||
| import com.revrobotics.CANSparkMax; | ||
| import com.team766.hal.MotorController; | ||
| import com.team766.controllers.PIDState.*; | ||
| import com.team766.framework.Exceptions.*; | ||
|
|
||
|
|
||
| public abstract class ExtendableCanSparkMaxSmartMotionPIDController { | ||
|
maxspier marked this conversation as resolved.
|
||
| /* | ||
| * These are the hardware variables for the hardware we have on the robot. | ||
| * It includes a CanSparkMax, MotorController of any type, and a SparkMaxAbsoluteEncoder. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes it sound like the CanSparkMax and MotorController are different devices
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought they were? Motorcontroller is like the rev neo and CSM is the motorcontroller. This is bad naming. We should fix it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are the same device. They are even the same Java object - it is a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i.e. why you can do
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting. From past testing, this is what works with Maroon Framework. We could try it the other way, but I'm afraid that removing the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ryan's right; you can actually do away with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whatever methods and properties
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get this - but I think that the way we use the motor stuff from the config file makes this work better. |
||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| private MotorController mc; | ||
| private CANSparkMax csm; | ||
| private SparkMaxAbsoluteEncoder abs; | ||
|
|
||
| /* | ||
| * These are PID related variables that we will store locally to run PIDs | ||
| * We also create an object for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| private SparkMaxPIDController pid; | ||
| private double deadzone = 0; | ||
| private double setPointPosition = 0; | ||
| private double comboOfTimesInsideDeadzone = 0; | ||
| private double minPos = 0; | ||
| private double maxPos = 0; | ||
|
|
||
| /* | ||
| * These are some variables that we can use so that we can reset the motor controller | ||
| * We use this so that we can get an accurate ratio for the hall encoder units to absolute encoder units. | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| private double encoderUnitsPerOneAbsolute = 0; | ||
|
maxspier marked this conversation as resolved.
|
||
|
|
||
| private double curAntigrav = 0; | ||
|
|
||
| private NeutralMode defaultNeutralMode; | ||
|
|
||
|
|
||
| /* | ||
| * The state of the PID controller so that we know whether to call the respective method for each state. | ||
| * For more documentation, see the PIDState class. | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
|
|
||
| private PIDState theState = PIDState.OFF; | ||
|
|
||
| /* | ||
| * Default constructor for the class. This is used to create a new CanSparkMaxSmartMotionPIDController object | ||
| * @param configName the configuration name for the motorcontroller | ||
| * @param absEncoderOffset the offset (ranging from 0.000 - 0.999) of the absolute encoder | ||
| * @param absEncoderOffsetForZeroEncoderUnits the offset that the absolute encoder should have in order for the relative encoder on the motorcontroller to equal zero. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in what case would
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want the arm to have 0 hall encoder units when the absolute encoder is at 0.362, so 0.362 is the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why wouldn't we set
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could if we wanted, but this makes it easier if, for example, a person installs the encoder wrong and it is off by 0.203 units, so we can use this to reset that. Even if we wanted 0 to be 0, it isn't much more work to add that in, and later we could also overload the constructor to make that default 0.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the motor encoder does not have a persistent offset - it just measures changes in position. you call so the only way to install it wrong would be to have it inverted (so moving the mechanism results in increasing values instead of decreasing and vice versa).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I don't understand what your saying -- lets talk on Saturday |
||
| * @param first the first offsetpoint used to reset the encoders - for more info see the OffsetPoint class | ||
| * @param second the second offsetpoint used to reset the encoders - for more info see the OffsetPoint class | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
|
|
||
|
|
||
| public ExtendableCanSparkMaxSmartMotionPIDController(final String configName, final double absEncoderOffset, final double absEncoderOffsetForZeroEncoderUnits, final OffsetPoint first, final OffsetPoint second, final NeutralMode defaultNeutralMode) { | ||
| try { | ||
| this.defaultNeutralMode = defaultNeutralMode; | ||
| mc = RobotProvider.instance.getMotor(configName); | ||
| csm = (CANSparkMax) mc; | ||
| pid = csm.getPIDController(); | ||
| abs = csm.getAbsoluteEncoder(Type.kDutyCycle); | ||
| abs.setZeroOffset(absEncoderOffset); | ||
|
|
||
| double absoluteDifference = second.getAbsoluteValue() - first.getAbsoluteValue(); | ||
| double motorEncoderDiference = second.getMotorEncoderValue() - first.getMotorEncoderValue(); | ||
|
|
||
| encoderUnitsPerOneAbsolute = motorEncoderDiference / absoluteDifference; | ||
|
|
||
| double relEncoder = absToEu(abs.getPosition() - absEncoderOffsetForZeroEncoderUnits); | ||
|
|
||
| mc.setSensorPosition(relEncoder); | ||
|
|
||
| mc.setNeutralMode(defaultNeutralMode); | ||
| } catch (IllegalArgumentException ill) { | ||
| throw new AbstractPIDRuntimeException("Error instantiating the PID controller: " + ill); | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * Method to convert the absolute encoder units into hall encoder units | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| private double absToEu(final double abs) { | ||
|
|
||
| if(encoderUnitsPerOneAbsolute == 0){ | ||
| throw new AbstractPIDArithmeticDevideByZeroException("Error: encoderUnitsPerOneAbsolute is zero."); | ||
| } | ||
| return encoderUnitsPerOneAbsolute * abs; | ||
| } | ||
|
|
||
| /* | ||
| * This is a method to get the hall sensor position from the motorcontroller, so we can use it in the antigravity calculations in the subclass. | ||
| * @return the value of the hall sensor position | ||
| * @author Max Spier - 9/13/2023 | ||
| */ | ||
| protected double getHallSensorPosition(){ | ||
| return mc.getSensorPosition(); | ||
| } | ||
|
|
||
| /* | ||
| * Method to change all PIDF values at once | ||
| * @params p-ff the values to set each one for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setPIDF(final double p, final double i, final double d, final double ff) { | ||
| pid.setP(p); | ||
| pid.setI(i); | ||
| pid.setD(d); | ||
| pid.setFF(ff); | ||
| } | ||
|
|
||
| /* | ||
| * Method to change the P value of the PIDController | ||
| * @param p the value of P to set for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setP(final double p) { | ||
| pid.setP(p); | ||
| } | ||
|
|
||
| /* | ||
| * Method to change the I value of the PIDController | ||
| * @param i the value of I to set for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setI(final double i) { | ||
| pid.setI(i); | ||
| } | ||
|
|
||
| /* | ||
| * Method to change the D value of the PIDController | ||
| * @param d the value of D to set for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setD(final double d) { | ||
| pid.setD(d); | ||
| } | ||
|
|
||
| /* | ||
| * Method to change the FF value of the PIDController | ||
| * @param ff the value of FF to set for the PIDController | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setFF(final double ff) { | ||
| pid.setFF(ff); | ||
| } | ||
|
|
||
| /* | ||
| * Method to update the value of the deadzone for the PIDController | ||
| * This deadzone is in hall encoder units. | ||
| * @param dz the value of the deadzone in hall encoder units | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setDeadzone(final double dz) { | ||
| deadzone = dz; | ||
| } | ||
|
|
||
| /* | ||
| * This is a method to update the minimum and maximum output ranges of motorpower for the mechanism | ||
| * @param min the minimum value for motor power (make sure that it is negative if you want the mechanism to be able to go both directions) | ||
| * @param max the maximum value for motor power (make sure that it is positive if you want the mechanism to be able to go both directions) | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setOutputRange(final double min, final double max) { | ||
| pid.setOutputRange(min, max); | ||
| } | ||
|
|
||
| /* | ||
| * This is the method to set the minimum and maximum locations of the mechanism. | ||
| * Please check your subclasses documentation for what units this is supposed to be in (usually whichever one is used as an input for the setSetpoint method) | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setMinMaxLocation(final double min, final double max) { | ||
| maxPos = max; | ||
| minPos = min; | ||
| } | ||
|
|
||
| /* | ||
| * This method is used to set the maximum velocity of the mechanism for use with PID | ||
| * @param max the value of the maximum amount of velocity for the motorcontroller | ||
| * The method also sets the minimum velocity to zero, as a failsafe. | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setMaxVelocity(final double max) { | ||
| pid.setSmartMotionMaxVelocity(max, 0); | ||
| pid.setSmartMotionMinOutputVelocity(0, 0); | ||
| } | ||
|
|
||
| /* | ||
| * This is the method to set the maximum acelleration of the robot for the PIDController | ||
| * @param max the value for the maximum acelleration | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setMaxAccel(final double max) { | ||
| pid.setSmartMotionMaxAccel(max, 0); | ||
| } | ||
|
|
||
| /* | ||
| * This is the method where you update the setpoint position from the subclass | ||
| * @param x the setpoint | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void setNewSetpoint(double x){ | ||
| x = MathUtil.clamp(x, minPos, maxPos); | ||
| setPointPosition = x; | ||
| } | ||
|
|
||
| /* | ||
| * Failsafe for the PIDController to stop it. | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void stop() { | ||
| theState = PIDState.OFF; | ||
| } | ||
|
|
||
| /* | ||
| * This method does the antigrav | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| private void antigrav(){ | ||
| mc.set(curAntigrav); | ||
| } | ||
|
|
||
| /* | ||
| * This method sets the antigrav power amount. | ||
| * It gets the value for this from the subclass | ||
| * @param passedValueFromSubclass the power to set the motorcontroller for antigrav | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| protected void updateAntigrav(double passedValueFromSubclass){ | ||
| curAntigrav = passedValueFromSubclass; | ||
| } | ||
|
|
||
| public void disableAntigrav(){ | ||
| curAntigrav = 0; | ||
| deadzone = 0; | ||
| } | ||
|
|
||
| /* | ||
|
maxspier marked this conversation as resolved.
|
||
| * This is the run loop that actually runs the PID Controller | ||
| * You need to call this as frequently as possible when running the mechanism | ||
| * @author Max Spier - 9/9/2023 | ||
| */ | ||
| public void run() { | ||
| switch (theState) { | ||
| case OFF: | ||
| mc.set(0); | ||
| break; | ||
| case ANTIGRAV: | ||
| if (setPointPosition <= (deadzone + mc.getSensorPosition()) && setPointPosition >= (mc.getSensorPosition() - deadzone)) { | ||
| antigrav(); | ||
| } else { | ||
| theState = PIDState.PID; | ||
| } | ||
| case PID: | ||
| if (setPointPosition <= (deadzone + mc.getSensorPosition()) && setPointPosition >= (mc.getSensorPosition() - deadzone)) { | ||
| comboOfTimesInsideDeadzone++; | ||
| } else { | ||
| comboOfTimesInsideDeadzone = 0; | ||
| pid.setReference(setPointPosition, ControlType.kSmartMotion); | ||
| } | ||
|
|
||
| if (comboOfTimesInsideDeadzone >= 6) { | ||
| theState = PIDState.ANTIGRAV; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need a flag to disable antigrav if its not needed also, I realized Brake mode is missing
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. finished brake
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. finished all
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you did not push your changes
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no i did
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how does it look? |
||
| } | ||
| break; | ||
| default: | ||
| throw new AbstractPIDRuntimeException("Invalid state error"); | ||
| } | ||
| } | ||
|
|
||
| public String toString(){ | ||
| return "Setpoint: " + setPointPosition + " Current Position: " + mc.getSensorPosition() + " Current State: " + theState; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package com.team766.controllers; | ||
| /* | ||
| * This is a class to make an offset point for the PID controller to be able to reset encoders. | ||
| * You input the absoluteEncoderValue that equals the motorEncoderValue. | ||
| * @author Max Spier - 9/12/23 | ||
| */ | ||
| public class OffsetPoint { | ||
|
maxspier marked this conversation as resolved.
maxspier marked this conversation as resolved.
|
||
|
|
||
| private double absoluteValue, motorValue; | ||
|
|
||
| public OffsetPoint(final double absoluteEncoderValue, final double motorEncoderValue) { | ||
| absoluteValue = absoluteEncoderValue; | ||
| motorValue = motorEncoderValue; | ||
| } | ||
|
|
||
| public double getAbsoluteValue() { | ||
| return absoluteValue; | ||
| } | ||
|
|
||
| public double getMotorEncoderValue() { | ||
| return motorValue; | ||
| } | ||
|
|
||
| /* | ||
| *@Override | ||
| *@author Max Spier - 9/15/2023 | ||
| */ | ||
| public String toString(){ | ||
| return "Absolute Value: " + absoluteValue + " Motor Value: " + motorValue; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.team766.controllers; | ||
|
|
||
| public enum PIDState { | ||
| PID, | ||
| OFF, | ||
| ANTIGRAV | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.team766.framework.Exceptions; | ||
|
|
||
| public class AbstractPIDArithmeticDevideByZeroException extends RuntimeException{ | ||
| public AbstractPIDArithmeticDevideByZeroException (String errorMessage) { | ||
| super(errorMessage); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.team766.framework.Exceptions; | ||
|
|
||
| public class AbstractPIDRuntimeException extends RuntimeException { | ||
|
maxspier marked this conversation as resolved.
|
||
| public AbstractPIDRuntimeException (String errorMessage) { | ||
| super(errorMessage); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.