13 Commits

16 changed files with 541 additions and 233 deletions

View File

@@ -5,11 +5,11 @@ import com.acmerobotics.dashboard.config.Config;
@Config @Config
public class ServoPositions { public class ServoPositions {
public static double spindexer_intakePos1 = 0.19; public static double spindexer_intakePos1 = 0.18;
public static double spindexer_intakePos2 = 0.35;//0.5; public static double spindexer_intakePos2 = 0.36;//0.5;
public static double spindexer_intakePos3 = 0.51;//0.66; public static double spindexer_intakePos3 = 0.54;//0.66;
public static double spindexer_outtakeBall3 = 0.47; public static double spindexer_outtakeBall3 = 0.47;

View File

@@ -21,4 +21,6 @@ public class ShooterVars {
// VELOCITY CONSTANTS // VELOCITY CONSTANTS
public static int AUTO_CLOSE_VEL = 3175; //3300; public static int AUTO_CLOSE_VEL = 3175; //3300;
public static int AUTO_FAR_VEL = 4000; //TODO: test this public static int AUTO_FAR_VEL = 4000; //TODO: test this
public static Types.Motif currentMotif = Types.Motif.NONE;
} }

View File

@@ -0,0 +1,10 @@
package org.firstinspires.ftc.teamcode.constants;
public class Types {
public enum Motif {
NONE,
GPP, // Green, Purple, Purple
PGP, // Purple, Green, Purple
PPG // Purple, Purple, Green
}
}

View File

@@ -1,4 +0,0 @@
package org.firstinspires.ftc.teamcode.constants;
public class blank {
}

View File

@@ -19,6 +19,7 @@ import com.acmerobotics.roadrunner.TranslationalVelConstraint;
import com.acmerobotics.roadrunner.Vector2d; import com.acmerobotics.roadrunner.Vector2d;
import com.acmerobotics.roadrunner.ftc.Actions; import com.acmerobotics.roadrunner.ftc.Actions;
import com.arcrobotics.ftclib.controller.PIDFController; import com.arcrobotics.ftclib.controller.PIDFController;
import com.qualcomm.hardware.limelightvision.Limelight3A;
import com.qualcomm.hardware.lynx.LynxModule; import com.qualcomm.hardware.lynx.LynxModule;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp; import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
@@ -26,7 +27,6 @@ import com.qualcomm.robotcore.hardware.DcMotor;
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit; import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive; import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive;
import org.firstinspires.ftc.teamcode.utils.AprilTagWebcam;
import org.firstinspires.ftc.teamcode.utils.Flywheel; import org.firstinspires.ftc.teamcode.utils.Flywheel;
import org.firstinspires.ftc.teamcode.utils.Robot; import org.firstinspires.ftc.teamcode.utils.Robot;
import org.firstinspires.ftc.teamcode.utils.Servos; import org.firstinspires.ftc.teamcode.utils.Servos;
@@ -166,10 +166,7 @@ public class TeleopV3 extends LinearOpMode {
// robot.limelight.start(); // robot.limelight.start();
AprilTagWebcam webcam = new AprilTagWebcam(); Turret turret = new Turret(robot, TELE, robot.limelight);
webcam.init(robot, TELE);
Turret turret = new Turret(robot, TELE, webcam);
waitForStart(); waitForStart();
waitForStart(); waitForStart();
@@ -399,8 +396,6 @@ public class TeleopV3 extends LinearOpMode {
turret.trackGoal(deltaPose); turret.trackGoal(deltaPose);
webcam.update();
//VELOCITY AUTOMATIC //VELOCITY AUTOMATIC
if (targetingVel) { if (targetingVel) {
vel = targetingSettings.flywheelRPM; vel = targetingSettings.flywheelRPM;
@@ -637,7 +632,6 @@ public class TeleopV3 extends LinearOpMode {
} }
} }
// //
// if (shootAll) { // if (shootAll) {
// //
@@ -809,7 +803,6 @@ public class TeleopV3 extends LinearOpMode {
for (LynxModule hub : allHubs) { for (LynxModule hub : allHubs) {
hub.clearBulkCache(); hub.clearBulkCache();
} }
// //
TELE.addData("Spin1Green", green1 + ": " + ballIn(1)); TELE.addData("Spin1Green", green1 + ": " + ballIn(1));
TELE.addData("Spin2Green", green2 + ": " + ballIn(2)); TELE.addData("Spin2Green", green2 + ": " + ballIn(2));
@@ -821,6 +814,8 @@ public class TeleopV3 extends LinearOpMode {
TELE.addData("hood", robot.hood.getPosition()); TELE.addData("hood", robot.hood.getPosition());
TELE.addData("targetVel", vel); TELE.addData("targetVel", vel);
TELE.addData("Velocity", flywheel.getVelo()); TELE.addData("Velocity", flywheel.getVelo());
TELE.addData("Velo1", flywheel.velo1);
TELE.addData("Velo2", flywheel.velo2);
TELE.addData("shootOrder", shootOrder); TELE.addData("shootOrder", shootOrder);
TELE.addData("oddColor", oddBallColor); TELE.addData("oddColor", oddBallColor);
@@ -836,7 +831,7 @@ public class TeleopV3 extends LinearOpMode {
TELE.addData("shootall commanded", shootAll); TELE.addData("shootall commanded", shootAll);
// Targeting Debug // Targeting Debug
TELE.addData("robotX", robotX); TELE.addData("robotX", robotX);
TELE.addData( "robotY", robotY); TELE.addData("robotY", robotY);
TELE.addData("robotInchesX", targeting.robotInchesX); TELE.addData("robotInchesX", targeting.robotInchesX);
TELE.addData( "robotInchesY", targeting.robotInchesY); TELE.addData( "robotInchesY", targeting.robotInchesY);
TELE.addData("Targeting Interpolate", turretInterpolate); TELE.addData("Targeting Interpolate", turretInterpolate);

View File

@@ -4,32 +4,35 @@ import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.config.Config;
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry; import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
import com.qualcomm.hardware.limelightvision.LLResult; import com.qualcomm.hardware.limelightvision.LLResult;
import com.qualcomm.hardware.limelightvision.LLResultTypes;
import com.qualcomm.hardware.limelightvision.Limelight3A;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp; import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import java.util.List; import org.firstinspires.ftc.teamcode.utils.Robot;
import org.firstinspires.ftc.teamcode.utils.Turret;
@Config @Config
@TeleOp @TeleOp
//TODO: fix to get the apriltag that it is reading //TODO: fix to get the apriltag that it is reading
public class LimelightTest extends LinearOpMode { public class LimelightTest extends LinearOpMode {
MultipleTelemetry TELE; MultipleTelemetry TELE;
Turret turret;
Robot robot;
public static int pipeline = 0; //0 is for test; 1 for obelisk; 2 is for blue track; 3 is for red track public static int pipeline = 0; //0 is for test; 1 for obelisk; 2 is for blue track; 3 is for red track
public static int mode = 0; //0 for bare testing, 1 for obelisk, 2 for blue track, 3 for red track public static int mode = 0; //0 for bare testing, 1 for obelisk, 2 for blue track, 3 for red track
public static boolean turretMode = false;
public static double turretPos = 0.501;
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
Limelight3A limelight = hardwareMap.get(Limelight3A.class, "limelight");
TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry()); TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
limelight.pipelineSwitch(pipeline); robot = new Robot(hardwareMap);
turret = new Turret(robot, TELE, robot.limelight);
robot.limelight.pipelineSwitch(pipeline);
waitForStart(); waitForStart();
if (isStopRequested()) return; if (isStopRequested()) return;
limelight.start();
while (opModeIsActive()){ while (opModeIsActive()){
if (mode == 0){ if (mode == 0){
limelight.pipelineSwitch(pipeline); robot.limelight.pipelineSwitch(pipeline);
LLResult result = limelight.getLatestResult(); LLResult result = robot.limelight.getLatestResult();
if (result != null) { if (result != null) {
if (result.isValid()) { if (result.isValid()) {
TELE.addData("tx", result.getTx()); TELE.addData("tx", result.getTx());
@@ -38,40 +41,29 @@ public class LimelightTest extends LinearOpMode {
} }
} }
} else if (mode == 1){ } else if (mode == 1){
limelight.pipelineSwitch(1); int obeliskID = turret.detectObelisk();
LLResult result = limelight.getLatestResult(); TELE.addData("Limelight ID", obeliskID);
if (result != null && result.isValid()) { TELE.update();
List<LLResultTypes.FiducialResult> fiducials = result.getFiducialResults(); } else if (mode == 2 || mode == 3){ // Use redAlliance variable to switch between red and blue
for (LLResultTypes.FiducialResult fiducial : fiducials) { double tx = turret.getBearing();
int id = fiducial.getFiducialId(); double ty = turret.getTy();
TELE.addData("ID", id); double x = turret.getLimelightX();
TELE.update(); double y = turret.getLimelightY();
} TELE.addData("tx", tx);
TELE.addData("ty", ty);
} TELE.addData("x", x);
} else if (mode == 2){ TELE.addData("y", y);
limelight.pipelineSwitch(4); TELE.update();
LLResult result = limelight.getLatestResult();
if (result != null) {
if (result.isValid()) {
TELE.addData("tx", result.getTx());
TELE.addData("ty", result.getTy());
TELE.update();
}
}
} else if (mode == 3){
limelight.pipelineSwitch(5);
LLResult result = limelight.getLatestResult();
if (result != null) {
if (result.isValid()) {
TELE.addData("tx", result.getTx());
TELE.addData("ty", result.getTy());
TELE.update();
}
}
} else { } else {
limelight.pipelineSwitch(0); robot.limelight.pipelineSwitch(0);
} }
if (turretMode){
if (turretPos != 0.501){
turret.manualSetTurret(turretPos);
}
}
} }
} }
} }

View File

@@ -1,5 +1,6 @@
package org.firstinspires.ftc.teamcode.tests; package org.firstinspires.ftc.teamcode.tests;
import static org.firstinspires.ftc.teamcode.constants.ServoPositions.spindexer_intakePos1;
import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_in; import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_in;
import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_out; import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_out;
@@ -12,28 +13,40 @@ import com.qualcomm.robotcore.hardware.DcMotorEx;
import org.firstinspires.ftc.teamcode.utils.Flywheel; import org.firstinspires.ftc.teamcode.utils.Flywheel;
import org.firstinspires.ftc.teamcode.utils.Robot; import org.firstinspires.ftc.teamcode.utils.Robot;
import org.firstinspires.ftc.teamcode.utils.Spindexer;
@Config @Config
@TeleOp @TeleOp
public class ShooterTest extends LinearOpMode { public class ShooterTest extends LinearOpMode {
public static int mode = 1;
public static int mode = 0;
public static double parameter = 0.0; public static double parameter = 0.0;
// --- CONSTANTS YOU TUNE --- // --- CONSTANTS YOU TUNE ---
//TODO: @Daniel FIX THE BELOW CONSTANTS A LITTLE IF NEEDED //TODO: @Daniel FIX THE BELOW CONSTANTS A LITTLE IF NEEDED
public static double Velocity = 0.0; public static double Velocity = 0.0;
public static double P = 40.0; public static double P = 255.0;
public static double I = 0.3; public static double I = 0.0;
public static double D = 7.0; public static double D = 0.0;
public static double F = 10.0; public static double F = 7.5;
public static double transferPower = 1.0; public static double transferPower = 1.0;
public static double hoodPos = 0.501; public static double hoodPos = 0.501;
public static double turretPos = 0.501; public static double turretPos = 0.501;
public static boolean shoot = false; public static boolean shoot = false;
public static boolean intake = false;
Robot robot; Robot robot;
Flywheel flywheel; Flywheel flywheel;
double shootStamp = 0.0;
boolean shootAll = false;
public double spinPow = 0.09;
public static boolean enableHoodAutoOpen = false;
public double hoodAdjust = 0.0;
public static double hoodAdjustFactor = 1.0;
Spindexer spindexer ;
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
@@ -41,6 +54,7 @@ public class ShooterTest extends LinearOpMode {
DcMotorEx leftShooter = robot.shooter1; DcMotorEx leftShooter = robot.shooter1;
DcMotorEx rightShooter = robot.shooter2; DcMotorEx rightShooter = robot.shooter2;
flywheel = new Flywheel(hardwareMap); flywheel = new Flywheel(hardwareMap);
spindexer = new Spindexer(hardwareMap);
MultipleTelemetry TELE = new MultipleTelemetry( MultipleTelemetry TELE = new MultipleTelemetry(
telemetry, FtcDashboard.getInstance().getTelemetry() telemetry, FtcDashboard.getInstance().getTelemetry()
@@ -61,15 +75,60 @@ public class ShooterTest extends LinearOpMode {
} }
if (hoodPos != 0.501) { if (hoodPos != 0.501) {
robot.hood.setPosition(hoodPos); if (enableHoodAutoOpen) {
robot.hood.setPosition(hoodPos+(hoodAdjustFactor*(flywheel.getVelo()/Velocity)));
} else {
robot.hood.setPosition(hoodPos);
}
} }
robot.transfer.setPower(transferPower); if (intake) {
if (shoot) { robot.intake.setPower(1);
robot.transferServo.setPosition(transferServo_in);
} else { } else {
robot.transferServo.setPosition(transferServo_out); robot.intake.setPower(0);
} }
if (shoot) {
shootStamp = getRuntime();
shootAll = true;
shoot = false;
robot.transfer.setPower(transferPower);
}
if (shootAll) {
//intake = false;
//reject = false;
// TODO: Change starting position based on desired order to shoot green ball
//spindexPos = spindexer_intakePos1;
if (getRuntime() - shootStamp < 3.5) {
robot.transferServo.setPosition(transferServo_in);
robot.spin1.setPower(-spinPow);
robot.spin2.setPower(spinPow);
} else {
robot.transferServo.setPosition(transferServo_out);
//spindexPos = spindexer_intakePos1;
shootAll = false;
robot.transferServo.setPosition(transferServo_out);
robot.transfer.setPower(0);
robot.spin1.setPower(0);
robot.spin2.setPower(0);
spindexer.resetSpindexer();
spindexer.processIntake();
}
} else {
spindexer.processIntake();
}
TELE.addData("Velocity", flywheel.getVelo()); TELE.addData("Velocity", flywheel.getVelo());
TELE.addData("Velocity 1", flywheel.getVelo1()); TELE.addData("Velocity 1", flywheel.getVelo1());
TELE.addData("Velocity 2", flywheel.getVelo2()); TELE.addData("Velocity 2", flywheel.getVelo2());

View File

@@ -8,15 +8,12 @@ import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive; import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive;
import org.firstinspires.ftc.teamcode.utils.AprilTagWebcam;
import org.firstinspires.ftc.teamcode.utils.Robot; import org.firstinspires.ftc.teamcode.utils.Robot;
import org.firstinspires.ftc.teamcode.utils.Turret; import org.firstinspires.ftc.teamcode.utils.Turret;
import org.firstinspires.ftc.vision.apriltag.AprilTagDetection;
@Autonomous @Autonomous
@Config @Config
public class TurretTest extends LinearOpMode { public class TurretTest extends LinearOpMode {
public static boolean zeroTurr = false; public static boolean zeroTurr = false;
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
@@ -26,13 +23,9 @@ public class TurretTest extends LinearOpMode {
telemetry, FtcDashboard.getInstance().getTelemetry() telemetry, FtcDashboard.getInstance().getTelemetry()
); );
AprilTagWebcam webcam = new AprilTagWebcam(); Turret turret = new Turret(robot, TELE, robot.limelight);
webcam.init(robot, TELE);
Turret turret = new Turret(robot, TELE, webcam);
waitForStart(); waitForStart();
MecanumDrive drive = new MecanumDrive(hardwareMap, new Pose2d(15, 0,0)); MecanumDrive drive = new MecanumDrive(hardwareMap, new Pose2d(15, 0,0));
while(opModeIsActive()){ while(opModeIsActive()){
@@ -40,17 +33,16 @@ public class TurretTest extends LinearOpMode {
drive.updatePoseEstimate(); drive.updatePoseEstimate();
turret.trackGoal(drive.localizer.getPose()); turret.trackGoal(drive.localizer.getPose());
webcam.update();
webcam.displayAllTelemetry();
TELE.addData("tpos", turret.getTurrPos()); TELE.addData("tpos", turret.getTurrPos());
TELE.addData("Limelight tx", turret.getBearing());
TELE.addData("Limelight ty", turret.getTy());
TELE.addData("Limelight X", turret.getLimelightX());
TELE.addData("Limelight Y", turret.getLimelightY());
if(zeroTurr){ if(zeroTurr){
turret.zeroTurretEncoder(); turret.zeroTurretEncoder();
} }
TELE.update(); TELE.update();
} }

View File

@@ -9,8 +9,8 @@ public class Flywheel {
Robot robot; Robot robot;
public PIDFCoefficients shooterPIDF1, shooterPIDF2; public PIDFCoefficients shooterPIDF1, shooterPIDF2;
double velo = 0.0; double velo = 0.0;
double velo1 = 0.0; public double velo1 = 0.0;
double velo2 = 0.0; public double velo2 = 0.0;
double targetVelocity = 0.0; double targetVelocity = 0.0;
double powPID = 0.0; double powPID = 0.0;
boolean steady = false; boolean steady = false;
@@ -38,10 +38,14 @@ public class Flywheel {
// Set the robot PIDF for the next cycle. // Set the robot PIDF for the next cycle.
public void setPIDF(double p, double i, double d, double f) { public void setPIDF(double p, double i, double d, double f) {
robot.shooterPIDF.p = p; shooterPIDF1.p = p;
robot.shooterPIDF.i = i; shooterPIDF1.i = i;
robot.shooterPIDF.d = d; shooterPIDF1.d = d;
robot.shooterPIDF.f = f; shooterPIDF1.f = f;
shooterPIDF2.p = p;
shooterPIDF2.i = i;
shooterPIDF2.d = d;
shooterPIDF2.f = f;
} }
// Convert from RPM to Ticks per Second // Convert from RPM to Ticks per Second
@@ -54,10 +58,6 @@ public class Flywheel {
targetVelocity = commandedVelocity; targetVelocity = commandedVelocity;
// Add code here to set PIDF based on desired RPM // Add code here to set PIDF based on desired RPM
//robot.shooterPIDF.p = P;
//robot.shooterPIDF.i = I;
//robot.shooterPIDF.d = D;
//robot.shooterPIDF.f = F;
robot.shooter1.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF1); robot.shooter1.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF1);
robot.shooter2.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF2); robot.shooter2.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF2);

View File

@@ -35,11 +35,9 @@ public class PositionalServoProgrammer extends LinearOpMode {
TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry()); TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
servo = new Servos(hardwareMap); servo = new Servos(hardwareMap);
AprilTagWebcam cam = new AprilTagWebcam();
cam.init(robot, TELE);
turret = new Turret(robot, TELE,cam ); turret = new Turret(robot, TELE, robot.limelight );
waitForStart(); waitForStart();
if (isStopRequested()) return; if (isStopRequested()) return;
while (opModeIsActive()){ while (opModeIsActive()){

View File

@@ -20,8 +20,8 @@ public class Robot {
//Initialize Public Components //Initialize Public Components
public static boolean usingLimelight = false; public static boolean usingLimelight = true;
public static boolean usingCamera = true; public static boolean usingCamera = false;
public DcMotorEx frontLeft; public DcMotorEx frontLeft;
public DcMotorEx frontRight; public DcMotorEx frontRight;
public DcMotorEx backLeft; public DcMotorEx backLeft;
@@ -29,10 +29,10 @@ public class Robot {
public DcMotorEx intake; public DcMotorEx intake;
public DcMotorEx transfer; public DcMotorEx transfer;
public PIDFCoefficients shooterPIDF; public PIDFCoefficients shooterPIDF;
public double shooterPIDF_P = 10.0; public double shooterPIDF_P = 255.0;
public double shooterPIDF_I = 0.6; public double shooterPIDF_I = 0.0;
public double shooterPIDF_D = 5.0; public double shooterPIDF_D = 0.0;
public double shooterPIDF_F = 10.0; public double shooterPIDF_F = 7.5;
public double[] shooterPIDF_StepSizes = {10.0, 1.0, 0.001, 0.0001}; public double[] shooterPIDF_StepSizes = {10.0, 1.0, 0.001, 0.0001};
public DcMotorEx shooter1; public DcMotorEx shooter1;
public DcMotorEx shooter2; public DcMotorEx shooter2;

View File

@@ -8,7 +8,7 @@ import com.qualcomm.robotcore.hardware.HardwareMap;
public class Servos { public class Servos {
//PID constants //PID constants
// TODO: get PIDF constants // TODO: get PIDF constants
public static double spinP = 3.3, spinI = 0, spinD = 0.1, spinF = 0.02; public static double spinP = 2.0, spinI = 0, spinD = 0.3, spinF = 0.02;
public static double turrP = 1.1, turrI = 0.25, turrD = 0.0625, turrF = 0; public static double turrP = 1.1, turrI = 0.25, turrD = 0.0625, turrF = 0;
public static double spin_scalar = 1.0086; public static double spin_scalar = 1.0086;
public static double spin_restPos = 0.0; public static double spin_restPos = 0.0;
@@ -40,7 +40,7 @@ public class Servos {
} }
public boolean spinEqual(double pos) { public boolean spinEqual(double pos) {
return Math.abs(pos - this.getSpinPos()) < 0.02; return Math.abs(pos - this.getSpinPos()) < 0.03;
} }
public double getTurrPos() { public double getTurrPos() {

View File

@@ -16,9 +16,11 @@ import static org.firstinspires.ftc.teamcode.utils.Servos.spinI;
import static org.firstinspires.ftc.teamcode.utils.Servos.spinP; import static org.firstinspires.ftc.teamcode.utils.Servos.spinP;
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit; import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
import org.firstinspires.ftc.teamcode.constants.Types;
import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive; import org.firstinspires.ftc.teamcode.libs.RR.MecanumDrive;
public class Spindexer { public class Spindexer {
Robot robot; Robot robot;
Servos servos; Servos servos;
Flywheel flywheel; Flywheel flywheel;
@@ -36,6 +38,7 @@ public class Spindexer {
public double distanceFrontDriver = 0.0; public double distanceFrontDriver = 0.0;
public double distanceFrontPassenger = 0.0; public double distanceFrontPassenger = 0.0;
public Types.Motif desiredMotif = Types.Motif.NONE;
// For Use // For Use
enum RotatedBallPositionNames { enum RotatedBallPositionNames {
REARCENTER, REARCENTER,
@@ -62,6 +65,8 @@ public class Spindexer {
SHOOTNEXT, SHOOTNEXT,
SHOOTMOVING, SHOOTMOVING,
SHOOTWAIT, SHOOTWAIT,
SHOOT_ALL_PREP,
SHOOT_ALL_READY
}; };
public IntakeState currentIntakeState = IntakeState.UNKNOWN_START; public IntakeState currentIntakeState = IntakeState.UNKNOWN_START;
@@ -272,7 +277,6 @@ public class Spindexer {
if (servos.spinEqual(intakePositions[commandedIntakePosition])) { if (servos.spinEqual(intakePositions[commandedIntakePosition])) {
currentIntakeState = Spindexer.IntakeState.UNKNOWN_DETECT; currentIntakeState = Spindexer.IntakeState.UNKNOWN_DETECT;
stopSpindexer(); stopSpindexer();
detectBalls(true, true);
unknownColorDetect = 0; unknownColorDetect = 0;
} else { } else {
// Keep moving the spindexer // Keep moving the spindexer
@@ -283,7 +287,7 @@ public class Spindexer {
if (unknownColorDetect >5) { if (unknownColorDetect >5) {
currentIntakeState = Spindexer.IntakeState.FINDNEXT; currentIntakeState = Spindexer.IntakeState.FINDNEXT;
} else { } else {
detectBalls(true, true); //detectBalls(true, true);
unknownColorDetect++; unknownColorDetect++;
} }
break; break;
@@ -301,32 +305,32 @@ public class Spindexer {
// Find Next Open Position and start movement // Find Next Open Position and start movement
double currentSpindexerPos = servos.getSpinPos(); double currentSpindexerPos = servos.getSpinPos();
double commandedtravelDistance = 2.0; double commandedtravelDistance = 2.0;
double proposedTravelDistance = Math.abs(intakePositions[0] - currentSpindexerPos); //double proposedTravelDistance = Math.abs(intakePositions[0] - currentSpindexerPos);
if (ballPositions[0].isEmpty && (proposedTravelDistance < commandedtravelDistance)) { //if (ballPositions[0].isEmpty && (proposedTravelDistance < commandedtravelDistance)) {
if (ballPositions[0].isEmpty) {
// Position 1 // Position 1
commandedIntakePosition = 0; commandedIntakePosition = 0;
servos.setSpinPos(intakePositions[commandedIntakePosition]); servos.setSpinPos(intakePositions[commandedIntakePosition]);
currentIntakeState = Spindexer.IntakeState.MOVING; currentIntakeState = Spindexer.IntakeState.MOVING;
commandedtravelDistance = proposedTravelDistance;
} }
proposedTravelDistance = Math.abs(intakePositions[1] - currentSpindexerPos); //proposedTravelDistance = Math.abs(intakePositions[1] - currentSpindexerPos);
if (ballPositions[1].isEmpty && (proposedTravelDistance < commandedtravelDistance)) { //if (ballPositions[1].isEmpty && (proposedTravelDistance < commandedtravelDistance)) {
if (ballPositions[1].isEmpty) {
// Position 2 // Position 2
commandedIntakePosition = 1; commandedIntakePosition = 1;
servos.setSpinPos(intakePositions[commandedIntakePosition]); servos.setSpinPos(intakePositions[commandedIntakePosition]);
currentIntakeState = Spindexer.IntakeState.MOVING; currentIntakeState = Spindexer.IntakeState.MOVING;
commandedtravelDistance = proposedTravelDistance;
} }
proposedTravelDistance = Math.abs(intakePositions[2] - currentSpindexerPos); //proposedTravelDistance = Math.abs(intakePositions[2] - currentSpindexerPos);
if (ballPositions[2].isEmpty && (proposedTravelDistance < commandedtravelDistance)) { if (ballPositions[2].isEmpty) {
// Position 3 // Position 3
commandedIntakePosition = 2; commandedIntakePosition = 2;
servos.setSpinPos(intakePositions[commandedIntakePosition]); servos.setSpinPos(intakePositions[commandedIntakePosition]);
currentIntakeState = Spindexer.IntakeState.MOVING; currentIntakeState = Spindexer.IntakeState.MOVING;
commandedtravelDistance = proposedTravelDistance;
} }
if (currentIntakeState != Spindexer.IntakeState.MOVING) { if (currentIntakeState != Spindexer.IntakeState.MOVING) {
// Full // Full
//commandedIntakePosition = bestFitMotif();
currentIntakeState = Spindexer.IntakeState.FULL; currentIntakeState = Spindexer.IntakeState.FULL;
} }
moveSpindexerToPos(intakePositions[commandedIntakePosition]); moveSpindexerToPos(intakePositions[commandedIntakePosition]);
@@ -337,7 +341,7 @@ public class Spindexer {
if (servos.spinEqual(intakePositions[commandedIntakePosition])) { if (servos.spinEqual(intakePositions[commandedIntakePosition])) {
currentIntakeState = Spindexer.IntakeState.INTAKE; currentIntakeState = Spindexer.IntakeState.INTAKE;
stopSpindexer(); stopSpindexer();
detectBalls(false, false); //detectBalls(false, false);
} else { } else {
// Keep moving the spindexer // Keep moving the spindexer
moveSpindexerToPos(intakePositions[commandedIntakePosition]); moveSpindexerToPos(intakePositions[commandedIntakePosition]);
@@ -355,6 +359,28 @@ public class Spindexer {
moveSpindexerToPos(intakePositions[commandedIntakePosition]); moveSpindexerToPos(intakePositions[commandedIntakePosition]);
break; break;
case SHOOT_ALL_PREP:
// We get here with function call to prepareToShootMotif
// Stopping when we get to the new position
if (servos.spinEqual(intakePositions[commandedIntakePosition])) {
currentIntakeState = Spindexer.IntakeState.SHOOT_ALL_READY;
} else {
// Keep moving the spindexer
moveSpindexerToPos(intakePositions[commandedIntakePosition]); // Possible error: should it be using "outakePositions" instead of "intakePositions"
}
break;
case SHOOT_ALL_READY:
// Double Check Colors
//detectBalls(false, false); // Minimize hardware calls
if (ballPositions[0].isEmpty && ballPositions[1].isEmpty && ballPositions[2].isEmpty) {
// All ball shot move to intake state
currentIntakeState = Spindexer.IntakeState.FINDNEXT;
}
// Maintain Position
moveSpindexerToPos(intakePositions[commandedIntakePosition]);
break;
case SHOOTNEXT: case SHOOTNEXT:
// Find Next Open Position and start movement // Find Next Open Position and start movement
if (!ballPositions[0].isEmpty) { if (!ballPositions[0].isEmpty) {
@@ -383,16 +409,6 @@ public class Spindexer {
// Stopping when we get to the new position // Stopping when we get to the new position
if (servos.spinEqual(outakePositions[commandedIntakePosition])) { if (servos.spinEqual(outakePositions[commandedIntakePosition])) {
currentIntakeState = Spindexer.IntakeState.SHOOTWAIT; currentIntakeState = Spindexer.IntakeState.SHOOTWAIT;
ballPositions[commandedIntakePosition].isEmpty = true;
// Advance to next full position and wait
// commandedIntakePosition++;
// if (commandedIntakePosition > 2) {
// commandedIntakePosition = 0;
// }
// // Continue moving to next position
// servos.setSpinPos(intakePositions[commandedIntakePosition]);
// currentIntakeState = Spindexer.IntakeState.MOVING;
} else { } else {
// Keep moving the spindexer // Keep moving the spindexer
moveSpindexerToPos(intakePositions[commandedIntakePosition]); // Possible error: should it be using "outakePositions" instead of "intakePositions" moveSpindexerToPos(intakePositions[commandedIntakePosition]); // Possible error: should it be using "outakePositions" instead of "intakePositions"
@@ -404,7 +420,7 @@ public class Spindexer {
if (servos.spinEqual(intakePositions[commandedIntakePosition])) { if (servos.spinEqual(intakePositions[commandedIntakePosition])) {
currentIntakeState = Spindexer.IntakeState.INTAKE; currentIntakeState = Spindexer.IntakeState.INTAKE;
stopSpindexer(); stopSpindexer();
detectBalls(true, false); //detectBalls(true, false);
} else { } else {
// Keep moving the spindexer // Keep moving the spindexer
moveSpindexerToPos(intakePositions[commandedIntakePosition]); moveSpindexerToPos(intakePositions[commandedIntakePosition]);
@@ -420,6 +436,55 @@ public class Spindexer {
return false; return false;
} }
public void setDesiredMotif (Types.Motif newMotif) {
desiredMotif = newMotif;
}
// Returns the best fit for the motiff
public int bestFitMotif () {
switch (desiredMotif) {
case GPP:
if (ballPositions[0].ballColor == BallColor.GREEN) {
return 2;
} else if (ballPositions[1].ballColor == BallColor.GREEN) {
return 0;
} else {
return 1;
}
//break;
case PGP:
if (ballPositions[0].ballColor == BallColor.GREEN) {
return 0;
} else if (ballPositions[1].ballColor == BallColor.GREEN) {
return 1;
} else {
return 3;
}
//break;
case PPG:
if (ballPositions[0].ballColor == BallColor.GREEN) {
return 1;
} else if (ballPositions[1].ballColor == BallColor.GREEN) {
return 0;
} else {
return 2;
}
//break;
case NONE:
return 0;
//break;
}
return 0;
}
void prepareToShootMotif () {
commandedIntakePosition = bestFitMotif();
}
void shootAllToIntake () {
currentIntakeState = Spindexer.IntakeState.FINDNEXT;
}
public void update() public void update()
{ {
} }

View File

@@ -9,11 +9,13 @@ import com.qualcomm.robotcore.hardware.HardwareMap;
public class Targeting { public class Targeting {
MultipleTelemetry TELE; MultipleTelemetry TELE;
double cancelOffsetX = 7.071067811; double cancelOffsetX = 0.0; // was -40.0
double cancelOffsetY = 7.071067811; double cancelOffsetY = 0.0; // was 7.0
double unitConversionFactor = 0.95; double unitConversionFactor = 0.95;
int tileSize = 24; //inches int tileSize = 24; //inches
public final int TILE_UPPER_QUARTILE = 18;
public final int TILE_LOWER_QUARTILE = 6;
public double robotInchesX, robotInchesY = 0.0; public double robotInchesX, robotInchesY = 0.0;
@@ -37,33 +39,33 @@ public class Targeting {
static { static {
KNOWNTARGETING = new Settings[6][6]; KNOWNTARGETING = new Settings[6][6];
// ROW 0 - Closet to the goals // ROW 0 - Closet to the goals
KNOWNTARGETING[0][0] = new Settings (3000.0, 0.25); KNOWNTARGETING[0][0] = new Settings (2300.0, 0.93);
KNOWNTARGETING[0][1] = new Settings (3001.0, 0.25); KNOWNTARGETING[0][1] = new Settings (2300.0, 0.93);
KNOWNTARGETING[0][2] = new Settings (3002.0, 0.25); KNOWNTARGETING[0][2] = new Settings (2500.0, 0.78);
KNOWNTARGETING[0][3] = new Settings (3302.0, 0.2); KNOWNTARGETING[0][3] = new Settings (2800.0, 0.68);
KNOWNTARGETING[0][4] = new Settings (3503.0, 0.15); KNOWNTARGETING[0][4] = new Settings (3000.0, 0.58);
KNOWNTARGETING[0][5] = new Settings (3505.0, 0.15); KNOWNTARGETING[0][5] = new Settings (3000.0, 0.58);
// ROW 1 // ROW 1
KNOWNTARGETING[1][0] = new Settings (3010.0, 0.25); KNOWNTARGETING[1][0] = new Settings (2300.0, 0.93);
KNOWNTARGETING[1][1] = new Settings (3011.0, 0.25); KNOWNTARGETING[1][1] = new Settings (2300.0, 0.93);
KNOWNTARGETING[1][2] = new Settings (3012.0, 0.25); KNOWNTARGETING[1][2] = new Settings (2600.0, 0.78);
KNOWNTARGETING[1][3] = new Settings (3313.0, 0.15); KNOWNTARGETING[1][3] = new Settings (2800.0, 0.62);
KNOWNTARGETING[1][4] = new Settings (3514.0, 0.15); KNOWNTARGETING[1][4] = new Settings (3000.0, 0.55);
KNOWNTARGETING[1][5] = new Settings (3515.0, 0.15); KNOWNTARGETING[1][5] = new Settings (3200.0, 0.50);
// ROW 2 // ROW 2
KNOWNTARGETING[2][0] = new Settings (3020.0, 0.1); KNOWNTARGETING[2][0] = new Settings (2500.0, 0.78);
KNOWNTARGETING[2][1] = new Settings (3000.0, 0.25); KNOWNTARGETING[2][1] = new Settings (2500.0, 0.78);
KNOWNTARGETING[2][2] = new Settings (3000.0, 0.15); KNOWNTARGETING[2][2] = new Settings (2700.0, 0.60);
KNOWNTARGETING[2][3] = new Settings (3000.0, 0.15); KNOWNTARGETING[2][3] = new Settings (2900.0, 0.53);
KNOWNTARGETING[2][4] = new Settings (3524.0, 0.15); KNOWNTARGETING[2][4] = new Settings (3100.0, 0.50);
KNOWNTARGETING[2][5] = new Settings (3525.0, 0.15); KNOWNTARGETING[2][5] = new Settings (3100.0, 0.50);
// ROW 3 // ROW 3
KNOWNTARGETING[3][0] = new Settings (3030.0, 0.15); KNOWNTARGETING[3][0] = new Settings (2900.0, 0.50);
KNOWNTARGETING[3][1] = new Settings (3031.0, 0.15); KNOWNTARGETING[3][1] = new Settings (2900.0, 0.50);
KNOWNTARGETING[3][2] = new Settings (3000.0, 0.15); KNOWNTARGETING[3][2] = new Settings (2900.0, 0.50);
KNOWNTARGETING[3][3] = new Settings (3000.0, 0.15); KNOWNTARGETING[3][3] = new Settings (3100.0, 0.47);
KNOWNTARGETING[3][4] = new Settings (3000.0, 0.03); KNOWNTARGETING[3][4] = new Settings (3100.0, 0.47);
KNOWNTARGETING[3][5] = new Settings (3535.0, 0.1); KNOWNTARGETING[3][5] = new Settings (3100.0, 0.47);
// ROW 4 // ROW 4
KNOWNTARGETING[4][0] = new Settings (4540.0, 0.1); KNOWNTARGETING[4][0] = new Settings (4540.0, 0.1);
KNOWNTARGETING[4][1] = new Settings (4541.0, 0.1); KNOWNTARGETING[4][1] = new Settings (4541.0, 0.1);
@@ -89,8 +91,8 @@ public class Targeting {
double cos45 = Math.cos(Math.toRadians(-45)); double cos45 = Math.cos(Math.toRadians(-45));
double sin45 = Math.sin(Math.toRadians(-45)); double sin45 = Math.sin(Math.toRadians(-45));
double rotatedY = (robotX - 40.0) * sin45 + (robotY + 7.0) * cos45; double rotatedY = (robotX + cancelOffsetX) * sin45 + (robotY + cancelOffsetY) * cos45;
double rotatedX = (robotX - 40.0) * cos45 - (robotY + 7.0) * sin45; double rotatedX = (robotX + cancelOffsetX) * cos45 - (robotY + cancelOffsetY) * sin45;
// Convert robot coordinates to inches // Convert robot coordinates to inches
robotInchesX = rotatedX * unitConversionFactor; robotInchesX = rotatedX * unitConversionFactor;
@@ -100,6 +102,60 @@ public class Targeting {
int gridX = Math.abs(Math.floorDiv((int) robotInchesX, tileSize) + 1); int gridX = Math.abs(Math.floorDiv((int) robotInchesX, tileSize) + 1);
int gridY = Math.abs(Math.floorDiv((int) robotInchesY, tileSize)); int gridY = Math.abs(Math.floorDiv((int) robotInchesY, tileSize));
int remX = Math.floorMod((int)robotInchesX, tileSize);
int remY = Math.floorMod((int)robotInchesX, tileSize);
// Determine if we need to interpolate based on tile position.
// if near upper or lower quarter or tile interpolate with next tile.
int x1 = 0;
int y1 = 0;
// interpolate = false;
// if ((remX > TILE_UPPER_QUARTILE) && (remY > TILE_UPPER_QUARTILE) &&
// (robotGridX < 5) && (robotGridY <5)) {
// // +X, +Y
// interpolate = true;
// x1 = robotGridX + 1;
// y1 = robotGridY + 1;
// } else if ((remX < TILE_LOWER_QUARTILE) && (remY < TILE_LOWER_QUARTILE) &&
// (robotGridX > 0) && (robotGridY > 0)) {
// // -X, -Y
// interpolate = true;
// x1 = robotGridX - 1;
// y1 = robotGridY - 1;
// } else if ((remX > TILE_UPPER_QUARTILE) && (remY < TILE_LOWER_QUARTILE) &&
// (robotGridX < 5) && (robotGridY > 0)) {
// // +X, -Y
// interpolate = true;
// x1 = robotGridX + 1;
// y1 = robotGridY - 1;
// } else if ((remX < TILE_LOWER_QUARTILE) && (remY > TILE_UPPER_QUARTILE) &&
// (robotGridX > 0) && (robotGridY < 5)) {
// // -X, +Y
// interpolate = true;
// x1 = robotGridX - 1;
// y1 = robotGridY + 1;
// } else if ((remX < TILE_LOWER_QUARTILE) && (robotGridX > 0)) {
// // -X, Y
// interpolate = true;
// x1 = robotGridX - 1;
// y1 = robotGridY;
// } else if ((remY < TILE_LOWER_QUARTILE) && (robotGridY > 0)) {
// // X, -Y
// interpolate = true;
// x1 = robotGridX;
// y1 = robotGridY - 1;
// } else if ((remX > TILE_UPPER_QUARTILE) && (robotGridX < 5)) {
// // +X, Y
// interpolate = true;
// x1 = robotGridX + 1;
// y1 = robotGridY;
// } else if ((remY > TILE_UPPER_QUARTILE) && (robotGridY < 5)) {
// // X, +Y
// interpolate = true;
// x1 = robotGridX;
// y1 = robotGridY + 1;
// }
//clamp //clamp
robotGridX = Math.max(0, Math.min(gridX, KNOWNTARGETING[0].length - 1)); robotGridX = Math.max(0, Math.min(gridX, KNOWNTARGETING[0].length - 1));
robotGridY = Math.max(0, Math.min(gridY, KNOWNTARGETING.length - 1)); robotGridY = Math.max(0, Math.min(gridY, KNOWNTARGETING.length - 1));
@@ -107,17 +163,17 @@ public class Targeting {
// basic search // basic search
if(!interpolate) { if(!interpolate) {
if ((robotGridY < 6) && (robotGridX <6)) { if ((robotGridY < 6) && (robotGridX <6)) {
recommendedSettings.flywheelRPM = KNOWNTARGETING[robotGridY][robotGridX].flywheelRPM; recommendedSettings.flywheelRPM = KNOWNTARGETING[robotGridX][robotGridY].flywheelRPM;
recommendedSettings.hoodAngle = KNOWNTARGETING[robotGridY][robotGridX].hoodAngle; recommendedSettings.hoodAngle = KNOWNTARGETING[robotGridX][robotGridY].hoodAngle;
} }
return recommendedSettings; return recommendedSettings;
} else { } else {
// bilinear interpolation // bilinear interpolation
int x0 = robotGridX; int x0 = robotGridX;
int x1 = Math.min(x0 + 1, KNOWNTARGETING[0].length - 1); //int x1 = Math.min(x0 + 1, KNOWNTARGETING[0].length - 1);
int y0 = gridY; int y0 = robotGridY;
int y1 = Math.min(y0 + 1, KNOWNTARGETING.length - 1); //int y1 = Math.min(y0 + 1, KNOWNTARGETING.length - 1);
double x = (robotInchesX - (x0 * tileSize)) / tileSize; double x = (robotInchesX - (x0 * tileSize)) / tileSize;
double y = (robotInchesY - (y0 * tileSize)) / tileSize; double y = (robotInchesY - (y0 * tileSize)) / tileSize;

View File

@@ -2,12 +2,20 @@ package org.firstinspires.ftc.teamcode.utils;
import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance; import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance;
import static java.lang.Math.abs;
import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.config.Config;
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry; import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
import com.acmerobotics.roadrunner.Pose2d; import com.acmerobotics.roadrunner.Pose2d;
import com.arcrobotics.ftclib.controller.PIDController;
import com.qualcomm.hardware.limelightvision.LLResult;
import com.qualcomm.hardware.limelightvision.LLResultTypes;
import com.qualcomm.hardware.limelightvision.Limelight3A;
import com.qualcomm.robotcore.hardware.DcMotor; import com.qualcomm.robotcore.hardware.DcMotor;
import org.firstinspires.ftc.vision.apriltag.AprilTagDetection; import org.firstinspires.ftc.robotcore.external.navigation.Pose3D;
import java.util.List;
@Config @Config
public class Turret { public class Turret {
@@ -16,33 +24,50 @@ public class Turret {
public static double turrPosScalar = 0.00011264432; public static double turrPosScalar = 0.00011264432;
public static double turret180Range = 0.4; public static double turret180Range = 0.4;
public static double turrDefault = 0.4; public static double turrDefault = 0.4;
public static double cameraBearingEqual = 1; public static double turrMin = 0.15;
public static double errorLearningRate = 0.15; public static double turrMax = 0.85;
public static double turrMin = 0.2;
public static double turrMax = 0.8; public static double visionCorrectionGain = 0.08; // Single tunable gain
public static double mult = 0.0; public static double maxOffsetChangePerCycle = 5.0; // Degrees per cycle
private boolean lockOffset = false; public static double cameraBearingEqual = 0.5; // Deadband
public static double deltaAngleThreshold = 0.02;
public static double angleMultiplier = 0.0; // TODO: tune these values for limelight
Robot robot;
MultipleTelemetry TELE;
AprilTagWebcam webcam;
private int obeliskID = 0;
private double turrPos = 0.0;
private double offset = 0.0;
private double bearing = 0.0;
public static double clampTolerance = 0.03; public static double clampTolerance = 0.03;
Robot robot;
MultipleTelemetry TELE;
Limelight3A webcam;
public Turret(Robot rob, MultipleTelemetry tele, AprilTagWebcam cam) { double tx = 0.0;
double ty = 0.0;
double limelightPosX = 0.0;
double limelightPosY = 0.0;
private boolean lockOffset = false;
private int obeliskID = 0;
private double offset = 0.0;
private double currentTrackOffset = 0.0;
private int currentTrackCount = 0;
private double permanentOffset = 0.0;
LLResult result;
private PIDController bearingPID;
public static double B_PID_P = 0.3, B_PID_I = 0.0, B_PID_D = 0.05;
boolean bearingAligned = false;
public Turret(Robot rob, MultipleTelemetry tele, Limelight3A cam) {
this.TELE = tele; this.TELE = tele;
this.robot = rob; this.robot = rob;
this.webcam = cam; this.webcam = cam;
} webcam.start();
if (redAlliance) {
public double getTurrPos() { webcam.pipelineSwitch(3);
return turrPosScalar * (robot.intake.getCurrentPosition()) + turrDefault; } else {
webcam.pipelineSwitch(2);
}
bearingPID = new PIDController(B_PID_P, B_PID_I, B_PID_D);
} }
public void zeroTurretEncoder() { public void zeroTurretEncoder() {
@@ -50,6 +75,11 @@ public class Turret {
robot.intake.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER); robot.intake.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
} }
public double getTurrPos() {
return turrPosScalar * (robot.turr1Pos.getVoltage() / 3.3) + turrDefault;
}
public void manualSetTurret(double pos) { public void manualSetTurret(double pos) {
robot.turr1.setPosition(pos); robot.turr1.setPosition(pos);
robot.turr2.setPosition(1 - pos); robot.turr2.setPosition(1 - pos);
@@ -59,36 +89,58 @@ public class Turret {
return Math.abs(pos - this.getTurrPos()) < turretTolerance; return Math.abs(pos - this.getTurrPos()) < turretTolerance;
} }
public double getBearing() { private void limelightRead() { // only for tracking purposes, not general reads
if (redAlliance) { if (redAlliance) {
AprilTagDetection d24 = webcam.getTagById(24); webcam.pipelineSwitch(4);
if (d24 != null) {
bearing = d24.ftcPose.bearing;
return bearing;
} else {
return 1000.0;
}
} else { } else {
AprilTagDetection d20 = webcam.getTagById(20); webcam.pipelineSwitch(2);
if (d20 != null) { }
bearing = d20.ftcPose.bearing;
return bearing; result = webcam.getLatestResult();
} else { if (result != null) {
return 1000.0; if (result.isValid()) {
tx = result.getTx();
ty = result.getTy();
// MegaTag1 code for receiving position
Pose3D botpose = result.getBotpose();
if (botpose != null) {
limelightPosX = botpose.getPosition().x;
limelightPosY = botpose.getPosition().y;
}
} }
} }
} }
public double getBearing() {
tx = 1000;
limelightRead();
return tx;
}
public double getTy() {
limelightRead();
return ty;
}
public double getLimelightX() {
limelightRead();
return limelightPosX;
}
public double getLimelightY() {
limelightRead();
return limelightPosY;
}
public int detectObelisk() { public int detectObelisk() {
AprilTagDetection id21 = webcam.getTagById(21); webcam.pipelineSwitch(1);
AprilTagDetection id22 = webcam.getTagById(22); LLResult result = webcam.getLatestResult();
AprilTagDetection id23 = webcam.getTagById(23); if (result != null && result.isValid()) {
if (id21 != null) { List<LLResultTypes.FiducialResult> fiducials = result.getFiducialResults();
obeliskID = 21; for (LLResultTypes.FiducialResult fiducial : fiducials) {
} else if (id22 != null) { obeliskID = fiducial.getFiducialId();
obeliskID = 22; }
} else if (id23 != null) {
obeliskID = 23;
} }
return obeliskID; return obeliskID;
} }
@@ -108,6 +160,48 @@ public class Turret {
/* /*
Param @deltaPos = Pose2d when subtracting robot x, y, heading from goal x, y, heading Param @deltaPos = Pose2d when subtracting robot x, y, heading from goal x, y, heading
*/ */
private double bearingAlign (LLResult llResult) {
double bearingOffset = 0.0;
double targetTx = llResult.getTx(); // How far left or right the target is (degrees)
final double MIN_OFFSET_POWER = 0.15;
final double TARGET_POSITION_TOLERANCE = 1.0;
// LL has 54.5 degree total Horizontal FOV; very edges are not useful.
final double HORIZONTAL_FOV_RANGE = 26.0; // Total usable horizontal degrees from center +/-
final double DRIVE_POWER_REDUCTION = 2.0;
if (abs(targetTx) < TARGET_POSITION_TOLERANCE) {
bearingAligned = true;
} else {
bearingAligned = false;
}
// Only with valid data and if too far off target
if (llResult.isValid() && !bearingAligned)
{
// Adjust Robot Speed based on how far the target is located
// Only drive at half speed max
// switched to PID but original formula left for reference in comments
//drivePower = targetTx/HORIZONTAL_FOV_RANGE / DRIVE_POWER_REDUCTION;
bearingOffset = -(bearingPID.calculate(targetTx, 0.0));
// // Make sure we have enough power to actually drive the wheels
// if (abs(bearingOffset) < MIN_OFFSET_POWER) {
// if (bearingOffset > 0.0) {
// bearingOffset = MIN_OFFSET_POWER;
// } else {
// bearingOffset = -MIN_OFFSET_POWER;
// }
//
// }
}
return bearingOffset;
}
public void trackGoal(Pose2d deltaPos) { public void trackGoal(Pose2d deltaPos) {
/* ---------------- FIELD → TURRET GEOMETRY ---------------- */ /* ---------------- FIELD → TURRET GEOMETRY ---------------- */
@@ -122,7 +216,6 @@ public class Turret {
// Turret angle needed relative to robot // Turret angle needed relative to robot
double turretAngleDeg = desiredTurretAngleDeg - robotHeadingDeg; double turretAngleDeg = desiredTurretAngleDeg - robotHeadingDeg;
turretAngleDeg = -turretAngleDeg; turretAngleDeg = -turretAngleDeg;
// Normalize to [-180, 180] // Normalize to [-180, 180]
@@ -130,43 +223,89 @@ public class Turret {
while (turretAngleDeg < -180) turretAngleDeg += 360; while (turretAngleDeg < -180) turretAngleDeg += 360;
/* ---------------- APRILTAG CORRECTION ---------------- */ /* ---------------- LIMELIGHT VISION CORRECTION ---------------- */
// Update local limelight results
//double tagBearingDeg = getBearing(); // + = target is to the left
//boolean hasValidTarget = (tagBearingDeg != 1000.0);
turretAngleDeg += permanentOffset;
limelightRead();
// Active correction if we see the target
if (result.isValid() && !lockOffset) {
currentTrackOffset += bearingAlign(result);
currentTrackCount++;
// double bearingError = Math.abs(tagBearingDeg);
// //
double tagBearingDeg = getBearing(); // + = target is to the left // if (bearingError > cameraBearingEqual) {
// // Apply sqrt scaling to reduce aggressive corrections at large errors
// double filteredBearing = Math.signum(tagBearingDeg) * Math.sqrt(Math.abs(tagBearingDeg));
//
turretAngleDeg += offset; // // Calculate correction
// double offsetChange = visionCorrectionGain * filteredBearing;
/* ---------------- ANGLE → SERVO ---------------- */ //
// // Limit rate of change to prevent jumps
double turretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360); // offsetChange = Math.max(-maxOffsetChangePerCycle,
// Math.min(maxOffsetChangePerCycle, offsetChange));
double currentEncoderPos = this.getTurrPos(); //
// // Accumulate the correction
if (!turretEqual(turretPos)) { // offset += offsetChange;
double diff = turretPos - currentEncoderPos; //
turretPos = turretPos + diff * mult; // TELE.addData("Bearing Error", tagBearingDeg);
} // TELE.addData("Offset Change", offsetChange);
// TELE.addData("Total Offset", offset);
if (currentEncoderPos < (turrMin + clampTolerance) || currentEncoderPos > (turrMax - clampTolerance)) { // } else {
// Clamp to servo range // // When centered, lock in the learned offset
turretPos = Math.max(turrMin, Math.min(turretPos, turrMax)); // permanentOffset = offset;
// offset = 0.0;
// }
} else { } else {
if (tagBearingDeg != 1000.0 && Math.abs(tagBearingDeg) > cameraBearingEqual && !lockOffset) { // only store perma update after 20+ successful tracks
// Slowly learn turret offset (persistent calibration) // this did not work good in testing; only current works best so far.
offset -= tagBearingDeg * errorLearningRate; // if (currentTrackCount > 20) {
} // offset = currentTrackOffset;
// }
currentTrackOffset = 0.0;
currentTrackCount = 0;
} }
// Apply accumulated offset
turretAngleDeg += offset + currentTrackOffset;
/* ---------------- ANGLE → SERVO POSITION ---------------- */
double targetTurretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360);
// Clamp to physical servo limits
targetTurretPos = Math.max(turrMin, Math.min(targetTurretPos, turrMax));
// Interpolate towards target position
double currentPos = getTurrPos();
double turretPos = targetTurretPos;
if (targetTurretPos == turrMin) {
turretPos = turrMin;
} else if (targetTurretPos == turrMax) {
turretPos = turrMax;
}
// Set servo positions
robot.turr1.setPosition(turretPos); robot.turr1.setPosition(turretPos);
robot.turr2.setPosition(1.0 - turretPos); robot.turr2.setPosition(1.0 - turretPos);
/* ---------------- TELEMETRY ---------------- */ /* ---------------- TELEMETRY ---------------- */
TELE.addData("Turret Angle", turretAngleDeg); TELE.addData("Turret Angle (deg)", "%.2f", turretAngleDeg);
TELE.addData("Bearing", tagBearingDeg); TELE.addData("Target Pos", "%.3f", targetTurretPos);
TELE.addData("Offset", offset); TELE.addData("Current Pos", "%.3f", currentPos);
TELE.addData("Commanded Pos", "%.3f", turretPos);
TELE.addData("LL Valid", result.isValid());
TELE.addData("LL getTx", result.getTx());
TELE.addData("LL Offset", offset);
//TELE.addData("Bearing Error", hasValidTarget ? String.format("%.2f", tagBearingDeg) : "NO TARGET");
TELE.addData("Learned Offset", "%.2f", offset);
} }
} }

View File

@@ -25,5 +25,9 @@ allprojects {
} }
repositories { repositories {
mavenCentral() repositories {
mavenCentral()
google()
maven { url 'https://maven.pedropathing.com' }
}
} }