Our team would like to be able to switch between control schemes (for the drivetrain) as a way to easily find out what our potential drivers are most comfortable with. There’s also potential situations on the field where we might want to switch to tank for a maneuver with the push or click of a button. The problem is, I really have no idea how to translate this into code. Some of the stuff is obvious, such as using a SendableChooser, but the rest is questionable. I have a few ideas, but no way to test them since our cRIO died and we’re awaiting a replacement.
We’re using command-based, fyi.
To the teams that have done this before: how did you do it?
And to people who haven’t: what do you feel is the best way to accomplish this?
Assuming you use Java (I deduced from your previous posts), the best OOP way to do something like this is to attach something of a “Drive” class to the SendableChooser, and have “TankDrive”, “ArcadeDrive”, etc. classes to represent the different driving techniques. With this you can just have an abstract method like “drive(Joystick)” and call that (based on the SendableChooser or something like that).
Otherwise, you can use a state machine do the work for you, and have a Smartdashboard (or Preferences - which would be better suited) field called driver type. (We did that last year, worked out fine)
I can’t say anything about the command structure, but in LabVIEW and C I’ve done similar things several times. Here is how I did it:
In LV, I created blocks to calculate each of the possible left/right pairs then a Switch to pick the correct one (this is approximately equivalent to an If and Else in C), using a boolean latch to store the chosen state. The code in LV was approximately equivalent to an If and Else in C (actually, I believe you would need 3 Ifs and 1 Else to do the entire thing including boolean latch). Other variations required a single switch to be held to switch modes. All of this would be wrapped into its own block which accepts the HMI data struct and outputs a drivetrain pair struct. I could drop in other blocks at will with minimal issue.
For a similar system of more than two modes, in LV or C, I would likely create an enumerated typedef to store the currently selected mode, calculate all possible pairs (which are stored in a pair struct) and shove them into an array, and index the array by the enum. The enum would be selected using a similar stack of LV Switches per button. The single Switch is slightly more elegant for a 2-choice solution than an array with index.
As for the lack of cRio, I like testing code on my laptop on a software test harness. Usually in LV I just take the core VI and write a computer-target VI to simulate the IO and display the output, it seems like it would be significantly harder to do in C++ or Java, but still doable.
In all cases that I have used, I was simply reversing the drivetrain, to drive backwards with trigger. We don’t do that any more with the Halo drive, since you only have to move one thumb stick instead of two full-sized joysticks.
While it’s perfectly fine (and highly encouraged) to experiment with multiple interfaces prior to heavy driver training, I highly recommend removing this functionality from the competition code so you don’t confuse your drivers more. A well tuned driver input algorithm should not require any additional driver input, or be significantly modal.
As for the command-based implementation, I can’t help there. I generally do embedded systems work in Simulink, we use state machines and lookup tables rampantly.
I’m not a programmer, but you may want to take a look at 254’s 2012 code. They had different drive modes(the two notable teleop ones being Cheesy Drive and and a drive lock). It is a different purpose than what you are looking to do but I’m pretty it was built so that it is easily repurposable.
We did it previously using a simple toggle button. Press A on the controller and front is front, press A again and now front is back.
Code wise, it’s simply a logical variable that toggles every time you hit a button. (logVar = !logVar).
Implementation wise, this is perhaps a wise decision for testing, but not as useful to swap control schemes during actual matches, unless of course, your drivers are super used to it.
public void switcher() {
driveSwitch = new SendableChooser();
driveSwitch.addDefault("Joystick Arcade (1)", new JoystickArcade());
driveSwitch.addDefault("Joystick Arcade (2)", new DualJoystickArcade());
driveSwitch.addDefault("Joystick Tank", new DualJoystickTank());
driveSwitch.addObject("Kinect", new KinectCommand());
SmartDashboard.putData("Drive Switch", driveSwitch);
}
This code would allow it to switch, but it would require switching modes beforehand. My question is, how should I set it up to be able to switch modes on the fly? I feel like calling driveCommand.start() over and over in testPeriodic wouldn’t work so well, but I have no idea. How should I check the current state of the SendableChooser against the current command running?
If you want that sort of functionality (on-the-fly) there are three options I can think of.
Update scheme periodically (lots of bandwidth - we did this last year and it was a bad idea)
Making a dashboard static widget that sends data through NetworkTable to the code and have your code adjust only when that data is sent. (I wouldn’t know where to start on this kind of implementation - if you figure it out, PM me. We’re looking at doing this)
Have a button on the joystick change schemes (ex. back button on xbox controller). No extra bandwidth required. (well, minute compared to smartdashboard)
My recommendation for less work though it to have it update every time teleop starts. It may cause some frustrations testing, but during competition I would hope you don’t switch schemes mid-match.