|
|
|
![]() |
|
|||||||
|
||||||||
![]() |
|
|
Thread Tools | Rate Thread | Display Modes |
|
|
|
#1
|
|||
|
|||
|
Impressions: Command Based Robot
So, this year I decided to climb out of my familiar cave and try something new with our robot coding by switching to the (C++) Command Based pattern (this decision was helped by the great presentation from Brad Miller at CMP last year), rather than using my usual home-brewed iterative robot (still C++).
Overall, I am glad I made the switch, because our robot this year is much more complex than past years, and I know I would have struggled with tuning various systems using the 5 analog inputs on the Driver Station dash board; but I've also caught myself longing to just ditch the commands and just code a bunch of GetRawButtons in TeleopPeriodic and an Iterative switch case in AutonomousPeriodic (which I know I can do without wholly ditching the command based pattern). A lot of the problems I've had can be attributed to unfamiliarity with the pattern, but there are a lot of cases where the pattern simply feels to restrictive or requires a lot of added complexity to implement something fairly simple. To the point, I thought it might be helpful/interesting to share what I perceived to be the pros and cons of the command based pattern: Pros: Cons: I think next season (assuming I am still with my team), I am certainly going to use RobotBuilder, SmartDashboard, and LiveWindow, however I don't know if I'm a fan of the Command system as a universal pattern, it certainly works great for special modes and cases, but for general robot operation it seems like it overcomplicates things; and in autonomous, simple sequences are very easy to build, however the command structure starts to feel burdensome when we begin trying to coordinate multiple actions to be time efficient. |
|
#2
|
|||||
|
|||||
|
Re: Impressions: Command Based Robot
One thing that will help with having a ton of similar commands is to try to make your commands as reusable as possible.
We have a DeployArmCommand this year that pretty much covers all of the uses we need. We pass in a boolean if the arm should be deployed or not and a speed at which the rollers should spin. This command can then be used for any situation we need it for. One of the biggest cons so far IMHO with the command based structure is the lack of conditionals for command groups. What we did to get around this was save the state that we need from a previous command in that command and pass the command in to the next command through the constructor. That way we can access the state of the first command after it has finished executing. Overall we have very much enjoyed using the command based structure as I believe it is a great tool to teach OOD. |
|
#3
|
|||
|
|||
|
Re: Impressions: Command Based Robot
One of the things we have realized that help cut down on the "weird and goofy commands" or commands that rely on each other is that a command only needs to "require" Subsystems that it intends to write to or change. If you're using a Subsystem in a read-only capacity you should be able to use it anywhere you want anytime you want.
|
|
#4
|
|||
|
|||
|
Re: Impressions: Command Based Robot
Quote:
If you build this functionality into your command, then does that simply leave your subsystem as container classes for actuators and sensors? |
|
#5
|
|||||
|
|||||
|
Re: Impressions: Command Based Robot
Quote:
If you'd like I can send you a presentation that our team did for Command Based programming, it's for Java, but a lot of the concepts should be the same. It is very basic though, so most of it is probably going to be at a lower level than you'd like. |
|
#6
|
|||
|
|||
|
Re: Impressions: Command Based Robot
Quote:
Code:
addSequential(new DropIntakeCmd()); addSequential(new FireShooterCmd()); This sort of construction allowed us to write our sequences and autons via flowcharting on a chalkboard. However, after attempting CommandBase last year with C++ I can say that it was a bit more of a pain. Java is much much cleaner in my opinion. Codebase for reference: https://github.com/FRC125/NU14 |
|
#7
|
||||||
|
||||||
|
Re: Impressions: Command Based Robot
Quote:
We've ended up putting Safety logic in the subsystem, so that everyone is guaranteed to use it, but put Business logic in the command. |
|
#8
|
|||||
|
|||||
|
Re: Impressions: Command Based Robot
Quote:
Also, I cannot wait until next year when we can use ENUMs to send in different states to subsystems and commands. |
|
#9
|
|||
|
|||
|
Re: Impressions: Command Based Robot
As an example of what I mean by business logic, here is the bulk of code from our collector:
We need to know the state of the collector when we control our arm (the two can collide while the collector is up). So having the collector subsystem know it's state seems like the appropriate way to manage this. Code:
void Collector::MoveCollector(bool extend)
{
if (extend == true)
{
if (collectorLifter->Get() != DoubleSolenoid::kForward)
{
timeTravel.Reset();
timeTravel.Start();
}
collectorLifter->Set(DoubleSolenoid::kForward);
}
else
{
if (collectorLifter->Get() != DoubleSolenoid::kReverse)
{
timeTravel.Reset();
timeTravel.Start();
}
collectorLifter->Set(DoubleSolenoid::kReverse);
}
}
Collector::CollectorState Collector::GetState()
{
if ((timeTravel.Get() >= TIME_TRAVELING_UP) && (collectorLifter->Get() == DoubleSolenoid::kReverse))
{
return UP;
}
if ((timeTravel.Get() < TIME_TRAVELING_UP) && (collectorLifter->Get() == DoubleSolenoid::kReverse))
{
return TRAVELING_UP;
}
if ((timeTravel.Get() >= TIME_TRAVELING_DOWN) && (collectorLifter->Get() == DoubleSolenoid::kForward))
{
return DOWN;
}
if ((timeTravel.Get() < TIME_TRAVELING_DOWN) && (collectorLifter->Get() == DoubleSolenoid::kForward))
{
return TRAVELING_DOWN;
}
return IDLE;
}
Last edited by DjScribbles : 26-02-2014 at 16:21. |
|
#10
|
|||
|
|||
|
Re: Impressions: Command Based Robot
Quote:
1. Don't test Booleans against true, just say "if (extend)" 2. Up your abstraction in your subsystem a bit -- for example, instead of saying Code:
collectorLifter->Get() != DoubleSolenoid::kForward Code:
collectorLifter->IsForward() Code:
collectorLifter->MoveForward() We agree that state is best maintained in the subsystem, not a command. I think it's easier to treat commands as transients. A final note -- we would use some sort of sensor (limit switches, encoders, or magnetic reed switches on the air cylinder) to detect the position of the actuator, rather than using timing, particularly in cases where the actuator's position can interfere with another part of the robot. If you look at my other post in this thread, there's an example of the supervisor command pattern that I really like for subsystems that require regular observation to work -- your subsystem qualifies in that you have the transient states where it is moving from one goal state to another. |
|
#11
|
|||||
|
|||||
|
Re: Impressions: Command Based Robot
Quote:
Quote:
|
|
#12
|
|||
|
|||
|
Re: Impressions: Command Based Robot
We use LabView but try to do the same thing. We categorize the behaviors we want the robots to perform. One advantage of this is we typically have some pretty complex autonomous & teleop routines and this makes it easier to develop both.
2012 Rebound Rumble Routines (for example) 1) Ball Management - Statemachine manages the inventory and location of balls in the robot, ensuring we never went over the limit and that all balls were under positive control at all times. Balls were automatically routed through the system to provide a ball as soon as possible to the shooter. 2) Shooter Management - Managed Shooter RPM and inhibited firing if RPM was not within tollerance 3) Shooter Targeting - Recieved location of target and automatically moved the turret to target 4) Ball Detection (implemented after season) - Could find balls automatically and provide the location to the drive system for pickup 5) Driver System - Could take the location of balls and drive towards them until they were picked up to be handled bythe Ball Mangement System This could be considered a "System of Systems" (groan all you want). Each subsystem handles it's task and is connected (as loosely as possible) to others. It's discrete portions of code instead of monolithic items. We're working on re-implementing this years code in a similar manner. It also makes it easier to maintain/troubleshoot. |
|
#13
|
||||||
|
||||||
|
Re: Impressions: Command Based Robot
If you use CommandGroups, the default command won't run until the CommandGroup finishes. However, you do have to think about your end and interrupted methods to make sure they don't do something you don't want when run in a sequence.
|
|
#14
|
|||
|
|||
|
Re: Impressions: Command Based Robot
Quote:
Quote:
This became a problem in autonomous as well, since the spin-up routine didn't end, a seperate command needed to be created to do the same thing, but with a different life-span. (I've since realized that adding this in parallel would have been the better solution, however the nuances of the parallel/sequential weren't clear at the time) |
|
#15
|
|||||
|
|||||
|
Re: Impressions: Command Based Robot
Quote:
Quote:
Quote:
Code:
addSequential(new SpinUp(speed)); addSequential(new DeployDisk()); Quote:
Code:
// Does nothing, and keeps doing it forever
public class DoNothing extends Command {
public DoNothing() {}
protected void initialize() {}
protected void execute() {}
protected boolean isFinished() {
return false;
}
protected void end() {}
protected void interrupted() {}
}
Quote:
|
![]() |
| Thread Tools | |
| Display Modes | Rate This Thread |
|
|