Swerve Builder API

To simplify the API surface, both builder and factory paradigms are used. Users create a SwerveDrivetrain by first defining the global drivetrain characteristics and then each module characteristics.

Note

Phoenix 6 supports the Java units library when applicable.

Defining Drivetrain Characteristics

Drivetrain, in this instance, refers to the SwerveDrivetrainConstants class (Java, C++, Python). This class defines characteristics that are not tied to the swerve modules, such as the CAN bus or Pigeon 2 device ID.

Note

All devices in the swerve drivetrain must be on the same CAN bus.

Users can optionally provide a configuration object to apply custom configs to the Pigeon 2, such as mount orientation. Leaving the configuration object null will skip applying configs to the Pigeon 2.

Defining Module Characteristics

The typical FRC drivetrain includes 4 identical modules. To simplify module creation, the SwerveModuleConstantsFactory (Java, C++, Python) class is used to set up constants common across all modules, such as the drive/steer gear ratios and the wheel radius.

Some extra steps may be required to determine some constants, described below.

CouplingGearRatio

The ratio at which the output wheel rotates when the azimuth spins. In a traditional swerve module, this is the inverse of the 1st stage of the drive motor.

To manually determine the coupling ratio, lock the drive wheel in-place, then rotate the azimuth three times. Observe the number of rotations reported by the drive motor. The coupling ratio will be \(driveRotations / 3\), or \(driveRotations / azimuthRotations\).

SlipCurrent

This is the amount of stator current the drive motors can apply without slippage. Follow the instructions in Preventing Wheel Slip to find the slip current of the drivetrain.

DriveMotorInitialConfigs/SteerMotorInitialConfigs/EncoderInitialConfigs

An initial configuration object that can be used to apply custom configs to the backing devices for each swerve module. This is useful for situations such as applying supply current limits.

Building the Swerve Module Constants

SwerveModuleConstants (Java, C++, Python) can be created from the previous SwerveModuleConstantsFactory. A typical swerve drivetrain consists of four identical modules: Front Left, Front Right, Back Left, Back Right. While these modules can be instantiated directly (only really useful if the modules have different physical characteristics), the modules can also be created by calling createModuleConstants(...) with the aforementioned factory.

Note

The X and Y position of the modules is measured from the center point of the robot along the X and Y axes, respectively. These values use the same coordinate system as Translation2d (Java, C++, Python), where forward is positive X and left is positive Y.

Building the SwerveDrivetrain

SwerveDrivetrain (Java, C++, Python) is the class that handles odometry, configuration and control of the drivetrain. The constructor for this class takes the previous SwerveDrivetrainConstants and a list of SwerveModuleConstants.

Utilization of SwerveDrivetrain consists of SwerveRequests that define the state of the drivetrain. For full details of using SwerveRequests to control your swerve, see Swerve Requests.

Full Example

Note

CommandSwerveDrivetrain is a version created by the Tuner X Swerve Project Generator that implements Subsystem (Java, C++, Python) for easy command-based integration.

  1// Generated by the Tuner X Swerve Project Generator
  2// https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html
  3public class TunerConstants {
  4    // Both sets of gains need to be tuned to your individual robot.
  5
  6    // The steer motor uses any SwerveModule.SteerRequestType control request with the
  7    // output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput
  8    private static final Slot0Configs steerGains = new Slot0Configs()
  9        .withKP(100).withKI(0).withKD(0.5)
 10        .withKS(0.1).withKV(1.91).withKA(0)
 11        .withStaticFeedforwardSign(StaticFeedforwardSignValue.UseClosedLoopSign);
 12    // When using closed-loop control, the drive motor uses the control
 13    // output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput
 14    private static final Slot0Configs driveGains = new Slot0Configs()
 15        .withKP(0.1).withKI(0).withKD(0)
 16        .withKS(0).withKV(0.124);
 17
 18    // The closed-loop output type to use for the steer motors;
 19    // This affects the PID/FF gains for the steer motors
 20    private static final ClosedLoopOutputType kSteerClosedLoopOutput = ClosedLoopOutputType.Voltage;
 21    // The closed-loop output type to use for the drive motors;
 22    // This affects the PID/FF gains for the drive motors
 23    private static final ClosedLoopOutputType kDriveClosedLoopOutput = ClosedLoopOutputType.Voltage;
 24
 25    // The type of motor used for the drive motor
 26    private static final DriveMotorArrangement kDriveMotorType = DriveMotorArrangement.TalonFX_Integrated;
 27    // The type of motor used for the drive motor
 28    private static final SteerMotorArrangement kSteerMotorType = SteerMotorArrangement.TalonFX_Integrated;
 29
 30    // The remote sensor feedback type to use for the steer motors;
 31    // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote*
 32    private static final SteerFeedbackType kSteerFeedbackType = SteerFeedbackType.FusedCANcoder;
 33
 34    // The stator current at which the wheels start to slip;
 35    // This needs to be tuned to your individual robot
 36    private static final Current kSlipCurrent = Amps.of(120.0);
 37
 38    // Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null.
 39    // Some configs will be overwritten; check the `with*InitialConfigs()` API documentation.
 40    private static final TalonFXConfiguration driveInitialConfigs = new TalonFXConfiguration();
 41    private static final TalonFXConfiguration steerInitialConfigs = new TalonFXConfiguration()
 42        .withCurrentLimits(
 43            new CurrentLimitsConfigs()
 44                // Swerve azimuth does not require much torque output, so we can set a relatively low
 45                // stator current limit to help avoid brownouts without impacting performance.
 46                .withStatorCurrentLimit(Amps.of(60.0))
 47                .withStatorCurrentLimitEnable(true)
 48        );
 49    private static final CANcoderConfiguration encoderInitialConfigs = new CANcoderConfiguration();
 50    // Configs for the Pigeon 2; leave this null to skip applying Pigeon 2 configs
 51    private static final Pigeon2Configuration pigeonConfigs = null;
 52
 53    // CAN bus that the devices are located on;
 54    // All swerve devices must share the same CAN bus
 55    public static final CANBus kCANBus = new CANBus("canivore", "./logs/example.hoot");
 56
 57    // Theoretical free speed (m/s) at 12 V applied output;
 58    // This needs to be tuned to your individual robot
 59    public static final LinearVelocity kSpeedAt12Volts = MetersPerSecond.of(4.54);
 60
 61    // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns;
 62    // This may need to be tuned to your individual robot
 63    private static final double kCoupleRatio = 3.8181818181818183;
 64
 65    private static final double kDriveGearRatio = 7.363636363636365;
 66    private static final double kSteerGearRatio = 15.42857142857143;
 67    private static final Distance kWheelRadius = Inches.of(2.167);
 68
 69    private static final boolean kInvertLeftSide = false;
 70    private static final boolean kInvertRightSide = true;
 71
 72    private static final int kPigeonId = 1;
 73
 74    // These are only used for simulation
 75    private static final MomentOfInertia kSteerInertia = KilogramSquareMeters.of(0.01);
 76    private static final MomentOfInertia kDriveInertia = KilogramSquareMeters.of(0.01);
 77    // Simulated voltage necessary to overcome friction
 78    private static final Voltage kSteerFrictionVoltage = Volts.of(0.2);
 79    private static final Voltage kDriveFrictionVoltage = Volts.of(0.2);
 80
 81    public static final SwerveDrivetrainConstants DrivetrainConstants = new SwerveDrivetrainConstants()
 82            .withCANBusName(kCANBus.getName())
 83            .withPigeon2Id(kPigeonId)
 84            .withPigeon2Configs(pigeonConfigs);
 85
 86    private static final SwerveModuleConstantsFactory<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> ConstantCreator =
 87        new SwerveModuleConstantsFactory<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration>()
 88            .withDriveMotorGearRatio(kDriveGearRatio)
 89            .withSteerMotorGearRatio(kSteerGearRatio)
 90            .withCouplingGearRatio(kCoupleRatio)
 91            .withWheelRadius(kWheelRadius)
 92            .withSteerMotorGains(steerGains)
 93            .withDriveMotorGains(driveGains)
 94            .withSteerMotorClosedLoopOutput(kSteerClosedLoopOutput)
 95            .withDriveMotorClosedLoopOutput(kDriveClosedLoopOutput)
 96            .withSlipCurrent(kSlipCurrent)
 97            .withSpeedAt12Volts(kSpeedAt12Volts)
 98            .withDriveMotorType(kDriveMotorType)
 99            .withSteerMotorType(kSteerMotorType)
100            .withFeedbackSource(kSteerFeedbackType)
101            .withDriveMotorInitialConfigs(driveInitialConfigs)
102            .withSteerMotorInitialConfigs(steerInitialConfigs)
103            .withEncoderInitialConfigs(encoderInitialConfigs)
104            .withSteerInertia(kSteerInertia)
105            .withDriveInertia(kDriveInertia)
106            .withSteerFrictionVoltage(kSteerFrictionVoltage)
107            .withDriveFrictionVoltage(kDriveFrictionVoltage);
108
109
110    // Front Left
111    private static final int kFrontLeftDriveMotorId = 3;
112    private static final int kFrontLeftSteerMotorId = 2;
113    private static final int kFrontLeftEncoderId = 1;
114    private static final Angle kFrontLeftEncoderOffset = Rotations.of(0.15234375);
115    private static final boolean kFrontLeftSteerMotorInverted = true;
116    private static final boolean kFrontLeftEncoderInverted = false;
117
118    private static final Distance kFrontLeftXPos = Inches.of(10);
119    private static final Distance kFrontLeftYPos = Inches.of(10);
120
121    // Front Right
122    private static final int kFrontRightDriveMotorId = 1;
123    private static final int kFrontRightSteerMotorId = 0;
124    private static final int kFrontRightEncoderId = 0;
125    private static final Angle kFrontRightEncoderOffset = Rotations.of(-0.4873046875);
126    private static final boolean kFrontRightSteerMotorInverted = true;
127    private static final boolean kFrontRightEncoderInverted = false;
128
129    private static final Distance kFrontRightXPos = Inches.of(10);
130    private static final Distance kFrontRightYPos = Inches.of(-10);
131
132    // Back Left
133    private static final int kBackLeftDriveMotorId = 7;
134    private static final int kBackLeftSteerMotorId = 6;
135    private static final int kBackLeftEncoderId = 3;
136    private static final Angle kBackLeftEncoderOffset = Rotations.of(-0.219482421875);
137    private static final boolean kBackLeftSteerMotorInverted = true;
138    private static final boolean kBackLeftEncoderInverted = false;
139
140    private static final Distance kBackLeftXPos = Inches.of(-10);
141    private static final Distance kBackLeftYPos = Inches.of(10);
142
143    // Back Right
144    private static final int kBackRightDriveMotorId = 5;
145    private static final int kBackRightSteerMotorId = 4;
146    private static final int kBackRightEncoderId = 2;
147    private static final Angle kBackRightEncoderOffset = Rotations.of(0.17236328125);
148    private static final boolean kBackRightSteerMotorInverted = true;
149    private static final boolean kBackRightEncoderInverted = false;
150
151    private static final Distance kBackRightXPos = Inches.of(-10);
152    private static final Distance kBackRightYPos = Inches.of(-10);
153
154
155    public static final SwerveModuleConstants<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> FrontLeft =
156        ConstantCreator.createModuleConstants(
157            kFrontLeftSteerMotorId, kFrontLeftDriveMotorId, kFrontLeftEncoderId, kFrontLeftEncoderOffset,
158            kFrontLeftXPos, kFrontLeftYPos, kInvertLeftSide, kFrontLeftSteerMotorInverted, kFrontLeftEncoderInverted
159        );
160    public static final SwerveModuleConstants<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> FrontRight =
161        ConstantCreator.createModuleConstants(
162            kFrontRightSteerMotorId, kFrontRightDriveMotorId, kFrontRightEncoderId, kFrontRightEncoderOffset,
163            kFrontRightXPos, kFrontRightYPos, kInvertRightSide, kFrontRightSteerMotorInverted, kFrontRightEncoderInverted
164        );
165    public static final SwerveModuleConstants<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> BackLeft =
166        ConstantCreator.createModuleConstants(
167            kBackLeftSteerMotorId, kBackLeftDriveMotorId, kBackLeftEncoderId, kBackLeftEncoderOffset,
168            kBackLeftXPos, kBackLeftYPos, kInvertLeftSide, kBackLeftSteerMotorInverted, kBackLeftEncoderInverted
169        );
170    public static final SwerveModuleConstants<TalonFXConfiguration, TalonFXConfiguration, CANcoderConfiguration> BackRight =
171        ConstantCreator.createModuleConstants(
172            kBackRightSteerMotorId, kBackRightDriveMotorId, kBackRightEncoderId, kBackRightEncoderOffset,
173            kBackRightXPos, kBackRightYPos, kInvertRightSide, kBackRightSteerMotorInverted, kBackRightEncoderInverted
174        );
175
176    /**
177     * Creates a CommandSwerveDrivetrain instance.
178     * This should only be called once in your robot program,.
179     */
180    public static CommandSwerveDrivetrain createDrivetrain() {
181        return new CommandSwerveDrivetrain(
182            DrivetrainConstants, FrontLeft, FrontRight, BackLeft, BackRight
183        );
184    }
185
186
187    /**
188     * Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected device types.
189     */
190    public static class TunerSwerveDrivetrain extends SwerveDrivetrain<TalonFX, TalonFX, CANcoder> {
191        /**
192         * Constructs a CTRE SwerveDrivetrain using the specified constants.
193         * <p>
194         * This constructs the underlying hardware devices, so users should not construct
195         * the devices themselves. If they need the devices, they can access them through
196         * getters in the classes.
197         *
198         * @param drivetrainConstants   Drivetrain-wide constants for the swerve drive
199         * @param modules               Constants for each specific module
200         */
201        public TunerSwerveDrivetrain(
202            SwerveDrivetrainConstants drivetrainConstants,
203            SwerveModuleConstants<?, ?, ?>... modules
204        ) {
205            super(
206                TalonFX::new, TalonFX::new, CANcoder::new,
207                drivetrainConstants, modules
208            );
209        }
210
211        /**
212         * Constructs a CTRE SwerveDrivetrain using the specified constants.
213         * <p>
214         * This constructs the underlying hardware devices, so users should not construct
215         * the devices themselves. If they need the devices, they can access them through
216         * getters in the classes.
217         *
218         * @param drivetrainConstants     Drivetrain-wide constants for the swerve drive
219         * @param odometryUpdateFrequency The frequency to run the odometry loop. If
220         *                                unspecified or set to 0 Hz, this is 250 Hz on
221         *                                CAN FD, and 100 Hz on CAN 2.0.
222         * @param modules                 Constants for each specific module
223         */
224        public TunerSwerveDrivetrain(
225            SwerveDrivetrainConstants drivetrainConstants,
226            double odometryUpdateFrequency,
227            SwerveModuleConstants<?, ?, ?>... modules
228        ) {
229            super(
230                TalonFX::new, TalonFX::new, CANcoder::new,
231                drivetrainConstants, odometryUpdateFrequency, modules
232            );
233        }
234
235        /**
236         * Constructs a CTRE SwerveDrivetrain using the specified constants.
237         * <p>
238         * This constructs the underlying hardware devices, so users should not construct
239         * the devices themselves. If they need the devices, they can access them through
240         * getters in the classes.
241         *
242         * @param drivetrainConstants       Drivetrain-wide constants for the swerve drive
243         * @param odometryUpdateFrequency   The frequency to run the odometry loop. If
244         *                                  unspecified or set to 0 Hz, this is 250 Hz on
245         *                                  CAN FD, and 100 Hz on CAN 2.0.
246         * @param odometryStandardDeviation The standard deviation for odometry calculation
247         *                                  in the form [x, y, theta]ᵀ, with units in meters
248         *                                  and radians
249         * @param visionStandardDeviation   The standard deviation for vision calculation
250         *                                  in the form [x, y, theta]ᵀ, with units in meters
251         *                                  and radians
252         * @param modules                   Constants for each specific module
253         */
254        public TunerSwerveDrivetrain(
255            SwerveDrivetrainConstants drivetrainConstants,
256            double odometryUpdateFrequency,
257            Matrix<N3, N1> odometryStandardDeviation,
258            Matrix<N3, N1> visionStandardDeviation,
259            SwerveModuleConstants<?, ?, ?>... modules
260        ) {
261            super(
262                TalonFX::new, TalonFX::new, CANcoder::new,
263                drivetrainConstants, odometryUpdateFrequency,
264                odometryStandardDeviation, visionStandardDeviation, modules
265            );
266        }
267    }
268}
  1#include "ctre/phoenix6/swerve/SwerveDrivetrain.hpp"
  2
  3using namespace ctre::phoenix6;
  4
  5namespace subsystems {
  6    /* Forward declaration */
  7    class CommandSwerveDrivetrain;
  8}
  9
 10// Generated by the Tuner X Swerve Project Generator
 11// https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html
 12class TunerConstants {
 13    // Both sets of gains need to be tuned to your individual robot.
 14
 15    // The steer motor uses any SwerveModule.SteerRequestType control request with the
 16    // output type specified by SwerveModuleConstants::SteerMotorClosedLoopOutput
 17    static constexpr configs::Slot0Configs steerGains = configs::Slot0Configs{}
 18        .WithKP(100).WithKI(0).WithKD(0.5)
 19        .WithKS(0.1).WithKV(1.91).WithKA(0)
 20        .WithStaticFeedforwardSign(signals::StaticFeedforwardSignValue::UseClosedLoopSign);
 21    // When using closed-loop control, the drive motor uses the control
 22    // output type specified by SwerveModuleConstants::DriveMotorClosedLoopOutput
 23    static constexpr configs::Slot0Configs driveGains = configs::Slot0Configs{}
 24        .WithKP(0.1).WithKI(0).WithKD(0)
 25        .WithKS(0).WithKV(0.124);
 26
 27    // The closed-loop output type to use for the steer motors;
 28    // This affects the PID/FF gains for the steer motors
 29    static constexpr swerve::ClosedLoopOutputType kSteerClosedLoopOutput = swerve::ClosedLoopOutputType::Voltage;
 30    // The closed-loop output type to use for the drive motors;
 31    // This affects the PID/FF gains for the drive motors
 32    static constexpr swerve::ClosedLoopOutputType kDriveClosedLoopOutput = swerve::ClosedLoopOutputType::Voltage;
 33
 34    // The type of motor used for the drive motor
 35    static constexpr swerve::DriveMotorArrangement kDriveMotorType = swerve::DriveMotorArrangement::TalonFX_Integrated;
 36    // The type of motor used for the drive motor
 37    static constexpr swerve::SteerMotorArrangement kSteerMotorType = swerve::SteerMotorArrangement::TalonFX_Integrated;
 38
 39    // The remote sensor feedback type to use for the steer motors;
 40    // When not Pro-licensed, Fused*/Sync* automatically fall back to Remote*
 41    static constexpr swerve::SteerFeedbackType kSteerFeedbackType = swerve::SteerFeedbackType::FusedCANcoder;
 42
 43    // The stator current at which the wheels start to slip;
 44    // This needs to be tuned to your individual robot
 45    static constexpr units::ampere_t kSlipCurrent = 120_A;
 46
 47    // Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null.
 48    // Some configs will be overwritten; check the `With*InitialConfigs()` API documentation.
 49    static constexpr configs::TalonFXConfiguration driveInitialConfigs{};
 50    static constexpr configs::TalonFXConfiguration steerInitialConfigs = configs::TalonFXConfiguration{}
 51        .WithCurrentLimits(
 52            configs::CurrentLimitsConfigs{}
 53                // Swerve azimuth does not require much torque output, so we can set a relatively low
 54                // stator current limit to help avoid brownouts without impacting performance.
 55                .WithStatorCurrentLimit(60_A)
 56                .WithStatorCurrentLimitEnable(true)
 57        );
 58
 59    static constexpr configs::CANcoderConfiguration encoderInitialConfigs{};
 60    // Configs for the Pigeon 2; leave this nullopt to skip applying Pigeon 2 configs
 61    static constexpr std::optional<configs::Pigeon2Configuration> pigeonConfigs = std::nullopt;
 62
 63    static constexpr std::string_view kCANBusName = "canivore";
 64
 65public:
 66    // CAN bus that the devices are located on;
 67    // All swerve devices must share the same CAN bus
 68    static inline const CANBus kCANBus{kCANBusName, "./logs/example.hoot"};
 69
 70    // Theoretical free speed (m/s) at 12 V applied output;
 71    // This needs to be tuned to your individual robot
 72    static constexpr units::meters_per_second_t kSpeedAt12Volts = 4.54_mps;
 73
 74private:
 75    // Every 1 rotation of the azimuth results in kCoupleRatio drive motor turns;
 76    // This may need to be tuned to your individual robot
 77    static constexpr units::scalar_t kCoupleRatio = 3.8181818181818183;
 78
 79    static constexpr units::scalar_t kDriveGearRatio = 7.363636363636365;
 80    static constexpr units::scalar_t kSteerGearRatio = 15.42857142857143;
 81    static constexpr units::inch_t kWheelRadius = 2.167_in;
 82
 83    static constexpr bool kInvertLeftSide = false;
 84    static constexpr bool kInvertRightSide = true;
 85
 86    static constexpr int kPigeonId = 1;
 87
 88    // These are only used for simulation
 89    static constexpr units::kilogram_square_meter_t kSteerInertia = 0.01_kg_sq_m;
 90    static constexpr units::kilogram_square_meter_t kDriveInertia = 0.01_kg_sq_m;
 91    // Simulated voltage necessary to overcome friction
 92    static constexpr units::volt_t kSteerFrictionVoltage = 0.2_V;
 93    static constexpr units::volt_t kDriveFrictionVoltage = 0.2_V;
 94
 95public:
 96    static constexpr swerve::SwerveDrivetrainConstants DrivetrainConstants = swerve::SwerveDrivetrainConstants{}
 97            .WithCANBusName(kCANBusName)
 98            .WithPigeon2Id(kPigeonId)
 99            .WithPigeon2Configs(pigeonConfigs);
100
101private:
102    static constexpr swerve::SwerveModuleConstantsFactory ConstantCreator =
103        swerve::SwerveModuleConstantsFactory<configs::TalonFXConfiguration, configs::TalonFXConfiguration, configs::CANcoderConfiguration>{}
104            .WithDriveMotorGearRatio(kDriveGearRatio)
105            .WithSteerMotorGearRatio(kSteerGearRatio)
106            .WithCouplingGearRatio(kCoupleRatio)
107            .WithWheelRadius(kWheelRadius)
108            .WithSteerMotorGains(steerGains)
109            .WithDriveMotorGains(driveGains)
110            .WithSteerMotorClosedLoopOutput(kSteerClosedLoopOutput)
111            .WithDriveMotorClosedLoopOutput(kDriveClosedLoopOutput)
112            .WithSlipCurrent(kSlipCurrent)
113            .WithSpeedAt12Volts(kSpeedAt12Volts)
114            .WithDriveMotorType(kDriveMotorType)
115            .WithSteerMotorType(kSteerMotorType)
116            .WithFeedbackSource(kSteerFeedbackType)
117            .WithDriveMotorInitialConfigs(driveInitialConfigs)
118            .WithSteerMotorInitialConfigs(steerInitialConfigs)
119            .WithEncoderInitialConfigs(encoderInitialConfigs)
120            .WithSteerInertia(kSteerInertia)
121            .WithDriveInertia(kDriveInertia)
122            .WithSteerFrictionVoltage(kSteerFrictionVoltage)
123            .WithDriveFrictionVoltage(kDriveFrictionVoltage);
124
125
126    // Front Left
127    static constexpr int kFrontLeftDriveMotorId = 3;
128    static constexpr int kFrontLeftSteerMotorId = 2;
129    static constexpr int kFrontLeftEncoderId = 1;
130    static constexpr units::turn_t kFrontLeftEncoderOffset = 0.15234375_tr;
131    static constexpr bool kFrontLeftSteerMotorInverted = true;
132    static constexpr bool kFrontLeftEncoderInverted = false;
133
134    static constexpr units::inch_t kFrontLeftXPos = 10_in;
135    static constexpr units::inch_t kFrontLeftYPos = 10_in;
136
137    // Front Right
138    static constexpr int kFrontRightDriveMotorId = 1;
139    static constexpr int kFrontRightSteerMotorId = 0;
140    static constexpr int kFrontRightEncoderId = 0;
141    static constexpr units::turn_t kFrontRightEncoderOffset = -0.4873046875_tr;
142    static constexpr bool kFrontRightSteerMotorInverted = true;
143    static constexpr bool kFrontRightEncoderInverted = false;
144
145    static constexpr units::inch_t kFrontRightXPos = 10_in;
146    static constexpr units::inch_t kFrontRightYPos = -10_in;
147
148    // Back Left
149    static constexpr int kBackLeftDriveMotorId = 7;
150    static constexpr int kBackLeftSteerMotorId = 6;
151    static constexpr int kBackLeftEncoderId = 3;
152    static constexpr units::turn_t kBackLeftEncoderOffset = -0.219482421875_tr;
153    static constexpr bool kBackLeftSteerMotorInverted = true;
154    static constexpr bool kBackLeftEncoderInverted = false;
155
156    static constexpr units::inch_t kBackLeftXPos = -10_in;
157    static constexpr units::inch_t kBackLeftYPos = 10_in;
158
159    // Back Right
160    static constexpr int kBackRightDriveMotorId = 5;
161    static constexpr int kBackRightSteerMotorId = 4;
162    static constexpr int kBackRightEncoderId = 2;
163    static constexpr units::turn_t kBackRightEncoderOffset = 0.17236328125_tr;
164    static constexpr bool kBackRightSteerMotorInverted = true;
165    static constexpr bool kBackRightEncoderInverted = false;
166
167    static constexpr units::inch_t kBackRightXPos = -10_in;
168    static constexpr units::inch_t kBackRightYPos = -10_in;
169
170
171public:
172    static constexpr swerve::SwerveModuleConstants FrontLeft = ConstantCreator.CreateModuleConstants(
173            kFrontLeftSteerMotorId, kFrontLeftDriveMotorId, kFrontLeftEncoderId, kFrontLeftEncoderOffset,
174            kFrontLeftXPos, kFrontLeftYPos, kInvertLeftSide, kFrontLeftSteerMotorInverted, kFrontLeftEncoderInverted);
175    static constexpr swerve::SwerveModuleConstants FrontRight = ConstantCreator.CreateModuleConstants(
176            kFrontRightSteerMotorId, kFrontRightDriveMotorId, kFrontRightEncoderId, kFrontRightEncoderOffset,
177            kFrontRightXPos, kFrontRightYPos, kInvertRightSide, kFrontRightSteerMotorInverted, kFrontRightEncoderInverted);
178    static constexpr swerve::SwerveModuleConstants BackLeft = ConstantCreator.CreateModuleConstants(
179            kBackLeftSteerMotorId, kBackLeftDriveMotorId, kBackLeftEncoderId, kBackLeftEncoderOffset,
180            kBackLeftXPos, kBackLeftYPos, kInvertLeftSide, kBackLeftSteerMotorInverted, kBackLeftEncoderInverted);
181    static constexpr swerve::SwerveModuleConstants BackRight = ConstantCreator.CreateModuleConstants(
182            kBackRightSteerMotorId, kBackRightDriveMotorId, kBackRightEncoderId, kBackRightEncoderOffset,
183            kBackRightXPos, kBackRightYPos, kInvertRightSide, kBackRightSteerMotorInverted, kBackRightEncoderInverted);
184
185    /**
186     * Creates a CommandSwerveDrivetrain instance.
187     * This should only be called once in your robot program.
188     */
189    static subsystems::CommandSwerveDrivetrain CreateDrivetrain();
190};
191
192
193/**
194 * \brief Swerve Drive class utilizing CTR Electronics' Phoenix 6 API with the selected device types.
195 */
196class TunerSwerveDrivetrain : public swerve::SwerveDrivetrain<hardware::TalonFX, hardware::TalonFX, hardware::CANcoder> {
197public:
198    using SwerveModuleConstants = swerve::SwerveModuleConstants<configs::TalonFXConfiguration, configs::TalonFXConfiguration, configs::CANcoderConfiguration>;
199
200    /**
201     * \brief Constructs a CTRE SwerveDrivetrain using the specified constants.
202     *
203     * This constructs the underlying hardware devices, so users should not construct
204     * the devices themselves. If they need the devices, they can access them
205     * through getters in the classes.
206     *
207     * \param drivetrainConstants Drivetrain-wide constants for the swerve drive
208     * \param modules             Constants for each specific module
209     */
210    template <std::same_as<SwerveModuleConstants>... ModuleConstants>
211    TunerSwerveDrivetrain(swerve::SwerveDrivetrainConstants const &driveTrainConstants, ModuleConstants const &... modules) :
212        SwerveDrivetrain{driveTrainConstants, modules...}
213    {}
214
215    /**
216     * \brief Constructs a CTRE SwerveDrivetrain using the specified constants.
217     *
218     * This constructs the underlying hardware devices, so users should not construct
219     * the devices themselves. If they need the devices, they can access them
220     * through getters in the classes.
221     *
222     * \param drivetrainConstants        Drivetrain-wide constants for the swerve drive
223     * \param odometryUpdateFrequency    The frequency to run the odometry loop. If
224     *                                   unspecified or set to 0 Hz, this is 250 Hz on
225     *                                   CAN FD, and 100 Hz on CAN 2.0.
226     * \param modules                    Constants for each specific module
227     */
228    template <std::same_as<SwerveModuleConstants>... ModuleConstants>
229    TunerSwerveDrivetrain(
230        swerve::SwerveDrivetrainConstants const &driveTrainConstants,
231        units::hertz_t odometryUpdateFrequency,
232        ModuleConstants const &... modules
233    ) :
234        SwerveDrivetrain{driveTrainConstants, odometryUpdateFrequency, modules...}
235    {}
236
237    /**
238     * \brief Constructs a CTRE SwerveDrivetrain using the specified constants.
239     *
240     * This constructs the underlying hardware devices, so users should not construct
241     * the devices themselves. If they need the devices, they can access them
242     * through getters in the classes.
243     *
244     * \param drivetrainConstants        Drivetrain-wide constants for the swerve drive
245     * \param odometryUpdateFrequency    The frequency to run the odometry loop. If
246     *                                   unspecified or set to 0 Hz, this is 250 Hz on
247     *                                   CAN FD, and 100 Hz on CAN 2.0.
248     * \param odometryStandardDeviation  The standard deviation for odometry calculation
249     *                                   in the form [x, y, theta]ᵀ, with units in meters
250     *                                   and radians
251     * \param visionStandardDeviation    The standard deviation for vision calculation
252     *                                   in the form [x, y, theta]ᵀ, with units in meters
253     *                                   and radians
254     * \param modules                    Constants for each specific module
255     */
256    template <std::same_as<SwerveModuleConstants>... ModuleConstants>
257    TunerSwerveDrivetrain(
258        swerve::SwerveDrivetrainConstants const &driveTrainConstants,
259        units::hertz_t odometryUpdateFrequency,
260        std::array<double, 3> const &odometryStandardDeviation,
261        std::array<double, 3> const &visionStandardDeviation,
262        ModuleConstants const &... modules
263    ) :
264        SwerveDrivetrain{
265            driveTrainConstants, odometryUpdateFrequency,
266            odometryStandardDeviation, visionStandardDeviation, modules...
267        }
268    {}
269};
1#include "generated/TunerConstants.h"
2#include "subsystems/CommandSwerveDrivetrain.h"
3
4subsystems::CommandSwerveDrivetrain TunerConstants::CreateDrivetrain()
5{
6    return {DrivetrainConstants, FrontLeft, FrontRight, BackLeft, BackRight};
7}
  1class TunerConstants:
  2    """
  3    Generated by the Tuner X Swerve Project Generator
  4    https://v6.docs.ctr-electronics.com/en/stable/docs/tuner/tuner-swerve/index.html
  5    """
  6
  7    # Both sets of gains need to be tuned to your individual robot
  8
  9    # The steer motor uses any SwerveModule.SteerRequestType control request with the
 10    # output type specified by SwerveModuleConstants.SteerMotorClosedLoopOutput
 11    _steer_gains = (
 12        configs.Slot0Configs()
 13        .with_k_p(100)
 14        .with_k_i(0)
 15        .with_k_d(0.5)
 16        .with_k_s(0.1)
 17        .with_k_v(1.91)
 18        .with_k_a(0)
 19        .with_static_feedforward_sign(signals.StaticFeedforwardSignValue.USE_CLOSED_LOOP_SIGN)
 20    )
 21    # When using closed-loop control, the drive motor uses the control
 22    # output type specified by SwerveModuleConstants.DriveMotorClosedLoopOutput
 23    _drive_gains = (
 24        configs.Slot0Configs()
 25        .with_k_p(0.1)
 26        .with_k_i(0)
 27        .with_k_d(0)
 28        .with_k_s(0)
 29        .with_k_v(0.124)
 30    )
 31
 32    # The closed-loop output type to use for the steer motors;
 33    # This affects the PID/FF gains for the steer motors
 34    _steer_closed_loop_output = swerve.ClosedLoopOutputType.VOLTAGE
 35    # The closed-loop output type to use for the drive motors;
 36    # This affects the PID/FF gains for the drive motors
 37    _drive_closed_loop_output = swerve.ClosedLoopOutputType.VOLTAGE
 38
 39    # The type of motor used for the drive motor
 40    _drive_motor_type = swerve.DriveMotorArrangement.TALON_FX_INTEGRATED
 41    # The type of motor used for the drive motor
 42    _steer_motor_type = swerve.SteerMotorArrangement.TALON_FX_INTEGRATED
 43
 44    # The remote sensor feedback type to use for the steer motors;
 45    # When not Pro-licensed, Fused*/Sync* automatically fall back to Remote*
 46    _steer_feedback_type = swerve.SteerFeedbackType.FUSED_CANCODER
 47
 48    # The stator current at which the wheels start to slip;
 49    # This needs to be tuned to your individual robot
 50    _slip_current: units.ampere = 120.0
 51
 52    # Initial configs for the drive and steer motors and the azimuth encoder; these cannot be null.
 53    # Some configs will be overwritten; check the `with_*_initial_configs()` API documentation.
 54    _drive_initial_configs = configs.TalonFXConfiguration()
 55    _steer_initial_configs = configs.TalonFXConfiguration().with_current_limits(
 56        configs.CurrentLimitsConfigs()
 57        # Swerve azimuth does not require much torque output, so we can set a relatively low
 58        # stator current limit to help avoid brownouts without impacting performance.
 59        .with_stator_current_limit(60.0).with_stator_current_limit_enable(True)
 60    )
 61    _encoder_initial_configs = configs.CANcoderConfiguration()
 62    # Configs for the Pigeon 2; leave this None to skip applying Pigeon 2 configs
 63    _pigeon_configs: configs.Pigeon2Configuration | None = None
 64
 65    # CAN bus that the devices are located on;
 66    # All swerve devices must share the same CAN bus
 67    canbus = CANBus("canivore", "./logs/example.hoot")
 68
 69    # Theoretical free speed (m/s) at 12 V applied output;
 70    # This needs to be tuned to your individual robot
 71    speed_at_12_volts: units.meters_per_second = 4.54
 72
 73    # Every 1 rotation of the azimuth results in _couple_ratio drive motor turns;
 74    # This may need to be tuned to your individual robot
 75    _couple_ratio = 3.8181818181818183
 76
 77    _drive_gear_ratio = 7.363636363636365
 78    _steer_gear_ratio = 15.42857142857143
 79    _wheel_radius: units.meter = inchesToMeters(2.167)
 80
 81    _invert_left_side = False
 82    _invert_right_side = True
 83
 84    _pigeon_id = 1
 85
 86    # These are only used for simulation
 87    _steer_inertia: units.kilogram_square_meter = 0.01
 88    _drive_inertia: units.kilogram_square_meter = 0.01
 89    # Simulated voltage necessary to overcome friction
 90    _steer_friction_voltage: units.volt = 0.2
 91    _drive_friction_voltage: units.volt = 0.2
 92
 93    drivetrain_constants = (
 94        swerve.SwerveDrivetrainConstants()
 95        .with_can_bus_name(canbus.name)
 96        .with_pigeon2_id(_pigeon_id)
 97        .with_pigeon2_configs(_pigeon_configs)
 98    )
 99
100    _constants_creator: swerve.SwerveModuleConstantsFactory[configs.TalonFXConfiguration, configs.TalonFXConfiguration, configs.CANcoderConfiguration] = (
101        swerve.SwerveModuleConstantsFactory()
102        .with_drive_motor_gear_ratio(_drive_gear_ratio)
103        .with_steer_motor_gear_ratio(_steer_gear_ratio)
104        .with_coupling_gear_ratio(_couple_ratio)
105        .with_wheel_radius(_wheel_radius)
106        .with_steer_motor_gains(_steer_gains)
107        .with_drive_motor_gains(_drive_gains)
108        .with_steer_motor_closed_loop_output(_steer_closed_loop_output)
109        .with_drive_motor_closed_loop_output(_drive_closed_loop_output)
110        .with_slip_current(_slip_current)
111        .with_speed_at12_volts(speed_at_12_volts)
112        .with_drive_motor_type(_drive_motor_type)
113        .with_steer_motor_type(_steer_motor_type)
114        .with_feedback_source(_steer_feedback_type)
115        .with_drive_motor_initial_configs(_drive_initial_configs)
116        .with_steer_motor_initial_configs(_steer_initial_configs)
117        .with_encoder_initial_configs(_encoder_initial_configs)
118        .with_steer_inertia(_steer_inertia)
119        .with_drive_inertia(_drive_inertia)
120        .with_steer_friction_voltage(_steer_friction_voltage)
121        .with_drive_friction_voltage(_drive_friction_voltage)
122    )
123
124
125    # Front Left
126    _front_left_drive_motor_id = 3
127    _front_left_steer_motor_id = 2
128    _front_left_encoder_id = 1
129    _front_left_encoder_offset: units.rotation = 0.15234375
130    _front_left_steer_motor_inverted = True
131    _front_left_encoder_inverted = False
132
133    _front_left_x_pos: units.meter = inchesToMeters(10)
134    _front_left_y_pos: units.meter = inchesToMeters(10)
135
136    # Front Right
137    _front_right_drive_motor_id = 1
138    _front_right_steer_motor_id = 0
139    _front_right_encoder_id = 0
140    _front_right_encoder_offset: units.rotation = -0.4873046875
141    _front_right_steer_motor_inverted = True
142    _front_right_encoder_inverted = False
143
144    _front_right_x_pos: units.meter = inchesToMeters(10)
145    _front_right_y_pos: units.meter = inchesToMeters(-10)
146
147    # Back Left
148    _back_left_drive_motor_id = 7
149    _back_left_steer_motor_id = 6
150    _back_left_encoder_id = 3
151    _back_left_encoder_offset: units.rotation = -0.219482421875
152    _back_left_steer_motor_inverted = True
153    _back_left_encoder_inverted = False
154
155    _back_left_x_pos: units.meter = inchesToMeters(-10)
156    _back_left_y_pos: units.meter = inchesToMeters(10)
157
158    # Back Right
159    _back_right_drive_motor_id = 5
160    _back_right_steer_motor_id = 4
161    _back_right_encoder_id = 2
162    _back_right_encoder_offset: units.rotation = 0.17236328125
163    _back_right_steer_motor_inverted = True
164    _back_right_encoder_inverted = False
165
166    _back_right_x_pos: units.meter = inchesToMeters(-10)
167    _back_right_y_pos: units.meter = inchesToMeters(-10)
168
169
170    front_left = _constants_creator.create_module_constants(
171        _front_left_steer_motor_id,
172        _front_left_drive_motor_id,
173        _front_left_encoder_id,
174        _front_left_encoder_offset,
175        _front_left_x_pos,
176        _front_left_y_pos,
177        _invert_left_side,
178        _front_left_steer_motor_inverted,
179        _front_left_encoder_inverted,
180    )
181    front_right = _constants_creator.create_module_constants(
182        _front_right_steer_motor_id,
183        _front_right_drive_motor_id,
184        _front_right_encoder_id,
185        _front_right_encoder_offset,
186        _front_right_x_pos,
187        _front_right_y_pos,
188        _invert_right_side,
189        _front_right_steer_motor_inverted,
190        _front_right_encoder_inverted,
191    )
192    back_left = _constants_creator.create_module_constants(
193        _back_left_steer_motor_id,
194        _back_left_drive_motor_id,
195        _back_left_encoder_id,
196        _back_left_encoder_offset,
197        _back_left_x_pos,
198        _back_left_y_pos,
199        _invert_left_side,
200        _back_left_steer_motor_inverted,
201        _back_left_encoder_inverted,
202    )
203    back_right = _constants_creator.create_module_constants(
204        _back_right_steer_motor_id,
205        _back_right_drive_motor_id,
206        _back_right_encoder_id,
207        _back_right_encoder_offset,
208        _back_right_x_pos,
209        _back_right_y_pos,
210        _invert_right_side,
211        _back_right_steer_motor_inverted,
212        _back_right_encoder_inverted,
213    )
214
215    @classmethod
216    def create_drivetrain(cls) -> CommandSwerveDrivetrain:
217        """
218        Creates a CommandSwerveDrivetrain instance.
219        This should only be called once in your robot program.
220        """
221        return CommandSwerveDrivetrain(
222            hardware.TalonFX,
223            hardware.TalonFX,
224            hardware.CANcoder,
225            cls.drivetrain_constants,
226            [
227                cls.front_left,
228                cls.front_right,
229                cls.back_left,
230                cls.back_right,
231            ],
232        )