githubEdit

playrun() vs runTo() Commands

Understanding when to use run() versus runTo() is critical for building reliable robot control. Choosing the wrong one leads to unexpected behavior, especially when combined with default commands.

Quick Reference

Method
Command Ends?
Motor Stops?
Best For

run(setpoint)

Never (runs until interrupted)

No

Continuous control, teleop

runTo(setpoint)

When goal reached

No

Autonomous sequences, one-shot movements

triangle-exclamation

run() - Continuous Control

Use run() when you expect the command to run indefinitely until interrupted by another command.

/**
 * Set the height of the elevator. Command runs forever until interrupted.
 * @param height Target height
 * @return A Command that never ends on its own
 */
public Command setHeight(Distance height) { 
  return elevator.run(height);
}

When to Use run()

  • Teleop button bindings with whileTrue() - command runs while button is held

  • Default commands - these should run forever by definition

  • Holding position - when you want to actively maintain a setpoint

  • Velocity control - flywheels that spin until told otherwise

Example: Teleop Elevator Control

What happens:

  1. Robot starts, default command runs, elevator goes to 0m

  2. Driver presses A, setHeight(0.5m) interrupts default, elevator goes to 0.5m

  3. Driver releases A, command is cancelled, default command resumes, elevator returns to 0m

This works correctly because run() never ends on its own - it's always interrupted by something else.

runTo() - Goal-Based Control

Use runTo() when you want the command to end automatically when the mechanism reaches its goal.

When to Use runTo()

  • Autonomous routines - move to position, then do the next thing

  • Sequential commands - moveArm.andThen(shoot).andThen(moveArmBack)

  • One-shot movements - press button once to go to a preset

circle-exclamation

Example: Autonomous Sequence

The Default Command Pitfall

triangle-exclamation

When runTo() finishes (because the goal was reached), the command scheduler looks for another command to run. If you have a default command set, it will immediately take over.

The Problem

What the programmer expects:

  1. Press A

  2. Elevator goes to 1 meter

  3. Elevator stays at 1 meter

What actually happens:

  1. Press A

  2. setHeightAndStop(1.0m) starts running

  3. Elevator moves toward 1 meter

  4. Elevator reaches 1 meter, runTo() command ends

  5. Default command immediately starts, commanding 0 meters

  6. Elevator goes back down to 0 meters

Solution 1: Use run() Instead

If you want the mechanism to stay at the commanded position, use run():

Now the run() command never ends on its own, so the default command never gets a chance to run. The elevator will stay at 1 meter until another command is scheduled.

Solution 2: No Default Command

If you don't need a default command, don't set one:

Since there's no default command, when runTo() ends, nothing else runs. The SmartMotorController keeps sending the last setpoint, holding position.

Solution 3: Use whileTrue() for Momentary Control

If you want the mechanism to return to a home position when not commanded, use whileTrue():

This makes the intent clear: hold the button to hold the position.

Solution 4: Use Triggers for State-Based Control

Following command-based best practicesarrow-up-right, use triggers to control behavior:

Understanding Motor Behavior

Both run() and runTo() set the SmartMotorController's setpoint. When the command ends:

  • The command stops running

  • The SmartMotorController does NOT stop

  • The motor continues driving toward the last setpoint

This is intentional! It allows for smooth transitions between commands without the mechanism "going limp" between commands.

If You Need to Stop the Motor

If you actually want to stop motor output when a command ends, use finallyDo():

Or create a dedicated stop command:

Autonomous with runTo()

runTo() shines in autonomous routines where you need sequential operations:

Common Pitfalls Summary

Pitfall
Symptom
Solution

runTo() with default command

Mechanism returns to default position after reaching goal

Use run() or remove default command

Using onTrue() with run() expecting it to end

Command runs forever, can't do anything else

Use runTo() for finite commands

Assuming motor stops when command ends

Mechanism drifts or holds unexpected position

Understand that SmartMotorController continues

No tolerance set

runTo() never ends because goal is never "reached"

Set withClosedLoopTolerance() in config

Tolerance too tight

runTo() ends and restarts repeatedly (oscillation)

Increase tolerance or add debounce

Decision Flowchart

Best Practices

  1. Default commands should use run() - They run forever by definition

  2. Teleop with whileTrue() should use run() - Natural pairing

  3. Autonomous sequences should use runTo() - Need to know when steps complete

  4. Be explicit about intent - Name methods clearly (setHeight vs setHeightAndStop)

  5. Consider state-based control - Use triggers instead of complex command logic

  6. Test both paths - Test what happens when commands end, not just when they run

Further Reading

Last updated