Recently my team’s controls mentor and I have been working together on a set of procedures and tools to teach our 100% rookie programming team before I head off to college. (I’ve been the sole programmer for four years and he’s only been with us for two, so we’re basically starting from scratch.)
In the process, we’ve been looking into the Command System, and in tweaking it to fit our needs I’ve come across a few roadblocks. Could any of you guys help clarify them?
Subsystems vs Commands: automated logic
For many of our mechanisms, we have some sort of automated control system that runs periodically - but we sometimes want to manually control the mechanism instead.
Is the proper solution to have said automation in the Subsystem’s
periodic() method? In a method called by Commands that want it? Or in every Command that uses it? (It’s sort of a separation of concerns problem?)
In one case, we actually had a three-jointed arm that was either manually controlled or automated but the driver was supposed to have the option to manually adjust the position of one of the joints. I’m not sure at all where/how that would be implemented in the Command system.
(There’s actually another related question to this: would an
arcadeDrive() method go in the Subsystem or a Command? I’m not sure every caller wants the arcade drive method, but then again I still use drive methods that only work when PID is enabled.)
Command structure: how and when?
One thing I believe in is consistency - a Command shouldn’t be tightly coupled to the implementation of the Subsystem it runs on. If so, then say I have a distance-based drive command for autonomous - how does it know to stop without accessing the drive encoders?
Where this becomes a problem is any “state” based automation system, like a lift with multiple positions or (sighs) the three-jointed arm. If the automation is done inside the Subsystem, then these Commands would only be able to set the position, and not be able to know when the mechanism’s reached the desired position. (Unless you run the automation in the Command, and then you’re back to the previous dilemma.)
And for that matter, do you really need a Command per state, like an OpenClaw and a CloseClaw Command, or a Command for each position, or can one parameterize them? Don’t we then end up exposing the Subsystem’s parameter type (which I often create an enum like Position for) to the Robot itself, which isn’t good in terms of coupling?
Speaking of states… do Commands start other Commands? I feel like Commands shouldn’t be managing their own state, but then is it Robot’s job to access auxiliary sensors to decide, say, to run a “tip protection drive” command instead of a normal drive command? But isn’t this just IterativeRobot again then?
Ugh, I never liked querying buttons every loop, but the OI templates are frustrating. For the most part you can bind Commands to buttons (though what does
whilePressed() mean? Does it constantly call
initialize()?) but for the things (axes, analog triggers, the POV selector) you can’t.
Does it then violate separation of concerns to also add getters to the OI object exposing those, and passing it into Commands? Is there really no way to avoid coupling the Commands to the driver station layout without resorting to something really complex, like a lambda?
In general, I want to be sure the syntax I’m using is both elegant and reasonable to teach relatively new programmers without compromising the effectiveness of the program. Yes, it’s a tall ask. No, I don’t know how to check myself on this.
For reference, the full code I’m working with is here.
(It’s sort of theoretical and probably won’t correspond to a real robot when it’s done.)
To those who actually made it to the end, thank you!