11package com .pedropathing .follower ;
22
33import static com .pedropathing .follower .FollowerConstants .automaticHoldEnd ;
4+ import static com .pedropathing .follower .FollowerConstants .cacheInvalidateSeconds ;
45import static com .pedropathing .follower .FollowerConstants .drivePIDFFeedForward ;
56import static com .pedropathing .follower .FollowerConstants .drivePIDFSwitch ;
67import static com .pedropathing .follower .FollowerConstants .forwardZeroPowerAcceleration ;
910import static com .pedropathing .follower .FollowerConstants .lateralZeroPowerAcceleration ;
1011import static com .pedropathing .follower .FollowerConstants .leftFrontMotorName ;
1112import static com .pedropathing .follower .FollowerConstants .leftRearMotorName ;
12- import static com .pedropathing .follower .FollowerConstants .localizers ;
13+ import static com .pedropathing .follower .FollowerConstants .nominalVoltage ;
1314import static com .pedropathing .follower .FollowerConstants .rightFrontMotorName ;
1415import static com .pedropathing .follower .FollowerConstants .rightRearMotorName ;
1516import static com .pedropathing .follower .FollowerConstants .leftFrontMotorDirection ;
2425import static com .pedropathing .follower .FollowerConstants .useSecondaryDrivePID ;
2526import static com .pedropathing .follower .FollowerConstants .useSecondaryHeadingPID ;
2627import static com .pedropathing .follower .FollowerConstants .useSecondaryTranslationalPID ;
28+ import static com .pedropathing .follower .FollowerConstants .useVoltageCompensationInAuto ;
29+ import static com .pedropathing .follower .FollowerConstants .useVoltageCompensationInTeleOp ;
2730
2831import android .util .Log ;
2932
3033import com .acmerobotics .dashboard .config .Config ;
3134import com .acmerobotics .dashboard .telemetry .MultipleTelemetry ;
3235import com .qualcomm .robotcore .hardware .DcMotor ;
3336import com .qualcomm .robotcore .hardware .DcMotorEx ;
34- import com .qualcomm .robotcore .hardware .DcMotorSimple ;
3537import com .qualcomm .robotcore .hardware .HardwareMap ;
38+ import com .qualcomm .robotcore .hardware .VoltageSensor ;
3639import com .qualcomm .robotcore .hardware .configuration .typecontainers .MotorConfigurationType ;
3740
3841import org .firstinspires .ftc .robotcore .external .Telemetry ;
@@ -88,7 +91,7 @@ public class Follower {
8891
8992 private PathChain currentPathChain ;
9093
91- private final int BEZIER_CURVE_BINARY_STEP_LIMIT = FollowerConstants .BEZIER_CURVE_BINARY_STEP_LIMIT ;
94+ private final int BEZIER_CURVE_SEARCH_LIMIT = FollowerConstants .BEZIER_CURVE_SEARCH_LIMIT ;
9295 private final int AVERAGED_VELOCITY_SAMPLE_NUMBER = FollowerConstants .AVERAGED_VELOCITY_SAMPLE_NUMBER ;
9396
9497 private int chainIndex ;
@@ -131,6 +134,8 @@ public class Follower {
131134 public Vector centripetalVector ;
132135 public Vector correctiveVector ;
133136
137+ private double centripetalScaling = FollowerConstants .centripetalScaling ;
138+
134139 private PIDFController secondaryTranslationalPIDF = new PIDFController (FollowerConstants .secondaryTranslationalPIDFCoefficients );
135140 private PIDFController secondaryTranslationalIntegral = new PIDFController (FollowerConstants .secondaryTranslationalIntegral );
136141 private PIDFController translationalPIDF = new PIDFController (FollowerConstants .translationalPIDFCoefficients );
@@ -151,10 +156,20 @@ public class Follower {
151156 public static boolean useHeading = true ;
152157 public static boolean useDrive = true ;
153158
159+ /*
160+ * Voltage Compensation
161+ * Credit to team 14343 Escape Velocity for the voltage code
162+ * Credit to team 23511 Seattle Solvers for implementing the voltage code into Follower.java
163+ */
164+ private boolean cached = false ;
165+
166+ private VoltageSensor voltageSensor ;
167+ public double voltage = 0 ;
168+ private final ElapsedTime voltageTimer = new ElapsedTime ();
169+
154170 private boolean logDebug = true ;
155171
156172 private ElapsedTime zeroVelocityDetectedTimer ;
157- private ElapsedTime pinpointRecalibrationTimer ;
158173
159174 /**
160175 * This creates a new Follower given a HardwareMap.
@@ -185,6 +200,9 @@ public void initialize() {
185200 poseUpdater = new PoseUpdater (hardwareMap );
186201 driveVectorScaler = new DriveVectorScaler (FollowerConstants .frontLeftVector );
187202
203+ voltageSensor = hardwareMap .voltageSensor .iterator ().next ();
204+ voltageTimer .reset ();
205+
188206 leftFront = hardwareMap .get (DcMotorEx .class , leftFrontMotorName );
189207 leftRear = hardwareMap .get (DcMotorEx .class , leftRearMotorName );
190208 rightRear = hardwareMap .get (DcMotorEx .class , rightRearMotorName );
@@ -221,6 +239,9 @@ public void initialize(Localizer localizer) {
221239 poseUpdater = new PoseUpdater (hardwareMap , localizer );
222240 driveVectorScaler = new DriveVectorScaler (FollowerConstants .frontLeftVector );
223241
242+ voltageSensor = hardwareMap .voltageSensor .iterator ().next ();
243+ voltageTimer .reset ();
244+
224245 leftFront = hardwareMap .get (DcMotorEx .class , leftFrontMotorName );
225246 leftRear = hardwareMap .get (DcMotorEx .class , leftRearMotorName );
226247 rightRear = hardwareMap .get (DcMotorEx .class , rightRearMotorName );
@@ -245,6 +266,10 @@ public void initialize(Localizer localizer) {
245266 breakFollowing ();
246267 }
247268
269+ public void setCentripetalScaling (double set ) {
270+ centripetalScaling = set ;
271+ }
272+
248273 /**
249274 * This sets the motors to the zero power behavior of brake.
250275 */
@@ -463,7 +488,7 @@ public void followPath(Path path, boolean holdEnd) {
463488 isBusy = true ;
464489 followingPathChain = false ;
465490 currentPath = path ;
466- closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_BINARY_STEP_LIMIT );
491+ closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_SEARCH_LIMIT );
467492 }
468493
469494 /**
@@ -514,7 +539,8 @@ public void followPath(PathChain pathChain, double maxPower, boolean holdEnd) {
514539 chainIndex = 0 ;
515540 currentPathChain = pathChain ;
516541 currentPath = pathChain .getPath (chainIndex );
517- closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_BINARY_STEP_LIMIT );
542+ closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_SEARCH_LIMIT );
543+ currentPathChain .resetCallbacks ();
518544 }
519545
520546 /**
@@ -524,7 +550,7 @@ public void resumePathFollowing() {
524550 pathStartTimes = new long [currentPathChain .size ()];
525551 pathStartTimes [0 ] = System .currentTimeMillis ();
526552 isBusy = true ;
527- closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_BINARY_STEP_LIMIT );
553+ closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_SEARCH_LIMIT );
528554 }
529555
530556 /**
@@ -566,20 +592,32 @@ public void update() {
566592
567593 for (int i = 0 ; i < motors .size (); i ++) {
568594 if (Math .abs (motors .get (i ).getPower () - drivePowers [i ]) > FollowerConstants .motorCachingThreshold ) {
569- motors .get (i ).setPower (drivePowers [i ]);
595+ double voltageNormalized = getVoltageNormalized ();
596+
597+ if (useVoltageCompensationInAuto ) {
598+ motors .get (i ).setPower (drivePowers [i ] * voltageNormalized );
599+ } else {
600+ motors .get (i ).setPower (drivePowers [i ]);
601+ }
570602 }
571603 }
572604 } else {
573605 if (isBusy ) {
574- closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_BINARY_STEP_LIMIT );
606+ closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_SEARCH_LIMIT );
575607
576608 if (followingPathChain ) updateCallbacks ();
577609
578610 drivePowers = driveVectorScaler .getDrivePowers (getCorrectiveVector (), getHeadingVector (), getDriveVector (), poseUpdater .getPose ().getHeading ());
579611
580612 for (int i = 0 ; i < motors .size (); i ++) {
581613 if (Math .abs (motors .get (i ).getPower () - drivePowers [i ]) > FollowerConstants .motorCachingThreshold ) {
582- motors .get (i ).setPower (drivePowers [i ]);
614+ double voltageNormalized = getVoltageNormalized ();
615+
616+ if (useVoltageCompensationInAuto ) {
617+ motors .get (i ).setPower (drivePowers [i ] * voltageNormalized );
618+ } else {
619+ motors .get (i ).setPower (drivePowers [i ]);
620+ }
583621 }
584622 }
585623 }
@@ -609,7 +647,7 @@ public void update() {
609647 followingPathChain = true ;
610648 chainIndex ++;
611649 currentPath = currentPathChain .getPath (chainIndex );
612- closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_BINARY_STEP_LIMIT );
650+ closestPose = currentPath .getClosestPoint (poseUpdater .getPose (), BEZIER_CURVE_SEARCH_LIMIT );
613651 } else {
614652 // At last path, run some end detection stuff
615653 // set isBusy to false if at end
@@ -656,7 +694,13 @@ public void update() {
656694
657695 for (int i = 0 ; i < motors .size (); i ++) {
658696 if (Math .abs (motors .get (i ).getPower () - drivePowers [i ]) > FollowerConstants .motorCachingThreshold ) {
659- motors .get (i ).setPower (drivePowers [i ]);
697+ double voltageNormalized = getVoltageNormalized ();
698+
699+ if (useVoltageCompensationInTeleOp ) {
700+ motors .get (i ).setPower (drivePowers [i ] * voltageNormalized );
701+ } else {
702+ motors .get (i ).setPower (drivePowers [i ]);
703+ }
660704 }
661705 }
662706 }
@@ -861,18 +905,18 @@ public double getDriveVelocityError() {
861905 Vector velocity = new Vector (MathFunctions .dotProduct (getVelocity (), MathFunctions .normalizeVector (currentPath .getClosestPointTangentVector ())), currentPath .getClosestPointTangentVector ().getTheta ());
862906
863907 Vector forwardHeadingVector = new Vector (1.0 , poseUpdater .getPose ().getHeading ());
908+
864909 double forwardVelocity = MathFunctions .dotProduct (forwardHeadingVector , velocity );
865910 double forwardDistanceToGoal = MathFunctions .dotProduct (forwardHeadingVector , distanceToGoalVector );
866-
867911 double forwardVelocityGoal = MathFunctions .getSign (forwardDistanceToGoal ) * Math .sqrt (Math .abs (-2 * currentPath .getZeroPowerAccelerationMultiplier () * forwardZeroPowerAcceleration * (forwardDistanceToGoal <= 0 ? 1 : -1 ) * forwardDistanceToGoal ));
868- double forwardVelocityZeroPowerDecay = forwardVelocity - MathFunctions .getSign (forwardDistanceToGoal ) * Math .sqrt (Math .abs (Math .pow (forwardVelocity , 2 ) + 2 * forwardZeroPowerAcceleration * forwardDistanceToGoal ));
912+ double forwardVelocityZeroPowerDecay = forwardVelocity - MathFunctions .getSign (forwardDistanceToGoal ) * Math .sqrt (Math .abs (Math .pow (forwardVelocity , 2 ) + 2 * forwardZeroPowerAcceleration * Math . abs ( forwardDistanceToGoal ) ));
869913
870914 Vector lateralHeadingVector = new Vector (1.0 , poseUpdater .getPose ().getHeading () - Math .PI / 2 );
871915 double lateralVelocity = MathFunctions .dotProduct (lateralHeadingVector , velocity );
872916 double lateralDistanceToGoal = MathFunctions .dotProduct (lateralHeadingVector , distanceToGoalVector );
873917
874918 double lateralVelocityGoal = MathFunctions .getSign (lateralDistanceToGoal ) * Math .sqrt (Math .abs (-2 * currentPath .getZeroPowerAccelerationMultiplier () * lateralZeroPowerAcceleration * (lateralDistanceToGoal <= 0 ? 1 : -1 ) * lateralDistanceToGoal ));
875- double lateralVelocityZeroPowerDecay = lateralVelocity - MathFunctions .getSign (lateralDistanceToGoal ) * Math .sqrt (Math .abs (Math .pow (lateralVelocity , 2 ) + 2 * lateralZeroPowerAcceleration * lateralDistanceToGoal ));
919+ double lateralVelocityZeroPowerDecay = lateralVelocity - MathFunctions .getSign (lateralDistanceToGoal ) * Math .sqrt (Math .abs (Math .pow (lateralVelocity , 2 ) + 2 * lateralZeroPowerAcceleration * Math . abs ( lateralDistanceToGoal ) ));
876920
877921 Vector forwardVelocityError = new Vector (forwardVelocityGoal - forwardVelocityZeroPowerDecay - forwardVelocity , forwardHeadingVector .getTheta ());
878922 Vector lateralVelocityError = new Vector (lateralVelocityGoal - lateralVelocityZeroPowerDecay - lateralVelocity , lateralHeadingVector .getTheta ());
@@ -1021,7 +1065,7 @@ public Vector getCentripetalForceCorrection() {
10211065 curvature = (yDoublePrime ) / (Math .pow (Math .sqrt (1 + Math .pow (yPrime , 2 )), 3 ));
10221066 }
10231067 if (Double .isNaN (curvature )) return new Vector ();
1024- centripetalVector = new Vector (MathFunctions .clamp (FollowerConstants . centripetalScaling * FollowerConstants .mass * Math .pow (MathFunctions .dotProduct (poseUpdater .getVelocity (), MathFunctions .normalizeVector (currentPath .getClosestPointTangentVector ())), 2 ) * curvature , -driveVectorScaler .getMaxPowerScaling (), driveVectorScaler .getMaxPowerScaling ()), currentPath .getClosestPointTangentVector ().getTheta () + Math .PI / 2 * MathFunctions .getSign (currentPath .getClosestPointNormalVector ().getTheta ()));
1068+ centripetalVector = new Vector (MathFunctions .clamp (centripetalScaling * FollowerConstants .mass * Math .pow (MathFunctions .dotProduct (poseUpdater .getVelocity (), MathFunctions .normalizeVector (currentPath .getClosestPointTangentVector ())), 2 ) * curvature , -driveVectorScaler .getMaxPowerScaling (), driveVectorScaler .getMaxPowerScaling ()), currentPath .getClosestPointTangentVector ().getTheta () + Math .PI / 2 * MathFunctions .getSign (currentPath .getClosestPointNormalVector ().getTheta ()));
10251069 return centripetalVector ;
10261070 }
10271071
@@ -1192,7 +1236,68 @@ public void drawOnDashBoard() {
11921236 }
11931237 }
11941238
1195- public boolean isPinpointCooked () {
1196- return poseUpdater .getLocalizer ().isPinpointCooked ();
1239+ public boolean isLocalizationNAN () {
1240+ return poseUpdater .getLocalizer ().isNAN ();
1241+ }
1242+
1243+ /**
1244+ * @return The last cached voltage measurement.
1245+ */
1246+ public double getVoltage () {
1247+ if (voltageTimer .seconds () > cacheInvalidateSeconds && cacheInvalidateSeconds >= 0 ) {
1248+ cached = false ;
1249+ }
1250+
1251+ if (!cached )
1252+ refreshVoltage ();
1253+
1254+ return voltage ;
1255+ }
1256+
1257+ /**
1258+ * @return A scalar that normalizes power outputs to the nominal voltage from the current voltage.
1259+ */
1260+ public double getVoltageNormalized () {
1261+ return Math .min (nominalVoltage / getVoltage (), 1 );
1262+ }
1263+
1264+ /**
1265+ * Overrides the voltage cooldown.
1266+ */
1267+ public void refreshVoltage () {
1268+ cached = true ;
1269+ voltage = voltageSensor .getVoltage ();
1270+ voltageTimer .reset ();
1271+ }
1272+
1273+ /** Turns a certain amount of degrees left
1274+ * @param radians the amount of radians to turn
1275+ * @param isLeft true if turning left, false if turning right
1276+ */
1277+ public void turn (double radians , boolean isLeft ) {
1278+ Pose temp = new Pose (getPose ().getX (), getPose ().getY (), getPose ().getHeading () + (isLeft ? radians : -radians ));
1279+ holdPoint (temp );
1280+ }
1281+
1282+ /** Turns to a specific heading
1283+ * @param radians the heading in radians to turn to
1284+ */
1285+ public void turnTo (double radians ) {
1286+ holdPoint (new Pose (getPose ().getX (), getPose ().getY (), Math .toRadians (radians )));
1287+ }
1288+
1289+ /** Turns to a specific heading in degrees
1290+ * @param degrees the heading in degrees to turn to
1291+ */
1292+ public void turnToDegrees (double degrees ) {
1293+ turnTo (Math .toRadians (degrees ));
1294+ }
1295+
1296+ /** Turns a certain amount of degrees left
1297+ * @param degrees the amount of degrees to turn
1298+ * @param isLeft true if turning left, false if turning right
1299+ */
1300+ public void turnDegrees (double degrees , boolean isLeft ) {
1301+ turn (Math .toRadians (degrees ), isLeft );
11971302 }
11981303}
0 commit comments