Skip to content

Conversation

@RobototesProgrammers
Copy link
Contributor

No description provided.

LiveWindow.disableAllTelemetry();
LiveWindow.enableTelemetry(PDH);
mech = new Mechanism2d(1, 2);
root = mech.getRoot("climber", 0.5 + Units.inchesToMeters(5.5), Units.inchesToMeters(19.5));
Copy link
Member

Choose a reason for hiding this comment

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

? Not sure what the "climber" is, as this year's robot isn't supposed to climb.

Copy link
Contributor

Choose a reason for hiding this comment

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

Funnel pivot?

root = mech.getRoot("climber", 0.5 + Units.inchesToMeters(5.5), Units.inchesToMeters(19.5));
SmartDashboard.putData("Mechanism", mech);
m_elevator =
root.append(new MechanismLigament2d("elevator", 1, 90, 2, new Color8Bit(Color.kRed)));
Copy link
Member

Choose a reason for hiding this comment

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

Worth a comment that this is a single-stage elevator even though the robot actually uses a multi-stage elevator.

public static final double CORAL_QUICK_INTAKE = 1.6;
public static final double MIN_EMPTY_GROUND_INTAKE = 4.5;
public static final double MIN_FULL_GROUND_INTAKE = 8.0;
private static final double MOTOR_ROTATIONS_PER_METER = 40; // Inaccurate
Copy link
Member

@jamesdooley4 jamesdooley4 Sep 30, 2025

Choose a reason for hiding this comment

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

Should be able to make this accurate by measuring how far the elevator extends (in meters) at CORAL_LEVEL_FOUR_PRE_POS and then simply change this to 37.5 / <actual relative elevator extension>, where <actual relative elevator extension> is what's measured with a tape measure.

Comment on lines +50 to +53
Mechanism2d mech;
MechanismRoot2d root;
MechanismLigament2d m_elevator;
MechanismLigament2d m_wrist;
Copy link
Member

Choose a reason for hiding this comment

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

Would be nicer to encapsulate the Mechanism (and similar "report physical position") code in a different class instead of adding it to the main Robot class. These are generally called 'digital twins', so could use something like DigitalTwin.java and have robotPeriodic() simply call an update() function on it.

m_motorOneSimState.setRawRotorPosition(targetPos);
m_motorTwoSimState.setRawRotorPosition(targetPos);

elevatorPose3d.set(new Pose3d(new Pose2d(0.0, getHeightMeters(), new Rotation2d(0.0,0.0))));
Copy link
Member

Choose a reason for hiding this comment

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

When running this in the simulator, it's continually logging this error here:

Error at java.base/java.lang.Thread.getStackTrace(Thread.java:1619): x and y components of Rotation2d are zero

        at java.base/java.lang.Thread.getStackTrace(Thread.java:1619)
        at edu.wpi.first.math.geometry.Rotation2d.<init>(Rotation2d.java:126)
        at frc.robot.subsystems.ElevatorSubsystem.periodic(ElevatorSubsystem.java:388)
        at edu.wpi.first.wpilibj2.command.CommandScheduler.run(CommandScheduler.java:269)
        at frc.robot.Robot.robotPeriodic(Robot.java:137)
        at edu.wpi.first.wpilibj.IterativeRobotBase.loopFunc(IterativeRobotBase.java:400)
        at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:148)
        at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:419)
        at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$1(RobotBase.java:490)
        at java.base/java.lang.Thread.run(Thread.java:840)

markpete
markpete previously approved these changes Oct 24, 2025
@markpete
Copy link

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a Mechanism2d visualization for the robot's arm and elevator, and refactors the AdvantageScope/Elastic layout for better organization. The changes also include updates to the auto-alignment and scoring logic, particularly for the solo controller.

My review focuses on improving code maintainability and fixing a few issues in the new simulation code. I've suggested refactoring some duplicated methods in Controls.java and replacing magic numbers with constants. There are also several minor cleanup items in ElevatorSubsystem.java, along with a critical bug in the Rotation3d usage for the simulation visualization that should be addressed.

Comment on lines +424 to +426
TESTpose.set(new Pose3d(
0.2, 0.0, targetZ,
new Rotation3d(0.0, 0.0, -135)));

Choose a reason for hiding this comment

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

critical

The Rotation3d constructor takes radians, but is being passed -135. This is likely intended to be degrees and should be converted using Units.degreesToRadians(-135). Using -135 directly results in a very large and incorrect rotation in radians.

Suggested change
TESTpose.set(new Pose3d(
0.2, 0.0, targetZ,
new Rotation3d(0.0, 0.0, -135)));
TESTpose.set(new Pose3d(
0.2, 0.0, targetZ,
new Rotation3d(0.0, 0.0, edu.wpi.first.math.util.Units.degreesToRadians(-135))));

Comment on lines +463 to 495
private Command getSoloCoralBranchHeightCommandL() {
return switch (branchHeight) {
case CORAL_LEVEL_FOUR -> superStructure
.coralLevelFour(soloController.rightBumper())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_THREE -> superStructure
.coralLevelThree(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_TWO -> superStructure
.coralLevelTwo(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_ONE -> superStructure
.coralLevelOne(soloController.rightBumper(), () -> AutoAlign.poseInPlaceL1L())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
};
}

private Command getSoloCoralBranchHeightCommandR() {
return switch (branchHeight) {
case CORAL_LEVEL_FOUR -> superStructure
.coralLevelFour(soloController.rightBumper())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_THREE -> superStructure
.coralLevelThree(soloController.rightBumper())
.coralLevelThree(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_TWO -> superStructure
.coralLevelTwo(soloController.rightBumper())
.coralLevelTwo(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_ONE -> superStructure
.coralLevelOne(soloController.rightBumper())
.coralLevelOne(soloController.rightBumper(), () -> AutoAlign.poseInPlaceL1R())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
};
}

Choose a reason for hiding this comment

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

medium

The methods getSoloCoralBranchHeightCommandL and getSoloCoralBranchHeightCommandR are nearly identical, with the only difference being the BooleanSupplier passed to superStructure.coralLevelOne. You can refactor them into a single method that accepts the BooleanSupplier as a parameter. This reduces code duplication and improves maintainability.

You can then call this new method from configureSoloControllerBindings like getSoloCoralBranchHeightCommand(() -> AutoAlign.poseInPlaceL1L()) and getSoloCoralBranchHeightCommand(() -> AutoAlign.poseInPlaceL1R()).

Suggested change
private Command getSoloCoralBranchHeightCommandL() {
return switch (branchHeight) {
case CORAL_LEVEL_FOUR -> superStructure
.coralLevelFour(soloController.rightBumper())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_THREE -> superStructure
.coralLevelThree(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_TWO -> superStructure
.coralLevelTwo(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_ONE -> superStructure
.coralLevelOne(soloController.rightBumper(), () -> AutoAlign.poseInPlaceL1L())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
};
}
private Command getSoloCoralBranchHeightCommandR() {
return switch (branchHeight) {
case CORAL_LEVEL_FOUR -> superStructure
.coralLevelFour(soloController.rightBumper())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_THREE -> superStructure
.coralLevelThree(soloController.rightBumper())
.coralLevelThree(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_TWO -> superStructure
.coralLevelTwo(soloController.rightBumper())
.coralLevelTwo(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_ONE -> superStructure
.coralLevelOne(soloController.rightBumper())
.coralLevelOne(soloController.rightBumper(), () -> AutoAlign.poseInPlaceL1R())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
};
}
private Command getSoloCoralBranchHeightCommand(BooleanSupplier l1PoseSupplier) {
return switch (branchHeight) {
case CORAL_LEVEL_FOUR -> superStructure
.coralLevelFour(soloController.rightBumper())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_THREE -> superStructure
.coralLevelThree(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_TWO -> superStructure
.coralLevelTwo(soloController.rightBumper(), () -> AutoAlign.poseInPlace())
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
case CORAL_LEVEL_ONE -> superStructure
.coralLevelOne(soloController.rightBumper(), l1PoseSupplier)
.andThen(() -> soloScoringMode = soloScoringMode.NO_GAME_PIECE);
};
}

Comment on lines +1129 to +1133
/* Trigger readyToScore = new Trigger(() -> AutoAlign.poseInPlace());
readyToScore.onTrue(
Commands.runOnce(() -> soloController.setRumble(RumbleType.kBothRumble, 1.0)));
readyToScore.onFalse(
Commands.runOnce(() -> soloController.setRumble(RumbleType.kBothRumble, 0.0)));
Commands.runOnce(() -> soloController.setRumble(RumbleType.kBothRumble, 0.0))); */

Choose a reason for hiding this comment

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

medium

This block of code has been commented out with the note (Removal for autoscoring). If it's no longer needed, it's better to remove it completely to improve code clarity. Version control can be used to retrieve it if needed in the future.

Comment on lines +67 to +78
root = mech.getRoot("climber", 0.5 + Units.inchesToMeters(5.5), Units.inchesToMeters(19.5));
SmartDashboard.putData("Mechanism", mech);
m_elevator =
root.append(new MechanismLigament2d("elevator", 1, 90, 2, new Color8Bit(Color.kRed)));
var pivot =
m_elevator.append(
new MechanismLigament2d(
"pivot offset", Units.inchesToMeters(4), -90, 2, new Color8Bit(Color.kDarkRed)));
m_wrist =
pivot.append(
new MechanismLigament2d(
"wrist", Units.inchesToMeters(14.5), 270, 6, new Color8Bit(Color.kFirstRed)));

Choose a reason for hiding this comment

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

medium

The initialization of the Mechanism2d uses several magic numbers for dimensions (e.g., 5.5, 19.5, 4, 14.5) and visualization properties (e.g., 1, 2, 90, -90, 270, 6). It would be better to define these as named constants in a relevant constants class (e.g., SubsystemConstants or a new RobotDimensions class). This improves readability and makes it easier to update these values.

import frc.robot.Robot;
import frc.robot.sensors.ArmSensor;
import frc.robot.subsystems.auto.AutoLogic;
import jdk.jfr.Timestamp;

Choose a reason for hiding this comment

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

medium

The import jdk.jfr.Timestamp is unused in this file and should be removed.

Comment on lines +109 to +111
private StructPublisher<Pose3d> elevatorPose3d = NetworkTableInstance.getDefault().getStructTopic("elevator/heightPose", Pose3d.struct).publish();
public StructPublisher<Pose3d> TESTpose = NetworkTableInstance.getDefault().getStructTopic("debug/TEST", Pose3d.struct).publish();
//public StructPublisher<Pose3d> TESTpose2 = NetworkTableInstance.getDefault().getStructTopic("debug/TEST2", Pose3d.struct).publish();

Choose a reason for hiding this comment

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

medium

This section contains commented-out code and inconsistent formatting. The commented-out publisher for TESTpose2 should be removed if it's not needed. Also, the extra spaces before StructPublisher can be removed for consistent formatting.

Suggested change
private StructPublisher<Pose3d> elevatorPose3d = NetworkTableInstance.getDefault().getStructTopic("elevator/heightPose", Pose3d.struct).publish();
public StructPublisher<Pose3d> TESTpose = NetworkTableInstance.getDefault().getStructTopic("debug/TEST", Pose3d.struct).publish();
//public StructPublisher<Pose3d> TESTpose2 = NetworkTableInstance.getDefault().getStructTopic("debug/TEST2", Pose3d.struct).publish();
private StructPublisher<Pose3d> elevatorPose3d = NetworkTableInstance.getDefault().getStructTopic("elevator/heightPose", Pose3d.struct).publish();
public StructPublisher<Pose3d> TESTpose = NetworkTableInstance.getDefault().getStructTopic("debug/TEST", Pose3d.struct).publish();

return runOnce(
() -> {
if (hasBeenZeroed) {
System.out.println("Setting elevator level to: " + pos);

Choose a reason for hiding this comment

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

medium

Using System.out.println for logging is generally discouraged in robot code as it can be slow and doesn't integrate with the standard FRC logging framework. Consider using DataLogManager or publishing to SmartDashboard for debugging information, or remove this line if it's no longer needed.

Comment on lines +401 to +402
double smoothedAngleZ = 0.4;
double smoothingFactor = 0.1;

Choose a reason for hiding this comment

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

medium

These member variables smoothedAngleZ and smoothingFactor are declared but never used. They should be removed to clean up the code.

Comment on lines +410 to +411
if (!Robot.getInstance().sensors.armSensor.booleanInClaw()) {
}

Choose a reason for hiding this comment

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

medium

This if statement has an empty body and should be removed.

Comment on lines +417 to +422
double smoothingFactor = 0.5;// Percentage Scaler
double bottomZ = 0.2;
double topZ = 1.55;
double minPos = 0.0;
double maxPos = 37.5;
double targetZ = (bottomZ + ((curPos - minPos) / (maxPos - minPos)) * (topZ - bottomZ));

Choose a reason for hiding this comment

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

medium

These lines use several magic numbers for an interpolation calculation (e.g., 0.5, 0.2, 1.55, 0.0, 37.5). These should be defined as named constants with descriptive names to improve readability and maintainability.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants