4 Commits

Author SHA1 Message Date
7625f9a640 stash 2026-01-24 17:53:02 -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
6 changed files with 180 additions and 68 deletions

View File

@@ -166,10 +166,7 @@ public class TeleopV3 extends LinearOpMode {
// robot.limelight.start();
AprilTagWebcam webcam = new AprilTagWebcam();
webcam.init(robot, TELE);
Turret turret = new Turret(robot, TELE, webcam);
Turret turret = new Turret(robot, TELE, robot.limelight);
waitForStart();
waitForStart();
@@ -399,7 +396,7 @@ public class TeleopV3 extends LinearOpMode {
turret.trackGoal(deltaPose);
webcam.update();
//VELOCITY AUTOMATIC
if (targetingVel) {
@@ -809,6 +806,7 @@ public class TeleopV3 extends LinearOpMode {
for (LynxModule hub : allHubs) {
hub.clearBulkCache();
}
//
TELE.addData("Spin1Green", green1 + ": " + ballIn(1));
TELE.addData("Spin2Green", green2 + ": " + ballIn(2));

View File

@@ -1,6 +1,7 @@
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.config.Config;
@@ -55,7 +56,7 @@ public class ShooterTest extends LinearOpMode {
rightShooter.setPower(parameter);
leftShooter.setPower(parameter);
} else if (mode == 1) {
flywheel.setPIDF(P,I,D,F);
flywheel.setPIDF(P, I, D, F);
flywheel.manageFlywheel((int) Velocity);
}
@@ -63,7 +64,6 @@ public class ShooterTest extends LinearOpMode {
robot.hood.setPosition(hoodPos);
}
robot.transfer.setPower(transferPower);
if (shoot) {
robot.transferServo.setPosition(transferServo_in);

View File

@@ -16,6 +16,8 @@ import org.firstinspires.ftc.vision.apriltag.AprilTagDetection;
@Autonomous
@Config
public class TurretTest extends LinearOpMode {
public static boolean zeroTurr = false;
@Override
public void runOpMode() throws InterruptedException {
@@ -24,10 +26,8 @@ public class TurretTest extends LinearOpMode {
telemetry, FtcDashboard.getInstance().getTelemetry()
);
AprilTagWebcam webcam = new AprilTagWebcam();
webcam.init(robot, TELE);
Turret turret = new Turret(robot, TELE, webcam);
Turret turret = new Turret(robot, TELE, robot.limelight);
waitForStart();
@@ -38,8 +38,13 @@ public class TurretTest extends LinearOpMode {
drive.updatePoseEstimate();
turret.trackGoal(drive.localizer.getPose());
webcam.update();
webcam.displayAllTelemetry();
TELE.addData("tpos", turret.getTurrPos());
if(zeroTurr){
turret.zeroTurretEncoder();
}

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

View File

@@ -20,8 +20,8 @@ public class Robot {
//Initialize Public Components
public static boolean usingLimelight = false;
public static boolean usingCamera = true;
public static boolean usingLimelight = true;
public static boolean usingCamera = false;
public DcMotorEx frontLeft;
public DcMotorEx frontRight;
public DcMotorEx backLeft;
@@ -79,10 +79,10 @@ public class Robot {
shooterPIDF = new PIDFCoefficients(shooterPIDF_P, shooterPIDF_I, shooterPIDF_D, shooterPIDF_F);
shooter1.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
shooter1.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF);
shooter1.setVelocity(1400);
shooter1.setVelocity(0);
shooter2.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
shooter2.setPIDFCoefficients(DcMotor.RunMode.RUN_USING_ENCODER, shooterPIDF);
shooter2.setVelocity(1400);
shooter2.setVelocity(0);
hood = hardwareMap.get(Servo.class, "hood");

View File

@@ -5,82 +5,140 @@ import static org.firstinspires.ftc.teamcode.constants.Color.redAlliance;
import com.acmerobotics.dashboard.config.Config;
import com.acmerobotics.dashboard.telemetry.MultipleTelemetry;
import com.acmerobotics.roadrunner.Pose2d;
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
public class Turret {
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 turrDefault = 0.4;
public static double cameraBearingEqual = 1;
public static double errorLearningRate = 0.15;
public static double errorLearningRate = -0.15;
public static double turrMin = 0.2;
public static double turrMax = 0.8;
public static double mult = 0.0;
public static double staticOffsetRate = -0.15;
public static double deltaAngleThreshold = 0.02;
public static double angleMultiplier = 0.0;
public static double fastSeekThreshold = 5.0; // Switch to medium mode below this
public static double mediumSeekThreshold = 2.0; // Switch to fine mode below this
public static double fastCorrectionGain = 0.75; // Correction gain for large errors
public static double mediumCorrectionGain = 0.4; // Correction gain for medium errors
public static double fineCorrectionGain = 0.2; // Correction gain for small errors
public static double maxOffsetChangePerCycle = 0.3; // Max offset change per cycle (degrees)
public static double finalInterpolation = 0.1; // Final position interpolation factor
// TODO: tune these values for limelight
public static double clampTolerance = 0.03;
Robot robot;
MultipleTelemetry TELE;
AprilTagWebcam webcam;
Limelight3A webcam;
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 turrPos = 0.0;
private double offset = 0.0;
private double bearing = 0.0;
public Turret(Robot rob, MultipleTelemetry tele, AprilTagWebcam cam) {
public Turret(Robot rob, MultipleTelemetry tele, Limelight3A cam) {
this.TELE = tele;
this.robot = rob;
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() {
return turrPosScalar * (robot.turr1Pos.getVoltage() / 3.3);
return turrPosScalar * (robot.turr1Pos.getVoltage() / 3.3) + turrDefault;
}
public void manualSetTurret(double pos){
public void manualSetTurret(double pos) {
robot.turr1.setPosition(pos);
robot.turr2.setPosition(1-pos);
robot.turr2.setPosition(1 - pos);
}
public boolean turretEqual(double pos) {
return Math.abs(pos - this.getTurrPos()) < turretTolerance;
}
public double getBearing() {
private void limelightRead() { // only for tracking purposes, not general reads
if (redAlliance) {
AprilTagDetection d24 = webcam.getTagById(24);
if (d24 != null) {
bearing = d24.ftcPose.bearing;
return bearing;
} else {
return 1000.0;
}
webcam.pipelineSwitch(3);
} else {
AprilTagDetection d20 = webcam.getTagById(20);
if (d20 != null) {
bearing = d20.ftcPose.bearing;
return bearing;
} else {
return 1000.0;
webcam.pipelineSwitch(2);
}
LLResult result = webcam.getLatestResult();
if (result != null) {
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() {
AprilTagDetection id21 = webcam.getTagById(21);
AprilTagDetection id22 = webcam.getTagById(22);
AprilTagDetection id23 = webcam.getTagById(23);
if (id21 != null) {
obeliskID = 21;
} else if (id22 != null) {
obeliskID = 22;
} else if (id23 != null) {
obeliskID = 23;
webcam.pipelineSwitch(1);
LLResult result = webcam.getLatestResult();
if (result != null && result.isValid()) {
List<LLResultTypes.FiducialResult> fiducials = result.getFiducialResults();
for (LLResultTypes.FiducialResult fiducial : fiducials) {
obeliskID = fiducial.getFiducialId();
}
}
return obeliskID;
}
@@ -89,11 +147,18 @@ public class Turret {
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
*/
public void trackGoal(Pose2d deltaPos) {
/* ---------------- FIELD → TURRET GEOMETRY ---------------- */
@@ -108,7 +173,6 @@ public class Turret {
// Turret angle needed relative to robot
double turretAngleDeg = desiredTurretAngleDeg - robotHeadingDeg;
turretAngleDeg = -turretAngleDeg;
// Normalize to [-180, 180]
@@ -116,32 +180,68 @@ public class Turret {
while (turretAngleDeg < -180) turretAngleDeg += 360;
/* ---------------- APRILTAG CORRECTION ---------------- */
//
/* ---------------- LIMELIGHT VISION CORRECTION ---------------- */
double tagBearingDeg = getBearing(); // + = target is to the left
boolean hasValidTarget = (tagBearingDeg != 1000.0);
if (tagBearingDeg != 1000.0 && Math.abs(tagBearingDeg) > cameraBearingEqual) {
// Slowly learn turret offset (persistent calibration)
offset -= tagBearingDeg * errorLearningRate;
}
// Apply persistent offset from previous corrections
turretAngleDeg += offset;
/* ---------------- ANGLE → SERVO ---------------- */
// Active correction if we see the target
if (hasValidTarget && !lockOffset) {
double bearingError = Math.abs(tagBearingDeg);
double turretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360);
if (bearingError > cameraBearingEqual) {
// Dual-mode correction: fast when far, gentle when close
double correctionGain;
if (bearingError > fastSeekThreshold) {
correctionGain = fastCorrectionGain;
} else if (bearingError > mediumSeekThreshold) {
correctionGain = mediumCorrectionGain;
} else {
correctionGain = fineCorrectionGain;
}
// Clamp to servo range
turretPos = Math.max(turrMin, Math.min(turretPos, turrMax));
// Immediate correction to turret angle
turretAngleDeg -= tagBearingDeg * correctionGain;
// Learn offset slowly for persistent calibration
double offsetChange = -tagBearingDeg * errorLearningRate;
// Rate limit to prevent oscillation
offsetChange = Math.max(-maxOffsetChangePerCycle, Math.min(offsetChange, maxOffsetChangePerCycle));
offset += offsetChange;
TELE.addData("Correction Mode", bearingError > fastSeekThreshold ? "FAST" :
bearingError > mediumSeekThreshold ? "MEDIUM" : "FINE");
}
}
/* ---------------- 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 = currentPos + (targetTurretPos - currentPos) * finalInterpolation;
// Set servo positions
robot.turr1.setPosition(turretPos);
robot.turr2.setPosition(1.0 - turretPos);
/* ---------------- TELEMETRY ---------------- */
TELE.addData("Turret Angle", turretAngleDeg);
TELE.addData("Bearing", tagBearingDeg);
TELE.addData("Offset", offset);
TELE.addData("Turret Angle (deg)", "%.2f", turretAngleDeg);
TELE.addData("Target Pos", "%.3f", targetTurretPos);
TELE.addData("Current Pos", "%.3f", currentPos);
TELE.addData("Commanded Pos", "%.3f", turretPos);
TELE.addData("Bearing Error", hasValidTarget ? String.format("%.2f", tagBearingDeg) : "NO TARGET");
TELE.addData("Learned Offset", "%.2f", offset);
}
}