Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/main/java/frc/robot/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public static class Drive {

public static final double TARGET_TRANSLATION_RADIUS = 2.0;

public static final double SHOOTING_SPEED_SCALE = 0.5; // Scale factor applied to drive speed while shooting

}

public static class Misc {
Expand Down Expand Up @@ -407,18 +409,23 @@ public static class Turret {
public static final double TURN_TABLE_RATIO = 24.0 / 200.0;
public static final double ENCODER_FACTOR = (TURRET_GEAR_REDUCTION) / (2.0 * Math.PI * TURN_TABLE_RATIO);

public static final double STOW_PUSH_DOWN_SPEED = -0.3; // percent of max speed
public static final double STOW_PUSH_DOWN_TIME = 0.5; // seconds
public static final double STOW_PUSH_DOWN_SPEED = 0.6; // percent of max speed
public static final double STOW_PUSH_DOWN_TIME = 0.1; // seconds
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STOW_PUSH_DOWN_TIME is still defined but is no longer used by getStowCommand(). Either remove the constant or reintroduce a timeout/safety bound that uses it (especially if the velocity-based settle condition could fail due to encoder noise).

Suggested change
public static final double STOW_PUSH_DOWN_TIME = 0.1; // seconds

Copilot uses AI. Check for mistakes.

public static final double PITCH_GEAR_RATIO = (26.0 / 447.2);
public static final double PITCH_ENCODER_FACTOR = PITCH_GEAR_RATIO * (2.0 * Math.PI);

public static final boolean PITCH_INVERTED = true;

public static final Rotation2d MAX_PITCH = Rotation2d.fromDegrees(45.0);
public static final Rotation2d MIN_PITCH = Rotation2d.fromDegrees(3.00);
public static final Rotation2d HOOD_START_POSITION = Rotation2d.fromDegrees(5.00);
public static final Rotation2d HOOD_STOW_POSITION = Rotation2d.fromDegrees(3.00);
public static final Rotation2d MIN_PITCH = Rotation2d.fromDegrees(8.00);
public static final Rotation2d HOOD_START_POSITION = Rotation2d.fromDegrees(8.00);

public static final double HOOD_STABLE_TIME = 0.1;
public static final double HOOD_STABLE = 5;
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOOD_STABLE is newly added but not referenced anywhere. If it was intended as a stability threshold/count, wire it into the stow/settle logic; otherwise remove it to avoid stale/unused configuration values.

Suggested change
public static final double HOOD_STABLE = 5;

Copilot uses AI. Check for mistakes.

public static final double HOOD_FINISH_VELOCITY = 0.1;
public static final Rotation2d HOOD_FAR_ANGLE = Rotation2d.fromDegrees(-50.0);

public static final double OUTPUT_RANGE_MAX = 1;
public static final double OUTPUT_RANGE_MIN = -1;
Expand Down Expand Up @@ -521,6 +528,7 @@ public static class Transfer {
public static final boolean FEEDER_INVERTED = true;


public static final double LOAD_DELAY = 0.1; //Time between shooter and transfer being triggered, shooter should always activate first
Comment on lines 530 to +531
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOAD_DELAY is newly added but not referenced anywhere in the codebase. If the intent is to enforce a shooter-before-transfer delay, incorporate this into the relevant command scheduling logic; otherwise remove the unused constant.

Suggested change
public static final double LOAD_DELAY = 0.1; //Time between shooter and transfer being triggered, shooter should always activate first

Copilot uses AI. Check for mistakes.
public static final double KICKER_LOAD_POWER = 0.25;
public static final double FEEDER_LOAD_POWER = 0.5;
}
Expand Down
32 changes: 28 additions & 4 deletions src/main/java/frc/robot/RobotContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,26 @@ public void updateFieldObjects() {

public void configureBindings() {

new Trigger(
() -> operatorController.getAButton()).onTrue(
new InstantCommand(() -> selectedFixedTarget = FixedTarget.A));

new Trigger(
() -> operatorController.getBButton()).onTrue(
new InstantCommand(() -> selectedFixedTarget = FixedTarget.B));

new Trigger(
() -> operatorController.getXButton()).onTrue(
new InstantCommand(() -> xHeld = true))
.onFalse(
new InstantCommand(() -> xHeld = false));

new Trigger(
() -> operatorController.getYButton()).onTrue(
new InstantCommand(() -> yHeld = true))
.onFalse(
new InstantCommand(() -> yHeld = false));

Comment on lines +270 to +289
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configureBindings() now registers A/B/X/Y button triggers near the top of the method, but the file already registers A/B/X triggers again later in the same method. This duplication schedules redundant InstantCommands and makes the binding logic harder to maintain; consolidate these into a single set of bindings (and keep Y consistent as well).

Suggested change
new Trigger(
() -> operatorController.getAButton()).onTrue(
new InstantCommand(() -> selectedFixedTarget = FixedTarget.A));
new Trigger(
() -> operatorController.getBButton()).onTrue(
new InstantCommand(() -> selectedFixedTarget = FixedTarget.B));
new Trigger(
() -> operatorController.getXButton()).onTrue(
new InstantCommand(() -> xHeld = true))
.onFalse(
new InstantCommand(() -> xHeld = false));
new Trigger(
() -> operatorController.getYButton()).onTrue(
new InstantCommand(() -> yHeld = true))
.onFalse(
new InstantCommand(() -> yHeld = false));

Copilot uses AI. Check for mistakes.
targetingSupplier = () -> {
Translation2d robotPosition = driveSubsystem.getPose().getTranslation();

Expand Down Expand Up @@ -379,6 +399,13 @@ public void configureBindings() {
driveSubsystem::getPose,
driveSubsystem::getVelocity));

/* Turret runs while button held */
shootTrigger.whileTrue(
turretSubsystem.getTargetCommand(
targetingSupplier,
driveSubsystem::getPose,
driveSubsystem::getVelocity));

/* Transfer runs ONLY while button AND solver valid */
shootTrigger.whileTrue(
transferSubsystem.getLoadCommand());
Expand Down Expand Up @@ -539,10 +566,7 @@ public void scheduleTeleOp() {
manualRotationVelocityDirective, null, null);
driveSubsystem.setDefaultCommand(manualDriveCommand);

turretSubsystem.setDefaultCommand(turretSubsystem.getTargetCommand(
targetingSupplier,
driveSubsystem::getPose,
driveSubsystem::getVelocity));
turretSubsystem.setDefaultCommand(turretSubsystem.getStowCommand());

}

Expand Down
19 changes: 19 additions & 0 deletions src/main/java/frc/robot/subsystems/DriveSubsystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public class DriveSubsystem extends SubsystemBase {
// Feature Flags
private boolean useVisionOdometry = true;

// Speed scaling (e.g. 0.5 to limit to 50% while shooting)
private Supplier<Double> speedScaleSupplier = () -> 1.0;

Comment on lines +61 to +63
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The speed-scale feature is currently not wired up anywhere (no calls to setSpeedScaleSupplier in the codebase), so speedScaleSupplier will always be 1.0 and this change has no effect. Either connect it from RobotContainer (e.g., scale while shootTrigger is active using Constants.Operator.Drive.SHOOTING_SPEED_SCALE) or remove the unused API/constant.

Suggested change
// Speed scaling (e.g. 0.5 to limit to 50% while shooting)
private Supplier<Double> speedScaleSupplier = () -> 1.0;

Copilot uses AI. Check for mistakes.
public DriveSubsystem() {

SwerveDriveTelemetry.verbosity = TelemetryVerbosity.HIGH;
Expand Down Expand Up @@ -190,6 +193,16 @@ public void removeObstaclesSupplier(Supplier<List<Obstacle>> supplier) {
obstaclesSuppliers.remove(supplier);
}

/**
* Sets a supplier that returns a speed scale factor applied to all drive output.
* A value of 1.0 is full speed; 0.5 limits the robot to 50% of normal speed.
*
* @param supplier Supplier returning a scale factor in the range [0.0, 1.0].
*/
public void setSpeedScaleSupplier(Supplier<Double> supplier) {
this.speedScaleSupplier = (supplier != null) ? supplier : () -> 1.0;
}

// ------------------------------
// POSE ACCESS
// ------------------------------
Expand Down Expand Up @@ -300,6 +313,12 @@ public void driveWithCompositeRequests(TranslationRequest tReq, RotationRequest
);
}

// Apply speed scale (e.g. 50% while shooting)
double scale = MathUtil.clamp(speedScaleSupplier.get(), 0.0, 1.0);
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MathUtil.clamp(...) does not guard against NaN/Infinity; if the supplier ever returns a non-finite value, the chassis speeds become non-finite and could propagate into the swerve drive. Consider sanitizing the supplier output (e.g., treat non-finite as 1.0 or 0.0) before applying it.

Suggested change
double scale = MathUtil.clamp(speedScaleSupplier.get(), 0.0, 1.0);
double rawScale = speedScaleSupplier.get();
double scale = MathUtil.clamp(Double.isFinite(rawScale) ? rawScale : 1.0, 0.0, 1.0);

Copilot uses AI. Check for mistakes.
chassisSpeeds.vxMetersPerSecond *= scale;
chassisSpeeds.vyMetersPerSecond *= scale;
chassisSpeeds.omegaRadiansPerSecond *= scale;

// --- Drive: field-oriented unless robot-relative velocity ---
if (tReq instanceof TranslationRequest.Velocity vel && !vel.fieldRelative()) {
swerveDrive.drive(chassisSpeeds); // robot-relative velocity
Expand Down
65 changes: 42 additions & 23 deletions src/main/java/frc/robot/subsystems/TurretSubsystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import edu.wpi.first.math.kinematics.ChassisSpeeds;
import edu.wpi.first.wpilibj.DriverStation;
import edu.wpi.first.wpilibj.PWM;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
import edu.wpi.first.wpilibj2.command.Command;
import edu.wpi.first.wpilibj2.command.Commands;
Expand Down Expand Up @@ -194,6 +195,10 @@ public Rotation2d getHoodAngle() {
- hoodOffsetSupplier.get().getRadians());
}

public double getHoodVelocity() {
return pitchEncoder.getVelocity().getValueAsDouble() * Constants.Turret.PITCH_ENCODER_FACTOR;
}

/**
* @return the current turret yaw in the field frame
*/
Expand Down Expand Up @@ -336,36 +341,50 @@ public Command getAngleCommand(

public Command getStowCommand() {

Command moveToTarget = new FunctionalCommand(
() -> SmartDashboard.putBoolean("Turret/IsStowing", true),
final Timer settleTimer = new Timer();
double stopThreshold = Math.max(1e-3, Math.abs(Constants.Turret.HOOD_FINISH_VELOCITY)); // rad/s
double stableTime = Constants.Turret.HOOD_STABLE_TIME; // seconds, or a separate constant

Command settleDown = Commands.run(
() -> {
setHoodAngle(Constants.Turret.HOOD_STOW_POSITION);
},

interrupted -> {
pitchServo.setSpeed((Constants.Turret.PITCH_INVERTED ? -1.0 : 1.0) * -Constants.Turret.STOW_PUSH_DOWN_SPEED);
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STOW_PUSH_DOWN_SPEED is defined as a positive magnitude, but it is negated at the callsite and then conditionally inverted again via PITCH_INVERTED. This makes the sign convention hard to reason about and easy to break if PITCH_INVERTED changes. Prefer keeping STOW_PUSH_DOWN_SPEED in the motor/servo's native sign convention and remove the extra negation here (or rename it to STOW_PUSH_DOWN_MAGNITUDE and handle sign in one place).

Suggested change
pitchServo.setSpeed((Constants.Turret.PITCH_INVERTED ? -1.0 : 1.0) * -Constants.Turret.STOW_PUSH_DOWN_SPEED);
pitchServo.setSpeed((Constants.Turret.PITCH_INVERTED ? 1.0 : -1.0) * Constants.Turret.STOW_PUSH_DOWN_SPEED);

Copilot uses AI. Check for mistakes.
SmartDashboard.putBoolean("Hood Pushing", true);
},
Comment on lines +348 to 352
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settleDown drives the hood servo open-loop but never disables hoodClosedLoopActive; periodic() will keep writing PID output while closed-loop is active, which can override/fight this command. Consider explicitly disabling closed-loop at the start of stow (e.g., call stopHoodServo() / set hoodClosedLoopActive = false) before applying the constant push-down speed.

Copilot uses AI. Check for mistakes.

() -> Math.abs(
getHoodAngle()
.minus(Constants.Turret.HOOD_STOW_POSITION)
.getRadians()) < Constants.Turret.HOOD_TOLERANCE.getRadians(),

this);

Command settleDown = new RunCommand(
() -> pitchServo.setSpeed(
(Constants.Turret.PITCH_INVERTED ? -1.0 : 1.0) * Constants.Turret.STOW_PUSH_DOWN_SPEED), // small
// constant
// downward
// speed
this).withTimeout(Constants.Turret.STOW_PUSH_DOWN_TIME); // enough to seat the gear

this)
// stop when encoder velocity magnitude <= threshold
.until(() -> {
boolean within = Math.abs(getHoodVelocity()) <= stopThreshold;

if (within) {
if (!settleTimer.isRunning()) {
settleTimer.reset();
settleTimer.start();
}
//Finish when considered stable
return settleTimer.hasElapsed(stableTime);
} else {
settleTimer.stop();
settleTimer.reset();
return false;
}
}
).until(
() -> getHoodAngle().getRadians() < Constants.Turret.HOOD_FAR_ANGLE.getRadians()
)
// ensure the servo is stopped when this command completes
.andThen(() -> {
pitchServo.setSpeed(0.0);
settleTimer.stop();
settleTimer.reset();
}
);
Comment on lines +374 to +380
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The servo stop/reset logic is in an .andThen(...), which only runs on normal completion. If getStowCommand() is interrupted (e.g., by a targeting command), the hood servo can be left running at the last push-down speed. Use a command form that defines an end(interrupted)/finallyDo to always stop the servo and reset/stop the timer on both completion and interruption.

Suggested change
// ensure the servo is stopped when this command completes
.andThen(() -> {
pitchServo.setSpeed(0.0);
settleTimer.stop();
settleTimer.reset();
}
);
// ensure the servo and timer are cleaned up on both completion and interruption
.finallyDo(() -> {
pitchServo.setSpeed(0.0);
settleTimer.stop();
settleTimer.reset();
});

Copilot uses AI. Check for mistakes.
Command finish = new InstantCommand(() -> {
SmartDashboard.putBoolean("Hood Pushing", false);
pitchServo.setSpeed(0.0);
resetHoodAngle(Constants.Turret.HOOD_START_POSITION);
});

return Commands.sequence(moveToTarget, settleDown, finish, Commands.idle())
return Commands.sequence(settleDown, finish, Commands.idle())
.withName("StowHood");
}

Expand Down
Loading