Hello-- I’m the programming mentor for 3018 Nordic Storm, and our programming team this year is new to java. But here is how we approached it.
What you are asking should be doable in 7 lines of Java written and about 10 labels provided to Robot builder and about 7 items added to robot builder, 2 chekbuttons pressed and 6 dropdown selections made in Robot builder.
Very Briefly
In Robot builder:
- Start RobotBuilder
- Specify project name and team number (e.g. MyRobot and 1234)
- Add Subsystem named DriveTrain
- Add ‘Robot Drive 4’ controller to DriveTrain
- Add 4 Speed Controllers to RobotDrive41, label them lf,lr, rf, rr
- If known, set port numbers for speed controllers, otherwise wire up according to how they were added in RobotBuilder
- Map lf to Left Front Motor, lr to Left Rear motor, etc in RobotDrive41
- Invert motor direction for Right Motors
- Add Command named DriveWithJoystick
- Set DriveWithJoystick to require DriveTrain
- Set DriveTrain Default Command to be DriveWithJoystic
- Add Joystick to Operator Interface
- Press Java --> Code will appear in c:/users/<username>/workspace/MyRobot
In Eclipse:
- File > Import…
Open General folder
Choose Existing Projects Into Workspace
Choose path specified in last step
- In MyRobot > src > org.usfirst.frc1234.MyRobot.commands > DriveWithJoystick.java file, change existing execute() to this:
protected void execute() {
double mag=Robot.oi.getJoystick1().getMagnitude();
double dir=Robot.oi.getJoystick1().getDirectionDegrees();
double rot=Robot.oi.getJoystick1().getTwist();
Robot.driveTrain.mecanumDrive(mag,dir,rot);
}
- hover over word ‘mecanumDrive’ in last line in previous step and select option to 'Create method 'mechanumDrive(double,double,double) in type ‘Drive Train’.
- In MyRobot > src > org.usfirst.frc1234.MyRobot.subsystems > DriveTrain.java file, change newly added mecanumDrive() Method to:
public void mecanumDrive(double mag, double dir, double rot) {
robotDrive41.mecanumDrive_Polar(mag, dir, rot);
}
Above is essentially how I led our programmers through the task of programming our mecanum chassis. There may be some typos, but it is relatively complete
Here is the above sequence with a lot more details
- after eclipse is up and installed and configured for wpilib, Select the menu WPILib > Run Robotbuilder
- provide your team number and a name for the project when robot builder opens
- if you use the default workspace no mods should be required-- otherwise click on the root of the robot tree and specify the path to your eclipse workspace
- Right click on Subsystems and add a subsystem. Name the subsystem DriveTrain
- Right click on DriveTrain in the tree and Add a controller-- add the Robot Drive 4 controller. Robot Drive is a java class with lots of very useful code for driving robots with a variety of drive trains.
- Right click on the new Robot Drive controller (default name will be Robot Drive 4 1) and either Add SPeed Controller or Add CAN Jaguar, whichever is your case. We used Talon SR’s in our first attempt so we added Speed Controller
- Repeat last step 3 more times and rename the 4 speed controllers to lf, rf,rr,lr
- Set the PWM port (Or can port if you are using Talon SRX or CAN Jag) for your controllers per your wiring, or choose the defaults and tell electrical what how to wire it.
- click on Robot Drive 4 1 and set the value for ‘Left Front Motor’ to lf, repeat for ‘Right Front Motor=rf’ and ‘Right RearMotor=rr’ and ‘left Rear Motor=lr’.
- Because your motors on the right have the shafts facing right a clockwise spin will run the wheels to drive the robot backwards while the shafts facing left have clockwise turn roll the robot forward, you must invert the motors on the rigght, so check the ‘Right Rear Motor Inverted’ and the ‘Right Front Motor Inverted’ boxes
- You need a command to drive a subsystem, so add a command (right click on Commands in tree and Add Command) and name it DriveWithJoystick
- This command needs to use your DriveTrain subsystem, so set the value of ‘Requires’ on your command to require DriveTrain. You don’t need a button on smart Dashboard, but it matters not if it is checked or not at this point.
- You will need a joystick, so right-click on Operator Interface and Add Joystick. Accept the rest of the defaults
- With the CommandBase robot pattern that RobotBuilder uses, you must schedule a command to run, and the easiest way to schedule our DriveWithJoystick command to run is to make it the default command for the DriveTrain. This means that it will be scheduled by default. Visit DriveTrain and set the ‘Default Command’ to ‘Drive With Joystick’
**At this point we’ve fully described our robot to Robot Builder. **
- Now press the ‘Java’ button in the toolbar at the top. This will create an entire eclipse project for you under the name you named the project inside your eclipse workspace.
- in Eclipse do File > Import…, open General branch for types of imports, select 'Existing Projects into Workspace,
- on import projects dialog, click browse for ‘Select root Diretory’ and go to your new project-- (most likely going to be c:/users//workspace/ )
- when it opens you have a few hundred lines of java already created for you by Robot builder. We now have to write about 10-15 lines of code to finish it off.
- so we set up a command named DriveWithJoystick. That command was set up to require DriveTrain Navigating in Project Explorer to > src > org.usfirst.frcNNNN..commands we can find our DriveWithJoysticks java code. A command has standard entry points
A constructor-- called any time you make a new DriveWithJoystick()
initialize()-- called once when scheduled
isFinished()-- called once every time through teleopPeriodic() to see if it is done
execute()-- called once every time through teleopPeriodic() after command only if command as returned false to isFinished()
end()-- called once before the command is removed from schedule after reporting true to isFinished()
interrupted()-- called once when another command requiring the same subsystem is scheduled.
So for a default command that will never end of its own accord (as long as telop is running, we want to be able to control the robot with the joystick), we simply return false from isFinished() and this is the default, so no code required.
No initialization for this command is required.
for execute() we would like to read some values from our joystick and tell our drivetrain to drive using those values. So we add 4 lines to execute so it ends up looking like:
protected void execute() {
double mag=Robot.oi.getJoystick1().getMagnitude();
double dir=Robot.oi.getJoystick1().getDirectionDegrees();
double rot=Robot.oi.getJoystick1().getTwist();
Robot.driveTrain.mecanumDrive(mag,dir,rot);
}
If you begin typing ‘double mag=Robot.’ as soon as you press the period, you will get a popup of all functions and fields on the Robot object and you will see oi as an option. When you press the period after typing or selectin oi, you will see a list of all the functions and fields available on oi-- including a function getJoystick1() and after choosing that and pressing period again you can find the getXXXX() functions.
TIP: Any time you need something, you can start by typing ‘Robot.’ and seeing what is available or sometimes you may need ‘RobotMap.’ and sometimes it helps to start with ‘this.’ to see what you might have inherited from your base class.
-
so in the previous step, you will see red undermarks under the function name mecanumDrive on the last line we wrote for execute(). This is because we’ve said to call a function that has not yet been written. But thinking top down, we know we need to call some mecanumDrive function and pass it the joystick direction, how far it is going and how much we are twisting it (assuming an Extreme 3d joystick). So we wrote it even though it does not yet exist. Eclipse has a nice ‘quick fix’ feature when you hover over a word with red undermarks. So if we do this on ‘mecanumDrive’ we are given the option to 'Create method 'mechanumDrive(double,double,double) in type ‘Drive Train’. Select this and it will write an empty method for you over in your DriveTrain.java file
-
Now visit DriveTrain class definition under the subsystems (open src branch, then open org.usfirst.frcNNNN..subsytems branch in the project explorer to find DriveTrain java). In this class you can see you have local fields for your robotDrive41 as well as your motor controllers lf, rf,rr,lr.
At the bottom of this, you should find
public void mecanumDrive(double mag, double dir, double rot) {
// TODO Auto-generated method stub
}
because of the quick fix we applied in the last step.
Now we need to write code for this method. As mentioned in the TIP last step, we can always type a name followed by a period to find out what we can do with it. In this class we have a RobotDrive object named robotDrive41 so if we start by typing robotDrive41. we will see two very useful functions for a robot with mecanum drive-- mecanumDrive_Polar and mecanumDrive_Cartesian. Since we got Magnitude and Direction from the joystick, the Polar form is appropriate so all we have to do in our method is this:
public void mecanumDrive(double mag, double dir, double rot) {
robotDrive41.mecanumDrive_Polar(mag, dir, rot);
}
- right click on the project root node in the Project Explorer (e.g. right click on the word MyRobot if that is what you named your project), select Run As > WPILib Java Deploy
Hopefully this will prove helpful. There are also some great YouTube videos about RobotBuilder published 2 years ago here: https://www.youtube.com/watch?v=k7PaYcjDEDc&list=PLYA9eZLlgz7t9Oleid2wtlgnvhGObeKzp that I recommend to understand the wonderful CommandBase architecture provided by WPILib.
It takes a bit of skill to properly use CommandBase-- choosing your subsystem functionally that you expose is key. Only using commands to access that functionality of your subsystems is key.
For example, say you have a switch in a toteCollector subsystem that lets you know when one tote is loaded.
You could write a method called bool getToteCollectorSwitchState(), or worse DigitalInput getToteCollectorSwitch() but you will find it it quickly tedious to use. The whole point of a subsystem is to hide details and expose only those things that you deem commandable or askable. Instead consider the approach
bool isTote1Loaded() {
}
Now the switch is hidden from the subsystem’s external callers-- they don’t need to know how the switch works in the subsystem, nor should they care. What they need to know is if the tote is loaded. This gives your subsystem flexibility to change to a light sensor or a pressure sensor instead of a digital switch, but any commands that need to know when tote #1 is loaded won’t need to be changed.