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
47 changes: 33 additions & 14 deletions Docs/Architecture/CommandBased.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,49 @@

## Command Based is a way of structuring our code to make it easy to do complicated tasks, and write control logic in a concise way

Command based programming revolves around two concepts: Subsystems and Commands.
Command based programming revolves around three concepts: **Subsystems**, **Commands**, and **Triggers**.

A Subsystem is a set of hardware that forms one system on our robot, like the drivetrain, elevator, arm, or intake.
Each subsystem contains some associated hardware (motors, pistons, sensors, etc.) They are the "nouns" of our robot, what it is.
Commands are the "verbs" of the robt, or what our robot does.
Each Subsystem is generally made to contain a broad set of hardware that will always operate as a unit.

Commands are the "verbs" of the robot, or what our robot does.
Each Subsystem can be used by one Command at the same time, but Commands may use many Subsystems.
Commands can be composed together, so the `Line Up`, `Extend`, and `Outake` Commands might be put together to make a `Score` Command.
Commands can be composed together, so the `LineUp`, `Extend`, and `Outake` Commands might be put together to make a `Score` Command.
Because each Subsystem can only be used by one Command at once, we are safe from multiple pieces of code trying to command the same motor to different speeds, for example.

Subsystems are ways to organize resources that can be used by one Command at a time.

Some hardware might not be stored in a Subsystem if multiple things can/should use it at the same time safely.
For example, a vision setup can be read from by many things, and might not need to be locked by Commands.
Therefore, it might not be stored in a Subsystem.

On the other hand, a roller that is driven by a motor can only go at one speed at a time.
Therefore, we would wrap it in a Subsystem so that only one Command can use it at once.

A Trigger is something which can start a Command.
The classic form of this is a button on the driver's controller.
Another common type is one which checks for when the robot enables.
One non-obvious Trigger we used in 2024 was one which checked when we had detected a game piece in the robot, which we used to flash our LEDs and vibrate the driver controller.
Triggers can be made of any function that returns a boolean which makes them very powerful.
Some large Commands are better represented by several Commands and some Triggers!

# update with superstructure stuff later

### Resources

