Skip to content

Commit c719b19

Browse files
committed
Add PID encoder drive to pre-made modules
1 parent c5cccd6 commit c719b19

File tree

1 file changed

+290
-0
lines changed

1 file changed

+290
-0
lines changed
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
package org.firstinspires.ftc.teamcode.FTC_Library.Autonomous.Modules.Premade;
2+
3+
import com.qualcomm.robotcore.hardware.DcMotor;
4+
import org.firstinspires.ftc.teamcode.FTC_Library.Autonomous.Modules.Module;
5+
import org.firstinspires.ftc.teamcode.FTC_Library.Robot.SubSystems.SidedDriveSystemTemplate;
6+
import org.firstinspires.ftc.teamcode.FTC_Library.Utilities.PIDController;
7+
8+
/**
9+
* PIDEncoderDrive is designed to make autonomous driving really easy for most teams but also extendable as teams get more advanced.
10+
* It has been known for teams to spend multiple hours tuning the PID values of their robot to get it exactly right but also at the same time,
11+
* spending 15 minutes can be more than enough to get acceptable results.
12+
* <p>
13+
* Ideally your main robot class will have a PIDEncoderDrive.PIDConfig that you can use for all your autonomous movement.
14+
* This way instead of having to set up each PIDEncoderDrive, you just have to give it the config.
15+
* Multiple configs for one program are also possible...
16+
*
17+
*/
18+
public class PIDEncoderDrive extends Module {
19+
private SidedDriveSystemTemplate drive;
20+
private double startTime;//TODO add watchdog
21+
22+
private double leftRotations;
23+
private double rightRotations;
24+
25+
private double leftSpeed;
26+
private double rightSpeed;
27+
private PIDController leftController;
28+
private PIDController rightController;
29+
private double wheelCircumference;
30+
31+
/**
32+
* -Gets the drive system
33+
* -sets each side's target encoder ticks for motors
34+
* -resets encoders
35+
* -zeros out speeds as needed
36+
*/
37+
@Override
38+
public void start() {
39+
if (robot.getDriveSystem() instanceof SidedDriveSystemTemplate) {
40+
drive = (SidedDriveSystemTemplate) robot.getDriveSystem();
41+
} else {
42+
//if not a sided drive system, then exit set up
43+
return;
44+
}
45+
46+
startTime = robot.getTimeMilliseconds();
47+
48+
//sets targets
49+
long leftTarget = -(int) (leftRotations * drive.getMotorType().getTicksPerRev());
50+
long rightTarget = -(int) (rightRotations * drive.getMotorType().getTicksPerRev());
51+
52+
drive.resetAllEncoders();
53+
drive.runUsingAllEncoders();
54+
55+
56+
leftController.setTarget(leftTarget, 0);//set PID controllers
57+
rightController.setTarget(rightTarget, 0);
58+
59+
//if either motor doesn't need to move then don't move it
60+
if (leftTarget == 0) {
61+
leftSpeed = 0;
62+
}
63+
if (rightTarget == 0) {
64+
rightSpeed = 0;
65+
}
66+
67+
drive.driveTank(leftSpeed, rightSpeed);//currently the speeds are 0 but I don't think it does any harm leaving it this way
68+
69+
if (hasTelemetry()) {
70+
telemetry.log().add("Right Target:" + rightTarget + " Left Target:" + leftTarget);
71+
telemetry.log().add("Right Rotations:" + rightRotations + " Left Rotations:" + leftRotations);
72+
}
73+
}
74+
75+
/**
76+
* This function runs:
77+
* -some fail safe code
78+
* -averages the motor encoder positions on each side of the robot
79+
* -feeds that into the PID controller
80+
* -gets the recommended output and applies that to the motors
81+
* -stops when both sides are on target
82+
* @return if this module is done
83+
*/
84+
@Override
85+
public boolean tick() {
86+
//some fail safes
87+
if (drive == null) {
88+
//if not a sided drive system, then exit
89+
telemetry.log().add("Need SidedDriveSystem");
90+
return true;
91+
}
92+
if (wheelCircumference == 0) {
93+
telemetry.log().add("Please add wheel circumference");
94+
return true;
95+
}
96+
97+
int currentLeft = 0;
98+
int motorsLeft = 0;
99+
int currentRight = 0;
100+
int motorsRight = 0;
101+
102+
for (DcMotor motor : drive.getLeftSideMotors()) {
103+
currentLeft += motor.getCurrentPosition();
104+
motorsLeft++;
105+
}
106+
for (DcMotor motor : drive.getRightSideMotors()) {
107+
currentRight += motor.getCurrentPosition();
108+
motorsRight++;
109+
}
110+
111+
if (motorsRight == 0 || motorsLeft == 0) {
112+
//need at least one motor per side
113+
telemetry.log().add("One or both sides of the robot don't have motors!");
114+
return true;
115+
}
116+
117+
//update speed so as to not overshoot
118+
leftSpeed = leftController.getOutput(currentLeft / motorsLeft);
119+
rightSpeed = rightController.getOutput(currentRight / motorsRight);
120+
drive.driveTank(leftSpeed, rightSpeed);
121+
122+
telemetry.addLine("Left Speed:" + leftSpeed + ", Right Speed:" + rightSpeed);
123+
124+
125+
//stop if on target, and end if both are on target
126+
if (leftController.isOnTarget()) {
127+
drive.stopLeftMotors();
128+
}
129+
if (rightController.isOnTarget()) {
130+
drive.stopRightMotors();
131+
}
132+
//when both are on target, this system is done
133+
return leftController.isOnTarget() && rightController.isOnTarget();
134+
}
135+
136+
@Override
137+
public int stop() {
138+
drive.runUsingAllEncoders();
139+
//just pass through the position
140+
return positionInArray;
141+
}
142+
143+
/**
144+
* Resets the position in array number so you can changing it in the next step
145+
*
146+
* @return this object for building
147+
*/
148+
public PIDEncoderDrive resetPositionInArray() {
149+
positionInArray = 0;
150+
return this;
151+
}
152+
153+
154+
public PIDEncoderDrive setPID(double p, double i, double d, double settlingTime, int tolerance, double maxSpeed) {
155+
leftController = new PIDController(p, i, d, 0, tolerance, settlingTime);
156+
rightController = new PIDController(p, i, d, 0, tolerance, settlingTime);
157+
158+
leftController.setNoOscillation(true);//we don't want to change direction to correct to target
159+
rightController.setNoOscillation(true);
160+
161+
leftController.setOutputRange(-maxSpeed, maxSpeed);
162+
rightController.setOutputRange(-maxSpeed, maxSpeed);
163+
return this;
164+
}
165+
166+
/**
167+
* @param distanceLeft distance in inches the left motor(s) should go
168+
* @param distanceRight distance in inches the right motor(s) should go
169+
* @return this object for building
170+
*/
171+
public PIDEncoderDrive setDistances(double distanceLeft, double distanceRight) {
172+
leftRotations = distanceLeft / wheelCircumference;
173+
rightRotations = distanceRight / wheelCircumference;
174+
return this;
175+
}
176+
177+
public PIDEncoderDrive setWheelCircumference(double circumference) {
178+
wheelCircumference = circumference;
179+
return this;
180+
}
181+
182+
public PIDEncoderDrive setConfig(PIDConfig config) {
183+
setWheelCircumference(config.wheelCircumference);
184+
setPID(config.p, config.i, config.d, config.settlingTime, config.tolerance, config.maxSpeed);
185+
return this;
186+
}
187+
188+
public static class PIDConfig {
189+
private double p, i, d, wheelCircumference, settlingTime;
190+
private int tolerance;
191+
private double maxSpeed;
192+
193+
/**
194+
*
195+
* @param p the proportional value in PID
196+
* @param i the integral value in PID
197+
* @param d the derivative value in PID
198+
* @return object for building
199+
*/
200+
public PIDConfig setPID(double p, double i, double d) {
201+
this.p = p;
202+
this.i = i;
203+
this.d = d;
204+
return this;
205+
}
206+
207+
public double getP() {
208+
return p;
209+
}
210+
211+
public PIDConfig setP(double p) {
212+
this.p = p;
213+
return this;
214+
}
215+
216+
public double getI() {
217+
return i;
218+
}
219+
220+
public PIDConfig setI(double i) {
221+
this.i = i;
222+
return this;
223+
}
224+
225+
public double getD() {
226+
return d;
227+
}
228+
229+
public PIDConfig setD(double d) {
230+
this.d = d;
231+
return this;
232+
}
233+
234+
public double getWheelCircumference() {
235+
return wheelCircumference;
236+
}
237+
238+
/**
239+
*
240+
* @param wheelCircumference The circumference of your wheels. It is okay to say diameter * 3.141, that is plenty of precision
241+
* @return object for building
242+
*/
243+
public PIDConfig setWheelCircumference(double wheelCircumference) {
244+
this.wheelCircumference = wheelCircumference;
245+
return this;
246+
}
247+
248+
public double getSettlingTime() {
249+
return settlingTime;
250+
}
251+
252+
/**
253+
*
254+
* @param settlingTime how long the robot needs to be within tolerance of the specified position before being considered done moving
255+
* @return object for building
256+
*/
257+
public PIDConfig setSettlingTime(double settlingTime) {
258+
this.settlingTime = settlingTime;
259+
return this;
260+
}
261+
262+
public int getTolerance() {
263+
return tolerance;
264+
}
265+
266+
/**
267+
*
268+
* @param tolerance how many encoder ticks away from the target value is considered 'close enough'
269+
* @return object for building
270+
*/
271+
public PIDConfig setTolerance(int tolerance) {
272+
this.tolerance = tolerance;
273+
return this;
274+
}
275+
276+
public double getMaxSpeed() {
277+
return maxSpeed;
278+
}
279+
280+
/**
281+
* Note that the PID drive may never reach this speed depending on distance and what PID values are given
282+
* @param maxSpeed max speed you want to let your robot go
283+
* @return object for building
284+
*/
285+
public PIDConfig setMaxSpeed(double maxSpeed) {
286+
this.maxSpeed = maxSpeed;
287+
return this;
288+
}
289+
}
290+
}

0 commit comments

Comments
 (0)