Using the FTC SDK¶
LinearOpMode vs OpMode¶
There are two OpMode classes within the FTC SDK:
OpMode
and LinearOpMode
.
The one you use affects how you write the program.
For examples of how to use OpMode and LinearOpMode,
refer to the example OpModes in the sdk.
LinearOpMode Methods¶
runOpMode()
: Code inside this method will run exactly once after you press the INIT button. This is where you should put all code for the OpMode.waitForStart()
: This method pauses the Op-Mode until you press the START button on the driver station.isStarted()
: returnstrue
if the START button has been pressed, otherwise it returnsfalse
.isStopRequested()
: returnstrue
if the STOP button has been pressed, otherwise it returnsfalse
.idle()
: puts the thread to sleepopModeIsActive()
: returnsisStarted() && !isStopRequested()
and callsidle()
.
OpMode Methods¶
init()
: Code inside this method will run exactly once after you press the INIT button on the driver station.init_loop()
: Once the code ininit()
has been run, code inside this method will run continuously until the START button is pressed on the driver station.start()
: Code inside this method will run exactly once after you press the START button on the driver station.loop()
: Once the code instart()
has been run, code inside this method will run continuously until the STOP button is pressed on the driver station.stop()
: Code inside this method will run exactly once after you press the STOP button on the driver station.
Note that unlike LinearOpMode, all methods in OpMode must be overwritten to be used.
Reading and Writing to Hardware¶
When using the FTC SDK, there are a variety of built in hardware classes. Objects can be created for these classes and then instantiated. These objects can then be used to communicate with hardware on the robot such as DC Motors, Servos, and Sensors.
Creating and Instantiating Hardware Objects¶
The first thing required to properly create an object is to import its class. In Android Studio, if the class is referenced without being imported Alt+Enter can be pressed to automatically import it. After it is imported, the next step is to create the object:
private DcMotor liftMotor;
After the object is created, it must be instantiated.
Part of the opmode superclass is something called hardwareMap
.
hardwareMap
is used in the FTC SDK to instantiate objects rather than
calling a constructor.
It contains all of the information entered into the configuration on the
Robot Controller, such as names of hardware and what port it is plugged into.
Here is an example of instantiating the motor we created above:
liftMotor = hardwareMap.get(DcMotor.class, "Lift Motor");
Whatever sensor you are using,
you will pass that class into the spot where DcMotor.class
is.
For example, if liftMotor was a Servo, Servo.class
would be passed
instead.
For the second argument, you pass whatever the device is named in the Robot
Controller configuration.
hardwareMap
will then go find what port the device with that name is
plugged into, which allows the hardware to be accessed.
Examples of Using Common Hardware Components¶
Built in to the FTC SDK are many examples of using things such as Color Sensors, Distance Sensors, Servos, Motors, etc. Here, we would like to give more of an explanation of using Motors and Servos because there are a few things the examples do not teach.
DC Motor¶
DcMotor leftMotor = hardwareMap.get(DcMotor.class, "Left Motor");
DcMotor rightMotor = hardwareMap.get(DcMotor.class, "Right Motor");
DcMotor elevatorMotor = hardware.get(DcMotor.class, "Elevator Motor");
DcMotor intakeMotor = hardware.get(DcMotor.class, "Intake Motor");
After a DcMotor
is instantiated,
there are a few variables you can set to affect how the DC Motor runs.
The first of these is direction:
leftMotor.setDirection(DcMotor.Direction.REVERSE);
rightMotor.setDirection(DcMotor.Direction.FORWARD);
Changing the direction of the motor does exactly what should be expected, it changes the direction. If a power of 1 is applied to the motor while it is in forward mode, it will turn one direction. If it is in reverse, a power of 1 will spin it in the other direction. If you face the shaft of the motor towards you, forward is counterclockwise (with the exception of NeveRest motors).
Next, there are two zero power behaviors that can be adjusted:
leftMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
rightMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.FLOAT);
Changing this variable affects how the DC Motor behaves while a power of 0 is
applied.
BRAKE
will cause the motor to try and slow itself down if it is moving
(it will NOT cause the motor to hold its position if not already moving),
while FLOAT
causes the motor to glide to a stop, letting friction do all
the work.
Finally, there are four different run modes that can be used with DC motors:
leftMotor.setMode(DcMotor.RunMode.RUN_WITHOUT_ENCODER);
rightMotor.setMode(DcMotor.RunMode.RUN_USING_ENCODER);
elevatorMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
intakeMotor.setMode(DcMotor.RunMode.STOP_AND_RESET_ENCODER);
It is important to note that encoder values can be read in any of these modes
provided an encoder is properly plugged in.
These modes just change how the motor reacts to these encoder values.
RUN_WITHOUT_ENCODER
makes the motor behave as if there is no encoder
plugged in.
When setPower()
is called, it sets the output voltage to the motor
directly.
Using RUN_WITH_ENCODER
, the power set takes a more indirect route to the
motor.
It first goes through a velocity PID,
and the output from that controller is output to the motor.
This effectively means that setPower() sets the speed of the motor,
not the power.
If a power of .2 were fed while this mode is active,
the motor will attempt to turn the same speed by fluctuating the output voltage
depending on the load on the motor.
This mode has one significant disadvantage, however.
The max speed of the motor is somewhat significantly decreased, so it is
recommended to use RUN_WITHOUT_ENCODER
if possible if maximum speed is
the goal; however, RUN_USING_ENCODER
will provide more consistent
results.
The final mode is RUN_TO_POSITION
.
To make the motor move with this mode,
the function setTargetPosition()
must be called.
When a power is applied to the motor,
a control loop will use that as the max power and try to drive the encoder
position to the target position.
This can be useful to newer teams for autonomous,
as it can be an easy way to have accurate driving functions.
Servo¶
Servo relicServo = hardwareMap.get(Servo.class, "Release Servo");
After instantiating a Servo, there are two main functions that can be called:
setPosition()
and getPosition()
.
releaseServo.setPosition(0.75);
telemetry.addData("Release Servo Target", releaseServo.getPosition());
setPosition()
sets the position of the servo.
The SDK will use a built-in control loop with the servo’s potentiometer to
drive the servo to that position and hold that position. setPosition()
takes in a double between 0 and 1, where 0 is the servo’s lower limit of
rotation and 1 is the servo’s upper limit of rotation.
Everything between is directly proportional,
so 0.5 is the middle, 0.75 is 3/4 the way up, etc.
getPosition()
does not return the servo’s current position,
rather its current target position.
If a variable for the servo’s current target position is stored properly,
this function should never be needed.
Continuous Rotation Servo¶
CRServo intakeServo = hardwareMap.get(CRServo.class, "Intake Servo");
A CRServo has one main method; setPower()
.
This works very similarly to DcMotor
‘s setPower()
, meaning that
passing it 0 makes it stop, passing it 1 makes it go forward at full speed,
passing it -1 makes it go backwards at full speed, and everything in between.
intakeServo.setPower(0.75);
Gamepad Input¶
A very important aspect of programming a driver controlled opmode is taking
driver controls.
Thankfully in the FTC SDK, this is very easy to do.
Inside of every opmode, there are already 2 working gamepad objects,
gamepad1
and gamepad2
.
gamepad1
is the controller that is connected using start+a, while
gamepad2
is the controller connected using start+b.
To get input, no functions need to be called,
rather static variables need to be accessed. Here are a few examples:
leftMotor.setPower(-gamepad1.left_stick_y);
rightMotor.setPower(-gamepad1.left_stick_y);
if (gamepad2.a) {
intakeServo.setPower(-1.0);
}
else if (gamepad2.b) {
intakeServo.setPower(1.0);
}