001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2017-2018 FIRST. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.drive;
009
010import edu.wpi.first.wpilibj.SpeedController;
011import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder;
012// import edu.wpi.first.wpilibj.hal.FRCNetComm.tInstances;
013// import edu.wpi.first.wpilibj.hal.FRCNetComm.tResourceType;
014// import edu.wpi.first.wpilibj.hal.HAL;
015
016/**
017 * A class for driving Killough drive platforms.
018 *
019 * <p>Killough drives are triangular with one omni wheel on each corner.
020 *
021 * <p>Drive base diagram:
022 * <pre>
023 *  /_____\
024 * / \   / \
025 *    \ /
026 *    ---
027 * </pre>
028 *
029 * <p>Each drive() function provides different inverse kinematic relations for a Killough drive. The
030 * default wheel vectors are parallel to their respective opposite sides, but can be overridden. See
031 * the constructor for more information.
032 *
033 * <p>This library uses the NED axes convention (North-East-Down as external reference in the world
034 * frame): http://www.nuclearprojects.com/ins/images/axis_big.png.
035 *
036 * <p>The positive X axis points ahead, the positive Y axis points right, and the positive Z axis
037 * points down. Rotations follow the right-hand rule, so clockwise rotation around the Z axis is
038 * positive.
039 */
040public class KilloughDrive extends RobotDriveBase {
041  public static final double kDefaultLeftMotorAngle = 60.0;
042  public static final double kDefaultRightMotorAngle = 120.0;
043  public static final double kDefaultBackMotorAngle = 270.0;
044
045  private static int instances = 0;
046
047  private SpeedController m_leftMotor;
048  private SpeedController m_rightMotor;
049  private SpeedController m_backMotor;
050
051  private Vector2d m_leftVec;
052  private Vector2d m_rightVec;
053  private Vector2d m_backVec;
054
055  private boolean m_reported = false;
056
057  /**
058   * Construct a Killough drive with the given motors and default motor angles.
059   *
060   * <p>The default motor angles make the wheels on each corner parallel to their respective
061   * opposite sides.
062   *
063   * <p>If a motor needs to be inverted, do so before passing it in.
064   *
065   * @param leftMotor  The motor on the left corner.
066   * @param rightMotor The motor on the right corner.
067   * @param backMotor  The motor on the back corner.
068   */
069  public KilloughDrive(SpeedController leftMotor, SpeedController rightMotor,
070                       SpeedController backMotor) {
071    this(leftMotor, rightMotor, backMotor, kDefaultLeftMotorAngle, kDefaultRightMotorAngle,
072        kDefaultBackMotorAngle);
073  }
074
075  /**
076   * Construct a Killough drive with the given motors.
077   *
078   * <p>Angles are measured in degrees clockwise from the positive X axis.
079   *
080   * @param leftMotor       The motor on the left corner.
081   * @param rightMotor      The motor on the right corner.
082   * @param backMotor       The motor on the back corner.
083   * @param leftMotorAngle  The angle of the left wheel's forward direction of travel.
084   * @param rightMotorAngle The angle of the right wheel's forward direction of travel.
085   * @param backMotorAngle  The angle of the back wheel's forward direction of travel.
086   */
087  public KilloughDrive(SpeedController leftMotor, SpeedController rightMotor,
088                       SpeedController backMotor, double leftMotorAngle, double rightMotorAngle,
089                       double backMotorAngle) {
090    m_leftMotor = leftMotor;
091    m_rightMotor = rightMotor;
092    m_backMotor = backMotor;
093    m_leftVec = new Vector2d(Math.cos(leftMotorAngle * (Math.PI / 180.0)),
094                             Math.sin(leftMotorAngle * (Math.PI / 180.0)));
095    m_rightVec = new Vector2d(Math.cos(rightMotorAngle * (Math.PI / 180.0)),
096                              Math.sin(rightMotorAngle * (Math.PI / 180.0)));
097    m_backVec = new Vector2d(Math.cos(backMotorAngle * (Math.PI / 180.0)),
098                             Math.sin(backMotorAngle * (Math.PI / 180.0)));
099    addChild(m_leftMotor);
100    addChild(m_rightMotor);
101    addChild(m_backMotor);
102    instances++;
103    setName("KilloughDrive", instances);
104  }
105
106  /**
107   * Drive method for Killough platform.
108   *
109   * <p>Angles are measured clockwise from the positive X axis. The robot's speed is independent
110   * from its angle or rotation rate.
111   *
112   * @param ySpeed    The robot's speed along the Y axis [-1.0..1.0]. Right is positive.
113   * @param xSpeed    The robot's speed along the X axis [-1.0..1.0]. Forward is positive.
114   * @param zRotation The robot's rotation rate around the Z axis [-1.0..1.0]. Clockwise is
115   *                  positive.
116   */
117  @SuppressWarnings("ParameterName")
118  public void driveCartesian(double ySpeed, double xSpeed, double zRotation) {
119    driveCartesian(ySpeed, xSpeed, zRotation, 0.0);
120  }
121
122  /**
123   * Drive method for Killough platform.
124   *
125   * <p>Angles are measured clockwise from the positive X axis. The robot's speed is independent
126   * from its angle or rotation rate.
127   *
128   * @param ySpeed    The robot's speed along the Y axis [-1.0..1.0]. Right is positive.
129   * @param xSpeed    The robot's speed along the X axis [-1.0..1.0]. Forward is positive.
130   * @param zRotation The robot's rotation rate around the Z axis [-1.0..1.0]. Clockwise is
131   *                  positive.
132   * @param gyroAngle The current angle reading from the gyro in degrees around the Z axis. Use
133   *                  this to implement field-oriented controls.
134   */
135  @SuppressWarnings("ParameterName")
136  public void driveCartesian(double ySpeed, double xSpeed, double zRotation,
137                             double gyroAngle) {
138    if (!m_reported) {
139      // HAL.report(tResourceType.kResourceType_RobotDrive, 3,
140      //            tInstances.kRobotDrive_KilloughCartesian);
141      m_reported = true;
142    }
143
144    ySpeed = limit(ySpeed);
145    ySpeed = applyDeadband(ySpeed, m_deadband);
146
147    xSpeed = limit(xSpeed);
148    xSpeed = applyDeadband(xSpeed, m_deadband);
149
150    // Compensate for gyro angle.
151    Vector2d input = new Vector2d(ySpeed, xSpeed);
152    input.rotate(-gyroAngle);
153
154    double[] wheelSpeeds = new double[3];
155    wheelSpeeds[MotorType.kLeft.value] = input.scalarProject(m_leftVec) + zRotation;
156    wheelSpeeds[MotorType.kRight.value] = input.scalarProject(m_rightVec) + zRotation;
157    wheelSpeeds[MotorType.kBack.value] = input.scalarProject(m_backVec) + zRotation;
158
159    normalize(wheelSpeeds);
160
161    m_leftMotor.set(wheelSpeeds[MotorType.kLeft.value] * m_maxOutput);
162    m_rightMotor.set(wheelSpeeds[MotorType.kRight.value] * m_maxOutput);
163    m_backMotor.set(wheelSpeeds[MotorType.kBack.value] * m_maxOutput);
164
165    m_safetyHelper.feed();
166  }
167
168  /**
169   * Drive method for Killough platform.
170   *
171   * <p>Angles are measured counter-clockwise from straight ahead. The speed at which the robot
172   * drives (translation) is independent from its angle or rotation rate.
173   *
174   * @param magnitude The robot's speed at a given angle [-1.0..1.0]. Forward is positive.
175   * @param angle     The angle around the Z axis at which the robot drives in degrees [-180..180].
176   * @param zRotation The robot's rotation rate around the Z axis [-1.0..1.0]. Clockwise is
177   *                  positive.
178   */
179  @SuppressWarnings("ParameterName")
180  public void drivePolar(double magnitude, double angle, double zRotation) {
181    if (!m_reported) {
182      // HAL.report(tResourceType.kResourceType_RobotDrive, 3,
183      //            tInstances.kRobotDrive_KilloughPolar);
184      m_reported = true;
185    }
186
187    driveCartesian(magnitude * Math.sin(angle * (Math.PI / 180.0)),
188                   magnitude * Math.cos(angle * (Math.PI / 180.0)), zRotation, 0.0);
189  }
190
191  @Override
192  public void stopMotor() {
193    m_leftMotor.stopMotor();
194    m_rightMotor.stopMotor();
195    m_backMotor.stopMotor();
196    m_safetyHelper.feed();
197  }
198
199  @Override
200  public String getDescription() {
201    return "KilloughDrive";
202  }
203
204  @Override
205  public void initSendable(SendableBuilder builder) {
206    builder.setSmartDashboardType("KilloughDrive");
207    builder.addDoubleProperty("Left Motor Speed", m_leftMotor::get, m_leftMotor::set);
208    builder.addDoubleProperty("Right Motor Speed", m_rightMotor::get, m_rightMotor::set);
209    builder.addDoubleProperty("Back Motor Speed", m_backMotor::get, m_backMotor::set);
210  }
211}