Motor Configuration

intermediate30 min

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

RequestCommandsTypical use
DutyCycleOutRaw output, −1 to 1Rollers, conveyors — anything open-loop
VoltageOutA fixed voltageCharacterization; consistent output as the battery sags
VelocityVoltageClosed-loop velocity, voltage outputFlywheels, swerve drive motors
VelocityDutyCycleClosed-loop velocity, duty-cycle outputVelocity control without voltage compensation
MotionMagicVoltageProfiled move to a positionArms, pivots, elevators
FollowerMirror another motorA 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.

Resources