Skip to content

Tutorial 01: Getting the robot to drive

James Hagborg edited this page Dec 17, 2018 · 1 revision

Getting the robot to drive is the FRC equivalent of "hello world." For now, I'll focus on getting things up and running, and getting familiar with how a project is laid out, without sweating what every last bit of syntax means. Once we're comfortable with that, we can look at how things work in general. If I reference a term or idea that I don't expect you to know, I'll say "whatever that means" to let you know.

If you haven't already, clone the quickstart project. Compile it and make sure you can deploy it to the robot. You'll know it's working when... absolutely nothing happens. Ok, but make sure you at least aren't getting any error messages yet.

In this project, look in the src directory, in the org.usfirst.frc.team69.robot package. There are three files already:

  • Robot.java: This is the "main file" of the project. Unlike in a desktop program, you don't have to write a main() method. Instead, you override the methods in Robot. You won't be messing with this file much, except for when you need to add a new subsystem (whatever that means).
  • OIMap.java: OI stands for "Operator Interface." This file describes how joysticks and buttons are layed out. As of right now, it should be empty, save for a few comments.
  • RobotMap.java: This file lists what hardware is connected to the robot, and what port everything is on. This should also be empty.

To get the robot to drive, we need to do a few distinct things:

  1. Tell the code about the motors (what ports? what kinds?)
  2. Tell the code about the joysticks (what ports? what kinds?)
  3. Create a subsystem (whatever that means), consisting of the drive motors.
  4. Create a command (whatever that means), which checks the value of the joysticks and sets the speed of the motors accordingly.
  5. Set the command we created to always run.

If this seems like a lot, don't worry! This is on the level of "to make a peanut butter and jelly sandwitch, you need to open the jar, then stick the knife in, then scoop some peanut butter onto the knife, and so on."

0. Find out what ports the motors/joysticks are on, and what type they are

You might need to ask someone for help with this. It could be four distinct ports for each wheel, or it could just be two, for left/right. Even if there are four motors, and four speed controllers, we may use a "Y" PWM cable, to control it from just two ports on the robot.

1. Tell the code about the motors

In RobotMap.java, add the following code (adjusting for what you found in step 0, of course):

public class Robot {
    public static class Drive {
        @Port(type = Type.PWM) public static final int LEFT_MOTOR = 0;
        @Port(type = Type.PWM) public static final int RIGHT_MOTOR = 1;
    }
}

If you're not used to putting classes in classes, or annotations like @Port, fear not! This file is just a big list of constants. The nested classes are just to organize things, like folders on your hard drive.

Also, after this paragraph, I'm not going to mention import statements, but they are something you need. In Eclipse, Ctrl-Shift-O will automatically set the imports to what they need to be. Get in the habit of doing this frequently.

2. Tell the code about the Joysticks

Now head over to OIMap.java, and add the following code:

public class OIMap {
    @MapJoystick(port = 0, role = Role.LEFT_DRIVER, type = Type.LOGITECH_2_AXIS)
    public static class LeftDriver {
    }

    @MapJoystick(port = 1, role = Role.RIGHT_DRIVER, type = Type.LOGITECH_2_AXIS)
    public static class RightDriver {
    }
}

Again, the numbers you put for port, and the value of type, may differ, depending on your situation.

3. Create a drive subsystem

Create a new class, DriveSubsystem, in the org.usfirst.frc.team69.robot.subsystems package. This class should extend subsystem. Add some objects corresponding to the motors on the drivetrain. The exact class depends on the type of motors you're using. Add a RobotDrive object as well. For example, if I've got two VictorSP's:

public class DriveSubsystem extends Subsystem {
    private VictorSP leftMotor = new VictorSP(RobotMap.Drive.LEFT_MOTOR);
    private VictorSP rightMotor = new VictorSP(RobotMap.Drive.RIGHT_MOTOR);
    private RobotDrive robotDrive = new RobotDrive(leftMotor, rightMotor);
    
    @Override
    protected void initDefaultCommand() {
        // TODO Auto-generated method stub
    }
}

(Pro tip: checking "inherited abstract methods" in Eclipse will do some of this for you.)

We made a new class, but a class is no good without an instance of it somewhere. Add a static instance to Robot.java:

public class Robot extends HYPERRobot {
    public static DriveSubsystem drive;

    @Override
    protected void initSubsystems() {
        drive = new DriveSubsystem();
    }
	
    // ... other stuff not shown ...
}

4. Create a drive command

Head back over to DriveSubsystem.java, and add the following wacky-looking method:

public Command driveCmd() {
    return QuickCommand.continuous(this, () -> {
        robotDrive.tankDrive(Robot.oi.leftDriver(), Robot.oi.rightDriver());
    });
}

Without going into too much detail, we can see this method returns something of type Command (but I haven't told you what that is yet). Writing () -> { ... } lets me pass a block of code as an argument. The QuickCommand.continuous method takes a block of code, and returns a Command that says "keep running this over and over, as long as you can". Inside this block, we use the robotDrive variable we made earlier. We read the current state of the joysticks, and set the motors accordingly.

This looks awkward, but is actually a pretty common pattern for us. If you've ever programmed something with a user interface before, you may have seen something like (made up code incoming):

myButton.whenClicked(() -> { System.out.println("I've been clicked!"); });

5. Set the drive command to run by default

We're almost there! We just need to set the drive command to always run. Remember that initDefaultCommand() method that Eclipse generated for us? Let's fill that in:

@Override
protected void initDefaultCommand() {
    setDefaultCommand(driveCmd());
}

Hit compile and admire your hard work. Make sure to run on blocks first, so you don't destroy someone else's hard work!

Clone this wiki locally