Skip to content

Robot Development w FlashLib

Tom Tzook edited this page Apr 9, 2023 · 23 revisions

Do you need to create an amazing robot but want to do so quickly? Do you need a framework for you robot software so you won't have to create everything from scratch? Do you need your robot to do complicated tasks but don't know how?

If any of these are true (or any other reason...) you have come to the right place! FlashLib is not just a simple library, it provides a framework and a huge array of features and tools for any robot or just software in general.

To help you get start, we will be reviewing what you can do with FlashLib and how to use it to create your robot software. We assume that you do know a thing or two when jumping into this place, such as: what are motors, robots, sensors, etc.

Robot Control

Before starting with our robot software, we need to learn how our robot software is going to be managed and controlled. That is because planning and writing a robot software works a lot differently than applications for personal computers.

Robot Main Class

A robot software contains a primary class, which serves as the base for the code and is responsible for integrating all the robot code into a single manageable place. The class will contain the objects which control our electronics, or perform algorithms and methods which use those objects.

Robot Systems

A robot is usually divided into systems, each responsible for performing different actions, and together they perform tasks. A system can be the robot's drive train, which includes the motors which move the robot. Another example can be an arm which is controlled by one or more actuators. All in all, a system is can be whatever we decide, as long as it makes sense.

Because our robot is divided into systems, we would usually divide the software similarly. Although not a must, we recommend making each robot system a different class. Those classes will have properties which will include objects and variables for controlling the system, and methods which will allow us to control the system from our main robot class. Once ready, we would create one object for each system in our main class and control those systems from there.

Read further here.

Electronics

Controlling a robot is basically controlling electronic devices on the robot which are connected to the robot computer. Those devices include actuators like motors or servos, as well as sensors like gyroscopes and ultrasonics.

To interact with those device our software needs to able to read and write into IO ports on the robot computer. There are several ways to do so:

  • Writing code which will interact with the IO ports. This can be a difficult task and requires knowledge in IO and maybe even low level development.
  • Using an external library. Chances are there is a library out there which already handles the interaction with IO ports for your platform, so it might be a better idea to find one and use it.

Having a way to use IO ports is not the entire story, because we also need to know how to control each device with those ports. FlashLib provides several classes for device control, but manual creation might be necessary in some cases. To allow usage of those classes with any IO port interaction code, FlashLib uses interfaces for each port type. So when using a custom IO interaction which is not included in FlashLib, simply implement those interfaces to use built-in control classes.

Operation Modes

FlashLib builds it's robot framework on the bases of operation modes: a robot has several operation modes, in each mode the robot is controlled and performs different operations. A robot must have at least 1 mode:

  • disabled: This mode is a safety mode where the robot should remain idle and do nothing. This mode is a must.

Other than disabled mode, users can decide which modes the robot has. If the robot only has one operation type, the it would have a disabled mode and an operation mode.

An example for user defined operation modes can be:

  • Manual: user manually controls the robot using controllers and joysticks, etc.
  • Automatic: the robot executes pre-programmed instruction for performing and action, without user interaction.

Each mode is defined by an integer which is used to identify the mode. Disabled mode is identified as 0, user-defined modes can choose their own mode value. No 2 modes can should use the same value.

Mode Suppliers

To select which operation mode is currently used, FlashLib uses the RobotModeSupplier interface. This interface has a single method: get() which returns the current mode value. A robot can have only one RobotModeSupplier.

There are several build-in mode selector types:

  • Manual: the robot software manually selects which operation mode is used

Robot Control Loop

Unlike desktop programs or applications, robot software is built iteratively, meaning that our robot is managed by a loop. FlashLib provides robots with a loop which runs while the robot is operational, and from this loop user code is called. The loop takes care of different operation modes and allows our robot to operate without a stop.

Human Interface Devices

FlashLib provides a package for working with controllers and joysticks, allowing users to manually control the robot. However the problem is that there are many ways data from those controllers could be sent to our robot softiware. So to allow flexibility, FlashLib uses the HidInterface interface which connects between the hid package and the actual controllers.

There are several built-in implementations:

  • Empty: basically acts like no controller is actually connected.

Robot Bases

Robot bases are classes which our main robot class should extend. Those classes are responsible for initialization and shutdown of the robot. FlashLib provides several robot bases.

RobotBase

RobotBase is the most basic base for robots. All other robots bases should extend this base. Here initialization and shutdown of the robot is handled. When the robot is started, the user implementation of this class is initialized, robot and FlashLib systems are initialized and user robot code is then started by calling robotMain(). When the program exists, robotShutdown() is called.

Using RobotBase is not recommended because it is very basic and doesn't provide a robot control loop. Instead, extend one of the following bases, each an extension of `RobotBase:

IterativeRobot

An extended robot base, provides a complex control loop with easier control over operations.

The control loop divides each operation mode into two types:

  • init: initialization of the operation mode
  • periodic: execution of the operation mode init is called every time the robot enters a new mode. periodic is called every ~10ms while the robot is in the operation mode.

When the robot enters disabled mode, disabledInit() is called at the beginning and them every ~10ms or so disabledPeriodic() is called. When in any other mode modeInit(int) is called and the mode value is passed and then every ~10ms or so modePeriodic(int) is called and the mode value is passed.

Before the control loop starts, robotInit() is called. In here initialization of robot systems should be done. When the robot enters shutdown mode robotFree() is called to allow user shutdown operations.

While the control loop is running, FlashLib's scheduling system is active allowing usage of it. When in disabled the Scheduler runs only Runnable objects and when in other modes both Runnable and Action objects are executed. When the robot switches operation modes, all Action objects are interrupted and stop running.

Getting Started

Depending on your build system, you will need to add a dependency requirement for FlashLib, which is located in maven.

Gradle

dependencies {
    implementation 'com.flash3388.flashlib:flashlib:$version'
}

Choosing a Robot Base

With the main robot class ready, all that we need to do is choose a base for our class which the main class will extend. A robot base contains the main method and is responsible for initialization of background operations and the robot operation loop. After choosing the robot base, extend the class with you main robot class and implement all necessary methods. Then we can start working on the robot software.

In most cases we recommend using the IterativeRobot base. When using this base the main class would look similar to this:

public class Robot extends IterativeRobot {

	@Override
	protected void robotInit() {
	}

	@Override
	protected void disabledInit() {
	}
	@Override
	protected void disabledPeriodic() {
	}

	@Override
	protected void modeInit(RobotMode mode) {
	}
	@Override
	protected void modePeriodic(RobotMode mode) {
	}
}

Main

Like any Java program, our code requires a main method to serve as the codes entry point. In it, we will launch the robot:

public class Main {
    public static void main(String[] args) {
        RobotMain.start((instanceId, resourceHolder)-> {
            RobotControl robotControl = new GenericRobotControl(instanceId, resourceHolder,
                    GenericRobotControl.Configuration.controlStubs());
            RobotBase robotBase = new LoopingRobotBase(UserRobot::new);
            return new RobotImplementation(robotControl, robotBase);
        });
    }
}

Notice that we provide a creation method to the RobotMain for creating out Robot class. It will be created after initialization of the framework has completed.

The robot project is now ready.

Robot Components

Each robot is represented with several components. These objects are used for several core operations with the robot. Basically, they serve as input sources and output destinations, or as operation performers. GenericRobotControl is an implementation of RobotControl, which is a simple container for those components.

Some of the components include:

  • HidInterface: provides information about HIDs. Can be accessed by getHidInterface.
  • RobotModeSupplier: provides the current robot operation mode. Can be accessed via getModeSupplier.
  • Clock: provides the clock for the robot, to use for timing. Can be accessed via getClock.
  • Logger: provides the logging platform for the robot. Can be accessed via getLogger.
  • Scheduler: provides the Scheduler for the robot. Can be accessed via getScheduler.

Most of these components depend on the platform used and more. So their implementation may differ. Moreover, some may need custom implementations, depending on the kind of robot and its specs.

Initializing Robot

With the base ready to implement, we now need to initialize different robot systems. This may differ between robots a lot, depending on the structures and systems in the robot.

private TankDriveSystem mDriveSystem;
private XboxController mXboxController;

@Override
protected void robotInit() {
    mDriveSystem = new TankDriveSystem(...);
    mXboxController = new XboxController(0);
}

The example above showcases initializing the robot with a single system: drive system, and controller to control the drive system.

Depending on the implementation and the robot, you may wish to perform teardown operations to the robot systems. If so, implement the robotShutdown method and perform the teardown logic there.

Handling Modes

In IterativeRobot base, handling of modes is separated into different methods provided by the class. Depending on the mode the robot is in, you may wish to perform different operations. This is done in the mode handling methods. There are 2 types of modes:

  • disabled: specific for DISABLED mode. Methods disabledInit and disabledPeriodic.
  • other modes: for all other modes. Methods modeInit and modePeriodic which receive the mode the robot is in.

As you might have noticed, there are 2 methods per mode:

  • init: called when the robot enters the mode, after the RobotModeSupplier as provided a new mode. Should perform any initialization logic for the mode.
  • periodic: called periodically while the robot is in the same mode. Should perform periodic logic with the robot while it is in the mode.

init will always be called before the periodic method. But take note that modeInit and modePeriodic may handle multiple modes, so checking what mode is involved is always important.

@Override
protected void disabledInit() {
    // let's perform some disabled-specific initialization logic.
}
@Override
protected void disabledPeriodic() {
    // let's perform some disabled-specific periodic logic.
}

@Override
protected void modeInit(RobotMode mode) {
    if (mode.equals(SOME_MODE)) {
        // let's perform some mode-specific initialization logic.
    } 
}
@Override
protected void modePeriodic(RobotMode mode) {
    if (mode.equals(SOME_MODE)) {
        // let's perform some mode-specific periodic logic.
    }
}
Clone this wiki locally