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