Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 1 | /*----------------------------------------------------------------------------*/ |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 2 | /* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */ |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 3 | /* Open Source Software - may be modified and shared by FRC teams. The code */ |
| 4 | /* must be accompanied by the FIRST BSD license file in the root directory of */ |
| 5 | /* the project. */ |
| 6 | /*----------------------------------------------------------------------------*/ |
| 7 | |
| 8 | #include "frc/RobotDrive.h" |
| 9 | |
| 10 | #include <algorithm> |
| 11 | #include <cmath> |
| 12 | |
James Kuszmaul | 4b81d30 | 2019-12-14 20:53:14 -0800 | [diff] [blame] | 13 | #include <hal/FRCUsageReporting.h> |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 14 | |
| 15 | #include "frc/GenericHID.h" |
| 16 | #include "frc/Joystick.h" |
| 17 | #include "frc/Talon.h" |
| 18 | #include "frc/Utility.h" |
| 19 | #include "frc/WPIErrors.h" |
| 20 | |
| 21 | using namespace frc; |
| 22 | |
| 23 | static std::shared_ptr<SpeedController> make_shared_nodelete( |
| 24 | SpeedController* ptr) { |
| 25 | return std::shared_ptr<SpeedController>(ptr, NullDeleter<SpeedController>()); |
| 26 | } |
| 27 | |
| 28 | RobotDrive::RobotDrive(int leftMotorChannel, int rightMotorChannel) { |
| 29 | InitRobotDrive(); |
| 30 | m_rearLeftMotor = std::make_shared<Talon>(leftMotorChannel); |
| 31 | m_rearRightMotor = std::make_shared<Talon>(rightMotorChannel); |
| 32 | SetLeftRightMotorOutputs(0.0, 0.0); |
| 33 | } |
| 34 | |
| 35 | RobotDrive::RobotDrive(int frontLeftMotor, int rearLeftMotor, |
| 36 | int frontRightMotor, int rearRightMotor) { |
| 37 | InitRobotDrive(); |
| 38 | m_rearLeftMotor = std::make_shared<Talon>(rearLeftMotor); |
| 39 | m_rearRightMotor = std::make_shared<Talon>(rearRightMotor); |
| 40 | m_frontLeftMotor = std::make_shared<Talon>(frontLeftMotor); |
| 41 | m_frontRightMotor = std::make_shared<Talon>(frontRightMotor); |
| 42 | SetLeftRightMotorOutputs(0.0, 0.0); |
| 43 | } |
| 44 | |
| 45 | RobotDrive::RobotDrive(SpeedController* leftMotor, |
| 46 | SpeedController* rightMotor) { |
| 47 | InitRobotDrive(); |
| 48 | if (leftMotor == nullptr || rightMotor == nullptr) { |
| 49 | wpi_setWPIError(NullParameter); |
| 50 | m_rearLeftMotor = m_rearRightMotor = nullptr; |
| 51 | return; |
| 52 | } |
| 53 | m_rearLeftMotor = make_shared_nodelete(leftMotor); |
| 54 | m_rearRightMotor = make_shared_nodelete(rightMotor); |
| 55 | } |
| 56 | |
| 57 | RobotDrive::RobotDrive(SpeedController& leftMotor, |
| 58 | SpeedController& rightMotor) { |
| 59 | InitRobotDrive(); |
| 60 | m_rearLeftMotor = make_shared_nodelete(&leftMotor); |
| 61 | m_rearRightMotor = make_shared_nodelete(&rightMotor); |
| 62 | } |
| 63 | |
| 64 | RobotDrive::RobotDrive(std::shared_ptr<SpeedController> leftMotor, |
| 65 | std::shared_ptr<SpeedController> rightMotor) { |
| 66 | InitRobotDrive(); |
| 67 | if (leftMotor == nullptr || rightMotor == nullptr) { |
| 68 | wpi_setWPIError(NullParameter); |
| 69 | m_rearLeftMotor = m_rearRightMotor = nullptr; |
| 70 | return; |
| 71 | } |
| 72 | m_rearLeftMotor = leftMotor; |
| 73 | m_rearRightMotor = rightMotor; |
| 74 | } |
| 75 | |
| 76 | RobotDrive::RobotDrive(SpeedController* frontLeftMotor, |
| 77 | SpeedController* rearLeftMotor, |
| 78 | SpeedController* frontRightMotor, |
| 79 | SpeedController* rearRightMotor) { |
| 80 | InitRobotDrive(); |
| 81 | if (frontLeftMotor == nullptr || rearLeftMotor == nullptr || |
| 82 | frontRightMotor == nullptr || rearRightMotor == nullptr) { |
| 83 | wpi_setWPIError(NullParameter); |
| 84 | return; |
| 85 | } |
| 86 | m_frontLeftMotor = make_shared_nodelete(frontLeftMotor); |
| 87 | m_rearLeftMotor = make_shared_nodelete(rearLeftMotor); |
| 88 | m_frontRightMotor = make_shared_nodelete(frontRightMotor); |
| 89 | m_rearRightMotor = make_shared_nodelete(rearRightMotor); |
| 90 | } |
| 91 | |
| 92 | RobotDrive::RobotDrive(SpeedController& frontLeftMotor, |
| 93 | SpeedController& rearLeftMotor, |
| 94 | SpeedController& frontRightMotor, |
| 95 | SpeedController& rearRightMotor) { |
| 96 | InitRobotDrive(); |
| 97 | m_frontLeftMotor = make_shared_nodelete(&frontLeftMotor); |
| 98 | m_rearLeftMotor = make_shared_nodelete(&rearLeftMotor); |
| 99 | m_frontRightMotor = make_shared_nodelete(&frontRightMotor); |
| 100 | m_rearRightMotor = make_shared_nodelete(&rearRightMotor); |
| 101 | } |
| 102 | |
| 103 | RobotDrive::RobotDrive(std::shared_ptr<SpeedController> frontLeftMotor, |
| 104 | std::shared_ptr<SpeedController> rearLeftMotor, |
| 105 | std::shared_ptr<SpeedController> frontRightMotor, |
| 106 | std::shared_ptr<SpeedController> rearRightMotor) { |
| 107 | InitRobotDrive(); |
| 108 | if (frontLeftMotor == nullptr || rearLeftMotor == nullptr || |
| 109 | frontRightMotor == nullptr || rearRightMotor == nullptr) { |
| 110 | wpi_setWPIError(NullParameter); |
| 111 | return; |
| 112 | } |
| 113 | m_frontLeftMotor = frontLeftMotor; |
| 114 | m_rearLeftMotor = rearLeftMotor; |
| 115 | m_frontRightMotor = frontRightMotor; |
| 116 | m_rearRightMotor = rearRightMotor; |
| 117 | } |
| 118 | |
| 119 | void RobotDrive::Drive(double outputMagnitude, double curve) { |
| 120 | double leftOutput, rightOutput; |
| 121 | static bool reported = false; |
| 122 | if (!reported) { |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 123 | HAL_Report(HALUsageReporting::kResourceType_RobotDrive, |
| 124 | HALUsageReporting::kRobotDrive_ArcadeRatioCurve, GetNumMotors()); |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 125 | reported = true; |
| 126 | } |
| 127 | |
| 128 | if (curve < 0) { |
| 129 | double value = std::log(-curve); |
| 130 | double ratio = (value - m_sensitivity) / (value + m_sensitivity); |
James Kuszmaul | 4b81d30 | 2019-12-14 20:53:14 -0800 | [diff] [blame] | 131 | if (ratio == 0) ratio = 0.0000000001; |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 132 | leftOutput = outputMagnitude / ratio; |
| 133 | rightOutput = outputMagnitude; |
| 134 | } else if (curve > 0) { |
| 135 | double value = std::log(curve); |
| 136 | double ratio = (value - m_sensitivity) / (value + m_sensitivity); |
James Kuszmaul | 4b81d30 | 2019-12-14 20:53:14 -0800 | [diff] [blame] | 137 | if (ratio == 0) ratio = 0.0000000001; |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 138 | leftOutput = outputMagnitude; |
| 139 | rightOutput = outputMagnitude / ratio; |
| 140 | } else { |
| 141 | leftOutput = outputMagnitude; |
| 142 | rightOutput = outputMagnitude; |
| 143 | } |
| 144 | SetLeftRightMotorOutputs(leftOutput, rightOutput); |
| 145 | } |
| 146 | |
| 147 | void RobotDrive::TankDrive(GenericHID* leftStick, GenericHID* rightStick, |
| 148 | bool squaredInputs) { |
| 149 | if (leftStick == nullptr || rightStick == nullptr) { |
| 150 | wpi_setWPIError(NullParameter); |
| 151 | return; |
| 152 | } |
| 153 | TankDrive(leftStick->GetY(), rightStick->GetY(), squaredInputs); |
| 154 | } |
| 155 | |
| 156 | void RobotDrive::TankDrive(GenericHID& leftStick, GenericHID& rightStick, |
| 157 | bool squaredInputs) { |
| 158 | TankDrive(leftStick.GetY(), rightStick.GetY(), squaredInputs); |
| 159 | } |
| 160 | |
| 161 | void RobotDrive::TankDrive(GenericHID* leftStick, int leftAxis, |
| 162 | GenericHID* rightStick, int rightAxis, |
| 163 | bool squaredInputs) { |
| 164 | if (leftStick == nullptr || rightStick == nullptr) { |
| 165 | wpi_setWPIError(NullParameter); |
| 166 | return; |
| 167 | } |
| 168 | TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis), |
| 169 | squaredInputs); |
| 170 | } |
| 171 | |
| 172 | void RobotDrive::TankDrive(GenericHID& leftStick, int leftAxis, |
| 173 | GenericHID& rightStick, int rightAxis, |
| 174 | bool squaredInputs) { |
| 175 | TankDrive(leftStick.GetRawAxis(leftAxis), rightStick.GetRawAxis(rightAxis), |
| 176 | squaredInputs); |
| 177 | } |
| 178 | |
| 179 | void RobotDrive::TankDrive(double leftValue, double rightValue, |
| 180 | bool squaredInputs) { |
| 181 | static bool reported = false; |
| 182 | if (!reported) { |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 183 | HAL_Report(HALUsageReporting::kResourceType_RobotDrive, |
| 184 | HALUsageReporting::kRobotDrive_Tank, GetNumMotors()); |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 185 | reported = true; |
| 186 | } |
| 187 | |
| 188 | leftValue = Limit(leftValue); |
| 189 | rightValue = Limit(rightValue); |
| 190 | |
| 191 | // square the inputs (while preserving the sign) to increase fine control |
| 192 | // while permitting full power |
| 193 | if (squaredInputs) { |
| 194 | leftValue = std::copysign(leftValue * leftValue, leftValue); |
| 195 | rightValue = std::copysign(rightValue * rightValue, rightValue); |
| 196 | } |
| 197 | |
| 198 | SetLeftRightMotorOutputs(leftValue, rightValue); |
| 199 | } |
| 200 | |
| 201 | void RobotDrive::ArcadeDrive(GenericHID* stick, bool squaredInputs) { |
| 202 | // simply call the full-featured ArcadeDrive with the appropriate values |
| 203 | ArcadeDrive(stick->GetY(), stick->GetX(), squaredInputs); |
| 204 | } |
| 205 | |
| 206 | void RobotDrive::ArcadeDrive(GenericHID& stick, bool squaredInputs) { |
| 207 | // simply call the full-featured ArcadeDrive with the appropriate values |
| 208 | ArcadeDrive(stick.GetY(), stick.GetX(), squaredInputs); |
| 209 | } |
| 210 | |
| 211 | void RobotDrive::ArcadeDrive(GenericHID* moveStick, int moveAxis, |
| 212 | GenericHID* rotateStick, int rotateAxis, |
| 213 | bool squaredInputs) { |
| 214 | double moveValue = moveStick->GetRawAxis(moveAxis); |
| 215 | double rotateValue = rotateStick->GetRawAxis(rotateAxis); |
| 216 | |
| 217 | ArcadeDrive(moveValue, rotateValue, squaredInputs); |
| 218 | } |
| 219 | |
| 220 | void RobotDrive::ArcadeDrive(GenericHID& moveStick, int moveAxis, |
| 221 | GenericHID& rotateStick, int rotateAxis, |
| 222 | bool squaredInputs) { |
| 223 | double moveValue = moveStick.GetRawAxis(moveAxis); |
| 224 | double rotateValue = rotateStick.GetRawAxis(rotateAxis); |
| 225 | |
| 226 | ArcadeDrive(moveValue, rotateValue, squaredInputs); |
| 227 | } |
| 228 | |
| 229 | void RobotDrive::ArcadeDrive(double moveValue, double rotateValue, |
| 230 | bool squaredInputs) { |
| 231 | static bool reported = false; |
| 232 | if (!reported) { |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 233 | HAL_Report(HALUsageReporting::kResourceType_RobotDrive, |
| 234 | HALUsageReporting::kRobotDrive_ArcadeStandard, GetNumMotors()); |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 235 | reported = true; |
| 236 | } |
| 237 | |
| 238 | // local variables to hold the computed PWM values for the motors |
| 239 | double leftMotorOutput; |
| 240 | double rightMotorOutput; |
| 241 | |
| 242 | moveValue = Limit(moveValue); |
| 243 | rotateValue = Limit(rotateValue); |
| 244 | |
| 245 | // square the inputs (while preserving the sign) to increase fine control |
| 246 | // while permitting full power |
| 247 | if (squaredInputs) { |
| 248 | moveValue = std::copysign(moveValue * moveValue, moveValue); |
| 249 | rotateValue = std::copysign(rotateValue * rotateValue, rotateValue); |
| 250 | } |
| 251 | |
| 252 | if (moveValue > 0.0) { |
| 253 | if (rotateValue > 0.0) { |
| 254 | leftMotorOutput = moveValue - rotateValue; |
| 255 | rightMotorOutput = std::max(moveValue, rotateValue); |
| 256 | } else { |
| 257 | leftMotorOutput = std::max(moveValue, -rotateValue); |
| 258 | rightMotorOutput = moveValue + rotateValue; |
| 259 | } |
| 260 | } else { |
| 261 | if (rotateValue > 0.0) { |
| 262 | leftMotorOutput = -std::max(-moveValue, rotateValue); |
| 263 | rightMotorOutput = moveValue + rotateValue; |
| 264 | } else { |
| 265 | leftMotorOutput = moveValue - rotateValue; |
| 266 | rightMotorOutput = -std::max(-moveValue, -rotateValue); |
| 267 | } |
| 268 | } |
| 269 | SetLeftRightMotorOutputs(leftMotorOutput, rightMotorOutput); |
| 270 | } |
| 271 | |
| 272 | void RobotDrive::MecanumDrive_Cartesian(double x, double y, double rotation, |
| 273 | double gyroAngle) { |
| 274 | static bool reported = false; |
| 275 | if (!reported) { |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 276 | HAL_Report(HALUsageReporting::kResourceType_RobotDrive, |
| 277 | HALUsageReporting::kRobotDrive_MecanumCartesian, GetNumMotors()); |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 278 | reported = true; |
| 279 | } |
| 280 | |
| 281 | double xIn = x; |
| 282 | double yIn = y; |
| 283 | // Negate y for the joystick. |
| 284 | yIn = -yIn; |
| 285 | // Compenstate for gyro angle. |
| 286 | RotateVector(xIn, yIn, gyroAngle); |
| 287 | |
| 288 | double wheelSpeeds[kMaxNumberOfMotors]; |
| 289 | wheelSpeeds[kFrontLeftMotor] = xIn + yIn + rotation; |
| 290 | wheelSpeeds[kFrontRightMotor] = -xIn + yIn - rotation; |
| 291 | wheelSpeeds[kRearLeftMotor] = -xIn + yIn + rotation; |
| 292 | wheelSpeeds[kRearRightMotor] = xIn + yIn - rotation; |
| 293 | |
| 294 | Normalize(wheelSpeeds); |
| 295 | |
| 296 | m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput); |
| 297 | m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput); |
| 298 | m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput); |
| 299 | m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput); |
| 300 | |
| 301 | Feed(); |
| 302 | } |
| 303 | |
| 304 | void RobotDrive::MecanumDrive_Polar(double magnitude, double direction, |
| 305 | double rotation) { |
| 306 | static bool reported = false; |
| 307 | if (!reported) { |
James Kuszmaul | 4f3ad3c | 2019-12-01 16:35:21 -0800 | [diff] [blame] | 308 | HAL_Report(HALUsageReporting::kResourceType_RobotDrive, |
| 309 | HALUsageReporting::kRobotDrive_MecanumPolar, GetNumMotors()); |
Brian Silverman | 41cdd3e | 2019-01-19 19:48:58 -0800 | [diff] [blame] | 310 | reported = true; |
| 311 | } |
| 312 | |
| 313 | // Normalized for full power along the Cartesian axes. |
| 314 | magnitude = Limit(magnitude) * std::sqrt(2.0); |
| 315 | // The rollers are at 45 degree angles. |
| 316 | double dirInRad = (direction + 45.0) * 3.14159 / 180.0; |
| 317 | double cosD = std::cos(dirInRad); |
| 318 | double sinD = std::sin(dirInRad); |
| 319 | |
| 320 | double wheelSpeeds[kMaxNumberOfMotors]; |
| 321 | wheelSpeeds[kFrontLeftMotor] = sinD * magnitude + rotation; |
| 322 | wheelSpeeds[kFrontRightMotor] = cosD * magnitude - rotation; |
| 323 | wheelSpeeds[kRearLeftMotor] = cosD * magnitude + rotation; |
| 324 | wheelSpeeds[kRearRightMotor] = sinD * magnitude - rotation; |
| 325 | |
| 326 | Normalize(wheelSpeeds); |
| 327 | |
| 328 | m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput); |
| 329 | m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput); |
| 330 | m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput); |
| 331 | m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput); |
| 332 | |
| 333 | Feed(); |
| 334 | } |
| 335 | |
| 336 | void RobotDrive::HolonomicDrive(double magnitude, double direction, |
| 337 | double rotation) { |
| 338 | MecanumDrive_Polar(magnitude, direction, rotation); |
| 339 | } |
| 340 | |
| 341 | void RobotDrive::SetLeftRightMotorOutputs(double leftOutput, |
| 342 | double rightOutput) { |
| 343 | wpi_assert(m_rearLeftMotor != nullptr && m_rearRightMotor != nullptr); |
| 344 | |
| 345 | if (m_frontLeftMotor != nullptr) |
| 346 | m_frontLeftMotor->Set(Limit(leftOutput) * m_maxOutput); |
| 347 | m_rearLeftMotor->Set(Limit(leftOutput) * m_maxOutput); |
| 348 | |
| 349 | if (m_frontRightMotor != nullptr) |
| 350 | m_frontRightMotor->Set(-Limit(rightOutput) * m_maxOutput); |
| 351 | m_rearRightMotor->Set(-Limit(rightOutput) * m_maxOutput); |
| 352 | |
| 353 | Feed(); |
| 354 | } |
| 355 | |
| 356 | void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted) { |
| 357 | if (motor < 0 || motor > 3) { |
| 358 | wpi_setWPIError(InvalidMotorIndex); |
| 359 | return; |
| 360 | } |
| 361 | switch (motor) { |
| 362 | case kFrontLeftMotor: |
| 363 | m_frontLeftMotor->SetInverted(isInverted); |
| 364 | break; |
| 365 | case kFrontRightMotor: |
| 366 | m_frontRightMotor->SetInverted(isInverted); |
| 367 | break; |
| 368 | case kRearLeftMotor: |
| 369 | m_rearLeftMotor->SetInverted(isInverted); |
| 370 | break; |
| 371 | case kRearRightMotor: |
| 372 | m_rearRightMotor->SetInverted(isInverted); |
| 373 | break; |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | void RobotDrive::SetSensitivity(double sensitivity) { |
| 378 | m_sensitivity = sensitivity; |
| 379 | } |
| 380 | |
| 381 | void RobotDrive::SetMaxOutput(double maxOutput) { m_maxOutput = maxOutput; } |
| 382 | |
| 383 | void RobotDrive::GetDescription(wpi::raw_ostream& desc) const { |
| 384 | desc << "RobotDrive"; |
| 385 | } |
| 386 | |
| 387 | void RobotDrive::StopMotor() { |
| 388 | if (m_frontLeftMotor != nullptr) m_frontLeftMotor->StopMotor(); |
| 389 | if (m_frontRightMotor != nullptr) m_frontRightMotor->StopMotor(); |
| 390 | if (m_rearLeftMotor != nullptr) m_rearLeftMotor->StopMotor(); |
| 391 | if (m_rearRightMotor != nullptr) m_rearRightMotor->StopMotor(); |
| 392 | Feed(); |
| 393 | } |
| 394 | |
| 395 | void RobotDrive::InitRobotDrive() { SetSafetyEnabled(true); } |
| 396 | |
| 397 | double RobotDrive::Limit(double number) { |
| 398 | if (number > 1.0) { |
| 399 | return 1.0; |
| 400 | } |
| 401 | if (number < -1.0) { |
| 402 | return -1.0; |
| 403 | } |
| 404 | return number; |
| 405 | } |
| 406 | |
| 407 | void RobotDrive::Normalize(double* wheelSpeeds) { |
| 408 | double maxMagnitude = std::fabs(wheelSpeeds[0]); |
| 409 | for (int i = 1; i < kMaxNumberOfMotors; i++) { |
| 410 | double temp = std::fabs(wheelSpeeds[i]); |
| 411 | if (maxMagnitude < temp) maxMagnitude = temp; |
| 412 | } |
| 413 | if (maxMagnitude > 1.0) { |
| 414 | for (int i = 0; i < kMaxNumberOfMotors; i++) { |
| 415 | wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude; |
| 416 | } |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | void RobotDrive::RotateVector(double& x, double& y, double angle) { |
| 421 | double cosA = std::cos(angle * (3.14159 / 180.0)); |
| 422 | double sinA = std::sin(angle * (3.14159 / 180.0)); |
| 423 | double xOut = x * cosA - y * sinA; |
| 424 | double yOut = x * sinA + y * cosA; |
| 425 | x = xOut; |
| 426 | y = yOut; |
| 427 | } |