Command based, using it? thoughts?

My team has been trying to use command based in Java this year and just going off loc I’m personally not happy with the results. Previous years that just used TimedRobot plain were able to reduce the complexity imo. It may just be we’re using it incorrectly but what’s your team’s experience with command based? Are there any other paradigms you’re using that you prefer?

1 Like

I first learned with timed robot, but I switched to command based pretty early because that was what my team used. I agree that it can be very complex, especially if it isn’t managed properly. I’ve found advantages with command based with the ease of creating autos, setting up command groups for complex tasks, and creating the program logically in subsystems. The biggest thing that made my code simpler and more readable was making sure I had everything well organized in subfolders. As long as I keep my code well organized by my subsystems and commands, I tend to find the code more organized than running it straight through in timed robot.

13 Likes

Command based is harder to learn and definitely a much bigger learning curve, but you will see gains in your autonomous capabilities with the change. However, it is a lot harder for people who don’t have an understanding of Object Oriented Programming and the Java language in general.

Our team has been using Command Based since 2018. Our software has been steadily improving since then, and this year is going to be one of our most advanced ever. Why? Because we are taking advantage of features like the RamseteCommand and Command Groups for automating the robot that are only accessible in command-based.

Yes, it is harder in the short run. But in the long run, if you learn how Java works and you study what features WPILib gives you, you will be better off in the long run.

11 Likes

We have seen command-based (properly implemented) massively increase our ability to write clean complex code. It allows us to think more critically about the code and “what” the robot does, as opposed to “how” the robot does it.

The major con’s of using it would be level of entry. If you don’t have someone to properly teach you methods, objects, and the general structure of code, you may struggle setting it up properly. Thankfully, there are a growing number of tutorials on how to properly set up command-based code.

Additional Benefits:

12 Likes

I agree with @Max2370 command based programming is a lot more similar to something you’d see in a computer science class because of the more complex oop associated with it. It allows us to separate different parts of our code.

1 Like

Once you have command like ‘OpenIntake’, ‘FollowTrajectory’, ‘ShootBall’, … it’s quite easy to create autos that follow a trajectory while concurrently opening the intake, then shoot 2 balls etc.
You can also bind commands to buttons for teleop, put them onto the dashboard.
Yes, it takes a little time to learn about it and to create commands which you can use that way, but consider the alternative. Using commands, you’re within an ecosystem that others use as well, you can ask for help, there are bugfixes and updates, there is documentation. If you do all that yourself you’re more on your own.

8 Likes

I recommend using inline commands where possible over team-written command classes (as was common in old pre-2020 command-based); they’re much more concise and less boilerplate (it’s closer functional programming).

9 Likes

