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; 008 009import java.util.Objects; 010import org.wpilib.math.geometry.Rotation2d; 011import org.wpilib.math.interpolation.Interpolatable; 012import org.wpilib.math.kinematics.proto.SwerveModuleVelocityProto; 013import org.wpilib.math.kinematics.struct.SwerveModuleVelocityStruct; 014import org.wpilib.units.measure.LinearVelocity; 015import org.wpilib.util.protobuf.ProtobufSerializable; 016import org.wpilib.util.struct.StructSerializable; 017 018/** Represents the velocity of one swerve module. */ 019public class SwerveModuleVelocity 020 implements Interpolatable<SwerveModuleVelocity>, 021 Comparable<SwerveModuleVelocity>, 022 ProtobufSerializable, 023 StructSerializable { 024 /** Velocity of the wheel of the module in meters per second. */ 025 public double velocity; 026 027 /** Angle of the module. */ 028 public Rotation2d angle = Rotation2d.kZero; 029 030 /** SwerveModuleVelocity protobuf for serialization. */ 031 public static final SwerveModuleVelocityProto proto = new SwerveModuleVelocityProto(); 032 033 /** SwerveModuleVelocity struct for serialization. */ 034 public static final SwerveModuleVelocityStruct struct = new SwerveModuleVelocityStruct(); 035 036 /** Constructs a SwerveModuleVelocity with zeros for velocity and angle. */ 037 public SwerveModuleVelocity() {} 038 039 /** 040 * Constructs a SwerveModuleVelocity. 041 * 042 * @param velocity The velocity of the wheel of the module in meters per second. 043 * @param angle The angle of the module. 044 */ 045 public SwerveModuleVelocity(double velocity, Rotation2d angle) { 046 this.velocity = velocity; 047 this.angle = angle; 048 } 049 050 /** 051 * Constructs a SwerveModuleVelocity. 052 * 053 * @param velocity The velocity of the wheel of the module. 054 * @param angle The angle of the module. 055 */ 056 public SwerveModuleVelocity(LinearVelocity velocity, Rotation2d angle) { 057 this(velocity.in(MetersPerSecond), angle); 058 } 059 060 @Override 061 public boolean equals(Object obj) { 062 return obj instanceof SwerveModuleVelocity other 063 && Math.abs(other.velocity - velocity) < 1E-9 064 && angle.equals(other.angle); 065 } 066 067 @Override 068 public int hashCode() { 069 return Objects.hash(velocity, angle); 070 } 071 072 /** 073 * Compares two swerve module velocities. One swerve module is "greater" than the other if its 074 * velocity is higher than the other. 075 * 076 * @param other The other swerve module. 077 * @return 1 if this is greater, 0 if both are equal, -1 if other is greater. 078 */ 079 @Override 080 public int compareTo(SwerveModuleVelocity other) { 081 return Double.compare(this.velocity, other.velocity); 082 } 083 084 @Override 085 public String toString() { 086 return String.format("SwerveModuleVelocity(Velocity: %.2f m/s, Angle: %s)", velocity, angle); 087 } 088 089 /** 090 * Minimize the change in heading this swerve module velocity would require by potentially 091 * reversing the direction the wheel spins. If this is used with the PIDController class's 092 * continuous input functionality, the furthest a wheel will ever rotate is 90 degrees. 093 * 094 * @param currentAngle The current module angle. 095 */ 096 public void optimize(Rotation2d currentAngle) { 097 var delta = angle.minus(currentAngle); 098 if (Math.abs(delta.getDegrees()) > 90.0) { 099 velocity *= -1; 100 angle = angle.rotateBy(Rotation2d.kPi); 101 } 102 } 103 104 /** 105 * Minimize the change in heading the desired swerve module velocity would require by potentially 106 * reversing the direction the wheel spins. If this is used with the PIDController class's 107 * continuous input functionality, the furthest a wheel will ever rotate is 90 degrees. 108 * 109 * @param desiredVelocity The desired velocity. 110 * @param currentAngle The current module angle. 111 * @return Optimized swerve module velocity. 112 * @deprecated Use the instance method instead. 113 */ 114 @Deprecated 115 public static SwerveModuleVelocity optimize( 116 SwerveModuleVelocity desiredVelocity, Rotation2d currentAngle) { 117 var delta = desiredVelocity.angle.minus(currentAngle); 118 if (Math.abs(delta.getDegrees()) > 90.0) { 119 return new SwerveModuleVelocity( 120 -desiredVelocity.velocity, desiredVelocity.angle.rotateBy(Rotation2d.kPi)); 121 } else { 122 return new SwerveModuleVelocity(desiredVelocity.velocity, desiredVelocity.angle); 123 } 124 } 125 126 /** 127 * Returns the linear interpolation of this SwerveModuleVelocity and another. The angle is 128 * interpolated using the shortest path between the two angles. 129 * 130 * @param endValue The end value for the interpolation. 131 * @param t How far between the two values to interpolate. This is clamped to [0, 1]. 132 * @return The interpolated value. 133 */ 134 @Override 135 public SwerveModuleVelocity interpolate(SwerveModuleVelocity endValue, double t) { 136 // Clamp t to [0, 1] 137 t = Math.max(0.0, Math.min(1.0, t)); 138 139 // Interpolate velocity linearly 140 double interpolatedVelocity = velocity + t * (endValue.velocity - velocity); 141 142 // Interpolate angle using the shortest path 143 Rotation2d interpolatedAngle = angle.interpolate(endValue.angle, t); 144 145 return new SwerveModuleVelocity(interpolatedVelocity, interpolatedAngle); 146 } 147 148 /** 149 * Scales velocity by cosine of angle error. This scales down movement perpendicular to the 150 * desired direction of travel that can occur when modules change directions. This results in 151 * smoother driving. 152 * 153 * @param currentAngle The current module angle. 154 */ 155 public void cosineScale(Rotation2d currentAngle) { 156 velocity *= angle.minus(currentAngle).getCos(); 157 } 158}