Added ultrashooter command and support methods, constants, controller activation
This commit is contained in:
parent
5986bc8f92
commit
1dd99e305f
|
|
@ -14,6 +14,8 @@ import edu.wpi.first.math.geometry.Transform2d;
|
||||||
import edu.wpi.first.math.geometry.Transform3d;
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
import edu.wpi.first.units.measure.Angle;
|
import edu.wpi.first.units.measure.Angle;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Constants class provides a convenient place for teams to hold robot-wide
|
* The Constants class provides a convenient place for teams to hold robot-wide
|
||||||
* numerical or boolean constants. This class should not be used for any other
|
* numerical or boolean constants. This class should not be used for any other
|
||||||
|
|
@ -141,10 +143,27 @@ public final class Constants {
|
||||||
public static final Angle LIMIT_AXIMUTH_NEG = edu.wpi.first.units.Units.Degrees.of(5);
|
public static final Angle LIMIT_AXIMUTH_NEG = edu.wpi.first.units.Units.Degrees.of(5);
|
||||||
public static final double HOMING_SPEED = .05;
|
public static final double HOMING_SPEED = .05;
|
||||||
public static final double TURRET_GEAR_RATIO = 6;
|
public static final double TURRET_GEAR_RATIO = 6;
|
||||||
|
public static final double TURRET_RANGE_ROTATIONS = 0.5; // The turret can rotate 180°
|
||||||
|
|
||||||
public static final int HOOD_SERVO_CHANNEL = 0;
|
public static final int HOOD_SERVO_CHANNEL = 0;
|
||||||
public static final boolean HOOD_SERVO_INVERTED = false; // TODO: Test this
|
public static final boolean HOOD_SERVO_INVERTED = false; // TODO: Test this
|
||||||
public static final int HOOD_ENCODER_ID = 4;
|
public static final int HOOD_ENCODER_ID = 4;
|
||||||
|
public static final int HOOD_ANGLE_MIN_DEG = 52; // Lowest possible position
|
||||||
|
public static final int HOOD_ANGLE_MAX_DEG = 70; // Highest possible position
|
||||||
|
public static final int HOOD_ANGLE_PASSING_DEG = 55; // Default hood angle for passing
|
||||||
|
public static final int HOOD_ANGLE_SCORING_MAX_DEG = 70; // Highest angle for scoring
|
||||||
|
public static final int HOOD_ANGLE_SCORING_MIN_DEG = 62; // Lowest angle for scoring
|
||||||
|
public static final double SCORING_DISTANCE_LONG_IN = 225; // Shortest scoring distance in inches used for finding hood angles
|
||||||
|
public static final double SCORING_DISTANCE_SHORT_IN = 60; // Shortest scoring distance in inches used for finding hood angles
|
||||||
|
public static final double SCORING_DISTANCE_FIELD_MAX_METERS = 7; // Sanity check value. Max distance from hub in Alliance Zone is ~6 meters
|
||||||
|
public static final double SCORING_DISTANCE_FIELD_MIN_METERS = 0.1; // Sanity check value.
|
||||||
|
public static final double SCORING_DISTANCE_MIN_ALLOWED_METERS = 1.5; // The minimum distance from the hub we'll allow the robot to shoot from
|
||||||
|
public static final double SCORING_DISTANCE_MAX_ALLOWED_METERS = 7.0; // The maximum distance from the hub we'll allow the robot to shoot from
|
||||||
|
// This is the distance increment in inches for adjusting hood angle according to distance from the target, in inches per degree:
|
||||||
|
public static final double HOOD_ANGLE_CALC_DIVISION = (SCORING_DISTANCE_LONG_IN - SCORING_DISTANCE_SHORT_IN) / (HOOD_ANGLE_SCORING_MAX_DEG - HOOD_ANGLE_SCORING_MIN_DEG);
|
||||||
|
public static final double SHOOTER_EFFICIENCY_GENERAL = 0.30; // This is the baseline shooter efficiency assumption
|
||||||
|
public static final double SHOOTER_FLYWHEEL_DIAMETER_IN = 3.0; // Flywheel diameter is 3.0 inches
|
||||||
|
public static final double SHOOTER_FLYWHEEL_GEAR_RATIO = 2.0; // Flywheel moves at twice the RPM of the flywheel motor
|
||||||
public static final Angle TARGET_ELEVATION_DIF = edu.wpi.first.units.Units.Degrees.of(1);
|
public static final Angle TARGET_ELEVATION_DIF = edu.wpi.first.units.Units.Degrees.of(1);
|
||||||
public static final Angle LIMIT_ELEVATION_POS = edu.wpi.first.units.Units.Degrees.of(90);
|
public static final Angle LIMIT_ELEVATION_POS = edu.wpi.first.units.Units.Degrees.of(90);
|
||||||
public static final Angle LIMIT_ELEVATION_NEG = edu.wpi.first.units.Units.Degrees.of(0);
|
public static final Angle LIMIT_ELEVATION_NEG = edu.wpi.first.units.Units.Degrees.of(0);
|
||||||
|
|
@ -153,6 +172,41 @@ public final class Constants {
|
||||||
public static final double DEFLECTOR_SET_ANGLE2 = 200;
|
public static final double DEFLECTOR_SET_ANGLE2 = 200;
|
||||||
public static final double DEFLECTOR_SET_ANGLE3 = 300;
|
public static final double DEFLECTOR_SET_ANGLE3 = 300;
|
||||||
public static final double DEFLECTOR_SERVO_RATIO = 1.0;
|
public static final double DEFLECTOR_SERVO_RATIO = 1.0;
|
||||||
|
|
||||||
|
// Below are sets of quadratic coefficients that are to be used with each valid hood angle, for scoring and passing.
|
||||||
|
// These are from Table 2-2 in the AutoShooter document for use in scoring when distance is in meters.
|
||||||
|
record QuadraticCoef(double a, double b, double c) {} // Creates a basic record type with no special methods ("{}")
|
||||||
|
|
||||||
|
public static Map<Integer, QuadraticCoef> SCORING_SPEED_COEFS_METERS = Map.of( // Access this map to get scoring coefficients for each hood angle
|
||||||
|
62, new QuadraticCoef(-0.08267, 3.41207, 12.20000),
|
||||||
|
63, new QuadraticCoef(-0.12400, 3.85827, 11.50000),
|
||||||
|
64, new QuadraticCoef(-0.09300, 3.74016, 11.70000),
|
||||||
|
65, new QuadraticCoef(-0.06200, 3.54331, 12.40000),
|
||||||
|
66, new QuadraticCoef(-0.09300, 3.89764, 12.00000),
|
||||||
|
67, new QuadraticCoef(-0.06200, 3.85827, 12.20000),
|
||||||
|
68, new QuadraticCoef(0.00000, 3.54331, 13.00000),
|
||||||
|
69, new QuadraticCoef(0.03100, 3.58268, 13.10000),
|
||||||
|
70, new QuadraticCoef(0.12056, 3.25022, 13.96667));
|
||||||
|
|
||||||
|
// These are from Table 2-2 in the AutoShooter document for use in scoring when distance is in inches.
|
||||||
|
public static Map<Integer, QuadraticCoef> SCORING_SPEED_COEFS_INCHES = Map.of( // Access this map to get scoring coefficients for each hood angle
|
||||||
|
62, new QuadraticCoef(-0.00005, 0.08667, 12.20000),
|
||||||
|
63, new QuadraticCoef(-0.00008, 0.09800, 11.50000),
|
||||||
|
64, new QuadraticCoef(-0.00006, 0.09500, 11.70000),
|
||||||
|
65, new QuadraticCoef(-0.00004, 0.09000, 12.40000),
|
||||||
|
66, new QuadraticCoef(-0.00006, 0.09900, 12.00000),
|
||||||
|
67, new QuadraticCoef(-0.00004, 0.09800, 12.20000),
|
||||||
|
68, new QuadraticCoef(0.00000, 0.09000, 13.00000),
|
||||||
|
69, new QuadraticCoef(0.00002, 0.09100, 13.10000),
|
||||||
|
70, new QuadraticCoef(0.00008, 0.08256, 13.96667));
|
||||||
|
|
||||||
|
// These are from Table 2-1 in the AutoShooter document for use in passing when distance is in meters.
|
||||||
|
public static Map<Integer, QuadraticCoef> PASSING_SPEED_COEFS_METERS = Map.of(
|
||||||
|
55, new QuadraticCoef(-0.09751, 3.47134, 7.8890));
|
||||||
|
|
||||||
|
// These are from Table 2-1 in the AutoShooter document for use in passing when distance is in inches.
|
||||||
|
public static Map<Integer, QuadraticCoef> PASSING_SPEED_COEFS_INCHES = Map.of(
|
||||||
|
55, new QuadraticCoef(-0.00006, 0.08817, 7.889));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class HopperConstants {
|
public static class HopperConstants {
|
||||||
|
|
@ -187,7 +241,7 @@ public final class Constants {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// These represent the dividing lines for the regions in our shooting strategy.
|
// These represent the dividing lines for the regions in our shooting strategy. These values are in meters.
|
||||||
public static class ShootingRegionDimensions {
|
public static class ShootingRegionDimensions {
|
||||||
|
|
||||||
public static double BLUE_ALLIANCE_ZONE_REGION_X_MAX = 3.5;
|
public static double BLUE_ALLIANCE_ZONE_REGION_X_MAX = 3.5;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import frc.robot.commands.AutoTurretCommand;
|
||||||
import frc.robot.commands.AutoTurretShootCommand;
|
import frc.robot.commands.AutoTurretShootCommand;
|
||||||
import frc.robot.commands.Reverse;
|
import frc.robot.commands.Reverse;
|
||||||
import frc.robot.commands.SetDeflectorCommand;
|
import frc.robot.commands.SetDeflectorCommand;
|
||||||
|
import frc.robot.commands.UltraShooterCommand;
|
||||||
import frc.robot.subsystems.DriveSubsystem;
|
import frc.robot.subsystems.DriveSubsystem;
|
||||||
import frc.robot.subsystems.HopperSubsystem;
|
import frc.robot.subsystems.HopperSubsystem;
|
||||||
import frc.robot.subsystems.IntakeSubsystem;
|
import frc.robot.subsystems.IntakeSubsystem;
|
||||||
|
|
@ -59,7 +60,9 @@ public class RobotContainer {
|
||||||
NamedCommands.registerCommand("RunIntake", intakeSubsystem.intake(m_driverController.getHID()));
|
NamedCommands.registerCommand("RunIntake", intakeSubsystem.intake(m_driverController.getHID()));
|
||||||
NamedCommands.registerCommand("TurretShoot", new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem));
|
NamedCommands.registerCommand("TurretShoot", new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem));
|
||||||
|
|
||||||
turretSubsystem.setDefaultCommand(new AutoTurretCommand(turretSubsystem, driveSubsystem));
|
// The default command below is commented out so that UltraShooterCommand can control turret while the shooter function is active.
|
||||||
|
// The default command can be re-instated if we choose to track a target at all times instead.
|
||||||
|
// turretSubsystem.setDefaultCommand(new AutoTurretCommand(turretSubsystem, driveSubsystem));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,6 +77,7 @@ public class RobotContainer {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private void configureBindings() {
|
private void configureBindings() {
|
||||||
|
// These let the driver set the shooter hood position using the controller.
|
||||||
// m_driverController.povDown().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_STORED_ANGLE));
|
// m_driverController.povDown().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_STORED_ANGLE));
|
||||||
// m_driverController.povLeft().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_SET_ANGLE1));
|
// m_driverController.povLeft().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_SET_ANGLE1));
|
||||||
// m_driverController.povRight().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_SET_ANGLE3));
|
// m_driverController.povRight().onTrue(new SetDeflectorCommand(turretSubsystem, Constants.ShooterConstants.DEFLECTOR_SET_ANGLE3));
|
||||||
|
|
@ -83,8 +87,10 @@ public class RobotContainer {
|
||||||
m_driverController.leftBumper().onTrue(Commands.runOnce(() -> turretSubsystem.homeTurret(), turretSubsystem));
|
m_driverController.leftBumper().onTrue(Commands.runOnce(() -> turretSubsystem.homeTurret(), turretSubsystem));
|
||||||
m_driverController.rightBumper().onTrue(Commands.parallel(intakeSubsystem.intake(m_driverController.getHID())));
|
m_driverController.rightBumper().onTrue(Commands.parallel(intakeSubsystem.intake(m_driverController.getHID())));
|
||||||
m_driverController.start().whileTrue(intakeSubsystem.resetDeploy());
|
m_driverController.start().whileTrue(intakeSubsystem.resetDeploy());
|
||||||
m_driverController.a().whileTrue((new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem)));
|
// m_driverController.a().whileTrue((new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem)));
|
||||||
m_driverController.x().whileTrue((new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem)));
|
// m_driverController.x().whileTrue((new AutoTurretShootCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem)));
|
||||||
|
//m_driverController.x().toggleOnTrue(getAutonomousCommand())((new UltraShooterCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem)));
|
||||||
|
m_driverController.x().toggleOnTrue(new UltraShooterCommand(hopperSubsystem, turretSubsystem, intakeSubsystem, driveSubsystem));
|
||||||
m_driverController.b().whileTrue(new Reverse(intakeSubsystem, hopperSubsystem, m_driverController.getHID()));
|
m_driverController.b().whileTrue(new Reverse(intakeSubsystem, hopperSubsystem, m_driverController.getHID()));
|
||||||
m_driverController.y().onTrue(intakeSubsystem.deploy());
|
m_driverController.y().onTrue(intakeSubsystem.deploy());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@ import frc.robot.Constants.ShootingRegionDimensions;
|
||||||
|
|
||||||
import org.littletonrobotics.junction.Logger;
|
import org.littletonrobotics.junction.Logger;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.MathUtil;
|
||||||
import edu.wpi.first.math.geometry.Pose3d;
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.util.Units;
|
||||||
import edu.wpi.first.units.measure.Angle;
|
import edu.wpi.first.units.measure.Angle;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,8 +41,8 @@ public class Utils {
|
||||||
/**
|
/**
|
||||||
* Determines the region for the robot based on its location and alliance.
|
* Determines the region for the robot based on its location and alliance.
|
||||||
* Assumes the robot is a 27" square. Thresholds based on observations from simulator.
|
* Assumes the robot is a 27" square. Thresholds based on observations from simulator.
|
||||||
* @param robotPose: the robot's pose
|
* @param robotPose the robot's pose
|
||||||
* @param alliance: the red/blue alliance of the robot, as a sanity check
|
* @param alliance the red/blue alliance of the robot, as a sanity check
|
||||||
* @return the robot's shooting region
|
* @return the robot's shooting region
|
||||||
*/
|
*/
|
||||||
public static ShootingRegion findRobotShootingRegion(Pose3d robotPose, Alliance alliance){
|
public static ShootingRegion findRobotShootingRegion(Pose3d robotPose, Alliance alliance){
|
||||||
|
|
@ -46,7 +50,7 @@ public class Utils {
|
||||||
double robotX = robotPose.getX();
|
double robotX = robotPose.getX();
|
||||||
double robotY = robotPose.getY();
|
double robotY = robotPose.getY();
|
||||||
|
|
||||||
// Receommended: Add a sanity check for invalid robot poses.
|
// Recommended: Add a sanity check for invalid robot poses.
|
||||||
|
|
||||||
if (alliance == Alliance.Blue) {
|
if (alliance == Alliance.Blue) {
|
||||||
if (robotX < Constants.ShootingRegionDimensions.BLUE_ALLIANCE_ZONE_REGION_X_MAX) {
|
if (robotX < Constants.ShootingRegionDimensions.BLUE_ALLIANCE_ZONE_REGION_X_MAX) {
|
||||||
|
|
@ -100,6 +104,39 @@ public class Utils {
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the ideal hood angle based on the provided distance
|
||||||
|
* between the robot's turret and the target (hub).
|
||||||
|
* Implements the equation to find hood angle for scoring:
|
||||||
|
* Angle = MaxAngle - ((Distance - ShortDistance) / Div), where
|
||||||
|
* MaxAngle = maximum hood angle for scoring
|
||||||
|
* Distance = distance to target
|
||||||
|
* ShortDistance = shortest distance used when evaluating hood angles
|
||||||
|
* LongDistance = longest distance used when evaluating hood angles
|
||||||
|
* Div = the distance from the target that requires 1° of hood angle change
|
||||||
|
* = (LongDistance - ShortDistance) / (MaxAngle - MinAngle)
|
||||||
|
* These were all defined as constants in Constants.java.
|
||||||
|
* @param distanceInMeters distance between turret and target, in meters
|
||||||
|
* @return the ideal nearest-integer hood angle in degrees relative to the floor
|
||||||
|
*/
|
||||||
|
public static int getScoringHoodAngleDegForDistance(double distanceInMeters){
|
||||||
|
double calculatedAngleDeg = Constants.ShooterConstants.HOOD_ANGLE_PASSING_DEG; // init to the passing value
|
||||||
|
double distanceInInches = Units.metersToInches(distanceInMeters); // Convert to inches to match theory equations
|
||||||
|
|
||||||
|
// Limit the distance according to the min and max that were used in the theory calculations
|
||||||
|
MathUtil.clamp(distanceInInches, Constants.ShooterConstants.SCORING_DISTANCE_SHORT_IN, Constants.ShooterConstants.SCORING_DISTANCE_LONG_IN);
|
||||||
|
|
||||||
|
// Perfom the main calculation (Equation 2-5 in the AutoShooter document), then round the result to the nearest degree
|
||||||
|
calculatedAngleDeg = Constants.ShooterConstants.HOOD_ANGLE_SCORING_MAX_DEG - (((distanceInInches) - Constants.ShooterConstants.SCORING_DISTANCE_SHORT_IN) /
|
||||||
|
Constants.ShooterConstants.HOOD_ANGLE_CALC_DIVISION);
|
||||||
|
int hoodAngleDeg = (int) Math.round(calculatedAngleDeg);
|
||||||
|
|
||||||
|
return (hoodAngleDeg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static double getHoodAngleForDistance(double distanceInMeters){
|
public static double getHoodAngleForDistance(double distanceInMeters){
|
||||||
if(distanceInMeters >= 4){
|
if(distanceInMeters >= 4){
|
||||||
return 200;
|
return 200;
|
||||||
|
|
@ -107,6 +144,93 @@ public class Utils {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the appropriate launch speed in ft/s for scoring based on
|
||||||
|
* the provided hood angle and target distance in meters.
|
||||||
|
* This is done based on a series of quadratic regressions
|
||||||
|
* See the AutoShooter theory document.
|
||||||
|
* @param hoodAngleDeg the hood angle that the calculation should assume
|
||||||
|
* @param distanceInMeters distance between turret and target, in meters
|
||||||
|
* @return the appropriate launch speed for scoring in feet per second
|
||||||
|
* Returns 0 if a launch speed cannot be determined.
|
||||||
|
*/
|
||||||
|
public static double findLaunchSpeedScoring(int hoodAngleDeg, double distanceInMeters){
|
||||||
|
// Check that the requested hood angle is in the expected shooting range
|
||||||
|
if ((hoodAngleDeg < Constants.ShooterConstants.HOOD_ANGLE_SCORING_MIN_DEG) ||
|
||||||
|
(hoodAngleDeg > Constants.ShooterConstants.HOOD_ANGLE_SCORING_MAX_DEG)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the requested distance is reasonable. If the scoring distance is outside of this range, there was a problem.
|
||||||
|
if ((distanceInMeters < Constants.ShooterConstants.SCORING_DISTANCE_FIELD_MIN_METERS) ||
|
||||||
|
(distanceInMeters > Constants.ShooterConstants.SCORING_DISTANCE_FIELD_MAX_METERS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the quadratic coefficients for the hood angle. Return with the error value if none are found.
|
||||||
|
Constants.ShooterConstants.QuadraticCoef coefficients = Constants.ShooterConstants.SCORING_SPEED_COEFS_METERS.get(hoodAngleDeg);
|
||||||
|
if (coefficients == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the quadratic calculation using the coefficients we retrieved if successful
|
||||||
|
double x = distanceInMeters; // Rename to "x" just to make the next expression simpler
|
||||||
|
double launchSpeedFPS = (coefficients.a() * x * x) + (coefficients.b() * x) + coefficients.c();
|
||||||
|
|
||||||
|
return launchSpeedFPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the appropriate launch speed in ft/s for passing based on
|
||||||
|
* the provided hood angle and target distance in meters.
|
||||||
|
* This is done based on a series of quadratic regressions
|
||||||
|
* See the AutoShooter theory document.
|
||||||
|
* @param hoodAngleDeg the hood angle that the calculation should assume
|
||||||
|
* @param distanceInMeters distance between turret and target, in meters
|
||||||
|
* @return the appropriate launch speed for passing in feet per second
|
||||||
|
* Returns 0 if a launch speed cannot be determined.
|
||||||
|
*/
|
||||||
|
public static double findLaunchSpeedPassing(int hoodAngleDeg, double distanceInMeters){
|
||||||
|
// Check that the requested hood angle is in the expected angle range for passing.
|
||||||
|
if (hoodAngleDeg != Constants.ShooterConstants.HOOD_ANGLE_PASSING_DEG) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the distance is reasonable. If the scoring distance is outside of this range, there was a problem.
|
||||||
|
if ((distanceInMeters < Constants.ShooterConstants.SCORING_DISTANCE_FIELD_MIN_METERS) ||
|
||||||
|
(distanceInMeters > Constants.ShooterConstants.SCORING_DISTANCE_FIELD_MAX_METERS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the quadratic coefficients for the hood angle. Return with the error value if none are found.
|
||||||
|
Constants.ShooterConstants.QuadraticCoef coefficients = Constants.ShooterConstants.PASSING_SPEED_COEFS_METERS.get(hoodAngleDeg);
|
||||||
|
if (coefficients == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the quadratic calculation using the coefficients we retrieved if successful
|
||||||
|
double x = distanceInMeters; // Rename to "x" just to make the next expression simpler
|
||||||
|
double launchSpeedFPS = (coefficients.a() * x * x) + (coefficients.b() * x) + coefficients.c();
|
||||||
|
|
||||||
|
return launchSpeedFPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the flywheel RPM that's required to achieve the provided
|
||||||
|
* launch speed in feet/second.
|
||||||
|
* This performs Equation 2-3 in the AutoShooter document.
|
||||||
|
* @param launchSpeedFPS the goal launch speed in ft/s
|
||||||
|
* @return the appropriate flywheel speed in RPM
|
||||||
|
*/
|
||||||
|
public static double GetShooterRPMForLaunchSpeed(double launchSpeedFPS){
|
||||||
|
double flywheelDiameterFeet = Constants.ShooterConstants.SHOOTER_FLYWHEEL_DIAMETER_IN / 12; // Change to feet to match launch speed units
|
||||||
|
double efficiency = Constants.ShooterConstants.SHOOTER_EFFICIENCY_GENERAL; // Use the appropriate efficiency
|
||||||
|
|
||||||
|
double shooterRPM = (launchSpeedFPS / (efficiency * flywheelDiameterFeet * Math.PI)) * 60; // Perform the calculation
|
||||||
|
return shooterRPM;
|
||||||
|
}
|
||||||
|
|
||||||
public static double getLauncherRPMForDistance(double distanceInMeters){
|
public static double getLauncherRPMForDistance(double distanceInMeters){
|
||||||
return Math.min((667.557*distanceInMeters) + 1500.699, 5000);
|
return Math.min((667.557*distanceInMeters) + 1500.699, 5000);
|
||||||
}
|
}
|
||||||
|
|
@ -117,8 +241,8 @@ public class Utils {
|
||||||
* Scoring hub, 2x passing from Neutral Zone, 2x passing from opponent's Alliance Zone,
|
* Scoring hub, 2x passing from Neutral Zone, 2x passing from opponent's Alliance Zone,
|
||||||
* and these exist for both red and blue alliances.
|
* and these exist for both red and blue alliances.
|
||||||
* Right/left are from driver's perspective.
|
* Right/left are from driver's perspective.
|
||||||
* @param region: the identifier of the robot's current field region
|
* @param region the identifier of the robot's current field region
|
||||||
* @param alliance: the red/blue alliance of the robot, as a sanity check
|
* @param alliance the red/blue alliance of the robot, as a sanity check
|
||||||
* @return the shooting target for a robot with the provided properties. Returns all-zero
|
* @return the shooting target for a robot with the provided properties. Returns all-zero
|
||||||
* Pose3d object if the region/alliance combination is invalid.
|
* Pose3d object if the region/alliance combination is invalid.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
package frc.robot.commands;
|
||||||
|
|
||||||
|
import org.littletonrobotics.junction.Logger;
|
||||||
|
|
||||||
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation2d;
|
||||||
|
import edu.wpi.first.math.geometry.Rotation3d;
|
||||||
|
import edu.wpi.first.math.geometry.Transform3d;
|
||||||
|
import edu.wpi.first.math.geometry.Translation2d;
|
||||||
|
import edu.wpi.first.math.geometry.Translation3d;
|
||||||
|
import edu.wpi.first.units.Units;
|
||||||
|
import edu.wpi.first.units.measure.Angle;
|
||||||
|
import edu.wpi.first.wpilibj.DriverStation;
|
||||||
|
import edu.wpi.first.wpilibj.DriverStation.Alliance;
|
||||||
|
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
|
||||||
|
import edu.wpi.first.wpilibj2.command.Command;
|
||||||
|
import edu.wpi.first.wpilibj2.command.CommandScheduler;
|
||||||
|
import edu.wpi.first.wpilibj2.command.Commands;
|
||||||
|
import frc.robot.Constants;
|
||||||
|
import frc.robot.Constants.ShootingRegion;
|
||||||
|
import frc.robot.Utils;
|
||||||
|
import frc.robot.subsystems.DriveSubsystem;
|
||||||
|
import frc.robot.subsystems.HopperSubsystem;
|
||||||
|
import frc.robot.subsystems.IntakeSubsystem;
|
||||||
|
import frc.robot.subsystems.TurretSubsystem;
|
||||||
|
|
||||||
|
|
||||||
|
public class UltraShooterCommand extends Command{
|
||||||
|
|
||||||
|
private HopperSubsystem hopperSubsystem;
|
||||||
|
private TurretSubsystem turretSubsystem;
|
||||||
|
private IntakeSubsystem intakeSubsystem; // TODO: is this necessary?
|
||||||
|
private DriveSubsystem driveSubsystem;
|
||||||
|
|
||||||
|
private Command waitCommand = Commands.waitSeconds(1.5);
|
||||||
|
private final Translation2d CENTER_TURRET_TO_ROBOT = new Translation2d(-0.1335024, -0.1824736); // Defines physical offset of turret from robot center
|
||||||
|
|
||||||
|
private Alliance alliance; // Shooting behavior is based on the robot's alliance
|
||||||
|
|
||||||
|
public UltraShooterCommand(HopperSubsystem hopperSubsystem, TurretSubsystem turretSubsystem, IntakeSubsystem intakeSubsystem, DriveSubsystem driveSubsystem){
|
||||||
|
this.hopperSubsystem = hopperSubsystem;
|
||||||
|
this.turretSubsystem = turretSubsystem;
|
||||||
|
this.intakeSubsystem = intakeSubsystem;
|
||||||
|
this.driveSubsystem = driveSubsystem;
|
||||||
|
|
||||||
|
addRequirements(hopperSubsystem, intakeSubsystem); // TODO: isintakeSubsystem necessary?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
hopperSubsystem.spindexerOn(); // Activate spindexer so it's on while command is active
|
||||||
|
hopperSubsystem.stopKicker(); // Make sure kicker is off when command is called
|
||||||
|
CommandScheduler.getInstance().schedule(waitCommand); // Kick off a wait timer so robot doesn't shoot until hopper is ready
|
||||||
|
alliance = DriverStation.getAlliance().get(); // Fetch our alliance
|
||||||
|
// Logger.recordOutput("AutoTurretShoot-State", "SHOOTING");
|
||||||
|
Logger.recordOutput("UltraShooter-State", "ACTIVE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
Boolean okToShoot = false; // Tracks whether conditions are OK for shooting.
|
||||||
|
Boolean scoring = false; // Are we shooting into the hub?
|
||||||
|
Pose3d botPose = driveSubsystem.getBotPose(); // Find the pose once since we'll use it a few times
|
||||||
|
|
||||||
|
// STEPS 1 & 2: Get the field location of the initial target
|
||||||
|
// First, find the robot's current field location.
|
||||||
|
ShootingRegion shootingRegion = Utils.findRobotShootingRegion(botPose, alliance);
|
||||||
|
|
||||||
|
// If robot is in a valid scoring or passing region, find the field pose for the appropriate target and perform calculations
|
||||||
|
if(shootingRegion != ShootingRegion.NON_SHOOTING_REGION){
|
||||||
|
|
||||||
|
okToShoot = true; // So far, so good
|
||||||
|
|
||||||
|
// Check whether we're scoring, since it require some extra steps
|
||||||
|
if ((shootingRegion == ShootingRegion.BLUE_OWN_ALLIANCE_ZONE) || (shootingRegion == ShootingRegion.RED_OWN_ALLIANCE_ZONE)){
|
||||||
|
scoring = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pose3d initialTargetPose = Utils.findShootingTarget(shootingRegion, alliance); // Get initial target based on robot location
|
||||||
|
Logger.recordOutput("UltraShooter-InitialTarget", initialTargetPose);
|
||||||
|
|
||||||
|
// STEPS 3 & 4: calculate hood and flywheel values based on distance to the intiial target.
|
||||||
|
// First, find the turret's pose based on the robot's pose and the location of the turret relative to the robot's center.
|
||||||
|
// Note that the turret points backward, so its rotational property is +0.5 rotations.
|
||||||
|
Pose3d turretPose = botPose.plus(new Transform3d(new Translation3d(CENTER_TURRET_TO_ROBOT),
|
||||||
|
new Rotation3d(Rotation2d.fromRotations(turretSubsystem.getTurretAngleRobotRelative() + 0.5))));
|
||||||
|
// Then calculate the linear distance between the initial target and the robot's turret.
|
||||||
|
double distanceToInitialTargetMeters = initialTargetPose.toPose2d().getTranslation().getDistance(turretPose.getTranslation().toTranslation2d());
|
||||||
|
Logger.recordOutput("UltraShooter-DistanceToInitialTarget(Meters)", distanceToInitialTargetMeters);
|
||||||
|
|
||||||
|
// STEP 3: Find the best hood angle
|
||||||
|
// Default to the passing hood angle if we're not shooting into a hub.
|
||||||
|
int initialHoodAngleDegrees = Constants.ShooterConstants.HOOD_ANGLE_PASSING_DEG;
|
||||||
|
// If we're scoring, run the calculations to find the best hood angle.
|
||||||
|
if (scoring) {
|
||||||
|
initialHoodAngleDegrees = Utils.getScoringHoodAngleDegForDistance(distanceToInitialTargetMeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 4: determine the right shooter flywheel speed
|
||||||
|
// First, find the correct launch speed in feet/second. Note the unit changes!
|
||||||
|
// This covers the scoring scenario then the passing scenario.
|
||||||
|
double intitialLaunchSpeedFPS = 0; // The target launch speed in feet per second (FPS)
|
||||||
|
if (scoring) {
|
||||||
|
intitialLaunchSpeedFPS = Utils.findLaunchSpeedScoring(initialHoodAngleDegrees, distanceToInitialTargetMeters);
|
||||||
|
} else {
|
||||||
|
intitialLaunchSpeedFPS = Utils.findLaunchSpeedPassing(initialHoodAngleDegrees, distanceToInitialTargetMeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle error condition in the launch speed calcs
|
||||||
|
if (intitialLaunchSpeedFPS == 0) {
|
||||||
|
okToShoot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 5: Find final target based on the intial target and the robot's velocity.
|
||||||
|
// THIS IS NOT IMPLEMENTED - SO ASSUME STATIONARY ROBOT.
|
||||||
|
Pose3d finalTargetPose = initialTargetPose;
|
||||||
|
double distanceToFinalTargetMeters = distanceToInitialTargetMeters;
|
||||||
|
Logger.recordOutput("UltraShooter-FinalTarget", finalTargetPose);
|
||||||
|
Logger.recordOutput("UltraShooter-DistanceToFinalTarget(Meters)", distanceToFinalTargetMeters);
|
||||||
|
|
||||||
|
// STEP 6: Find hood angle and launch speed for the final target.
|
||||||
|
// THIS IS NOT IMPLEMENTED - SO ASSUME STATIONARY ROBOT:
|
||||||
|
double finalLaunchSpeedFPS = intitialLaunchSpeedFPS;
|
||||||
|
int finalHoodAngleDegrees = initialHoodAngleDegrees;
|
||||||
|
|
||||||
|
// Confirm we're at a valid scoring distance for the robot's capabilities. If not, don't shoot.
|
||||||
|
if ((scoring) &&
|
||||||
|
((distanceToFinalTargetMeters > Constants.ShooterConstants.SCORING_DISTANCE_MAX_ALLOWED_METERS) ||
|
||||||
|
(distanceToFinalTargetMeters < Constants.ShooterConstants.SCORING_DISTANCE_MIN_ALLOWED_METERS))) {
|
||||||
|
okToShoot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 7: Find the flywheel RPM needed to achieve the launch speed we just calculated.
|
||||||
|
double finalShooterRPM = Utils.GetShooterRPMForLaunchSpeed(finalLaunchSpeedFPS);
|
||||||
|
|
||||||
|
// STEP 8: Command turretSubsystem to do what we need it to do for the launch speed and launch angle.
|
||||||
|
// Adjust for the shooter flywheel-to-motor gear ratio when setting the flywheel motor speed.
|
||||||
|
turretSubsystem.setHoodToLaunchAngle(finalHoodAngleDegrees);
|
||||||
|
turretSubsystem.setLauncher(finalShooterRPM / Constants.ShooterConstants.SHOOTER_FLYWHEEL_GEAR_RATIO);
|
||||||
|
|
||||||
|
// STEP 9: Command the turretSubsystem to aim at the intended field target (azimuth control).
|
||||||
|
// First, find the x,y translation between the field positions of the robot's turret and the final target.
|
||||||
|
Translation2d finalTargetTranslation = finalTargetPose.getTranslation().toTranslation2d().minus(turretPose.getTranslation().toTranslation2d());
|
||||||
|
|
||||||
|
// Find the rotations value, field-relative, between turret point and target point.
|
||||||
|
double turretToTargetRotation = finalTargetTranslation.getAngle().getRotations();
|
||||||
|
|
||||||
|
// Command the turret to get to this field-relative angle.
|
||||||
|
// Don't try to set the turret if it's outside of it's physical capability, and prevent shooting.
|
||||||
|
if(turretSubsystem.isTurretReady() && turretSubsystem.isTurretAnglePossible(botPose, finalTargetTranslation)) {
|
||||||
|
turretSubsystem.setTurretFieldRelative(botPose, turretToTargetRotation);
|
||||||
|
} else {
|
||||||
|
okToShoot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// If we're in a non-shooting region while the shooting function is active,
|
||||||
|
// set the hood to a low setting to get under the trench and prevent shooting.
|
||||||
|
turretSubsystem.setHoodToLaunchAngle(Constants.ShooterConstants.HOOD_ANGLE_PASSING_DEG);
|
||||||
|
okToShoot = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 10: Run the kicker if all the calculations went ok, the turret is pointed at the target, and the motors are ready
|
||||||
|
// Note we don't call turretSubsystem.isLauncherUpToSpeed() because it currently does nothing.
|
||||||
|
if (waitCommand.isFinished() && (okToShoot)) {
|
||||||
|
hopperSubsystem.feedKicker();
|
||||||
|
} else{
|
||||||
|
hopperSubsystem.stopKicker();
|
||||||
|
}
|
||||||
|
Logger.recordOutput("UltraShooter-OkToShoot", okToShoot);
|
||||||
|
|
||||||
|
// OPTIONAL: Activate rumble if it's not ok to shoot
|
||||||
|
if (!okToShoot) {
|
||||||
|
// ACTIVATE CONTROLLER RUMBLE
|
||||||
|
} else {
|
||||||
|
// DEACTIVATE CONTROLLER RUMBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end(boolean interrupted) {
|
||||||
|
waitCommand.cancel();
|
||||||
|
hopperSubsystem.spindexerOff();
|
||||||
|
hopperSubsystem.stopKicker();
|
||||||
|
|
||||||
|
turretSubsystem.setLauncher(0);
|
||||||
|
turretSubsystem.setTargetElevation(Constants.ShooterConstants.DEFLECTOR_STORED_ANGLE);
|
||||||
|
|
||||||
|
Logger.recordOutput("UltraShooter-State", "INACTIVE");
|
||||||
|
|
||||||
|
DriverStation.getAlliance().ifPresent((aliance) -> {
|
||||||
|
if (aliance == Alliance.Blue) {
|
||||||
|
turretSubsystem.setTurretTargetPose(Constants.FieldPoses.BLUE_HUB_POSE);
|
||||||
|
} else {
|
||||||
|
turretSubsystem.setTurretTargetPose(Constants.FieldPoses.RED_HUB_POSE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// turretSubsystem.setTurretMotorRotation(-0.34); // Return to 0 after MIAMI VALLEY
|
||||||
|
// TODO: Deactivate rumble if we decided to use the rumble feature when the robot prevents shooting
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -38,6 +38,7 @@ import frc.robot.Constants;
|
||||||
import frc.robot.Robot;
|
import frc.robot.Robot;
|
||||||
import edu.wpi.first.math.geometry.Pose3d;
|
import edu.wpi.first.math.geometry.Pose3d;
|
||||||
import edu.wpi.first.math.geometry.Rotation2d;
|
import edu.wpi.first.math.geometry.Rotation2d;
|
||||||
|
import edu.wpi.first.math.geometry.Translation2d;
|
||||||
import edu.wpi.first.units.AngleUnit;
|
import edu.wpi.first.units.AngleUnit;
|
||||||
import edu.wpi.first.units.Units;
|
import edu.wpi.first.units.Units;
|
||||||
import edu.wpi.first.units.measure.Angle;
|
import edu.wpi.first.units.measure.Angle;
|
||||||
|
|
@ -130,6 +131,14 @@ public class TurretSubsystem extends TestableSubsystem {
|
||||||
turretState = TurretState.INIT;
|
turretState = TurretState.INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the launcher motor controller (shooter flywheel motor) to achieve
|
||||||
|
* the provided motor speed (in RPM) using the Spark controller's velocity control mode.
|
||||||
|
* The resulting shooter flywheel speed is twice the motor speed.
|
||||||
|
* @param speed the commanded motor speed in RPM
|
||||||
|
* @param distanceInMeters distance between turret and target, in meters
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
public void setLauncher(double speed) {
|
public void setLauncher(double speed) {
|
||||||
if(speed == 0)
|
if(speed == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -146,6 +155,12 @@ public class TurretSubsystem extends TestableSubsystem {
|
||||||
// return Math.abs(launcherControler.getSetpoint() - launcherMotor.getEncoder().getVelocity()) >= launcherControler.getSetpoint() * 0.03;
|
// return Math.abs(launcherControler.getSetpoint() - launcherMotor.getEncoder().getVelocity()) >= launcherControler.getSetpoint() * 0.03;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hood angle when the target elevation servo position is known,
|
||||||
|
* from zero (lowest hood angle) to 300 (highest hood angle).
|
||||||
|
* @param elev the target servo motor position, from 0 to 300
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
public void setTargetElevation(double elev)
|
public void setTargetElevation(double elev)
|
||||||
{
|
{
|
||||||
double pos = Math.abs((elev - 300)/300);
|
double pos = Math.abs((elev - 300)/300);
|
||||||
|
|
@ -153,6 +168,31 @@ public class TurretSubsystem extends TestableSubsystem {
|
||||||
hood.set(pos);
|
hood.set(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the launch (hood) angle when the target angle is known, in degrees.
|
||||||
|
* @param angleDegrees the commanded motor speed in RPM
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
|
public void setHoodToLaunchAngle(int angleDegrees)
|
||||||
|
{
|
||||||
|
// Confirm target is within expected range. If not, do nothing.
|
||||||
|
if ((angleDegrees < Constants.ShooterConstants.HOOD_ANGLE_MIN_DEG) ||
|
||||||
|
(angleDegrees > Constants.ShooterConstants.HOOD_ANGLE_MAX_DEG)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab min and max values to simplify the expressions and ensure precision
|
||||||
|
double minAngle = (double) Constants.ShooterConstants.HOOD_ANGLE_MIN_DEG;
|
||||||
|
double maxAngle = (double) Constants.ShooterConstants.HOOD_ANGLE_MAX_DEG;
|
||||||
|
|
||||||
|
// Map so that min angle maps to "1" and max angle maps to "0."
|
||||||
|
double pos = (maxAngle - angleDegrees) / (maxAngle - minAngle);
|
||||||
|
|
||||||
|
Logger.recordOutput("ShooterHood-Angle", pos);
|
||||||
|
hood.set(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public Command revSpeedCommand()
|
public Command revSpeedCommand()
|
||||||
{
|
{
|
||||||
return this.runOnce(() -> setLauncher(-.5));
|
return this.runOnce(() -> setLauncher(-.5));
|
||||||
|
|
@ -246,6 +286,13 @@ public class TurretSubsystem extends TestableSubsystem {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commands turret to point in a specific field direction given
|
||||||
|
* the current heading of the robot.
|
||||||
|
* @param robotPose the robot's field-relative pose
|
||||||
|
* @param pos the field-relative rotations of angle to the target
|
||||||
|
* @return nothing
|
||||||
|
*/
|
||||||
public void setTurretFieldRelative(Pose3d robotPose, double pos)
|
public void setTurretFieldRelative(Pose3d robotPose, double pos)
|
||||||
{
|
{
|
||||||
if(turretState != TurretState.READY)
|
if(turretState != TurretState.READY)
|
||||||
|
|
@ -305,6 +352,24 @@ public class TurretSubsystem extends TestableSubsystem {
|
||||||
return Math.abs(turretPositionError.refresh().getValueAsDouble()) <= 0.1;
|
return Math.abs(turretPositionError.refresh().getValueAsDouble()) <= 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the turret can physically achieve the angle to the
|
||||||
|
* target based on the robot's heading.
|
||||||
|
* Uses Rotation2d to simplify the math.
|
||||||
|
* Assumes turret rotational midpoint is 180° opposite robot's heading.
|
||||||
|
* @param botPose the robot's field-relative pose
|
||||||
|
* @param targetTranslation the translation from the turret to the intended target
|
||||||
|
* @return true if angle is achieveable
|
||||||
|
*/
|
||||||
|
public boolean isTurretAnglePossible(Pose3d botPose, Translation2d targetTranslation){
|
||||||
|
Rotation2d robotHeading = botPose.getRotation().toRotation2d(); // Robot's field-relative heading (forward)
|
||||||
|
Rotation2d turretCenterPos = robotHeading.plus(Rotation2d.fromRotations(0.5)); // Turret's neutral azimuth
|
||||||
|
Rotation2d targetDirection = targetTranslation.getAngle(); // Field-relative aiming angle
|
||||||
|
Rotation2d turretOffset = targetDirection.minus(turretCenterPos); // Difference between the two
|
||||||
|
|
||||||
|
return (Math.abs(turretOffset.getRotations()) <= (Constants.ShooterConstants.TURRET_RANGE_ROTATIONS / 2));
|
||||||
|
}
|
||||||
|
|
||||||
private double getRequestedPosTurretRelative() {
|
private double getRequestedPosTurretRelative() {
|
||||||
return (turretControlMode.getPositionMeasure().in(Units.Rotations) - turretOffsetInRotations) / Constants.ShooterConstants.TURRET_GEAR_RATIO;
|
return (turretControlMode.getPositionMeasure().in(Units.Rotations) - turretOffsetInRotations) / Constants.ShooterConstants.TURRET_GEAR_RATIO;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue