Lesson 4 - Robot Container Basics



It will be helpful to view the RobotContainer class in our 2024 robot code here as you read the lesson

Robot Container Basics

The most important class in the entire robot project is the RobotContainer class. The RobotContainer class is where all the subsystems of the robot are declared and where all button are binded.

Now, that was a mouthful. Saying that in words you actually understand: The RobotContainer is responsible for coordinating the commands and subsystems together in a robot.

It also controls all the button bindings, so the commands work when you press a button on the robot controller.

You should open the 2024 robot code and open RobotContainer.java, and follow along wit the lesson


Instance Variables (Lines 62-77)

CommandXboxController (Line 62) This is the instance variable where the Xbox controller is declared. This allows for button presses to call certain commands.
Subsystems (Lines 69-73) Subsystems are each declared as private final variables. A single subsystem should only be defined once.
SendableChooser (Lines 76-77) SendableChoosers are declared as instance variables. This allows you to choose between different autons and allows for delays. You don't need to worry too much about them right now.

Constructor (Lines 80-153)

In the constructor, all the instance variables are instatinsated (creating an instance of that object) and their constructors called.

The if statements are there to check whether the subsystem should be instatinsated depending on the robot.

For example, if we are making a testing robot that does not have an Amp Arm, it doesn't try to instansiate an Amp Arm Subsystem.


AutoAlign and PathPlanner Methods (Lines 155-264)

The methods which return a command are all used for AutoAlign, which we will get into in later lessons.

The registerNamedCommands() is for Pathplanner autonomous paths. This allows the robot to choose between which autonomous path to run based off certain criteria.

We will not be using Pathplanner this year, so you can ignore that method.


Configuring Button Bindings (Lines 266-320)

This method is used to bind buttons on the xbox controller to robot commands.

To bind buttons, we use something called a Trigger

A Trigger basically has methods that allow us to detect if something about the robot or Xbox controller has recently changed.

They are mainly used for binding buttons, but they do have many other uses.

The Trigger class has several methods:

TriggerMethod(Command) Each trigger method takes in an instantiated command as a parameter.
onTrue() Runs the command once the trigger becomes true and stops running the command when it becomes false.
whileTrue() Runs the command as long as the trigger is true and stops running the command when it become false.
onFalse() Runs the command once the trigger becomes false and stops running the command when it becomes true.
whileFalse() Runs the command while the trigger is false and stop running the command when it becomes true
toggleOnTrue() Begins running the command when the trigger turns true. Continues running it as usual when it turns false
toggleOnFalse() Begins running the command when the trigger turns false. Continues running it as usual when it turns true

You will almost never declare your own Triggers. In the RobotContainer class for 2024, we declare our own custom Trigger once. Instead the CommandXboxController class provides Triggers with methods such as a(), b(), x()

This is so that you can bind commands to every single button on the xbox controller


Some Advanced Trigger Stuff(you do not need to know this, but it has some useful information)

You can chain triggers with and(), or(), and not(), just like a normal boolean, but with methods instead of the symbols. You cannot use the symbols with Triggers


It's important to understand that Triggers are not booleans, even though they're similar

You can also get the left trigger axis and right trigger axis as a double between 0 and 1 with getLeftTriggerAxis() and getRightTriggerAxis() if you're interested in how hard the driver is pressing the triggers. You also need to do this if you want a command to activate at a trigger axis of less than 0.5(that's how much it takes for the trigger to turn true)


Here's an example from last year's robot of using getLeftTriggerAxis()



getAutonomousCommand

This methods returns the autonomous command to run using the information from the Sendable Chooser, using the getSelected() method. It uses a ProxyCommand, which I'll talk about later in the lesson.

Command Compositions

Now, for a slight change in pace, we're moving on to command compositions. A command composition is a command that consists of other commands.


This is useful in many cases. For instance, in last year's code, we created a command composition for shooting into the amp, one for shooting into the speaker, etc.


