Code Structure

Structure of Your Robot Program

It would be wrong of me to say there is one right way of setting up your code. There is not. You can set up your code any way you would like, as long as you can understand the structure and effectively navigate your code. I am going to explain one structure of code that is useful for teams using SampleRobot, non-command IterativeRobot, PeriodicRobot, and MagicRobot. It is the same structure used by 5 time Innovation in Control Award recipient Team 1418 Vae Victis.

Robot/
    robot/
        autonomous/ # In this folder are separate autonomous modes
        components/ # Different components of the robot such as drive, shooter, intake, etc.
        common/ # Parts of the robot that belong to all parts, such as drivers for sensors.
        robot # Main Robot File
    tests/ # Tests for your code. Unit tests are very important.

For command based robots, the structure is pretty similar.

Robot/
    robot/
        commands/ # Commands that cause the robot to perform actions
        subsystems/ # Similar to components in Iterative. Subsystems such as drive, shooter, intake, etc.
        robot # Main robot File
    tests/

RobotMap and OI

A common occurrence in robot code is a file called RobotMap. This file contains constants use throughout the robot. Such constants include motor controller port numbers, button mapping for certain robot functions, and PID constants for your control loops. Many teams use RobotMap for keeping track of constants, but to me it makes sense for constants such as PID to be within their respective component classes, and just having the values in robotInit where you initialize all of your hardware.

OI goes hand in hand with RobotMap, since they both serve similar purposes. The main purpose is for all of your inputs (such as joysticks) to go into OI and the main robot program will call functions from OI. A simple setup will look like this (syntax aside)

teleopPeriodic() # Method inside main robot code file
    if OI.getShooter()
        shooter.spin()



# In OI file
getShooter()
    return Joystick.getRawButton(1)

The use of an extra file for some more readability may be a worthwhile tradeoff. Talk with your team to decide if you want to use this form of structuring your code. Both ways are fine, but the most important thing is to be consistent. It is actually less helpful to only have some of the joystick inputs in OI than to have no OI file at all.

Main Robot Structure

You know the old saying “Cleanliness is close to godliness”. The same goes for programming. Clean code -> God Tier code. The first step to having clean code is to have good organization. Let’s start with robotInit.

robotInit

Note

The following examples show IterativeRobot, however the same logic an be applied to Sample and Periodic.

public class MyRobot(wpilib.IterativeRobot) {

    Joystick joystick1, joystick2;
    Drive drive;

    public void robotInit(){
        joystick1 = new Joystick(0);
        joystick2 = new Joystick(1);
        drive = new Drive(new Talon(1), new Talon(2));
    }
}
class MyRobot : wpilib.IterativeRobot{
    Joystick joystick1, joystick2;
    Talon motor1, motor2;
    Drive drive;
public:

    void robotInit(){
        joystick1 = Joystick(0);
        joystick2 = Joystick(1);
        drive = Drive(Talon(1), Talon(2));
    }

}
class MyRobot(wpilib.IterativeRobot):

    def robotInit(self):
        self.joystick1 = Joystick(0)
        self.joystick2 = Joystick(1)
        self.motor1 = Talon(1)
        self.motor2 = Talon(2)
        self.drive = Drive(motor1, motor2)

Let’s talk about what’s happening in these methods. In the Java and C++ examples, the code starts with declaring the variables in the class scope (outside of any method). This allows the other methods you will use such as teleopPeriodic or operatorControl to have access to your robot components.

Inside robotInit is where we actually initializing the variables. There is no real significance to doing this inside robotInit or when you declare the variables except structure, which is what we’re going for. Also notice how I never created variables for the drive motors. If you aren’t going to use the variables outside of the drive class, there is no need to declare them as variables here. It makes more sense to declare them as variables inside the drive class, where you can customize them (such as setting PID if they are CANTalons, or reversing them if need be).

Note

If you are using RobotMap, this is where the values stored in RobotMap would be used. Instead of joystick1 = new Joystick(0) you might do joystick1 = new Joystick(RobotMap.LEFT_JOYSTICK)

Teleop

Now that you’ve created all of the robot components, we can focus on teleop code. The main basis of teleop code is using if statements to check for input, and the performing some action based on these events. For example

drive.drive(joystick.getY(), joystick.getX())

if joystick.getRawButton(1)
    shootBall()

if joystick.getRawButton(2)
    intakeBall()

if joystick.getRawButton(3)
    climb()

This structure allows for easy configuration of joystick -> action. The drive code shouldn’t involve an if statement, since you always want control over the drivetrain, and you should call the command that drives every loop. You probably want it to be at the top, that way if you have any code that edits the drive (such as angle rotation code) the values will not get overwritten by the joysticks.

Components

The components should be made up of setters, getters, and an execute method. The setters will be used to set variables used in the execute method. A good example is a function move in the drive class that sets the fwd and rot variables. These variables can then be used in the execute method to set motors. In order for this structure to work, it is crucial that the only place motors, relays, etc. get set is in the execute method. This prevents different parts of the robot overwriting each other. Here’s an example of a move function in the drive class.

function move(fwd, rot):
    global fwd = fwd
    global rot = rot


function execute():
    DifferentialDrive.arcadeDrive(fwd, rot)

The reason for the setters setting variables and then an execute method passing those variables to the motors is to prevent ‘race conditions’. Essentially, imagine you have two buttons on your joystick. One is to set a motor to full forward, the other, full reverse. If in your code you had

if button1:
    component.setFullForward()

if button2:
    component.setFullReverse()

then as the code looped, it would constantly switch the motors between forward and reverse. Now you could you an else if loop, but it can be annoying to manage precedence like this. Using verb methods allows every button to affect the outcome, but only the last one to actually show on the robot. This is helpful if you create autonomous commands that interact like a human. You can put them all the way at the bottom or top, and guarantee they either always take precedence, always yield, or a mix of the two.