2023-02-13 17:21:24 -06:00
package swervelib.motors ;
2023-02-13 14:37:05 -06:00
import com.revrobotics.AbsoluteEncoder ;
import com.revrobotics.CANSparkMax ;
import com.revrobotics.CANSparkMax.ControlType ;
import com.revrobotics.CANSparkMax.IdleMode ;
import com.revrobotics.CANSparkMaxLowLevel.MotorType ;
import com.revrobotics.CANSparkMaxLowLevel.PeriodicFrame ;
import com.revrobotics.RelativeEncoder ;
import com.revrobotics.SparkMaxPIDController ;
2023-02-13 17:21:24 -06:00
import swervelib.encoders.SwerveAbsoluteEncoder ;
import swervelib.parser.PIDFConfig ;
2023-02-13 14:37:05 -06:00
/ * *
* An implementation of { @link CANSparkMax } as a { @link SwerveMotor } .
* /
public class SparkMaxSwerve extends SwerveMotor
{
/ * *
* SparkMAX Instance .
* /
public CANSparkMax motor ;
/ * *
* Integrated encoder .
* /
public RelativeEncoder encoder ;
/ * *
* Absolute encoder attached to the SparkMax ( if exists )
* /
public AbsoluteEncoder absoluteEncoder ;
/ * *
* Closed - loop PID controller .
* /
public SparkMaxPIDController pid ;
/ * *
* Factory default already occurred .
* /
private boolean factoryDefaultOccurred = false ;
/ * *
* Initialize the swerve motor .
*
* @param motor The SwerveMotor as a SparkMax object .
* @param isDriveMotor Is the motor being initialized a drive motor ?
* /
public SparkMaxSwerve ( CANSparkMax motor , boolean isDriveMotor )
{
this . motor = motor ;
this . isDriveMotor = isDriveMotor ;
factoryDefaults ( ) ;
clearStickyFaults ( ) ;
encoder = motor . getEncoder ( ) ;
pid = motor . getPIDController ( ) ;
pid . setFeedbackDevice ( encoder ) ; // Configure feedback of the PID controller as the integrated encoder.
motor . setCANTimeout ( 0 ) ; // Spin off configurations in a different thread.
}
/ * *
* Initialize the { @link SwerveMotor } as a { @link CANSparkMax } connected to a Brushless Motor .
*
* @param id CAN ID of the SparkMax .
* @param isDriveMotor Is the motor being initialized a drive motor ?
* /
public SparkMaxSwerve ( int id , boolean isDriveMotor )
{
this ( new CANSparkMax ( id , MotorType . kBrushless ) , isDriveMotor ) ;
}
/ * *
* Set the voltage compensation for the swerve module motor .
*
* @param nominalVoltage Nominal voltage for operation to output to .
* /
@Override
public void setVoltageCompensation ( double nominalVoltage )
{
motor . enableVoltageCompensation ( nominalVoltage ) ;
}
/ * *
* Set the current limit for the swerve drive motor , remember this may cause jumping if used in conjunction with
* voltage compensation . This is useful to protect the motor from current spikes .
*
* @param currentLimit Current limit in AMPS at free speed .
* /
@Override
public void setCurrentLimit ( int currentLimit )
{
motor . setSmartCurrentLimit ( currentLimit ) ;
}
/ * *
* Set the maximum rate the open / closed loop output can change by .
*
* @param rampRate Time in seconds to go from 0 to full throttle .
* /
@Override
public void setLoopRampRate ( double rampRate )
{
motor . setOpenLoopRampRate ( rampRate ) ;
motor . setClosedLoopRampRate ( rampRate ) ;
}
/ * *
* Get the motor object from the module .
*
* @return Motor object .
* /
@Override
public Object getMotor ( )
{
return motor ;
}
/ * *
* Queries whether the absolute encoder is directly attached to the motor controller .
*
* @return connected absolute encoder state .
* /
@Override
public boolean isAttachedAbsoluteEncoder ( )
{
return absoluteEncoder ! = null ;
}
/ * *
* Configure the factory defaults .
* /
@Override
public void factoryDefaults ( )
{
if ( ! factoryDefaultOccurred )
{
motor . restoreFactoryDefaults ( ) ;
factoryDefaultOccurred = true ;
}
}
/ * *
* Clear the sticky faults on the motor controller .
* /
@Override
public void clearStickyFaults ( )
{
motor . clearFaults ( ) ;
}
/ * *
* Set the absolute encoder to be a compatible absolute encoder .
*
* @param encoder The encoder to use .
* @return The { @link SwerveMotor } for easy instantiation .
* /
@Override
public SwerveMotor setAbsoluteEncoder ( SwerveAbsoluteEncoder encoder )
{
if ( encoder . getAbsoluteEncoder ( ) instanceof AbsoluteEncoder )
{
absoluteEncoder = ( AbsoluteEncoder ) encoder . getAbsoluteEncoder ( ) ;
pid . setFeedbackDevice ( absoluteEncoder ) ;
}
return this ;
}
/ * *
* Configure the integrated encoder for the swerve module . Sets the conversion factors for position and velocity .
*
* @param positionConversionFactor The conversion factor to apply .
* /
@Override
public void configureIntegratedEncoder ( double positionConversionFactor )
{
if ( absoluteEncoder = = null )
{
encoder . setPositionConversionFactor ( positionConversionFactor ) ;
encoder . setVelocityConversionFactor ( positionConversionFactor / 60 ) ;
// Taken from https://github.com/frc3512/SwerveBot-2022/blob/9d31afd05df6c630d5acb4ec2cf5d734c9093bf8/src/main/java/frc/lib/util/CANSparkMaxUtil.java#L67
configureCANStatusFrames ( 10 , 20 , 20 , 500 , 500 ) ;
} else
{
absoluteEncoder . setPositionConversionFactor ( positionConversionFactor ) ;
absoluteEncoder . setVelocityConversionFactor ( positionConversionFactor / 60 ) ;
}
}
/ * *
* Configure the PIDF values for the closed loop controller .
*
* @param config Configuration class holding the PIDF values .
* /
@Override
public void configurePIDF ( PIDFConfig config )
{
int pidSlot = isDriveMotor ? SparkMAX_slotIdx . Velocity . ordinal ( ) : SparkMAX_slotIdx . Position . ordinal ( ) ;
pid . setP ( config . p , pidSlot ) ;
pid . setI ( config . i , pidSlot ) ;
pid . setD ( config . d , pidSlot ) ;
pid . setFF ( config . f , pidSlot ) ;
pid . setIZone ( config . iz , pidSlot ) ;
pid . setOutputRange ( config . output . min , config . output . max , pidSlot ) ;
}
/ * *
* Configure the PID wrapping for the position closed loop controller .
*
* @param minInput Minimum PID input .
* @param maxInput Maximum PID input .
* /
@Override
public void configurePIDWrapping ( double minInput , double maxInput )
{
pid . setPositionPIDWrappingEnabled ( true ) ;
pid . setPositionPIDWrappingMinInput ( minInput ) ;
pid . setPositionPIDWrappingMaxInput ( maxInput ) ;
}
/ * *
* Set the CAN status frames .
*
* @param CANStatus0 Applied Output , Faults , Sticky Faults , Is Follower
* @param CANStatus1 Motor Velocity , Motor Temperature , Motor Voltage , Motor Current
* @param CANStatus2 Motor Position
* @param CANStatus3 Analog Sensor Voltage , Analog Sensor Velocity , Analog Sensor Position
* @param CANStatus4 Alternate Encoder Velocity , Alternate Encoder Position
* /
public void configureCANStatusFrames ( int CANStatus0 , int CANStatus1 , int CANStatus2 , int CANStatus3 , int CANStatus4 )
{
motor . setPeriodicFramePeriod ( PeriodicFrame . kStatus0 , CANStatus0 ) ;
motor . setPeriodicFramePeriod ( PeriodicFrame . kStatus1 , CANStatus1 ) ;
motor . setPeriodicFramePeriod ( PeriodicFrame . kStatus2 , CANStatus2 ) ;
motor . setPeriodicFramePeriod ( PeriodicFrame . kStatus3 , CANStatus3 ) ;
motor . setPeriodicFramePeriod ( PeriodicFrame . kStatus4 , CANStatus4 ) ;
// TODO: Configure Status Frame 5 and 6 if necessary
// https://docs.revrobotics.com/sparkmax/operating-modes/control-interfaces
}
/ * *
* Set the idle mode .
*
* @param isBrakeMode Set the brake mode .
* /
@Override
public void setMotorBrake ( boolean isBrakeMode )
{
motor . setIdleMode ( isBrakeMode ? IdleMode . kBrake : IdleMode . kCoast ) ;
}
/ * *
* Set the motor to be inverted .
*
* @param inverted State of inversion .
* /
@Override
public void setInverted ( boolean inverted )
{
motor . setInverted ( inverted ) ;
}
/ * *
* Save the configurations from flash to EEPROM .
* /
@Override
public void burnFlash ( )
{
motor . burnFlash ( ) ;
}
/ * *
* Set the percentage output .
*
* @param percentOutput percent out for the motor controller .
* /
@Override
public void set ( double percentOutput )
{
motor . set ( percentOutput ) ;
}
/ * *
* Set the closed loop PID controller reference point .
*
* @param setpoint Setpoint in MPS or Angle in degrees .
* @param feedforward Feedforward in volt - meter - per - second or kV .
* /
@Override
public void setReference ( double setpoint , double feedforward )
{
int pidSlot = isDriveMotor ? SparkMAX_slotIdx . Velocity . ordinal ( ) : SparkMAX_slotIdx . Position . ordinal ( ) ;
pid . setReference ( setpoint , isDriveMotor ? ControlType . kVelocity : ControlType . kPosition , pidSlot , feedforward ) ;
}
/ * *
* Get the velocity of the integrated encoder .
*
* @return velocity
* /
@Override
public double getVelocity ( )
{
return absoluteEncoder = = null ? encoder . getVelocity ( ) : absoluteEncoder . getVelocity ( ) ;
}
/ * *
* Get the position of the integrated encoder .
*
* @return Position
* /
@Override
public double getPosition ( )
{
return absoluteEncoder = = null ? encoder . getPosition ( ) : absoluteEncoder . getPosition ( ) ;
}
/ * *
* Set the integrated encoder position .
*
* @param position Integrated encoder position .
* /
@Override
public void setPosition ( double position )
{
if ( absoluteEncoder = = null )
{
encoder . setPosition ( position ) ;
}
}
/ * *
* REV Slots for PID configuration .
* /
enum SparkMAX_slotIdx
{
2023-02-14 22:03:02 -06:00
/ * *
* Slot 1 , used for position PID ' s .
* /
Position ,
/ * *
* Slot 2 , used for velocity PID ' s .
* /
Velocity ,
/ * *
* Slot 3 , used arbitrarily . ( Documentation show simulations ) .
* /
Simulation
2023-02-13 14:37:05 -06:00
}
}