BLRS Template
Introduction
This project is a starting point for all software sub-teams. It allows us to have a common code base to work from, speed up trouble shooting, and reduce the prerequisite knowledge required to create consistent autonomous programs.
Libraries
In order to reduce the amount of redundant code between the robots, we packaged the most important stuff into libraries for easy reuse.
ARMS
This library includes the autoSelector and Greenhat (now just called ARMS) all future work is done here.
Greenhat
This library handles chassis movement. Any function that moves the robot around the field will be controlled by functions located in this library. Documentation for it can be found here. This library was developed by Micah Rassi.
autoSelector
This library handles the selection of the autonomous program for matches, hence the name. It creates a graphical menu on the brain's display to allow easy selection between the multiple autonomous programs. Documentation for it can be found here. This library was developed by Kunwar Sahni.
Project Structure
The project structure is focused around dividing the subsystems and autons into
separate files. The main.cpp
should contain little actual code.
Subsystems
The subsystems folder contains one file for each subsystem. The general template for subsystems should follow this pattern:
#include "main.h"
namespace intake{
okapi::MotorGroup motors = {5,-6};
void init() {
motors.setGearing(okapi::AbstractMotor::gearset::green);
motors.setBrakeMode(okapi::AbstractMotor::brakeMode::hold);
motors.setEncoderUnits(okapi::AbstractMotor::encoderUnits::degrees);
}
void move (int speed) {
motors.moveVoltage(speed*120);
}
void opcontrol() {
static int speed;
if(master.get_digital(DIGITAL_R1))
speed = 100;
else if(master.get_digital(DIGITAL_R2))
speed = -100;
else
speed = 0;
move(speed);
}
} //namespace intake
The entire subsystem is placed into a unique namespace to allow a general naming convention to be applied to each subsystem.
The motors (or singular motor) are declared using okapi. We use okapi because of it's native support for motor groups, which allow for more compact and readable autonomous programs.
The init()
function should be called from main.cpp during robot initialization.
It allows the setting of gearing, brake mode, encoder units, and any other subsystem
specific initialization.
The move()
function is simply a shorthand for voltage based movement. The function
takes a number between -100 and 100. Which is much easier to use than the regular
moveVoltage
function.
The opcontrol()
function utilizes a static variable to keep track of the speed
assigned to the subsystem. This approach can make some tasks easier by storing
the previously assigned motor speed.
The chassis subsystem
All configuration for the chassis can be done from the file:
main.cpp
This is where you can define which ports the drive motors are plugged into and what internal gearset to use. Negative numbers mean reversed motor
{1, 2}, // left motors
{-3, -4}, // right motors
200, // motor rpm
Below that are the distance_constant
and degree_constant
. These affect how
far the robot moves per unit during auton. When calling the function drive(1)
the distance constant defines what 1
is. It can be 1 inch, 1 foot, 1 meter, or
any other measurement you choose. By default, we use feet for simplicity. Just
adjust the value until you achieve the desired movement. The same is true of the
degree_constant
, which should be tuned to produce a consistent 90 degree turn.
Writing an autonomous
All autonomous programs are located in the scripts
folder. By default there
is just red
, blue
, and skills
. The macros.cpp
file is for putting
reusable pieces of code for common actions. If a block of code is used more than
once, you can often make it a macro. This improves program readability greatly.
Chassis movement
To start moving the robot around the field, you can use the drive()
function
and the turn()
function supplied by Greenhat. The template is
using namespace greenhat;
by default, so all functions from the library are
available with no prefix. To preform more complicated drive maneuvers, read the
Greenhat documentation linked above.
Other subsystem movement
Other subsystems can be moved by accessing the move function in their namespace.
intake::move(100);
The move function takes a value between -100 and 100. It will produce a constant
movement at the given voltage until the command intake::move(0)
is given.
That means that a delay must be put between the starting and stopping of the movement.
intake::move(100);
delay(1000); // delay for 1 second
intake::move(0);
This type of time-based movement is good enough for many subsystems, but sometimes
more precision is required. In these cases PID is the best option.
Vex motors have a built-in PID that can be used through the moveVelocity
, moveAbsolute
, and moveRelative
commands.
Here is an example of their usage:
intake::motors.moveAbsolute(180); // move 180 degrees
while(intake::motors.getPosition() != 180)
delay(10); //wait until the motor reaches the target
Documentation on how to use the built-in functions can be found here.
But even the built-in PID can often be insufficient. This is where an okapi async position controller comes in handy. They can be added to a subsystem following the format in the documentation
std::shared_ptr<okapi::AsyncPositionController<double, double>> controller = okapi::AsyncPosControllerBuilder()
.withMotor(motors)
.withGains({0.001, 0})
.build();
The controller can then be utilized with the following syntax:
intake::controller->setTarget(180); // move 180 degrees
intake::controller->waitUntilSettled();
The setup for this type of controller is a little verbose, but it can be much more consistent than either time based or the built in PID.
Code Formatting
The template contains a clang-format according to the BLRS style guide
This can be run by executing the run-clang-format.sh
bash script and will
automatically adjust all code in the project to follow the proper coding standards.
To make life easier its best to install a clang-format extension to whatever editor
your using. These extensions for Atom (the PROS Editor) or VS Code
will automaticaly format your code to the standard everytime you save.