Skip to content

Adds adjustable hood as a subsystem and its elastic properties - #121

Open
NRG948DriverStation wants to merge 3 commits into
mainfrom
Programming/AdjustableHood
Open

Adds adjustable hood as a subsystem and its elastic properties#121
NRG948DriverStation wants to merge 3 commits into
mainfrom
Programming/AdjustableHood

Conversation

@NRG948DriverStation

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new AdjustableHood manipulator subsystem and wires it into the robot’s subsystem container and the “Pass” shooting routine, along with Elastic dashboard configuration for testing.

Changes:

  • Introduces AdjustableHood subsystem with dashboard widgets/commands and basic angle control loop.
  • Registers the hood in Subsystems and includes it in the manipulators list.
  • Updates ShootingCommands.pass() to set the hood to a passing angle and restore to stowed at the end; adds Elastic Testing tab layout.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/main/java/frc/robot/subsystems/Subsystems.java Adds hood instance and includes it in the manipulators collection.
src/main/java/frc/robot/subsystems/AdjustableHood.java New hood subsystem implementation (motor, telemetry, dashboard bindings, control loop).
src/main/java/frc/robot/Constants.java Adds CAN ID constant for adjustable hood motor.
src/main/java/frc/robot/commands/ShootingCommands.java Integrates hood behavior into the pass command.
src/main/deploy/elastic-testing.json Adds Elastic dashboard tab/widgets for Adjustable Hood testing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

public static final int SHOOTER_UPPER_RIGHT_ID = 18;
public static final int SHOOTER_LOWER_LEFT_ID = 17;
public static final int SHOOTER_UPPER_LEFT_ID = 19;
public static final int ADJUSTABLE_HOOD_ID = 99; // TODO: Change ID

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

ADJUSTABLE_HOOD_ID is set to 99, which is outside the valid FRC CAN device ID range (0–63) used by CTRE devices. This will prevent the TalonFX from being constructed/communicated with; update this to a real, in-range ID (and ensure it doesn’t collide with existing IDs).

Suggested change
public static final int ADJUSTABLE_HOOD_ID = 99; // TODO: Change ID
public static final int ADJUSTABLE_HOOD_ID = 20;

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +76
private static final MotorParameters MOTOR_PARAMS =
RobotPreferences.ROBOT_TYPE.selectOrDefault(
Map.of(
RobotSelector.CompetitionRobot2026, MotorParameters.KrakenX60,
RobotSelector.PracticeRobot2026, MotorParameters.NullMotor),
MotorParameters.NullMotor);

private static final double TOLERANCE = Units.degreesToRadians(2.0);
private static final double STALL_VOLTAGE = 0.1; // TODO: Change values
private static final double STALL_VELOCITY = Units.degreesToRadians(1);

private static final double GEAR_RATIO = 25;
private static final double RADIANS_PER_ROTATION = (2 * Math.PI) / GEAR_RATIO;
private static final double MAX_VELOCITY =
(RADIANS_PER_ROTATION * (MOTOR_PARAMS.getFreeSpeedRPM()) / 60.0);

public static final double STOW_ANGLE = Units.degreesToRadians(75); // TODO: Change angles
public static final double PASSING_ANGLE = Units.degreesToRadians(50);
public static final double MIN_ANGLE = PASSING_ANGLE; // TODO: Change angles
public static final double MAX_ANGLE = STOW_ANGLE;

public static final double KS = MOTOR_PARAMS.getKs();
public static final double KV = (MAX_BATTERY_VOLTAGE - KS) / MAX_VELOCITY;
private final SimpleMotorFeedforward feedforwardController;

private final TalonFX talonFX = new TalonFX(ADJUSTABLE_HOOD_ID);
// TODO: Determine direction
private final TalonFXAdapter motor =
new TalonFXAdapter(
"/AdjustableHood/Motor", talonFX, CLOCKWISE_POSITIVE, BRAKE, RADIANS_PER_ROTATION);

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

