-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathTurretcontrol.java
More file actions
149 lines (117 loc) · 6.17 KB
/
Turretcontrol.java
File metadata and controls
149 lines (117 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package org.firstinspires.ftc.teamcode;
import static java.lang.Math.atan2;
import com.qualcomm.hardware.gobilda.GoBildaPinpointDriver;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
import com.qualcomm.robotcore.hardware.AnalogInput;
import com.qualcomm.robotcore.hardware.CRServoImplEx;
import org.firstinspires.ftc.robotcore.external.navigation.AngleUnit;
import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit;
import org.firstinspires.ftc.robotcore.external.navigation.Pose2D;
import com.qualcomm.hardware.limelightvision.LLResult;
import com.qualcomm.hardware.limelightvision.Limelight3A;
@TeleOp(name="Turret_Final_ShortestPath", group="Control")
public class Turretcontrol extends LinearOpMode {
// Hardware
private CRServoImplEx turretServo;
private AnalogInput turretAnalog;
private GoBildaPinpointDriver odo;
// Tracking
private double lastEncoderRad = 0;
private int encoderRotations = 0;
private double totalTurretRadians = 0;
// Constants
private final double MAX_VOLTAGE = 5.0;
private final double TWO_PI = 2.0 * Math.PI;
// ADJUST THIS: 5.0 if 5 servo turns = 1 turret turn.
// Use 5.812 if that is your exact physical gear ratio.
private final double GEAR_RATIO = 5.812;
@Override
public void runOpMode() {
// 1. Hardware Map
odo = hardwareMap.get(GoBildaPinpointDriver.class, "pinpoint");
turretServo = hardwareMap.get(CRServoImplEx.class, "turretServo");
turretAnalog = hardwareMap.get(AnalogInput.class, "turretFeedback");
Limelight3A limelight = hardwareMap.get(Limelight3A.class, "limelight");
// 2. Odo Config
odo.setOffsets(-3.75, -3.17, DistanceUnit.INCH);
odo.setEncoderResolution(GoBildaPinpointDriver.GoBildaOdometryPods.goBILDA_4_BAR_POD);
odo.setEncoderDirections(GoBildaPinpointDriver.EncoderDirection.REVERSED, GoBildaPinpointDriver.EncoderDirection.FORWARD);
odo.resetPosAndIMU();
// Initialize starting position (Negative sign flips direction if needed)
lastEncoderRad = (turretAnalog.getVoltage() / MAX_VOLTAGE) * TWO_PI;
limelight.start();
waitForStart();
while (opModeIsActive()) {
LLResult result = limelight.getLatestResult();
if (result != null && result.isValid()) {
// tx: Horizontal offset (degrees)
// ty: Vertical offset (degrees)
// ta: Target area (0%-100% of image)
double tx = result.getTx();
double ty = result.getTy();
double ta = result.getTa();
double targettx = 0;
double kp = 0.03;
double error = tx - targettx;
double motorpower = kp * error;
// --- Deadband to prevent jitter ---
if (Math.abs(error) < 0.5) {
motorpower = 0;
}
// --- Safety clipping ---
motorpower = Math.max(-1, Math.min(1, motorpower));
turretServo.setPower(motorpower);
telemetry.addData("Target X", tx);
telemetry.addData("Target Y", ty);
telemetry.addData("Tx", tx);
telemetry.addData("MotorPower", motorpower);
} else {
odo.update();
// --- STEP 1: TRACK SERVO ROTATIONS ---
// Read raw analog and convert to 0 - 2PI
double currentEncoderRad = -((turretAnalog.getVoltage() / MAX_VOLTAGE) * TWO_PI);
// Detect when the small gear (servo) passes the 5V -> 0V gap
double delta = currentEncoderRad - lastEncoderRad;
if (delta < -Math.PI) encoderRotations++; // Wrapped forward
else if (delta > Math.PI) encoderRotations--; // Wrapped backward
// Total radians the SERVO has spun
double totalEncoderRad = (encoderRotations * TWO_PI) + currentEncoderRad;
lastEncoderRad = currentEncoderRad;
// --- STEP 2: SCALE TO TURRET RADIANS ---
// Divide by the gear ratio to get the big circle's position
totalTurretRadians = totalEncoderRad / GEAR_RATIO;
// --- STEP 3: CALCULATE TARGET ---
Pose2D robotPose = odo.getPosition();
double robotH = robotPose.getHeading(AngleUnit.RADIANS);
// Absolute angle to Goal (Replace coordinates with your goal location)
double targetFieldAngle = atan2(127.64 - robotPose.getX(DistanceUnit.INCH), 13.63 - robotPose.getY(DistanceUnit.INCH));
// Where the turret should be relative to the robot's front
double targetRelativeAngle = targetFieldAngle - robotH;
// --- STEP 4: SHORTEST PATH LOGIC ---
// Calculate the difference between target and current position
double error = targetRelativeAngle - totalTurretRadians;
// Normalize the error to the range [-PI, PI]
// This ensures it never turns more than 180 degrees to find the target
while (error > Math.PI) error -= TWO_PI;
while (error < -Math.PI) error += TWO_PI;
// --- STEP 5: OUTPUT TO SERVO ---
double kP = 0.35; // Power multiplier
double deadband = 0.01; // Stops jitter
double pos = 0;
if (Math.abs(error) > deadband) {
// Proportional Power + Friction Feedforward (0.04)
pos = -(error * kP) + (Math.signum(error) * 0.04);
}
// Safety Cap
turretServo.setPower(Math.max(-1, Math.min(1, pos)));
// --- TELEMETRY ---
telemetry.addData("Turret Deg", Math.toDegrees(totalTurretRadians));
telemetry.addData("Target Deg", Math.toDegrees(targetRelativeAngle));
telemetry.addData("Error Deg", Math.toDegrees(error));
telemetry.addData("Servo Turns", encoderRotations);
telemetry.update();
}
}
}
}