Merge remote-tracking branch 'origin/testRoboReveal' into cleanup

This commit is contained in:
nathanrsxtn
2022-03-22 17:41:02 -06:00
46 changed files with 3154 additions and 929 deletions
+11 -4
View File
@@ -1,5 +1,12 @@
Distance (in) ,Hood Ext. (u) ,Drum Velocity (u/ds)
96 ,-25.00 ,0.425
144 ,-47.57 ,0.475
192 ,-55.55 ,0.500
240 ,-96.00 ,0.525
0 ,-29.5 ,8000
78.5 ,-29.5 ,8000
99 ,-39.62 ,8500
127.25 ,-49.12 ,9500
150 ,-66.22 ,10000
164.5 ,-75.52 ,10000
186 ,-76.24 ,10000
207 ,-104.07 ,11000
227 ,-105.32 ,11500
255.5 ,-105.8 ,13500
999 ,-105.8 ,13500
1 Distance (in) Hood Ext. (u) Drum Velocity (u/ds)
2 96 0 -25.00 -29.5 0.425 8000
3 144 78.5 -47.57 -29.5 0.475 8000
4 192 99 -55.55 -39.62 0.500 8500
5 240 127.25 -96.00 -49.12 0.525 9500
6 150 -66.22 10000
7 164.5 -75.52 10000
8 186 -76.24 10000
9 207 -104.07 11000
10 227 -105.32 11500
11 255.5 -105.8 13500
12 999 -105.8 13500
@@ -0,0 +1,12 @@
Distance (in) ,Duration (s)
0 ,0
78.5 ,0
99 ,0
127.25 ,0
150 ,0
164.5 ,0
186 ,0
207 ,0
227 ,0
255.5 ,0
999 ,0
1 Distance (in) Duration (s)
2 0 0
3 78.5 0
4 99 0
5 127.25 0
6 150 0
7 164.5 0
8 186 0
9 207 0
10 227 0
11 255.5 0
12 999 0
@@ -1 +1 @@
{"waypoints":[{"anchorPoint":{"x":2.0,"y":2.0},"prevControl":null,"nextControl":{"x":2.014863901928194,"y":2.0},"holonomicAngle":0.0,"isReversal":false,"velOverride":null,"isLocked":false},{"anchorPoint":{"x":5.0,"y":2.0},"prevControl":{"x":4.99458955727988,"y":2.0},"nextControl":{"x":4.99458955727988,"y":2.0},"holonomicAngle":0.0,"isReversal":true,"velOverride":null,"isLocked":false},{"anchorPoint":{"x":2.0,"y":2.0},"prevControl":{"x":1.4597663280316806,"y":2.0},"nextControl":null,"holonomicAngle":0.0,"isReversal":false,"velOverride":null,"isLocked":false}],"maxVelocity":1.0,"maxAcceleration":1.0,"isReversed":null}
{"waypoints":[{"anchorPoint":{"x":3.8761638155965237,"y":2.097178183811552},"prevControl":null,"nextControl":{"x":3.891027717524718,"y":2.097178183811552},"holonomicAngle":0.0,"isReversal":false,"velOverride":null,"isLocked":false},{"anchorPoint":{"x":4.989837747689554,"y":2.097178183811552},"prevControl":{"x":4.984427304969435,"y":2.097178183811552},"nextControl":{"x":4.984427304969435,"y":2.097178183811552},"holonomicAngle":91.52752544221289,"isReversal":true,"velOverride":null,"isLocked":false},{"anchorPoint":{"x":3.8761638155965237,"y":2.140568077269722},"prevControl":{"x":3.3359301436282043,"y":2.140568077269722},"nextControl":null,"holonomicAngle":178.2100893917539,"isReversal":false,"velOverride":null,"isLocked":false}],"maxVelocity":1.0,"maxAcceleration":1.0,"isReversed":null}
+161 -39
View File
@@ -4,6 +4,9 @@
package frc4388.robot;
import java.security.PublicKey;
import com.ctre.phoenix.motorcontrol.StatorCurrentLimitConfiguration;
import com.ctre.phoenix.motorcontrol.SupplyCurrentLimitConfiguration;
import edu.wpi.first.math.controller.PIDController;
@@ -30,28 +33,31 @@ import frc4388.utility.LEDPatterns;
* constants are needed, to reduce verbosity.
*/
public final class Constants {
public static final double TICKS_PER_ROTATION_FX = 2048;
public static final double LOOP_TIME = 0.02;
public static final class SwerveDriveConstants {
public static final double ROTATION_SPEED = 4;
public static final double WIDTH = 23.5;
public static final double HEIGHT = 23.5;
public static final double ROTATION_SPEED = 2.0;
public static final double WIDTH = 23.75;
public static final double HEIGHT = 23.75;
public static final double JOYSTICK_TO_METERS_PER_SECOND_FAST = 11;
public static final double JOYSTICK_TO_METERS_PER_SECOND_SLOW = 2;
public static final double MAX_SPEED_FEET_PER_SEC = 20; // TODO: redundant constant?
public static final double SPEED_FEET_PER_SECOND_AT_FULL_POWER = 20; // TODO: redundant constant?
// IDs
public static final int LEFT_FRONT_STEER_CAN_ID = 2;
public static final int LEFT_FRONT_WHEEL_CAN_ID = 3;
public static final int RIGHT_FRONT_STEER_CAN_ID = 4;
public static final int RIGHT_FRONT_WHEEL_CAN_ID = 5;
public static final int LEFT_BACK_STEER_CAN_ID = 6;
public static final int LEFT_BACK_WHEEL_CAN_ID = 7;
public static final int RIGHT_BACK_STEER_CAN_ID = 8;
public static final int RIGHT_BACK_WHEEL_CAN_ID = 9;
public static final int LEFT_FRONT_STEER_CAN_ENCODER_ID = 10;
public static final int RIGHT_FRONT_STEER_CAN_ENCODER_ID = 11;
public static final int LEFT_BACK_STEER_CAN_ENCODER_ID = 12;
public static final int RIGHT_BACK_STEER_CAN_ENCODER_ID = 13;
public static final int LEFT_FRONT_STEER_CAN_ID = 2; // *
public static final int LEFT_FRONT_WHEEL_CAN_ID = 3; // *
public static final int RIGHT_FRONT_STEER_CAN_ID = 4; // *
public static final int RIGHT_FRONT_WHEEL_CAN_ID = 5; // *
public static final int LEFT_BACK_STEER_CAN_ID = 6; // *
public static final int LEFT_BACK_WHEEL_CAN_ID = 7; // *
public static final int RIGHT_BACK_STEER_CAN_ID = 8; // *
public static final int RIGHT_BACK_WHEEL_CAN_ID = 9; // *
public static final int LEFT_FRONT_STEER_CAN_ENCODER_ID = 10; // *
public static final int RIGHT_FRONT_STEER_CAN_ENCODER_ID = 11; // *
public static final int LEFT_BACK_STEER_CAN_ENCODER_ID = 12; // *
public static final int RIGHT_BACK_STEER_CAN_ENCODER_ID = 13; // *
public static final int GYRO_ID = 14;
// offsets are in degrees
@@ -65,10 +71,10 @@ public final class Constants {
// public static final double RIGHT_BACK_ENCODER_OFFSET = 360. + 2.15 - 3.637;//
// 180-2.021484375;//0.0;//134.384765625
public static final double RIGHT_FRONT_ENCODER_OFFSET = (4 * 360. - 152.05 - 180 - 90) % 360.;
public static final double LEFT_FRONT_ENCODER_OFFSET = (4 * 360. - 232.58 + 180 - 90 ) % 360.;
public static final double LEFT_BACK_ENCODER_OFFSET = (4 * 360. - 189.50 - 90) % 360.;
public static final double RIGHT_BACK_ENCODER_OFFSET = (4 * 360. - 9.31 - 90 - 180) % 360.;
public static final double LEFT_FRONT_ENCODER_OFFSET = (4 * 360. - 232.58 + 180 ) % 360.;
public static final double RIGHT_FRONT_ENCODER_OFFSET = (4 * 360. - 152.05 - 180 ) % 360.;
public static final double LEFT_BACK_ENCODER_OFFSET = (4 * 360. - 189.50) % 360.;
public static final double RIGHT_BACK_ENCODER_OFFSET = (4 * 360. - 9.31 - 180) % 360.;
// swerve PID constants
public static final int SWERVE_SLOT_IDX = 0;
@@ -104,7 +110,6 @@ public final class Constants {
public static final double INCHES_PER_WHEEL_REV = WHEEL_DIAMETER_INCHES * Math.PI;
public static final double INCHES_PER_METER = 39.370;
public static final double METERS_PER_INCH = 1 / INCHES_PER_METER;
public static final double WHEEL_REV_PER_MOTOR_REV = 1 / MOTOR_REV_PER_WHEEL_REV;
public static final double TICKS_PER_WHEEL_REV = TICKS_PER_MOTOR_REV * MOTOR_REV_PER_WHEEL_REV;
public static final double TICKS_PER_INCH = TICKS_PER_WHEEL_REV / INCHES_PER_WHEEL_REV;
@@ -112,6 +117,18 @@ public final class Constants {
public static final double TICK_TIME_TO_SECONDS = 0.1;
public static final double SECONDS_TO_TICK_TIME = 1 / TICK_TIME_TO_SECONDS;
// current limits
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG_STEER = new SupplyCurrentLimitConfiguration(
false, 10, 0, 0);
public static final StatorCurrentLimitConfiguration STATOR_CURRENT_LIMIT_CONFIG_STEER = new StatorCurrentLimitConfiguration(
false, 15, 0, 0);
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG_WHEEL = new SupplyCurrentLimitConfiguration(
false, 10, 0, 0);
public static final StatorCurrentLimitConfiguration STATOR_CURRENT_LIMIT_CONFIG_WHEEL = new StatorCurrentLimitConfiguration(
false, 15, 0, 0);
// misc
public static final int SMARTDASHBOARD_UPDATE_FRAME = 2;
public static final Pose2d HUB_POSE = new Pose2d(new Translation2d(0, 0), new Rotation2d(0));
@@ -129,12 +146,23 @@ public final class Constants {
// CAN IDs
public static final int INTAKE_MOTOR = 15;
public static final int EXTENDER_MOTOR = 16;
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG_INTAKE = new SupplyCurrentLimitConfiguration(
false, 10, 0, 0); //Find
public static final StatorCurrentLimitConfiguration STATOR_CURRENT_LIMIT_CONFIG_INTAKE = new StatorCurrentLimitConfiguration(
false, 15, 0, 0);
public static final double INTAKE_SPEED_MULTIPLIER = 0.4;
}
public static final class ExtenderConstants {
public static final double EXTENDER_FORWARD_LIMIT = 235.0;//250.0;
public static final double EXTENDER_REVERSE_LIMIT = 0.0;
}
public static final class StorageConstants {
public static final int STORAGE_CAN_ID = 18;
public static final int BEAM_SENSOR_SHOOTER = 28; //TODO
public static final int BEAM_SENSOR_INTAKE = 29; //TODO
public static final double STORAGE_SPEED = 0.75;
public static final double STORAGE_SPEED = 0.9;
}
public static final class LEDConstants {
public static final int LED_SPARK_ID = 0;
@@ -142,14 +170,90 @@ public final class Constants {
public static final LEDPatterns DEFAULT_PATTERN = LEDPatterns.FOREST_WAVES;
}
public static final class ClimberConstants {
public static final int SHOULDER_ID = 30;
public static final int ELBOW_ID = 31;
public static final int GYRO_ID = 14;
public static final double INPUT_MULTIPLIER = 1.0;
public static final double ELBOW_SOFT_LIMIT_TOLERANCE = 20000.0;
public static final double SHOULDER_SOFT_LIMIT_TOLERANCE = 12000.0;
// TODO Update this stuff too
public static final double UPPER_ARM_LENGTH = 26; // Units should be in cm
public static final double LOWER_ARM_LENGTH = 27;
public static final double MAX_ARM_LENGTH = 53;
public static final double MIN_ARM_LENGTH = 1;
public static final double MOVE_SPEED = 30000; // ticks per second
public static final double SHOULDER_RESTING_ANGLE = 0;
public static final double ELBOW_RESTING_ANGLE = 0;
public static final double SHOULDER_MAX_ANGLE = 135;
public static final double ELBOW_MAX_ANGLE = 180;
public static final double ELBOW_GB_RATIO = 1.d;
public static final double SHOULDER_GB_RATIO = 1.d;
public static final double SHOULDER_FORWARD_SOFT_LIMIT = 53869;
public static final double SHOULDER_REVERSE_SOFT_LIMIT = 0;
public static final double ELBOW_FORWARD_SOFT_LIMIT = 281717;
public static final double ELBOW_REVERSE_SOFT_LIMIT = 0;
// PID Constants
public static final int SHOULDER_POSITION_SLOT_IDX = 0;
public static final int SHOULDER_VELOCITY_SLOT_IDX = 0;
public static final int SHOULDER_PID_LOOP_IDX = 0;
public static final int ELBOW_POSITION_SLOT_IDX = 0;
public static final int ELBOW_VELOCITY_SLOT_IDX = 0;
public static final int ELBOW_PID_LOOP_IDX = 0;
public static final Gains SHOULDER_POSITION_GAINS = new Gains(0.0, 0.0, 0.0, 0.0, 0, 0.2);
public static final Gains ELBOW_POSITION_GAINS = new Gains(0.0, 0.0, 0.0, 0.0, 0, 0.2);
public static final Gains SHOULDER_VELOCITY_GAINS = new Gains(0.2, 0.0, 5.0, 0.0, 0, 0.2);
public static final Gains ELBOW_VELOCITY_GAINS = new Gains(0.3, 0.005, 5.0, 0.0, 0, 1.0);
public static final int CLIMBER_TIMEOUT_MS = 50;
public static final double THRESHOLD = 0;
// TODO: Update Constants
// Robot Angle
public static final double ROBOT_ANGLE_ID = 0;
}
public static final class ClawConstants {
public static final int LEFT_CLAW_ID = 44;
public static final int RIGHT_CLAW_ID = 45;
public static final double OPEN_POSITION = 0; // TODO: find actual position
public static final double CLOSE_POSITION = 1; // TODO: find actual position
public static final double THRESHOLD = .1; // TODO: find actual threshold
public static final double CALIBRATION_SPEED = 0;
public static final double CURRENT_LIMIT = 0.0; // TODO: set actual current limit;
public static final int TOP_LIMIT = 1800;
public static final int BOTTOM_LIMIT = 1200;
}
/**
* The OIConstants class contains the ID for the XBox controllers
*/
public static final class OIConstants {
public static final int XBOX_DRIVER_ID = 0;
public static final int XBOX_OPERATOR_ID = 1;
public static final int BUTTON_BOX_ID = 2;
public static final double LEFT_AXIS_DEADBAND = 0.1;
public static final double RIGHT_AXIS_DEADBAND = 0.6;
public static final boolean SKEW_STICKS = true;
}
public static final class ShooterConstants {
@@ -159,36 +263,50 @@ public final class Constants {
public static final int SHOOTER_TIMEOUT_MS = 32;
public static final int SHOOTER_SLOT_IDX = 0;
public static final int SHOOTER_PID_LOOP_IDX = 1;
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG = new SupplyCurrentLimitConfiguration(
true, 60, 40, 0.5);
public static final SupplyCurrentLimitConfiguration SUPPLY_CURRENT_LIMIT_CONFIG_SHOOTER = new SupplyCurrentLimitConfiguration(
true, 10, 0, 0);
public static final StatorCurrentLimitConfiguration STATOR_CURRENT_LIMIT_CONFIG_SHOOTER = new StatorCurrentLimitConfiguration(
true, 27, 0, 0);
public static final int SHOOTER_FALCON_LEFT_CAN_ID = 21;
public static final int SHOOTER_FALCON_RIGHT_CAN_ID = 22;
public static final double TURRET_SPEED_MULTIPLIER = 0.4d;
public static final int DEGREES_PER_ROT = 0;
public static final double TURRET_SPEED_MULTIPLIER = 0.4;
public static final double TURRET_CALIBRATION_MULTIPLIER = 0.5;
public static final double TURRET_DEGREES_PER_ROT = 180.0/105.45445251464844;
public static final int TURRET_MOTOR_POS_AT_ZERO_ROT = 0;
public static final int TURRET_MOTOR_ROTS_PER_ROT = 0;
public static final double ENCODER_TICKS_PER_REV = 2048;
public static final double TURRET_CLIMBING_POS = -3.76;
// Shoot Command Constants
public static final Gains SHOOT_GAINS = new Gains(5.0, 0.0, 0.0, 0.0, 0, 1.0);
public static final Gains TURRET_SHOOT_GAINS = new Gains(3.0, 0.0, 0.0, 0.0, 0, 1.0);
public static final Gains SWERVE_SHOOT_GAINS = new Gains(6.0, 0.0, 0.0, 0.0, 0, 1.0);
/* Turret Constants */
// ID
public static final int TURRET_MOTOR_CAN_ID = 19;
public static final Gains SHOOTER_TURRET_GAINS = new Gains(0.6, 0.0, 0.0, 0.0, 0, 1.0);
public static final Gains SHOOTER_ANGLE_GAINS = new Gains(0.05, 0.0, 0.0, 0.0, 0, 0.3);
public static final double SHOOTER_TURRET_MIN = -1.0;
public static final double TURRET_FORWARD_LIMIT = 61.7; // TODO: find
public static final double TURRET_REVERSE_LIMIT = -42.3; // TODO: find
//Gains for turret
public static final Gains SHOOTER_TURRET_GAINS = new Gains(0.1, 0.0, 0.1, 0.0, 0, 0.3/*TURRET_SPEED_MULTIPLIER*/);
public static final double SHOOTER_TURRET_MIN = -0.3;//-TURRET_SPEED_MULTIPLIER;
//Gains for hood
public static final Gains SHOOTER_ANGLE_GAINS = new Gains(0.1, 0.0, 0.0, 0.0, 0, 0.7);
public static final Gains DRUM_SHOOTER_GAINS = new Gains(0, 0, 0, 0, 0, 0); // TODO: tune values
public static final double TURRET_FORWARD_HARD_LIMIT = 18.429;
public static final double TURRET_REVERSE_HARD_LIMIT = -117.8;
public static final double TURRET_FORWARD_SOFT_LIMIT = TURRET_FORWARD_HARD_LIMIT - 5;
public static final double TURRET_REVERSE_SOFT_LIMIT = TURRET_REVERSE_HARD_LIMIT + 2;
public static final double TURRET_SOFT_LIMIT_TOLERANCE = 20.0;
//Shooter gains for actual Drum
public static final Gains DRUM_SHOOTER_GAINS = new Gains(0.4, 0.0, 15.0, 0.05, 0, 0);
/* Hood Constants */
public static final int SHOOTER_ANGLE_ADJUST_ID = 20;
public static final double HOOD_MOTOR_ROTS_PER_ROT = 1; // TODO: Find
public static final double HOOD_MOTOR_POS_AT_ZERO_ROT = 0; // TODO: Find
public static final double HOOD_FORWARD_LIMIT = 48.69; // TODO: find
public static final double HOOD_REVERSE_LIMIT = -100; // TODO: find
public static final double HOOD_FORWARD_SOFT_LIMIT = 0.0; // TODO: find
public static final double HOOD_REVERSE_SOFT_LIMIT = -150; // TODO: find
public static final double HOOD_SOFT_LIMIT_TOLERANCE = 20.0;
}
@@ -199,21 +317,25 @@ public final class Constants {
// public static final double TARGET_HEIGHT = 67.5;
// public static final double FOV = 29.8; //Field of view limelight
public static final double LIME_ANGLE = 24.7;
public static final double LIME_ANGLE = 56.4;
public static final String NAME = "photonCamera";
public static final double TARGET_HEIGHT = 8*12 + 8; //TODO: Convert to metric (does this still need to be converted?)
public static final double LIME_HEIGHT = 26;
public static final double TARGET_RADIUS = 4*12; //TODO: Convert to metric (does this still need to be converted?)
public static final double H_FOV = 59.6;
public static final double V_FOV = 49.7;
public static final double LIME_VIXELS = 960;
public static final double LIME_HIXELS = 720;
public static final double V_FOV = 45.7;
public static final double LIME_HIXELS = 920;
public static final double LIME_VIXELS = 720;
public static final double TURRET_kP = 0.1;
public static final double POINTS_THRESHOLD = 400;
public static final double RANGE = 10;
public static final double LIMELIGHT_RADIUS = 1.d;
public static final double EDGE_TO_CENTER = 20;
public static final double LIMELIGHT_RADIUS = 8;
public static final double SHOOTER_CORRECTION = 1.d;
}
}
+39 -54
View File
@@ -20,6 +20,7 @@ import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.Filesystem;
import edu.wpi.first.wpilibj.TimedRobot;
import edu.wpi.first.wpilibj.drive.Vector2d;
import edu.wpi.first.wpilibj.DriverStation.Alliance;
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
@@ -27,6 +28,7 @@ import edu.wpi.first.wpilibj2.command.CommandScheduler;
import edu.wpi.first.wpilibj2.command.RunCommand;
import frc4388.utility.RobotTime;
import frc4388.utility.VelocityCorrection;
import frc4388.utility.desmos.DesmosServer;
/**
* The VM is configured to automatically run this class, and to call the
@@ -41,10 +43,12 @@ public class Robot extends TimedRobot {
private RobotTime m_robotTime = RobotTime.getInstance();
private RobotContainer m_robotContainer;
private double current;
private SendableChooser<Pose2d> odoChooser = new SendableChooser<Pose2d>();
private HashMap<String, Pose2d> odoChoices = new HashMap<>();
private Pose2d selectedOdo;
private static DesmosServer desmosServer = new DesmosServer(8000);
public static Alliance alliance;
/**
* This function is run when the robot is first started up and should be
@@ -116,6 +120,10 @@ public class Robot extends TimedRobot {
return "Not Running";
}
});
desmosServer.start();
m_robotContainer.m_robotVisionOdometry.setLEDs(false);
// DesmosServer.putTable("table", "x1", new double[] {44}, "y1", new double[] {0});
}
/**
@@ -130,8 +138,19 @@ public class Robot extends TimedRobot {
@Override
public void robotPeriodic() {
m_robotTime.updateTimes();
SmartDashboard.putNumber("Turret", m_robotContainer.m_robotMap.shooterTurret.getEncoder().getPosition());
SmartDashboard.putNumber("Hood", m_robotContainer.m_robotMap.angleAdjusterMotor.getEncoder().getPosition());
// current =
// m_robotContainer.m_robotBoomBoom.getCurrent() +
// m_robotContainer.m_robotClimber.getCurrent(); //+
// m_robotContainer.m_robotHood.getCurrent() +
// m_robotContainer.m_robotIntake.getCurrent() +
// m_robotContainer.m_robotExtender.getCurrent() +
// m_robotContainer.m_robotSerializer.getCurrent() +
// m_robotContainer.m_robotStorage.getCurrent() +
// m_robotContainer.m_robotSwerveDrive.getCurrent();
// m_robotContainer.m_robotTurret.getCurrent();
// SmartDashboard.putNumber("Total Robot Current Draw", current);
// SmartDashboard.putNumber("Drive Train Voltage", m_robotContainer.m_robotSwerveDrive.getVoltage());
// SmartDashboard.putNumber("Drive Train Current", m_robotContainer.m_robotSwerveDrive.getCurrent());
// Runs the Scheduler. This is responsible for polling buttons, adding
// newly-scheduled
// commands, running already-scheduled commands, removing finished or
@@ -141,44 +160,8 @@ public class Robot extends TimedRobot {
// block in order for anything in the Command-based framework to work.
CommandScheduler.getInstance().run();
// velocity correction tests
VelocityCorrection vc = new VelocityCorrection(m_robotContainer.m_robotSwerveDrive, m_robotContainer.m_robotBoomBoom);
//SmartDashboard.putNumber("Turret Encoder Position", m_robotContainer.m_robotTurret.m_boomBoomRotateEncoder.getPosition());
//SmartDashboard.putNumber("Hood Encoder Position", m_robotContainer.m_robotHood.m_angleEncoder.getPosition());
// System.out.println("Position: (" + vc.position.x + ", " + vc.position.y + ")");
// System.out.println("Velocity: (" + vc.cartesianVelocity.x + ", " + vc.cartesianVelocity.y + ")");
// System.out.println("Tangential Velocity: (" + vc.tangentialVelocity.x + ", " + vc.tangentialVelocity.y + ")");
// System.out.println("Radial Velocity: (" + vc.radialVelocity.x + ", " + vc.radialVelocity.y + ")");
SmartDashboard.putNumber("Turret Encoder Position", m_robotContainer.m_robotTurret.m_boomBoomRotateEncoder.getPosition());
SmartDashboard.putNumber("Hood Encoder Position", m_robotContainer.m_robotHood.m_angleEncoder.getPosition());
// odo chooser stuff
addOdoChoices(new Pose2d(1, 1, new Rotation2d(Math.PI/6)),
new Pose2d(1, 2, new Rotation2d(Math.PI/3)),
new Pose2d(1, 3, new Rotation2d(Math.PI/4)));
updateOdoChooser();
SmartDashboard.putData("Odometry Chooser", odoChooser);
// print odometry data to smart dashboard for debugging (if causing timeout
// errors, you can comment it)
SmartDashboard.putNumber("Odometry X", m_robotContainer.getOdometry().getX());
SmartDashboard.putNumber("Odometry Y", m_robotContainer.getOdometry().getY());
SmartDashboard.putNumber("Odometry Theta", m_robotContainer.getOdometry().getRotation().getDegrees());
}
public void updateOdoChooser() {
for (Map.Entry<String,Pose2d> entry : odoChoices.entrySet()) {
odoChooser.addOption(entry.getKey(), entry.getValue());
}
}
public void addOdoChoices(Pose2d... points) {
for (Pose2d point : points) {
String key = "(" + point.getX() + ", " + point.getY() + ", " + point.getRotation().getDegrees() + "°)";
odoChoices.put(key, point);
}
}
/**
@@ -190,6 +173,18 @@ public class Robot extends TimedRobot {
public void disabledInit() {
LOGGER.fine("disabledInit()");
m_robotTime.endMatchTime();
if (isTest()) {
// IMPORTANT: Had to chown the pathplanner folder in order to save autos.
File outputFile = Filesystem.getDeployDirectory().toPath().resolve("pathplanner")
.resolve("recording." + System.currentTimeMillis() + ".path").toFile();
if (Boolean.TRUE.equals(Errors.log().getWithDefault(outputFile::createNewFile, false))) {
// m_robotContainer.createPath(null, null, false).write(outputFile);
LOGGER.log(Level.SEVERE, "Recorded path to {0}.", outputFile.getPath());
} else
LOGGER.log(Level.SEVERE, "Unable to record path to {0}", outputFile.getPath());
}
m_robotContainer.m_robotVisionOdometry.setLEDs(false);
}
@Override
@@ -204,11 +199,7 @@ public class Robot extends TimedRobot {
public void autonomousInit() {
LOGGER.fine("autonomousInit()");
selectedOdo = odoChooser.getSelected();
if (selectedOdo == null) {
selectedOdo = m_robotContainer.getOdometry();
}
m_robotContainer.resetOdometry(selectedOdo);
Robot.alliance = DriverStation.getAlliance();
m_autonomousCommand = m_robotContainer.getAutonomousCommand();
@@ -229,13 +220,8 @@ public class Robot extends TimedRobot {
@Override
public void teleopInit() {
LOGGER.fine("teleopInit()");
m_robotContainer.m_robotSwerveDrive.m_gyro.addYaw(-1 * m_robotContainer.m_robotSwerveDrive.m_gyro.getYaw());
selectedOdo = odoChooser.getSelected();
if (selectedOdo == null) {
selectedOdo = m_robotContainer.getOdometry();
}
m_robotContainer.resetOdometry(selectedOdo);
Robot.alliance = DriverStation.getAlliance();
// This makes sure that the autonomous stops running when
// teleop starts running. If you want the autonomous to
@@ -252,8 +238,7 @@ public class Robot extends TimedRobot {
* This function is called periodically during operator control.
*/
@Override
public void teleopPeriodic() {
}
public void teleopPeriodic() {}
@Override
public void testInit() {
+231 -69
View File
@@ -4,8 +4,12 @@
package frc4388.robot;
import java.util.ArrayList;
import java.util.Objects;
import java.util.logging.Logger;
import com.pathplanner.lib.PathPlanner;
import com.pathplanner.lib.PathPlannerTrajectory;
import com.pathplanner.lib.PathPlannerTrajectory.PathPlannerState;
import com.pathplanner.lib.commands.PPSwerveControllerCommand;
@@ -15,7 +19,6 @@ import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.wpilibj.GenericHID;
import edu.wpi.first.wpilibj.XboxController;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.InstantCommand;
import edu.wpi.first.wpilibj2.command.PrintCommand;
@@ -26,13 +29,22 @@ import frc4388.robot.Constants.OIConstants;
import frc4388.robot.Constants.StorageConstants;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.commands.PathRecorder;
// import frc4388.robot.commands.ButtonBoxCommands.TurretManual;
import frc4388.robot.commands.ExtenderIntakeCommands.ExtenderIntakeGroup;
import frc4388.robot.commands.ShooterCommands.Shoot;
import frc4388.robot.commands.ShooterCommands.TrackTarget;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Claws;
import frc4388.robot.subsystems.Climber;
import frc4388.robot.subsystems.Extender;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.Intake;
import frc4388.robot.subsystems.Serializer;
import frc4388.robot.subsystems.Storage;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
import frc4388.utility.controller.ButtonBox;
import frc4388.utility.controller.DeadbandedXboxController;
/**
@@ -44,26 +56,45 @@ import frc4388.utility.controller.DeadbandedXboxController;
*/
public class RobotContainer {
private static final Logger LOGGER = Logger.getLogger(RobotContainer.class.getSimpleName());
/* RobotMap */
// RobotMap
public final RobotMap m_robotMap = new RobotMap();
/* Subsystems */
public final Climber m_robotClimber = new Climber(m_robotMap.elbow);
public final Claws m_robotClaws = new Claws(m_robotMap.leftClaw, m_robotMap.rightClaw);
public final SwerveDrive m_robotSwerveDrive = new SwerveDrive(m_robotMap.leftFront, m_robotMap.leftBack, m_robotMap.rightFront, m_robotMap.rightBack, m_robotMap.gyro);
public final Serializer m_robotSerializer = new Serializer(m_robotMap.serializerBelt, /*m_robotMap.serializerShooterBelt,*/ m_robotMap.serializerBeam);
public final Intake m_robotIntake = new Intake(m_robotMap.intakeMotor, m_robotMap.extenderMotor, m_robotSerializer);
public final Intake m_robotIntake = new Intake(m_robotMap.intakeMotor);
public final Extender m_robotExtender = new Extender(m_robotMap.extenderMotor);
public final Storage m_robotStorage = new Storage(m_robotMap.storageMotor);
// private final LED m_robotLED = new LED(m_robotMap.LEDController);
public final BoomBoom m_robotBoomBoom = new BoomBoom(m_robotMap.shooterFalconLeft, m_robotMap.shooterFalconRight);
public final Hood m_robotHood = new Hood(m_robotMap.angleAdjusterMotor);
public final Turret m_robotTurret = new Turret(m_robotMap.shooterTurret);
// private final VisionOdometry m_robotVisionOdometry = new VisionOdometry(m_robotSwerveDrive, m_robotTurret);*/
/* Controllers */
private final XboxController m_driverXbox = new DeadbandedXboxController(OIConstants.XBOX_DRIVER_ID);
private final XboxController m_operatorXbox = new DeadbandedXboxController(OIConstants.XBOX_OPERATOR_ID);
public final VisionOdometry m_robotVisionOdometry = new VisionOdometry(m_robotSwerveDrive, m_robotTurret);
/* Autonomous */
private final PathRecorder m_pathChooser = new PathRecorder(m_robotSwerveDrive);
// Controllers
private final static XboxController m_driverXbox = new DeadbandedXboxController(OIConstants.XBOX_DRIVER_ID);
private final static XboxController m_operatorXbox = new DeadbandedXboxController(OIConstants.XBOX_OPERATOR_ID);
private final ButtonBox m_buttonBox = new ButtonBox(OIConstants.BUTTON_BOX_ID);
public static boolean softLimits = true;
// control mode switching
public static enum ControlMode { SHOOTER, CLIMBER };
public static ControlMode currentControlMode = ControlMode.SHOOTER;
// turret mode switching
private enum TurretMode { MANUAL, AUTONOMOUS };
private TurretMode currentTurretMode = TurretMode.MANUAL;
// climber mode switching
private enum ClimberMode { MANUAL, AUTONOMOUS };
private ClimberMode currentClimberMode = ClimberMode.MANUAL;
/**
* The container for the robot. Contains subsystems, OI devices, and commands.
@@ -71,36 +102,60 @@ public class RobotContainer {
public RobotContainer() {
configureButtonBindings();
/* Default Commands */
// Swerve Drive with Input
// Swerve Drive with Input
m_robotSwerveDrive.setDefaultCommand(
new RunCommand(() -> m_robotSwerveDrive.driveWithInput(
getDriverController().getLeftX(),
getDriverController().getLeftY(),
getDriverController().getRightX(),
getDriverController().getRightY(),
true),
m_robotSwerveDrive));
// Intake with Triggers
new RunCommand(() -> {
if (RobotContainer.currentControlMode.equals(ControlMode.SHOOTER)) {
m_robotSwerveDrive.driveWithInput( getDriverController().getLeftX(),
getDriverController().getLeftY(),
getDriverController().getRightX(),
getDriverController().getRightY(),
true); }
if (RobotContainer.currentControlMode.equals(ControlMode.CLIMBER)) {
m_robotSwerveDrive.driveWithInput( 0,
0,
0,
0,
true);
}}
, m_robotSwerveDrive).withName("Swerve driveWithInput defaultCommand"));
// Intake with Triggers
m_robotIntake.setDefaultCommand(
new RunCommand(() -> m_robotIntake.runWithTriggers(
getOperatorController().getLeftTriggerAxis(),
getOperatorController().getRightTriggerAxis()),
m_robotIntake));
// Storage Management
// m_robotStorage.setDefaultCommand(new RunCommand(() -> m_robotStorage.manageStorage(), m_robotStorage).withName("Storage manageStorage defaultCommand"));
// Serializer Management
// m_robotSerializer.setDefaultCommand(new RunCommand(() -> m_robotSerializer.setSerializer(0.8),//m_robotSerializer.setSerializerStateWithBeam(), m_robotSerializer).withName("Serializer setSerializerStateWithBeam defaultCommand"));
// Turret Manual
m_robotTurret.setDefaultCommand(new RunCommand(() -> m_robotTurret.runTurretWithInput(getOperatorController().getLeftX()), m_robotTurret));
m_robotHood.setDefaultCommand(new RunCommand(() -> m_robotHood.runHood(getOperatorController().getRightY() * 0.1), m_robotHood));
// m_robotTurret.setDefaultCommand(new AimToCenter(m_robotTurret, m_robotSwerveDrive, m_robotVisionOdometry));
m_robotIntake).withName("Intake runWithTriggers defaultCommand"));
m_robotBoomBoom.setDefaultCommand(new RunCommand(() -> m_robotBoomBoom.runDrumShooter(0.0), m_robotBoomBoom));
// continually sends updates to the Blinkin LED controller to keep the lights on
// m_robotLED.setDefaultCommand(new RunCommand(m_robotLED::updateLED, m_robotLED).withName("LED update defaultCommand"));
// Serializer Manual
m_robotSerializer.setDefaultCommand(
new RunCommand(() -> m_robotSerializer.setSerializer(getOperatorController().getLeftTriggerAxis() * 0.8),//m_robotSerializer.setSerializerStateWithBeam(),
m_robotSerializer).withName("Serializer setSerializerStateWithBeam defaultCommand"));
// Creates a button on the SmartDashboard that will record the path of the robot.
SmartDashboard.putData("Path Recording", m_pathChooser);
// Turret Manual
m_robotTurret.setDefaultCommand(
new RunCommand(() -> {
if (RobotContainer.currentControlMode.equals(ControlMode.SHOOTER)) { m_robotTurret.runTurretWithInput(getOperatorController().getLeftX()); }
if (RobotContainer.currentControlMode.equals(ControlMode.CLIMBER)) { m_robotTurret.runTurretWithInput(0); }
}, m_robotTurret));
// Hood Manual
m_robotHood.setDefaultCommand(
new RunCommand(() -> {
if (RobotContainer.currentControlMode.equals(ControlMode.SHOOTER)) { m_robotHood.runHood(getOperatorController().getRightY()); }
if (RobotContainer.currentControlMode.equals(ControlMode.CLIMBER)) { m_robotHood.runHood(0); }
}, m_robotHood));
//Climber Manual
m_robotClimber.setDefaultCommand(
new RunCommand(() -> {
if (RobotContainer.currentControlMode.equals(ControlMode.SHOOTER)) { m_robotClimber.setMotors(0.0); }
if (RobotContainer.currentControlMode.equals(ControlMode.CLIMBER)) { m_robotClimber.setMotors(getOperatorController().getRightY()); }
}, m_robotClimber));
// autoInit();
// recordInit();
}
/**
@@ -117,7 +172,7 @@ public class RobotContainer {
if (binding == XboxController.Button.kLeftBumper)
/* Left Bumper > Shift Down */ button.whenPressed(() -> m_robotSwerveDrive.highSpeed(false));
else if (binding == XboxController.Button.kRightBumper)
/* Right Bumper > Shift Up */ button.whenPressed(() -> m_robotSwerveDrive.highSpeed(true));
/* Right Bumper > Shift Up */ button.whenPressed(() -> m_robotSwerveDrive.highSpeed(false));
else if (binding == XboxController.Button.kLeftStick)
/* Left Stick > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kRightStick)
@@ -127,45 +182,133 @@ public class RobotContainer {
else if (binding == XboxController.Button.kB)
/* B > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kX)
/* X > TEMP */ button.whenPressed(() -> {
m_robotMap.leftFront.reset();
m_robotMap.rightFront.reset();
m_robotMap.leftBack.reset();
m_robotMap.rightBack.reset();
});
/* X > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kY)
/* Y > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kBack)
/* Back > Reset Odometry */ button.whenPressed(() -> resetOdometry(new Pose2d(0, 0, new Rotation2d(0))));
/* Back > Calibrate Odometry */ button.whenPressed(() -> resetOdometry(new Pose2d(0, 0, new Rotation2d(0))));
else if (binding == XboxController.Button.kStart)
/* Start > Reset Gyro */ button.whenPressed(m_robotSwerveDrive::resetGyro);
/* Start > Calibrate Odometry */ button.whenPressed(m_robotSwerveDrive::resetGyro);
/* ------------------------------------ Operator ------------------------------------ */
button = new JoystickButton(getDriverController(), binding.value);
if (binding == XboxController.Button.kLeftBumper)
/* Left Bumper > Storage Out */ button.whileHeld(() -> m_robotStorage.runStorage(-StorageConstants.STORAGE_SPEED), m_robotStorage)
.whenReleased(() -> m_robotStorage.runStorage(0.0), m_robotStorage);
/* Left Bumper > Storage In */
button.whileHeld(new RunCommand(() -> m_robotStorage.runStorage(StorageConstants.STORAGE_SPEED)))
.whenReleased(new RunCommand(() -> m_robotStorage.runStorage(0.0)));
else if (binding == XboxController.Button.kRightBumper)
/* Right Bumper > Storage In */ button.whileHeld(() -> m_robotStorage.runStorage(StorageConstants.STORAGE_SPEED), m_robotStorage)
.whenReleased(() -> m_robotStorage.runStorage(0.0), m_robotStorage);
/* Right Bumper > Storage Out */
button.whileHeld(new RunCommand(() -> m_robotStorage.runStorage(-StorageConstants.STORAGE_SPEED)))
.whenReleased(new RunCommand(() -> m_robotStorage.runStorage(0.0)));
else if (binding == XboxController.Button.kLeftStick)
/* Left Stick > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kRightStick)
/* Right Stick > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kA)
/* A > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
/* A > Spit Out Ball */
button.whileHeld(new RunCommand(m_robotTurret::gotoMidpoint, m_robotTurret))
.whileHeld(new RunCommand(() -> m_robotBoomBoom.runDrumShooter(0.25)));
else if (binding == XboxController.Button.kB)
/* B > Reset Hood*/ button.whenPressed(new InstantCommand(() -> m_robotHood.m_angleEncoder.setPosition(0)));
/* B > Toggle Claws */ button.whenPressed(new InstantCommand(m_robotClaws::toggleClaws, m_robotClaws));
else if (binding == XboxController.Button.kX)
/* X > Run Shooter */ button.whileHeld(() -> m_robotBoomBoom.runDrumShooter(0.3))
.whenReleased(() -> m_robotBoomBoom.runDrumShooter(0.0));
/* X > TEST BUTTON */ button.whileHeld(new TrackTarget(m_robotSwerveDrive, m_robotTurret, m_robotBoomBoom, m_robotHood, m_robotVisionOdometry));
///* X > Toggles extender in and out */ button.whenPressed(new ExtenderIntakeGroup(m_robotIntake, m_robotExtender));
else if (binding == XboxController.Button.kY)
/* Y > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
/* Y > TEST BUTTON */ button.whenPressed(new Shoot(m_robotSwerveDrive, m_robotBoomBoom, m_robotTurret, m_robotHood, m_robotVisionOdometry, false, false));
///* Y > Full aim command */ button.whileHeld(new Seek(m_robotSwerveDrive, m_robotBoomBoom, m_robotTurret, m_robotHood, m_robotVisionOdometry));
else if (binding == XboxController.Button.kBack)
/* Back > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == XboxController.Button.kStart)
/* Start > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
}
// Iterate over all button box buttons.
for (ButtonBox.Button binding : ButtonBox.Button.values()) {
/* ---------------------------------- Button Box ---------------------------------- */
JoystickButton button = new JoystickButton(getButtonBox(), binding.value);
if (binding == ButtonBox.Button.kRightSwitch)
/* Right Switch > Unbound */ button.whenPressed(new PrintCommand("Unbound"));
else if (binding == ButtonBox.Button.kMiddleSwitch)
/* Middle Switch > Climber and Shooter mode switching */
button.whenPressed(new InstantCommand(() -> currentControlMode = ControlMode.CLIMBER))
.whenReleased(new InstantCommand(() -> currentControlMode = ControlMode.SHOOTER));
else if (binding == ButtonBox.Button.kLeftSwitch)
/* Left Switch > Disables soft limits on press, release resets encoders (all for turret, hood, climber, and extender) */
button.whenPressed(new InstantCommand(() -> m_robotTurret.setTurretSoftLimits(false), m_robotTurret))
.whenPressed(new InstantCommand(() -> m_robotTurret.calibrationSpeed = 0.3, m_robotTurret))
.whenPressed(new InstantCommand(() -> m_robotHood.setHoodSoftLimits(false), m_robotHood))
.whenPressed(new InstantCommand(() -> m_robotHood.calibrationSpeed = 0.3, m_robotHood))
.whenPressed(new InstantCommand(() -> m_robotExtender.setExtenderSoftLimits(false), m_robotExtender))
.whenReleased(new InstantCommand(() -> m_robotTurret.setTurretSoftLimits(true), m_robotTurret))
.whenReleased(new InstantCommand(() -> m_robotTurret.calibrationSpeed = 1.0, m_robotTurret))
.whenReleased(new InstantCommand(() -> m_robotHood.setHoodSoftLimits(true), m_robotHood))
.whenReleased(new InstantCommand(() -> m_robotHood.calibrationSpeed = 1.0, m_robotHood))
.whenReleased(new InstantCommand(() -> m_robotExtender.setExtenderSoftLimits(true), m_robotExtender))
.whenReleased(new InstantCommand(() -> m_robotTurret.m_boomBoomRotateEncoder.setPosition(0), m_robotTurret))
.whenReleased(new InstantCommand(() -> m_robotHood.m_angleEncoder.setPosition(0), m_robotHood))
.whenReleased(new InstantCommand(() -> m_robotExtender.setEncoder(0), m_robotExtender))
.whenReleased(new InstantCommand(() -> ExtenderIntakeGroup.setDirectionToOut(), m_robotIntake, m_robotExtender))
.whenReleased(new InstantCommand(() -> m_robotClimber.setEncoders(0), m_robotClimber));
else if (binding == ButtonBox.Button.kRightButton)
/* Right Button > Extender Out */
button.whileHeld(new RunCommand(() -> m_robotExtender.runExtender(1.0), m_robotExtender))
.whenReleased(new RunCommand(() -> m_robotExtender.runExtender(0.0), m_robotExtender));
else if (binding == ButtonBox.Button.kLeftButton)
/* Left Button > Extender In */
button.whileHeld(new RunCommand(() -> m_robotExtender.runExtender(-1.0), m_robotExtender))
.whenReleased(new RunCommand(() -> m_robotExtender.runExtender(0.0), m_robotExtender));
}
}
/**
* Generate autonomous
* @param maxVel max velocity for the path (null to override default value of 5.0)
* @param maxAccel max acceleration for the path (null to override default value of 5.0)
* @param inputs strings (paths) or commands you want to run (in order)
* @return array of commands
*/
public Command[] buildAuto(Double maxVel, Double maxAccel, Object... inputs) {
maxVel = Objects.requireNonNullElse(maxVel, SwerveDriveConstants.PATH_MAX_VELOCITY);
maxAccel = Objects.requireNonNullElse(maxAccel, SwerveDriveConstants.PATH_MAX_ACCELERATION);
ArrayList<Command> commands = new ArrayList<Command>();
PIDController xController = SwerveDriveConstants.X_CONTROLLER;
PIDController yController = SwerveDriveConstants.Y_CONTROLLER;
ProfiledPIDController thetaController = SwerveDriveConstants.THETA_CONTROLLER;
thetaController.enableContinuousInput(-Math.PI, Math.PI);
// parse input
for (int i=0; i<inputs.length; i++) {
if (inputs[i] instanceof String) {
PathPlannerTrajectory traj = PathPlanner.loadPath(inputs[i].toString(), maxVel, maxAccel);
PathPlannerState initState = (PathPlannerState) traj.sample(0);
commands.add(new InstantCommand(() -> m_robotSwerveDrive.resetOdometry(new Pose2d(initState.poseMeters.getTranslation(), initState.holonomicRotation))));
commands.add(new PPSwerveControllerCommand(
traj,
m_robotSwerveDrive::getOdometry,
m_robotSwerveDrive.m_kinematics,
xController,
yController,
thetaController,
m_robotSwerveDrive::setModuleStates,
m_robotSwerveDrive));
}
if (inputs[i] instanceof Command) {
commands.add((Command) inputs[i]);
}
}
commands.add(new InstantCommand(() -> m_robotSwerveDrive.stopModules()));
Command[] ret = new Command[commands.size()];
ret = commands.toArray(ret);
return ret;
}
/**
@@ -174,29 +317,40 @@ public class RobotContainer {
* @return the command to run in autonomous
*/
public Command getAutonomousCommand() {
if (m_pathChooser.getPath() != null) {
PIDController xController = SwerveDriveConstants.X_CONTROLLER;
PIDController yController = SwerveDriveConstants.Y_CONTROLLER;
ProfiledPIDController thetaController = SwerveDriveConstants.THETA_CONTROLLER;
thetaController.enableContinuousInput(-Math.PI, Math.PI);
// if (loadedPathTrajectory != null) {
// PIDController xController = SwerveDriveConstants.X_CONTROLLER;
// PIDController yController = SwerveDriveConstants.Y_CONTROLLER;
// ProfiledPIDController thetaController = SwerveDriveConstants.THETA_CONTROLLER;
// thetaController.enableContinuousInput(-Math.PI, Math.PI);
PathPlannerState initialState = m_pathChooser.getPath().getInitialState();
Pose2d initialPosition = new Pose2d(initialState.poseMeters.getTranslation(), initialState.holonomicRotation);
return new SequentialCommandGroup(
new InstantCommand(m_robotSwerveDrive.m_gyro::reset),
new InstantCommand(() -> m_robotSwerveDrive.resetOdometry(initialPosition)),
new PPSwerveControllerCommand(m_pathChooser.getPath(), m_robotSwerveDrive::getOdometry,
m_robotSwerveDrive.m_kinematics, xController, yController, thetaController,
m_robotSwerveDrive::setModuleStates, m_robotSwerveDrive),
new InstantCommand(m_robotSwerveDrive::stopModules)).withName("Run Autonomous Path");
} else {
LOGGER.severe("No auto selected.");
return new RunCommand(() -> {
}).withName("No Autonomous Path");
}
// PathPlannerState initialState = loadedPathTrajectory.getInitialState();
// Pose2d initialPosition = new Pose2d(initialState.poseMeters.getTranslation(), initialState.holonomicRotation);
// return new SequentialCommandGroup(
// new InstantCommand(m_robotSwerveDrive.m_gyro::reset),
// new InstantCommand(() -> m_robotSwerveDrive.resetOdometry(initialPosition)),
// new PPSwerveControllerCommand(loadedPathTrajectory, m_robotSwerveDrive::getOdometry,
// m_robotSwerveDrive.m_kinematics, xController, yController, thetaController,
// m_robotSwerveDrive::setModuleStates, m_robotSwerveDrive),
// new InstantCommand(m_robotSwerveDrive::stopModules)).withName("Run Autonomous Path");
// } else {
// LOGGER.severe("No auto selected.");
// return new RunCommand(() -> {
// }).withName("No Autonomous Path");
// }
// ! this will run each of the specified PathPlanner paths in sequence.
// * return new SequentialCommandGroup(buildAuto(5.0, 5.0, "Path1", "Path2", "Path3"));
// ! this will run each of the specified PathPlanner paths in sequence, while simultaneously running the intake throughout all the paths.
// * return new ParallelCommandGroup(buildAuto(null,
// * null,
// * new SequentialCommandGroup(buildAuto(5.0, 5.0, "Path1", "Path2", "Path3")),
// * new RunCommand(() -> m_robotIntake.runAtOutput(0.5))));
return new SequentialCommandGroup(buildAuto(1.0, 1.0, new InstantCommand(() -> m_robotSwerveDrive.m_gyro.reset()), "Move Forward")); // test command
}
public XboxController getDriverController() {
public static XboxController getDriverController() {
return m_driverXbox;
}
@@ -204,6 +358,14 @@ public class RobotContainer {
return m_operatorXbox;
}
public ButtonBox getButtonBox() {
return m_buttonBox;
}
public static void setSoftLimits(boolean set) {
softLimits = set;
}
/**
* Get odometry.
*
+185 -117
View File
@@ -4,20 +4,29 @@
package frc4388.robot;
import com.ctre.phoenix.motorcontrol.LimitSwitchNormal;
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
import com.ctre.phoenix.sensors.CANCoder;
import com.ctre.phoenix.sensors.WPI_Pigeon2;
import com.ctre.phoenix.sensors.WPI_PigeonIMU;
import com.ctre.phoenix.motorcontrol.NeutralMode;
import com.ctre.phoenix.motorcontrol.RemoteSensorSource;
import com.ctre.phoenix.motorcontrol.StatorCurrentLimitConfiguration;
import com.ctre.phoenix.motorcontrol.SupplyCurrentLimitConfiguration;
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
import com.ctre.phoenix.sensors.CANCoder;
import com.ctre.phoenix.sensors.WPI_PigeonIMU;
import com.ctre.phoenix.sensors.WPI_Pigeon2;
import com.revrobotics.CANSparkMax;
import com.revrobotics.CANSparkMax.IdleMode;
import com.revrobotics.CANSparkMaxLowLevel.MotorType;
import edu.wpi.first.wpilibj.CAN;
import edu.wpi.first.networktables.NetworkTableType;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Servo;
import edu.wpi.first.wpilibj.motorcontrol.Spark;
import frc4388.robot.Constants.ClimberConstants;
import frc4388.robot.Constants.ClawConstants;
import frc4388.robot.Constants.IntakeConstants;
import frc4388.robot.Constants.LEDConstants;
import frc4388.robot.Constants.ShooterConstants;
@@ -36,6 +45,11 @@ public class RobotMap {
// configureLEDMotorControllers();
configureSwerveMotorControllers();
configureShooterMotorControllers();
configureIntakeMotors();
configureExtenderMotors();
configureSerializerMotors();
configureStorageMotors();
configureClimberMotors();
}
/* LED Subsystem */
@@ -43,30 +57,30 @@ public class RobotMap {
// void configureLEDMotorControllers() {}
/* Swerve Subsystem */
public final WPI_TalonFX leftFrontSteerMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_FRONT_STEER_CAN_ID);
public final WPI_TalonFX leftFrontWheelMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_FRONT_WHEEL_CAN_ID);
public final WPI_TalonFX rightFrontSteerMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_FRONT_STEER_CAN_ID);
public final WPI_TalonFX rightFrontWheelMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_FRONT_WHEEL_CAN_ID);
public final WPI_TalonFX leftBackSteerMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_BACK_STEER_CAN_ID);
public final WPI_TalonFX leftBackWheelMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_BACK_WHEEL_CAN_ID);
public final WPI_TalonFX rightBackSteerMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_BACK_STEER_CAN_ID);
public final WPI_TalonFX rightBackWheelMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_BACK_WHEEL_CAN_ID);
public final CANCoder leftFrontEncoder = new CANCoder(SwerveDriveConstants.LEFT_FRONT_STEER_CAN_ENCODER_ID);
public final CANCoder rightFrontEncoder = new CANCoder(SwerveDriveConstants.RIGHT_FRONT_STEER_CAN_ENCODER_ID);
public final CANCoder leftBackEncoder = new CANCoder(SwerveDriveConstants.LEFT_BACK_STEER_CAN_ENCODER_ID);
public final CANCoder rightBackEncoder = new CANCoder(SwerveDriveConstants.RIGHT_BACK_STEER_CAN_ENCODER_ID);
/* Swerve Subsystem */
public final WPI_TalonFX leftFrontSteerMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_FRONT_STEER_CAN_ID);
public final WPI_TalonFX leftFrontWheelMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_FRONT_WHEEL_CAN_ID);
public final WPI_TalonFX rightFrontSteerMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_FRONT_STEER_CAN_ID);
public final WPI_TalonFX rightFrontWheelMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_FRONT_WHEEL_CAN_ID);
public final WPI_TalonFX leftBackSteerMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_BACK_STEER_CAN_ID);
public final WPI_TalonFX leftBackWheelMotor = new WPI_TalonFX(SwerveDriveConstants.LEFT_BACK_WHEEL_CAN_ID);
public final WPI_TalonFX rightBackSteerMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_BACK_STEER_CAN_ID);
public final WPI_TalonFX rightBackWheelMotor = new WPI_TalonFX(SwerveDriveConstants.RIGHT_BACK_WHEEL_CAN_ID);
public final CANCoder leftFrontEncoder = new CANCoder(SwerveDriveConstants.LEFT_FRONT_STEER_CAN_ENCODER_ID);
public final CANCoder rightFrontEncoder = new CANCoder(SwerveDriveConstants.RIGHT_FRONT_STEER_CAN_ENCODER_ID);
public final CANCoder leftBackEncoder = new CANCoder(SwerveDriveConstants.LEFT_BACK_STEER_CAN_ENCODER_ID);
public final CANCoder rightBackEncoder = new CANCoder(SwerveDriveConstants.RIGHT_BACK_STEER_CAN_ENCODER_ID);
public final WPI_PigeonIMU gyro = new WPI_PigeonIMU(SwerveDriveConstants.GYRO_ID);
public final WPI_Pigeon2 gyro = new WPI_Pigeon2(SwerveDriveConstants.GYRO_ID);
public SwerveModule leftFront;
public SwerveModule leftBack;
public SwerveModule rightFront;
public SwerveModule rightBack;
void configureSwerveMotorControllers() {
public SwerveModule leftFront;
public SwerveModule leftBack;
public SwerveModule rightFront;
public SwerveModule rightBack;
void configureSwerveMotorControllers() {
leftFrontSteerMotor.configFactoryDefault();
leftFrontWheelMotor.configFactoryDefault();
rightFrontSteerMotor.configFactoryDefault();
@@ -75,106 +89,139 @@ public class RobotMap {
leftBackWheelMotor.configFactoryDefault();
rightBackSteerMotor.configFactoryDefault();
rightBackWheelMotor.configFactoryDefault();
leftFrontSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftFrontWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightFrontSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightFrontWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
leftBackSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
leftBackWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightBackSteerMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightBackWheelMotor.configOpenloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftFrontWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
leftFrontWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftFrontSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
leftFrontSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightFrontSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
rightFrontWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackSteerMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackWheelMotor.configClosedloopRamp(SwerveDriveConstants.OPEN_LOOP_RAMP_RATE,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftFrontWheelMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftFrontSteerMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontSteerMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontWheelMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackSteerMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackWheelMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackSteerMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackWheelMotor.configNeutralDeadband(SwerveDriveConstants.NEUTRAL_DEADBAND,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
NeutralMode mode = NeutralMode.Coast;
leftFrontSteerMotor.setNeutralMode(mode);
leftFrontSteerMotor.setNeutralMode(NeutralMode.Brake);
leftFrontWheelMotor.setNeutralMode(mode);// Coast
rightFrontSteerMotor.setNeutralMode(mode);
rightFrontSteerMotor.setNeutralMode(NeutralMode.Brake);
rightFrontWheelMotor.setNeutralMode(mode);// Coast
leftBackSteerMotor.setNeutralMode(mode);
leftBackSteerMotor.setNeutralMode(NeutralMode.Brake);
leftBackWheelMotor.setNeutralMode(mode);// Coast
rightBackSteerMotor.setNeutralMode(mode);
rightBackSteerMotor.setNeutralMode(NeutralMode.Brake);
rightBackWheelMotor.setNeutralMode(mode);// Coast
// current limits
leftFrontSteerMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_STEER);
rightFrontSteerMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_STEER);
leftBackSteerMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_STEER);
rightBackSteerMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_STEER);
leftFrontWheelMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_WHEEL);
rightFrontWheelMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_WHEEL);
leftBackWheelMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_WHEEL);
rightBackWheelMotor.configSupplyCurrentLimit(SwerveDriveConstants.SUPPLY_CURRENT_LIMIT_CONFIG_WHEEL);
leftFrontSteerMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_STEER);
rightFrontSteerMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_STEER);
leftBackSteerMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_STEER);
rightBackSteerMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_STEER);
leftFrontWheelMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_WHEEL);
rightFrontWheelMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_WHEEL);
leftBackWheelMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_WHEEL);
rightBackWheelMotor.configStatorCurrentLimit(SwerveDriveConstants.STATOR_CURRENT_LIMIT_CONFIG_WHEEL);
leftFront = new SwerveModule(leftFrontWheelMotor, leftFrontSteerMotor, leftFrontEncoder,
SwerveDriveConstants.LEFT_FRONT_ENCODER_OFFSET);
SwerveDriveConstants.LEFT_FRONT_ENCODER_OFFSET);
leftBack = new SwerveModule(leftBackWheelMotor, leftBackSteerMotor, leftBackEncoder,
SwerveDriveConstants.LEFT_BACK_ENCODER_OFFSET);
SwerveDriveConstants.LEFT_BACK_ENCODER_OFFSET);
rightFront = new SwerveModule(rightFrontWheelMotor, rightFrontSteerMotor, rightFrontEncoder,
SwerveDriveConstants.RIGHT_FRONT_ENCODER_OFFSET);
SwerveDriveConstants.RIGHT_FRONT_ENCODER_OFFSET);
rightBack = new SwerveModule(rightBackWheelMotor, rightBackSteerMotor, rightBackEncoder,
SwerveDriveConstants.RIGHT_BACK_ENCODER_OFFSET);
SwerveDriveConstants.RIGHT_BACK_ENCODER_OFFSET);
// config cancoder as remote encoder for swerve steer motors
leftFrontSteerMotor.configRemoteFeedbackFilter(leftFrontEncoder.getDeviceID(),
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
leftBackSteerMotor.configRemoteFeedbackFilter(leftBackEncoder.getDeviceID(),
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightFrontSteerMotor.configRemoteFeedbackFilter(rightFrontEncoder.getDeviceID(),
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
rightBackSteerMotor.configRemoteFeedbackFilter(rightBackEncoder.getDeviceID(),
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
}
RemoteSensorSource.CANCoder, SwerveDriveConstants.REMOTE_0,
SwerveDriveConstants.SWERVE_TIMEOUT_MS);
}
// Shooter Config
/* Boom Boom Subsystem */
public final WPI_TalonFX shooterFalconLeft = new WPI_TalonFX(ShooterConstants.SHOOTER_FALCON_LEFT_CAN_ID);
public final WPI_TalonFX shooterFalconRight = new WPI_TalonFX(ShooterConstants.SHOOTER_FALCON_RIGHT_CAN_ID);
/* Climb Subsystem */
public final WPI_TalonFX elbow = new WPI_TalonFX(ClimberConstants.ELBOW_ID); // TODO
// turret subsystem
public final CANSparkMax shooterTurret = new CANSparkMax(ShooterConstants.TURRET_MOTOR_CAN_ID, MotorType.kBrushless);
private void configureClimberMotors() {
elbow.configFactoryDefault();
elbow.setNeutralMode(NeutralMode.Brake);
}
/* Hooks Subsystem */
public final Servo leftClaw = new Servo(1);
public final Servo rightClaw = new Servo(2);
// hood subsystem
public CANSparkMax angleAdjusterMotor = new CANSparkMax(ShooterConstants.SHOOTER_ANGLE_ADJUST_ID, MotorType.kBrushless);
// Shooter Config
/* Boom Boom Subsystem */
public final WPI_TalonFX shooterFalconLeft = new WPI_TalonFX(ShooterConstants.SHOOTER_FALCON_LEFT_CAN_ID);
public final WPI_TalonFX shooterFalconRight = new WPI_TalonFX(ShooterConstants.SHOOTER_FALCON_RIGHT_CAN_ID);
// Create motor CANSparkMax
void configureShooterMotorControllers() {
// turret subsystem
public final CANSparkMax shooterTurret = new CANSparkMax(ShooterConstants.TURRET_MOTOR_CAN_ID, MotorType.kBrushless);
// hood subsystem
public final CANSparkMax angleAdjusterMotor = new CANSparkMax(ShooterConstants.SHOOTER_ANGLE_ADJUST_ID, MotorType.kBrushless);
// Create motor CANSparkMax
void configureShooterMotorControllers() {
// LEFT FALCON
shooterFalconLeft.configFactoryDefault();
shooterFalconLeft.setNeutralMode(NeutralMode.Coast);
@@ -183,55 +230,76 @@ public class RobotMap {
shooterFalconLeft.configClosedloopRamp(0.75, ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.configPeakOutputReverse(0, ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.setSelectedSensorPosition(0, ShooterConstants.SHOOTER_PID_LOOP_IDX,
ShooterConstants.SHOOTER_TIMEOUT_MS);
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.configClosedLoopPeriod(0, ShooterConstants.CLOSED_LOOP_TIME_MS,
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.configSupplyCurrentLimit(ShooterConstants.SUPPLY_CURRENT_LIMIT_CONFIG,
ShooterConstants.SHOOTER_TIMEOUT_MS);
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.configSupplyCurrentLimit(ShooterConstants.SUPPLY_CURRENT_LIMIT_CONFIG_SHOOTER,
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconLeft.configStatorCurrentLimit(ShooterConstants.STATOR_CURRENT_LIMIT_CONFIG_SHOOTER,
ShooterConstants.SHOOTER_TIMEOUT_MS);
// RIGHT FALCON
shooterFalconRight.setInverted(false);
shooterFalconRight.setNeutralMode(NeutralMode.Coast);
shooterFalconRight.configFactoryDefault();
shooterFalconRight.setNeutralMode(NeutralMode.Coast);
shooterFalconRight.setInverted(false);
shooterFalconRight.configOpenloopRamp(1, ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.configClosedloopRamp(0.75, ShooterConstants.SHOOTER_TIMEOUT_MS);
// m_shooterFalconRight.configPeakOutputForward(0,
// ShooterConstants.SHOOTER_TIMEOUT_MS);(comment it in if necessary)
shooterFalconRight.setSelectedSensorPosition(0, ShooterConstants.SHOOTER_PID_LOOP_IDX,
ShooterConstants.SHOOTER_TIMEOUT_MS);
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.configClosedLoopPeriod(0, ShooterConstants.CLOSED_LOOP_TIME_MS,
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.configSupplyCurrentLimit(ShooterConstants.SUPPLY_CURRENT_LIMIT_CONFIG,
ShooterConstants.SHOOTER_TIMEOUT_MS);
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.configSupplyCurrentLimit(ShooterConstants.SUPPLY_CURRENT_LIMIT_CONFIG_SHOOTER,
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.configStatorCurrentLimit(ShooterConstants.STATOR_CURRENT_LIMIT_CONFIG_SHOOTER,
ShooterConstants.SHOOTER_TIMEOUT_MS);
shooterFalconRight.follow(shooterFalconLeft);
// /* Turret Subsytem */
// shooterFalconRight.configStatorCurrentLimit(new
// StatorCurrentLimitConfiguration(true, 6, 9, 4.2)); // TODO: dont pull numbers
// out of our ass anymore
// shooterFalconLeft.configSupplyCurrentLimit(new
// SupplyCurrentLimitConfiguration(true, 12, 13, 0.4)); // TODO: dont pull
// numbers out of our ass anymore
// turret
shooterTurret.restoreFactoryDefaults();
shooterTurret.setIdleMode(IdleMode.kBrake);
shooterTurret.setInverted(true);
// hood subsystem
angleAdjusterMotor.restoreFactoryDefaults();
angleAdjusterMotor.setIdleMode(IdleMode.kBrake);
}
/* Serializer Subsystem */
public final CANSparkMax serializerBelt = new CANSparkMax(SerializerConstants.SERIALIZER_BELT, MotorType.kBrushless);
// public final CANSparkMax serializerShooterBelt = new CANSparkMax(SerializerConstants.SERIALIZER_SHOOTER_BELT, MotorType.kBrushless);
public final DigitalInput serializerBeam = new DigitalInput(SerializerConstants.SERIALIZER_BELT_BEAM);
/* Intake Subsytem */
public final WPI_TalonFX intakeMotor = new WPI_TalonFX(IntakeConstants.INTAKE_MOTOR);
public final CANSparkMax extenderMotor = new CANSparkMax(IntakeConstants.EXTENDER_MOTOR, MotorType.kBrushless);
/* Storage Subsystem */
public final CANSparkMax storageMotor = new CANSparkMax(StorageConstants.STORAGE_CAN_ID, MotorType.kBrushless);
// public final DigitalInput beamShooter = new DigitalInput(StorageConstants.BEAM_SENSOR_SHOOTER);
// public final DigitalInput beamIntake = new DigitalInput(StorageConstants.BEAM_SENSOR_INTAKE);
angleAdjusterMotor.setInverted(true);
}
/* Serializer Subsystem */
public final CANSparkMax serializerBelt = new CANSparkMax(SerializerConstants.SERIALIZER_BELT, MotorType.kBrushless);
public final DigitalInput serializerBeam = new DigitalInput(SerializerConstants.SERIALIZER_BELT_BEAM);
/* Intake Subsystem */
public final WPI_TalonFX intakeMotor = new WPI_TalonFX(IntakeConstants.INTAKE_MOTOR);
public final CANSparkMax extenderMotor = new CANSparkMax(IntakeConstants.EXTENDER_MOTOR, MotorType.kBrushless);
void configureIntakeMotors() {
intakeMotor.configFactoryDefault();
intakeMotor.setInverted(false);
intakeMotor.setNeutralMode(NeutralMode.Coast);
intakeMotor.configSupplyCurrentLimit(IntakeConstants.SUPPLY_CURRENT_LIMIT_CONFIG_INTAKE);
intakeMotor.configStatorCurrentLimit(IntakeConstants.STATOR_CURRENT_LIMIT_CONFIG_INTAKE);
}
void configureExtenderMotors() {
extenderMotor.restoreFactoryDefaults();
extenderMotor.setInverted(true);
extenderMotor.setIdleMode(IdleMode.kBrake);
}
void configureSerializerMotors() {
serializerBelt.restoreFactoryDefaults();
}
/* Storage Subsystem */
public final CANSparkMax storageMotor = new CANSparkMax(StorageConstants.STORAGE_CAN_ID, MotorType.kBrushless);
void configureStorageMotors() {
storageMotor.restoreFactoryDefaults();
}
}
@@ -1,85 +0,0 @@
// 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.
package frc4388.robot.commands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.Constants.VisionConstants;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
public class AimToCenter extends CommandBase {
/** Creates a new AimWithOdometry. */
Turret m_turret;
SwerveDrive m_drive;
VisionOdometry m_visionOdometry;
// use odometry to find x and y later
double x;
double y;
double m_targetAngle;
// public static Gains m_aimGains;
public AimToCenter(Turret turret, SwerveDrive drive, VisionOdometry visionOdometry) {
// Use addRequirements() here to declare subsystem dependencies.
m_turret = turret;
m_drive = drive;
m_visionOdometry = visionOdometry;
addRequirements(m_turret, m_drive, m_visionOdometry);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
x = 0;
y = 0;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
m_targetAngle = angleToCenter(x, y, m_drive.getRegGyro().getDegrees());
m_turret.runshooterRotatePID(m_targetAngle);
// Check if limelight is within range (comment out to disable vision odo)
if (Math.abs(m_turret.getBoomBoomAngleDegrees() - m_targetAngle) < VisionConstants.RANGE){
m_visionOdometry.updateOdometryWithVision();
m_visionOdometry.setLEDs(true);
}
else{
m_visionOdometry.setLEDs(false);
}
}
public static double angleToCenter(double x, double y, double gyro) {
double angle = ((Math.atan2(y, x) * (180./Math.PI) - gyro) + 180. + 360.) % 360.; // Finds the angle between the gyro of the robot and the target (positive x is gyro 0)
return angle;
}
/**
* Checks if in deadzone.
* @param angle Angle to check.
* @return True if in deadzone.
*/
public static boolean isDeadzone(double angle) {
if (angle == Double.NaN) {
return false;
}
return !((ShooterConstants.TURRET_REVERSE_LIMIT <= angle) && (angle <= ShooterConstants.TURRET_FORWARD_LIMIT));
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
}
}
@@ -0,0 +1,61 @@
// 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.
package frc4388.robot.commands.ButtonBoxCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
public class RunMiddleSwitch extends CommandBase {
private static boolean manual = false;
private boolean newManual = false;
private boolean changes = false;
/** Creates a new RunMiddleSwitch. */
public RunMiddleSwitch() {
// Use addRequirements() here to declare subsystem dependencies.
}
// Called when the command is initially scheduled.
@Override
public void initialize() {}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
changes = (newManual == manual);
if (manual) {
printManual();
} else {
printNormal();
}
newManual = manual;
}
public void printNormal(){
System.out.println("Normal");
}
public void printManual(){
System.out.println("Manual");
}
public static void setManual(boolean set)
{
manual = set;
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return changes;
}
}
@@ -0,0 +1,62 @@
// 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.
package frc4388.robot.commands.ButtonBoxCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.subsystems.Turret;
public class TurretManual extends CommandBase {
// subsystems
private Turret turret;
// booleans
private static boolean manual = false;
private boolean newManual = false;
private boolean changes = false;
/** Creates a new TurretManual. */
public TurretManual(Turret turret) {
// Use addRequirements() here to declare subsystem dependencies.
this.turret = turret;
addRequirements(this.turret);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
changes = (newManual != manual);
if (manual) {
System.out.println("Manual Turret"); // TODO: turret manual controls
} else {
System.out.println("Auto Turret"); // TODO: turret auto controls;
}
newManual = manual;
}
public static void setManual(boolean set)
{
manual = set;
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
manual = !manual;
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return changes;
}
}
@@ -0,0 +1,53 @@
// 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.
package frc4388.robot.commands.ClimberCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ClawConstants;
import frc4388.robot.subsystems.Claws;
public class RunClaw extends CommandBase {
// parameters
public Claws m_claws;
public Claws.ClawType clawType;
public boolean open;
/**
* Creates a new RunClaw, which runs a claw.
* @param sClaws Claws subsystem.
* @param which Which claw to run.
* @param open Whether to open or close the claw.
*/
public RunClaw(Claws sClaws, Claws.ClawType which, boolean open) {
// Use addRequirements() here to declare subsystem dependencies.
m_claws = sClaws;
clawType = which;
this.open = open;
addRequirements(m_claws);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
// m_claws.runClaw(clawType, open);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
// return m_claws.checkSwitchAndCurrent(clawType);
return false; // TODO: real return
}
}
@@ -0,0 +1,74 @@
// 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.
package frc4388.robot.commands.ClimberCommands;
import org.opencv.core.Point;
import edu.wpi.first.wpilibj.drive.Vector2d;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ClimberConstants;
import frc4388.robot.subsystems.Claws;
import frc4388.robot.subsystems.Climber;
import frc4388.utility.Vector2D;
public class RunClimberPath extends CommandBase {
Climber climber;
Claws claws;
Point[] path;
int nextIndex;
boolean endPath;
/** Creates a new RunClimberPath. */
public RunClimberPath(Climber _climber, Claws _claws, Point[] _path) {
path = _path;
endPath = false;
climber = _climber;
claws = _claws;
addRequirements(climber, claws);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
claws.setOpen(true);
nextIndex = 0;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
// if(!claws.fullyOpen())
return;
// Point climberPos = ClimberRewrite.getClimberPosition(climber.getJointAngles());
// Vector2D dir = new Vector2D(path[nextIndex]);
// dir.subtract(new Vector2D(climberPos));
// if(!endPath && dir.magnitude() < ClimberConstants.THRESHOLD && nextIndex < path.length-1)
// nextIndex++;
// else if(!endPath && dir.magnitude() < ClimberConstants.THRESHOLD) {
// endPath = true;
// claws.setOpen(false);
// } else if(!endPath) {
// dir = dir.unit();
// climber.controlWithInput(dir.x, dir.y);
// }
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
// return endPath && claws.fullyClosed();
return false;
}
}
@@ -0,0 +1,34 @@
// 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.
package frc4388.robot.commands.ExtenderIntakeCommands;
import edu.wpi.first.wpilibj2.command.ParallelRaceGroup;
import frc4388.robot.subsystems.Extender;
import frc4388.robot.subsystems.Intake;
// NOTE: Consider using this command inline, rather than writing a subclass. For more
// information, see:
// https://docs.wpilib.org/en/stable/docs/software/commandbased/convenience-features.html
public class ExtenderIntakeGroup extends ParallelRaceGroup {
public static int direction;
/** Creates a new RunExtenderAndIntake. */
public ExtenderIntakeGroup(Intake intake, Extender extender) {
// Add your commands in the addCommands() call, e.g.
// addCommands(new FooCommand(), new BarCommand());
ExtenderIntakeGroup.direction = 1; // assume extender starts retracted completely
addCommands(new RunIntakeConditionally(intake), new RunExtender(extender));
}
public static void setDirectionToOut() {
ExtenderIntakeGroup.direction = 1;
}
public static void changeDirection() { // Never implemented?
ExtenderIntakeGroup.direction *= -1;
}
}
@@ -0,0 +1,66 @@
// 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.
package frc4388.robot.commands.ExtenderIntakeCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ExtenderConstants;
import frc4388.robot.subsystems.Extender;
import frc4388.robot.subsystems.Intake;
public class RunExtender extends CommandBase {
private Extender extender;
private double error;
private double tolerance;
/** Creates a new RunExtender. */
public RunExtender(Extender extender) {
// Use addRequirements() here to declare subsystem dependencies.
this.extender = extender;
updateError();
tolerance = 5.0;
addRequirements(this.extender);
}
public void updateError() {
if (ExtenderIntakeGroup.direction > 0) {
this.error = Math.abs(this.extender.getPosition() - ExtenderConstants.EXTENDER_FORWARD_LIMIT);
} else {
this.error = Math.abs(this.extender.getPosition() - ExtenderConstants.EXTENDER_REVERSE_LIMIT);
}
}
// Called when the command is initially scheduled.
@Override
public void initialize() {}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
System.out.println("RunExtender is working");
this.extender.runExtender(ExtenderIntakeGroup.direction * 1.0);
updateError();
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
ExtenderIntakeGroup.changeDirection();
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
if (error < tolerance) {
System.out.println("RunExtender finished");
this.extender.runExtender(0.0);
return true;
}
return false;
}
}
@@ -0,0 +1,47 @@
// 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.
package frc4388.robot.commands.ExtenderIntakeCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.IntakeConstants;
import frc4388.robot.subsystems.Intake;
public class RunIntakeConditionally extends CommandBase {
private Intake intake;
/** Creates a new RunIntakeConditionally. */
public RunIntakeConditionally(Intake intake) {
// Use addRequirements() here to declare subsystem dependencies.
this.intake = intake;
addRequirements(this.intake);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
if (ExtenderIntakeGroup.direction > 0) {
this.intake.runAtOutput(-1);
} else {
this.intake.runAtOutput(0);
}
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
}
}
@@ -1,195 +0,0 @@
// 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.
package frc4388.robot.commands;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.utility.DummySensor;
import frc4388.utility.Gains;
public class Shoot extends CommandBase {
// subsystems
public SwerveDrive m_swerve;
public BoomBoom m_boomBoom;
public Turret m_turret;
public Hood m_hood;
// given
public double m_gyroAngle;
public double m_odoX;
public double m_odoY;
public double m_distance;
// targets
public double m_targetVel;
public double m_targetHood;
public double m_targetAngle;
public Pose2d m_targetPoint;
// pid
public double error;
public double prevError;
public Gains gains = ShooterConstants.SHOOT_GAINS;
public double kP, kI, kD;
public double proportional, integral, derivative;
public double time;
public double output;
public double normOutput;
public double tolerance;
public boolean isAimedInTolerance;
public int inverted;
// testing
public boolean simMode = true;
public DummySensor driveDummy;
public DummySensor turretDummy;
/**
* Creates a new shoot command, allowing the robot to aim and be ready to fire a ball
* TODO: Velocity Correction
* @param sDrive Drive Train
* @param sShooter Shooter Drum
* @param sTurret Shooter Turret
* @param sHood Shooter Hood
*/
public Shoot(SwerveDrive sDrive, BoomBoom sShooter, Turret sTurret, Hood sHood) {
// Use addRequirements() here to declare subsystem dependencies.
m_swerve = sDrive;
m_boomBoom = sShooter;
m_turret = sTurret;
m_hood = sHood;
addRequirements(m_swerve, m_boomBoom, m_turret, m_hood);
kP = gains.kP;
kI = gains.kI;
kD = gains.kD;
proportional = 0;
integral = 0;
derivative = 0;
time = 0.02;
tolerance = 5.0;
isAimedInTolerance = false;
if (simMode) {
driveDummy = new DummySensor(180);
turretDummy = new DummySensor(180);
DummySensor.resetAll();
}
}
/**
* Updates error for custom PID.
*/
public void updateError() {
m_targetPoint = SwerveDriveConstants.HUB_POSE;
m_targetAngle = AimToCenter.angleToCenter(m_odoX, m_odoY, driveDummy.get());
// m_targetAngle = AimToCenter.angleToCenter(m_odoX, m_odoY, m_swerve.getRegGyro().getDegrees());
error = (m_targetAngle - turretDummy.get() + 360) % 360;
// error = (m_targetAngle - m_turret.getBoomBoomAngleDegrees() + 360) % 360;
isAimedInTolerance = (Math.abs(error) <= tolerance);
if (simMode) {
SmartDashboard.putBoolean("isAimed?", isAimedInTolerance);
System.out.println("Target Angle: " + m_targetAngle);
System.out.println("Error: " + error);
}
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
m_odoX = 0;//m_swerve.getOdometry().getX();
m_odoY = -1;//m_swerve.getOdometry().getY();
m_gyroAngle = m_swerve.getRegGyro().getDegrees();
// get targets (shooter tables)
m_targetVel = m_boomBoom.getVelocity(m_distance);
m_targetHood = m_boomBoom.getHood(m_distance);
m_targetAngle = ((Math.atan2(m_odoY, m_odoX) * (180./Math.PI) - m_gyroAngle) + 180. + 360.) % 360.;
// deadzone processing
if (AimToCenter.isDeadzone(m_targetAngle)) {}
// initial error
updateError();
System.out.println("Error: " + error);
prevError = error;
}
/**
* Run custom PID.
*/
public void runPID() {
if (error > 180){
error = 360 - error;
inverted = -1;
}
else{
inverted = 1;
}
prevError = error;
updateError();
proportional = error;
integral = integral + error * time;
derivative = (error - prevError) / time;
output = kP * proportional + kI * integral + kD * derivative;
normOutput = output/360 * inverted;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
if (simMode) {
System.out.println("Normalized Output: " + normOutput);
}
// custom pid
runPID();
if (simMode) {
driveDummy.apply(normOutput);
System.out.println("Drive Dummy: " + driveDummy.get());
}
m_swerve.driveWithInput(0, 0, normOutput, true); // i have no idea if this is how you rotate the
// entire swerve drive or its the line below
// m_swerve.driveWithInput(0, 0, Math.cos(output), Math.sin(output), true);
m_hood.runAngleAdjustPID(m_targetHood);
m_boomBoom.runDrumShooterVelocityPID(m_targetVel);
if (simMode) {
turretDummy.apply(normOutput);
System.out.println("Turret Dummy: " + turretDummy.get());
}
m_turret.m_boomBoomRotateMotor.set(normOutput);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
if (simMode) {
return isAimedInTolerance;
}
return false;
}
}
@@ -0,0 +1,118 @@
// 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.
package frc4388.robot.commands.ShooterCommands;
import java.util.function.Supplier;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.Constants.VisionConstants;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
public class AimToCenter extends CommandBase {
/** Creates a new AimWithOdometry. */
Turret m_turret;
VisionOdometry m_visionOdometry;
Supplier<Pose2d> supplier;
Pose2d odo;
// use odometry to find x and y later
double x;
double y;
double m_targetAngle;
// public static Gains m_aimGains;
public AimToCenter(Turret turret, VisionOdometry visionOdometry, Supplier<Pose2d> supplier) {
// Use addRequirements() here to declare subsystem dependencies.
m_turret = turret;
m_visionOdometry = visionOdometry;
this.supplier = supplier;
addRequirements(m_turret, m_visionOdometry);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
odo = this.supplier.get();
// ! Yes I realize this stupid, yes it works I promise, coordinate system is funky
x = -odo.getY();
y = -odo.getX();
SmartDashboard.putNumber("trans x", x);
SmartDashboard.putNumber("trans y", y);
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
odo = this.supplier.get(); // * update odometry using really cool supplier -aarav
m_targetAngle = (aaravAngleToCenter(x, y, odo.getRotation().getDegrees())) % 360;
SmartDashboard.putNumber("Target Angle", m_targetAngle);
m_turret.runShooterRotatePID(m_targetAngle);
// Check if limelight is within range (comment out to disable vision odo)
if (Math.abs(m_turret.getBoomBoomAngleDegrees() - m_targetAngle) < VisionConstants.RANGE){
// m_visionOdometry.updateOdometryWithVision();
// m_visionOdometry.setLEDs(true);
}
else{
// m_visionOdometry.setLEDs(false);
}
}
public static double angleToCenter(double x, double y, double gyro) {
double angle = ((Math.atan2(y, x) * (180./Math.PI) - gyro) + 180. + (360. * 4)) % 360.; // Finds the angle between the gyro of the robot and the target (positive x is gyro 0)
// double angle = Math.toDegrees(Math.atan2(y, -x) - gyro);
return (angle - 360);
}
public static double aaravAngleToCenter(double x, double y, double gyro) {
double actualGyro = -gyro + 90;
double exp = Math.toDegrees(Math.atan(y/-x)) - actualGyro;
if (-x > 0) { return (180 + exp); }
if (-x < 0) { return (360 + exp); }
if (x == 0 && y > 0) { return (270 - actualGyro); } // TODO: try putting these two lines before exp is calculated
if (x == 0 && y < 0) { return (90 - actualGyro); }
System.err.println("Invalid case.");
return 0;
}
/**
* Checks if in deadzone.
* @param angle Angle to check.
* @return True if in deadzone.
*/
public static boolean isDeadzone(double angle) {
if (angle == Double.NaN) {
return false;
}
return !((ShooterConstants.TURRET_REVERSE_SOFT_LIMIT <= angle) && (angle <= ShooterConstants.TURRET_FORWARD_SOFT_LIMIT));
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
}
}
@@ -0,0 +1,29 @@
// 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.
package frc4388.robot.commands.ShooterCommands;
import edu.wpi.first.wpilibj2.command.SequentialCommandGroup;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
// NOTE: Consider using this command inline, rather than writing a subclass. For more
// information, see:
// https://docs.wpilib.org/en/stable/docs/software/commandbased/convenience-features.html
public class Seek extends SequentialCommandGroup {
/** Seeks.
* Seeking -> Sought
* @author Aarav Shah
* @blame Aarav Shah (thomas did this)
*/
public Seek(SwerveDrive swerve, BoomBoom drum, Turret turret, Hood hood, VisionOdometry visionOdometry) {
// Add your commands in the addCommands() call, e.g.
// addCommands(new FooCommand(), new BarCommand());
addCommands(new Shoot(swerve, drum, turret, hood, visionOdometry, false, false), new TrackTarget(swerve, turret, drum, hood, visionOdometry));
}
}
@@ -0,0 +1,217 @@
// 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.
package frc4388.robot.commands.ShooterCommands;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants;
import frc4388.robot.Robot;
import frc4388.robot.RobotContainer;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.Constants.SwerveDriveConstants;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
import frc4388.utility.DummySensor;
import frc4388.utility.Gains;
public class Shoot extends CommandBase {
// subsystems
private SwerveDrive swerve;
private Turret turret;
private BoomBoom drum;
private Hood hood;
private VisionOdometry visionOdometry;
private boolean toShoot;
// given
private double odoX, odoY;
private double distance;
private double gyroAngle;
// targets
private double targetAngle, targetVel, targetHood;
// pid
private Gains turretGains = ShooterConstants.TURRET_SHOOT_GAINS;
private Gains swerveGains = ShooterConstants.SWERVE_SHOOT_GAINS;
private double error;
private double prevError;
private double kP, kI, kD;
private double proportional, integral, derivative;
private double output, normOutput;
private double tolerance;
boolean timerStarted;
long startTime;
private double timeTolerance;
private boolean isAimedInTolerance;
private int inverted;
private double initialSwerveRotation;
private boolean endsWithLimelight;
/**
* Creates a new shoot command, allowing the robot to aim and be ready to fire a ball
*
* @param swerve Drive Train
* @param drum Shooter Drum
* @param turret Shooter Turret
* @param hood Shooter Hood
*
* @author Aarav Shah
*/
public Shoot(SwerveDrive swerve, BoomBoom drum, Turret turret, Hood hood, VisionOdometry visionOdometry, boolean toShoot, boolean endsWithLimelight) {
// Use addRequirements() here to declare subsystem dependencies.
this.swerve = swerve;
this.drum = drum;
this.turret = turret;
this.hood = hood;
this.visionOdometry = visionOdometry;
this.toShoot = toShoot;
this.endsWithLimelight = endsWithLimelight;
kP = turretGains.kP;
kI = turretGains.kI;
kD = turretGains.kD;
proportional = 0;
integral = 0;
derivative = 0;
tolerance = 10.0;
timeTolerance = 500;
isAimedInTolerance = false;
this.inverted = 1;
addRequirements(this.swerve, this.drum, this.turret, this.hood, this.visionOdometry);
}
// public Shoot(SwerveDrive swerve, BoomBoom drum, Turret turret, Hood hood) {
// this(swerve, drum, turret, hood, false);
// }
/**
* Updates error for custom PID.
*/
public void updateError() {
targetAngle = AimToCenter.aaravAngleToCenter(odoX, odoY, swerve.getRegGyro().getDegrees());
error = (targetAngle - turret.getBoomBoomAngleDegrees()) % 360;
// if (error > 180) {
// error = error - 360;
// this.inverted = -1;
// } else {
// this.inverted = 1;
// }
if (error > 180) { error = error - 360; }
isAimedInTolerance = (Math.abs(error) <= tolerance);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
timerStarted = false;
startTime = 0;
this.odoX = -this.swerve.getOdometry().getY(); // 3.2766
this.odoY = -this.swerve.getOdometry().getX(); // -0.9398
this.distance = Math.hypot(odoX, odoY);
this.gyroAngle = this.swerve.getRegGyro().getDegrees();
this.initialSwerveRotation = gyroAngle;
// get targets (shooter tables)
this.targetVel = drum.getVelocity(distance);
this.targetHood = drum.getHood(distance);
this.targetAngle = AimToCenter.aaravAngleToCenter(odoX, odoY, swerve.getRegGyro().getDegrees());
// initial error
updateError();
prevError = error;
}
/**
* Run custom PID.
*/
public void runPID() {
prevError = error;
updateError();
this.proportional = error;
this.integral = integral + (error * Constants.LOOP_TIME);
this.derivative = (error - prevError) / Constants.LOOP_TIME;
this.output = kP * proportional + kI * integral + kD * derivative;
this.normOutput = (output / 360); // inverted;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
runPID();
SmartDashboard.putNumber("Error", this.error);
SmartDashboard.putNumber("Shoot.java TargetAngle", this.targetAngle);
SmartDashboard.putNumber("Normalized Output", this.normOutput);
this.turret.runTurretWithCustomPID(normOutput);
this.swerve.driveWithInput(RobotContainer.getDriverController().getLeftX(), RobotContainer.getDriverController().getLeftY(), normOutput * (this.swerveGains.kP/this.turretGains.kP), true); // ? should the output be field relative
if (this.toShoot) {
this.hood.runAngleAdjustPID(this.targetHood);
this.drum.runDrumShooterVelocityPID(this.targetVel);
}
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
System.err.println("SHOOT IS FINISHED: " + Boolean.toString(interrupted).toUpperCase());
// ? return to initial swerve rotation
// swerve.driveWithInput(0, 0, initialSwerveRotation, true);
// this.swerve.driveWithInput(0.0, 0.0, 0.0, 0.0, true);
// this.turret.m_boomBoomRotateMotor.set(0.0);
// ? should stop the turret and the swerve
////this.swerve.stopModules();
this.turret.runTurretWithInput(0.0);
if (this.toShoot) {
this.hood.runHood(0.0);
this.drum.runDrumShooter(0.0);
}
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
if (!endsWithLimelight) {
if (isAimedInTolerance && !timerStarted) {
timerStarted = true;
startTime = System.currentTimeMillis();
}
SmartDashboard.putBoolean("isDone", isAimedInTolerance && timerStarted && ((System.currentTimeMillis() - startTime) > timeTolerance));
return (isAimedInTolerance && timerStarted && ((System.currentTimeMillis() - startTime) > timeTolerance));
} else {
return this.visionOdometry.m_camera.getLatestResult().hasTargets();
}
}
}
@@ -0,0 +1,164 @@
// 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.
package frc4388.robot.commands.ShooterCommands;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.opencv.core.Point;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.wpilibj.motorcontrol.MotorController;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.RobotContainer;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.Constants.VisionConstants;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.Vision;
import frc4388.robot.subsystems.VisionOdometry;
import frc4388.utility.Vector2D;
import frc4388.utility.desmos.DesmosServer;
public class TrackTarget extends CommandBase {
/** Creates a new TrackTarget. */
SwerveDrive m_swerve;
Turret m_turret;
VisionOdometry m_visionOdometry;
BoomBoom m_boomBoom;
Hood m_hood;
static double velocity;
static double hoodPosition;
ArrayList<Point> points = new ArrayList<>();
private boolean targetLocked = false;
private double velocityTolerance = 100.0;
private double hoodTolerance = 5.0;
boolean isExecuted = false;
public TrackTarget (SwerveDrive swerve, Turret turret, BoomBoom boomBoom, Hood hood, VisionOdometry visionOdometry) {
m_swerve = swerve;
m_turret = turret;
m_boomBoom = boomBoom;
m_hood = hood;
m_visionOdometry = visionOdometry;
addRequirements(m_turret, m_boomBoom, m_hood, m_visionOdometry);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
velocity = 0;
hoodPosition = 0;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
try {
m_visionOdometry.setLEDs(true);
points = m_visionOdometry.getTargetPoints();
points = filterPoints(points);
Point average = VisionOdometry.averagePoint(points);
double output = (average.x - VisionConstants.LIME_HIXELS/2.d) / VisionConstants.LIME_HIXELS;
output *= 2;
m_turret.runTurretWithInput(output);
double position = m_turret.m_boomBoomRotateEncoder.getPosition();
if(Math.abs(position - ShooterConstants.TURRET_FORWARD_SOFT_LIMIT) < 5 ||
Math.abs(position - ShooterConstants.TURRET_REVERSE_SOFT_LIMIT) < 5)
m_swerve.driveWithInput(RobotContainer.getDriverController().getLeftX(), RobotContainer.getDriverController().getLeftY(), output, true);
else
m_swerve.driveWithInput(RobotContainer.getDriverController().getLeftX(), RobotContainer.getDriverController().getLeftY(),
RobotContainer.getDriverController().getRightX(), RobotContainer.getDriverController().getRightY(),
true);
double regressedDistance = getDistance(average.y);
// ! add 30 to the distance to get in target. May need to be adjusted
velocity = m_boomBoom.getVelocity(regressedDistance + 30);
hoodPosition = m_boomBoom.getHood(regressedDistance + 30);
m_boomBoom.runDrumShooterVelocityPID(velocity);
m_hood.runAngleAdjustPID(hoodPosition);
double currentDrumVel = this.m_boomBoom.m_shooterFalconLeft.getSelectedSensorVelocity();
double currentHood = this.m_hood.getEncoderPosition();
targetLocked = (Math.abs(currentDrumVel - velocity) < velocityTolerance) && (Math.abs(currentHood - hoodPosition) < hoodTolerance);
SmartDashboard.putNumber("Regressed Distance", regressedDistance);
// SmartDashboard.putNumber("Distance", distance);
SmartDashboard.putNumber("Hood Target Angle Track", hoodPosition);
SmartDashboard.putNumber("Vel Target Track", velocity);
SmartDashboard.putBoolean("Target Locked", targetLocked);
} catch (Exception e){
e.printStackTrace();
}
}
public ArrayList<Point> filterPoints(ArrayList<Point> points) {
Point average = VisionOdometry.averagePoint(points);
HashMap<Point, Double> pointDistances = new HashMap<>();
double distanceSum = 0;
for(Point point : points) {
Vector2D difference = new Vector2D(point);
difference.subtract(new Vector2D(average));
double mag = difference.magnitude();
distanceSum += mag;
pointDistances.put(point, mag);
}
final double averageDist = distanceSum / points.size();
return (ArrayList<Point>) pointDistances.keySet().stream().filter(p -> pointDistances.get(p) < 2 * averageDist).collect(Collectors.toList());
}
public final double getDistance(double averageY) {
double y_rot = averageY / VisionConstants.LIME_VIXELS;
y_rot *= Math.toRadians(VisionConstants.V_FOV);
y_rot -= Math.toRadians(VisionConstants.V_FOV) / 2;
y_rot += Math.toRadians(VisionConstants.LIME_ANGLE);
double distance = (VisionConstants.TARGET_HEIGHT - VisionConstants.LIME_HEIGHT) / Math.tan(y_rot);
double regressedDistance = distanceRegression(distance);
regressedDistance += VisionConstants.EDGE_TO_CENTER + VisionConstants.LIMELIGHT_RADIUS;
return regressedDistance;
}
public final double distanceRegression(double distance) {
return (79.6078 * Math.pow(1.01343, distance) - 56.6671);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
// return isExecuted && Math.abs(output) < .1;
}
}
@@ -0,0 +1,76 @@
// 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.
package frc4388.robot.commands.StorageCommands;
import edu.wpi.first.wpilibj.DriverStation.Alliance;
import edu.wpi.first.wpilibj.util.Color;
import edu.wpi.first.wpilibj2.command.CommandBase;
import edu.wpi.first.wpilibj2.command.CommandScheduler;
import edu.wpi.first.wpilibj2.command.ScheduleCommand;
import frc4388.robot.Robot;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Storage;
import frc4388.robot.subsystems.Turret;
public class ManageStorage extends CommandBase {
// subsystems
private Storage storage;
private BoomBoom drum;
private Turret turret;
private Alliance alliance;
private boolean rightColor;
/** Creates a new ManageStorage. */
public ManageStorage(Storage storage, BoomBoom drum, Turret turret) {
// Use addRequirements() here to declare subsystem dependencies.
this.storage = storage;
this.drum = drum;
this.turret = turret;
rightColor = true;
addRequirements(this.storage, this.drum, this.turret);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
checkColor();
if (rightColor) {
this.storage.manageStorage();
} else {
// * CommandScheduler.getInstance().schedule(new ExampleCommand());
// * new ExampleCommand().schedule();
// * new ExampleCommand().execute(); (accompanied by initialize and onFinished)
new SpitOutWrongColor(this.storage, this.drum, this.turret); // ? is this how you run a command inside a command
}
}
private void checkColor() {
// this.alliance = this.storage.getColor();
// rightColor = this.alliance.equals(Robot.alliance);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
}
}
@@ -0,0 +1,72 @@
// 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.
package frc4388.robot.commands.StorageCommands;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Storage;
import frc4388.robot.subsystems.Turret;
public class SpitOutWrongColor extends CommandBase {
// subsystems
private Storage storage;
private BoomBoom drum;
private Turret turret;
// time (in milliseconds)
private long initialTime;
private long elapsedTime;
private long threshold;
private double initialTurret;
private int spitVelocity;
/** Creates a new SpitOutWrongColor. */
public SpitOutWrongColor(Storage storage, BoomBoom drum, Turret turret) {
// Use addRequirements() here to declare subsystem dependencies.
this.storage = storage;
this.drum = drum;
this.turret = turret;
addRequirements(this.storage, this.drum, this.turret);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
initialTime = System.currentTimeMillis();
elapsedTime = 0;
threshold = 2000;
initialTurret = this.turret.getEncoderPosition();
spitVelocity = 2000;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
elapsedTime = System.currentTimeMillis() - initialTime;
this.storage.runStorage(0.9);
this.turret.gotoMidpoint();
this.drum.runDrumShooterVelocityPID(spitVelocity);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
this.storage.runStorage(0.0);
this.turret.runShooterRotatePID(initialTurret * ShooterConstants.TURRET_DEGREES_PER_ROT);
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return (elapsedTime >= threshold);
}
}
@@ -1,97 +0,0 @@
// 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.
package frc4388.robot.commands;
import java.util.ArrayList;
import org.opencv.core.Point;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.wpilibj2.command.CommandBase;
import frc4388.robot.Constants.VisionConstants;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.Hood;
import frc4388.robot.subsystems.SwerveDrive;
import frc4388.robot.subsystems.Turret;
import frc4388.robot.subsystems.VisionOdometry;
public class TrackTarget extends CommandBase {
/** Creates a new TrackTarget. */
Turret m_turret;
SwerveDrive m_drive;
VisionOdometry m_visionOdometry;
BoomBoom m_boomBoom;
Hood m_hood;
// use odometry to find x and y later
double x;
double y;
double distance;
double vel;
double hood;
double average;
double output;
Pose2d pos = new Pose2d();
ArrayList<Point> points = new ArrayList<>();
// public static Gains m_aimGains;
public TrackTarget (Turret turret, BoomBoom boomBoom, Hood hood, SwerveDrive drive, VisionOdometry visionOdometry) {
// Use addRequirements() here to declare subsystem dependencies.
m_turret = turret;
m_drive = drive;
m_boomBoom = boomBoom;
m_hood = hood;
m_visionOdometry = visionOdometry;
addRequirements(m_turret, m_drive, m_visionOdometry);
}
// Called when the command is initially scheduled.
@Override
public void initialize() {
x = 0;
y = 0;
}
// Called every time the scheduler runs while the command is scheduled.
@Override
public void execute() {
//m_targetAngle = angleToCenter(x, y, m_drive.getRegGyro().getDegrees());
m_visionOdometry.setLEDs(true);
points = m_visionOdometry.getTargetPoints();
double pointTotal = 0;
for(Point point : points)
{
pointTotal = pointTotal + point.x;
}
average = pointTotal/points.size();
output = average/VisionConstants.LIME_HIXELS * VisionConstants.TURRET_kP;
m_turret.runTurretWithInput(output);
try{
pos = m_visionOdometry.getVisionOdometry();
distance = Math.hypot(pos.getX(), pos.getY());
}
catch (Exception e){
}
vel = m_boomBoom.getVelocity(distance); // has been changed for robot reveal night so this variable is percent output
hood = m_boomBoom.getHood(distance);
m_boomBoom.runDrumShooter(vel);
// m_boomBoom.runDrumShooterVelocityPID(vel);
m_hood.runAngleAdjustPID(hood);
//m_turret.runshooterRotatePID(m_targetAngle);
}
// Called once the command ends or is interrupted.
@Override
public void end(boolean interrupted) {
}
// Returns true when the command should end.
@Override
public boolean isFinished() {
return false;
}
}
@@ -20,8 +20,10 @@ import com.ctre.phoenix.motorcontrol.TalonFXControlMode;
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
import com.diffplug.common.base.Errors;
import edu.wpi.first.util.sendable.Sendable;
import edu.wpi.first.wpilibj.Filesystem;
import edu.wpi.first.wpilibj.RobotBase;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.utility.CSV;
@@ -34,9 +36,11 @@ public class BoomBoom extends SubsystemBase {
public WPI_TalonFX m_shooterFalconRight;
public static Gains m_drumShooterGains = ShooterConstants.DRUM_SHOOTER_GAINS;
public static BoomBoom m_boomBoom;
double speed2;
double velP;
double input;
public double pidOffset = 0;
public boolean m_isDrumReady = false;
public double m_fireVel;
@@ -52,48 +56,64 @@ public class BoomBoom extends SubsystemBase {
private ShooterTableEntry[] m_shooterTable;
/*
* Creates new BoomBoom subsystem, has drum shooter and angle adjuster
*/
/** Creates a new BoomBoom. */
/** Creates a new BoomBoom, which has a drum shooter and angle adjuster. */
public BoomBoom(WPI_TalonFX shooterFalconLeft, WPI_TalonFX shooterFalconRight) {
m_shooterFalconLeft = shooterFalconLeft;
m_shooterFalconRight = shooterFalconRight;
setShooterGains();
m_shooterTable = readShooterTable();
// Run a helper method that logs the contents of the table on a new thread.
new Thread(() -> LOGGER.fine(() -> CSV.ReflectionTable.create(m_shooterTable, RobotBase.isSimulation()))).start();
}
public Double getVelocity(Double distance) {
// This is a function that takes a value (distance) and returns a value (drumVelocity) that is a
// linear interpolation of the two values (drumVelocity) at the two closest points in the table
// (m_shooterTable) to the given value (distance).
/**
* This is a function that takes a value (distance) and returns a value (drumVelocity) that is a
* linear interpolation of the two values (drumVelocity) at the two closest points in the table
* (m_shooterTable) to the given value (distance).
* @param distance Distance in shooter table
* @return Drum Velocity in units per 100 ms
*/
public Double getVelocity(final Double distance) {
return NumericData.linearInterpolate(m_shooterTable, distance, e -> e.distance, e -> e.drumVelocity).doubleValue();
}
public Double getHood(Double distance) {
// This is a function that takes a value (distance) and returns a value (hoodExt) that is a linear
// interpolation of the two values (hoodExt) at the two closest points in the table (m_shooterTable)
// to the given value (distance).
/**
* This is a function that takes a value (distance) and returns a value (hoodExt) that is a linear
* interpolation of the two values (hoodExt) at the two closest points in the table (m_shooterTable)
* to the given value (distance).
* @param distance Distance in shooter table
* @return Hood extension in units
*/
public Double getHood(final Double distance) {
return NumericData.linearInterpolate(m_shooterTable, distance, e -> e.distance, e -> e.hoodExt).doubleValue();
}
@Override
public void periodic() {
// This method will be called once per scheduler run
// speed2 = SmartDashboard.getNumber("Shooter Offset", 0.0);
SmartDashboard.putNumber("Shooter Current", getCurrent());
SmartDashboard.putNumber("Shooter Voltage", m_shooterFalconLeft.getMotorOutputVoltage());
SmartDashboard.putNumber("Shooter Actual Velocity", m_shooterFalconLeft.getSelectedSensorVelocity());
}
public void passRequiredSubsystem(Hood subsystem0, Turret subsystem1) {
m_hoodSubsystem = subsystem0;
m_turretSubsystem = subsystem1;
}
}
/**
* Runs the Drum motor at a given speed
* @param speed percent output form -1.0 to 1.0
*/
public void runDrumShooter(double speed) {
m_shooterFalconLeft.set(TalonFXControlMode.PercentOutput, speed);
// m_shooterFalconLeft.set(TalonFXControlMode.PercentOutput, speed + speed2);
m_shooterFalconLeft.set(speed);
SmartDashboard.putNumber("BoomBoom percent speed", speed + speed2);
SmartDashboard.putNumber("BoomBoom current stator", m_shooterFalconLeft.getStatorCurrent());
SmartDashboard.putNumber("BoomBoom current supply", m_shooterFalconLeft.getSupplyCurrent());
}
@@ -106,6 +126,7 @@ public class BoomBoom extends SubsystemBase {
}
public void runDrumShooterVelocityPID(double targetVel) {
SmartDashboard.putNumber("Target Drum Velocity", 10000 + pidOffset);
m_shooterFalconLeft.set(TalonFXControlMode.Velocity, targetVel); // Init
// New BoomBoom controller stuff
@@ -151,4 +172,17 @@ public class BoomBoom extends SubsystemBase {
return new ShooterTableEntry[] { dummyEntry };
}
}
public void updateOffset(double change) {
pidOffset = pidOffset + change;
}
public void increaseSpeed(double amount)
{
speed2 = speed2 + amount;
}
public double getCurrent(){
return m_shooterFalconLeft.getSupplyCurrent() + m_shooterFalconRight.getSupplyCurrent();
}
}
@@ -0,0 +1,66 @@
package frc4388.robot.subsystems;
import java.nio.file.ClosedWatchServiceException;
import com.ctre.phoenix.motorcontrol.LimitSwitchNormal;
import com.revrobotics.CANSparkMax;
import com.revrobotics.SparkMaxLimitSwitch;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.Servo;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ClawConstants;
import frc4388.robot.Constants.ClimberConstants;
public class Claws extends SubsystemBase {
public Servo m_leftClaw;
public Servo m_rightClaw;
// public CANSparkMax m_leftClaw;
// public CANSparkMax m_rightClaw;
// private SparkMaxLimitSwitch m_leftLimitSwitchF;
// private SparkMaxLimitSwitch m_rightLimitSwitchF;
// private SparkMaxLimitSwitch m_leftLimitSwitchR;
// private SparkMaxLimitSwitch m_rightLimitSwitchR;
private double m_leftOffset;
private double m_rightOffset;
private boolean m_open;
public static enum ClawType {LEFT, RIGHT}
public Claws(Servo leftClaw, Servo rightClaw) {
m_leftClaw = leftClaw;
m_rightClaw = rightClaw;
m_open = false;
}
/**
* Sets the state of both hooks
* @param open The state of the hooks
*/
public void setOpen(boolean open) {
if(open) {
m_leftClaw.setRaw(ClawConstants.BOTTOM_LIMIT - 900);
m_rightClaw.setRaw(ClawConstants.TOP_LIMIT + 100);
SmartDashboard.putBoolean("Claws Closed", false);
} else {
m_leftClaw.setRaw(ClawConstants.TOP_LIMIT - 400);
m_rightClaw.setRaw(ClawConstants.BOTTOM_LIMIT - 400);
SmartDashboard.putBoolean("Claws Closed", true);
}
}
public void toggleClaws() {
m_open = !m_open;
setOpen(m_open);
}
@Override
public void periodic() {
}
}
@@ -0,0 +1,23 @@
// 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.
package frc4388.robot.subsystems;
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ClimberConstants;
public class Climber extends SubsystemBase {
private WPI_TalonFX elbow;
/** Creates a new Climber */
public Climber(WPI_TalonFX elbow) { this.elbow = elbow; }
public void setEncoders(double value) { this.elbow.setSelectedSensorPosition(value); }
public double getCurrent() { return this.elbow.getSupplyCurrent(); }
public void setMotors(double elbowOutput) { this.elbow.set(elbowOutput * ClimberConstants.INPUT_MULTIPLIER); }
@Override
public void periodic() {}
}
@@ -0,0 +1,69 @@
// 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.
package frc4388.robot.subsystems;
import com.ctre.phoenix.led.ColorFlowAnimation.Direction;
import com.revrobotics.CANSparkMax;
import com.revrobotics.SparkMaxLimitSwitch;
import com.revrobotics.CANSparkMax.SoftLimitDirection;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ExtenderConstants;
public class Extender extends SubsystemBase {
private CANSparkMax m_extenderMotor;
// private SparkMaxLimitSwitch m_inLimit;
// private SparkMaxLimitSwitch m_outLimit;
/** Creates a new Extender. */
public Extender(CANSparkMax extenderMotor) {
m_extenderMotor = extenderMotor;
// m_inLimit = m_extenderMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_outLimit = m_extenderMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_inLimit.enableLimitSwitch(false);
// m_outLimit.enableLimitSwitch(false);
m_extenderMotor.setSoftLimit(SoftLimitDirection.kForward, (float) ExtenderConstants.EXTENDER_FORWARD_LIMIT);
m_extenderMotor.setSoftLimit(SoftLimitDirection.kReverse, (float) ExtenderConstants.EXTENDER_REVERSE_LIMIT);
setExtenderSoftLimits(true);
}
/**
* Set status of extender motor soft limits.
* @param set Boolean to set soft limits to.
*/
public void setExtenderSoftLimits(boolean set) {
m_extenderMotor.enableSoftLimit(SoftLimitDirection.kForward, set);
m_extenderMotor.enableSoftLimit(SoftLimitDirection.kReverse, set);
}
@Override
public void periodic() {
// This method will be called once per scheduler run
SmartDashboard.putNumber("Extender Position", m_extenderMotor.getEncoder().getPosition());
}
public void runExtender(double input) {
// if (!m_serializer.getBeam() && input < 0.) return;
m_extenderMotor.set(input);
}
public double getPosition() {
return m_extenderMotor.getEncoder().getPosition();
}
public void setEncoder(double position) {
m_extenderMotor.getEncoder().setPosition(position);
}
public double getCurrent() {
return m_extenderMotor.getOutputCurrent();
}
}
@@ -10,7 +10,9 @@ import com.revrobotics.SparkMaxPIDController;
import com.revrobotics.SparkMaxLimitSwitch;
import com.revrobotics.CANSparkMax.ControlType;
import com.revrobotics.CANSparkMax.SoftLimitDirection;
import com.revrobotics.SparkMaxRelativeEncoder.Type;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ShooterConstants;
@@ -20,8 +22,8 @@ public class Hood extends SubsystemBase {
public BoomBoom m_shooterSubsystem;
public CANSparkMax m_angleAdjusterMotor;
public SparkMaxLimitSwitch m_hoodUpLimitSwitch;
public SparkMaxLimitSwitch m_hoodDownLimitSwitch;
// public SparkMaxLimitSwitch m_hoodUpLimitSwitch;
// public SparkMaxLimitSwitch m_hoodDownLimitSwitch;
public static Gains m_angleAdjusterGains;
public RelativeEncoder m_angleEncoder;
@@ -30,8 +32,10 @@ public class Hood extends SubsystemBase {
public boolean m_isHoodReady = false;
public double m_fireAngle;
public double m_fireAngle;
public double speedLimiter;
public double calibrationSpeed = 1.0;
/** Creates a new Hood. */
public Hood(CANSparkMax angleAdjusterMotor) {
@@ -41,20 +45,50 @@ public double m_fireAngle;
m_angleAdjusterPIDController = m_angleAdjusterMotor.getPIDController();
m_angleAdjusterGains = ShooterConstants.SHOOTER_ANGLE_GAINS;
m_hoodUpLimitSwitch = m_angleAdjusterMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_hoodDownLimitSwitch = m_angleAdjusterMotor.getReverseLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_hoodUpLimitSwitch.enableLimitSwitch(true);
m_hoodDownLimitSwitch.enableLimitSwitch(true);
// m_hoodUpLimitSwitch = m_angleAdjusterMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_hoodDownLimitSwitch = m_angleAdjusterMotor.getReverseLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_hoodUpLimitSwitch.enableLimitSwitch(true);
// m_hoodDownLimitSwitch.enableLimitSwitch(true);
m_angleAdjusterMotor.setSoftLimit(SoftLimitDirection.kForward, (float) ShooterConstants.HOOD_FORWARD_LIMIT);
m_angleAdjusterMotor.setSoftLimit(SoftLimitDirection.kReverse, (float) ShooterConstants.HOOD_REVERSE_LIMIT);
m_angleAdjusterMotor.setSoftLimit(SoftLimitDirection.kForward, (float) ShooterConstants.HOOD_FORWARD_SOFT_LIMIT);
m_angleAdjusterMotor.setSoftLimit(SoftLimitDirection.kReverse, (float) ShooterConstants.HOOD_REVERSE_SOFT_LIMIT);
setHoodSoftLimits(true);
this.speedLimiter = 1.0;
}
@Override
public void periodic() {
// This method will be called once per scheduler run
// SmartDashboard.putNumber("Hood Angle", m_angleEncoder.getPosition());
// * speed limiting near soft limits. tolerance (distance when ramping starts) is 20 rotations. speed at hard limits is 0.2 (percent output).
runVelocityRamping();
}
public void runVelocityRamping() {
if (areSoftLimitsEnabled()) {
double currentPos = this.getEncoderPosition();
double forwardDistance = Math.abs(currentPos - ShooterConstants.HOOD_FORWARD_SOFT_LIMIT);
double reverseDistance = Math.abs(currentPos - ShooterConstants.HOOD_REVERSE_SOFT_LIMIT);
if (forwardDistance < ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE) {
this.speedLimiter = 0.2 + (forwardDistance * (1 / ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE));
}
if (reverseDistance < ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE) {
this.speedLimiter = 0.2 + (reverseDistance * (1 / ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE));
}
if ((forwardDistance > ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE) && (reverseDistance > ShooterConstants.HOOD_SOFT_LIMIT_TOLERANCE)) {
this.speedLimiter = 1.0;
}
}
}
public boolean areSoftLimitsEnabled() {
return this.m_angleAdjusterMotor.isSoftLimitEnabled(SoftLimitDirection.kForward) && this.m_angleAdjusterMotor.isSoftLimitEnabled(SoftLimitDirection.kReverse);
}
/**
@@ -79,20 +113,34 @@ public double m_fireAngle;
m_angleAdjusterPIDController.setReference(targetAngle, ControlType.kPosition);
}
/**
* Runs the hood with the given input
* @param input value from -1.0 to 1.0, postive is upward (more horizontal shootijng angle)
*/
public void runHood(double input) {
m_angleAdjusterMotor.set(input);
m_angleAdjusterMotor.set(input * this.speedLimiter * this.calibrationSpeed);
}
public void resetGyroAngleAdj(){
/**
* Run a PID to go to the zero position.
*/
public void gotoZero() {
runAngleAdjustPID(0);
}
public void resetGyroAngleAdj() {
m_angleEncoder.setPosition(0);
}
public double getAnglePosition(){
return 0.0;//m_angleEncoder.getPosition();
public double getEncoderPosition(){
return m_angleEncoder.getPosition();
}
public double getAnglePositionDegrees(){
return 0.0;//((m_angleEncoder.getPosition() - ShooterConstants.HOOD_MOTOR_POS_AT_ZERO_ROT) * 360/ShooterConstants.HOOD_MOTOR_ROTS_PER_ROT) - 90;
}
public double getCurrent(){
return m_angleAdjusterMotor.getOutputCurrent();
}
}
@@ -4,74 +4,48 @@
package frc4388.robot.subsystems;
//Imported Limit switch ONLY
import com.revrobotics.SparkMaxLimitSwitch;
import com.ctre.phoenix.motorcontrol.NeutralMode;
import com.ctre.phoenix.motorcontrol.can.WPI_TalonFX;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import com.revrobotics.CANSparkMax;
import frc4388.robot.Constants.IntakeConstants;
import frc4388.robot.commands.ExtenderIntakeCommands.ExtenderIntakeGroup;
public class Intake extends SubsystemBase {
private WPI_TalonFX m_intakeMotor;
private CANSparkMax m_extenderMotor;
private Serializer m_serializer;
private SparkMaxLimitSwitch m_inLimit;
private SparkMaxLimitSwitch m_outLimit;
public boolean toggle;
/** Creates a new Intake. */
public Intake(WPI_TalonFX intakeMotor, CANSparkMax extenderMotor, Serializer serializer) {
public Intake(WPI_TalonFX intakeMotor) {
m_intakeMotor = intakeMotor;
m_extenderMotor = extenderMotor;
m_serializer = serializer;
m_extenderMotor.restoreFactoryDefaults();
m_intakeMotor.setNeutralMode(NeutralMode.Brake);
m_intakeMotor.setInverted(false);
m_extenderMotor.setInverted(true);
m_inLimit = m_extenderMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_outLimit = m_extenderMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_inLimit.enableLimitSwitch(true);
m_outLimit.enableLimitSwitch(true);
}
@Override
public void periodic() {
// This method will be called once per scheduler run
SmartDashboard.putNumber("Intake Percent Output", m_intakeMotor.get());
SmartDashboard.putNumber("Extender Direction", ExtenderIntakeGroup.direction);
}
/**
* Runs The Intake With Triggers.
* @param leftTrigger Left Trigger to Run -
* @param rightTrigger Right Trigger to Run +
* Runs The Intake With Triggers as input
* @param leftTrigger Left Trigger to Run Inward
* @param rightTrigger Right Trigger to Run Outward
*/
public void runWithTriggers(double leftTrigger, double rightTrigger) {
m_intakeMotor.set((rightTrigger - leftTrigger) * 0.3);
}
/**
* Runs The Extender-
* @param extended Wether the Extender Is Extended
*/
public void runExtender(boolean extended) {
if (!m_serializer.getBeam() && !extended) return;
double extenderMotorSpeed = extended ? 0.25d : -0.25d;
m_extenderMotor.set(extenderMotorSpeed);
m_intakeMotor.set((rightTrigger - leftTrigger) * IntakeConstants.INTAKE_SPEED_MULTIPLIER);
SmartDashboard.putNumber("Intake Current Supply", m_intakeMotor.getSupplyCurrent());
SmartDashboard.putNumber("Intake Current Stator", m_intakeMotor.getStatorCurrent());
}
public void runExtender(double input) {
if (!m_serializer.getBeam() && input < 0.) return;
m_extenderMotor.set(input);
public void runAtOutput(double output, double multiplier) {
m_intakeMotor.set(output * multiplier);
}
/**
* Toggles The Extender
*/
public void toggleExtender() {
toggle = !toggle;
runExtender(toggle);
public void runAtOutput(double output) {
m_intakeMotor.set(output * IntakeConstants.INTAKE_SPEED_MULTIPLIER);
}
public double getCurrent() {
return m_intakeMotor.getSupplyCurrent();
}
}
@@ -57,4 +57,5 @@ public class LED extends SubsystemBase {
public LEDPatterns getPattern() {
return m_currentPattern;
}
}
@@ -19,7 +19,10 @@ public class Serializer extends SubsystemBase{
// m_serializerShooterBelt.set(0);
}
/**
*
* @param input from -1.0 to 1.0, positive is inward
*/
public void setSerializer(double input){
m_serializerBelt.set(input);
}
@@ -48,4 +51,7 @@ public class Serializer extends SubsystemBase{
public boolean getSerializerState() {
return serializerState;
}
public double getCurrent(){
return m_serializerBelt.getOutputCurrent();
}
}
@@ -6,57 +6,67 @@ package frc4388.robot.subsystems;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import com.revrobotics.CANSparkMax;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.wpilibj.DigitalInput;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.DriverStation.Alliance;
import com.revrobotics.ColorSensorV3;
import edu.wpi.first.wpilibj.util.Color;
public class Storage extends SubsystemBase {
public CANSparkMax m_storageMotor;
private DigitalInput m_beamShooter;
private DigitalInput m_beamIntake;
public ColorSensorV3 m_colorSensor;
/** Creates a new Storage. */
public Storage(CANSparkMax storageMotor, DigitalInput beamShooter, DigitalInput beamIntake) {
public Storage(CANSparkMax storageMotor, ColorSensorV3 colorSensor) {
m_storageMotor = storageMotor;
m_beamShooter = beamShooter;
m_beamIntake = beamIntake;
m_colorSensor = colorSensor;
}
public Storage(CANSparkMax storageMotor) {
m_storageMotor = storageMotor;
m_beamShooter = null;
m_beamIntake = null;
m_colorSensor = null;
}
/**
* If The Beam Is Broken, Run Storage
* If Else, Stop Running Storage
*/
public void manageStorage() {
if (getBeamIntake()) runStorage(0.d);
else runStorage(1.d);
if (getColorBroken()) runStorage(0.d);
else runStorage(0.9);
}
/**
* Runs The Storage at a Specifyed Speed
* @param input The Specifyed Speed
* @param input The value frm -1.0 to 1.0, positive is inwards (towards the shooter)
*/
public void runStorage(double input) {
m_storageMotor.set(input);
}
/**
* Gets The Beam State On The Shooter
* Gets the state of the colorsensor as a beam break
* @return The State Of The Beam on the Shooter
*/
public boolean getBeamShooter(){
return m_beamShooter.get();//True if open
public boolean getColorBroken(){
return (getRed() || getBlue());
}
/**
* Gets The Beam State Of The Intake
* @return The Beam State Of The Intake
*/
public boolean getBeamIntake(){
return m_beamIntake.get(); //True if open
public boolean getRed(){
// return (m_colorSensor.getRed() >= 200 && m_colorSensor.getBlue() < 100 && m_colorSensor.getGreen() < 100);
return (m_colorSensor.getColor() == Color.kRed);
}
public boolean getBlue(){
// return (m_colorSensor.getBlue() >= 200 && m_colorSensor.getRed() < 100 && m_colorSensor.getGreen() < 100);
return (m_colorSensor.getColor() == Color.kBlue);
}
public Alliance getColor() {
return (getRed() ? Alliance.Red : Alliance.Blue);
}
@Override
/**
@@ -65,4 +75,7 @@ public class Storage extends SubsystemBase {
public void periodic() {
//manageStorage();
}
public double getCurrent(){
return m_storageMotor.getOutputCurrent();
}
}
@@ -4,19 +4,15 @@
package frc4388.robot.subsystems;
import com.ctre.phoenix.sensors.PigeonIMU.FusionStatus;
import com.ctre.phoenix.sensors.PigeonIMUConfiguration;
import com.ctre.phoenix.sensors.WPI_PigeonIMU;
import com.ctre.phoenix.sensors.WPI_Pigeon2;
import edu.wpi.first.math.VecBuilder;
import edu.wpi.first.math.Vector;
import edu.wpi.first.math.estimator.SwerveDrivePoseEstimator;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.math.kinematics.SwerveDriveKinematics;
import edu.wpi.first.math.kinematics.SwerveDriveOdometry;
import edu.wpi.first.math.kinematics.SwerveModuleState;
import edu.wpi.first.math.util.Units;
import edu.wpi.first.wpilibj.smartdashboard.Field2d;
@@ -38,29 +34,19 @@ public class SwerveDrive extends SubsystemBase {
public static Gains m_swerveGains = SwerveDriveConstants.SWERVE_GAINS;
Translation2d m_frontLeftLocation = new Translation2d(Units.inchesToMeters(halfHeight),
Units.inchesToMeters(halfWidth));
Translation2d m_frontRightLocation = new Translation2d(Units.inchesToMeters(halfHeight),
Units.inchesToMeters(-halfWidth));
Translation2d m_backLeftLocation = new Translation2d(Units.inchesToMeters(-halfHeight),
Units.inchesToMeters(halfWidth));
Translation2d m_backRightLocation = new Translation2d(Units.inchesToMeters(-halfHeight),
Units.inchesToMeters(-halfWidth));
Translation2d m_frontLeftLocation = new Translation2d(Units.inchesToMeters(halfWidth), Units.inchesToMeters(-halfHeight));
Translation2d m_frontRightLocation = new Translation2d(Units.inchesToMeters(halfWidth), Units.inchesToMeters(halfHeight));
Translation2d m_backLeftLocation = new Translation2d(Units.inchesToMeters(-halfWidth), Units.inchesToMeters(-halfHeight));
Translation2d m_backRightLocation = new Translation2d(Units.inchesToMeters(-halfWidth), Units.inchesToMeters(halfHeight));
public SwerveDriveKinematics m_kinematics = new SwerveDriveKinematics(m_frontLeftLocation, m_frontRightLocation,
m_backLeftLocation, m_backRightLocation);
public SwerveModule[] modules;
public WPI_PigeonIMU m_gyro;
protected FusionStatus fstatus = new FusionStatus();
public WPI_Pigeon2 m_gyro;
/*
* Here we use SwerveDrivePoseEstimator so that we can fuse odometry readings.
* The numbers used
* below are robot specific, and should be tuned.
*/
public SwerveDrivePoseEstimator m_poseEstimator;
public SwerveDriveOdometry m_odometry;
// public SwerveDriveOdometry m_odometry;
public double speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW;
public boolean ignoreAngles;
@@ -70,7 +56,7 @@ public class SwerveDrive extends SubsystemBase {
private final Field2d m_field = new Field2d();
public SwerveDrive(SwerveModule leftFront, SwerveModule leftBack, SwerveModule rightFront, SwerveModule rightBack,
WPI_PigeonIMU gyro) {
WPI_Pigeon2 gyro) {
m_leftFront = leftFront;
m_leftBack = leftBack;
@@ -78,26 +64,25 @@ public class SwerveDrive extends SubsystemBase {
m_rightBack = rightBack;
m_gyro = gyro;
modules = new SwerveModule[] { m_leftFront, m_rightFront, m_leftBack, m_rightBack };
modules = new SwerveModule[] {m_leftFront, m_rightFront, m_leftBack, m_rightBack};
m_poseEstimator = new SwerveDrivePoseEstimator(
m_gyro.getRotation2d(),
getRegGyro(),//m_gyro.getRotation2d(),
new Pose2d(),
m_kinematics,
VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1)),
VecBuilder.fill(Units.degreesToRadians(1)),
VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1)));
VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1)), // TODO: tune
VecBuilder.fill(Units.degreesToRadians(1)), // TODO: tune
VecBuilder.fill(1.0, 1.0, Units.degreesToRadians(1))); // TODO: tune
m_odometry = new SwerveDriveOdometry(m_kinematics, m_gyro.getRotation2d());
// m_odometry = new SwerveDriveOdometry(m_kinematics, m_gyro.getRotation2d());
m_gyro.reset();
SmartDashboard.putData("Field", m_field);
}
// https://github.com/ZachOrr/MK3-Swerve-Example
/**
* Method to drive the robot using joystick info.
*
* @link https://github.com/ZachOrr/MK3-Swerve-Example
* @param speeds[0] Speed of the robot in the x direction (forward).
* @param speeds[1] Speed of the robot in the y direction (sideways).
* @param rot Angular rate of the robot.
@@ -105,41 +90,53 @@ public class SwerveDrive extends SubsystemBase {
* field.
*/
public void driveWithInput(double speedX, double speedY, double rot, boolean fieldRelative) {
if (speedX == 0 && speedY == 0 && rot == 0)
ignoreAngles = true;
else
ignoreAngles = false;
Translation2d speed = new Translation2d(-speedX, speedY);
ignoreAngles = (speedX == 0) && (speedY == 0) && (rot == 0);
Translation2d speed = new Translation2d(speedX, speedY);
double mag = speed.getNorm();
speed = speed.times(mag * speedAdjust);
double xSpeedMetersPerSecond = -speed.getX();
double xSpeedMetersPerSecond = speed.getX();
double ySpeedMetersPerSecond = speed.getY();
chassisSpeeds = fieldRelative
? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond,
rot * SwerveDriveConstants.ROTATION_SPEED, m_gyro.getRotation2d())
: new ChassisSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond,
rot * SwerveDriveConstants.ROTATION_SPEED);
-rot * SwerveDriveConstants.ROTATION_SPEED * speedAdjust, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2)))
: new ChassisSpeeds(ySpeedMetersPerSecond, -xSpeedMetersPerSecond,
-rot * SwerveDriveConstants.ROTATION_SPEED * speedAdjust);
SwerveModuleState[] states = m_kinematics.toSwerveModuleStates(chassisSpeeds);
setModuleStates(states);
}
// new Rotation2d((360 - m_gyro.getRotation2d().getDegrees() + 90) * (Math.PI/180)))
public void driveWithInput(double leftX, double leftY, double rightX, double rightY, boolean fieldRelative) {
ignoreAngles = leftX == 0 && leftY == 0 && rightX == 0 && rightY == 0;
Translation2d speed = new Translation2d(-leftX, leftY);
Translation2d speed = new Translation2d(leftX, leftY);
speed = speed.times(speed.getNorm() * speedAdjust);
if (Math.abs(rightX) > OIConstants.RIGHT_AXIS_DEADBAND || Math.abs(rightY) > OIConstants.RIGHT_AXIS_DEADBAND)
rotTarget = new Rotation2d(rightX, -rightY).minus(new Rotation2d(0, 1));
rotTarget = new Rotation2d(rightX, -rightY).minus(new Rotation2d(0,1));
double rot = rotTarget.minus(m_gyro.getRotation2d()).getRadians();
double xSpeedMetersPerSecond = -speed.getX();
if (ignoreAngles) {
rot = 0;
}
double xSpeedMetersPerSecond = speed.getX();
double ySpeedMetersPerSecond = speed.getY();
chassisSpeeds = fieldRelative
? ChassisSpeeds.fromFieldRelativeSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond,
rot * SwerveDriveConstants.ROTATION_SPEED, m_gyro.getRotation2d())
: new ChassisSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, rightX * SwerveDriveConstants.ROTATION_SPEED);
rot * SwerveDriveConstants.ROTATION_SPEED * speedAdjust, new Rotation2d(-m_gyro.getRotation2d().getRadians() + (Math.PI*2) + (Math.PI /2)))
: new ChassisSpeeds(xSpeedMetersPerSecond, ySpeedMetersPerSecond, rightX * SwerveDriveConstants.ROTATION_SPEED * speedAdjust);
SwerveModuleState[] states = m_kinematics.toSwerveModuleStates(
chassisSpeeds);
// if (ignoreAngles) {
// SwerveModuleState[] lockedStates = new SwerveModuleState[states.length];
// for (int i = 0; i < states.length; i ++) {
// SwerveModuleState state = states[i];
// lockedStates[i]= new SwerveModuleState(0, state.angle);
// }
// setModuleStates(lockedStates);
// }
setModuleStates(states);
// SmartDashboard.putNumber("rot", rot);
// SmartDashboard.putNumber("rotarget", rotTarget.getDegrees());
}
/**
@@ -150,6 +147,7 @@ public class SwerveDrive extends SubsystemBase {
public void setModuleStates(SwerveModuleState[] desiredStates) {
SwerveDriveKinematics.desaturateWheelSpeeds(desiredStates,
Units.feetToMeters(SwerveDriveConstants.MAX_SPEED_FEET_PER_SEC));
// int i = 2; {
for (int i = 0; i < desiredStates.length; i++) {
SwerveModule module = modules[i];
SwerveModuleState state = desiredStates[i];
@@ -164,9 +162,12 @@ public class SwerveDrive extends SubsystemBase {
updateOdometry();
updateSmartDash();
SmartDashboard.putNumber("Pigeon getRotation2d", m_gyro.getRotation2d().getDegrees());
SmartDashboard.putNumber("Pigeon getAngle", m_gyro.getAngle());
SmartDashboard.putNumber("Pigeon Yaw", m_gyro.getYaw());
SmartDashboard.putNumber("Pigeon Yaw (0 to 360)", m_gyro.getYaw() % 360);
m_field.setRobotPose(m_poseEstimator.getEstimatedPosition());
// m_field.setRobotPose(m_poseEstimator.getEstimatedPosition());
super.periodic();
}
@@ -174,7 +175,7 @@ public class SwerveDrive extends SubsystemBase {
// odometry
SmartDashboard.putNumber("Odometry: X", getOdometry().getX());
SmartDashboard.putNumber("Odometry: Y", getOdometry().getY());
SmartDashboard.putNumber("Odometry: θ", getOdometry().getRotation().getDegrees());
SmartDashboard.putNumber("Odometry: Theta", getOdometry().getRotation().getDegrees());
// chassis speeds
// TODO: find the actual max velocity in m/s of the robot in fast mode to have accurate chassis speeds
@@ -206,9 +207,12 @@ public class SwerveDrive extends SubsystemBase {
*
* @return Rotation2d object holding current gyro in radians
*/
public Rotation2d getRegGyro() {
double regCur = 0.6552670369 + m_gyro.getRotation2d().getDegrees() * 0.9926871527;
return new Rotation2d(regCur * Math.PI / 180);
public Rotation2d getRegGyro() {
// * test chassis regression
// double regCur = 0.6552670369 + m_gyro.getRotation2d().getDegrees() * 0.9926871527;
// * new robot regression
double regCur = 0.2507023948 + m_gyro.getRotation2d().getDegrees() * 0.999034743;
return new Rotation2d(Math.toRadians(regCur));
}
/**
@@ -260,4 +264,12 @@ public class SwerveDrive extends SubsystemBase {
speedAdjust = SwerveDriveConstants.JOYSTICK_TO_METERS_PER_SECOND_SLOW;
}
}
public double getCurrent(){
return m_leftFront.getCurrent() + m_rightFront.getCurrent() + m_rightBack.getCurrent() + m_leftBack.getCurrent();
}
public double getVoltage(){
return m_leftFront.getVoltage() + m_rightFront.getVoltage() + m_rightBack.getVoltage() + m_leftBack.getVoltage();
}
}
@@ -38,6 +38,9 @@ public class SwerveModule extends SubsystemBase {
public double m_currentPos;
public double m_lastPos;
public SwerveModuleState lastState = new SwerveModuleState();
public SwerveModuleState currentState;
/** Creates a new SwerveModule. */
public SwerveModule(WPI_TalonFX driveMotor, WPI_TalonFX angleMotor, CANCoder canCoder, double offset) {
this.driveMotor = driveMotor;
@@ -79,6 +82,7 @@ public class SwerveModule extends SubsystemBase {
// driveMotor.configAllSettings(driveTalonFXConfiguration);
CANCoderConfiguration canCoderConfiguration = new CANCoderConfiguration();
canCoderConfiguration.sensorCoefficient = 0.087890625;
canCoderConfiguration.magnetOffsetDegrees = offset;
canCoderConfiguration.sensorDirection = true;
canCoder.configAllSettings(canCoderConfiguration);
@@ -90,9 +94,7 @@ public class SwerveModule extends SubsystemBase {
}
private Rotation2d getAngle() {
// Note: This assumes the CANCoders are setup with the default feedback
// coefficient
// and the sensor value reports degrees.
// ! Note: This assumes the CANCoders are setup with the default feedback coefficient and the sensor value reports degrees.
return Rotation2d.fromDegrees(canCoder.getAbsolutePosition());
}
@@ -120,6 +122,7 @@ public class SwerveModule extends SubsystemBase {
double desiredTicks = currentTicks + deltaTicks;
if (!ignoreAngle) {
angleMotor.set(TalonFXControlMode.Position, desiredTicks);
}
@@ -181,15 +184,26 @@ public class SwerveModule extends SubsystemBase {
@Override
public void periodic() {
currentState = this.getState();
Rotation2d currentRotation = getAngle();
SmartDashboard.putNumber("Angle Motor " + angleMotor.getDeviceID(), currentRotation.getDegrees());
SmartDashboard.putNumber("Drive Motor " + driveMotor.getDeviceID(),
((driveMotor.getSelectedSensorPosition() / 2048) * 360) % 360);
lastState = currentState;
}
public void reset() {
canCoder.setPositionToAbsolute();
// canCoder.configSensorInitializationStrategy(initializationStrategy)
}
public double getCurrent(){
return angleMotor.getSupplyCurrent() + driveMotor.getSupplyCurrent();
}
public double getVoltage(){
return (Math.abs(angleMotor.getMotorOutputVoltage()) + Math.abs(driveMotor.getMotorOutputVoltage()));
}
}
@@ -18,6 +18,8 @@ import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import frc4388.robot.Constants.ShooterConstants;
import frc4388.robot.commands.ShooterCommands.Shoot;
import frc4388.robot.subsystems.BoomBoom.ShooterTableEntry;
import frc4388.utility.Gains;
public class Turret extends SubsystemBase {
@@ -29,48 +31,149 @@ public class Turret extends SubsystemBase {
public CANSparkMax m_boomBoomRotateMotor;// = new CANSparkMax(ShooterConstants.SHOOTER_ROTATE_ID,
// MotorType.kBrushless);
public static Gains m_shooterTGains = ShooterConstants.SHOOTER_TURRET_GAINS;
// SparkMaxLimitSwitch m_boomBoomRightLimit, m_boomBoomLeftLimit;
public Gyro m_turretGyro;
public double m_targetDistance = 0;
public SparkMaxPIDController m_boomBoomRotatePIDController;
public RelativeEncoder m_boomBoomRotateEncoder;
public boolean m_isAimReady = false;
SparkMaxLimitSwitch m_boomBoomLeftLimit;
SparkMaxLimitSwitch m_boomBoomRightLimit;
SparkMaxPIDController m_boomBoomRotatePIDController;// = m_boomBoomRotateMotor.getPIDController();
public RelativeEncoder m_boomBoomRotateEncoder;// = m_boomBoomRotateMotor.getEncoder();
boolean leftState;
boolean recentlyPressed;
boolean leftPrevState = false;
boolean hasLeftSwitchChanged = false;
// Variables
public Turret(CANSparkMax boomBoomRotateMotor) { // Take in rotate motor as an argument
long leftCurrentTime;
long leftElapsedTime;
public double speedLimiter;
public double calibrationSpeed = 1.0;
public Turret(CANSparkMax boomBoomRotateMotor) {
m_boomBoomRotateMotor = boomBoomRotateMotor;
m_boomBoomRotatePIDController = m_boomBoomRotateMotor.getPIDController();
m_boomBoomRotateEncoder = m_boomBoomRotateMotor.getEncoder();
m_boomBoomRotateMotor.setIdleMode(IdleMode.kBrake);
// m_boomBoomLeftLimit = m_boomBoomRotateMotor.getReverseLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_boomBoomRightLimit = m_boomBoomRotateMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// m_boomBoomRightLimit.enableLimitSwitch(true);
// m_boomBoomLeftLimit.enableLimitSwitch(true);
// SmartDashboard.putBoolean("Right Limit Switch Enabled", m_boomBoomRightLimit.isLimitSwitchEnabled());
// SmartDashboard.putBoolean("Left Limit Switch Enabled", m_boomBoomLeftLimit.isLimitSwitchEnabled());
// setTurretSoftLimits(true);
m_boomBoomRotateMotor.enableSoftLimit(SoftLimitDirection.kForward, true);
m_boomBoomRotateMotor.enableSoftLimit(SoftLimitDirection.kReverse, true);
m_boomBoomRotateMotor.setSoftLimit(SoftLimitDirection.kForward, (float) ShooterConstants.TURRET_FORWARD_SOFT_LIMIT);
m_boomBoomRotateMotor.setSoftLimit(SoftLimitDirection.kReverse, (float) ShooterConstants.TURRET_REVERSE_SOFT_LIMIT);
m_boomBoomLeftLimit = m_boomBoomRotateMotor.getReverseLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_boomBoomRightLimit = m_boomBoomRotateMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
m_boomBoomRightLimit.enableLimitSwitch(true);
m_boomBoomLeftLimit.enableLimitSwitch(true);
m_boomBoomRotateMotor.setSoftLimit(SoftLimitDirection.kForward, (float) ShooterConstants.TURRET_FORWARD_LIMIT);
m_boomBoomRotateMotor.setSoftLimit(SoftLimitDirection.kReverse, (float) ShooterConstants.TURRET_REVERSE_LIMIT);
setTurretSoftLimits(true);
this.speedLimiter = 1.0;
setTurretPIDGains();
}
m_boomBoomRotateMotor.setInverted(true);
// public void toggleLeftLimitSwitch() {
// TODO: find better way to do this, but im in a hurry
// if (leftSwitch.isLimitSwitchEnabled()) {
// leftSwitch.enableLimitSwitch(false);
// } else {
// leftSwitch.enableLimitSwitch(true);
// }
// }
// public void turnOnLeftLimitSwitch() {
// SparkMaxLimitSwitch leftSwitch = m_boomBoomRotateMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// System.out.println("Left Switch ENABLED");
// leftSwitch.enableLimitSwitch(true);
// }
// public void turnOffLeftLimitSwitch() {
// SparkMaxLimitSwitch leftSwitch = m_boomBoomRotateMotor.getForwardLimitSwitch(SparkMaxLimitSwitch.Type.kNormallyOpen);
// System.out.println("Left Switch DISABLED");
// leftSwitch.enableLimitSwitch(false);
// }
/**
* Set gains for turret PIDs.
*/
public void setTurretPIDGains() {
m_boomBoomRotatePIDController.setP(m_shooterTGains.kP);
m_boomBoomRotatePIDController.setI(m_shooterTGains.kI);
m_boomBoomRotatePIDController.setD(m_shooterTGains.kD);
m_boomBoomRotatePIDController.setFF(m_shooterTGains.kF);
m_boomBoomRotatePIDController.setIZone(m_shooterTGains.kIzone);
m_boomBoomRotatePIDController.setOutputRange(ShooterConstants.SHOOTER_TURRET_MIN, m_shooterTGains.kPeakOutput);
// TODO: add 0.1
}
@Override
public void periodic() {
// This method will be called once per scheduler run
// SmartDashboard.putBoolean("Right Limit Switch Enabled", m_boomBoomRightLimit.isLimitSwitchEnabled());
// SmartDashboard.putBoolean("Left Limit Switch Enabled", m_boomBoomLeftLimit.isLimitSwitchEnabled());
SmartDashboard.putNumber("Turret Angle Rotations", m_boomBoomRotateEncoder.getPosition());
SmartDashboard.putNumber("Turret Angle Degrees", m_boomBoomRotateEncoder.getPosition() * ShooterConstants.TURRET_DEGREES_PER_ROT);
SmartDashboard.putBoolean("Left Limit Switch Pressed", m_boomBoomLeftLimit.isPressed());
SmartDashboard.putBoolean("Right Limit Switch Pressed", m_boomBoomRightLimit.isPressed());
// limit switch annoying time thing but actually worked first try wtf
leftState = m_boomBoomLeftLimit.isPressed(); // * Get the state of the left limit switch (true for pressed).
hasLeftSwitchChanged = (leftState != leftPrevState); // * Get whether the state of the left limit switch has changed, based on its previous state.
if (leftState && hasLeftSwitchChanged) { // * If the left limit switch is pressed, and it recently changed, start the time.
leftCurrentTime = System.currentTimeMillis();
leftElapsedTime = 0;
}
if (!m_boomBoomRightLimit.isPressed()) recentlyPressed = false;
if(m_boomBoomRightLimit.isPressed() && !recentlyPressed){
recentlyPressed = true;
m_boomBoomRotateEncoder.setPosition(ShooterConstants.TURRET_FORWARD_HARD_LIMIT);// 0/*ShooterConstants.TURRET_REVERSE_LIMIT + 2*/);
}
SmartDashboard.putBoolean("Recently Pressed", recentlyPressed);
if (leftState && !hasLeftSwitchChanged) { // * If the left limit switch is still pressed, but the state hasn't changed, then calculate elapsed time.
leftElapsedTime = System.currentTimeMillis() - leftCurrentTime;
}
if (leftState && (leftElapsedTime > 500)) { // * If the left limit switch is pressed for more than half a second, update the encoder position.
m_boomBoomRotateEncoder.setPosition(ShooterConstants.TURRET_REVERSE_HARD_LIMIT);
}
leftPrevState = leftState; // * Update the state of the left limit switch.
// * speed limiting near soft limits. tolerance (distance when ramping starts) is 20 rotations. speed at hard limits is 0.2 (percent output).
runVelocityRamping();
}
public void runVelocityRamping() {
if (areSoftLimitsEnabled()) {
double currentPos = this.getEncoderPosition();
double forwardDistance = Math.abs(currentPos - ShooterConstants.TURRET_FORWARD_SOFT_LIMIT);
double reverseDistance = Math.abs(currentPos - ShooterConstants.TURRET_REVERSE_SOFT_LIMIT);
if (forwardDistance < ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE) {
this.speedLimiter = 0.2 + (forwardDistance * (1 / ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE));
}
if (reverseDistance < ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE) {
this.speedLimiter = 0.2 + (reverseDistance * (1 / ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE));
}
if ((forwardDistance > ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE) && (reverseDistance > ShooterConstants.TURRET_SOFT_LIMIT_TOLERANCE)) {
this.speedLimiter = 1.0;
}
}
}
public boolean areSoftLimitsEnabled() {
return this.m_boomBoomRotateMotor.isSoftLimitEnabled(SoftLimitDirection.kForward) && this.m_boomBoomRotateMotor.isSoftLimitEnabled(SoftLimitDirection.kReverse);
}
/**
@@ -82,17 +185,42 @@ public class Turret extends SubsystemBase {
m_boomBoomRotateMotor.enableSoftLimit(SoftLimitDirection.kReverse, set);
}
/**
* Set status of turret limit switches.
* @param set Boolean to set limit switches to.
*/
public void setTurretLimitSwitches(boolean set) {
m_boomBoomRightLimit.enableLimitSwitch(set);
m_boomBoomLeftLimit.enableLimitSwitch(set);
}
public void passRequiredSubsystem(BoomBoom subsystem0, SwerveDrive subsystem1) {
m_boomBoomSubsystem = subsystem0;
m_sDriveSubsystem = subsystem1;
}
/**
* Move the turret with an input
* @param input from -1.0 to 1.0, positive is clockwise
*/
public void runTurretWithInput(double input) {
m_boomBoomRotateMotor.set(input * ShooterConstants.TURRET_SPEED_MULTIPLIER);
m_boomBoomRotateMotor.set(input * ShooterConstants.TURRET_SPEED_MULTIPLIER * this.speedLimiter * this.calibrationSpeed);
}
public void runshooterRotatePID(double targetAngle) {
targetAngle = targetAngle / ShooterConstants.DEGREES_PER_ROT;
public void runTurretWithCustomPID(double input) {
m_boomBoomRotateMotor.set(input * this.speedLimiter);
}
public void runShooterRotatePID(double targetAngle) {
double softMid = (ShooterConstants.TURRET_FORWARD_SOFT_LIMIT - ShooterConstants.TURRET_REVERSE_SOFT_LIMIT) / 2;
targetAngle = (targetAngle % 360);
if (targetAngle > softMid) {
targetAngle = targetAngle - 360;
}
targetAngle = targetAngle / ShooterConstants.TURRET_DEGREES_PER_ROT;
m_boomBoomRotatePIDController.setReference(targetAngle, ControlType.kPosition);
}
@@ -100,13 +228,30 @@ public class Turret extends SubsystemBase {
m_boomBoomRotateEncoder.setPosition(0);
}
public double getboomBoomRotatePosition() {
/**
* Run a PID to go to the zero position.
*/
public void gotoZero() {
runShooterRotatePID(0);
}
/**
* Run a PID to go to the midpoint position, between the two soft limits.
*/
public void gotoMidpoint() {
runShooterRotatePID(-44 * ShooterConstants.TURRET_DEGREES_PER_ROT);
}
public double getEncoderPosition() {
return m_boomBoomRotateEncoder.getPosition();
}
public double getBoomBoomAngleDegrees() {
return (m_boomBoomRotateEncoder.getPosition() - ShooterConstants.TURRET_MOTOR_POS_AT_ZERO_ROT) * 360
/ ShooterConstants.TURRET_MOTOR_ROTS_PER_ROT;
return (getEncoderPosition() * ShooterConstants.TURRET_DEGREES_PER_ROT);
}
public double getCurrent(){
return m_boomBoomRotateMotor.getOutputCurrent();
}
}
@@ -86,10 +86,10 @@ public void track(){
public void checkFinished(){
if (xAngle < 0.5 && xAngle > -0.5 && target == 1){
m_turret.m_isAimReady = true;
// m_turret.m_isAimReady = true;
}
else{
m_turret.m_isAimReady = false;
// m_turret.m_isAimReady = false;
}
}
@@ -31,7 +31,7 @@ import frc4388.utility.VisionObscuredException;
*/
public class VisionOdometry extends SubsystemBase {
// roborio ip & port: 10.43.88.2:1735
private PhotonCamera m_camera;
public PhotonCamera m_camera;
private SwerveDrive m_drive;
private Turret m_shooter;
@@ -54,13 +54,16 @@ public class VisionOdometry extends SubsystemBase {
* Breaks down targets into 4 corners and uses the top 2 points
*
* @return Vision points on the rim of the target in screen space
* @throws VisionObscuredException
*/
public ArrayList<Point> getTargetPoints() {
public ArrayList<Point> getTargetPoints() throws VisionObscuredException {
PhotonPipelineResult result = m_camera.getLatestResult();
latency = result.getLatencyMillis();
System.out.println("Result: " + result.hasTargets() + ", latency: " + latency);
if(!result.hasTargets())
return new ArrayList<Point>();
throw new VisionObscuredException();
ArrayList<Point> points = new ArrayList<>();
@@ -90,16 +93,11 @@ public class VisionOdometry extends SubsystemBase {
m_camera.setLED(on ? VisionLEDMode.kOn : VisionLEDMode.kOff);
}
/** Gets estimated odometry based on limelight data
*
* @return The estimated odometry pose, including gyro rotation
* @throws VisionObscuredException
*/
public Pose2d getVisionOdometry() throws VisionObscuredException {
public Point getTargetOffset() throws VisionObscuredException {
ArrayList<Point> screenPoints = getTargetPoints();
if(screenPoints.size() < 3)
throw new VisionObscuredException("Not enough vision points available");
throw new VisionObscuredException("Less than 3 vision points available");
ArrayList<Point3> points3d = get3dPoints(screenPoints);
ArrayList<Point> points = topView(points3d);
@@ -110,14 +108,25 @@ public class VisionOdometry extends SubsystemBase {
guess = iterateGuess(guess, points);
}
guess = correctGuessForCenter(guess, m_shooter.getBoomBoomAngleDegrees());
guess = correctGuessForGyro(guess, m_drive.getRegGyro().getDegrees());
return guess;
}
SmartDashboard.putNumber("Vision ODO x: ", guess.x);
SmartDashboard.putNumber("Vision ODO y: ", guess.y);
/** Gets estimated odometry based on limelight data
*
* @return The estimated odometry pose, including gyro rotation
* @throws VisionObscuredException
*/
public Pose2d getVisionOdometry() throws VisionObscuredException {
Point targetOffset = getTargetOffset();
targetOffset = correctGuessForCenter(targetOffset, m_shooter.getBoomBoomAngleDegrees());
targetOffset = correctGuessForGyro(targetOffset, m_drive.getRegGyro().getDegrees());
SmartDashboard.putNumber("Vision ODO x: ", targetOffset.x);
SmartDashboard.putNumber("Vision ODO y: ", targetOffset.y);
Rotation2d rotation = new Rotation2d(Math.toDegrees(m_drive.getRegGyro().getDegrees()));
Pose2d odometryPose = new Pose2d(guess.x, guess.y, rotation);
Pose2d odometryPose = new Pose2d(targetOffset.x, targetOffset.y, rotation);
return odometryPose;
}
@@ -125,9 +134,9 @@ public class VisionOdometry extends SubsystemBase {
public double getLatency() {
return latency;
}
public boolean getLEDs() {
if (m_camera.getLEDMode() == VisionLEDMode.kOff) return false;
return true;
return m_camera.getLEDMode() != VisionLEDMode.kOff;
}
public void updateOdometryWithVision(){
@@ -139,6 +148,7 @@ public class VisionOdometry extends SubsystemBase {
err.printStackTrace();
}
}
/** Reverse 3d projects target points from screen coordinates to 3d space
* <p>
* Uses the known height of the target to project points
@@ -284,9 +294,8 @@ public class VisionOdometry extends SubsystemBase {
* @return The angle corrected for the quadrent
*/
public static final double correctQuadrent(double angle, Point guess, Point circlePoint) {
if(circlePoint.x - guess.x < 0) {
if(circlePoint.x - guess.x < 0)
return angle - Math.PI;
}
return angle;
}
+158
View File
@@ -0,0 +1,158 @@
// 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.
package frc4388.utility;
import java.security.InvalidParameterException;
import org.opencv.core.Point;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.wpilibj.drive.Vector2d;
/** Aarav's good vector class (better than WPILib)
* @author Aarav Shah */
public class Vector2D extends Vector2d {
public double x;
public double y;
public double angle;
public Vector2D() {
this(0, 0);
}
public Vector2D(double x, double y) {
super(x, y);
this.x = x;
this.y = y;
this.angle = Math.atan2(this.y, this.x);
}
public Vector2D(double[] vec) {
this(vec[0], vec[1]);
if (vec.length != 2) {
throw new InvalidParameterException();
}
}
public Vector2D(Point p) {
this(p.x, p.y);
}
public Vector2D(Translation2d t) {
this(t.getX(), t.getY());
}
/**
* Add two vectors, component-wise.
* @param v1 First vector in the addition.
* @param v2 Second vector in the addition.
* @return New vector which is the sum.
*/
public static Vector2D add(Vector2D v1, Vector2D v2) {
return new Vector2D(v1.x + v2.x, v1.y + v2.y);
}
/**
* Adds vector to current object
* @param v Vector to add
*/
public void add(Vector2D v) {
this.x += v.x;
this.y += v.x;
}
/**
* Subtract two vectors, component-wise.
* @param v1 First vector in the subtraction.
* @param v2 Second vector in the subtraction.
* @return New vector which is the difference.
*/
public static Vector2D subtract(Vector2D v1, Vector2D v2) {
return new Vector2D(v1.x - v2.x, v1.y - v2.y);
}
/**
* Subtracts vector from current object
* @param v Vector to subtract
*/
public void subtract(Vector2D v) {
this.x -= v.x;
this.y -= v.x;
}
/**
* Multiply a vector with a scalar, component-wise.
* @param v1 Vector to multiply.
* @param v2 Scalar to multiply.
* @return New vector which is the product.
*/
public static Vector2D multiply(Vector2D v1, double scalar) {
return new Vector2D(scalar * v1.x, scalar * v1.y);
}
/**
* Multiply a vector with a scalar, component-wise.
* @param scalar Scalar to multiply
*/
public void multiply(double scalar) {
this.x *= scalar;
this.y *= scalar;
}
/**
* Divide a vector with a scalar, component-wise.
* @param v1 Vector to divide.
* @param v2 Scalar to divide.
* @return New vector which is the division.
*/
public static Vector2D divide(Vector2D v1, double scalar) {
return new Vector2D(v1.x / scalar, v1.y / scalar);
}
/**
* Divide a vector with a scalar, component-wise.
* @param scalar Scalar to divide
*/
public void divide(double scalar) {
this.x /= scalar;
this.y /= scalar;
}
/**
* Find unit vector.
* @return The unit vector.
*/
public Vector2D unit() {
return new Vector2D(this.x / this.magnitude(), this.y / this.magnitude());
}
/**
* Round a vector to a certain number of places, component-wise.
* @param v Vector to round.
* @param places Number of places to round to.
* @return New rounded vector.
*/
public static Vector2D round(Vector2D v, int places) {
int scale = (int) Math.pow(10, places);
v = Vector2D.multiply(v, scale);
v.x = Math.round(v.x);
v.y = Math.round(v.y);
v.x = v.x / scale;
v.y = v.y / scale;
return v;
}
@Override
public String toString() {
return "<" + this.x + ", " + this.y + ">";
}
}
@@ -4,11 +4,6 @@
package frc4388.utility;
import edu.wpi.first.math.geometry.Pose2d;
import edu.wpi.first.math.geometry.Rotation2d;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.wpilibj.drive.Vector2d;
import frc4388.robot.subsystems.BoomBoom;
import frc4388.robot.subsystems.SwerveDrive;
@@ -18,80 +13,30 @@ public class VelocityCorrection {
SwerveDrive swerve;
BoomBoom boomBoom;
// vectors
public Vector2d position;
public Vector2d cartesianVelocity;
public Vector2d tangentialVelocity, unitTangentialVelocity;
public Vector2d radialVelocity, unitRadialVelocity;
double radius;
// vectors (in ft and ft/sec)
public Vector2D position;
public Vector2D cartesianVelocity;
// find
public Vector2d target;
public Vector2D target;
public VelocityCorrection(SwerveDrive swerve, BoomBoom boomBoom) {
this.swerve = swerve;
this.boomBoom = boomBoom;
position = new Vector2d(5, 0);//new Vector2d(this.swerve.getOdometry().getX(), this.swerve.getOdometry().getY());
cartesianVelocity = new Vector2d(1, 0);//new Vector2d(this.swerve.getChassisSpeeds().vxMetersPerSecond, this.swerve.getChassisSpeeds().vyMetersPerSecond);
tangentialVelocity = getTangentialVelocity();
radialVelocity = getRadialVelocity();
radius = position.magnitude();
position = new Vector2D(5, 0);//new Vector2D(this.swerve.getOdometry().getX(), this.swerve.getOdometry().getY());
cartesianVelocity = new Vector2D(-2, 3);//new Vector2D(this.swerve.getChassisSpeeds().vxMetersPerSecond, this.swerve.getChassisSpeeds().vyMetersPerSecond);
target = getTargetPoint();
}
private Vector2d getRadialVelocity() {
Vector2d ret = VelocityCorrection.subtract(cartesianVelocity, tangentialVelocity);
unitRadialVelocity = VelocityCorrection.multiply(ret, (1/ret.magnitude()));
return ret;
}
private Vector2d getTangentialVelocity() {
double hubElevation = Math.atan2(position.y, position.x);
unitTangentialVelocity = new Vector2d(Math.cos(hubElevation - (Math.PI/2)), Math.sin(hubElevation - (Math.PI/2)));
double tangentialVelocityMag = cartesianVelocity.scalarProject(unitTangentialVelocity);
return VelocityCorrection.multiply(unitTangentialVelocity, tangentialVelocityMag);
}
private Vector2d getTargetPoint() {
double drumVelocity = boomBoom.getVelocity(radius);
double ballVelocity = drumVelocity; // TODO: convert from drum velocity to actual ball velocity
double approxShotTime = radius / ballVelocity; // TODO: better approximation to get shot time (physics/calculus?)
Vector2d tangentialTargetPoint = VelocityCorrection.multiply(unitTangentialVelocity, -1 * approxShotTime);
Vector2d radialTargetPoint = VelocityCorrection.multiply(unitRadialVelocity, -1 * approxShotTime);
Vector2d targetPoint = VelocityCorrection.add(tangentialTargetPoint, radialTargetPoint);
return targetPoint;
}
public static Vector2d add(Vector2d v1, Vector2d v2) {
return new Vector2d(v1.x + v2.x, v1.y + v2.y);
}
public static Vector2d subtract(Vector2d v1, Vector2d v2) {
return new Vector2d(v1.x - v2.x, v1.y - v2.y);
}
public static Vector2d multiply(Vector2d v1, double scalar) {
return new Vector2d(scalar * v1.x, scalar * v1.y);
}
public static Vector2d round(Vector2d v) {
v = VelocityCorrection.multiply(v, 1000);
v.x = Math.round(v.x);
v.y = Math.round(v.y);
v.x = v.x / 1000;
v.y = v.y / 1000;
return v;
private Vector2D getTargetPoint() {
double approxShotTime = 1; // TODO: get shot time from shooter tables
Vector2D targetPoint = Vector2D.multiply(this.cartesianVelocity, -1 * approxShotTime);
return Vector2D.round(targetPoint, 5);
}
}
@@ -0,0 +1,23 @@
package frc4388.utility.controller;
import edu.wpi.first.wpilibj.GenericHID;
public class ButtonBox extends GenericHID {
public ButtonBox(int port) {
super(port);
}
public enum Button {
kRightSwitch(1),
kMiddleSwitch(2),
kLeftSwitch(3),
kRightButton(4),
kLeftButton(5);
@SuppressWarnings("MemberName")
public final int value;
Button(int value) {
this.value = value;
}
}
}
@@ -3,6 +3,7 @@ package frc4388.utility.controller;
import static frc4388.robot.Constants.OIConstants.LEFT_AXIS_DEADBAND;
import edu.wpi.first.math.geometry.Translation2d;
import frc4388.robot.Constants.OIConstants;
public class DeadbandedRawXboxController extends RawXboxController {
public DeadbandedRawXboxController(int port) { super(port); }
@@ -13,7 +14,7 @@ public class DeadbandedRawXboxController extends RawXboxController {
public static Translation2d skewToDeadzonedCircle(double x, double y) {
Translation2d translation2d = new Translation2d(x, y);
double magnitude = translation2d.getNorm();
if (magnitude >= 1) return translation2d.div(magnitude);
if (OIConstants.SKEW_STICKS && magnitude >= 1) return translation2d.div(magnitude);
if (magnitude < LEFT_AXIS_DEADBAND) return new Translation2d(0,0);
return translation2d;
}
@@ -4,6 +4,7 @@ import static frc4388.robot.Constants.OIConstants.LEFT_AXIS_DEADBAND;
import edu.wpi.first.math.geometry.Translation2d;
import edu.wpi.first.wpilibj.XboxController;
import frc4388.robot.Constants.OIConstants;
public class DeadbandedXboxController extends XboxController {
public DeadbandedXboxController(int port) { super(port); }
@@ -14,7 +15,7 @@ public class DeadbandedXboxController extends XboxController {
public static Translation2d skewToDeadzonedCircle(double x, double y) {
Translation2d translation2d = new Translation2d(x, y);
double magnitude = translation2d.getNorm();
if (magnitude >= 1) return translation2d.div(magnitude);
if (OIConstants.SKEW_STICKS && magnitude >= 1) return translation2d.div(magnitude);
if (magnitude < LEFT_AXIS_DEADBAND) return new Translation2d(0,0);
return translation2d;
}
@@ -0,0 +1,274 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://www.desmos.com/api/v1.6/calculator.js?apiKey=dcb31709b452b1cf9dc26972add0fda6"></script>
<style>
#status {
position: absolute;
right: 0;
bottom: 0;
width: 60pt;
height: 20pt;
background-color: rgba(0, 0, 0, .8);
font-family: Arial, Helvetica, sans-serif;
text-align: center;
line-height: 20pt;
}
</style>
<title>Desmos Client</title>
</head>
<body>
<div id="calculator" style="width: 100wh; height: 100vh; position: absolute; top: 0; left: 0; right: 0; bottom: 0; overflow: hidden;"></div>
<div id="status" style="color: #00FF00;">Active</div>
<script>
const EXPRESSION_TYPES = {
'expression': ['number', 'point', 'array'],
'note': ['note'],
'table': ['table'],
'clear': ['all', 'except', 'single']
};
let status = document.getElementById('status');
let elt = document.getElementById('calculator');
let calculator = Desmos.GraphingCalculator(elt);
const clearState = calculator.getState();
let helperExpressions = {};
let variables = [];
let url = prompt("Robot URL", "localhost");
let port = 8000;
function requestVariables() {
let http = new XMLHttpRequest();
http.addEventListener("load", setVariables);
http.addEventListener("error", handleError);
http.open("POST", `http://${url}:${port}`);
let json = serverStringify(getVariables());
http.send(json);
}
function handleError() {
status.style = 'color: #FF0000;';
status.innerHTML = 'Offline';
console.log('Request failed');
setTimeout(requestVariables, 0);
}
function setVariables() {
status.style = 'color: #00FF00;';
status.innerHTML = 'Active';
variables = JSON.parse(this.responseText);
for(let variable of variables) {
if(EXPRESSION_TYPES['expression'].includes(variable['type'])) {
variable['lname'] = latexName(variable['name']);
calculator.setExpression({ id: variable['name'], latex: variable['lname'] + '=' + variable['value']});
} else if(EXPRESSION_TYPES['table'].includes(variable['type'])) {
let cols = getColumns(variable['value'].split(' '));
calculator.setExpression({ type: 'table', id: variable['name'], columns: cols});
} else if(EXPRESSION_TYPES['clear'].includes(variable['type'])) {
clear(variable['value'], variable['type']);
} else
console.log('Invalid input type : ' + variable['type']);
}
setTimeout(requestVariables, 0);
}
function getColumns(unparsedCols) {
let cols = [];
for(let col of unparsedCols) {
col = col.split(',');
let latexStr = latexName(col[0]);
let valuesArr = col.slice(1, col.length);
cols.push({ latex: latexStr, values: valuesArr });
}
return cols;
}
function clear(lname, type) {
let state = calculator.getState();
let expressions = state.expressions.list;
if(expressions.length === 0)
return;
let lnames = lname.split(',');
switch(type) {
case 'all':
state = clearState;
break;
case 'except':
for(let i = 0; i < expressions.length; i++) {
if(!expressions[i].latex.includes('='))
return;
let expressionName = regularName(expressions[i].latex.split('=')[0]);
if(!lnames.includes(expressionName)) {
expressions.splice(i, 1);
i--;
}
}
break;
case 'single':
for(let i = 0; i < expressions.length; i++) {
if(!expressions[i].latex.includes('='))
return;
if(lnames.includes(regularName(expressions[i].split('=')[0]))) {
expressions.splice(i, 1);
i--;
}
}
break;
}
calculator.setState(state);
}
function getVariables() {
let vars = [];
let expressions = calculator.getExpressions();
for(expression of expressions) {
if(expression.type === 'expression')
vars = readVariable(expression.latex);
else if(expression.type === 'table')
vars = readTable(expression);
else
console.log('Invalid output type : ' + expression.type);
}
return vars;
}
function findExpressionType(lvalue) {
let raw = lvalue.replace('\\left', '')
.replace('\\right', '');
if(raw.match(/\([a-zA-Z0-9._{}\(\)]*,[a-zA-Z0-9._{}\(\)]*\)/))
return 'point';
else if(raw.match(/\[[a-zA-Z0-9.,_{}\(\)]*\]/))
return 'array';
else
return 'number';
}
function readVariable(latex) {
if(!latex.includes('='))
return [];
let vars = [];
let lname = latex.split('=')[0];
let lvalue = latex.split('=')[1];
if(lname && lname != '' && lvalue && lvalue != '') {
let name = regularName(lname);
let value = regularValue(lvalue);
let type = findExpressionType(lvalue);
let numericValue, numericValueX, numericValueY, listValue;
switch(type) {
case 'number':
numericValue = getNumericValue(name, lname);
break;
case 'point':
numericValueX = getNumericValue(name + 'x', lname + '.x');
numericValueY = getNumericValue(name + 'y', lname + '.y');
break;
case 'array':
listValue = getListValue(name, lname);
break;
}
if(numericValue)
vars.push({"name": name, "type": "number", "value": numericValue});
if(numericValueX && numericValueY)
vars.push({"name": name, "type": "point", "value": numericValueX + ',' + numericValueY});
if(listValue)
vars.push({"name": name, "type": "array", "value": listValue.toString()});
}
return vars;
}
function readTable(expression) {
let vars = [];
for(let column of expression.columns) {
let name = regularName(column.latex);
let listValue = getListValue(name, column.latex);
if(listValue)
vars.push({"name": name, "type": "array", "value": listValue.toString()});
}
return vars;
}
function getNumericValue(key, lname) {
if(!(key in helperExpressions))
helperExpressions[key] = calculator.HelperExpression({ latex: lname });
else
return helperExpressions[key].numericValue;
}
function getListValue(key, lname) {
if(!(key in helperExpressions))
helperExpressions[key] = calculator.HelperExpression({ latex: lname });
else
return helperExpressions[key].listValue;
}
function latexName(name) {
return name.substring(0, 1) + '_{' + name.substring(1, name.length) + '}';
}
function regularName(lname) {
if(lname.length == 1)
return lname;
return lname.substring(0, 1) + lname.substring(3, lname.length - 1);
}
function regularValue(lvalue) {
let value = lvalue.replace('\\left(', '')
.replace('\\right)', '')
.replace('\\left[', '')
.replace('\\right]', '')
.replace('(', '')
.replace(')', '')
.replace('[', '')
.replace(']', '');
return value;
}
function serverStringify(vars) {
let stringified = '';
for(let variable of vars) {
stringified += variable['name'] + '\t'
+ variable['type'] + '\t'
+ variable['value'] + '\n';
}
return stringified;
}
setTimeout(requestVariables, 0);
</script>
</body>
</html>
@@ -0,0 +1,352 @@
package frc4388.utility.desmos;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.opencv.core.Point;
/**
* A http server that allows the robot to communicate with Desmos Graphing Calculator
*
* @author Daniel McGrath
* */
public class DesmosServer extends Thread {
private static HashMap<String, String[]> desmosQueue = new HashMap<>();
private static HashMap<String, String[]> readVariables = new HashMap<>();
private static int clearCount = 0;
private static boolean running = false;
private ServerSocket serverSocket;
private int activePort;
/**
* Creates DesmosServer running on port
* <p>
* Use this for cases when the robot is using the default port
*
* @param port The port the server will run on
* */
public DesmosServer(int port) {
activePort = port;
}
/**
* Creates DesmosServer running on port 5500
* */
public DesmosServer() {
activePort = 8000;
}
@Override
public void run() {
try {
runServer(activePort);
} catch(IOException err) {
err.printStackTrace();
}
}
/**
* Runs server on port
*
* @param port The port the server runs on
* @throws IOException
* */
public void runServer(int port) throws IOException {
System.out.println("Initializing DesmosServer on port " + port + "...");
serverSocket = new ServerSocket(port);
running = true;
System.out.println("DesmosServer is active!");
while(true) {
Socket client = serverSocket.accept();
handleClient(client);
}
}
/**
* Handles client requests
*
* @param client The client connection
* @throws IOException
* */
public void handleClient(Socket client) throws IOException {
InputStreamReader clientStream = new InputStreamReader(client.getInputStream());
BufferedReader bufferedReader = new BufferedReader(clientStream);
ArrayList<String> headers = new ArrayList<>();
String header;
while((header = bufferedReader.readLine()).length() != 0) {
headers.add(header);
}
String body = "";
while(bufferedReader.ready()) {
body += (char) bufferedReader.read();
}
readVariables(body);
sendResponse(client);
}
/**
* Sends JSON response with format
* <p>
* [
* {"int": "24"},
* {"double": "2.4"},
* {"point": "(2,4)"},
* {"list": "[2,4]"}
* ]
*
* @param client The client connection
* @throws IOException
* */
public void sendResponse(Socket client) throws IOException {
OutputStream clientOutput = client.getOutputStream();
// Write Headers
clientOutput.write("HTTP/1.1 200 OK\r\n".getBytes());
clientOutput.write("Access-Control-Allow-Origin: *\r\n".getBytes());
clientOutput.write("Keep-Alive: timeout=2, max=100\r\n".getBytes());
clientOutput.write("Connection: Keep-Alive\r\n".getBytes());
clientOutput.write("Content-Type: application/json\r\n\r\n".getBytes());
clientOutput.write(getJSONOutput().getBytes());
clientOutput.flush();
clientOutput.close();
clearCount = 0;
}
/**
* Produces JSON output containing Desmos output.
*
* @return JSON string to be read by Desmos client
* */
public static String getJSONOutput() {
String json = "[";
if(!desmosQueue.isEmpty()) {
Set<String> keySet = new HashSet<>(desmosQueue.keySet());
for(String key : keySet) {
json += "\n\t{"
+ "\"name\":\"" + key + "\","
+ "\"type\":\"" + desmosQueue.get(key)[0] + "\","
+ "\"value\":\"" + desmosQueue.get(key)[1] + "\""
+ "},";
desmosQueue.remove(key);
}
json = json.substring(0, json.length()-1); // remove comma at the end
}
json += "\n]";
return json;
}
/**
* Interpret client request and update variables
*
* @param requestBody Client request
*/
public static void readVariables(String requestBody) {
for(String variable : requestBody.split("\n")) {
if(variable.equals(""))
break;
String[] readVar = variable.split("\t");
readVariables.put(readVar[0], new String[] {readVar[1], readVar[2]});
}
}
/**
* Checks if the server is running
*
* @return The server status
*/
public static boolean isRunning() {
return running;
}
// ---------------------------------------------------------------------
/**
* Adds integer to desmos queue
*
* @param name Name of desmos variable
* @param value Integer value
* */
public static void putInteger(String name, Integer value) {
desmosQueue.put(name, new String[] {"number", value.toString()});
}
/**
* Adds double to desmos queue
*
* @param name Name of desmos variable
* @param value Double value
* */
public static void putDouble(String name, Double value) {
desmosQueue.put(name, new String[] {"number", value.toString()});
}
/**
* Adds point to desmos queue
*
* @param name Name of desmos variable
* @param value Point value
* */
public static void putPoint(String name, Point point) {
desmosQueue.put(name, new String[] {"point", "(" + point.x + "," + point.y + ")"});
}
public static void putArray(String name, double... arr) {
desmosQueue.put(name, new String[] {"array", Arrays.toString(arr).replace(" ", "")});
}
/**
* Adds table to desmos queue
*
* @param name ID of table
* @param id Column ID
* @param column Double array containing column values
* @param table Repeat id and column in sequence
* */
public static void putTable(String name, Object... table) {
String tableStr = "";
for(int i = 0; i < table.length; i += 2) {
// Check parameters
if(!(table[i] instanceof String)) { return; }
if(!(table[i+1] instanceof double[])) { return; }
tableStr += table[i] + ",";
String values = Arrays.toString((double[]) table[i+1]).replace(" ", "");
tableStr += values.substring(1, values.length() - 1);
tableStr += ' ';
}
tableStr = tableStr.substring(0, tableStr.length()-1); // remove space at the end
desmosQueue.put(name, new String[] {"table", tableStr});
}
/**
* Clears entire expression panel
*/
public static void clearAll() {
desmosQueue.put("clear" + clearCount, new String[] {"all", "n/a"});
clearCount++;
}
/**
* Clears entire expression panel except for expressions in {@code names}
*
* @param names Names which should be preserved in clear
*/
public static void clearExcept(String... names) {
String namesStr = Arrays.toString(names).replace(" ", "");
namesStr = namesStr.substring(1, namesStr.length()-1);
desmosQueue.put("clear" + clearCount, new String[] {"except", namesStr});
clearCount++;
}
/**
* Removes expressions in {@code names}
*
* @param names Names which should be removed
*/
public static void clearSpecific(String... names) {
String namesStr = Arrays.toString(names).replace(" ", "");
namesStr = namesStr.substring(1, namesStr.length()-1);
desmosQueue.put("clear" + clearCount, new String[] {"specific", namesStr});
clearCount++;
}
// ---------------------------------------------------------------------
/**
* Reads desmos integer
*
* @param name Desmos variable name
* @return Numeric value, if variable is an expression it will be evaluated
* <p>if variable is a double it will be cast to int
* */
public static int readInteger(String name) {
if(!readVariables.containsKey(name) || !readVariables.get(name)[0].equals("number"))
return 0;
return (int) Double.parseDouble(readVariables.get(name)[1]);
}
/**
* Reads desmos double
*
* @param name Desmos variable name
* @return Numeric value, if variable is an expression it will be evaluated
* */
public static double readDouble(String name) {
if(!readVariables.containsKey(name) || !readVariables.get(name)[0].equals("number"))
return 0;
return Double.parseDouble(readVariables.get(name)[1]);
}
/**
* Reads desmos point
*
* @param name Desmos variable name
* @return Point, if variable contains expressions they will be evaluated
* */
public static Point readPoint(String name) {
Point point = new Point();
if(!readVariables.containsKey(name) || !readVariables.get(name)[0].equals("point"))
return point;
String pointStr = readVariables.get(name)[1];
point.x = Double.parseDouble(pointStr.split(",")[0]);
point.y = Double.parseDouble(pointStr.split(",")[1]);
return point;
}
/**
* Reads desmos array, including table columns
*
* @param name Desmos variable name
* @returns Array of numeric values, if array contains expressions they will be evaluated
* */
public static double[] readArray(String name) {
if(!readVariables.containsKey(name) || !readVariables.get(name)[0].equals("array"))
return new double[0];
String[] unparsed = readVariables.get(name)[1].split(",");
double[] arr = new double[unparsed.length];
for(int i = 0; i < arr.length; i++)
arr[i] = Integer.parseInt(unparsed[i]);
return arr;
}
}
@@ -2,14 +2,19 @@ package frc4388.commands;
import org.junit.Test;
import frc4388.robot.commands.AimToCenter;
import frc4388.robot.commands.ShooterCommands.AimToCenter;
import org.junit.Assert;
public class AimToCenterTest {
private static final double DELTA = 1e-15;
@Test
/**
* Unit tests the isDeadzone function in AimToCenter.java
* @author Ryan Manley
* @link www.hoohle.com
*/
public void givenAngle_whenTestIfDeadzone_thenReturnIfInDeadzone() {
boolean output;