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.Meters;
008
009import org.wpilib.math.geometry.Twist2d;
010import org.wpilib.math.kinematics.proto.DifferentialDriveKinematicsProto;
011import org.wpilib.math.kinematics.struct.DifferentialDriveKinematicsStruct;
012import org.wpilib.math.util.MathSharedStore;
013import org.wpilib.units.measure.Distance;
014import org.wpilib.util.protobuf.ProtobufSerializable;
015import org.wpilib.util.struct.StructSerializable;
016
017/**
018 * Helper class that converts a chassis velocity (dx and dtheta components) to left and right wheel
019 * velocities for a differential drive.
020 *
021 * <p>Inverse kinematics converts a desired chassis velocity into left and right velocity components
022 * whereas forward kinematics converts left and right component velocities into a linear and angular
023 * chassis velocity.
024 */
025public class DifferentialDriveKinematics
026    implements Kinematics<
027            DifferentialDriveWheelPositions,
028            DifferentialDriveWheelVelocities,
029            DifferentialDriveWheelAccelerations>,
030        ProtobufSerializable,
031        StructSerializable {
032  /** Differential drive trackwidth in meters. */
033  public final double trackwidth;
034
035  /** DifferentialDriveKinematics protobuf for serialization. */
036  public static final DifferentialDriveKinematicsProto proto =
037      new DifferentialDriveKinematicsProto();
038
039  /** DifferentialDriveKinematics struct for serialization. */
040  public static final DifferentialDriveKinematicsStruct struct =
041      new DifferentialDriveKinematicsStruct();
042
043  /**
044   * Constructs a differential drive kinematics object.
045   *
046   * @param trackwidth The trackwidth of the drivetrain in meters. Theoretically, this is the
047   *     distance between the left wheels and right wheels. However, the empirical value may be
048   *     larger than the physical measured value due to scrubbing effects.
049   */
050  public DifferentialDriveKinematics(double trackwidth) {
051    this.trackwidth = trackwidth;
052    MathSharedStore.reportUsage("DifferentialDriveKinematics", "");
053  }
054
055  /**
056   * Constructs a differential drive kinematics object.
057   *
058   * @param trackwidth The trackwidth of the drivetrain in meters. Theoretically, this is the
059   *     distance between the left wheels and right wheels. However, the empirical value may be
060   *     larger than the physical measured value due to scrubbing effects.
061   */
062  public DifferentialDriveKinematics(Distance trackwidth) {
063    this(trackwidth.in(Meters));
064  }
065
066  /**
067   * Returns a chassis velocity from left and right component velocities using forward kinematics.
068   *
069   * @param wheelVelocities The left and right velocities.
070   * @return The chassis velocity.
071   */
072  @Override
073  public ChassisVelocities toChassisVelocities(DifferentialDriveWheelVelocities wheelVelocities) {
074    return new ChassisVelocities(
075        (wheelVelocities.left + wheelVelocities.right) / 2,
076        0,
077        (wheelVelocities.right - wheelVelocities.left) / trackwidth);
078  }
079
080  /**
081   * Returns left and right component velocities from a chassis velocity using inverse kinematics.
082   *
083   * @param chassisVelocities The linear and angular (dx and dtheta) components that represent the
084   *     chassis' velocity.
085   * @return The left and right velocities.
086   */
087  @Override
088  public DifferentialDriveWheelVelocities toWheelVelocities(ChassisVelocities chassisVelocities) {
089    return new DifferentialDriveWheelVelocities(
090        chassisVelocities.vx - trackwidth / 2 * chassisVelocities.omega,
091        chassisVelocities.vx + trackwidth / 2 * chassisVelocities.omega);
092  }
093
094  @Override
095  public ChassisAccelerations toChassisAccelerations(
096      DifferentialDriveWheelAccelerations wheelAccelerations) {
097    return new ChassisAccelerations(
098        (wheelAccelerations.left + wheelAccelerations.right) / 2,
099        0.0,
100        (wheelAccelerations.right - wheelAccelerations.left) / trackwidth);
101  }
102
103  @Override
104  public DifferentialDriveWheelAccelerations toWheelAccelerations(
105      ChassisAccelerations chassisAccelerations) {
106    return new DifferentialDriveWheelAccelerations(
107        chassisAccelerations.ax - trackwidth / 2 * chassisAccelerations.alpha,
108        chassisAccelerations.ax + trackwidth / 2 * chassisAccelerations.alpha);
109  }
110
111  @Override
112  public Twist2d toTwist2d(
113      DifferentialDriveWheelPositions start, DifferentialDriveWheelPositions end) {
114    return toTwist2d(end.left - start.left, end.right - start.right);
115  }
116
117  /**
118   * Performs forward kinematics to return the resulting Twist2d from the given left and right side
119   * distance deltas. This method is often used for odometry -- determining the robot's position on
120   * the field using changes in the distance driven by each wheel on the robot.
121   *
122   * @param leftDistance The distance measured by the left side encoder in meters.
123   * @param rightDistance The distance measured by the right side encoder in meters.
124   * @return The resulting Twist2d.
125   */
126  public Twist2d toTwist2d(double leftDistance, double rightDistance) {
127    return new Twist2d(
128        (leftDistance + rightDistance) / 2, 0, (rightDistance - leftDistance) / trackwidth);
129  }
130
131  @Override
132  public DifferentialDriveWheelPositions copy(DifferentialDriveWheelPositions positions) {
133    return new DifferentialDriveWheelPositions(positions.left, positions.right);
134  }
135
136  @Override
137  public void copyInto(
138      DifferentialDriveWheelPositions positions, DifferentialDriveWheelPositions output) {
139    output.left = positions.left;
140    output.right = positions.right;
141  }
142
143  @Override
144  public DifferentialDriveWheelPositions interpolate(
145      DifferentialDriveWheelPositions startValue,
146      DifferentialDriveWheelPositions endValue,
147      double t) {
148    return startValue.interpolate(endValue, t);
149  }
150}