Skip to content

Guide to Making Our First Robot

Tom Tzook edited this page Nov 21, 2020 · 9 revisions

Welcome! In this guide we will be making our first FlashLib robot. We want be talking about mechanics and electronics, but rather only software. You're free to build an actual robot, but the purpose of this guide is to familiarize ourselves with the structure and components of FlashLib robots.

Environment

We won't be focusing much about the development environment used, since it doesn't matter much. However, we will be showing some images and information about our preferred environment, in order to clarify some stuff that might not be obvious. The environment of our choice is a gradle with a gradle wrapper and using IntelliJ IDEA. The Java versioned we will use is 1.8, which is the minimal supported version by FlashLib.

Setting Up a Robot Project

Setting Up the Build

We won't be explaining how to setup a gradle project, but rather skip ahead.

Initial Project

This should be the look of your gradle project if you're using gradle. At the moment it is empty. Let's start by setting up the build script. Open up the build.gradle file, which should be empty (if not, clear it).

We are going to be using the application plugin which provides an simple setup for running Java applications. Add a plugins DSL statement to use the plugin:

plugins {
    id 'application'
}

We also need to setup the application configuration, which includes the main class. We don't have main class yet, but we would make it in accordance to what we define here:

application {
    mainClassName = 'robot.Main'
}

Next we need to define FlashLib as a dependency of the project. We will start by defining the project repositories which are were dependencies will be retrieved from, and then add the dependency:

repositories {
    mavenCentral()
}

dependencies {
    implementation group: "com.flash3388.flashlib", name: "flashlib.core.robot", version: "$version"
}

Where $version is the FlashLib version you wish to use.