This subsystem selects MotorParameters.NullMotor for some robot types, but still unconditionally instantiates a TalonFX and TalonFXAdapter. That defeats the purpose of NullMotor selection and can cause CAN timeouts/log spam (or runtime issues) on robots without the hood hardware. Consider constructing the motor via MOTOR_PARAMS.newController(...) and using the MotorController interface (or remove the NullMotor selection if the hood is always present).

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +252
double feedforward = feedforwardController.calculate(goalAngle);
double feedback = pidController.calculate(currentAngle, goalAngle);
double voltage = feedforward + feedback;

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

SimpleMotorFeedforward.calculate(...) expects a velocity setpoint (and optional acceleration), but this code passes goalAngle (a position in radians). This is a units/logic bug and will generate incorrect voltages. If you’re doing position control, either (a) use an appropriate position/arm feedforward model (e.g., ArmFeedforward with kG term for gravity) and calculate based on angle/velocity, or (b) remove the feedforward and rely on PID until a correct feedforward model is implemented.

Suggested change
double feedforward = feedforwardController.calculate(goalAngle);
double feedback = pidController.calculate(currentAngle, goalAngle);
double voltage = feedforward + feedback;
double voltage = pidController.calculate(currentAngle, goalAngle);

Copilot uses AI. Check for mistakes.
Comment on lines +253 to +257
// Check if the mechanism is stalled
if (Math.abs(voltage) > STALL_VOLTAGE && Math.abs(encoder.getVelocity()) < STALL_VELOCITY) {
voltage = 0;
}
motor.setVoltage(voltage);

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

The stall detection will zero the commanded voltage whenever |encoder velocity| is below the threshold, which is true at the start of any move. Since voltage is then forced to 0, the mechanism may never begin moving. Stall detection usually needs a time component (e.g., only after N ms of low velocity while commanding significant voltage) and/or current-based detection, rather than a single-cycle check.

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +160
private void resetHoodAngle(double angleRadians) {
encoder.setPosition(angleRadians);
goalAngle = angleRadians;
logCurrentAngle.append(currentAngle);
logGoalAngle.append(goalAngle);
}

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

resetHoodAngle() appends currentAngle to the log but never updates currentAngle to match the encoder position it just set, so initial telemetry/logging will be wrong until the next periodic update. Set currentAngle = angleRadians (or read back encoder position) before logging, and consider also appending current/goal angle from updateTelemetry() if these logs are intended to be time-series telemetry.

Copilot uses AI. Check for mistakes.
}

/**
* {@return the current intake arm angle in degrees} The angle is relative to horizontal with

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

These Javadocs appear to be copy/pasted from IntakeArm (e.g., references to “intake arm” and “degress”), which makes the public API misleading. Please update the descriptions/return text to refer to the hood and correct the spelling (“degrees”).

Suggested change
* {@return the current intake arm angle in degrees} The angle is relative to horizontal with
* {@return the current hood angle in degrees} The angle is relative to horizontal with

Copilot uses AI. Check for mistakes.
AdjustableHood hood = subsystems.hood;

return Commands.parallel(
Commands.runOnce(hood::setPassingAngle),

Copilot AI Apr 3, 2026

Copy link

Choose a reason for hiding this comment

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

Commands.runOnce(hood::setPassingAngle) is created without requiring the hood subsystem, so the scheduler won’t prevent other hood commands from running concurrently and stomping the goal state. Consider using Commands.runOnce(hood::setPassingAngle, hood) (and similarly ensure the stow action in finallyDo can’t conflict) so hood ownership is explicit.

Suggested change
Commands.runOnce(hood::setPassingAngle),
Commands.runOnce(hood::setPassingAngle, hood),

Copilot uses AI. Check for mistakes.
@edreed

edreed commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

I wonder if we should re-use the IntakeArm implementation here; share an implementation like, the intake and hopper rollers do now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants