Motor Configuration
Essentials
Every motor controller should be configured for safety and consistency. Key parameters:
Parameters
Configure these:
- Inversion: Positive input = Forward motion.
- Idle Mode: Brake (Hold) vs Coast (Spin).
- Current Limit: Protect motor/battery (e.g., 40A).
- Voltage Comp: Consistent output despite battery drain (e.g., 12V).
- Ramp Rate: Limit acceleration to prevent brownouts/wear.
Configuration Code
package frc.robot.subsystems;
import com.ctre.phoenix6.hardware.TalonFX;
import com.ctre.phoenix6.configs.TalonFXConfiguration;
import com.ctre.phoenix6.signals.InvertedValue;
import com.ctre.phoenix6.signals.NeutralModeValue;
public class Drivetrain extends SubsystemBase {
private TalonFX motor = new TalonFX(1);
// ... (other fields and methods)
public void configureMotor() {
TalonFXConfiguration config = new TalonFXConfiguration();
// Set motor direction
config.MotorOutput.Inverted = InvertedValue.CounterClockwise_Positive;
// Set neutral mode (Brake or Coast)
config.MotorOutput.NeutralMode = NeutralModeValue.Brake;
// Current limiting (Stator = motor protection, Supply = battery protection)
config.CurrentLimits.StatorCurrentLimitEnable = true;
config.CurrentLimits.StatorCurrentLimit = 40.0;
// Voltage compensation (maintains consistent output despite battery voltage)
config.Voltage.PeakForwardVoltage = 12.0;
config.Voltage.PeakReverseVoltage = -12.0;
// Ramp rate: time to reach full output (reduces sudden changes)
config.ClosedLoopRamps.VoltageClosedLoopRampPeriod = 0.25; // 0.25 seconds
motor.getConfigurator().apply(config);
}
// ... (rest of class)Applying Configuration Can Fail
getConfigurator().apply(config) returns a StatusCode. On a busy CAN bus — twenty-odd devices all powering up at once — it sometimes does not succeed.
A failed apply is silent. The motor keeps whatever configuration it had before, which might be last season’s inversion or no current limit at all, and nothing reports it. You find out when a mechanism runs backwards or a breaker trips.
Retry it:
package frc.robot.util;
import com.ctre.phoenix6.StatusCode;
import java.util.function.Supplier;
public class PhoenixUtil {
/** Attempts to run the command until no error is produced. */
public static void tryUntilOk(int maxAttempts, Supplier<StatusCode> command) {
for (int i = 0; i < maxAttempts; i++) {
var error = command.get();
if (error.isOK()) break;
}
}
}
PhoenixUtil.tryUntilOk(5, () -> motor.getConfigurator().apply(config));
Five attempts cost nothing and remove the failure mode. Our robot code routes every apply through this helper.
Control Request Types
Once configured, a Talon FX is commanded with a control request object. Reuse one instance per motor rather than allocating a new one each loop, and set its value with the with... methods.
Phoenix 6 control requests
| Request | Commands | Typical use |
|---|---|---|
DutyCycleOut | Raw output, −1 to 1 | Rollers, conveyors — anything open-loop |
VoltageOut | A fixed voltage | Characterization; consistent output as the battery sags |
VelocityVoltage | Closed-loop velocity, voltage output | Flywheels, swerve drive motors |
VelocityDutyCycle | Closed-loop velocity, duty-cycle output | Velocity control without voltage compensation |
MotionMagicVoltage | Profiled move to a position | Arms, pivots, elevators |
Follower | Mirror another motor | A second motor on the same mechanism |
// Fields — created once, reused every loop
private final VelocityVoltage velocityRequest = new VelocityVoltage(0);
private final MotionMagicVoltage motionMagic = new MotionMagicVoltage(0);
public void setFlywheelVelocity(double rotationsPerSecond) {
rightShooter.setControl(velocityRequest.withVelocity(rotationsPerSecond));
}
public void setPivotPosition(double rotations) {
pivot.setControl(motionMagic.withPosition(rotations));
}
Motion Magic needs a profile as well as gains, configured alongside Slot0:
MotionMagicConfigs motionMagicConfigs = config.MotionMagic;
motionMagicConfigs.MotionMagicCruiseVelocity = 80; // rotations per second
motionMagicConfigs.MotionMagicAcceleration = 160; // rotations per second squared
motionMagicConfigs.MotionMagicJerk = 1600; // rotations per second cubed
Following
When two motors drive the same mechanism, configure both, then set one to follow the other:
PhoenixUtil.tryUntilOk(5, () -> rightShooter.getConfigurator().apply(config));
config.MotorOutput.Inverted = InvertedValue.Clockwise_Positive;
PhoenixUtil.tryUntilOk(5, () -> leftShooter.getConfigurator().apply(config));
leftShooter.setControl(new Follower(rightShooter.getDeviceID(), MotorAlignmentValue.Opposed));
MotorAlignmentValue.Opposed means the follower runs the opposite direction from the leader — correct for motors mounted facing each other. Use Aligned when they face the same way.
Following happens on the motor controller, so the follower keeps tracking even if the roboRIO loop is slow. Commanding both motors from code instead works, but adds CAN traffic and lets the two drift apart.
Software Limit Switches
For a mechanism with hard stops, let the controller enforce the range rather than checking it in code:
config.SoftwareLimitSwitch.ForwardSoftLimitEnable = true;
config.SoftwareLimitSwitch.ForwardSoftLimitThreshold = INTAKE_PIVOT_DOWN;
config.SoftwareLimitSwitch.ReverseSoftLimitEnable = true;
config.SoftwareLimitSwitch.ReverseSoftLimitThreshold = 0;
The motor controller refuses to drive past these positions regardless of what is commanded. That protects the mechanism even from a bug in your own code.