Why did we do that? We did it because both shooting into the amp and speaker used the shooter command, but the amp command also involved raising the arm, creeping forward, then shoot, then move back(Note it was more complicated, but that's the gist)


Now let's talk about different types of command compositions.


Each type of command composition is backed by a specific class and also one of the 38 inherited methods in the Command class(that's why Command has 38 inherited methods). Some of them also have a factory method.


We'll usually use the decorator methods.

RepeatCommand

In RepeatCommand, a command is restarted after isFinished() returns true


RepeatCommand is backed by the RepeatCommand class. You can also get a RepeatCommand from a normal Command by calling the repeatedly() method.

SequentialCommandGroup

You can have a command scheduled after another in a SequentialCommandGroup. It is backed by the SequentialCommandGroup class. It is backed by the sequence() factory in the Commands class(not Command) class...


For the decorator method, you can use andThen() to attach something after a command and beforeStarting() to attach a command before a command.


Parallel Commands

There are 3 types of parallel commands that are all slightly different.


A parallel command group runs all commands in parallel, and ends when all of the commands are finished. A parallel deadline group has a deadline command and the members and ends when the deadline ends and interrupts all the members. A parallel race group ends when one of the commands in the group ends

Let's talk about all 3 types in more detail.


A parallel command group is backed by the ParalllelCommandGroup class. To use a decorator, you use the alongWith() method.


A parallel race group is backed by the ParallelRaceGroup class. To use a decorator, use the raceWith() method


A parallel deadline group is backed by the ParallelDeadlineGroup. To use a decorator, use the deadlineWith() method


Adding an end condition

To add a end condition for a command, use the until() method.


If you want to add a timeout, use the withTimeout() method. The method takes the number of seconds of timeout as a parameter


Adding end behavior

finallyDo() allows you to make the robot execute a lambda after the command ends. A lambda expression is basically a method that has no names, takes no parameters, returns nothing, and is directly declared. For instance, you can have a call of command.finallyDo(() -> System.out.println("Hello, World");) if you want "Hello, World" to be printed after the command ends.


handleInterrupt() allows you to make the robot execute a lambda only if it's interrupted(e.g. if the interrupted parameter in end() is true)


Choosing commands

A select command is a command that allows you to create a map and allow you to choose based on the current robot state(That's actually a large part of auto-align and why there are a bajillion methods in RobotContainer)


You use the SelectCommand class: there is no decorator for this one


Conditional Command

A conditional command runs a command only if a condition is true


There is an unless() decorator that allows you to pass a condition to not run the command


Proxy Command

This is the most non-intituitve type of command compostion we're learning today. The only reason it exists is because the command scheduler is bad and it's a little workaround around the bad command scheduler


As you remember from Lesson 3(hopefully), each command has some requirement subsystems. The command scheduler uses this to prevent scheduling 2 commands that require the same subsystem


Command compositions are treated as one command by the command scheduler though, and every single requirement is inherited by the composition


This causes problems if you need to use a subsystem that's a requirement of a command composition but no longer used.


The whole point of a proxy command is to ensure that the command composition doesn't inherit all of the requirements of that command. That's about it.


If you didn't get that explanation, ask someone on the code team and they'll explain it to you better


Here is a reference of the type of command compositions and an example

Composition What is it? Decorator method
RepeatCommand Runs the command repeatedly repeatedly()
SequentialCommandGroup Runs the command in the group in order beforeStarting() to add a command before the current command and andThen() to add a command after the current command
ParallelCommandGroup Runs the commands in the group at the same time. Ends the group when all of the commands in the group end alongWith()
ParallelRaceGroup Runs the commands in the group at the same time. Ends the group when one of the commands in the group ends raceWith()
ParallelDeadlineGroup Runs the commands in the group at the same time. Ends the group when the desiginated deadline command ends deadlineWith() in 2024 and deadlineFor() in 2025
An end condition Adds a condition that ends the command early until()
A timeout Adds a timeout withTimeout()
End behavior Adds behavior for the robot to execute to handle after the end of the command finallyDo() for all cases, handleInterrupt() for only an interruption
SelectCommand Has the robot choose the command based on a map and curret state N/A
ConditionalCommand Has the robot execute the command based on a condition unless()
ProxyCommand Allows the command composition to not inherit all of the requirements of the child commands N/A

A good example of a use of command composition is the forAmpAutoFire() method in ThrowCommand, which is shown for your convienence