Straight-line driving robot with wheel encoders: design worksheet and code

A ₹1,610 design worksheet for a two-wheel Arduino robot that holds a straight line using slotted speed sensors, with a pin map, a sketch and a gain-tuning method.

About the author · Sources, testing and AI policy

  • Parts₹1,610
  • Bench time4–5 h
  • LevelIntermediate
  • ReviewedNov 2026

A ₹1,610 two-wheel robot that drives straight using wheel encoders. Two slotted speed sensors count the pulses from each wheel’s encoder disc. A simple proportional correction slows the faster wheel and speeds up the slower one, so the robot holds a line instead of curving. The page includes the pin map, a complete sketch without third-party libraries, a method to tune the gain, and a test plan that measures drift over a fixed distance.

Scope: intermediate. This is a design worksheet with illustrative costs. It has not been bench-tested by this site; the pulse count per wheel turn, the gain and the speeds in the sketch are starting values that you measure and tune on your own chassis.

Why two identical motors still drift

Two TT gear motors given the same PWM value do not turn at the same speed. Friction, the battery, the gearbox and the wheel all differ a little, so the robot curves to one side. Most beginner robots hide this with a human holding the controller. An encoder measures how far each wheel has actually turned, so the controller can correct the difference in software. This build is also the base for later work: odometry, fixed-distance moves and square-path driving all start with counted wheel pulses.

Bill of materials

PartQtyUnitTotal
Arduino Uno R31₹520₹520
L298N motor driver1₹180₹180
Slotted speed sensor module (LM393)2₹80₹160
Encoder disc2₹40₹80
TT gear motor with wheel2₹110₹220
Acrylic chassis1₹80₹80
2×18650 holder with cells1₹200₹200
Caster wheel and screws1₹40₹40
Mounting brackets and screws1₹50₹50
Jumper wire set1₹50₹50
Switch1₹30₹30
Parts total₹1,610

Scroll the table sideways to see every column.

Prices checked Nov 2026. Shipping and tools are not included.

Prices are illustrative figures for Indian sellers in November 2026, before shipping, and have not been checked against current listings. The encoder discs are small slotted wheels that fit on the motor shaft or the wheel axle; some chassis kits include them, so check before you buy.

Pin map

ModuleArduino UnoNote
Left speed sensor OUTD2External interrupt 0
Right speed sensor OUTD3External interrupt 1
Speed sensors VCC / GND5V / GNDCheck that your module accepts 5 V
L298N IN1 / IN2D4 / D5Left motor direction
L298N IN3 / IN4D6 / D7Right motor direction
L298N ENAD9Left motor PWM. Remove the onboard jumper.
L298N ENBD10Right motor PWM. Remove the onboard jumper.
L298N +5V5V pinPowers the Uno from the driver’s regulator
All groundsCommon GNDBattery, L298N, Uno and both sensors
The Uno has only two external interrupt pins, D2 and D3, so the encoders take both. That is why the motor PWM moves to D9 and D10 compared with the obstacle avoider.

Fitting the encoders

  1. Fit one slotted disc on each wheel axle or motor shaft. Count the slots; that number is your pulses per turn if the sensor gives one pulse per slot.
  2. Mount each slotted sensor so the disc passes through its gap without touching. Rub marks mean it is too close.
  3. If the disc is on the motor shaft rather than the wheel, remember the gearbox sits between the disc and the wheel, so the count per wheel turn is higher.
  4. Turn each wheel by hand and watch the count on the Serial Monitor to confirm that one full turn gives the pulses you expect.

The sketch

// Straight-line driving with wheel encoders: Uno + L298N + 2 slotted speed sensors
// Design worksheet: measure PULSES_PER_TURN and tune KP on your own robot.

const int LEFT_ENC = 2;    // interrupt 0
const int RIGHT_ENC = 3;   // interrupt 1

const int IN1 = 4;
const int IN2 = 5;
const int IN3 = 6;
const int IN4 = 7;
const int ENA = 9;
const int ENB = 10;

const int BASE_SPEED = 150;      // PWM 0 to 255
const float KP = 0.6;            // PWM change per pulse of difference
const int MAX_CORRECTION = 60;

volatile long leftCount = 0;
volatile long rightCount = 0;

void leftPulse()  { leftCount++; }
void rightPulse() { rightCount++; }

void setup() {
  Serial.begin(9600);
  pinMode(LEFT_ENC, INPUT_PULLUP);
  pinMode(RIGHT_ENC, INPUT_PULLUP);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(LEFT_ENC), leftPulse, RISING);
  attachInterrupt(digitalPinToInterrupt(RIGHT_ENC), rightPulse, RISING);
  delay(2000);               // time to put the robot down
}

