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 org.wpilib.math.kinematics; 006 007import static org.wpilib.units.Units.MetersPerSecond; 008import static org.wpilib.units.Units.RadiansPerSecond; 009 010import java.util.Arrays; 011import org.ejml.simple.SimpleMatrix; 012import org.wpilib.annotation.NoDiscard; 013import org.wpilib.math.geometry.Rotation2d; 014import org.wpilib.math.geometry.Translation2d; 015import org.wpilib.math.geometry.Twist2d; 016import org.wpilib.math.kinematics.proto.SwerveDriveKinematicsProto; 017import org.wpilib.math.kinematics.struct.SwerveDriveKinematicsStruct; 018import org.wpilib.math.util.MathSharedStore; 019import org.wpilib.units.measure.AngularVelocity; 020import org.wpilib.units.measure.LinearVelocity; 021import org.wpilib.util.protobuf.ProtobufSerializable; 022import org.wpilib.util.struct.Struct; 023import org.wpilib.util.struct.StructSerializable; 024 025/** 026 * Helper class that converts a chassis velocity (dx, dy, and dtheta components) into individual 027 * module states (velocity and angle). 028 * 029 * <p>The inverse kinematics (converting from a desired chassis velocity to individual module 030 * states) uses the relative locations of the modules with respect to the center of rotation. The 031 * center of rotation for inverse kinematics is also variable. This means that you can set your 032 * center of rotation in a corner of the robot to perform special evasion maneuvers. 033 * 034 * <p>Forward kinematics (converting an array of module states into the overall chassis motion) is 035 * performs the exact opposite of what inverse kinematics does. Since this is an overdetermined 036 * system (more equations than variables), we use a least-squares approximation. 037 * 038 * <p>The inverse kinematics: [moduleVelocities] = [moduleLocations] * [chassisVelocities] We take 039 * the Moore-Penrose pseudoinverse of [moduleLocations] and then multiply by [moduleVelocities] to 040 * get our chassis velocities. 041 * 042 * <p>Forward kinematics is also used for odometry -- determining the position of the robot on the 043 * field using encoders and a gyro. 044 */ 045@SuppressWarnings("overrides") 046public class SwerveDriveKinematics 047 implements Kinematics< 048 SwerveModulePosition[], SwerveModuleVelocity[], SwerveModuleAcceleration[]>, 049 ProtobufSerializable, 050 StructSerializable { 051 private final SimpleMatrix m_firstOrderInverseKinematics; 052 private final SimpleMatrix m_firstOrderForwardKinematics; 053 private final SimpleMatrix m_secondOrderInverseKinematics; 054 private final SimpleMatrix m_secondOrderForwardKinematics; 055 056 private final int m_numModules; 057 private final Translation2d[] m_modules; 058 private Rotation2d[] m_moduleHeadings; 059 private Translation2d m_prevCoR = Translation2d.kZero; 060 061 /** 062 * Constructs a swerve drive kinematics object. This takes in a variable number of module 063 * locations as Translation2d objects. The order in which you pass in the module locations is the 064 * same order that you will receive the module states when performing inverse kinematics. It is 065 * also expected that you pass in the module states in the same order when calling the forward 066 * kinematics methods. 067 * 068 * @param moduleTranslations The locations of the modules relative to the physical center of the 069 * robot. 070 */ 071 public SwerveDriveKinematics(Translation2d... moduleTranslations) { 072 if (moduleTranslations.length < 2) { 073 throw new IllegalArgumentException("A swerve drive requires at least two modules"); 074 } 075 m_numModules = moduleTranslations.length; 076 m_modules = Arrays.copyOf(moduleTranslations, m_numModules); 077 m_moduleHeadings = new Rotation2d[m_numModules]; 078 Arrays.fill(m_moduleHeadings, Rotation2d.kZero); 079 m_firstOrderInverseKinematics = new SimpleMatrix(m_numModules * 2, 3); 080 m_secondOrderInverseKinematics = new SimpleMatrix(m_numModules * 2, 4); 081 082 setInverseKinematics(Translation2d.kZero); 083 084 m_firstOrderForwardKinematics = m_firstOrderInverseKinematics.pseudoInverse(); 085 m_secondOrderForwardKinematics = m_secondOrderInverseKinematics.pseudoInverse(); 086 087 MathSharedStore.reportUsage("SwerveDriveKinematics", ""); 088 } 089 090 /** 091 * Reset the internal swerve module headings. 092 * 093 * @param moduleHeadings The swerve module headings. The order of the module headings should be 094 * same as passed into the constructor of this class. 095 */ 096 public void resetHeadings(Rotation2d... moduleHeadings) { 097 if (moduleHeadings.length != m_numModules) { 098 throw new IllegalArgumentException( 099 "Number of headings is not consistent with number of module locations provided in " 100 + "constructor"); 101 } 102 m_moduleHeadings = Arrays.copyOf(moduleHeadings, m_numModules); 103 } 104 105 /** 106 * Performs inverse kinematics to return the module states from a desired chassis velocity. This 107 * method is often used to convert joystick values into module velocities and angles. 108 * 109 * <p>This function also supports variable centers of rotation. During normal operations, the 110 * center of rotation is usually the same as the physical center of the robot; therefore, the 111 * argument is defaulted to that use case. However, if you wish to change the center of rotation 112 * for evasive maneuvers, vision alignment, or for any other use case, you can do so. 113 * 114 * <p>In the case that the desired chassis velocities are zero (i.e. the robot will be 115 * stationary), the previously calculated module angle will be maintained. 116 * 117 * @param chassisVelocities The desired chassis velocity. 118 * @param centerOfRotation The center of rotation. For example, if you set the center of rotation 119 * at one corner of the robot and provide a chassis velocity that only has a dtheta component, 120 * the robot will rotate around that corner. 121 * @return An array containing the module states. Use caution because these module states are not 122 * normalized. Sometimes, a user input may cause one of the module velocities to go above the 123 * attainable max velocity. Use the {@link #desaturateWheelVelocities(SwerveModuleVelocity[], 124 * double) DesaturateWheelVelocities} function to rectify this issue. 125 */ 126 public SwerveModuleVelocity[] toSwerveModuleVelocities( 127 ChassisVelocities chassisVelocities, Translation2d centerOfRotation) { 128 var moduleVelocities = new SwerveModuleVelocity[m_numModules]; 129 130 if (chassisVelocities.vx == 0.0 131 && chassisVelocities.vy == 0.0 132 && chassisVelocities.omega == 0.0) { 133 for (int i = 0; i < m_numModules; i++) { 134 moduleVelocities[i] = new SwerveModuleVelocity(0.0, m_moduleHeadings[i]); 135 } 136 137 return moduleVelocities; 138 } 139 140 if (!centerOfRotation.equals(m_prevCoR)) { 141 setInverseKinematics(centerOfRotation); 142 } 143 144 var chassisVelocitiesVector = new SimpleMatrix(3, 1); 145 chassisVelocitiesVector.setColumn( 146 0, 0, chassisVelocities.vx, chassisVelocities.vy, chassisVelocities.omega); 147 148 var moduleVelocitiesMatrix = m_firstOrderInverseKinematics.mult(chassisVelocitiesVector); 149 150 for (int i = 0; i < m_numModules; i++) { 151 double x = moduleVelocitiesMatrix.get(i * 2, 0); 152 double y = moduleVelocitiesMatrix.get(i * 2 + 1, 0); 153 154 double velocity = Math.hypot(x, y); 155 Rotation2d angle = velocity > 1e-6 ? new Rotation2d(x, y) : m_moduleHeadings[i]; 156 157 moduleVelocities[i] = new SwerveModuleVelocity(velocity, angle); 158 m_moduleHeadings[i] = angle; 159 } 160 161 return moduleVelocities; 162 } 163 164 /** 165 * Performs inverse kinematics. See {@link #toSwerveModuleVelocities(ChassisVelocities, 166 * Translation2d)} toSwerveModuleVelocities for more information. 167 * 168 * @param chassisVelocities The desired chassis velocity. 169 * @return An array containing the module states. 170 */ 171 public SwerveModuleVelocity[] toSwerveModuleVelocities(ChassisVelocities chassisVelocities) { 172 return toSwerveModuleVelocities(chassisVelocities, Translation2d.kZero); 173 } 174 175 @Override 176 public SwerveModuleVelocity[] toWheelVelocities(ChassisVelocities chassisVelocities) { 177 return toSwerveModuleVelocities(chassisVelocities); 178 } 179 180 /** 181 * Performs forward kinematics to return the resulting chassis state from the given module states. 182 * This method is often used for odometry -- determining the robot's position on the field using 183 * data from the real-world velocity and angle of each module on the robot. 184 * 185 * @param moduleVelocities The state of the modules (as a SwerveModuleVelocity type) as measured 186 * from respective encoders and gyros. The order of the swerve module states should be same as 187 * passed into the constructor of this class. 188 * @return The resulting chassis velocity. 189 */ 190 @Override 191 public ChassisVelocities toChassisVelocities(SwerveModuleVelocity... moduleVelocities) { 192 if (moduleVelocities.length != m_numModules) { 193 throw new IllegalArgumentException( 194 "Number of modules is not consistent with number of module locations provided in " 195 + "constructor"); 196 } 197 var moduleVelocitiesMatrix = new SimpleMatrix(m_numModules * 2, 1); 198 199 for (int i = 0; i < m_numModules; i++) { 200 var module = moduleVelocities[i]; 201 moduleVelocitiesMatrix.set(i * 2, 0, module.velocity * module.angle.getCos()); 202 moduleVelocitiesMatrix.set(i * 2 + 1, module.velocity * module.angle.getSin()); 203 } 204 205 var chassisVelocitiesVector = m_firstOrderForwardKinematics.mult(moduleVelocitiesMatrix); 206 return new ChassisVelocities( 207 chassisVelocitiesVector.get(0, 0), 208 chassisVelocitiesVector.get(1, 0), 209 chassisVelocitiesVector.get(2, 0)); 210 } 211 212 /** 213 * Performs forward kinematics to return the resulting chassis state from the given module states. 214 * This method is often used for odometry -- determining the robot's position on the field using 215 * data from the real-world velocity and angle of each module on the robot. 216 * 217 * @param moduleDeltas The latest change in position of the modules (as a SwerveModulePosition 218 * type) as measured from respective encoders and gyros. The order of the swerve module states 219 * should be same as passed into the constructor of this class. 220 * @return The resulting Twist2d. 221 */ 222 public Twist2d toTwist2d(SwerveModulePosition... moduleDeltas) { 223 if (moduleDeltas.length != m_numModules) { 224 throw new IllegalArgumentException( 225 "Number of modules is not consistent with number of module locations provided in " 226 + "constructor"); 227 } 228 var moduleDeltaMatrix = new SimpleMatrix(m_numModules * 2, 1); 229 230 for (int i = 0; i < m_numModules; i++) { 231 var module = moduleDeltas[i]; 232 moduleDeltaMatrix.set(i * 2, 0, module.distance * module.angle.getCos()); 233 moduleDeltaMatrix.set(i * 2 + 1, 0, module.distance * module.angle.getSin()); 234 } 235 236 var chassisDeltaVector = m_firstOrderForwardKinematics.mult(moduleDeltaMatrix); 237 return new Twist2d( 238 chassisDeltaVector.get(0, 0), chassisDeltaVector.get(1, 0), chassisDeltaVector.get(2, 0)); 239 } 240 241 @Override 242 public Twist2d toTwist2d(SwerveModulePosition[] start, SwerveModulePosition[] end) { 243 if (start.length != end.length) { 244 throw new IllegalArgumentException("Inconsistent number of modules!"); 245 } 246 var newPositions = new SwerveModulePosition[start.length]; 247 for (int i = 0; i < start.length; i++) { 248 newPositions[i] = new SwerveModulePosition(end[i].distance - start[i].distance, end[i].angle); 249 } 250 return toTwist2d(newPositions); 251 } 252 253 /** 254 * Renormalizes the wheel velocities if any individual velocity is above the specified maximum. 255 * 256 * <p>Sometimes, after inverse kinematics, the requested velocity from one or more modules may be 257 * above the max attainable velocity for the driving motor on that module. To fix this issue, one 258 * can reduce all the wheel velocities to make sure that all requested module velocities are 259 * at-or-below the absolute threshold, while maintaining the ratio of velocities between modules. 260 * 261 * <p>Scaling down the module velocities rotates the direction of net motion in the opposite 262 * direction of rotational velocity, which makes discretizing the chassis velocities inaccurate 263 * because the discretization did not account for this translational skew. 264 * 265 * @param moduleVelocities The array of module velocities. 266 * @param attainableMaxVelocity The absolute max velocity in meters per second that a module can 267 * reach. 268 * @return The array of desaturated module velocities. 269 */ 270 @NoDiscard 271 public static SwerveModuleVelocity[] desaturateWheelVelocities( 272 SwerveModuleVelocity[] moduleVelocities, double attainableMaxVelocity) { 273 double realMaxVelocity = 0; 274 for (SwerveModuleVelocity moduleVelocity : moduleVelocities) { 275 realMaxVelocity = Math.max(realMaxVelocity, Math.abs(moduleVelocity.velocity)); 276 } 277 var velocities = new SwerveModuleVelocity[moduleVelocities.length]; 278 if (realMaxVelocity > attainableMaxVelocity) { 279 for (int i = 0; i < velocities.length; i++) { 280 velocities[i] = 281 new SwerveModuleVelocity( 282 moduleVelocities[i].velocity / realMaxVelocity * attainableMaxVelocity, 283 moduleVelocities[i].angle); 284 } 285 } else { 286 // Copy in the event someone wants to mutate the desaturated velocities but also wants the 287 // original velocities 288 for (int i = 0; i < velocities.length; i++) { 289 velocities[i] = 290 new SwerveModuleVelocity(moduleVelocities[i].velocity, moduleVelocities[i].angle); 291 } 292 } 293 return velocities; 294 } 295 296 /** 297 * Renormalizes the wheel velocities if any individual velocity is above the specified maximum. 298 * 299 * <p>Sometimes, after inverse kinematics, the requested velocity from one or more modules may be 300 * above the max attainable velocity for the driving motor on that module. To fix this issue, one 301 * can reduce all the wheel velocities to make sure that all requested module velocities are 302 * at-or-below the absolute threshold, while maintaining the ratio of velocities between modules. 303 * 304 * <p>Scaling down the module velocities rotates the direction of net motion in the opposite 305 * direction of rotational velocity, which makes discretizing the chassis velocities inaccurate 306 * because the discretization did not account for this translational skew. 307 * 308 * @param moduleVelocities The array of module velocities. 309 * @param attainableMaxVelocity The absolute max velocity in meters per second that a module can 310 * reach. 311 * @return The array of desaturated module velocities. 312 */ 313 @NoDiscard 314 public static SwerveModuleVelocity[] desaturateWheelVelocities( 315 SwerveModuleVelocity[] moduleVelocities, LinearVelocity attainableMaxVelocity) { 316 return desaturateWheelVelocities(moduleVelocities, attainableMaxVelocity.in(MetersPerSecond)); 317 } 318 319 /** 320 * Renormalizes the wheel velocities if any individual velocity is above the specified maximum, as 321 * well as getting rid of joystick saturation at edges of joystick. 322 * 323 * <p>Sometimes, after inverse kinematics, the requested velocity from one or more modules may be 324 * above the max attainable velocity for the driving motor on that module. To fix this issue, one 325 * can reduce all the wheel velocities to make sure that all requested module velocities are 326 * at-or-below the absolute threshold, while maintaining the ratio of velocities between modules. 327 * 328 * <p>Scaling down the module velocities rotates the direction of net motion in the opposite 329 * direction of rotational velocity, which makes discretizing the chassis velocities inaccurate 330 * because the discretization did not account for this translational skew. 331 * 332 * @param moduleVelocities The array of module velocities 333 * @param desiredChassisVelocity The desired velocity of the robot 334 * @param attainableMaxModuleVelocity The absolute max velocity in meters per second that a module 335 * can reach 336 * @param attainableMaxTranslationalVelocity The absolute max velocity in meters per second that 337 * your robot can reach while translating 338 * @param attainableMaxRotationalVelocity The absolute max velocity in radians per second the 339 * robot can reach while rotating 340 * @return The array of desaturated module velocities 341 */ 342 @NoDiscard 343 public static SwerveModuleVelocity[] desaturateWheelVelocities( 344 SwerveModuleVelocity[] moduleVelocities, 345 ChassisVelocities desiredChassisVelocity, 346 double attainableMaxModuleVelocity, 347 double attainableMaxTranslationalVelocity, 348 double attainableMaxRotationalVelocity) { 349 double realMaxVelocity = 0; 350 for (SwerveModuleVelocity moduleVelocity : moduleVelocities) { 351 realMaxVelocity = Math.max(realMaxVelocity, Math.abs(moduleVelocity.velocity)); 352 } 353 354 var velocities = new SwerveModuleVelocity[moduleVelocities.length]; 355 if (attainableMaxTranslationalVelocity == 0 356 || attainableMaxRotationalVelocity == 0 357 || realMaxVelocity == 0) { 358 // Copy in the event someone wants to mutate the desaturated velocities but also wants the 359 // original velocities 360 for (int i = 0; i < velocities.length; i++) { 361 velocities[i] = 362 new SwerveModuleVelocity(moduleVelocities[i].velocity, moduleVelocities[i].angle); 363 } 364 return velocities; 365 } 366 double translationalK = 367 Math.hypot(desiredChassisVelocity.vx, desiredChassisVelocity.vy) 368 / attainableMaxTranslationalVelocity; 369 double rotationalK = Math.abs(desiredChassisVelocity.omega) / attainableMaxRotationalVelocity; 370 double k = Math.max(translationalK, rotationalK); 371 double scale = Math.min(k * attainableMaxModuleVelocity / realMaxVelocity, 1); 372 for (int i = 0; i < velocities.length; i++) { 373 velocities[i] = 374 new SwerveModuleVelocity(moduleVelocities[i].velocity * scale, moduleVelocities[i].angle); 375 } 376 return velocities; 377 } 378 379 /** 380 * Renormalizes the wheel velocities if any individual velocity is above the specified maximum, as 381 * well as getting rid of joystick saturation at edges of joystick. 382 * 383 * <p>Sometimes, after inverse kinematics, the requested velocity from one or more modules may be 384 * above the max attainable velocity for the driving motor on that module. To fix this issue, one 385 * can reduce all the wheel velocities to make sure that all requested module velocities are 386 * at-or-below the absolute threshold, while maintaining the ratio of velocities between modules. 387 * 388 * <p>Scaling down the module velocities rotates the direction of net motion in the opposite 389 * direction of rotational velocity, which makes discretizing the chassis velocities inaccurate 390 * because the discretization did not account for this translational skew. 391 * 392 * @param moduleVelocities The array of module velocities 393 * @param desiredChassisVelocity The desired velocity of the robot 394 * @param attainableMaxModuleVelocity The absolute max velocity that a module can reach 395 * @param attainableMaxTranslationalVelocity The absolute max velocity that your robot can reach 396 * while translating 397 * @param attainableMaxRotationalVelocity The absolute max velocity the robot can reach while 398 * rotating 399 * @return The array of desaturated module velocities 400 */ 401 @NoDiscard 402 public static SwerveModuleVelocity[] desaturateWheelVelocities( 403 SwerveModuleVelocity[] moduleVelocities, 404 ChassisVelocities desiredChassisVelocity, 405 LinearVelocity attainableMaxModuleVelocity, 406 LinearVelocity attainableMaxTranslationalVelocity, 407 AngularVelocity attainableMaxRotationalVelocity) { 408 return desaturateWheelVelocities( 409 moduleVelocities, 410 desiredChassisVelocity, 411 attainableMaxModuleVelocity.in(MetersPerSecond), 412 attainableMaxTranslationalVelocity.in(MetersPerSecond), 413 attainableMaxRotationalVelocity.in(RadiansPerSecond)); 414 } 415 416 @Override 417 public SwerveModulePosition[] copy(SwerveModulePosition[] positions) { 418 var newPositions = new SwerveModulePosition[positions.length]; 419 for (int i = 0; i < positions.length; ++i) { 420 newPositions[i] = positions[i].copy(); 421 } 422 return newPositions; 423 } 424 425 @Override 426 public void copyInto(SwerveModulePosition[] positions, SwerveModulePosition[] output) { 427 if (positions.length != output.length) { 428 throw new IllegalArgumentException("Inconsistent number of modules!"); 429 } 430 for (int i = 0; i < positions.length; ++i) { 431 output[i].distance = positions[i].distance; 432 output[i].angle = positions[i].angle; 433 } 434 } 435 436 @Override 437 public SwerveModulePosition[] interpolate( 438 SwerveModulePosition[] startValue, SwerveModulePosition[] endValue, double t) { 439 if (endValue.length != startValue.length) { 440 throw new IllegalArgumentException("Inconsistent number of modules!"); 441 } 442 var newPositions = new SwerveModulePosition[startValue.length]; 443 for (int i = 0; i < startValue.length; ++i) { 444 newPositions[i] = startValue[i].interpolate(endValue[i], t); 445 } 446 return newPositions; 447 } 448 449 /** 450 * Gets the locations of the modules relative to the center of rotation. 451 * 452 * @return The locations of the modules relative to the center of rotation. This array should not 453 * be modified. 454 */ 455 @SuppressWarnings("PMD.MethodReturnsInternalArray") 456 public Translation2d[] getModules() { 457 return m_modules; 458 } 459 460 /** SwerveDriveKinematics protobuf for serialization. */ 461 public static final SwerveDriveKinematicsProto proto = new SwerveDriveKinematicsProto(); 462 463 /** 464 * Creates an implementation of the {@link Struct} interface for swerve drive kinematics objects. 465 * 466 * @param numModules The number of modules of the kinematics objects this serializer processes. 467 * @return The struct implementation. 468 */ 469 public static final SwerveDriveKinematicsStruct getStruct(int numModules) { 470 return new SwerveDriveKinematicsStruct(numModules); 471 } 472 473 /** 474 * Performs inverse kinematics to return the module accelerations from a desired chassis 475 * acceleration. This method is often used for dynamics calculations -- converting desired robot 476 * accelerations into individual module accelerations. 477 * 478 * <p>This function also supports variable centers of rotation. During normal operations, the 479 * center of rotation is usually the same as the physical center of the robot; therefore, the 480 * argument is defaulted to that use case. However, if you wish to change the center of rotation 481 * for evasive maneuvers, vision alignment, or for any other use case, you can do so. 482 * 483 * @param chassisAccelerations The desired chassis accelerations. 484 * @param angularVelocity The desired robot angular velocity. 485 * @param centerOfRotation The center of rotation. For example, if you set the center of rotation 486 * at one corner of the robot and provide a chassis acceleration that only has a dtheta 487 * component, the robot will rotate around that corner. 488 * @return An array containing the module accelerations. 489 */ 490 public SwerveModuleAcceleration[] toSwerveModuleAccelerations( 491 ChassisAccelerations chassisAccelerations, 492 double angularVelocity, 493 Translation2d centerOfRotation) { 494 // Derivation for second-order kinematics from "Swerve Drive Second Order Kinematics" 495 // by FRC Team 449 - The Blair Robot Project, Rafi Pedersen 496 // https://www.chiefdelphi.com/uploads/short-url/qzj4k2LyBs7rLxAem0YajNIlStH.pdf 497 498 var moduleAccelerations = new SwerveModuleAcceleration[m_numModules]; 499 500 if (chassisAccelerations.ax == 0.0 501 && chassisAccelerations.ay == 0.0 502 && chassisAccelerations.alpha == 0.0) { 503 for (int i = 0; i < m_numModules; i++) { 504 moduleAccelerations[i] = new SwerveModuleAcceleration(0.0, Rotation2d.kZero); 505 } 506 return moduleAccelerations; 507 } 508 509 if (!centerOfRotation.equals(m_prevCoR)) { 510 setInverseKinematics(centerOfRotation); 511 } 512 513 var chassisAccelerationsVector = new SimpleMatrix(4, 1); 514 chassisAccelerationsVector.setColumn( 515 0, 516 0, 517 chassisAccelerations.ax, 518 chassisAccelerations.ay, 519 angularVelocity * angularVelocity, 520 chassisAccelerations.alpha); 521 522 var moduleAccelerationsMatrix = m_secondOrderInverseKinematics.mult(chassisAccelerationsVector); 523 524 for (int i = 0; i < m_numModules; i++) { 525 double x = moduleAccelerationsMatrix.get(i * 2, 0); 526 double y = moduleAccelerationsMatrix.get(i * 2 + 1, 0); 527 528 // For swerve modules, we need to compute both linear acceleration and angular acceleration 529 // The linear acceleration is the magnitude of the acceleration vector 530 double linearAcceleration = Math.hypot(x, y); 531 532 if (linearAcceleration <= 1e-6) { 533 moduleAccelerations[i] = new SwerveModuleAcceleration(linearAcceleration, Rotation2d.kZero); 534 } else { 535 moduleAccelerations[i] = 536 new SwerveModuleAcceleration(linearAcceleration, new Rotation2d(x, y)); 537 } 538 } 539 540 return moduleAccelerations; 541 } 542 543 /** 544 * Performs inverse kinematics. See {@link #toSwerveModuleAccelerations(ChassisAccelerations, 545 * double, Translation2d)} toSwerveModuleAccelerations for more information. 546 * 547 * @param chassisAccelerations The desired chassis accelerations. 548 * @param angularVelocity The desired robot angular velocity. 549 * @return An array containing the module accelerations. 550 */ 551 public SwerveModuleAcceleration[] toSwerveModuleAccelerations( 552 ChassisAccelerations chassisAccelerations, double angularVelocity) { 553 return toSwerveModuleAccelerations(chassisAccelerations, angularVelocity, Translation2d.kZero); 554 } 555 556 @Override 557 public SwerveModuleAcceleration[] toWheelAccelerations( 558 ChassisAccelerations chassisAccelerations) { 559 return toSwerveModuleAccelerations(chassisAccelerations, 0.0); 560 } 561 562 /** 563 * Performs forward kinematics to return the resulting chassis accelerations from the given module 564 * accelerations. This method is often used for dynamics calculations -- determining the robot's 565 * acceleration on the field using data from the real-world acceleration of each module on the 566 * robot. 567 * 568 * @param moduleAccelerations The accelerations of the modules as measured from respective 569 * encoders and gyros. The order of the swerve module accelerations should be same as passed 570 * into the constructor of this class. 571 * @return The resulting chassis accelerations. 572 */ 573 @Override 574 public ChassisAccelerations toChassisAccelerations( 575 SwerveModuleAcceleration... moduleAccelerations) { 576 // Derivation for second-order kinematics from "Swerve Drive Second Order Kinematics" 577 // by FRC Team 449 - The Blair Robot Project, Rafi Pedersen 578 // https://www.chiefdelphi.com/uploads/short-url/qzj4k2LyBs7rLxAem0YajNIlStH.pdf 579 580 if (moduleAccelerations.length != m_numModules) { 581 throw new IllegalArgumentException( 582 "Number of modules is not consistent with number of module locations provided in " 583 + "constructor"); 584 } 585 var moduleAccelerationsMatrix = new SimpleMatrix(m_numModules * 2, 1); 586 587 for (int i = 0; i < m_numModules; i++) { 588 var module = moduleAccelerations[i]; 589 590 moduleAccelerationsMatrix.set(i * 2 + 0, 0, module.acceleration * module.angle.getCos()); 591 moduleAccelerationsMatrix.set(i * 2 + 1, 0, module.acceleration * module.angle.getSin()); 592 } 593 594 var chassisAccelerationsVector = m_secondOrderForwardKinematics.mult(moduleAccelerationsMatrix); 595 596 // the second order kinematics equation for swerve drive yields a state vector [aₓ, a_y, ω², α] 597 return new ChassisAccelerations( 598 chassisAccelerationsVector.get(0, 0), 599 chassisAccelerationsVector.get(1, 0), 600 chassisAccelerationsVector.get(3, 0)); 601 } 602 603 /** 604 * Sets both inverse kinematics matrices based on the new center of rotation. This does not check 605 * if the new center of rotation is different from the previous one, so a check should be included 606 * before the call to this function. 607 * 608 * @param centerOfRotation new center of rotation 609 */ 610 private void setInverseKinematics(Translation2d centerOfRotation) { 611 for (int i = 0; i < m_numModules; i++) { 612 var rx = m_modules[i].getX() - centerOfRotation.getX(); 613 var ry = m_modules[i].getY() - centerOfRotation.getY(); 614 615 m_firstOrderInverseKinematics.setRow(i * 2 + 0, 0, /* Start Data */ 1, 0, -ry); 616 m_firstOrderInverseKinematics.setRow(i * 2 + 1, 0, /* Start Data */ 0, 1, rx); 617 618 m_secondOrderInverseKinematics.setRow(i * 2 + 0, 0, /* Start Data */ 1, 0, -rx, -ry); 619 m_secondOrderInverseKinematics.setRow(i * 2 + 1, 0, /* Start Data */ 0, 1, -ry, +rx); 620 } 621 m_prevCoR = centerOfRotation; 622 } 623}