It’s certainly a major paradigm shift if you’re used to FORTRAN, C, and assembly (as I was by the early '90’s). But after a transition through Postscript and early Java in the mid '90s and early '00s (I still have the Postscript Language Reference Manual 2^{nd} edition and The Java Programming Language 1996 (v. 1.0.2) where I can reach them without getting up as I sit here in my home office.), and event-driven (X11 GUI) programming, it really was the next step.

The key item is that you have to unlearn the procedural programming instinct to translate problem space into solution space up front. An event-based OO infrastructure can often let you code completely or nearly completely in solution space. When you do have to move into a separate solution space, do it at the most limited and well-defined scope possible. And still think that you aren’t doing the best thing, just falling back on “the last refuge of the incompetent” ^*.

*

Yes, an obscure quote from one of my favorite bits of science fiction. Salvor Hardin in Foundation referring to violence. This was Asimov’s writings, probably not the streaming series; I bailed on the series after a few minutes, and felt really good about it after reading synopses of the episodes.

5 Likes

I had an idea of what the programmers could accomplish this year that was maybe optimistic. It’s been a while since they could properly program a robot. Although we do some offseason training, they didn’t get into command based programming as far as I know, and it was only once the season started that they really delved into it.

With all of those caveats, I think it was slow going to start. It was a new API. I think the understanding of what certain functions would do based on names alone was an issue, but they should have been reading the docs. I don’t necessarily think it’s intuitive, but it’s not like they struggled all season.

Now I think it’s going smoothly. While I had hoped for more to be accomplished so far, I’m very pleased with what they’ve been spending time on. It’s not debugging poorly architected code, it’s adding in more complex features. I like the (more or less) enforced organization as well. Once they finally got into the swing of things it seemed like the command based programming helped them test new things rapidly, which in turn helped the build team test their prototypes.

1 Like

As complicated as a bots physical interactions can be I prefer the small bites of work that exist in the event driven command based architecture.

7 Likes

In Iterative/TimedRobot, it’s fairly trivial to write always-on routines (e.g., every TeleopPeriodic, read the joystick axes, perform math, set the drivetrain motor proportions, or even read current flywheel velocity, read setpoint, calc error, do proportional controller math, set flywheel motor proportion) and routines that run only while a button is held (e.g., if buttonX.get(), set intake motor proportion to -1, else set to 0).

Where the Command-based framework helps is:

  • when something needs to happen exactly once, usually when a button is pressed (requires tracking the previous state of the button and only performing the action when the button wasn’t pressed before, but is pressed now)
  • when something needs to continue beyond when the button is released (requires some variable to track the “mode” or state of the robot – when that button was pressed, change modes)
  • when a particular routine needs to initialize some variables when it begins, then perform actions every *Periodic after that, using those variables (more instance variables in the Robot file)
  • when a particular routine needs to clean up after itself before returning control to something else (requires remembering to do that and coding it properly, and bugs are not highly visible)
  • an action needs to time out after x seconds or delay starting (combine the “initialize” case above with some time-tracking variable, which needs to be updated and evaluated properly)

But most of all:

  • stringing together autonomous actions, some of which end based on time and others which end based on other completion criteria, and some of which run simultaneously
  • code reuse in multiple possible autonomous routines

You can do all of this manually, but you end up with a lot of variables to manage, at least once you have more than 1-2 of the above cases. Some variables are easy to move into their own routines or files; others end up needing to stay in the Robot class. The Command system manages these cases for you, and has been tested to handle them properly.

The tradeoff is that you need to be comfortable internalizing the system that the library writers intended, and writing code that follows their rules. It’s no longer procedural programming – you don’t call them, they call you.

It also used to be that some of the trivial examples above (e.g., drive with joysticks, do-something-while-button-is-held) required an entire Command subclass file and the associated hierarchy dive, but with the Command rewrite in 2020 and lambda expressions, these can be condensed down to a line or two in RobotContainer.

7 Likes

The Command based model is great. It’s a great way to breakdown the robot and organize the code. It definitely helps isolate the code for individual mechanisms so that different things don’t step on each other. I’d recommend a full read-through of the WPI documentation as it does describe how Commands and the Scheduler work, which I think is helpful to understand how to build your commands properly. The convenience features section has a lot of good stuff that can drastically reduce the amount of code needed, too…so that part is especially worth checking out if lines of code is a particular concern.

3 Likes

We do not spend a lot of time on programming. We used command based. It’s great

Make every action a command, the autonomous is basically just listing commands you want your robot to do. Super simple

4 Likes

But is this really better?

There are plenty of examples in coding where “concise code” is actually detrimental. It can obscure the actual mean of the code. Take the ternary operator for example. It significantly increases the complexity while decreasing the readability. Command-based directly attempts to make code more declarative in nature, and inline commands have a tendency to detract from that.

While it will lead to some boilerplate code, how much is there really? Due to the nature of inheritance, you can delete any unused methods. The classes end up being quite small, but their names can clearly show what they are doing.

Inline commands also discourage the principle of DRY (Do not Repeat Yourself). If your inline command is needed anywhere else it must be rewritten as opposed to referencing the object. While one could argue that if the inline command is needed in multiple locations it can then be written as an object, this requires developers to know that the inline command exists else were. This opposes the idea that commands should be organized in a location that is easily found.

Sometimes inline commands can be useful for testing, but if you choose to keep that test mode around for long periods of time, the inline command slowly leads to code rot over time. It will promote more use of inline commands, which will further rot your code.

Due to added difficulty in reading, opposition of DRY, and detracting from declarative programming paradigm, I do not recommend using inline commands.

9 Likes

Inline commands via factories in the subsystem is how my team has done it for a few years now, with much success. It avoids the problem with DRY that you’re talking about, by making the primary interface to a subsystem be the commands themselves.

7 Likes

Intriguing, would you be willing to share a github repo that has an example? If so, that would be awesome!

1 Like

Though not necessarily the paragon of good design (or good formatting, something I’ve been meaning to play with), this is the most recent iteration using our wrapper library:

I suggest looking at subsystems/Intake.java and Robot.java itself. It does differ from the WPIlib convention of “Robot/RobotContainer separation” mainly because developed this library and style before they were separated, and the inheritance suits our needs well enough.

2 Likes

I’m sure the Model that don’t follow the FRC docs examples are great, and I will look deeper into these after posting here. I do caution that when you steer into your own convention you become you own support.

It is my experience that here in FIRST our programmers are a revolving door or NOOBS. With there young programmers I feel it is important not to take the shortcuts (Single line If statements, inline commands, and the others) but to make the code clear and obvious.

I do agree that the Command Classes often are bloated with unused methods and could be better constructed, but when I’m attempting to help a team diagnose why their system is not running and I come into a Runcommand(() → someGetter, … , subsystem) adding the bread crumbs to trace the data becomes difficult.

But having a button.whileheld( new Command(parameters…, subsystem)
then a command with initialize(), execute(), isFinnished(), end() that call a method in the controlled subsystem its clear and obvious the intent of the control and value sources.

To answer the OP
Using it? YES
Thought? Start out using the FRC docs, examples, and use RobotBuilder to get the core structure of the code

3 Likes

Here’s what we use. Here you can see how the factories include creation methods for very basic commands as well as more complex command groups built from those.

1 Like

+1 for this.
It is much readable and easier to code to have

button.whenPressed(new DeployIntake(intake));

than

button.whenPressed(new InstantCommand(() -> intake.setDeployedState(true)));
1 Like