This should be the end product of your build.gradle (we've changed the order a bit):

plugins {
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation group: "com.flash3388.flashlib", name: "flashlib.core.robot", version: "$version"
}

application {
    mainClassName = 'robot.Main'
}

Setting up

Robot Package

We'll create the package which will contain the robot code. Let's call it robot.

Create the robot package by right clicking the java folder -> New -> Package and name it robot: create package 1 create package 2 create package 3

Creating Main Class

Now we add the main class for the project. We will make it under robot package in a class named Main (so robot.Main).

Create the main class by right clicking the robot package -> New -> Java Class and name it Main: create main class 1 create main class 2 create main class 3

This will result in an empty class. Thanks to how we configured gradle (setting mainClassName) this class will be our main class. We now need to implement the main method which will start running the robot.

package robot;

public class Main {
    public static void main(String[] args) {

    }
}

Let's leave this for now, we'll come back to this class and finish it.

Robot Control

The RobotControl of a robot is responsible for providing different control components, which are necessary in order to operate the robot.

Unfortunately because we don't have a physical robot, it is difficult to fully implement this, since the components can be platform dependent. Because of this, we will be using stub components for parts which depend on the platform used; they can be easily substituted later.

Create a new class called MyRobotControl that implements the RobotControl interface:

public class MyRobotControl implements RobotControl {

    @Override
    public Supplier<? extends RobotMode> getModeSupplier() {
        return null;
    }

    @Override
    public IoInterface getIoInterface() {
        return null;
    }

    @Override
    public HidInterface getHidInterface() {
        return null;
    }

    @Override
    public Scheduler getScheduler() {
        return null;
    }

    @Override
    public Clock getClock() {
        return null;
    }

    @Override
    public Logger getLogger() {
        return null;
    }

    @Override
    public void registerCloseables(Collection<? extends AutoCloseable> closeables) {

    }
}

There are a bunch of methods we need to implement. Let's create an instance variable for all the components:

public class MyRobotControl implements RobotControl {

    private final Supplier<? extends RobotMode> mRobotModeSupplier;
    private final IoInterface mIoInterface;
    private final HidInterface mHidInterface;
    private final Scheduler mScheduler;
    private final Clock mClock;
    private final Logger mLogger;
    private final ResourceHolder mResourceHolder;

...

The components are:

  • mRobotModeSupplier: supplies the operation modes of the robot.
  • mIoInterface: allows interfacing with the hardware of the local platform, in order to control electronic components.
  • mHidInterface: allows interfacing with controllers and other human interface devices, providing human control.
  • mScheduler: the execution component for action-based robots.
  • [Clock]((https://github.com/Flash3388/FlashLib/wiki/Robot-Clock): provides timestamp information. Used by other components.
  • Logger: the robot log.
  • ResourceHolder: an helper class for holding resources used by the robot.

We now need to provide values for our variables. We'll do this via the constructor. The basic constructor should receive Logger and ResourceHolder and create the rest. But this can be modified if needed:

    public MyRobotControl(Logger logger, ResourceHolder resourceHolder) {
        mLogger = logger;
        mResourceHolder = resourceHolder;
        
        mClock = RobotFactory.newDefaultClock();
        mRobotModeSupplier = new StaticRobotModeSupplier(RobotMode.DISABLED);
        mIoInterface = new IoInterface.Stub();
        mHidInterface = new HidInterface.Stub();
        mScheduler = RobotFactory.newDefaultScheduler(mClock, mLogger);
    }

The Clock and Scheduler implementations are easily provided from FlashLib and are fully functional.

The RobotModeSupplier implementation used here is one that uses only a single mode: RobotMode.DISABLED. Normally we would want something that can be modified remotely, but there is no built-in implementation which provides that.

The IoInterface is platform-dependent since it interacts of hardware. Because of that, it's a stub.

The HidInterface should normally be controlled remotely, but there is no such built-in implementation. Thus, we use a stub here.

With the components created, we can implement the methods now:

public class MyRobotControl implements RobotControl {

    private final Supplier<? extends RobotMode> mRobotModeSupplier;
    private final IoInterface mIoInterface;
    private final HidInterface mHidInterface;
    private final Scheduler mScheduler;
    private final Clock mClock;
    private final Logger mLogger;
    private final ResourceHolder mResourceHolder;

    public MyRobotControl(Logger logger, ResourceHolder resourceHolder) {
        mLogger = logger;
        mResourceHolder = resourceHolder;

        mClock = RobotFactory.newDefaultClock();
        mRobotModeSupplier = new StaticRobotModeSupplier(RobotMode.DISABLED);
        mIoInterface = new IoInterface.Stub();
        mHidInterface = new HidInterface.Stub();
        mScheduler = RobotFactory.newDefaultScheduler(mClock, mLogger);
    }

    @Override
    public Supplier<? extends RobotMode> getModeSupplier() {
        return mRobotModeSupplier;
    }

    @Override
    public IoInterface getIoInterface() {
        return mIoInterface;
    }

    @Override
    public HidInterface getHidInterface() {
        return mHidInterface;
    }

    @Override
    public Scheduler getScheduler() {
        return mScheduler;
    }

    @Override
    public Clock getClock() {
        return mClock;
    }

    @Override
    public Logger getLogger() {
        return mLogger;
    }

    @Override
    public void registerCloseables(Collection<? extends AutoCloseable> closeables) {
        mResourceHolder.add(closeables);
    }
}

It's not a complex class, but a crucial one. Changing the implementation of components is simple, as all that is needed is to change the instance created in the constructor to the wanted one.

Setting Up the Robot Class

Not to be confused with the Main class, the Robot Class (or Robot Main Class) is the class responsible for defining and running the robot. It is based on a Robot Base, which defines a running context for the robot.

We will start by creating a class MyRobot under robot package:

package robot;

public class MyRobot {

}

We are going to use the LoopingRobotBase which provides an iterative flow based on the robot mode. To use this base we will implement the IterativeRobot interface public class MyRobot implements IterativeRobot and add a single constructor, which receives RobotControl and saves it in an instance variable:

public class MyRobot implements IterativeRobot {

    private final RobotControl mRobotControl;

    public MyRobot(RobotControl robotControl) {
        mRobotControl = robotControl;
    }

    @Override
    public void disabledInit() {

    }

    @Override
    public void disabledPeriodic() {

    }

    @Override
    public void modeInit(RobotMode mode) {

    }

    @Override
    public void modePeriodic(RobotMode mode) {

    }

    @Override
    public void robotPeriodic() {

    }

    @Override
    public void robotStop() {

    }
}

Running Robot from Main

With all our classes set-up, we can go back to main and implement the code that will run the robot. We will start by creating the Logger for the robot:

Logger logger = new LoggerBuilder("robot")
       .build();

You can add calls to the LoggerBuilder methods in order to configure the logger, as described here.

With that, we call RobotMain.start which will run our robot program. It receives information about our robot base and robot control in the form of RobotCreator interface, which will come from the classes we created. We can do this simply with a lambda:

public class Main {

    public static void main(String[] args) {
        Logger logger = new LoggerBuilder("robot")
                .build();

        RobotMain.start((_logger, resourceHolder) -> {
            RobotControl robotControl = new MyRobotControl(logger, resourceHolder);
            RobotBase robotBase = new LoopingRobotBase(MyRobot::new);

            return new RobotImplementation(robotControl, robotBase);
        }, logger);
    }
}

And with that our main is complete.