2022-01-15 13:24:09 -07:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
|
|
|
|
|
2022-01-20 19:58:05 -07:00
|
|
|
/*
|
|
|
|
|
Safety
|
|
|
|
|
Hooks
|
2022-01-21 17:20:31 -07:00
|
|
|
Add
|
2022-01-20 19:58:05 -07:00
|
|
|
*/
|
|
|
|
|
|
2022-01-15 13:24:09 -07:00
|
|
|
package frc4388.robot.subsystems;
|
|
|
|
|
|
2022-01-20 18:13:11 -07:00
|
|
|
import com.ctre.phoenix.motorcontrol.NeutralMode;
|
|
|
|
|
import com.ctre.phoenix.motorcontrol.TalonFXControlMode;
|
2022-01-15 15:56:17 -07:00
|
|
|
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
|
2022-01-18 20:00:03 -07:00
|
|
|
import com.ctre.phoenix.sensors.WPI_PigeonIMU;
|
2022-01-15 15:56:17 -07:00
|
|
|
|
2022-01-15 13:24:09 -07:00
|
|
|
import edu.wpi.first.wpilibj2.command.SubsystemBase;
|
2022-01-21 17:20:31 -07:00
|
|
|
import frc4388.robot.Constants;
|
2022-01-15 15:56:17 -07:00
|
|
|
import frc4388.robot.Constants.ClimberConstants;
|
2022-01-15 13:24:09 -07:00
|
|
|
|
|
|
|
|
public class Climber extends SubsystemBase {
|
2022-01-18 20:00:03 -07:00
|
|
|
private WPI_TalonFX m_shoulder;
|
|
|
|
|
private WPI_TalonFX m_elbow;
|
|
|
|
|
|
|
|
|
|
private WPI_PigeonIMU m_gyro;
|
2022-01-20 17:10:07 -07:00
|
|
|
|
2022-01-21 17:20:31 -07:00
|
|
|
private double[] m_position = {ClimberConstants.MIN_ARM_LENGTH, 0.d};
|
2022-01-15 13:24:09 -07:00
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
public Climber(WPI_TalonFX shoulder, WPI_TalonFX elbow, WPI_PigeonIMU gyro) {
|
2022-01-15 15:56:17 -07:00
|
|
|
m_shoulder = shoulder;
|
2022-01-20 18:13:11 -07:00
|
|
|
m_shoulder.configFactoryDefault();
|
|
|
|
|
m_shoulder.setNeutralMode(NeutralMode.Brake);
|
|
|
|
|
|
2022-01-15 15:56:17 -07:00
|
|
|
m_elbow = elbow;
|
2022-01-20 18:13:11 -07:00
|
|
|
m_elbow.configFactoryDefault();
|
|
|
|
|
m_elbow.setNeutralMode(NeutralMode.Brake);
|
|
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
m_gyro = gyro;
|
2022-01-15 13:24:09 -07:00
|
|
|
}
|
|
|
|
|
|
2022-01-15 15:56:17 -07:00
|
|
|
/* getJointAngles
|
|
|
|
|
* Gets joint angles for climber arm
|
2022-01-18 20:00:03 -07:00
|
|
|
* xTarget and yTarget are set in the xy plane of the climber, accounting for the
|
2022-01-15 15:56:17 -07:00
|
|
|
* rotation of the robot. Both parameters should be in cm.
|
2022-01-18 20:00:03 -07:00
|
|
|
* returns [shoulderAngle, elbowAngle] in radians
|
2022-01-15 15:56:17 -07:00
|
|
|
* Does not set the motors automatically
|
|
|
|
|
*
|
|
|
|
|
* IK resource: https://devforum.roblox.com/t/2-joint-2-limb-inverse-kinematics/252399 */
|
2022-01-18 20:00:03 -07:00
|
|
|
public static double[] getJointAngles(double xTarget, double yTarget, double tiltAngle) {
|
|
|
|
|
double [] angles = new double[2];
|
|
|
|
|
|
2022-01-15 15:56:17 -07:00
|
|
|
double mag = Math.hypot(xTarget, yTarget);
|
2022-01-18 20:00:03 -07:00
|
|
|
if(mag > ClimberConstants.MAX_ARM_LENGTH) {
|
|
|
|
|
xTarget = (xTarget / mag) * ClimberConstants.MAX_ARM_LENGTH;
|
|
|
|
|
yTarget = (yTarget / mag) * ClimberConstants.MAX_ARM_LENGTH;
|
|
|
|
|
} else if(mag < ClimberConstants.MIN_ARM_LENGTH) {
|
|
|
|
|
xTarget = (xTarget / mag) * ClimberConstants.MIN_ARM_LENGTH;
|
|
|
|
|
yTarget = (yTarget / mag) * ClimberConstants.MIN_ARM_LENGTH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The angle to the target point
|
|
|
|
|
double theta = Math.atan(yTarget / xTarget) + tiltAngle; // TODO rename variable
|
2022-01-20 16:37:57 -07:00
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
// Correct target position for tilt
|
|
|
|
|
xTarget = Math.cos(theta) * mag;
|
|
|
|
|
yTarget = Math.sin(theta) * mag;
|
|
|
|
|
|
|
|
|
|
// Law and Order: Cosines edition
|
2022-01-20 16:37:57 -07:00
|
|
|
double shoulderAngle = Math.acos((Math.pow(ClimberConstants.LOWER_ARM_LENGTH, 2) + Math.pow(mag, 2) - Math.pow(ClimberConstants.UPPER_ARM_LENGTH, 2)) /
|
2022-01-18 20:00:03 -07:00
|
|
|
(2.d * ClimberConstants.LOWER_ARM_LENGTH * mag));
|
2022-01-20 19:58:05 -07:00
|
|
|
//shoulderAngle = acos(LowerArmLength^2 + mag^2)
|
2022-01-18 20:00:03 -07:00
|
|
|
shoulderAngle = theta - shoulderAngle;
|
|
|
|
|
|
|
|
|
|
double elbowAngle = Math.acos((Math.pow(ClimberConstants.LOWER_ARM_LENGTH, 2) + Math.pow(ClimberConstants.UPPER_ARM_LENGTH, 2) - Math.pow(mag, 2)) /
|
|
|
|
|
(2.d * ClimberConstants.LOWER_ARM_LENGTH * ClimberConstants.UPPER_ARM_LENGTH));
|
|
|
|
|
elbowAngle = Math.PI - elbowAngle;
|
|
|
|
|
|
|
|
|
|
// If the climber is resting on the robot base, rotate the upper arm in the direction of the target
|
2022-01-21 17:20:31 -07:00
|
|
|
if(shoulderAngle <= ClimberConstants.SHOULDER_RESTING_ANGLE) {
|
|
|
|
|
shoulderAngle = ClimberConstants.SHOULDER_RESTING_ANGLE;
|
2022-01-18 20:00:03 -07:00
|
|
|
double xDiff = xTarget - ClimberConstants.LOWER_ARM_LENGTH;
|
|
|
|
|
|
|
|
|
|
elbowAngle = Math.atan(-yTarget / xDiff);
|
2022-01-21 17:20:31 -07:00
|
|
|
if(elbowAngle < ClimberConstants.ELBOW_RESTING_ANGLE)
|
|
|
|
|
elbowAngle = ClimberConstants.ELBOW_RESTING_ANGLE;
|
|
|
|
|
|
|
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
elbowAngle = Math.PI - elbowAngle;
|
2022-01-15 15:56:17 -07:00
|
|
|
|
2022-01-21 17:20:31 -07:00
|
|
|
// Deal with negative wraparound
|
|
|
|
|
if(xDiff >= 0.d)
|
2022-01-18 20:00:03 -07:00
|
|
|
elbowAngle += Math.PI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return angles;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-20 16:37:57 -07:00
|
|
|
public double getRobotTilt() {
|
|
|
|
|
double[] ypr = new double[3];
|
|
|
|
|
m_gyro.getYawPitchRoll(ypr);
|
2022-01-20 17:10:07 -07:00
|
|
|
return Math.toRadians(ypr[1]); // Pitch
|
2022-01-20 16:37:57 -07:00
|
|
|
}
|
|
|
|
|
|
2022-01-20 18:13:11 -07:00
|
|
|
public void setClimberGains() {
|
|
|
|
|
// shoulder PIDs
|
|
|
|
|
m_shoulder.selectProfileSlot(ClimberConstants.SHOULDER_SLOT_IDX, ClimberConstants.SHOULDER_PID_LOOP_IDX);
|
|
|
|
|
m_shoulder.config_kF(ClimberConstants.SHOULDER_SLOT_IDX, ClimberConstants.SHOULDER_GAINS.m_kF, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_shoulder.config_kP(ClimberConstants.SHOULDER_SLOT_IDX, ClimberConstants.SHOULDER_GAINS.m_kP, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_shoulder.config_kI(ClimberConstants.SHOULDER_SLOT_IDX, ClimberConstants.SHOULDER_GAINS.m_kI, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_shoulder.config_kD(ClimberConstants.SHOULDER_SLOT_IDX, ClimberConstants.SHOULDER_GAINS.m_kD, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
|
|
|
|
|
// elbow PIDs
|
|
|
|
|
m_elbow.selectProfileSlot(ClimberConstants.ELBOW_SLOT_IDX, ClimberConstants.ELBOW_PID_LOOP_IDX);
|
|
|
|
|
m_elbow.config_kF(ClimberConstants.ELBOW_SLOT_IDX, ClimberConstants.ELBOW_GAINS.m_kF, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_elbow.config_kP(ClimberConstants.ELBOW_SLOT_IDX, ClimberConstants.ELBOW_GAINS.m_kP, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_elbow.config_kI(ClimberConstants.ELBOW_SLOT_IDX, ClimberConstants.ELBOW_GAINS.m_kI, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
m_elbow.config_kD(ClimberConstants.ELBOW_SLOT_IDX, ClimberConstants.ELBOW_GAINS.m_kD, ClimberConstants.CLIMBER_TIMEOUT_MS);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
public void setJointAngles(double[] angles) {
|
|
|
|
|
setJointAngles(angles[0], angles[1]);
|
|
|
|
|
}
|
2022-01-15 15:56:17 -07:00
|
|
|
|
2022-01-18 20:00:03 -07:00
|
|
|
public void setJointAngles(double shoulderAngle, double elbowAngle) {
|
2022-01-20 18:13:11 -07:00
|
|
|
// Convert radians to ticks
|
2022-01-21 17:20:31 -07:00
|
|
|
double shoulderPosition = (shoulderAngle * (Constants.TICKS_PER_ROTATION_FX/2.d)) / Math.PI;
|
|
|
|
|
double elbowPosition = (elbowAngle * (Constants.TICKS_PER_ROTATION_FX/2.d)) / Math.PI;
|
2022-01-20 18:13:11 -07:00
|
|
|
|
|
|
|
|
m_shoulder.set(TalonFXControlMode.Position, shoulderPosition);
|
|
|
|
|
m_elbow.set(TalonFXControlMode.Position, elbowPosition);
|
2022-01-15 13:24:09 -07:00
|
|
|
}
|
2022-01-20 17:10:07 -07:00
|
|
|
|
2022-01-21 17:20:31 -07:00
|
|
|
public void controlWithInput(double xInput, double yInput, boolean safety) {
|
|
|
|
|
if(!safety)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_position[0] += xInput * ClimberConstants.MOVE_SPEED;
|
|
|
|
|
m_position[1] += yInput * ClimberConstants.MOVE_SPEED;
|
2022-01-20 17:10:07 -07:00
|
|
|
|
|
|
|
|
double tiltAngle = getRobotTilt();
|
|
|
|
|
|
2022-01-21 17:20:31 -07:00
|
|
|
double[] jointAngles = getJointAngles(m_position[0], m_position[1], tiltAngle);
|
2022-01-20 17:10:07 -07:00
|
|
|
setJointAngles(jointAngles);
|
|
|
|
|
}
|
2022-01-15 13:24:09 -07:00
|
|
|
}
|