Advanced 11: Motion Calibration
Prerequisites: Advanced 09 (Encoders & Speed Control) Time: ~120 min
Overview
In Advanced 09, you calibrated a single encoder by manually counting ticks per revolution. That's a good start — but every robot is slightly different. Gear ratios vary, motors have different friction characteristics, wheels wear unevenly, and battery voltage changes over time.
This module builds a complete calibration pipeline that measures your robot's actual behavior using multiple sensors, profiles each motor individually, and validates the results statistically.
You'll learn:
- How to calibrate encoder-to-distance mapping with multiple methods
- How to detect motor dead zones and build speed curves
- How to measure and correct left-right motor imbalance
- How to validate calibration quality statistically
- How to save per-robot calibration data persistently
Part 1: Why Calibrate? (~10 min)
Every Robot is Different
Two "identical" robots from the same production batch will behave differently:
| Source of Variation | Effect |
|---|---|
| Gear backlash | Different distance per tick |
| Motor winding tolerance | Different speed at same PWM |
| Wheel diameter difference | Curved motion instead of straight |
| Friction and bearing wear | Asymmetric drag |
| Battery voltage | Speed changes as battery drains |
If you set both motors to PWM 120, they won't actually spin at the same speed. One robot might need set_speed(120, 115) to drive straight while another needs set_speed(120, 125).
What Calibration Gives You
After calibration, you'll have a per-robot profile stored in calibration.json:
{
"distance": { "mm_per_tick": 0.123, "method": "ultrasonic" },
"motors": {
"left": { "dead_zone": 28, "speed_curve": [[60, 380], [80, 640]] },
"right": { "dead_zone": 32, "speed_curve": [[60, 370], [80, 635]] },
"balance_drift_dps": 2.3
},
"validation": { "distance_error_pct": 1.8, "grade": "A" }
}
This data enables:
- Accurate distance control: Drive exactly 500 mm, not "about half a meter"
- Dead-zone compensation: Skip the PWM range where motors don't move
- Motor balancing: Automatically trim left-right speed for straight driving
- Quality assurance: Know how accurate your robot is
Calibration Files
The calibration scripts in this module are located in src/picobot/calibration/. They all use shared helper functions from cal_utils.py and save results to calibration.json on the Pico's filesystem.
Part 2: Basic Distance Calibration (~25 min)
The fundamental calibration value is mm_per_tick — how many millimeters the robot moves per encoder tick. Advanced 09 computed this from PPR and wheel circumference. Now we measure it directly.
Setup
The calibration scripts use the same picobot library. Copy the calibration/ folder to your Pico:
Task 1 — Ruler Method
The simplest method: drive, measure, divide.
The script shows an interactive menu. Choose Method A (Ruler):
- Place the robot at a marked start position
- The robot drives forward at PWM 120 for 3 seconds
- Measure the distance traveled with a ruler
- Enter the measurement when prompted
The script computes:
Record your result:
| Value | Measurement |
|---|---|
| Encoder ticks (left) | |
| Encoder ticks (right) | |
| Distance measured (mm) | |
| mm_per_tick |
Measurement Tips
- Start and stop marks should be at the wheel contact point, not the front of the robot
- Use a long straight ruler or tape measure
- Repeat 2-3 times — if results vary by more than 5%, your measurement technique needs work
Task 2 — Ultrasonic Wall Method
No ruler? Use the robot's own ultrasonic sensor as ground truth.
Choose Method B (Ultrasonic) from the menu:
- Face the robot toward a flat wall, about 80 cm away
- The script reads baseline distance (median of 5 readings)
- The robot drives backward (away from wall) for 3 seconds
- The script reads the new distance
The computation:
Record your result:
| Value | Measurement |
|---|---|
| Start distance (cm) | |
| End distance (cm) | |
| Delta (mm) | |
| Average ticks | |
| mm_per_tick |
Why Backward?
We drive the robot away from the wall (backward) to avoid the ultrasonic dead zone (< 2 cm) and to ensure the distance always increases.
Tip
Compare your ruler and ultrasonic results. They should agree within 5%. If they don't, the ultrasonic wall surface may not be ideal — try a large, flat, hard wall (not fabric or rough material).
Part 3: Advanced Calibration Methods (~20 min)
Task 3 — Opto Track Calibration
The most accurate method uses a striped calibration track:
Track specification: - Alternating black and white stripes - Each stripe is 5 mm wide (10 mm per cycle) - At least 30 cm total length - Print on paper or use electrical tape on white cardboard
The robot drives over the track slowly while the first line sensor (GP2) counts 0→1 and 1→0 transitions (edges). Each edge corresponds to exactly 5 mm.
Choose Method C (Opto) from the menu.
How it works:
Track: ██████░░░░░░██████░░░░░░██████░░░░░░
Sensor: 1 0 1 0 1 0 1 0 1 0
Edges: ↑ ↑ ↑ ↑ ↑ ↑
Count: 1 2 3 4 5 6 → 6 edges × 5mm = 30mm
Note
The opto method uses a slower PWM (80) for better edge accuracy. At least 4 edges must be detected for a valid measurement.
Record your result:
| Value | Measurement |
|---|---|
| Edges detected | |
| Track distance (mm) | |
| Average ticks | |
| mm_per_tick |
Task 4 — Cross-Validate All Methods
Run all available methods and choose Q to see the comparison table:
========================================
Method Comparison
========================================
Method mm/tick
------------ ----------
ruler 0.1234
ultrasonic 0.1228
opto 0.1231
Mean: 0.1231 Std: 0.0003 Range: 0.0006
Checkpoint — Methods Agree
If all methods agree within 2-3%, your calibration is solid. The script automatically saves the best result (preferring opto > ToF > ultrasonic > ruler).
Prediction exercise: Before running, predict which method you think will be most accurate and why. Record your prediction, then compare with results.
| Method | Predicted Accuracy | Actual mm/tick | Notes |
|---|---|---|---|
| Ruler | |||
| Ultrasonic | |||
| Opto |
Part 4: Motor Characterization (~25 min)
Distance calibration tells you how far each tick goes. Motor characterization tells you how each motor behaves at different power levels.
Task 5 — Dead Zone Detection
Every motor has a "dead zone" — a range of PWM values too low to overcome static friction. Sending PWM 10 to a motor wastes power without producing motion.
The script ramps each motor's PWM from 0 to 80 in steps of 2, waiting 200 ms per step. The first PWM where the encoder detects motion (> 5 ticks/sec) is the dead zone boundary.
Record your measurements:
| Motor | Dead Zone PWM | Notes |
|---|---|---|
| Left | ||
| Right |
Tip
Dead zones are typically 20-40 PWM. If a motor's dead zone is above 50, check for mechanical binding or low battery.
Warning
Dead zones change with battery level! A freshly charged battery has lower dead zones. Consider re-running this test at different battery levels.
Task 6 — Speed Curve
The script tests each motor at multiple PWM levels and records the average speed in ticks per second:
========================================
Speed Curve: left motor
========================================
PWM tps
----- --------
38 120.5
60 380.2
80 640.8
100 850.3
120 1020.5
160 1380.1
200 1640.3
255 1850.6
Record the speed curve for your motors:
| PWM | Left tps | Right tps |
|---|---|---|
| dead_zone + 10 | ||
| 60 | ||
| 80 | ||
| 100 | ||
| 120 | ||
| 160 | ||
| 200 | ||
| 255 |
Analyzing the Curve
The speed curve reveals:
- Linearity: Is speed proportional to PWM? (Roughly yes in the middle range, but saturates at the top)
- Matching: Do left and right give similar speeds at the same PWM?
- Maximum speed: The flat region at high PWM is where the motor is saturated
If you've studied Advanced 07 — Data-Driven Methods, you can fit a polynomial to this data!
Task 7 — Balance Test
The script calibrates the IMU, drives both motors at PWM 120 for 3 seconds, and measures heading drift:
Record your result:
| Measurement | Value |
|---|---|
| Heading drift (deg) | |
| Drift rate (deg/s) | |
| Suggested correction |
Note
- < 1 deg/s: Excellent balance — no correction needed
- 1-3 deg/s: Acceptable, but a small PWM trim improves straight-line driving
- > 3 deg/s: Significant imbalance — reduce the faster motor's PWM by
drift_rate × 2
Using Balance Data
If your right motor is faster (robot drifts left, positive heading), reduce right PWM:
Part 5: Validation (~20 min)
Calibration without validation is just guessing with extra steps. This part measures how accurate your calibrated robot actually is.
Task 8 — Distance Accuracy Test
The validation script uses your mm_per_tick from Part 2 to command exact distances:
The test procedure (repeated 5 times):
- Face a flat wall at ~60 cm
- Read baseline distance (ultrasonic or ToF)
- Drive forward exactly 200 mm by encoder count
- Read end distance
- Compare: actual distance moved vs commanded 200 mm
========================================
Distance Accuracy (5 trials)
========================================
Commanded: 200 mm per trial
Trial Start End Actual Error
----- ------- ------- ------- -------
1 602 405 197 -1.5%
2 598 401 197 -1.5%
3 605 402 203 +1.5%
4 601 399 202 +1.0%
5 603 401 202 +1.0%
Record your data:
| Trial | Start (mm) | End (mm) | Actual (mm) | Error (%) |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 |
Task 9 — Turn Accuracy Test
The validation script also tests turn accuracy using IMU feedback:
- Calibrate IMU (keep robot still!)
- Command 90° turn using IMU heading
- Record final heading vs 90° target
- Repeat 5 times, alternating with return turns
Record your data:
| Trial | Actual (deg) | Error (deg) |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | ||
| 5 |
Grading
The script assigns a letter grade based on error statistics:
| Grade | Distance Error | Turn Error | Meaning |
|---|---|---|---|
| A | < 2% | < 2° | Excellent — competition-ready |
| B | < 5% | < 5° | Good — reliable for most tasks |
| C | < 10% | < 10° | Acceptable — needs improvement |
| F | > 10% | > 10° | Re-calibrate! |
Your grade: ______
Not Getting Grade A?
Common causes of poor accuracy:
- Wheel slippage on smooth surfaces — try rougher surface or add rubber bands to wheels
- Stale calibration — battery level changed since calibration
- Ultrasonic noise — use ToF sensor for better validation or use a harder wall surface
- IMU drift — make sure calibration runs on a completely still robot
Part 6: Per-Robot Calibration File (~10 min)
Task 10 — Save, Load, and Use
After running all three scripts (distance_cal, motor_profile, validate), your calibration.json contains a complete robot profile. Verify it:
Use the calibration in your own scripts:
import json
# Load calibration
with open("calibration.json") as f:
cal = json.load(f)
mm_per_tick = cal["distance"]["mm_per_tick"]
left_dz = cal["motors"]["left"]["dead_zone"]
right_dz = cal["motors"]["right"]["dead_zone"]
# Drive exactly 300mm
from picobot import Robot
robot = Robot(encoders=True)
target_ticks = 300 / mm_per_tick
robot.encoders.reset()
# Use dead zone info: start above dead zone
robot.motors.set_speed(max(80, left_dz + 20), max(80, right_dz + 20))
import time
while True:
left = abs(robot.encoders.left.ticks)
right = abs(robot.encoders.right.ticks) if robot.encoders.right else left
if (left + right) / 2 >= target_ticks:
break
time.sleep(0.01)
robot.motors.stop()
print(f"Driven 300 mm ({target_ticks:.0f} ticks)")
Calibration Workflow
Establish this routine:
- Beginning of each lab session: Run
distance_cal.py(Method B — fast, no ruler) - After swapping motors or wheels: Run full
motor_profile.py - Before competitions or demos: Run
validate.pyto confirm accuracy - Keep your calibration.json — it's your robot's "passport"
What You Discovered
| Concept | What You Learned |
|---|---|
| Manufacturing variation | No two robots are identical |
| Multi-sensor calibration | Different sensors give different accuracy |
| Dead zones | Motors need minimum PWM to overcome friction |
| Speed curves | Motor response is non-linear |
| Balance testing | Equal PWM ≠ equal speed |
| Statistical validation | Calibration quality needs measurement |
| Persistent configuration | Per-robot data enables portability |
Connections to Other Modules
- Advanced 01 — Sensor Theory: The noise and calibration theory applies directly to distance measurement
- Advanced 04 — Sensor Fusion: Your
mm_per_tickfeeds into odometry calculations - Advanced 07 — Data-Driven Methods: Speed curves are system identification data
- Advanced 09 — Encoders & Speed Control: Foundation for everything in this module
Mini-Project
Calibrate your robot to drive within 2% distance accuracy (Grade A).
Requirements:
- Run at least 2 distance calibration methods and cross-validate
- Complete motor profiling for both motors
- Achieve Grade A or B on the validation test
- Demonstrate driving a precise distance (e.g., exactly 500 mm to a wall)
- Show your
calibration.jsonand explain each value
Further Reading
- The Handbook of Small Electric Motors — Chapter on DC motor characterization
- Probabilistic Robotics (Thrun et al.) — Chapter 5: Odometry calibration
- Embedded Systems: Introduction to Arm Cortex-M Microcontrollers (Valvano) — Motor control and calibration