Skip to content

Coding Your First Robot

Boomaa23 edited this page Jun 3, 2020 · 4 revisions

Coding Your First Robot

This tutorial assumes you've already installed and setup the GradleRIO-Redux-example project.

Creating a robot using GradleRIO-Redux is fairly easy. Try to write as much of the code as you can on your own, rather than copy and paste, even if you're just retyping it.

You should also take a look at the robot code from 2017's preseason, available here: PS2017KB-Red. The layout of the code is very similar.

Our end goal is to create a robot that moves forward when a button is pressed on a joystick. This is a pretty simple task, but it will cover most of the basic concepts we use.

Note: Interfacing with a robot requires the use of WPILib. This is included in GradleRIO-Redux by default, so there's no need to install it, but many of the concepts here are from WPILib. WPILib for 2020+ is very different than earlier versions, so some available information may be outdated. If you need more help on the 2020+ system, the WPILib documentation is available here.

In the code samples below, do not copy any line starting with //. These are called "comments", and do not affect the code itself. They are used to document the code, or in this case, indicate that some snippets of code are not full classes: // ....

If at any point you have questions or need help, ask in #sw_general on Slack!

Creating the Robot Class

The Robot class is the entry point for everything that will be done. It must be placed in a specific location to be created & called by WPIlib, src/main/java/org/rivierarobotics/robot/Robot.java.

The first step to making a robot class is to make sure your GradleRIO-Redux project is imported into IntelliJ as a new project. To do this, click the open button on the welcome page and select the folder enclosing your GradleRIO-Redux example:

Open Project

Once opened, you will notice the Project Tools Window on the left-hand side of your screen. This will show you all the files associated with your project. While there are a lot of them, we will be focusing on the src directory, as that is where all robot code will be stored (as .java files).

Project Tool Window

The src directory is already laid out for you in the example project. The directory src/main/java is the "source root", meaning that every Java package and class is enclosed in it. Note that a package called org.rivierarobotics.robot is actually in the directory src/main/java/org/rivierarobotics/robot/, but IntelliJ helpfully squashes down the directory tree for you.

If you expand the packages all the way down, you'll notice that the Robot Java file has already been created for you. However, it's still very important to understand how to create new class files, and thankfully doing so is very easy in IntelliJ. Simply select anywhere inside your "source root" and right-click. Mouse over New and then click Java Class.

New Java Class IntelliJ

