|
6 | 6 | import com.ctre.phoenix6.swerve.SwerveRequest; |
7 | 7 | import edu.wpi.first.epilogue.Logged; |
8 | 8 | import edu.wpi.first.math.geometry.Rotation2d; |
| 9 | +import edu.wpi.first.math.geometry.Translation2d; |
| 10 | +import edu.wpi.first.math.kinematics.ChassisSpeeds; |
9 | 11 | import edu.wpi.first.math.kinematics.SwerveModuleState; |
10 | 12 | import edu.wpi.first.wpilibj2.command.Command; |
11 | 13 | import frc.robot.config.TunerConstants; |
12 | 14 | import frc.robot.helpers.ApplyModuleStates; |
| 15 | +import frc.robot.helpers.ShotCalculator; |
13 | 16 | import frc.robot.subsystems.CommandSwerveDrivetrain; |
14 | 17 | import java.util.function.DoubleSupplier; |
15 | 18 |
|
@@ -40,6 +43,8 @@ public static enum Position { |
40 | 43 | SwerveModule.DriveRequestType |
41 | 44 | .OpenLoopVoltage); // Use open-loop control for drive motors |
42 | 45 |
|
| 46 | + private static final double NOTE_EXIT_VELOCITY = 10.0; // This needs to be tuned |
| 47 | + |
43 | 48 | SwerveModuleState[] states = { |
44 | 49 | new SwerveModuleState(0, Rotation2d.fromDegrees(45)), |
45 | 50 | new SwerveModuleState(0, Rotation2d.fromDegrees(135)), |
@@ -88,12 +93,35 @@ public void execute() { |
88 | 93 | System.out.println("Drivetrain: Still Shot Configuration"); |
89 | 94 | break; |
90 | 95 |
|
91 | | - // (Incomplete) Mode for moving shots |
| 96 | + // (Incomplete) Mode for moving shots; pretty much finished but needs tuning and testing, and |
| 97 | + // may need to be changed to use a different method of calculating the shot |
92 | 98 | case SOTM: |
93 | | - // shoot on the move, reference Mechanical Advantage build log |
94 | | - System.out.println("Drivetrain: SOTM Drive"); |
95 | | - break; |
| 99 | + double vx = -leftY.getAsDouble() * MaxSpeed; |
| 100 | + double vy = -leftX.getAsDouble() * MaxSpeed; |
| 101 | + Translation2d robotPosition = subsystem.getState().Pose.getTranslation(); |
| 102 | + ChassisSpeeds speeds = subsystem.getState().Speeds; |
| 103 | + Translation2d robotVelocity = |
| 104 | + new Translation2d(speeds.vxMetersPerSecond, speeds.vyMetersPerSecond); |
| 105 | + Translation2d goal = ShotCalculator.calculateGoalPosition(); |
| 106 | + double distance = robotPosition.getDistance(goal); |
| 107 | + double flightTime = distance / NOTE_EXIT_VELOCITY; |
| 108 | + Translation2d toGoal = goal.minus(robotPosition); |
| 109 | + Translation2d toGoalDir = |
| 110 | + toGoal.getNorm() > 1e-6 ? toGoal.div(toGoal.getNorm()) : new Translation2d(); |
| 111 | + Translation2d lateralVelocity = |
| 112 | + robotVelocity.minus(toGoalDir.times(robotVelocity.dot(toGoalDir))); |
| 113 | + Translation2d virtualGoal = goal.minus(lateralVelocity.times(flightTime)); |
| 114 | + Rotation2d targetAngle = virtualGoal.minus(robotPosition).getAngle(); |
| 115 | + double currentHeading = subsystem.getState().Pose.getRotation().getRadians(); |
| 116 | + double targetHeading = targetAngle.getRadians(); |
| 117 | + double error = targetHeading - currentHeading; |
| 118 | + error = Math.atan2(Math.sin(error), Math.cos(error)); |
| 119 | + double kP = 4.0; // tune |
| 120 | + double omega = error * kP; |
| 121 | + omega = Math.max(-MaxAngularRate, Math.min(MaxAngularRate, omega)); |
| 122 | + subsystem.setControl(drive.withVelocityX(vx).withVelocityY(vy).withRotationalRate(omega)); |
96 | 123 |
|
| 124 | + break; |
97 | 125 | default: |
98 | 126 | break; |
99 | 127 | } |
|
0 commit comments