Compare commits
2 Commits
SpindexerU
...
e665ddf032
| Author | SHA1 | Date | |
|---|---|---|---|
| e665ddf032 | |||
| b08fe5ada5 |
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
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;
|
||||||
@@ -6,7 +5,6 @@ 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.LLResult;
|
||||||
import com.qualcomm.hardware.limelightvision.LLResultTypes;
|
import com.qualcomm.hardware.limelightvision.LLResultTypes;
|
||||||
import com.qualcomm.hardware.limelightvision.Limelight3A;
|
import com.qualcomm.hardware.limelightvision.Limelight3A;
|
||||||
@@ -18,43 +16,35 @@ 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 = 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 turrMin = 0.15;
|
||||||
|
public static double turrMax = 0.85;
|
||||||
|
|
||||||
|
public static double visionCorrectionGain = 0.08; // Single tunable gain
|
||||||
|
public static double maxOffsetChangePerCycle = 5.0; // Degrees per cycle
|
||||||
|
public static double cameraBearingEqual = 0.5; // Deadband
|
||||||
|
|
||||||
// TODO: tune these values for limelight
|
// 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 clampTolerance = 0.03;
|
||||||
|
|
||||||
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 turrMin = 0.2;
|
|
||||||
public static double turrMax = 0.8;
|
|
||||||
public static double mult = 0.0;
|
|
||||||
private boolean lockOffset = false;
|
|
||||||
Robot robot;
|
Robot robot;
|
||||||
MultipleTelemetry TELE;
|
MultipleTelemetry TELE;
|
||||||
Limelight3A webcam;
|
Limelight3A webcam;
|
||||||
private int obeliskID = 0;
|
|
||||||
private double offset = 0.0;
|
|
||||||
|
|
||||||
private PIDFController controller = new PIDFController(kP, kI, kD, kF);
|
|
||||||
double tx = 0.0;
|
double tx = 0.0;
|
||||||
double ty = 0.0;
|
double ty = 0.0;
|
||||||
double limelightPosX = 0.0;
|
double limelightPosX = 0.0;
|
||||||
double limelightPosY = 0.0;
|
double limelightPosY = 0.0;
|
||||||
public static double clampTolerance = 0.03;
|
private boolean lockOffset = false;
|
||||||
|
private int obeliskID = 0;
|
||||||
|
|
||||||
|
private double offset = 0.0;
|
||||||
|
|
||||||
|
private double permanentOffset = 0.0;
|
||||||
|
|
||||||
public Turret(Robot rob, MultipleTelemetry tele, Limelight3A cam) {
|
public Turret(Robot rob, MultipleTelemetry tele, Limelight3A cam) {
|
||||||
this.TELE = tele;
|
this.TELE = tele;
|
||||||
@@ -158,9 +148,9 @@ 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
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
|
||||||
@@ -173,55 +163,81 @@ 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]
|
||||||
while (turretAngleDeg > 180) turretAngleDeg -= 360;
|
while (turretAngleDeg > 180) turretAngleDeg -= 360;
|
||||||
while (turretAngleDeg < -180) turretAngleDeg += 360;
|
while (turretAngleDeg < -180) turretAngleDeg += 360;
|
||||||
|
|
||||||
/* ---------------- APRILTAG CORRECTION ---------------- */
|
|
||||||
//
|
|
||||||
double tagBearingDeg = getBearing(); // + = target is to the left
|
|
||||||
|
|
||||||
|
/* ---------------- LIMELIGHT VISION CORRECTION ---------------- */
|
||||||
|
double tagBearingDeg = getBearing(); // + = target is to the left
|
||||||
|
boolean hasValidTarget = (tagBearingDeg != 1000.0);
|
||||||
|
|
||||||
|
turretAngleDeg += permanentOffset;
|
||||||
|
|
||||||
|
// Active correction if we see the target
|
||||||
|
if (hasValidTarget && !lockOffset) {
|
||||||
|
double bearingError = Math.abs(tagBearingDeg);
|
||||||
|
|
||||||
|
if (bearingError > cameraBearingEqual) {
|
||||||
|
// Apply sqrt scaling to reduce aggressive corrections at large errors
|
||||||
|
double filteredBearing = Math.signum(tagBearingDeg) * Math.sqrt(Math.abs(tagBearingDeg));
|
||||||
|
|
||||||
|
// Calculate correction
|
||||||
|
double offsetChange = visionCorrectionGain * filteredBearing;
|
||||||
|
|
||||||
|
// Limit rate of change to prevent jumps
|
||||||
|
offsetChange = Math.max(-maxOffsetChangePerCycle,
|
||||||
|
Math.min(maxOffsetChangePerCycle, offsetChange));
|
||||||
|
|
||||||
|
// Accumulate the correction
|
||||||
|
offset += offsetChange;
|
||||||
|
|
||||||
|
TELE.addData("Bearing Error", tagBearingDeg);
|
||||||
|
TELE.addData("Offset Change", offsetChange);
|
||||||
|
TELE.addData("Total Offset", offset);
|
||||||
|
} else {
|
||||||
|
// When centered, lock in the learned offset
|
||||||
|
permanentOffset = offset;
|
||||||
|
offset = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply accumulated offset
|
||||||
turretAngleDeg += offset;
|
turretAngleDeg += offset;
|
||||||
|
|
||||||
/* ---------------- ANGLE → SERVO ---------------- */
|
|
||||||
|
|
||||||
double turretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360);
|
/* ---------------- ANGLE → SERVO POSITION ---------------- */
|
||||||
|
|
||||||
// Clamp to servo range
|
double targetTurretPos = turrDefault + (turretAngleDeg * (turret180Range * 2.0) / 360);
|
||||||
double currentEncoderPos = this.getTurrPos();
|
|
||||||
|
|
||||||
if (!turretEqual(turretPos)) {
|
// Clamp to physical servo limits
|
||||||
double diff = turretPos - currentEncoderPos;
|
targetTurretPos = Math.max(turrMin, Math.min(targetTurretPos, turrMax));
|
||||||
turretPos = turretPos + diff * mult;
|
|
||||||
}
|
// Interpolate towards target position
|
||||||
|
double currentPos = getTurrPos();
|
||||||
if (currentEncoderPos < (turrMin + clampTolerance) || currentEncoderPos > (turrMax - clampTolerance)) {
|
double turretPos = targetTurretPos;
|
||||||
// Clamp to servo range
|
|
||||||
turretPos = Math.max(turrMin, Math.min(turretPos, turrMax));
|
if (targetTurretPos == turrMin) {
|
||||||
} else { // TODO: add so it only adds error when standstill
|
turretPos = turrMin;
|
||||||
if (tagBearingDeg != 1000.0 && Math.abs(tagBearingDeg) > cameraBearingEqual && !lockOffset) {
|
} else if (targetTurretPos == turrMax) {
|
||||||
// PID-based offset correction for faster, smoother tracking
|
turretPos = turrMax;
|
||||||
|
|
||||||
// Proportional: respond to current error
|
|
||||||
|
|
||||||
offset = -controller.calculate(tagBearingDeg);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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("Bearing Error", hasValidTarget ? String.format("%.2f", tagBearingDeg) : "NO TARGET");
|
||||||
|
TELE.addData("Learned Offset", "%.2f", offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user