-
Notifications
You must be signed in to change notification settings - Fork 8
Add preliminary articles for commands V3 #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ecea4be
971f196
5fa2de0
91e4921
f068b21
d6dbc69
2857706
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| --- | ||
| title: The Command Scheduler | ||
| description: How to use the command scheduler | ||
| prev: section-2-commands/why-mechanisms | ||
| next: false | ||
| --- | ||
| import Aside from '../../../components/Aside.astro'; | ||
|
|
||
| # The Command Scheduler | ||
|
|
||
| Commands within commands v3 are run and handled by the command scheduler. | ||
| The command scheduler can be fetched via ```Scheduler.getDefault()```. | ||
|
|
||
| In order for commands to work, you must call ```Scheduler.getDefault().run()``` | ||
| periodically, in the ```robotPeriodic``` method of your robot class. This will work | ||
| with robot classes that extend ```TimedRobot``` and ```OpModeRobot```. | ||
|
|
||
| ```java | ||
| public class Robot extends TimedRobot { | ||
| @Override | ||
| public void robotPeriodic() { | ||
| Scheduler.getDefault().run(); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| <Aside type="note"> | ||
| If you use the ```CommandRobot``` class, you should call ```Scheduler.getDefault().run()``` | ||
| by overriding the ```periodic()``` method of subclassed opmodes, instead of calling it | ||
| inside the robot class. Inline opmodes created by ```createXXXOpMode``` will run the command scheduler | ||
| by default. | ||
| </Aside> | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| --- | ||
| title: Why Commands | ||
| description: The what, how, and why of commands | ||
| prev: false | ||
| next: section-2-commands/why-mechanisms | ||
| --- | ||
| import Aside from '../../../components/Aside.astro'; | ||
|
|
||
| # What is a Command? | ||
|
|
||
| A command is an action a robot can take - which can include something as simple | ||
| as raising an arm joint or complex as an autonomous program. Think of them as powered-up | ||
| methods that can handle the requirements for controlling a robot over long periods of time. | ||
|
|
||
| # What's wrong with methods? | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about "Why not use methods?" because I think that's the question most new programmers might think. also "what's wrong" could sound like methods aren't supposed to be used in FRC programming |
||
|
|
||
| Let's say you're writing a method that prints "hello world!" to the console, | ||
| then sets a motor to 5 volts indefinitely. Your first instinct might be to do this: | ||
| ```java | ||
| public void printHiAndRunMotor() { | ||
| System.out.println("Hello World!"); | ||
| while (true) { | ||
| motor.setVoltage(5.0); | ||
| } | ||
| } | ||
| ``` | ||
| Here, the problem is with the while loop. While it's running, we are only setting the voltage of the motor; | ||
| not listening to button presses, logging data, and running other background tasks. To function properly, WPILib | ||
| requires many of these background tasks to be run periodically(at a 0.02 second or smaller interval). | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about
Comment on lines
+15
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a poor example of explaining the benefits of command-based, since it's solved in iterative robots. I don't think the comparison between commands and methods necessarily needs to happen. Commands at their heart are methods, the power of the paradigm is the declarative nature and deferred execution. |
||
|
|
||
| ## Enter: Commands! | ||
|
|
||
| Functionally, commands are superpowered methods that gives you a special statement that runs | ||
| these background tasks within a while loop: `coroutine.yield()`. | ||
|
Comment on lines
+33
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise, they're much more than just this.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean, are they? The only other feature that a command itself introduces is scoped triggers. If anything, its trigger declarations themselves that are declarative, not commands. You might have a better idea of how to phrase this, though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you don't use a while loop, you still have a command. |
||
| ```java | ||
| System.out.println("Hello World!"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. System.out.println isn't used for print outs in FRC but I think this is a good spot to introduce and use Driverstation.reportWarning() |
||
| while (true) { | ||
| motor.setVoltage(5.0); | ||
| coroutine.yield(); | ||
| } | ||
| ``` | ||
| In other words, it "yields control" to any pending background tasks before resuming the while loop. | ||
|
|
||
| <Aside type="note"> | ||
| Because `coroutine.yield()` can't be called outside of commands, it is highly discouraged | ||
| to use while loops outside of commands unless if you know what you're doing. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't include |
||
| </Aside> | ||
|
|
||
| # Command Syntax | ||
|
|
||
| A fully constructed command looks like this: | ||
| ```java | ||
| Command runMotor = | ||
| Command.noRequirements(coroutine -> { | ||
| System.out.println("Hello World!"); | ||
| while (true) { | ||
| motor.setVoltage(5.0); | ||
| coroutine.yield(); | ||
| } | ||
| }) | ||
| .named("Set to 5 volts"); | ||
| ``` | ||
|
|
||
| There's a lot to digest here, so let's break it down step-by-step. | ||
| - Even though commands are more like methods, they act like objects in java. | ||
| Hence, the `Command runMotor` definition. | ||
| - `Command.noRequirements` creates the command. | ||
| We'll explain what "noRequirements" means in the following section. | ||
| - Every line of code between `coroutine -> {` and `}` will be run | ||
| when the command runs: just like a method body. | ||
| - `named("Set to 5 Volts")` gives the command a name, which allows programmers | ||
| to distinguish this command from others when using debugging tools. | ||
|
|
||
| # Running Commands | ||
|
|
||
| The following allows you to start a command asynchronously: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did clarify it in the following sentence (in this case...), but i'll try and clarify this. Thanks! |
||
|
|
||
| ```java | ||
| Command runMotor = ...; // see previous example | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just put the previous code there instead so viewers don't have to keep scrolling up to reference it. In addition, you could put a comment like |
||
| Scheduler.getDefault().schedule(runMotor); | ||
| System.out.println("Hello!"); | ||
| ``` | ||
|
|
||
| Think of calling `schedule()` as ordering a robot to start washing the dishes | ||
| without waiting for it to finish. In this case, the `println("Hello!")` statement | ||
| will run before the `runMotor` command completes. | ||
|
|
||
| But the vast majority of the time, you want a command to run when a button on a controller is pressed or held down. | ||
| To do this, we use a `CommandGamepad` instead of a `Gamepad`, like so: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point, they don't know what a
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't they have to know it due to section 1a being about coding a kitbot? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would first introduce triggers, then introduce the concept of creating triggers manually using existing gamepad methods that they're familiar with, then introduce CommandGamepad as a wrapper around Gamepad that handles creating the triggers for you. Then they understand the full stack
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm... i feel that most newbie programmers wouldn't need specialized triggers outside of the ones mapping to each button. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Triggers are really the only interface you have to controlling a command-based robot, so I think it's important even if they're not going to use them right away. They're very important tools to understand, and even if they aren't used immediately it'll be important to constantly be reminding what they are and how they work |
||
| ```java | ||
| private CommandGamepad xbox = new CommandGamepad(0); | ||
|
|
||
| public Robot() { | ||
| Command runMotor = ...; | ||
| xbox.leftTrigger().onTrue(runMotor); | ||
| } | ||
| ``` | ||
|
|
||
| In this case, the `runMotor` command will run once the left trigger on the controller is pressed. | ||
| If it is desired to cancel the runMotor command once the left trigger is released, | ||
| the `onTrue` statement can be replaced with `whileTrue`: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would explain more on why that is the case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trigger binding functions could use their own section with the nice graphs from that one site |
||
| ```java | ||
| public Robot() { | ||
| Command runMotor = ...; | ||
| driverController.leftTrigger().whileTrue(runMotor); | ||
| } | ||
| ``` | ||
|
|
||
| Generally, whileTrue is desireable over onTrue since it provides a way for a driver/operator | ||
| to quickly cancel a command if it goes awry. | ||
|
Comment on lines
+109
to
+110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like telling people what they generally should or shouldn't do. It's better to explain the tools they have at their disposal, and let them determine what tool is best for the task at hand. |
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| --- | ||
| title: Mechanisms in Command-Based Programming | ||
| description: Why we need mechanisms in command-based | ||
| prev: section-2-commands/why-commands | ||
| next: section-2-commands/the-command-scheduler | ||
| --- | ||
| import Aside from '../../../components/Aside.astro'; | ||
|
|
||
| # An overview of Mechanisms | ||
|
|
||
| A `Mechanism`, on the surface level, is a part of the robot, like a double-jointed arm or a shooter. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also might help to briefly explain the connection between Commands and Mechanisms. Could be as simple as explaining how Mechanism use Commands to move a part of the robot. Like spinning a motor on a shooter to shot a ball. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a mechanism is any part of the robot you want to control separately from any other mechanism. Mechanisms run a single command at a time. If you want to run two separate commands, you need two mechanisms. Sometimes this corresponds to subsystem-level components, but they really should be thought of as two separate ways of grouping the robot. The mapping of real world mechanisms to command mechanisms is not always 1:1 |
||
| If you've used commands V2 before, they're the exact same thing as subsystems (and in that case, feel free | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth putting that in a note rather than in a paragraph There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And at the top probably |
||
| to skip to the "Defining a Mechanism" section). | ||
|
|
||
| # Conflicting Commands | ||
|
|
||
| Let's say we have the following code: | ||
|
|
||
| ```java | ||
| CommandGamepad driverController = new CommandGamepad(0); | ||
| Command shootAt5Volts = ...; // assume we have a command defined here. | ||
| Command shootAt7Volts = ...; // same here. | ||
| driverController.leftTrigger().whileTrue(shootAt5Volts); | ||
| driverController.rightTrigger().whileTrue(shootAt7Volts); | ||
| ``` | ||
|
|
||
| When we hold down the left trigger, the shooter is run at 5 volts, and when | ||
| we hold down the right trigger, it's set to 7 volts. Pretty simple. | ||
|
|
||
| Notice here, though, that it's possible for both triggers to be held down at the same time, | ||
| which would cause `shootAt5Volts` and `shootAt7Volts` to run simultaneously. This is obviously | ||
| undefined behavior that we want to prevent - but how? | ||
|
|
||
| # Using Mechanisms to fix conflicting commands | ||
|
|
||
| Ideally, we could declare what motors each command requires. If 2 commands that | ||
| required the same motor were ran simultaneously, one would get cancelled. | ||
|
|
||
| However, there are many varieties of motors and actuating components on a robot. As a result, the commands | ||
| v3 framework leaves it to the coder to group these motors/components into categories, called mechanisms. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you're redefining mechanism in this section here |
||
| Each command will then declare the mechanisms they require. | ||
|
|
||
| For instance, the 4 motors that control a robot's movement on the field could be grouped into a "drivetrain" mechanism. | ||
| If we were to declare a command that drives the robot forward by 5 meters, it would require this drivetrain mechanism. | ||
|
Comment on lines
+36
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not understand what this section is trying to convey. The example above introduces conflicting commands, and suggests there's a way to resolve conflicts. The opening paragraph here says ideally we could add requirements. The second paragraph opens with 'However', suggesting we can't add requirements. But then says v3 leaves it to us to define our mechanisms, and that the command will add requirements. The last paragraph uses a drivetrain as an example, but this section was supposed to explain how to resolve our issue with our shooter from the previous section
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair, I should probably change this section to entirely reference the shooter or the drivetrain instead. |
||
|
|
||
| # Defining a Mechanism | ||
|
|
||
| Mechanisms are represented as classes in java, inside their own separate files. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything is represented as classes in java, inside their own separate files |
||
| Note that a mechanism class slightly deviates from a traditional class with the `implements Mechanism` keyword at the end. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't try and suggest that a 'traditional' class can't implement interfaces. Just say that Mechanisms must implement the interface, and maybe explain that that means they tell other parts of the code that they are guaranteed to have certain functionality that is required of mechanisms |
||
|
|
||
| ```java | ||
| public class Shooter implements Mechanism { | ||
| private final TalonFX motor = new TalonFX(4); | ||
| } | ||
| ``` | ||
|
|
||
| Then, in the `Robot.java` file, you would simply define an instance of each mechanism like so: | ||
| ```java | ||
| public class Robot { | ||
| private final Shooter shooter = new Shooter(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is gonna make it hard to access in command compositions that don't exist in this class. Make it public or package private imo |
||
| } | ||
| ``` | ||
|
|
||
| # Defining commands that require a mechanism | ||
|
|
||
| In general, commands that require a mechanism should be defined inside of that mechanism's class. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid 'In general' or other suggestive phrases. One of the harder parts is teaching things like 'where should commands be defined', and other code structure decisions. |
||
| If that approach is followed, you can create a command that requires the shooter mechanism like so: | ||
|
|
||
| ```java | ||
| public class Shooter implements Mechanism { | ||
| private final TalonFX motor = new TalonFX(4); | ||
|
|
||
| public Shooter() { | ||
| Command shoot = | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is local to the constructor |
||
| run(coroutine -> { | ||
| while (true) { | ||
| motor.setVoltage(5,0); | ||
| coroutine.yield(); | ||
| } | ||
| }) | ||
| .named("Shoot"); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| <Aside type="note"> | ||
| Recall that a command without any requirements is defined with `Command.noRequirements()` | ||
| instead of `run()`. | ||
| </Aside> | ||
|
|
||
| However, this poses a problem: the `shoot` command isn't accessible to the Robot class, | ||
| since it only exists inside of the constructor of the Shooter class. To remedy this, we | ||
| can instead make a method that creates an instance of the `shoot` command: | ||
|
Comment on lines
+91
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh. Don't make an example just to say 'btw this is a bad example' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is missing the explanation that this needs to be a method rather than a variable, especially since previous examples stored commands in variables |
||
|
|
||
| ```java | ||
| public class Shooter implements Mechanism { | ||
| private final TalonFX motor = new TalonFX(4); | ||
|
|
||
| public Command shoot() { | ||
| return run(coroutine -> { | ||
| while (true) { | ||
| motor.setVoltage(5,0); | ||
| coroutine.yield(); | ||
| } | ||
| }) | ||
| .named("Shoot"); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Then, in Robot.java, you can use that command like so: | ||
|
|
||
| ```java | ||
| public class Robot { | ||
| private final CommandGamepad xbox = new CommandGamepad(0); | ||
| private final Shooter shooter = new Shooter(); | ||
|
|
||
| public Robot() { | ||
| xbox.leftTrigger().whileTrue(shooter.shoot()); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| <Aside type="note"> | ||
| Note here that the `shooter.shoot()` call, by itself, doesn't actually run the shooter! | ||
| In other words, this won't do anything at the beginning of teleop: | ||
| ```java | ||
| public class Robot extends TimedRobot { | ||
| private final Shooter shooter = new Shooter(); | ||
|
|
||
| @Override | ||
| public void teleopInit() { | ||
| shooter.shoot(); | ||
| } | ||
| } | ||
| ``` | ||
| Rather, it creates a command that, when scheduled or bound to a trigger, | ||
| will run the desired action. The java compiler will throw a compile-time error | ||
| if it detects this mistake in your code. | ||
| </Aside> | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it needs some explanation of what command based programming is to make the transition between intro to Java and FRC programming smoother. I also think that knowing what is Command Based programming as a concept, even if it's in simple terms, is important.
This introduction could also mention what WPILib is since it's sort of mentioned in #9 but it's never correctly explained.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would probably be better to direct them to the WPILib docs on commands / subsystems and say 'familiarize yourself with commands and subsystems before continuing with this section'.
In this section you can refer back to it, but you're likely going to need to reiterate everything that's already said in those docs in order to provide the necessary context for teaching this lesson.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't do that because when I first read wpilib's command docs as a freshman, they pretty much threw everything at you all at once instead of starting with the basics.
Maybe V3 docs would be different, im down to moving some of the stuff covered here to the wpilib docs instead and redirecting people.