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}