Why Your Two-Wheeled Robot Won't Drive Straight

Identical motors do not spin at identical speeds. Here is why hardcoded power trims fail and how to use wheel encoders for true straight-line tracking.
When you send a 100% duty cycle signal to two identical DC motors on a differential-drive robot, the robot almost always curves to the left or right. This happens because two mass-produced DC motors are never electrically or mechanically identical, and small variances in internal friction, magnetic strength, and wheel traction produce different rotational speeds for the exact same electrical input.
In a classroom or competition workshop, this drift is one of the first frustrating hurdles students encounter. Understanding why it happens—and why quick software hacks do not solve it—is the bridge between toy robotics and genuine engineering.
The Anatomy of Motor Variance
Small direct-current (DC) gearmotors, particularly the yellow-cased hobby motors common in educational kits, are built to loose manufacturing tolerances. Several microscopic differences guarantee that Motor A will behave differently from Motor B:
- Coil winding and internal resistance: The copper wire wound around the armature varies slightly in length and tension, creating slight differences in resistance and back-EMF (electromotive force).
- Permanent magnet strength: Sintered ferrite magnets have small variations in magnetic flux density, altering torque output.
- Brush contact friction: Mechanical brushes press against the commutator with varying spring tension, adding uneven drag.
- Gearbox tolerances: The multi-stage spur gearboxes attached to these motors experience varying levels of friction depending on tooth profile consistency, shaft alignment, and internal lubricant distribution.
Even outside the motor itself, mechanical factors compound the problem. If one rubber tyre is seated 0.5 mm further onto a rim, its effective rolling radius changes. If the robot's battery pack sits 10 mm to the left of the centerline, the left wheel experiences higher normal force and greater rolling resistance. The result is an asymmetrical drive base.
Why Hardcoded 'Trims' Always Fail
The intuitive student response is to manually balance the motors in code:
// The standard beginner workaround
motorLeft.setSpeed(255);
motorRight.setSpeed(238); // trim down the faster motorThis is known as open-loop control: sending a command and assuming the real-world output matches the request without checking the result. While a manual trim might make the robot drive straight for three metres on a smooth linoleum floor on a fresh set of batteries, it reliably falls apart under real conditions:
- Battery discharge curves: As battery voltage drops over a 45-minute lesson, DC motors do not scale their speed losses linearly. A trim calibrated at 8.2V will pull to the side once the pack drops to 7.4V.
- Surface friction changes: Moving from smooth lab tiles onto commercial short-pile carpet alters the rolling resistance unevenly across both tyres.
- Thermal changes: As gearboxes and motor windings heat up during continuous operation, internal friction drops, shifting the speed ratio between the two sides.
The Solution: Closed-Loop Feedback with Encoders
To make a robot drive in a straight line regardless of battery voltage or surface texture, the microcontroller needs to measure what the wheels are actually doing in real time. This requires closed-loop control.
The simplest way to achieve this is with rotary wheel encoders. An encoder consists of a sensor (usually an optical interrupter or a Hall-effect magnetic sensor) and a rotating element attached to the motor shaft or wheel (a slotted disc or a multi-pole magnetic ring). Every time the wheel turns, the sensor pulses.
| Control Approach | Mechanism | Behaviour on Low Battery | Behaviour on New Surface |
|---|---|---|---|
| Open-Loop (Fixed PWM) | Assumes identical motors | Drifts unpredictably | Drifts unpredictably |
| Open-Loop (Hardcoded Trim) | Manually offsets one motor | Loses calibration | Loses calibration |
| Closed-Loop (Encoder Feedback) | Dynamically adjusts power based on measured wheel ticks | Maintains straight heading | Maintains straight heading |
Implementing Proportional Speed Control
You do not need an advanced calculus background to implement a functional closed-loop controller. A basic proportional (P) control loop running every 50 to 100 milliseconds is sufficient for classroom robotics.
Instead of commanding raw motor power, your program measures wheel rotations over a fixed time interval and adjusts power to eliminate the difference:
- Count the encoder pulses on both wheels over a fixed window (for example, every 50 ms).
- Calculate the error:
error = leftTicks - rightTicks. - If
error > 0, the left wheel is spinning faster than the right wheel. Decrease left motor PWM slightly, increase right motor PWM slightly, or apply a correction factor proportional to the size of the error:correction = error * Kp(whereKpis a tuned gain constant). - Reset the tick counters and repeat.
If you are building your own classroom curriculum or sourcing hardware platforms, choosing kits that include hardware interrupt-capable microcontrollers and integrated magnetic encoders—such as the modular ecosystems supported on platforms like Sheen Canvas or the robotics platforms under Infinity—saves dozens of hours of student frustration. It shifts the learning focus from fighting mechanical defects to mastering sensor feedback and control theory.
The Core Takeaway
Mechanical imperfections are not defects to be hidden with temporary software band-aids; they are the fundamental reality of physical computing. Teaching students to measure actual motor output using encoders and closed-loop algorithms turns a common source of workshop frustration into an accessible, practical lesson in real-world control systems.



