Skip to content

Stage 2 CTRE Solution Code - #175

Open
roboteer5291 wants to merge 37 commits into
frcsoftware:stage2from
roboteer5291:stage2-ctre-solution
Open

roboteer5291 wants to merge 37 commits into
frcsoftware:stage2from
roboteer5291:stage2-ctre-solution

Conversation

@roboteer5291

@roboteer5291 roboteer5291 commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

Creates stage 2 CTRE Solution Code. Still very much a WIP

Claude assisted with some debugging and outlining of the parts of the collision avoidance logic. All code written by hand, in the end.

Todo:

  • Add AdvantageScope model and rig
  • Create intake and indexer mechanisms
  • Add sensor simulation (indexer and claw)
  • Add coral on reef visualization for AdvantageScope
  • Create a fully-featured coral-only teleop utilizing a state machine
  • Validate and clean up AutoAlignCommand code
  • Create a basic auto mode (no vision, assume coral is at same position on ground)
  • Evaluate whether the initial teleops (BasicScoringTeleop and FullScoringTeleop) are at the correct level or if they should be simpler
  • Lint and format
  • Final pass through all code for cleanliness, appropriate commenting, and any bad habits
  • Replace DogLog with the Telemetry API
  • Add Tunable support for setpoint tuning
  • Update to alpha 7 controllers
  • Collision avoidance

@roboteer5291 roboteer5291 added CTRE Anything starter code or curriculum material touching CTRE libraries Java Code For all content relating to Java code (includes robot code and Stage 0 code) Stage 2 labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

🌐 Preview URL: https://pr-175.frcsoftware.pages.dev

@Daniel1464 Daniel1464 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.

I know this is still a draft, but left some preliminary comments since i have time

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🌐 Preview URL: https://pr-175-frcsoftware.frcsoftware.workers.dev

@roboteer5291
roboteer5291 changed the base branch from main to stage2 September 10, 2026 18:35
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🌐 Preview URL: https://pr-175.frcsoftware.org

Changed pages
No changed pages detected.

Comment on lines +97 to +98
pivot = new TalonFX(Constants.PIVOT_ID, Constants.BUS);
rollers = new TalonFX(Constants.ROLLERS_ID, Constants.BUS);

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.

why are these here? Instantiate where they're declared

@roboteer5291
roboteer5291 requested review from Adrianamm, ArchdukeTim, Daniel1464 and DylanB5402 and removed request for ArchdukeTim October 31, 2026 22:15
@roboteer5291
roboteer5291 marked this pull request as ready for review November 1, 2026 00:34
}

