VEX V5 PYTHON | 02.3 ENCODERS AND CALIBRATION
Guide objective
Read motor encoder positions, explain what resets change and calibrate repeatable distance errors using measured floor travel.
01 | WHAT THE ENCODER MEASURES
A V5 Smart Motor provides position feedback from its built-in encoder. Your program can read rotation in degrees or turns. One output revolution corresponds to 360 degrees when the motor is configured correctly for its installed cartridge.
The encoder measures motor rotation, not the robot’s location on the field. A spinning wheel can report rotation while slipping on the floor. A broken or loose mechanical connection can also separate motor motion from wheel travel.
02 | READ POSITION WITH A CLEAR UNIT
angle = left_motor_1.position(DEGREES) turns = left_motor_1.position(TURNS)
720 degrees is two turns. Read the motor names from your own declarations, not names copied from another robot.
Record each motor separately during diagnosis. A group reading can hide differences between individual motors. Before calculating signed travel, verify how your configuration reports forward and reverse position changes.
If signs differ, investigate reversal and configuration first. Do not immediately take absolute values everywhere: that can conceal a motor moving against its partners.
03 | RESETTING IS A REFERENCE CHANGE
left_motor_1.set_position(0, DEGREES)
This changes the reported position reference. It does not move the motor shaft or return the robot to its starting location.
Reset before a new measurement while stopped. Do not reset a motor during an active position-target movement because changing the reference can disrupt that target.
Alternatively, keep the existing reference and subtract readings:
start_angle = left_motor_1.position(DEGREES) # Run one controlled movement. end_angle = left_motor_1.position(DEGREES) angle_change = end_angle - start_angle
Resetting drive encoders does not reset an inertial heading. These are different measurements.
04 | CONVERT ROTATION TO ESTIMATED TRAVEL
For direct drive, where one motor-output turn equals one wheel turn:
Wheel turns = motor angle change ÷ 360 Estimated distance = wheel turns × wheel circumference 720° with 319.19 mm circumference: 720 ÷ 360 × 319.19 = 638.38 mm
With external gearing, include the actual wheel-turns-per-motor-output-turn factor Q:
Estimated distance = (motor angle change ÷ 360) × Q × circumference
For a 36-tooth motor driver driving a 60-tooth wheel gear, Q = 36 ÷ 60 = 0.6. The same 720-degree motor change estimates 383.03 mm rather than 638.38 mm.
This is a theoretical rolling estimate. It assumes matching cartridge configuration, correct gearing and no slip. Consult 01.4 for gear-ratio conventions.
05 | COLLECT REPEATABLE MEASUREMENTS
Use a consistent chassis reference point, floor surface, speed and load. Mark the start and measure that same point after the movement. Record the individual motor readings as additional evidence.
| Trial | Commanded distance | Measured floor travel |
|---|---|---|
| 1 | 500 mm | 518 mm |
| 2 | 500 mm | 520 mm |
| 3 | 500 mm | 522 mm |
The mean is 520 mm and the range is 4 mm. The robot travels about 4% too far, with relatively small variation in these three trials.
Large trial-to-trial variation suggests mechanical or traction issues. A calibration factor can correct a consistent scale error; it cannot reliably repair random slip.
06 | CORRECT THE EFFECTIVE WHEEL TRAVEL
For the direct-drive distance model, requested motor rotation is proportional to commanded distance divided by configured wheel travel. If measured travel is consistently too large, increasing the configured wheel travel reduces the rotation requested next time.
New wheel travel = old wheel travel × mean measured distance ÷ commanded distance 319.19 × 520 ÷ 500 = 331.9576 mm Suggested effective value ≈ 331.96 mm
This becomes an effective calibration value, not a claim that the literal tyre circumference is now 331.96 mm. Keep the physical measurement and the tuned setting labelled separately.
Apply this only after checking units, cartridge settings and external gearing. Do not change wheel travel and external ratio together to correct the same error. Validate the result at another distance.
07 | TRY THE CALIBRATION CALCULATOR
Enter three measured trials using the same commanded distance. The tool suggests a proportional correction; it cannot judge whether slip or mechanical errors invalidate your data.
08 | COMPLETE ENCODER TEST PROJECT
This uses the explicit four-motor configuration and driver controls from earlier guides. Autonomous performs one forward distance test, then displays each motor’s encoder reading in degrees.
Keep generated imports and the Brain above your user code, with math imported. Paste below # Begin project code, replacing earlier starters. Do not duplicate the manually declared devices in the Devices panel.
Verify all example ports, cartridges, reversals and geometry. First check supported-wheel direction, then conduct a measured floor trial with clear space and suitable competition controls.
# =============================================================================
# TEAM [MY TEAM NAME / NUMBER]
# =============================================================================
# ROBOT CONFIGURATION
# - FOUR-motor drivetrain:
# Left = Ports 3 and 4
# Right = Ports 1 and 2
# - Example: blue cartridges, left normal, right reversed.
# VERIFY your actual ports, cartridges and direction settings.
# DRIVER CONTROLS
# - Left joystick vertical (axis3): forward/back
# - Right joystick horizontal (axis1): turn
# - This program MOVES in autonomous and driver-control modes.
# - Autonomous: one forward test, then report each motor encoder.
# - Example geometry assumes DIRECT drive (external ratio 1).
# - Add BELOW # Begin project code, outside generated configuration.
# - Do not also define these drive motors in the Devices panel.
# - Keep generated from vex import * and brain = Brain() above.
# =============================================================================
# FOUR-MOTOR DRIVETRAIN
# =============================================================================
left_motor_1 = Motor(Ports.PORT3, GearSetting.RATIO_6_1, False)
left_motor_2 = Motor(Ports.PORT4, GearSetting.RATIO_6_1, False)
right_motor_1 = Motor(Ports.PORT1, GearSetting.RATIO_6_1, True)
right_motor_2 = Motor(Ports.PORT2, GearSetting.RATIO_6_1, True)
left_drive = MotorGroup(left_motor_1, left_motor_2)
right_drive = MotorGroup(right_motor_1, right_motor_2)
# =============================================================================
# MEASURED GEOMETRY: EXAMPLES, REPLACE WITH YOUR MEASUREMENTS
# =============================================================================
WHEEL_DIAMETER_MM = 101.6
WHEEL_TRAVEL_MM = math.pi * WHEEL_DIAMETER_MM
TRACK_WIDTH_MM = 300
WHEEL_BASE_MM = 250
EXTERNAL_GEAR_RATIO = 1.0 # This example is direct drive only
drivetrain = DriveTrain(
left_drive, right_drive,
WHEEL_TRAVEL_MM, TRACK_WIDTH_MM, WHEEL_BASE_MM,
MM, EXTERNAL_GEAR_RATIO
)
# =============================================================================
# CONTROLLER, SETTINGS AND HELPERS
# =============================================================================
controller = Controller(PRIMARY) # Do not duplicate a generated Controller
DEADBAND = 5
DRIVE_LIMIT_PERCENT = 30 # Start low; increase only after testing
TURN_GAIN = 0.7
LOOP_DELAY_MS = 20
AUTON_SPEED_PERCENT = 20
TEST_DISTANCE_MM = 500
MOVE_TIMEOUT_SECONDS = 3
PAUSE_MS = 500
def apply_deadband(value):
return 0 if abs(value) <= DEADBAND else value
def drive_mix(forward, turn):
left = forward + turn
right = forward - turn
largest = max(100, abs(left), abs(right))
left = left * 100 / largest
right = right * 100 / largest
return (left * DRIVE_LIMIT_PERCENT / 100,
right * DRIVE_LIMIT_PERCENT / 100)
def set_side_output(side, output):
if abs(output) < 0.01:
side.stop(BRAKE)
elif output > 0:
side.spin(FORWARD, output, PERCENT)
else:
side.spin(REVERSE, -output, PERCENT)
def show_status(message):
brain.screen.clear_screen()
brain.screen.set_cursor(1, 1)
brain.screen.print(message)
def stop_drive():
left_drive.stop(BRAKE)
right_drive.stop(BRAKE)
def reset_drive_encoders():
# Reset readings only when stopped and no movement command is active.
left_motor_1.set_position(0, DEGREES)
left_motor_2.set_position(0, DEGREES)
right_motor_1.set_position(0, DEGREES)
right_motor_2.set_position(0, DEGREES)
def report_encoders():
brain.screen.clear_screen()
motors = (left_motor_1, left_motor_2, right_motor_1, right_motor_2)
labels = ("L1", "L2", "R1", "R2")
for row, motor in enumerate(motors):
brain.screen.set_cursor(row + 1, 1)
brain.screen.print(labels[row] + ": " + str(round(motor.position(DEGREES), 1)))
brain.screen.set_cursor(5, 1)
brain.screen.print("Degrees; measure floor travel")
def drive_distance(direction, distance_mm):
# Use a positive magnitude; the direction argument selects travel direction.
if distance_mm <= 0:
raise ValueError("Distance must be positive")
if not (competition.is_enabled() and competition.is_autonomous()):
stop_drive()
return
drivetrain.set_drive_velocity(AUTON_SPEED_PERCENT, PERCENT)
drivetrain.set_stopping(BRAKE)
drivetrain.set_timeout(MOVE_TIMEOUT_SECONDS, SECONDS)
drivetrain.drive_for(direction, distance_mm, MM) # Blocking by default
drivetrain.stop()
# Command ended; this does NOT prove the floor target was reached.
# =============================================================================
# PRE-AUTON: CALLED ONCE AT PROJECT STARTUP
# =============================================================================
def pre_autonomous():
stop_drive()
show_status("Setup ready")
# Later: initialise sensors and starting values here.
# Finish setup before the robot is enabled for a match.
# =============================================================================
# AUTONOMOUS: A FINITE ROUTINE
# =============================================================================
def autonomous():
stop_drive()
reset_drive_encoders()
show_status("Forward encoder test")
drive_distance(FORWARD, TEST_DISTANCE_MM)
stop_drive()
report_encoders()
# Measure chassis displacement and record all four readings.
# A returned command or encoder value does not prove floor accuracy.
# =============================================================================
# DRIVER CONTROL: REPEATING UPDATES WHILE ENABLED
# =============================================================================
def driver_control():
show_status("Driver control")
while competition.is_enabled() and competition.is_driver_control():
forward = apply_deadband(controller.axis3.position())
turn = apply_deadband(controller.axis1.position()) * TURN_GAIN
left_output, right_output = drive_mix(forward, turn)
set_side_output(left_drive, left_output)
set_side_output(right_drive, right_output)
wait(LOOP_DELAY_MS, MSEC)
stop_drive()
# =============================================================================
# PROGRAM START: SETUP, THEN CALLBACK REGISTRATION
# =============================================================================
pre_autonomous()
competition = Competition(driver_control, autonomous)
# Keep the main task alive without running a driving loop here.
while True:
wait(100, MSEC)
The reading display records motor rotation after the command ends. A timeout may end the command early. The readings alone do not prove the robot reached the floor-distance target.
Keep the displayed motor values in your logbook alongside the independently measured chassis displacement. This separates what the controller observed from what physically happened.
09 | VALIDATE AT ANOTHER DISTANCE
After a justified correction, test a different short distance and repeat the trials. A good proportional model should improve more than one target.
Then test reverse separately. Backlash and loading can produce direction-dependent differences. Do not assume a forward calibration automatically removes every reverse error.
If errors change with speed or distance, investigate acceleration, braking, slip and mechanical play rather than adding one large correction factor.
10 | COMMON ENCODER MISTAKES
- Reset means return home: no, it changes the reading reference only.
- Encoder distance equals floor distance: only under the model’s assumptions.
- All motors look fine from one group value: inspect individual readings when diagnosing.
- One good run proves calibration: collect repeats and validate a second distance.
- Use a correction to hide wrong gearing: fix configuration first.
- Reset during motion: stop before redefining the encoder reference.
CHALLENGES | BUILD A CALIBRATION RECORD
- Read units: compare degrees and turns for one stopped motor.
- Reference test: reset a stopped encoder and confirm that the shaft does not move.
- Predict: calculate estimated travel for a known output rotation and wheel circumference.
- Collect: run three short forward trials and record motor values plus floor travel.
- Calculate: find mean distance, trial range and percentage error.
- Diagnose: check gearing, units and repeatability before proposing a correction.
- Validate: apply one justified change and retest at a different distance.
- Reverse: collect a separate reverse test record.
Success evidence
A saved project and calibration log showing original measurements, any effective correction, repeated results and a validation test. You can distinguish motor rotation from chassis travel.
OFFICIAL REFERENCES
This is an independent learning resource; VEX and VEXcode are trademarks of their respective owners.