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.
CouplingGearRatioThe 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\).
SlipCurrentThis 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/EncoderInitialConfigsAn 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.
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 )