001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.math.kinematics;
006
007import static edu.wpi.first.units.Units.MetersPerSecond;
008import static edu.wpi.first.units.Units.RadiansPerSecond;
009
010import edu.wpi.first.math.MathSharedStore;
011import edu.wpi.first.math.MathUsageId;
012import edu.wpi.first.math.geometry.Rotation2d;
013import edu.wpi.first.math.geometry.Translation2d;
014import edu.wpi.first.math.geometry.Twist2d;
015import edu.wpi.first.units.Angle;
016import edu.wpi.first.units.Distance;
017import edu.wpi.first.units.Measure;
018import edu.wpi.first.units.Velocity;
019import java.util.Arrays;
020import org.ejml.simple.SimpleMatrix;
021
022/**
023 * Helper class that converts a chassis velocity (dx, dy, and dtheta components) into individual
024 * module states (speed and angle).
025 *
026 * <p>The inverse kinematics (converting from a desired chassis velocity to individual module
027 * states) uses the relative locations of the modules with respect to the center of rotation. The
028 * center of rotation for inverse kinematics is also variable. This means that you can set your
029 * center of rotation in a corner of the robot to perform special evasion maneuvers.
030 *
031 * <p>Forward kinematics (converting an array of module states into the overall chassis motion) is
032 * performs the exact opposite of what inverse kinematics does. Since this is an overdetermined
033 * system (more equations than variables), we use a least-squares approximation.
034 *
035 * <p>The inverse kinematics: [moduleStates] = [moduleLocations] * [chassisSpeeds] We take the
036 * Moore-Penrose pseudoinverse of [moduleLocations] and then multiply by [moduleStates] to get our
037 * chassis speeds.
038 *
039 * <p>Forward kinematics is also used for odometry -- determining the position of the robot on the
040 * field using encoders and a gyro.
041 */
042public class SwerveDriveKinematics
043    implements Kinematics<SwerveDriveKinematics.SwerveDriveWheelStates, SwerveDriveWheelPositions> {
044  public static class SwerveDriveWheelStates {
045    public SwerveModuleState[] states;
046
047    /**
048     * Creates a new SwerveDriveWheelStates instance.
049     *
050     * @param states The swerve module states. This will be deeply copied.
051     */
052    public SwerveDriveWheelStates(SwerveModuleState[] states) {
053      this.states = new SwerveModuleState[states.length];
054      for (int i = 0; i < states.length; i++) {
055        this.states[i] = new SwerveModuleState(states[i].speedMetersPerSecond, states[i].angle);
056      }
057    }
058  }
059
060  private final SimpleMatrix m_inverseKinematics;
061  private final SimpleMatrix m_forwardKinematics;
062
063  private final int m_numModules;
064  private final Translation2d[] m_modules;
065  private Rotation2d[] m_moduleHeadings;
066  private Translation2d m_prevCoR = new Translation2d();
067
068  /**
069   * Constructs a swerve drive kinematics object. This takes in a variable number of module
070   * locations as Translation2d objects. The order in which you pass in the module locations is the
071   * same order that you will receive the module states when performing inverse kinematics. It is
072   * also expected that you pass in the module states in the same order when calling the forward
073   * kinematics methods.
074   *
075   * @param moduleTranslationsMeters The locations of the modules relative to the physical center of
076   *     the robot.
077   */
078  public SwerveDriveKinematics(Translation2d... moduleTranslationsMeters) {
079    if (moduleTranslationsMeters.length < 2) {
080      throw new IllegalArgumentException("A swerve drive requires at least two modules");
081    }
082    m_numModules = moduleTranslationsMeters.length;
083    m_modules = Arrays.copyOf(moduleTranslationsMeters, m_numModules);
084    m_moduleHeadings = new Rotation2d[m_numModules];
085    Arrays.fill(m_moduleHeadings, new Rotation2d());
086    m_inverseKinematics = new SimpleMatrix(m_numModules * 2, 3);
087
088    for (int i = 0; i < m_numModules; i++) {
089      m_inverseKinematics.setRow(i * 2 + 0, 0, /* Start Data */ 1, 0, -m_modules[i].getY());
090      m_inverseKinematics.setRow(i * 2 + 1, 0, /* Start Data */ 0, 1, +m_modules[i].getX());
091    }
092    m_forwardKinematics = m_inverseKinematics.pseudoInverse();
093
094    MathSharedStore.reportUsage(MathUsageId.kKinematics_SwerveDrive, 1);
095  }
096
097  /**
098   * Reset the internal swerve module headings.
099   *
100   * @param moduleHeadings The swerve module headings. The order of the module headings should be
101   *     same as passed into the constructor of this class.
102   */
103  public void resetHeadings(Rotation2d... moduleHeadings) {
104    if (moduleHeadings.length != m_numModules) {
105      throw new IllegalArgumentException(
106          "Number of headings is not consistent with number of module locations provided in "
107              + "constructor");
108    }
109    m_moduleHeadings = Arrays.copyOf(moduleHeadings, m_numModules);
110  }
111
112  /**
113   * Performs inverse kinematics to return the module states from a desired chassis velocity. This
114   * method is often used to convert joystick values into module speeds and angles.
115   *
116   * <p>This function also supports variable centers of rotation. During normal operations, the
117   * center of rotation is usually the same as the physical center of the robot; therefore, the
118   * argument is defaulted to that use case. However, if you wish to change the center of rotation
119   * for evasive maneuvers, vision alignment, or for any other use case, you can do so.
120   *
121   * <p>In the case that the desired chassis speeds are zero (i.e. the robot will be stationary),
122   * the previously calculated module angle will be maintained.
123   *
124   * @param chassisSpeeds The desired chassis speed.
125   * @param centerOfRotationMeters The center of rotation. For example, if you set the center of
126   *     rotation at one corner of the robot and provide a chassis speed that only has a dtheta
127   *     component, the robot will rotate around that corner.
128   * @return An array containing the module states. Use caution because these module states are not
129   *     normalized. Sometimes, a user input may cause one of the module speeds to go above the
130   *     attainable max velocity. Use the {@link #desaturateWheelSpeeds(SwerveModuleState[], double)
131   *     DesaturateWheelSpeeds} function to rectify this issue.
132   */
133  public SwerveModuleState[] toSwerveModuleStates(
134      ChassisSpeeds chassisSpeeds, Translation2d centerOfRotationMeters) {
135    var moduleStates = new SwerveModuleState[m_numModules];
136
137    if (chassisSpeeds.vxMetersPerSecond == 0.0
138        && chassisSpeeds.vyMetersPerSecond == 0.0
139        && chassisSpeeds.omegaRadiansPerSecond == 0.0) {
140      for (int i = 0; i < m_numModules; i++) {
141        moduleStates[i] = new SwerveModuleState(0.0, m_moduleHeadings[i]);
142      }
143
144      return moduleStates;
145    }
146
147    if (!centerOfRotationMeters.equals(m_prevCoR)) {
148      for (int i = 0; i < m_numModules; i++) {
149        m_inverseKinematics.setRow(
150            i * 2 + 0,
151            0, /* Start Data */
152            1,
153            0,
154            -m_modules[i].getY() + centerOfRotationMeters.getY());
155        m_inverseKinematics.setRow(
156            i * 2 + 1,
157            0, /* Start Data */
158            0,
159            1,
160            +m_modules[i].getX() - centerOfRotationMeters.getX());
161      }
162      m_prevCoR = centerOfRotationMeters;
163    }
164
165    var chassisSpeedsVector = new SimpleMatrix(3, 1);
166    chassisSpeedsVector.setColumn(
167        0,
168        0,
169        chassisSpeeds.vxMetersPerSecond,
170        chassisSpeeds.vyMetersPerSecond,
171        chassisSpeeds.omegaRadiansPerSecond);
172
173    var moduleStatesMatrix = m_inverseKinematics.mult(chassisSpeedsVector);
174
175    for (int i = 0; i < m_numModules; i++) {
176      double x = moduleStatesMatrix.get(i * 2, 0);
177      double y = moduleStatesMatrix.get(i * 2 + 1, 0);
178
179      double speed = Math.hypot(x, y);
180      Rotation2d angle = new Rotation2d(x, y);
181
182      moduleStates[i] = new SwerveModuleState(speed, angle);
183      m_moduleHeadings[i] = angle;
184    }
185
186    return moduleStates;
187  }
188
189  /**
190   * Performs inverse kinematics. See {@link #toSwerveModuleStates(ChassisSpeeds, Translation2d)}
191   * toSwerveModuleStates for more information.
192   *
193   * @param chassisSpeeds The desired chassis speed.
194   * @return An array containing the module states.
195   */
196  public SwerveModuleState[] toSwerveModuleStates(ChassisSpeeds chassisSpeeds) {
197    return toSwerveModuleStates(chassisSpeeds, new Translation2d());
198  }
199
200  @Override
201  public SwerveDriveWheelStates toWheelSpeeds(ChassisSpeeds chassisSpeeds) {
202    return new SwerveDriveWheelStates(toSwerveModuleStates(chassisSpeeds));
203  }
204
205  /**
206   * Performs forward kinematics to return the resulting chassis state from the given module states.
207   * This method is often used for odometry -- determining the robot's position on the field using
208   * data from the real-world speed and angle of each module on the robot.
209   *
210   * @param moduleStates The state of the modules (as a SwerveModuleState type) as measured from
211   *     respective encoders and gyros. The order of the swerve module states should be same as
212   *     passed into the constructor of this class.
213   * @return The resulting chassis speed.
214   */
215  public ChassisSpeeds toChassisSpeeds(SwerveModuleState... moduleStates) {
216    if (moduleStates.length != m_numModules) {
217      throw new IllegalArgumentException(
218          "Number of modules is not consistent with number of module locations provided in "
219              + "constructor");
220    }
221    var moduleStatesMatrix = new SimpleMatrix(m_numModules * 2, 1);
222
223    for (int i = 0; i < m_numModules; i++) {
224      var module = moduleStates[i];
225      moduleStatesMatrix.set(i * 2, 0, module.speedMetersPerSecond * module.angle.getCos());
226      moduleStatesMatrix.set(i * 2 + 1, module.speedMetersPerSecond * module.angle.getSin());
227    }
228
229    var chassisSpeedsVector = m_forwardKinematics.mult(moduleStatesMatrix);
230    return new ChassisSpeeds(
231        chassisSpeedsVector.get(0, 0),
232        chassisSpeedsVector.get(1, 0),
233        chassisSpeedsVector.get(2, 0));
234  }
235
236  @Override
237  public ChassisSpeeds toChassisSpeeds(SwerveDriveWheelStates wheelStates) {
238    return toChassisSpeeds(wheelStates.states);
239  }
240
241  /**
242   * Performs forward kinematics to return the resulting chassis state from the given module states.
243   * This method is often used for odometry -- determining the robot's position on the field using
244   * data from the real-world speed and angle of each module on the robot.
245   *
246   * @param moduleDeltas The latest change in position of the modules (as a SwerveModulePosition
247   *     type) as measured from respective encoders and gyros. The order of the swerve module states
248   *     should be same as passed into the constructor of this class.
249   * @return The resulting Twist2d.
250   */
251  public Twist2d toTwist2d(SwerveModulePosition... moduleDeltas) {
252    if (moduleDeltas.length != m_numModules) {
253      throw new IllegalArgumentException(
254          "Number of modules is not consistent with number of module locations provided in "
255              + "constructor");
256    }
257    var moduleDeltaMatrix = new SimpleMatrix(m_numModules * 2, 1);
258
259    for (int i = 0; i < m_numModules; i++) {
260      var module = moduleDeltas[i];
261      moduleDeltaMatrix.set(i * 2, 0, module.distanceMeters * module.angle.getCos());
262      moduleDeltaMatrix.set(i * 2 + 1, module.distanceMeters * module.angle.getSin());
263    }
264
265    var chassisDeltaVector = m_forwardKinematics.mult(moduleDeltaMatrix);
266    return new Twist2d(
267        chassisDeltaVector.get(0, 0), chassisDeltaVector.get(1, 0), chassisDeltaVector.get(2, 0));
268  }
269
270  @Override
271  public Twist2d toTwist2d(SwerveDriveWheelPositions start, SwerveDriveWheelPositions end) {
272    if (start.positions.length != end.positions.length) {
273      throw new IllegalArgumentException("Inconsistent number of modules!");
274    }
275    var newPositions = new SwerveModulePosition[start.positions.length];
276    for (int i = 0; i < start.positions.length; i++) {
277      var startModule = start.positions[i];
278      var endModule = end.positions[i];
279      newPositions[i] =
280          new SwerveModulePosition(
281              endModule.distanceMeters - startModule.distanceMeters, endModule.angle);
282    }
283    return toTwist2d(newPositions);
284  }
285
286  /**
287   * Renormalizes the wheel speeds if any individual speed is above the specified maximum.
288   *
289   * <p>Sometimes, after inverse kinematics, the requested speed from one or more modules may be
290   * above the max attainable speed for the driving motor on that module. To fix this issue, one can
291   * reduce all the wheel speeds to make sure that all requested module speeds are at-or-below the
292   * absolute threshold, while maintaining the ratio of speeds between modules.
293   *
294   * @param moduleStates Reference to array of module states. The array will be mutated with the
295   *     normalized speeds!
296   * @param attainableMaxSpeedMetersPerSecond The absolute max speed that a module can reach.
297   */
298  public static void desaturateWheelSpeeds(
299      SwerveModuleState[] moduleStates, double attainableMaxSpeedMetersPerSecond) {
300    double realMaxSpeed = 0;
301    for (SwerveModuleState moduleState : moduleStates) {
302      realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond));
303    }
304    if (realMaxSpeed > attainableMaxSpeedMetersPerSecond) {
305      for (SwerveModuleState moduleState : moduleStates) {
306        moduleState.speedMetersPerSecond =
307            moduleState.speedMetersPerSecond / realMaxSpeed * attainableMaxSpeedMetersPerSecond;
308      }
309    }
310  }
311
312  /**
313   * Renormalizes the wheel speeds if any individual speed is above the specified maximum.
314   *
315   * <p>Sometimes, after inverse kinematics, the requested speed from one or more modules may be
316   * above the max attainable speed for the driving motor on that module. To fix this issue, one can
317   * reduce all the wheel speeds to make sure that all requested module speeds are at-or-below the
318   * absolute threshold, while maintaining the ratio of speeds between modules.
319   *
320   * @param moduleStates Reference to array of module states. The array will be mutated with the
321   *     normalized speeds!
322   * @param attainableMaxSpeed The absolute max speed that a module can reach.
323   */
324  public static void desaturateWheelSpeeds(
325      SwerveModuleState[] moduleStates, Measure<Velocity<Distance>> attainableMaxSpeed) {
326    desaturateWheelSpeeds(moduleStates, attainableMaxSpeed.in(MetersPerSecond));
327  }
328
329  /**
330   * Renormalizes the wheel speeds if any individual speed is above the specified maximum, as well
331   * as getting rid of joystick saturation at edges of joystick.
332   *
333   * <p>Sometimes, after inverse kinematics, the requested speed from one or more modules may be
334   * above the max attainable speed for the driving motor on that module. To fix this issue, one can
335   * reduce all the wheel speeds to make sure that all requested module speeds are at-or-below the
336   * absolute threshold, while maintaining the ratio of speeds between modules.
337   *
338   * @param moduleStates Reference to array of module states. The array will be mutated with the
339   *     normalized speeds!
340   * @param desiredChassisSpeed The desired speed of the robot
341   * @param attainableMaxModuleSpeedMetersPerSecond The absolute max speed that a module can reach
342   * @param attainableMaxTranslationalSpeedMetersPerSecond The absolute max speed that your robot
343   *     can reach while translating
344   * @param attainableMaxRotationalVelocityRadiansPerSecond The absolute max speed the robot can
345   *     reach while rotating
346   */
347  public static void desaturateWheelSpeeds(
348      SwerveModuleState[] moduleStates,
349      ChassisSpeeds desiredChassisSpeed,
350      double attainableMaxModuleSpeedMetersPerSecond,
351      double attainableMaxTranslationalSpeedMetersPerSecond,
352      double attainableMaxRotationalVelocityRadiansPerSecond) {
353    double realMaxSpeed = 0;
354    for (SwerveModuleState moduleState : moduleStates) {
355      realMaxSpeed = Math.max(realMaxSpeed, Math.abs(moduleState.speedMetersPerSecond));
356    }
357
358    if (attainableMaxTranslationalSpeedMetersPerSecond == 0
359        || attainableMaxRotationalVelocityRadiansPerSecond == 0
360        || realMaxSpeed == 0) {
361      return;
362    }
363    double translationalK =
364        Math.hypot(desiredChassisSpeed.vxMetersPerSecond, desiredChassisSpeed.vyMetersPerSecond)
365            / attainableMaxTranslationalSpeedMetersPerSecond;
366    double rotationalK =
367        Math.abs(desiredChassisSpeed.omegaRadiansPerSecond)
368            / attainableMaxRotationalVelocityRadiansPerSecond;
369    double k = Math.max(translationalK, rotationalK);
370    double scale = Math.min(k * attainableMaxModuleSpeedMetersPerSecond / realMaxSpeed, 1);
371    for (SwerveModuleState moduleState : moduleStates) {
372      moduleState.speedMetersPerSecond *= scale;
373    }
374  }
375
376  /**
377   * Renormalizes the wheel speeds if any individual speed is above the specified maximum, as well
378   * as getting rid of joystick saturation at edges of joystick.
379   *
380   * <p>Sometimes, after inverse kinematics, the requested speed from one or more modules may be
381   * above the max attainable speed for the driving motor on that module. To fix this issue, one can
382   * reduce all the wheel speeds to make sure that all requested module speeds are at-or-below the
383   * absolute threshold, while maintaining the ratio of speeds between modules.
384   *
385   * @param moduleStates Reference to array of module states. The array will be mutated with the
386   *     normalized speeds!
387   * @param desiredChassisSpeed The desired speed of the robot
388   * @param attainableMaxModuleSpeed The absolute max speed that a module can reach
389   * @param attainableMaxTranslationalSpeed The absolute max speed that your robot can reach while
390   *     translating
391   * @param attainableMaxRotationalVelocity The absolute max speed the robot can reach while
392   *     rotating
393   */
394  public static void desaturateWheelSpeeds(
395      SwerveModuleState[] moduleStates,
396      ChassisSpeeds desiredChassisSpeed,
397      Measure<Velocity<Distance>> attainableMaxModuleSpeed,
398      Measure<Velocity<Distance>> attainableMaxTranslationalSpeed,
399      Measure<Velocity<Angle>> attainableMaxRotationalVelocity) {
400    desaturateWheelSpeeds(
401        moduleStates,
402        desiredChassisSpeed,
403        attainableMaxModuleSpeed.in(MetersPerSecond),
404        attainableMaxTranslationalSpeed.in(MetersPerSecond),
405        attainableMaxRotationalVelocity.in(RadiansPerSecond));
406  }
407}