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; 009import static edu.wpi.first.units.Units.Seconds; 010 011import edu.wpi.first.math.geometry.Pose2d; 012import edu.wpi.first.math.geometry.Rotation2d; 013import edu.wpi.first.math.geometry.Translation2d; 014import edu.wpi.first.math.kinematics.proto.ChassisSpeedsProto; 015import edu.wpi.first.math.kinematics.struct.ChassisSpeedsStruct; 016import edu.wpi.first.units.Angle; 017import edu.wpi.first.units.Distance; 018import edu.wpi.first.units.Measure; 019import edu.wpi.first.units.Time; 020import edu.wpi.first.units.Velocity; 021import edu.wpi.first.util.protobuf.ProtobufSerializable; 022import edu.wpi.first.util.struct.StructSerializable; 023 024/** 025 * Represents the speed of a robot chassis. Although this class contains similar members compared to 026 * a Twist2d, they do NOT represent the same thing. Whereas a Twist2d represents a change in pose 027 * w.r.t to the robot frame of reference, a ChassisSpeeds object represents a robot's velocity. 028 * 029 * <p>A strictly non-holonomic drivetrain, such as a differential drive, should never have a dy 030 * component because it can never move sideways. Holonomic drivetrains such as swerve and mecanum 031 * will often have all three components. 032 */ 033public class ChassisSpeeds implements ProtobufSerializable, StructSerializable { 034 /** Velocity along the x-axis. (Fwd is +) */ 035 public double vxMetersPerSecond; 036 037 /** Velocity along the y-axis. (Left is +) */ 038 public double vyMetersPerSecond; 039 040 /** Represents the angular velocity of the robot frame. (CCW is +) */ 041 public double omegaRadiansPerSecond; 042 043 /** ChassisSpeeds protobuf for serialization. */ 044 public static final ChassisSpeedsProto proto = new ChassisSpeedsProto(); 045 046 /** ChassisSpeeds struct for serialization. */ 047 public static final ChassisSpeedsStruct struct = new ChassisSpeedsStruct(); 048 049 /** Constructs a ChassisSpeeds with zeros for dx, dy, and theta. */ 050 public ChassisSpeeds() {} 051 052 /** 053 * Constructs a ChassisSpeeds object. 054 * 055 * @param vxMetersPerSecond Forward velocity. 056 * @param vyMetersPerSecond Sideways velocity. 057 * @param omegaRadiansPerSecond Angular velocity. 058 */ 059 public ChassisSpeeds( 060 double vxMetersPerSecond, double vyMetersPerSecond, double omegaRadiansPerSecond) { 061 this.vxMetersPerSecond = vxMetersPerSecond; 062 this.vyMetersPerSecond = vyMetersPerSecond; 063 this.omegaRadiansPerSecond = omegaRadiansPerSecond; 064 } 065 066 /** 067 * Constructs a ChassisSpeeds object. 068 * 069 * @param vx Forward velocity. 070 * @param vy Sideways velocity. 071 * @param omega Angular velocity. 072 */ 073 public ChassisSpeeds( 074 Measure<Velocity<Distance>> vx, 075 Measure<Velocity<Distance>> vy, 076 Measure<Velocity<Angle>> omega) { 077 this(vx.in(MetersPerSecond), vy.in(MetersPerSecond), omega.in(RadiansPerSecond)); 078 } 079 080 /** 081 * Discretizes a continuous-time chassis speed. 082 * 083 * <p>This function converts a continuous-time chassis speed into a discrete-time one such that 084 * when the discrete-time chassis speed is applied for one timestep, the robot moves as if the 085 * velocity components are independent (i.e., the robot moves v_x * dt along the x-axis, v_y * dt 086 * along the y-axis, and omega * dt around the z-axis). 087 * 088 * <p>This is useful for compensating for translational skew when translating and rotating a 089 * swerve drivetrain. 090 * 091 * @param vxMetersPerSecond Forward velocity. 092 * @param vyMetersPerSecond Sideways velocity. 093 * @param omegaRadiansPerSecond Angular velocity. 094 * @param dtSeconds The duration of the timestep the speeds should be applied for. 095 * @return Discretized ChassisSpeeds. 096 */ 097 public static ChassisSpeeds discretize( 098 double vxMetersPerSecond, 099 double vyMetersPerSecond, 100 double omegaRadiansPerSecond, 101 double dtSeconds) { 102 // Construct the desired pose after a timestep, relative to the current pose. The desired pose 103 // has decoupled translation and rotation. 104 var desiredDeltaPose = 105 new Pose2d( 106 vxMetersPerSecond * dtSeconds, 107 vyMetersPerSecond * dtSeconds, 108 new Rotation2d(omegaRadiansPerSecond * dtSeconds)); 109 110 // Find the chassis translation/rotation deltas in the robot frame that move the robot from its 111 // current pose to the desired pose 112 var twist = Pose2d.kZero.log(desiredDeltaPose); 113 114 // Turn the chassis translation/rotation deltas into average velocities 115 return new ChassisSpeeds(twist.dx / dtSeconds, twist.dy / dtSeconds, twist.dtheta / dtSeconds); 116 } 117 118 /** 119 * Discretizes a continuous-time chassis speed. 120 * 121 * <p>This function converts a continuous-time chassis speed into a discrete-time one such that 122 * when the discrete-time chassis speed is applied for one timestep, the robot moves as if the 123 * velocity components are independent (i.e., the robot moves v_x * dt along the x-axis, v_y * dt 124 * along the y-axis, and omega * dt around the z-axis). 125 * 126 * <p>This is useful for compensating for translational skew when translating and rotating a 127 * swerve drivetrain. 128 * 129 * @param vx Forward velocity. 130 * @param vy Sideways velocity. 131 * @param omega Angular velocity. 132 * @param dt The duration of the timestep the speeds should be applied for. 133 * @return Discretized ChassisSpeeds. 134 */ 135 public static ChassisSpeeds discretize( 136 Measure<Velocity<Distance>> vx, 137 Measure<Velocity<Distance>> vy, 138 Measure<Velocity<Angle>> omega, 139 Measure<Time> dt) { 140 return discretize( 141 vx.in(MetersPerSecond), vy.in(MetersPerSecond), omega.in(RadiansPerSecond), dt.in(Seconds)); 142 } 143 144 /** 145 * Discretizes a continuous-time chassis speed. 146 * 147 * <p>This function converts a continous-time chassis speed into a discrete-time one such that 148 * when the discrete-time chassis speed is applied for one timestep, the robot moves as if the 149 * velocity components are independent (i.e., the robot moves v_x * dt along the x-axis, v_y * dt 150 * along the y-axis, and omega * dt around the z-axis). 151 * 152 * <p>This is useful for compensating for translational skew when translating and rotating a 153 * swerve drivetrain. 154 * 155 * @param continuousSpeeds The continuous speeds. 156 * @param dtSeconds The duration of the timestep the speeds should be applied for. 157 * @return Discretized ChassisSpeeds. 158 */ 159 public static ChassisSpeeds discretize(ChassisSpeeds continuousSpeeds, double dtSeconds) { 160 return discretize( 161 continuousSpeeds.vxMetersPerSecond, 162 continuousSpeeds.vyMetersPerSecond, 163 continuousSpeeds.omegaRadiansPerSecond, 164 dtSeconds); 165 } 166 167 /** 168 * Converts a user provided field-relative set of speeds into a robot-relative ChassisSpeeds 169 * object. 170 * 171 * @param vxMetersPerSecond The component of speed in the x direction relative to the field. 172 * Positive x is away from your alliance wall. 173 * @param vyMetersPerSecond The component of speed in the y direction relative to the field. 174 * Positive y is to your left when standing behind the alliance wall. 175 * @param omegaRadiansPerSecond The angular rate of the robot. 176 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 177 * considered to be zero when it is facing directly away from your alliance station wall. 178 * Remember that this should be CCW positive. 179 * @return ChassisSpeeds object representing the speeds in the robot's frame of reference. 180 */ 181 public static ChassisSpeeds fromFieldRelativeSpeeds( 182 double vxMetersPerSecond, 183 double vyMetersPerSecond, 184 double omegaRadiansPerSecond, 185 Rotation2d robotAngle) { 186 // CW rotation into chassis frame 187 var rotated = 188 new Translation2d(vxMetersPerSecond, vyMetersPerSecond).rotateBy(robotAngle.unaryMinus()); 189 return new ChassisSpeeds(rotated.getX(), rotated.getY(), omegaRadiansPerSecond); 190 } 191 192 /** 193 * Converts a user provided field-relative set of speeds into a robot-relative ChassisSpeeds 194 * object. 195 * 196 * @param vx The component of speed in the x direction relative to the field. Positive x is away 197 * from your alliance wall. 198 * @param vy The component of speed in the y direction relative to the field. Positive y is to 199 * your left when standing behind the alliance wall. 200 * @param omega The angular rate of the robot. 201 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 202 * considered to be zero when it is facing directly away from your alliance station wall. 203 * Remember that this should be CCW positive. 204 * @return ChassisSpeeds object representing the speeds in the robot's frame of reference. 205 */ 206 public static ChassisSpeeds fromFieldRelativeSpeeds( 207 Measure<Velocity<Distance>> vx, 208 Measure<Velocity<Distance>> vy, 209 Measure<Velocity<Angle>> omega, 210 Rotation2d robotAngle) { 211 return fromFieldRelativeSpeeds( 212 vx.in(MetersPerSecond), vy.in(MetersPerSecond), omega.in(RadiansPerSecond), robotAngle); 213 } 214 215 /** 216 * Converts a user provided field-relative ChassisSpeeds object into a robot-relative 217 * ChassisSpeeds object. 218 * 219 * @param fieldRelativeSpeeds The ChassisSpeeds object representing the speeds in the field frame 220 * of reference. Positive x is away from your alliance wall. Positive y is to your left when 221 * standing behind the alliance wall. 222 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 223 * considered to be zero when it is facing directly away from your alliance station wall. 224 * Remember that this should be CCW positive. 225 * @return ChassisSpeeds object representing the speeds in the robot's frame of reference. 226 */ 227 public static ChassisSpeeds fromFieldRelativeSpeeds( 228 ChassisSpeeds fieldRelativeSpeeds, Rotation2d robotAngle) { 229 return fromFieldRelativeSpeeds( 230 fieldRelativeSpeeds.vxMetersPerSecond, 231 fieldRelativeSpeeds.vyMetersPerSecond, 232 fieldRelativeSpeeds.omegaRadiansPerSecond, 233 robotAngle); 234 } 235 236 /** 237 * Converts a user provided robot-relative set of speeds into a field-relative ChassisSpeeds 238 * object. 239 * 240 * @param vxMetersPerSecond The component of speed in the x direction relative to the robot. 241 * Positive x is towards the robot's front. 242 * @param vyMetersPerSecond The component of speed in the y direction relative to the robot. 243 * Positive y is towards the robot's left. 244 * @param omegaRadiansPerSecond The angular rate of the robot. 245 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 246 * considered to be zero when it is facing directly away from your alliance station wall. 247 * Remember that this should be CCW positive. 248 * @return ChassisSpeeds object representing the speeds in the field's frame of reference. 249 */ 250 public static ChassisSpeeds fromRobotRelativeSpeeds( 251 double vxMetersPerSecond, 252 double vyMetersPerSecond, 253 double omegaRadiansPerSecond, 254 Rotation2d robotAngle) { 255 // CCW rotation out of chassis frame 256 var rotated = new Translation2d(vxMetersPerSecond, vyMetersPerSecond).rotateBy(robotAngle); 257 return new ChassisSpeeds(rotated.getX(), rotated.getY(), omegaRadiansPerSecond); 258 } 259 260 /** 261 * Converts a user provided robot-relative set of speeds into a field-relative ChassisSpeeds 262 * object. 263 * 264 * @param vx The component of speed in the x direction relative to the robot. Positive x is 265 * towards the robot's front. 266 * @param vy The component of speed in the y direction relative to the robot. Positive y is 267 * towards the robot's left. 268 * @param omega The angular rate of the robot. 269 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 270 * considered to be zero when it is facing directly away from your alliance station wall. 271 * Remember that this should be CCW positive. 272 * @return ChassisSpeeds object representing the speeds in the field's frame of reference. 273 */ 274 public static ChassisSpeeds fromRobotRelativeSpeeds( 275 Measure<Velocity<Distance>> vx, 276 Measure<Velocity<Distance>> vy, 277 Measure<Velocity<Angle>> omega, 278 Rotation2d robotAngle) { 279 return fromRobotRelativeSpeeds( 280 vx.in(MetersPerSecond), vy.in(MetersPerSecond), omega.in(RadiansPerSecond), robotAngle); 281 } 282 283 /** 284 * Converts a user provided robot-relative ChassisSpeeds object into a field-relative 285 * ChassisSpeeds object. 286 * 287 * @param robotRelativeSpeeds The ChassisSpeeds object representing the speeds in the robot frame 288 * of reference. Positive x is towards the robot's front. Positive y is towards the robot's 289 * left. 290 * @param robotAngle The angle of the robot as measured by a gyroscope. The robot's angle is 291 * considered to be zero when it is facing directly away from your alliance station wall. 292 * Remember that this should be CCW positive. 293 * @return ChassisSpeeds object representing the speeds in the field's frame of reference. 294 */ 295 public static ChassisSpeeds fromRobotRelativeSpeeds( 296 ChassisSpeeds robotRelativeSpeeds, Rotation2d robotAngle) { 297 return fromRobotRelativeSpeeds( 298 robotRelativeSpeeds.vxMetersPerSecond, 299 robotRelativeSpeeds.vyMetersPerSecond, 300 robotRelativeSpeeds.omegaRadiansPerSecond, 301 robotAngle); 302 } 303 304 /** 305 * Adds two ChassisSpeeds and returns the sum. 306 * 307 * <p>For example, ChassisSpeeds{1.0, 0.5, 0.75} + ChassisSpeeds{2.0, 1.5, 0.25} = 308 * ChassisSpeeds{3.0, 2.0, 1.0} 309 * 310 * @param other The ChassisSpeeds to add. 311 * @return The sum of the ChassisSpeeds. 312 */ 313 public ChassisSpeeds plus(ChassisSpeeds other) { 314 return new ChassisSpeeds( 315 vxMetersPerSecond + other.vxMetersPerSecond, 316 vyMetersPerSecond + other.vyMetersPerSecond, 317 omegaRadiansPerSecond + other.omegaRadiansPerSecond); 318 } 319 320 /** 321 * Subtracts the other ChassisSpeeds from the current ChassisSpeeds and returns the difference. 322 * 323 * <p>For example, ChassisSpeeds{5.0, 4.0, 2.0} - ChassisSpeeds{1.0, 2.0, 1.0} = 324 * ChassisSpeeds{4.0, 2.0, 1.0} 325 * 326 * @param other The ChassisSpeeds to subtract. 327 * @return The difference between the two ChassisSpeeds. 328 */ 329 public ChassisSpeeds minus(ChassisSpeeds other) { 330 return new ChassisSpeeds( 331 vxMetersPerSecond - other.vxMetersPerSecond, 332 vyMetersPerSecond - other.vyMetersPerSecond, 333 omegaRadiansPerSecond - other.omegaRadiansPerSecond); 334 } 335 336 /** 337 * Returns the inverse of the current ChassisSpeeds. This is equivalent to negating all components 338 * of the ChassisSpeeds. 339 * 340 * @return The inverse of the current ChassisSpeeds. 341 */ 342 public ChassisSpeeds unaryMinus() { 343 return new ChassisSpeeds(-vxMetersPerSecond, -vyMetersPerSecond, -omegaRadiansPerSecond); 344 } 345 346 /** 347 * Multiplies the ChassisSpeeds by a scalar and returns the new ChassisSpeeds. 348 * 349 * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} * 2 = ChassisSpeeds{4.0, 5.0, 1.0} 350 * 351 * @param scalar The scalar to multiply by. 352 * @return The scaled ChassisSpeeds. 353 */ 354 public ChassisSpeeds times(double scalar) { 355 return new ChassisSpeeds( 356 vxMetersPerSecond * scalar, vyMetersPerSecond * scalar, omegaRadiansPerSecond * scalar); 357 } 358 359 /** 360 * Divides the ChassisSpeeds by a scalar and returns the new ChassisSpeeds. 361 * 362 * <p>For example, ChassisSpeeds{2.0, 2.5, 1.0} / 2 = ChassisSpeeds{1.0, 1.25, 0.5} 363 * 364 * @param scalar The scalar to divide by. 365 * @return The scaled ChassisSpeeds. 366 */ 367 public ChassisSpeeds div(double scalar) { 368 return new ChassisSpeeds( 369 vxMetersPerSecond / scalar, vyMetersPerSecond / scalar, omegaRadiansPerSecond / scalar); 370 } 371 372 @Override 373 public String toString() { 374 return String.format( 375 "ChassisSpeeds(Vx: %.2f m/s, Vy: %.2f m/s, Omega: %.2f rad/s)", 376 vxMetersPerSecond, vyMetersPerSecond, omegaRadiansPerSecond); 377 } 378}