void loop() {
  noInterrupts();
  long l = leftCount;
  long r = rightCount;
  interrupts();

  long error = l - r;        // positive: the left wheel is ahead
  int correction = (int)(KP * error);
  correction = constrain(correction, -MAX_CORRECTION, MAX_CORRECTION);

  int leftPwm = constrain(BASE_SPEED - correction, 0, 255);
  int rightPwm = constrain(BASE_SPEED + correction, 0, 255);

  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH);
  digitalWrite(IN4, LOW);
  analogWrite(ENA, leftPwm);
  analogWrite(ENB, rightPwm);

  Serial.print(l);
  Serial.print(" ");
  Serial.print(r);
  Serial.print(" ");
  Serial.println(error);
  delay(50);
}

The controller compares the two running totals, so any difference builds up until the correction removes it. That is a proportional controller on position, not speed: the slow wheel is pushed to catch up, and the faster wheel is held back. If the robot swerves back and forth, lower KP. If it still curves, raise it. Cheap slotted sensors can double-count when a slot edge is slow or the disc wobbles, so check the pulse counts by hand before tuning.

Tuning the gain

  1. Set KP to 0 and drive a fixed distance, for example 2 m along a taped line. Note the drift at the end. This is your uncorrected baseline.
  2. Set KP to 0.3 and repeat three times. Note the drift each time.
  3. Raise KP in steps until the path starts to snake, then back off to about half that value.
  4. Repeat on a different floor and with a different battery charge, because both change the motors.

Test plan

  1. Count check: turn each wheel by hand through ten full turns and confirm the printed count is ten times your pulses per turn. A drifting count means a bad disc or a loose sensor.
  2. Motor direction: confirm both wheels drive forward with the wheels off the ground. Swap motor leads, not code, if a side is reversed.
  3. Uncorrected drift: run with KP at 0 along a 2 m taped line and measure the sideways error.
  4. Corrected drift: run with your tuned KP over the same line three times and compare the errors with the baseline.
  5. Battery effect: repeat with a part-discharged pack. A good controller still holds the line as the speed falls.
  6. Stall: hold one wheel briefly. The error grows and the correction saturates at MAX_CORRECTION. Release it quickly so the motor does not overheat.

What usually goes wrong

SymptomLikely causeFix
Counts do not changeSensor not powered, disc not in the gap, or wrong pinCheck 5 V, the gap and D2/D3
Counts jump by extra pulsesNoise or a wobbling discShorten wires, keep them away from motor leads, fit the disc true
Robot snakes left and rightGain too highLower KP
Still curves to one sideGain too low, or a wheel is slippingRaise KP, clean the wheels, add weight over the drive wheels
Starts straight, drifts laterSmall count error builds up over a long runCheck slot counts and consider a higher-resolution encoder
Motors do not run at low PWMDead band of the motors and the L298N voltage dropRaise BASE_SPEED

Upgrades

  • Fixed-distance moves: convert pulses to centimetres using the wheel diameter, then stop after a set number of pulses.
  • Speed control: measure pulses per interval and control speed, not position, for smoother motion.
  • Direction sensing: use quadrature encoders so the count rises and falls with direction.
  • Odometry: combine both counts with the wheel base to track position, as in the differential drive odometry guide.

Cost and parts notes

The ₹1,610 total is an illustration for the parts above and does not include shipping. The L298N guide explains the voltage drop that makes a 7.4 V pack behave like a 5.8 V one at the motors. Use the cost calculator to compare sellers.

Common questions

Do I need the encoders if I only drive short distances?

For short, slow runs the drift may be small enough to ignore. Encoders pay off when you drive a set distance, repeat a path or run at different battery levels.

Why not use a gyroscope instead?

A gyro corrects heading, an encoder corrects wheel travel. Many robots use both. This build keeps to encoders because they are cheaper and easy to understand.

What happens when the pulse count overflows?

The counters are long integers, so they take a very long time to overflow at these speeds. For long logs, reset them at the start of each run.

Can I use an ESP32 instead?

Yes. Any GPIO can take an interrupt on an ESP32, so the pin choices are looser. Check the board's rules for which pins are safe to use.

Last reviewed: November 2026. This is a design worksheet with illustrative costs. It has not been bench-tested by this site; pulse counts, gain and speeds are starting values, and measurements from your build are welcome.

More builds

Next builds to try

Free: the final-year project pack

A shortlist with BOMs, a report outline that matches the usual chapter scheme, a costing sheet and 40 viva questions. Built for the February–April submission season.

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.