11 Commits

Author SHA1 Message Date
8dc03adfd3 Merge with LimelightTesting. 2026-01-25 11:39:26 -06:00
7ffc51f60a Add shoot all ball order 2026-01-25 11:33:56 -06:00
7625f9a640 stash 2026-01-24 17:53:02 -06:00
fefeeb1f2e i need you @KeshavAnandCode 2026-01-24 17:18:57 -06:00
b5a31afe52 i need you @KeshavAnandCode 2026-01-24 15:42:32 -06:00
8d29a80696 need to add zero code to properly test 2026-01-24 14:45:35 -06:00
5922f4e935 need to add zero code to properly test 2026-01-23 22:50:33 -06:00
78d38481a7 stash 2026-01-23 21:44:29 -06:00
8a4bfecbf8 turret 2026-01-23 21:24:38 -06:00
3591e20001 Merge branch 'Targeting' 2026-01-23 20:24:16 -06:00
16ffdd003f stash 2026-01-23 19:38:47 -06:00
12 changed files with 278 additions and 124 deletions

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) {
// //
@@ -835,7 +829,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,6 +1,7 @@
package org.firstinspires.ftc.teamcode.tests; package org.firstinspires.ftc.teamcode.tests;
import static org.firstinspires.ftc.teamcode.constants.ServoPositions.*; import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_in;
import static org.firstinspires.ftc.teamcode.constants.ServoPositions.transferServo_out;
import com.acmerobotics.dashboard.FtcDashboard; import com.acmerobotics.dashboard.FtcDashboard;
import com.acmerobotics.dashboard.config.Config; import com.acmerobotics.dashboard.config.Config;
@@ -55,7 +56,7 @@ public class ShooterTest extends LinearOpMode {
rightShooter.setPower(parameter); rightShooter.setPower(parameter);
leftShooter.setPower(parameter); leftShooter.setPower(parameter);
} else if (mode == 1) { } else if (mode == 1) {
flywheel.setPIDF(P,I,D,F); flywheel.setPIDF(P, I, D, F);
flywheel.manageFlywheel((int) Velocity); flywheel.manageFlywheel((int) Velocity);
} }
@@ -63,7 +64,6 @@ public class ShooterTest extends LinearOpMode {
robot.hood.setPosition(hoodPos); robot.hood.setPosition(hoodPos);
} }
robot.transfer.setPower(transferPower); robot.transfer.setPower(transferPower);
if (shoot) { if (shoot) {
robot.transferServo.setPosition(transferServo_in); robot.transferServo.setPosition(transferServo_in);

View File

@@ -8,14 +8,13 @@ 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;
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
@@ -24,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()){
@@ -38,10 +33,15 @@ public class TurretTest extends LinearOpMode {
drive.updatePoseEstimate(); drive.updatePoseEstimate();
turret.trackGoal(drive.localizer.getPose()); turret.trackGoal(drive.localizer.getPose());
webcam.update(); TELE.addData("tpos", turret.getTurrPos());
webcam.displayAllTelemetry(); 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){
turret.zeroTurretEncoder();
}
TELE.update(); TELE.update();
} }

View File

