Lesson 10.3: Automating Targeted Movement with RUN_TO_POSITION
Why You Should Let the Hub Handle the Stopping
A common beginner approach to encoder-based movement looks like this: set a target, run the motor, and write a while loop that keeps checking getCurrentPosition() until it gets close enough, then set power to zero. This works, but it has a real problem -- by the time your code reads the position, evaluates the condition, and sends the stop command, the motor has already traveled past the target. At high speeds this overshoot can be significant.
RUN_TO_POSITION solves this by moving the stopping logic off of your code and onto the Expansion Hub's processor itself. The Hub runs a PID controller at a much higher frequency than your loop() method ever could, so it can decelerate the motor smoothly and stop it precisely at the target tick without any overshoot management on your end.
The Three-Step Setup You Must Follow in Order
This mode requires three commands in a specific sequence -- getting them out of order is one of the most common bugs in FTC encoder code.
Step 1: Set the target. Call setTargetPosition(int ticks) first. This tells the Hub where you want to go. The parameter is an absolute tick value, not a relative offset from the current position.
Step 2: Switch the run mode. Call setMode(DcMotor.RunMode.RUN_TO_POSITION). This activates the Hub's internal PID controller. If you try to set the target after switching modes, the behavior can be unpredictable.
Step 3: Apply power. Call setPower() with a non-zero value. In this mode, power acts as a speed limit rather than a voltage command -- the Hub will not exceed that fraction of full speed as it drives toward the target. The motor will not move at all if power is still at zero after the mode switch.
Once the motor reaches the target, the Hub continues to apply holding current to resist any external force pushing the shaft away from the target position.
Annotated Code
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.hardware.DcMotor;
@Autonomous(name="Run_To_Position_Demo")
public class RunToPositionDemo extends LinearOpMode {
@Override
public void runOpMode() {
DcMotor liftMotor = hardwareMap.get(DcMotor.class, "lift");
liftMotor.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
waitForStart();
// Step 1: Tell the Hub where to go
liftMotor.setTargetPosition(1500);
// Step 2: Activate the Hub's internal PID controller
liftMotor.setMode(DcMotor.RunMode.RUN_TO_POSITION);
// Step 3: Set a speed limit -- motor starts moving now
liftMotor.setPower(0.6);
// Wait here while the Hub drives to the target
while (opModeIsActive() && liftMotor.isBusy()) {
telemetry.addData("Target", liftMotor.getTargetPosition());
telemetry.addData("Position", liftMotor.getCurrentPosition());
telemetry.update();
}
// Stop cleanly after arriving
liftMotor.setPower(0);
}
}
Fill-in-the-Blank Practice
- Before activating
RUN_TO_POSITIONmode, you must define the destination using the__________method. - Once the target is reached, the motor continues to draw current to
__________its position against external forces. - The
setTargetPosition()method only accepts parameters of the__________data type.
Show answers
setTargetPosition()- hold
int
Template Challenge
Robot Scenario: Program an autonomous lift to move to tick position 1200 as soon as the match begins. Wait for it to finish before ending the OpMode.
package org.firstinspires.ftc.teamcode;
import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode;
import com.qualcomm.robotcore.eventloop.opmode.Autonomous;
import com.qualcomm.robotcore.hardware.DcMotor;
@Autonomous(name="Auto_Lift_Challenge")
public class AutoLift extends LinearOpMode {
@Override
public void runOpMode() {
DcMotor lift = hardwareMap.get(DcMotor.class, "lift");
lift.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE);
waitForStart();
// INSERT CODE HERE: Set target to 1200
// INSERT CODE HERE: Switch mode to RUN_TO_POSITION
// INSERT CODE HERE: Apply 0.7 power to begin the move
while (opModeIsActive() && lift.isBusy()) {
telemetry.addData("Status", "Lifting...");
telemetry.addData("Position", lift.getCurrentPosition());
telemetry.update();
}
lift.setPower(0);
telemetry.addData("Status", "Done");
telemetry.update();
}
}
Show answer
lift.setTargetPosition(1200);
lift.setMode(DcMotor.RunMode.RUN_TO_POSITION);
lift.setPower(0.7);
Ready to move on?
Sign in with Google to save your progress with Telemark, or continue without saving.