- [WPILib intro to functional programming](https://docs.wpilib.org/en/stable/docs/software/basic-programming/functions-as-data.html).
Read through this article on lambda expressions and functional programming if you haven't already.
- [WPILib docs](https://docs.wpilib.org/en/stable/docs/software/commandbased/index.html).
Read through these docs until you finish "The Command Scheduler"
Read through these docs until you finish "Organizing Command-Based Robot Projects"
OR watch [this video](https://drive.google.com/file/d/1ykFDfXVYk27aHlXYKTAqtj1U2T80Szdj/view?usp=drive_link).
Presentation notes for the video are [here](CommandBasedPresentationNotes.md)
The important segment to remember is:
Presentation notes for the video are [here](CommandBasedPresentationNotes.md).
If you watch the video, it is recommended to also read the [Subsystems](https://docs.wpilib.org/en/stable/docs/software/commandbased/subsystems.html), [Binding Commands to Triggers](https://docs.wpilib.org/en/stable/docs/software/commandbased/binding-commands-to-triggers.html), and [Organizing Command-Based Robot Projects](https://docs.wpilib.org/en/stable/docs/software/commandbased/organizing-command-based.html#) for addition details on using Command-Based.

The important segment of all of this to remember is:
> Commands represent actions the robot can take. Commands run when scheduled, until they are interrupted or their end condition is met. Commands are very recursively composable: commands can be composed to accomplish more-complicated tasks. See Commands for more info.
>
> Subsystems represent independently-controlled collections of robot hardware (such as motor controllers, sensors, pneumatic actuators, etc.) that operate together. Subsystems back the resource-management system of command-based: only one command can use a given subsystem at the same time. Subsystems allow users to “hide” the internal complexity of their actual hardware from the rest of their code - this both simplifies the rest of the robot code, and allows changes to the internal details of a subsystem’s hardware without also changing the rest of the robot code.
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 sure if you want to have this as a block quote, do you also want to add a summary for triggers?

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.

these are taken directly from the wpilib docs, hence the block quote. not sure if it still needs to be there since its decently redundant w the intro

- [WPILib intro to functional programming](https://docs.wpilib.org/en/stable/docs/software/basic-programming/functions-as-data.html).
Read through this article on lambda expressions and functional programming.
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.

Is this covered elsewhere?

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.

it got moved up in the list


### Examples

Expand All @@ -35,11 +58,7 @@ Because each Subsystem can only be used by one Command at once, we are safe from
### Notes

- We prefer making simple Commands with Command factories, or methods in a subsystem that return a Command.
These methods should be simple interactions like `setTargetExtensionInches()` or `extendIntake()`.
These methods should be simple interactions like `setTargetExtensionMeters()` or `extendIntake()`.
Then you can use decorators as described [here](https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html) to compose the basic Commands into more complex sequences.
Generally we make these compositions in `RobotContainer` but you can also make single-Subsystem compositions within that Subsystem.
Generally we make these compositions in `Robot` and `Superstructure` but you can also make single-Subsystem compositions within that Subsystem.
See our code from previous years for examples of this pattern, or talk to a software lead.
- In our 2023 code we started using a pattern called the `SuperstructureSubsystem`.
This pattern involves creating a Subsystem that has references to most other Subsystems to make Command factory methods with multiple Subsystems.
This pattern doesn't really make sense on inspection though, since the `SuperstructureSubsystem` doesn't actually lock any hardware from multiple-access and you can just make the composed Commands in `RobotContainer` instead.
In the future, just make multiple-Subsystem Command factories in `RobotContainer`.
26 changes: 12 additions & 14 deletions Docs/Architecture/CommandBasedPresentationNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Note that some of the code shown in the video differs from the current version o
- Motors
- Pneumatics
- Sensors
- Mutex, prevents multiple things from using the same hardware at the same time
- [Mutex (short for "mutually exclusive")](https://en.wikipedia.org/wiki/Lock_(computer_science)), prevents multiple things from using the same hardware at the same time

### Commands

Expand All @@ -36,24 +36,24 @@ For example:

```Java
// Runs the intake rollers forever
Commands.run(() -> intakeSubsystem.spinRoller(), intakeSubsystem);
Commands.run(intakeSubsystem::spinRoller, intakeSubsystem);

// Retracts the intake
// Note that the real hardware doesn't move instantly
// But we only need to set it once in code
Commands.runOnce(() -> intakeSubsystem.retract(), intakeSubsystem);
Commands.runOnce(intakeSubsystem::retract, intakeSubsystem);
```

If these Commands are defined in a Subsystem file, we can make them even simpler by calling `run` and `runOnce` on the subsystem itself
Currently, we tend to define these Commands in the Subsystem file, so we can make them even simpler by calling `run` and `runOnce` on the subsystem itself

```Java
// Inside IntakeSubsystem.java

// Notice how we don't need to define the requirements for these
// The subsystem does it implicitly
this.run(() -> spinRoller());
this.run(this::spinRoller);

this.runOnce(() -> retract());
this.runOnce(this::retract);
```

Anatomy of a Command declaration:
Expand All @@ -70,18 +70,18 @@ Anatomy of a Command declaration:
```Java
// In RobotContainer.java
// When the a button on the controller is pressed, run the rollers on the intake
controller.a().whenPressed(Commands.run(() -> intakeSubsystem.runRollers(), intakeSubsystem));
controller.a().whenPressed(Commands.run(intakeSubsystem::runRollers, intakeSubsystem));
```

- This is somewhat wordy
- But Commands are objects, so we can pass them around!
- Let's make a method that returns the `RunCommand` instead of having to make it here
- Let's make a method that returns the `Command` instead of having to make it here

```Java
// In IntakeSubsystem.java
public CommandBase runRollersCommand() {
public Command runRollersCommand() {
// Note implicit requirements
return this.run(() -> runRollers());
return this.run(this::runRollers);
}
```

Expand Down Expand Up @@ -113,9 +113,7 @@ intakeSubsystem.extendCommand().andThen(intakeSubsystem.runRollersCommand())
// An example of a more complex group

intakeSubsystem.extend().andThen(
intakeSubsystem.runRollers().until(() ->
intakeSubsystem.hasGamePiece()
),
intakeSubsystem.runRollers().until(intakeSubsystem::hasGamePiece),
intakeSubsystem.retract()
)

Expand All @@ -124,7 +122,7 @@ intakeSubsystem.extend().andThen(
elevatorSubsystem.runToScoringHeight()
.alongWith(
grabberSubsystem.holdGamePiece()
).until(() -> elevatorSubsystem.isAtScoringHeight())
).until(elevatorSubsystem::isAtScoringHeight)
.andThen(
// Outtake game piece for 1 second
grabberSubsystem.outtakeGamePiece().withTimeout(1.0)
Expand Down
Loading