@@ -8,6 +8,8 @@ import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
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 org.firstinspires.ftc.vision.apriltag.AprilTagDetection;
@TeleOp @TeleOp
@Config @Config
public class PositionalServoProgrammer extends LinearOpMode { public class PositionalServoProgrammer extends LinearOpMode {
@@ -25,11 +27,17 @@ public class PositionalServoProgrammer extends LinearOpMode {
public static double hoodPos = 0.501; public static double hoodPos = 0.501;
public static int mode = 0; //0 for positional, 1 for power public static int mode = 0; //0 for positional, 1 for power
Turret turret;
@Override @Override
public void runOpMode() throws InterruptedException { public void runOpMode() throws InterruptedException {
robot = new Robot(hardwareMap); robot = new Robot(hardwareMap);
TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry()); TELE = new MultipleTelemetry(telemetry, FtcDashboard.getInstance().getTelemetry());
servo = new Servos(hardwareMap); servo = new Servos(hardwareMap);
turret = new Turret(robot, TELE, robot.limelight );
waitForStart(); waitForStart();
if (isStopRequested()) return; if (isStopRequested()) return;
while (opModeIsActive()){ while (opModeIsActive()){
@@ -66,12 +74,13 @@ public class PositionalServoProgrammer extends LinearOpMode {
//TODO: @KeshavAnandCode do the above please //TODO: @KeshavAnandCode do the above please
TELE.addData("spindexer pos", servo.getSpinPos()); TELE.addData("spindexer pos", servo.getSpinPos());
TELE.addData("turret pos", servo.getTurrPos()); TELE.addData("turret pos", robot.turr1.getPosition());
TELE.addData("spindexer voltage 1", robot.spin1Pos.getVoltage()); TELE.addData("spindexer voltage 1", robot.spin1Pos.getVoltage());
TELE.addData("spindexer voltage 2", robot.spin2Pos.getVoltage()); TELE.addData("spindexer voltage 2", robot.spin2Pos.getVoltage());
TELE.addData("hood pos", robot.hood.getPosition()); TELE.addData("hood pos", robot.hood.getPosition());
TELE.addData("transferServo voltage", robot.transferServoPos.getVoltage()); TELE.addData("transferServo voltage", robot.transferServoPos.getVoltage());
TELE.addData("spindexer pow", robot.spin1.getPower()); TELE.addData("spindexer pow", robot.spin1.getPower());
TELE.addData("tpos ", turret.getTurrPos() );
TELE.update(); TELE.update();
} }
} }

View File

@@ -20,7 +20,7 @@ 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 = true;
public DcMotorEx frontLeft; public DcMotorEx frontLeft;
public DcMotorEx frontRight; public DcMotorEx frontRight;
@@ -79,10 +79,10 @@ public class Robot {
shooterPIDF = new PIDFCoefficients(shooterPIDF_P, shooterPIDF_I, shooterPIDF_D, shooterPIDF_F); shooterPIDF = new PIDFCoefficients(shooterPIDF_P, shooterPIDF_I, shooterPIDF_D, shooterPIDF_F);
shooter1.setMode(DcMotor.RunMode.RUN_USING_ENCODER); shooter1.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
shooter1.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF); shooter1.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF);
shooter1.setVelocity(1400); shooter1.setVelocity(0);
shooter2.setMode(DcMotor.RunMode.RUN_USING_ENCODER); shooter2.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
shooter2.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF); shooter2.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF);
shooter2.setVelocity(1400); shooter2.setVelocity(0);
hood = hardwareMap.get(Servo.class, "hood"); hood = hardwareMap.get(Servo.class, "hood");

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;
@@ -327,6 +332,7 @@ public class Spindexer {
} }
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]);
@@ -355,6 +361,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 +411,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"
@@ -420,6 +438,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

@@ -1,3 +1,4 @@
package org.firstinspires.ftc.teamcode.utils; package org.firstinspires.ftc.teamcode.utils;
import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance; import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance;
@@ -5,40 +6,75 @@ import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance;
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.PIDFController;
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 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 {
public static double turretTolerance = 0.02; public static double turretTolerance = 0.02;
public static double turrPosScalar = 1.009; 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;
// TODO: tune these values for limelight
// At the top with other static variables:
public static double kP = 0.015; // Proportional gain - tune this first
public static double kI = 0.0005; // Integral gain - add slowly if needed
public static double kD = 0.002; // Derivative gain - helps prevent overshoot
public static double kF = 0.002; // Derivative gain - helps prevent overshoot
public static double maxOffset = 10; // degrees - safety limit
// Add these as instance variables:
private double lastTagBearing = 0.0;
private double offsetIntegral = 0.0;
public static double cameraBearingEqual = 1; public static double cameraBearingEqual = 1;
public static double errorLearningRate = 0.15;
public static double turrMin = 0.2; public static double turrMin = 0.2;
public static double turrMax = 0.8; public static double turrMax = 0.8;
public static double deltaAngleThreshold = 0.02; public static double mult = 0.0;
public static double angleMultiplier = 0.0; private boolean lockOffset = false;
Robot robot; Robot robot;
MultipleTelemetry TELE; MultipleTelemetry TELE;
AprilTagWebcam webcam; Limelight3A webcam;
private int obeliskID = 0; private int obeliskID = 0;
private double turrPos = 0.0;
private double offset = 0.0; private double offset = 0.0;
private double bearing = 0.0;
private PIDFController controller = new PIDFController(kP, kI, kD, kF);
double tx = 0.0;
double ty = 0.0;
double limelightPosX = 0.0;
double limelightPosY = 0.0;
public static double clampTolerance = 0.03;
public Turret(Robot rob, MultipleTelemetry tele, Limelight3A cam) {
public Turret(Robot rob, MultipleTelemetry tele, AprilTagWebcam cam) {
this.TELE = tele; this.TELE = tele;
this.robot = rob; this.robot = rob;
this.webcam = cam; this.webcam = cam;
webcam.start();
if (redAlliance){
webcam.pipelineSwitch(3);
} else {
webcam.pipelineSwitch(2);
}
}
public void zeroTurretEncoder() {
robot.intake.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
robot.intake.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
} }
public double getTurrPos() { public double getTurrPos() {
return turrPosScalar * (robot.turr1Pos.getVoltage() / 3.3); return turrPosScalar * (robot.turr1Pos.getVoltage() / 3.3) + turrDefault;
} }
@@ -51,36 +87,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(3);
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; LLResult 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;
} }
@@ -89,13 +147,20 @@ public class Turret {
return obeliskID; return obeliskID;
} }
public void zeroOffset() {
offset = 0.0;
}
public void lockOffset(boolean lock) {
lockOffset = lock;
}
/* /*
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
*/ */
public void trackGoal(Pose2d deltaPos) { public void trackGoal(Pose2d deltaPos) {
controller.setPIDF(kP, kI, kD, kF);
/* ---------------- FIELD → TURRET GEOMETRY ---------------- */ /* ---------------- FIELD → TURRET GEOMETRY ---------------- */
// Angle from robot to goal in robot frame // Angle from robot to goal in robot frame
@@ -115,16 +180,10 @@ public class Turret {
while (turretAngleDeg > 180) turretAngleDeg -= 360; while (turretAngleDeg > 180) turretAngleDeg -= 360;
while (turretAngleDeg < -180) turretAngleDeg += 360; while (turretAngleDeg < -180) turretAngleDeg += 360;
/* ---------------- APRILTAG CORRECTION ---------------- */ /* ---------------- APRILTAG CORRECTION ---------------- */
// //
double tagBearingDeg = getBearing(); // + = target is to the left double tagBearingDeg = getBearing(); // + = target is to the left
if (tagBearingDeg != 1000.0 && Math.abs(tagBearingDeg) > cameraBearingEqual) {
// Slowly learn turret offset (persistent calibration)
offset -= tagBearingDeg * errorLearningRate;
}
turretAngleDeg += offset; turretAngleDeg += offset;
/* ---------------- ANGLE → SERVO ---------------- */ /* ---------------- ANGLE → SERVO ---------------- */
@@ -132,7 +191,28 @@ public class Turret {
double turretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360); double turretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360);
// Clamp to servo range // Clamp to servo range
turretPos = Math.max(turrMin, Math.min(turretPos, turrMax)); double currentEncoderPos = this.getTurrPos();
if (!turretEqual(turretPos)) {
double diff = turretPos - currentEncoderPos;
turretPos = turretPos + diff * mult;
}
if (currentEncoderPos < (turrMin + clampTolerance) || currentEncoderPos > (turrMax - clampTolerance)) {
// Clamp to servo range
turretPos = Math.max(turrMin, Math.min(turretPos, turrMax));
} else { // TODO: add so it only adds error when standstill
if (tagBearingDeg != 1000.0 && Math.abs(tagBearingDeg) > cameraBearingEqual && !lockOffset) {
// PID-based offset correction for faster, smoother tracking
// Proportional: respond to current error
offset = -controller.calculate(tagBearingDeg);
}
}
robot.turr1.setPosition(turretPos); robot.turr1.setPosition(turretPos);
robot.turr2.setPosition(1.0 - turretPos); robot.turr2.setPosition(1.0 - turretPos);

View File

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