Can I write my commands in the periodic() method?

If they’re in the subsystem periodic() block, they’re not commands.

It’s entirely valid, for a simple robot, to just run a few parallel state machines, and Subsystem is one convenient way to do this.

You’ll likely find, compared to using the full command-based framework, that doing this results in the need to write a lot of edge-detection logic and other boilerplate that the Command and Button code would otherwise handle for you. For a small, simple project, this is not necessarily an issue. For a larger project, it can become cumbersome.

1 Like

Sounds good. Thank you!

I don’t really have any pros for you. I think the best solution for most robots is to think of a Subsystem as a collection of sensors and actuators, but a system that in and of itself doesn’t do anything “smart”, except it should be self-protective. Such as react to current draw, or the like.

The Subsystem itself, should be actuated through Commands, which gives you both the clarity of what is going on within the robot, the simplicity of designing discrete functionality in small blocks, and the complexity of being able to stack individual commands together using the framework with the schedule arbitrating who has access to subsystem components.

The major con to your approach is losing all the pros that come with Command-Based.

1 Like

Alright. Thank you!

Subsystems can appropriately be a bit smarter than that. As an example for last year, some of the methods for a lift that took in game pieces and also carried a scoring mechanism might have been:

  • gotoCargoIntakeHeight()
  • gotoCargoShipHeight()
  • gotoCargoRocketL1Height()
  • gotoCargoRocketL2Height()
  • gotoCargoRocketL3Height()
  • gotoHPIntake+L1Height()
  • gotoHPRocketL2Height()
  • gotoHPRocketL3Height()

These methods would move the lift to the appropriate height, then terminate. It would still be up to commands to call these methods to move the lift, and composite commands to then do the scoring.

Your post articulates perfectly what I believe the design should be, in fact that is exactly how we do it.

The Subsystem owns all the components that are needed to safely do it’s job (the sensors and actuators), but it’s up to the Commands to ask the subsystem to do things, and to report back the status of said request.

Commands are the objects out there that are performing the actions, and by that I mean, the subsystem pieces (Arm, Intake, whatever) aren’t actually doing much on their own without a command asking it to do the work.

Nothing inherently wrong with doing this, but, typically in Command-Based, especially with the paradigm shifts with the new rewrite, these would be Commands, not methods in the subsystem. The Subsystem would just have a generic goToHeight(double height) type of method with logic to validate input (check allowed ranges and such), and you’d set the setpoint in Commands for the various positions you wanted.

You’d likely do this either by creating a command for each height, or creating one Command that also takes in a height to the constructor. In either case probably pulling the height setpoints themselves from the RobotMap or Constants Class so they are labelled and in one place to be quickly referenced and changed. I’d also argue that the former method would be more common for the prior Command Based framework, with the latter being preferable with the new framework.

Of course, you are the conductor of your own robo-Orchestra!

1 Like

What I try to teach my students is readable code. So in our piece, we’d have a a Command called CargoRocketHeight3, that called subsystem.gotoCargoRocketL3Height(), which would act as a wrapper function around a private subsystem method that would call goToHeight(double height).

The school system where I help doesn’t have really strong computer scientists or programmers teaching the programming and computer science related courses. So we focus on simplicity and basics such that any code written by one student MUST be written in a way that is easily understood and extended by any other student on the team.

I agree with what you said though, do what works for you. When I teach my kids what is the “right” way to do things, there are two criteria: 1) Is it readable code? 2) Does it get the job done?

Nothing else really (greatly) matters for small scale software like these robots.

2 Likes

Like I said, nothing inherently wrong with the approach. More highlighting the approach the framework tends to lean towards, particularly in many of its provided examples.

I will also admit that my goals and the students I mentor’s goals aren’t the same. Theirs is mostly to program a robot and have fun while doing it. Mine is to teach the bare basics of programming, and computer science to prepare them to understand if they want to pursue it as a career or not, and have fun while doing it. If that means Robotics, well the fun part just gets a bit easier! So I do tend to teach from a much broader context than just robotics, with a lot more why and context behind ideas than most. Gotta go make things harder on myself than I need to and all that, right?

For example, I had a blast teaching the basics of Boolean logic diagrams, and how we could use them to mock up Command Trigger conditions from JoystickButtons and other sensor states passed into Trigger/Button objects. Then, translate that directly into our ButtonBindings through the and() or() and negate() methods provided by the Trigger class. Necessary for robots, no, but for a greater understanding of what Computer Science is, absolutely.

OK, I think it’s a language difference. To me, moving to a predefined position using an encoder is a “smart” action. I certainly agree that the subsystem shouldn’t do anything except initialize and possibly maintain itself unless directed by a command or interrupt. As I look at it, the subsystem should manage the “how do we do this basic task” and the commands should tell the subsystem when to do them and build them into composite tasks.

In that situation, I would definitely not have separate methods; the subsystem could expose a setHeight(double) method, and the command could accept an ElevatorHeight enum for readability.

1 Like

The one confusion I have seen when commands appear to be little more than wrappers is that it obfuscates the difference between a command and a subsystem method.

e.g. if I have a subsystem method elevator.goToRocketLevel3(), and a command GoToRocketLevel3 which just calls elevator.goToRocketLevel3(), students start wondering what the purpose of the Commands are. It seems convoluted and redundant initially. It takes some of them a while to realize the other benefits such as the scheduling and the ability to do CommandGroups and such. (I have some students that just learned Java this fall, and it is a rapid acceleration from “hello world” on your PC to an implementation of the command pattern run via periodic function calls on a real-time processor!)

A Command called GoToRocketLevel3 which calls a subsystem elevator.GoToHeight(x) would make that difference more evident. Though I don’t necessarily argue that one approach vs the other is more correct.

1 Like

It took me fifteen years to get used to methods without arguments, and now arguments are in fashion. :disappointed_relieved: But I do like @Oblarg’s idea about using enums (or numeric constants defined by the class).

I’m still trying to figure out all these new-fangled Java expressions. Templates? Lambdas??

1 Like

Lambdas were added in Java 8 in March 2014 - it’s been nearly 6 years since their introduction. Generics were added in Java 5 in September 2005 - that’s older than some students competing on FRC teams this season!

oof. Guess I need to bring out the textbooks.

That also explains it somewhat - at my current job, I’ve been working with Java since 2014, but we’re using Java 7 due to <insert_irrelevant_reasoning_here>.

The large amount of boilerplate required to write a simple command wrapper on a subsystem method was a driving motivation behind the new command framework’s emphasis on inline-able commands (e.g. InstantCommand, StartEndCommand, FunctionalCommand, etc).

For something like goToHeight, if the command does not add any code, it is probably a good idea to simply inline it with InstantCommand. In fact, one often doesn’t even need to define the command:

button.whenPressed(() -> elevator.goToHeight(Elevator.kMaxHeight), elevator);
1 Like

BASH is my favorite language!.. ugh…

I too will agree with this, I like the idea of an enum instead of just the static values in the Constants class.

This method is really good for fetching errors. We currently use it to get motor faults and check if limit switches are pressed, update LED states, etc. It could be used for state-machines as well, but we haven’t really felt like there is an advantage for us to go that way just yet.