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}