Skip to content

Instantly share code, notes, and snippets.

@jcorcoran
Last active Mar 21, 2022
Embed
What would you like to do?
Example robot code for TalonFX (Falcon500) with MotionMagic
/**
* Phoenix Software License Agreement
*
* Copyright (C) Cross The Road Electronics. All rights
* reserved.
*
* Cross The Road Electronics (CTRE) licenses to you the right to
* use, publish, and distribute copies of CRF (Cross The Road) firmware files (*.crf) and
* Phoenix Software API Libraries ONLY when in use with CTR Electronics hardware products
* as well as the FRC roboRIO when in use in FRC Competition.
*
* THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
* LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* CROSS THE ROAD ELECTRONICS BE LIABLE FOR ANY INCIDENTAL, SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
* PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
* BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
* THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
* SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
* (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE
*/
/**
*
* This gist is a modification to Robot.java within the example project provided by CTRE
* (modified for use with the TalonFX(Falon500s).
* For other files required to compile this project see:
* https://github.com/CrossTheRoadElec/Phoenix-Examples-Languages/tree/master/Java/MotionMagic_AuxStraightPigeon
*
*
* ------------------------------------------------------------------------------------------
*
* Description:
*
* The MotionMagic_AuxStraightPigeon example demonstrates the new Talon/Victor auxiliary and
* remote features to peform complex closed loops. This example has the robot performing
* Motion Magic with an auxiliary closed loop on Pigeon Yaw to keep the robot straight.
*
* This example uses:
* - 2x Quad Encoders, One on both sides of robot for Primary Closed Loop on Position
* A Talon/Victor calculates the distance by taking the sum of both sensors and dividing it by 2.
* - Pigeon IMU wired on CAN Bus for Auxiliary Closed Loop on Yaw
*
* This example has two modes of operation, which can be switched between with Button 2.
* 1.) Arcade Drive
* 2.) Motion Magic with Quadrature Encoders and Drive Straight With Pigeon yaw
*
* Controls:
* Button 1: When pressed, zero sensors. Set quadrature encoders' ostions + Pigeon yaw to 0.
* Button 2: When pressed, toggle between Arcade Drive and Motion Magic
* When toggling into Motion Magic, the current heading is saved and used as the
* auxiliary closed loop target. Can be changed by toggling out and in again.
* Button 5(Left shoulder): When pushed, will decrement the smoothing of the motion magic down to 0
* Button 6(Right shoulder): When pushed, will increment the smoothing of the motion magic up to 8
* Left Joystick Y-Axis:
* + Arcade Drive: Drive robot forward and reverse
* + Motion Magic: Servo robot forward and reverse [-6, 6] rotations
* Right Joystick X-Axis:
* + Arcade Drive: Turn robot in left and right direction
* + Motion Magic: Not used
*
* Gains for Motion Magic and Auxiliary may need to be adjusted in Constants.java
*
* Supported Version:
* - Talon SRX: 4.00
* - Victor SPX: 4.00
* - Pigeon IMU: 4.00
* - CANifier: 4.00
*/
package org.team2168;
import com.ctre.phoenix.motorcontrol.ControlMode;
import com.ctre.phoenix.motorcontrol.DemandType;
import com.ctre.phoenix.motorcontrol.FeedbackDevice;
import com.ctre.phoenix.motorcontrol.FollowerType;
import com.ctre.phoenix.motorcontrol.NeutralMode;
import com.ctre.phoenix.motorcontrol.RemoteSensorSource;
import com.ctre.phoenix.motorcontrol.SensorTerm;
import com.ctre.phoenix.motorcontrol.StatusFrame;
import com.ctre.phoenix.motorcontrol.StatusFrameEnhanced;
import com.ctre.phoenix.motorcontrol.can.TalonFX;
import com.ctre.phoenix.sensors.PigeonIMU;
import com.ctre.phoenix.sensors.PigeonIMU_StatusFrame;
import edu.wpi.first.wpilibj.Joystick;
import edu.wpi.first.wpilibj.TimedRobot;
public class Robot extends TimedRobot {
/** Hardware */
TalonFX _leftMaster = new TalonFX(1);
TalonFX _rightMaster = new TalonFX(15);
PigeonIMU _pidgey = new PigeonIMU(17);
Joystick _gamepad = new Joystick(0);
/** Latched values to detect on-press events for buttons */
boolean[] _previous_currentBtns = new boolean[Constants.kNumButtonsPlusOne];
boolean[] _currentBtns = new boolean[Constants.kNumButtonsPlusOne];
/** Tracking variables */
boolean _firstCall = false;
boolean _state = false;
double _targetAngle = 0;
/** How much smoothing [0,8] to use during MotionMagic */
int _smoothing;
@Override
public void robotInit() {
/* Not used in this project */
}
@Override
public void teleopInit(){
/* Disable all motor controllers */
_rightMaster.set(ControlMode.PercentOutput, 0);
_leftMaster.set(ControlMode.PercentOutput, 0);
/* Factory Default all hardware to prevent unexpected behavior */
_rightMaster.configFactoryDefault();
_leftMaster.configFactoryDefault();
_pidgey.configFactoryDefault();
/* Set Neutral Mode */
_leftMaster.setNeutralMode(NeutralMode.Brake);
_rightMaster.setNeutralMode(NeutralMode.Brake);
/** Feedback Sensor Configuration */
/* Configure the left Talon's selected sensor as local QuadEncoder */
_leftMaster.configSelectedFeedbackSensor( FeedbackDevice.IntegratedSensor, // Local Feedback Source
Constants.PID_PRIMARY, // PID Slot for Source [0, 1]
Constants.kTimeoutMs); // Configuration Timeout
/* Configure the Remote Talon's selected sensor as a remote sensor for the right Talon */
_rightMaster.configRemoteFeedbackFilter(_leftMaster.getDeviceID(), // Device ID of Source
RemoteSensorSource.TalonSRX_SelectedSensor, // Remote Feedback Source
Constants.REMOTE_0, // Source number [0, 1]
Constants.kTimeoutMs); // Configuration Timeout
/* Configure the Pigeon IMU to the other remote slot available on the right Talon */
_rightMaster.configRemoteFeedbackFilter(_pidgey.getDeviceID(),
RemoteSensorSource.Pigeon_Yaw,
Constants.REMOTE_1,
Constants.kTimeoutMs);
/* Setup Sum signal to be used for Distance */
_rightMaster.configSensorTerm(SensorTerm.Diff0, FeedbackDevice.RemoteSensor0, Constants.kTimeoutMs); // Feedback Device of Remote Talon
_rightMaster.configSensorTerm(SensorTerm.Diff1, FeedbackDevice.IntegratedSensor, Constants.kTimeoutMs); // Quadrature Encoder of current Talon
/* Configure Sum [Sum of both QuadEncoders] to be used for Primary PID Index */
_rightMaster.configSelectedFeedbackSensor( FeedbackDevice.SensorDifference,
Constants.PID_PRIMARY,
Constants.kTimeoutMs);
/* Scale Feedback by 0.5 to half the sum of Distance */ //not working--do this within the setpoint
_rightMaster.configSelectedFeedbackCoefficient( 1, // Coefficient
Constants.PID_PRIMARY, // PID Slot of Source
Constants.kTimeoutMs); // Configuration Timeout
/* Configure Remote 1 [Pigeon IMU's Yaw] to be used for Auxiliary PID Index */
_rightMaster.configSelectedFeedbackSensor( FeedbackDevice.RemoteSensor1,
Constants.PID_TURN,
Constants.kTimeoutMs);
/* Scale the Feedback Sensor using a coefficient */
_rightMaster.configSelectedFeedbackCoefficient( 1,
Constants.PID_TURN,
Constants.kTimeoutMs);
/* Configure output and sensor direction */
_leftMaster.setInverted(false);
_leftMaster.setSensorPhase(false);
_rightMaster.setInverted(true);
_rightMaster.setSensorPhase(true);
/* Set status frame periods to ensure we don't have stale data */
_rightMaster.setStatusFramePeriod(StatusFrame.Status_12_Feedback1, 20, Constants.kTimeoutMs);
_rightMaster.setStatusFramePeriod(StatusFrame.Status_13_Base_PIDF0, 20, Constants.kTimeoutMs);
_rightMaster.setStatusFramePeriod(StatusFrame.Status_14_Turn_PIDF1, 20, Constants.kTimeoutMs);
_rightMaster.setStatusFramePeriod(StatusFrame.Status_10_Targets, 20, Constants.kTimeoutMs);
_leftMaster.setStatusFramePeriod(StatusFrame.Status_2_Feedback0, 5, Constants.kTimeoutMs);
_pidgey.setStatusFramePeriod(PigeonIMU_StatusFrame.CondStatus_9_SixDeg_YPR , 5, Constants.kTimeoutMs);
/* Configure neutral deadband */
_rightMaster.configNeutralDeadband(Constants.kNeutralDeadband, Constants.kTimeoutMs);
_leftMaster.configNeutralDeadband(Constants.kNeutralDeadband, Constants.kTimeoutMs);
/* Motion Magic Configurations */
_rightMaster.configMotionAcceleration(2000, Constants.kTimeoutMs);
_rightMaster.configMotionCruiseVelocity(2000, Constants.kTimeoutMs);
/**
* Max out the peak output (for all modes).
* However you can limit the output of a given PID object with configClosedLoopPeakOutput().
*/
_leftMaster.configPeakOutputForward(+1.0, Constants.kTimeoutMs);
_leftMaster.configPeakOutputReverse(-1.0, Constants.kTimeoutMs);
_rightMaster.configPeakOutputForward(+1.0, Constants.kTimeoutMs);
_rightMaster.configPeakOutputReverse(-1.0, Constants.kTimeoutMs);
/* FPID Gains for distance servo */
_rightMaster.config_kP(Constants.kSlot_Distanc, Constants.kGains_Distanc.kP, Constants.kTimeoutMs);
_rightMaster.config_kI(Constants.kSlot_Distanc, Constants.kGains_Distanc.kI, Constants.kTimeoutMs);
_rightMaster.config_kD(Constants.kSlot_Distanc, Constants.kGains_Distanc.kD, Constants.kTimeoutMs);
_rightMaster.config_kF(Constants.kSlot_Distanc, Constants.kGains_Distanc.kF, Constants.kTimeoutMs);
_rightMaster.config_IntegralZone(Constants.kSlot_Distanc, Constants.kGains_Distanc.kIzone, Constants.kTimeoutMs);
_rightMaster.configClosedLoopPeakOutput(Constants.kSlot_Distanc, Constants.kGains_Distanc.kPeakOutput, Constants.kTimeoutMs);
/* FPID Gains for turn servo */
_rightMaster.config_kP(Constants.kSlot_Turning, Constants.kGains_Turning.kP, Constants.kTimeoutMs);
_rightMaster.config_kI(Constants.kSlot_Turning, Constants.kGains_Turning.kI, Constants.kTimeoutMs);
_rightMaster.config_kD(Constants.kSlot_Turning, Constants.kGains_Turning.kD, Constants.kTimeoutMs);
_rightMaster.config_kF(Constants.kSlot_Turning, Constants.kGains_Turning.kF, Constants.kTimeoutMs);
_rightMaster.config_IntegralZone(Constants.kSlot_Turning, Constants.kGains_Turning.kIzone, Constants.kTimeoutMs);
_rightMaster.configClosedLoopPeakOutput(Constants.kSlot_Turning, Constants.kGains_Turning.kPeakOutput, Constants.kTimeoutMs);
/**
* 1ms per loop. PID loop can be slowed down if need be.
* For example,
* - if sensor updates are too slow
* - sensor deltas are very small per update, so derivative error never gets large enough to be useful.
* - sensor movement is very slow causing the derivative error to be near zero.
*/
int closedLoopTimeMs = 1;
_rightMaster.configClosedLoopPeriod(0, closedLoopTimeMs, Constants.kTimeoutMs);
_rightMaster.configClosedLoopPeriod(1, closedLoopTimeMs, Constants.kTimeoutMs);
/**
* configAuxPIDPolarity(boolean invert, int timeoutMs)
* false means talon's local output is PID0 + PID1, and other side Talon is PID0 - PID1
* true means talon's local output is PID0 - PID1, and other side Talon is PID0 + PID1
*/
_rightMaster.configAuxPIDPolarity(false, Constants.kTimeoutMs);
/* Initialize */
_firstCall = true;
_state = false;
_rightMaster.setStatusFramePeriod(StatusFrameEnhanced.Status_10_Targets, 10);
zeroSensors();
}
@Override
public void teleopPeriodic() {
/* Gamepad processing */
double forward = -1 * _gamepad.getY();
double turn = _gamepad.getTwist();
forward = Deadband(forward);
turn = Deadband(turn);
// _pidgey.setYaw(0, Constants.kTimeoutMs);
// _pidgey.setAccumZAngle(0, Constants.kTimeoutMs);
/* Button processing for state toggle and sensor zeroing */
getButtons(_currentBtns, _gamepad);
if(_currentBtns[2] && !_previous_currentBtns[2]){
_state = !_state; // Toggle state
_firstCall = true; // State change, do first call operation
_targetAngle = _rightMaster.getSelectedSensorPosition(1);
System.out.print(_state);
}else if (_currentBtns[1] && !_previous_currentBtns[1]) {
zeroSensors(); // Zero Sensors
}
if(_currentBtns[5] && !_previous_currentBtns[5]) {
_smoothing--; // Decrement smoothing
if(_smoothing < 0) _smoothing = 0; // Cap smoothing
_rightMaster.configMotionSCurveStrength(_smoothing);
System.out.println("Smoothing value is: " + _smoothing);
}
if(_currentBtns[6] && !_previous_currentBtns[6]) {
_smoothing++; // Increment smoothing
if(_smoothing > 8) _smoothing = 8; // Cap smoothing
_rightMaster.configMotionSCurveStrength(_smoothing);
System.out.println("Smoothing value is: " + _smoothing);
}
System.arraycopy(_currentBtns, 0, _previous_currentBtns, 0, Constants.kNumButtonsPlusOne);
if(!_state){
if (_firstCall)
System.out.println("This is Arcade Drive.\n");
_leftMaster.set(ControlMode.PercentOutput, forward, DemandType.ArbitraryFeedForward, +turn);
_rightMaster.set(ControlMode.PercentOutput, forward, DemandType.ArbitraryFeedForward, -turn);
}else{
if (_firstCall) {
System.out.println("This is Motion Magic with the Auxiliary PID using the Pigeon yaw.");
System.out.println("Servo [-6,6] rotations while also maintaining a straight heading.\n");
zeroDistance();
/* Determine which slot affects which PID */
_rightMaster.selectProfileSlot(Constants.kSlot_Distanc, Constants.PID_PRIMARY);
_rightMaster.selectProfileSlot(Constants.kSlot_Turning, Constants.PID_TURN);
}
/* Calculate targets from gamepad inputs */
double target_sensorUnits = 2 * forward * Constants.kSensorUnitsPerRotation * Constants.kRotationsToTravel;
double target_turn = _targetAngle;
System.out.println("Target-sensor units" + target_sensorUnits);
System.out.println("commanded heading" + target_turn);
/* Configured for MotionMagic on Quad Encoders' Sum and Auxiliary PID on Pigeon */
_rightMaster.set(ControlMode.MotionMagic, target_sensorUnits, DemandType.AuxPID, target_turn);
_leftMaster.follow(_rightMaster, FollowerType.AuxOutput1);
}
_firstCall = false;
}
/** Zero all sensors, both Talons and Pigeon */
void zeroSensors() {
_leftMaster.getSensorCollection().setIntegratedSensorPosition(0, Constants.kTimeoutMs);
_rightMaster.getSensorCollection().setIntegratedSensorPosition(0, Constants.kTimeoutMs);
_pidgey.setYaw(0, Constants.kTimeoutMs);
_pidgey.setAccumZAngle(0, Constants.kTimeoutMs);
System.out.println("[Integrated Encoders + Pigeon] All sensors are zeroed.\n");
}
/** Zero QuadEncoders, used to reset position when initializing Motion Magic */
void zeroDistance(){
_leftMaster.getSensorCollection().setIntegratedSensorPosition(0, Constants.kTimeoutMs);
_rightMaster.getSensorCollection().setIntegratedSensorPosition(0, Constants.kTimeoutMs);
System.out.println("[Integrated Encoders] All encoders are zeroed.\n");
}
/** Deadband 5 percent, used on the gamepad */
double Deadband(double value) {
/* Upper deadband */
if (value >= +0.5)
return value;
/* Lower deadband */
if (value <= -0.5)
return value;
/* Outside deadband */
return 0;
}
/** Gets all buttons from gamepad */
void getButtons(boolean[] _currentBtns, Joystick gamepad) {
for (int i = 1; i < Constants.kNumButtonsPlusOne; ++i) {
_currentBtns[i] = gamepad.getRawButton(i);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment