public static TurretState withMinimumSpeed(Translation2d initialVelocity, Translation3d target,
double tolerance) {
+ // System.out.println("!!! inv:" + initialVelocity + " tgt:" + target + " tlr:" + tolerance);
// trying to calculate a shot for height=0 returns NaN
- double effectiveMinHeight = Math.max(target.getZ(), 0.01);
+ double effectiveMinHeight = Math.max(target.getZ(), 0.001);
// guess a peak height
- double guess = target.getZ() + 2;
+ double guess = effectiveMinHeight + 5;
+ double lastDerivative = Double.NaN;
+ double lastGuess = Double.NaN;
int maxIters = 50;
while (maxIters >= 0) {
maxIters--;
Translation3d guessVelocity = getRequiredExitVelocity(initialVelocity, target, guess);
Translation3d guessVelocityMore = getRequiredExitVelocity(initialVelocity, target, guess + 0.1);
+
double derivative = (guessVelocityMore.getNorm() - guessVelocity.getNorm()) / 0.1;
- // System.out.println(guess + "\t\t" + guessVelocity.getNorm() + "\t\t" +
- // derivative);
// we've already hit minimum height and are trying to go lower
if (guess <= effectiveMinHeight && derivative > 0)
if (Math.abs(derivative) <= tolerance)
return cvtShot(guessVelocity, guess);
- // desmos guesstimation indicates this works quite well
- guess -= derivative * (Math.pow(guess, 2) + 1) / 10;
+ double secondDerivative;
+ if (Double.isNaN(lastDerivative) || Double.isNaN(lastGuess))
+ secondDerivative = 0;
+ else
+ secondDerivative = (lastDerivative - derivative) / (lastGuess - guess);
+
+ assert Double.isFinite(secondDerivative);
+ lastGuess = guess;
+ lastDerivative = derivative;
+
+ // System.out.println(guess + "\t\t" + "\t\t" + derivative + "\t\t" + secondDerivative);
+ if (secondDerivative == 0)
+ guess -= derivative * 2;
+ else
+ guess -= derivative / Math.abs(secondDerivative);
}
throw new RuntimeException("Failed to compute a trajectory for a minimum speed.");
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import edu.wpi.first.math.geometry.Rotation2d;
public void velocityTest() {
// var t1 = new Translation3d(100, 0, 0);
- // for (int i = 0; i < 1000; i++) {
+ // for (int i = 1; i < 1000; i++) {
// var x = ShooterPhysics.getShotParams(Translation2d.kZero, t1, i / 10.);
// System.out.println(i / 10. + ", " + x.exitVel());
// }
var state1 = ShooterPhysics.withMinimumSpeed(Translation2d.kZero, t1);
// check moving either way is higher velocity
var state1Plus = ShooterPhysics.getShotParams(Translation2d.kZero, t1,
- state1.height() + 0.1);
+ state1.height() + 0.2);
var state1Minus = ShooterPhysics.getShotParams(Translation2d.kZero, t1,
- state1.height() - 0.1);
+ state1.height() - 0.2);
assertTrue(state1.exitVel() < state1Plus.exitVel(), state1Plus.toString());
assertTrue(state1.exitVel() < state1Minus.exitVel(), state1Minus.toString());
assertEquals(Math.PI / 4, state1.pitch(), epsilon);
var t2 = new Translation3d(1, 1, 100);
var state2 = ShooterPhysics.withMinimumSpeed(Translation2d.kZero, t2);
// this should get to the minimum height
- var state2Plus = ShooterPhysics.getShotParams(Translation2d.kZero, t2, state2.height() + 0.1);
+ var state2Plus = ShooterPhysics.getShotParams(Translation2d.kZero, t2,
+ state2.height() + 0.1);
assertTrue(state2.exitVel() < state2Plus.exitVel(), state2Plus.toString());
assertEquals(t2.getZ(), state2.height(), epsilon);
var v3 = new Translation2d(10, -20);
var state3 = ShooterPhysics.withMinimumSpeed(v3, t3);
// check moving either way is higher velocity
- var state3Plus = ShooterPhysics.getShotParams(v3, t3, state3.height() + 0.1);
- var state3Minus = ShooterPhysics.getShotParams(v3, t3, state3.height() - 0.1);
+ var state3Plus = ShooterPhysics.getShotParams(v3, t3, state3.height() + 0.2);
+ var state3Minus = ShooterPhysics.getShotParams(v3, t3, state3.height() - 0.2);
assertTrue(state3.exitVel() < state3Plus.exitVel(), state3Plus.toString());
assertTrue(state3.exitVel() < state3Minus.exitVel(), state3Minus.toString());
}
// compute 1000 random shots and simulate them
for (int i = 0; i < 1000; i++) {
- Translation2d initPos = new Translation2d(rng.nextDouble() * 20 - 10, rng.nextDouble() * 20 - 10);
- Translation3d target = new Translation3d(rng.nextDouble() * 20 - 10, rng.nextDouble() * 20 - 10,
- rng.nextDouble() * 10 + 1);
- Translation2d initVel = new Translation2d(rng.nextDouble() * 10 - 5, rng.nextDouble() * 10 - 5);
+ Translation2d initPos = new Translation2d(rng.nextDouble(-10, 10), rng.nextDouble(-10, 10));
+ Translation3d target = new Translation3d(rng.nextDouble(-10, 10), rng.nextDouble(-10, 10),
+ rng.nextDouble(1, 11));
+ Translation2d initVel = new Translation2d(rng.nextDouble(-5, 5), rng.nextDouble(-5, 5));
Translation3d robotToTarget = target.minus(new Translation3d(initPos));
- double arcHeight = target.getZ() + rng.nextDouble() * 10;
+ double arcHeight = target.getZ() + rng.nextDouble(10);
Translation3d exitVel = ShooterPhysics.getRequiredExitVelocity(initVel, robotToTarget, arcHeight);
}
}
+ @Disabled
+ @Test
+ public void simulatedConstraintsTest() {
+ Random rng = new Random(6328);
+
+ for (int i = 0; i < 1000; i++) {
+ Translation2d initPos = new Translation2d(rng.nextDouble(-10, 10), rng.nextDouble(-10, 10));
+ Translation3d target = new Translation3d(rng.nextDouble(-10, 10), rng.nextDouble(-10, 10),
+ rng.nextDouble(1, 11));
+ Translation2d initVel = new Translation2d(rng.nextDouble(-5, 5), rng.nextDouble(-5, 5));
+ Translation3d robotToTarget = target.minus(new Translation3d(initPos));
+
+ double minAngle = rng.nextDouble(.01, Math.PI / 2 - .03);
+ Constraints constraints = new Constraints(rng.nextDouble(20), rng.nextDouble(30), minAngle,
+ rng.nextDouble(minAngle + .01, Math.PI / 2 - .01));
+
+ var state = ShooterPhysics.getConstrainedParams(initVel, robotToTarget, constraints);
+
+ if (state.isPresent()) {
+ assertTrue(state.get().satisfies(constraints));
+
+ // check going down breaks a constraint; we've found the minimum
+ var lower = ShooterPhysics.withAngle(initVel, robotToTarget, state.get().pitch() - .1);
+ assertTrue(lower.isEmpty());
+ // check going up is okay
+ var higher = ShooterPhysics.withAngle(initVel, robotToTarget, state.get().pitch() + .1);
+ assertTrue(higher.isPresent());
+ }
+ }
+ }
+
private class PhysicsObject {
private Translation3d pos;
private Translation3d vel;