Name it whatever you need (if you were making the robot class, you'd call it Robot) in the following dialog box and press enter.

If you need to put it in a package, create a package first by clicking on Package instead of Java Class. This needs to be in a dotted form, where each dot represents a / in a file structure.

For example: org.rivierarobotics.robot turns into org/rivierarobotics/robot/

Congratulations! You've created the first class required to run a WPILib robot.

The file should have text very similar to this inside (if it doesn't, check that you did all the steps correctly):

package org.rivierarobotics.robot;

import edu.wpi.first.wpilibj.TimedRobot;


public class Robot extends TimedRobot {

}

Creating the DriveTrain and DriveSide Classes

Following the steps above, create two more classes called DriveTrain and DriveSide. Do not change the Superclass as above, we want to leave it as java.lang.Object.

You should have two new files, DriveTrain.java and DriveSide.java with content similar to this:

package org.rivierarobotics.robot;

public class DriveXXXXX {

}

These two classes will hide the fact that we have 2 sets of 2 motors. If we were to ever change the robot to use 3 motors, or have a triangular base instead of a square, we want to avoid making any changes elsewhere. The only changes that would be made are to these classes, and Robot would remain the same.

To actually implement these classes, take a look at how the preseason code does it. There is a left and right DriveSide in the DriveTrain class. The constructor (public DriveTrain() { ... }) creates the two DriveSides. setPower() { ... } simply passes each power to the left and right sides. Don't worry about writing getPositionInches or setArcade, we only need setPower for now. DriveTrain should have a way to access the two DriveSides separately, such as in two instance variables (fields).

DriveSide can be done like the preseason code but without the encoder class. Try something like this:

package org.rivierarobotics.robot;

public class DriveSide {
    private final WPI_TalonSRX motor1;
    private final WPI_TalonSRX motor2;

    public DriveSide(boolean invert) {
        if (invert) {
            motor1 = new WPI_TalonSRX(1);
            motor2 = new WPI_TalonSRX(2);
        } else {
            motor1 = new WPI_TalonSRX(3);
            motor2 = new WPI_TalonSRX(4);
            motor1.setInverted(true);
            motor2.setInverted(true);
        }
    }

    public void setPower(double pow) {
        motor1.set(pow);
        motor2.set(pow);
    }
}

Note: Brushless motors using Spark MAX motor controllers should use CANSparkMax instead of WPI_TalonSRX, which is only for Talon controlled brushed motors. Likewise, Falcon 500 motors should use the WPI_TalonFX. Most method calls are the same, including all those made in this tutorial.

Note: In 2018+ versions of WPILib, CANTalon has been changed to WPI_TalonSRX. This is why the code here uses it, while the preseason code does not.

This works because we typically have two sides of the robot, left and right. As such, we need to tell the robot that there are a number of motors on each side (the constructors for WPI_TalonSRX) and provide a way to activate those motors (the method setPower(double pow)). The two sides are identical, so instead of creating a DriveSideLeft and DriveSideRight we can simply create two instances of the same DriveSide in DriveTrain.

The only difference between the two is that one side must be inverted from the other. A motor goes a specific direction when provided with power, and if the motor is physically flipped, it will rotate in the opposite direction when provided with the same power. As such, we use the setInverted(boolean invert) method on each WPI_TalonSRX motor controller to tell the motor to rotate the opposite direction given the same power input (via setPower(double pow)) and as such create movement overall in the correct direction. Inversion state is robot-dependent, so just pick one side to invert and test it.

Make sure you have no red underlines in your code, which indicates that you wrote something incorrectly and must fix it. If you don't know what is wrong, ask on Slack.

Preparing Everything in Robot

Now that we have the DriveTrain class, we can create a new DriveTrain in Robot to actually move the robot. We will add two fields to Robot, inside the existing curly braces (Robot { ... }):

// Robot { ...

private final DriveTrain driveTrain;
private final Joystick joystick;

// ... }

These will hold the joystick and driveTrain after we create them. We want to do this when the robot is first started, so we will override robotInit(). We can do this by placing this code after the fields we wrote above:

// Robot { ...

    @Override
    public void robotInit() {
    }

// ... }

The @Override indicates that we intend to override something from TimedRobot, our superclass. This makes it so we can run code when the robot starts.

We want to create our joystick and drive train here, so we put the following code inside robotInit()'s curly braces:

// robotInit() { ...

    driveTrain = new DriveTrain();
    joystick = new Joystick(0);

// ... }

The Joystick class makes the buttons and axes of a physical joystick available to our robot. We put a 0 because we want to use the physical joystick available on the Driver Station computer's USB port 0.

Now that we have everything set up, we can write the code to drive our robot.

Detecting Joystick Input & Moving the Robot

We need to override another method from TimedRobot: teleopPeriodic(). This method is called repeatedly while the robot is in "teleop" mode. Again, we override methods like so:

// Robot { ...

    @Override
    public void teleopPeriodic() {
    }

// ... }

We then put some simple code inside of this method's curly braces to check if the button we're using is pressed, and move the robot if so:

// teleopPeriodic() { ...

    // Check if Button 1 is pressed:
    if (joystick.getRawButton(1)) {
        // Set power to the drive train:
        // `0.2` means 20% power.
        // `0.0` would mean `0%`, and `1.0` means `100%`.
        driveTrain.setPower(0.2, 0.2);
    } else {
        driveTrain.setPower(0.0, 0.0);
    }

// ... }

With this short code inserted into teleopPerodic(), the robot will now move forward while Button 1 is held down on the joystick.

Notes

Most of the code was provided for this tutorial, but you should take the time to understand what each line does and what each symbol means. If you don't, you're doing busy work! Use the simple code offered here to begin thinking about how you might add another button to go backwards, or spin around. Come up with your own ideas for what you want the robot to do, and try to add them. Ask questions and find the meaning of anything you don't understand. These are some qualities that make for excellent programmers.

If you'd like a working sample of this "basic" robot, an additional repository is available here. Please note there are some more advanced concepts included there, but the structure should remain the same. There are some integrated javadocs as well.

The full code for this small project is available at Tutorial-CodingYourFirstRobot.

Previous Tutorial | Next Tutorial

Clone this wiki locally