-
Notifications
You must be signed in to change notification settings - Fork 0
START HERE Simple Robot with a Subsystem
Lets suppose I have a simple robot, that has wheels we want to control.... How would we do that with this library?
We want to have the ability to add extra features to this robot and make is really simple to make changes to the basis of the control so it is changed on all our programs. This is all made possible by this library.
Download the latest stable release of this repository and put the extracted folder (renamed "FTC_Library") into the teamcode folder on Android Studio.
If you open one of the classes, you should see a statement like package org.firstinspires.ftc.teamcode.FTC_Library.Autonomous;
. This should match the folder path it is in. //TODO: Add more here
It is recommended to set your teamcode folder up with a couple of additional folders:
- Autonomous
- Modules
- Teleop
- Robot
- Subsystems
- (in addition to the API_FTC folder you just added)
Then, inside the the Robot folder, go ahead and create a file called SimpleRobot.java
.
Open this file and enter the following code
import org.firstinspires.ftc.teamcode.FTC_Library.Robot.RobotBase;
public class SimpleRobot extends RobotBase {
//Will store subsystems as pubic fields here.
//This is bad practice generally, but it makes a lot of sense so we can manipulate them directly
SimpleRobot() {
//This is the constructor where we will call "addSubsystem()"
}
}
This is the start of the hardware for the bot. This is intentional, keep control and hardware separate. That way, you can modify the hardware (changing a limit from lets say 0.9 to 0.8) once, and have it change for all of the programs you might be using ("TestTeleop", "Test2Teleop", "CompTeleop", "OnlyArmTelop", etc.).
Let's leave this class alone for now and create the subsystem we will actually be controlling.
Now that we have the robot, we should actually create the subsystems so we can control the robot. For this example we are going to create two subsystems, a driving subsystem and servo controlled claw that can open and close. Now, we don't actually have a robot with these systems, they are simply theoretical but should help to demonstrate how to create subsystems.
A subsystem is made up of a single required method and a couple of optional methods. The simplest subsystem would look like this:
public class ExampleSubsystem extends Subsystem{
@Override
public boolean init(HardwareMap hardwareDevices) {
telemetry.log("HELLO WORLD!");
return true;
}
}
ExampleSubsystem
would print "HELLO WORLD!" when you initialized it. If you want to run something every tick, update a sensor value or monitor a motor, you would override the tick method with something like this:
...
private int counter = 0;
@Override
public void tick(){
counter++;
telemetry.log("This has executed: " + counter + " times.");
}
...
The tick
method is really flexible and can be used in a lot of different ways. That is the basics, but what happens if you want to respond to user action? What about setting configuration settings?
So now we need to set motor names, or perhaps the speed of a motor. If the value isn't going to change you can create a constant within the subsystem. For example:
private static final double DEFAULT_MOTOR_SPEED = 0.8;
but for things like motor names, you might want to have a way to change them centrally. You can do this via builder methods that you can append to when creating the subsystem. Let me demonstrate.
...
private String leftMotorName;
public ExampleSubsystem setMotorName(String motorName){
leftMotorName = motorName;
return this;
}
...
Now you can use leftMotorName
in the init()
method to find the left motor. You can call this in the robot by appending the method to the creation of the varible.
public class SimpleRobot extends RobotBase {
public ExampleSubsystem example = new ExampleSubsystem().setMotorName("ThingName");
SimpleRobot() {
addSubsystem(example);
}
}
In order to do anything, it is important to drive. No matter what the challenge for FTC, there is a 100% chance that you will be driving. The Library understands this and has a dedicated piece of code to make that happen. Let me just show you some of the code and we can work from there. To create a driving subsystem with 2 motors, this is the required code:
import com.qualcomm.robotcore.hardware.DcMotor;
import com.qualcomm.robotcore.hardware.HardwareMap;
import org.firstinspires.ftc.teamcode.FTC_Library.Robot.SubSystems.SidedDriveSystemTemplate;
public class Drive extends SidedDriveSystemTemplate {
protected DcMotor leftMotor;
protected DcMotor rightMotor;
private String leftMotorName;
private String rightMotorName;
@Override
public boolean init(HardwareMap hardwareDevices) {
leftMotor = hardwareDevices.dcMotor.get(leftMotorName);
rightMotor = hardwareDevices.dcMotor.get(rightMotorName);
return true;
}
public Drive setMotorNames(String left, String right) {
leftMotorName = left;
rightMotorName = right;
return this;
}
@Override
public DcMotor[] getRightSideMotors() { return new DcMotor[]{rightMotor};}
@Override
public DcMotor[] getLeftSideMotors() { return new DcMotor[]{leftMotor};}
@Override
public void driveTank(double leftPower, double rightPower) {
leftMotor.setPower(leftPower);
rightMotor.setPower(rightPower);
}
@Override
public void driveArcade(double forward, double turn) {
double left = forward + turn;
double right = forward - turn;
driveTank(left, right);
}
@Override
public void driveMecanum(double forward, double turn, double strafe) {
driveArcade(forward, turn);
}
}
Behind the scenes this has a whole bunch of added functionality. You are guaranteed that your driving subsystem will at least have driveMecanum()
, driveArcade()
, driveTank()
and other methods to deal with encoders and getting the motors from each side. This makes it simple to switch out drive trains because it simply requires the change of system in the robot class and all teleop programs should continue to work as normal. It also means that most driving modules for autonomous will work with all systems. The library will soon (as in after Jan 2019) include a couple default modules that will handle encoder driving automatically.
Now that we have all the 'backend' code, now it is time to actually drive the robot. Lets the SimpleRobot class that we have been working on so we can add the Drive subsystem and use to create our first teleop program.
import org.firstinspires.ftc.teamcode.FTC_Library.Robot.RobotBase;
class SimpleRobot extends RobotBase {
//Createthe 'Drive' subsystem
Drive drive = new Drive().setMotorNames("Left_Motor", "Right_Motor");
SimpleRobot() {
addSubSystem(drive);
}
}
We can modify that robot as we need and can now create our teleop class:
import com.qualcomm.robotcore.eventloop.opmode.OpMode;
import com.qualcomm.robotcore.eventloop.opmode.TeleOp;
@TeleOp(name = "SimpleTeleop")
public class SimpleTeleop extends OpMode{
private SimpleRobot robot = new SimpleRobot();
@Override
public void init() {
robot.init(hardwareMap);
}
@Override
public void loop() {
robot.tick();
robot.drive.drive(gamepad1.left_stick_y,gamepad1.right_stick_y);
}
}
Look how short it is! This is what makes this library so nice, when it comes to actually using the code we have written it can be really short. As an example, team 4711's main teleop loop for the 2018 - 2019 season is only 30 lines and controls every function of our robot (At this point we have made it to state in Oregon if that helps give perspective). The entire class is only about 50 lines (see here)!
This is the end of the initial tutorial, you can look at other pages to provide some more information about all the capabilities of the library!