@Override
public void simulationPeriodic() {

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.

I think it's better to put coralSim.periodic() under a RobotBase.isSimulation check inside robotPeriodic instead. (Just my opinion, but I think the dedicated simulationPeriodic method is kind of an anti-pattern)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Why so? It's an included method. I do agree that the lack of clarity in the docs on the run order isn't great.

Comment on lines +111 to +113
table.log("Applied Voltage", Volts.of(getAppliedVoltage()));
table.log("Position", Rotations.of(getPosition()));
table.log("Velocity", RotationsPerSecond.of(getVelocity()));

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.

These getters should return Units

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I disagree. We made a conscious decision to not use the units library because of the clunky math. Having getters that we use the value of for logic reasons (ie the position) return a non-unit value, or return a unit value just to unpackage it, doesn't really make sense. So all of the getters should return a non-unit value to be consistent there.

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.

or return a unit value just to unpackage it

Yet here you're unpacking it just to pack it up again.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah I don't love the unpack -> repack. Since we (presumably) want to log this data with units, I think it might be better to just get the value (with units) from the status signal directly, rather than having an intermediate helper function? You can keep the helpers that just return doubles

.a()
.onTrue(
Command.noRequirements(
(coro) -> {

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.

Why Coro instead of just co?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I saw coro used somewhere, and decided to use that. That's it. I do feel like coro is a bit too short since you cut out the entire second part of coroutine (routine)

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.

Yeah I guess for teaching, anything shorter than coroutine is vague, but I wonder if wpilib examples use a specific name for this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I believe the wpilib examples all use coroutine

.onTrue(
Command.parallel(
intake.setState(Intake.State.INTAKING),
indexer.setVoltage(12),

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.

A command for indexing would be better than manually applying the voltage

public ArmTest(Robot robot) {
CommandXboxController controller = new CommandXboxController(0);

controller.a().onTrue(robot.superstructure.setArmPosition(0));

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.

Should this OpMode move the elevator to a safe position?

public ElevatorTest(Robot robot) {
CommandXboxController controller = new CommandXboxController(0);

controller.a().onTrue(robot.superstructure.setElevatorPosition(0));

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.

Likewise should this put the arm up so elevator can move freely

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You run into a catch-22 on these with your other comment to do this on the arm test opmode. I'll change it for the arm test opmode, but on the elevator test, I'm assuming you'd physically constrain the arm up there somehow. If you were bringing up a real robot, you'd only be able to tune one at once, and the same applies here since the arm code won't be written yet at the time of tuning the elevator.

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.

This isn't a tuning opmode, this is just testing that it goes to different positions. If it doesn't command the arm, then it'll become invalid once the arm is attached.

It should still command the arm, and the arm could have the API, but not actually be connected to anything requiring physical constraints until it does get written

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I definitely think this is a tuning opmode, as the intent is to use it with phoenix tuner or rev hardware client. In the context of the course, I think it's definitely a tuning opmode, since it'll be created immediately after the "Mechanisms" themselves to facilitate testing and tuning them.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

To be clear, I don't think putting it in a safe position is bad here. It's just odd from a sequence of events standpoint in terms of the course. The overall solution code here is the final state of the code, and I'm not sure how to make it so that an intermediate snapshot would work here, considering this code will likely be RLI'd into the course

@DylanB5402 DylanB5402 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

partial review - just the thoughts I've had so far, going to keep reading through this offline


private static class Constants {
static final int MOTOR_ID = 22;
static final CANBus BUS = new CANBus(CANPort.CAN_S1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(nitpick) we define the CANBus as its own variable here, but we don't in other subsystems. I don't think we're using the CANBus variable here, so we should probably just construct it inline in the TalonFX constructor. Or maybe define the bus variables somewhere else and pass them around? idk how I feel about that


public class Arm {

private static class Constants {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can't remember if I commented on the Constants static class before. I don't love it, probably wouldn't run it with my own team, but if that's a convention we want to use and there's consensus on it, I'm ok with it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

From what I recall there wasn't ever a clear consensus in any direction. I personally like it as a least-bad option. Dedicated constants file being bad, and not liking the constants just scattered among everything else.

Comment on lines +111 to +113
table.log("Applied Voltage", Volts.of(getAppliedVoltage()));
table.log("Position", Rotations.of(getPosition()));
table.log("Velocity", RotationsPerSecond.of(getVelocity()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah I don't love the unpack -> repack. Since we (presumably) want to log this data with units, I think it might be better to just get the value (with units) from the status signal directly, rather than having an intermediate helper function? You can keep the helpers that just return doubles

@DylanB5402 DylanB5402 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

second round of reviews! Also, can we get an ascope layout for this / maybe double check keybinds too in simgui-ds.json? I was able to drive with WASD once I set the keyboard as a joystick in the simgui, not sure what other buttons were supposed to do.

new Slot0Configs().withKP(0.3).withKI(0).withKD(0).withKS(0.2).withKV(0.125))
// .withDriveMotorGains(new Slot0Configs()
// .withKP(0.3).withKI(0).withKD(0)
// .withKS(0.1498).withKV(3.16356))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should clean up the comment code in this file? unless you have a reason to keep it

.named("Stop Drive");
}

public static class AutoAlignCommand implements Command {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's the reasoning for making this its own Class? Generally have an aversion to class commands, though I'm not sure how relevant that is for 2026. There also is a lot of logic going on here, +1 to the other comment asking about breaking it out into its own file. I wonder if there's any way we could simplify this or write it in a more functional/imperative way?

Might just be a preference / code style thing going on here: this command looks like it's trying to handle a number of different scenarios, while I think I'd rather build smaller commands that are more specific in purpose. This command feels a little too stateful in a way that I'm not sure we want to encourage students to write their own code

new Slot0Configs().withKP(0.3).withKI(0).withKD(0).withKS(0.2).withKV(0.125))
// .withDriveMotorGains(new Slot0Configs()
// .withKP(0.3).withKI(0).withKD(0)
// .withKS(0.1498).withKV(3.16356))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Decent bit of commented out code throughout this file, can we clean it up (or if there's a reason for it to be commented out that's fine)

}

private final TalonFX pivot = new TalonFX(Constants.PIVOT_ID, Constants.BUS);
private final TalonFX rollers = new TalonFX(Constants.ROLLERS_ID, Constants.BUS);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not entirely sure how I feel about the pivot and rollers being in the same Mechanism. I'm personally a fan of splitting them just to get a little bit of separation and looser coupling, but I can see the case for having them as the same mechanism too, not sure if there's been other thought/discussion about this

table.log("Active Commands", getRunningCommands().toString());
}

public Command setArmVoltage(double voltage) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't having commands that only run half the subsystem (just arm or just elevator) open up some edge cases where you have a command for the elevator that runs but then gets interrupted by an arm command?

These commands also only look to be called by the opmode, so maybe it's not a big deal? Maybe it's worth having setArm also explicitly set elevator to 0 volts or something

// .named("Set Position");
// }

public Command setPosition(double elevatorPosition, double armPosition) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe more thought experiment than actual feedback: I wonder how this command changes if you split arm and elevator into separate mechanisms?

I'm thinking that you get to make use of some of the more interesting ways to chain/compose/fork commands if you run them as separate subsystems, but I don't think it matters too much in practice, pretty sure Superstructure being its own mechanism is pretty set in stone at this point.

This branch has not been deployed

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

Labels

Backend Anything relating to non-content aspects of the website or its infrastructure CTRE Anything starter code or curriculum material touching CTRE libraries Java Code For all content relating to Java code (includes robot code and Stage 0 code) Stage 2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants