Calibrating a 2WD robot to drive a metre and turn 90 degrees without encoders: design worksheet and code

A ₹1,440 design worksheet for making a basic two-wheel robot move reliably by distance and angle using timed moves, with a calibration routine, a square test and the heading-error maths for mismatched wheels.

About the author · Sources, testing and AI policy

  • Parts₹1,440
  • Bench time3–5 h
  • LevelBeginner
  • ReviewedNov 2026

A ₹1,440 way to make a cheap robot drive “50 cm, then turn 90 degrees” without encoders. You measure how far it goes per second at one speed and how fast it turns, store those two numbers, and let the sketch convert distances and angles into times. The page also shows, with numbers, why it will always drift and how much.

Scope: beginner. This is a design worksheet with illustrative costs. It has not been bench-tested by this site. The drift table was computed by script. The speed and turn rate in the sketch are examples, not facts about your robot: you must measure your own, and they change with battery charge and floor. Open-loop driving drifts, so treat it as a teaching step before encoders.

How it works

At a fixed PWM a robot moves at a roughly constant speed. If you measure that it covers 100 cm in 3.3 seconds, its speed is about 30 cm/s, and driving 50 cm takes 50 / 30 = 1.67 seconds. Spinning on the spot at the same PWM turns the robot at a roughly constant rate, for example 150 degrees per second, so 90 degrees takes 0.6 second.

Wheels never match exactly. If the right wheel is just 1 percent faster than the left, the robot curves. The two wheel-speed difference over 1 m is 1 cm, spread across a 13 cm wheel base, which turns the robot by 1 / 13 radians, about 4.4 degrees, and leaves it about 3.8 cm to one side by the end of the metre. Calibration can trim this with a small correction factor on one wheel.

Bill of materials

PartQtyUnitTotal
Arduino Uno R3 (or compatible)1₹550₹550
L298N motor driver board1₹180₹180
2WD chassis kit with two gear motors and wheels1₹450₹450
Battery holder with two 18650 cells1₹210₹210
Tape measure and masking tape (for the test course)1₹50₹50
Parts total₹1,440

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 numbers

Wheel speed mismatchHeading error after 1 mSideways offset after 1 m
0.5%2.2°1.9 cm
1%4.4°3.8 cm
2%8.8°7.7 cm
5%22°19 cm
Heading error = mismatch × 100 cm / 13 cm wheel base (radians), offset ≈ 100 cm × heading error / 2. These use an example 13 cm wheel base.

Example move times at 30 cm/s: 25 cm takes 0.83 s, 50 cm takes 1.67 s, 100 cm takes 3.33 s. At 150 degrees per second a 45 degree turn takes 0.3 s and 180 degrees takes 1.2 s. Replace these with your own measurements.

Power and wiring

PartConnects toNote
L298N ENA / IN1 / IN2D5 / D7 / D8Left motor
L298N ENB / IN3 / IN4D6 / D9 / D4Right motor
BatteryL298N 12V input and Uno VINCheck your pack voltage against the board limits
Use a fresh, fully charged battery for every calibration run and for every test; speed changes as the battery drains.

The sketch

// Open-loop drive and turn calibration: Uno + L298N
// Design worksheet: measure CM_PER_S and DEG_PER_S on YOUR robot.

const int L_EN = 5, L_IN1 = 7, L_IN2 = 8;
const int R_EN = 6, R_IN3 = 9, R_IN4 = 4;
void drive(int left, int right) {   // -255..255 per side
  digitalWrite(L_IN1, left >= 0);  digitalWrite(L_IN2, left < 0);
  digitalWrite(R_IN3, right >= 0); digitalWrite(R_IN4, right < 0);
  analogWrite(L_EN, constrain(abs(left), 0, 255));
  analogWrite(R_EN, constrain(abs(right), 0, 255));
}
void motorsInit() {
  pinMode(L_EN, OUTPUT); pinMode(L_IN1, OUTPUT); pinMode(L_IN2, OUTPUT);
  pinMode(R_EN, OUTPUT); pinMode(R_IN3, OUTPUT); pinMode(R_IN4, OUTPUT);
}

const int DRIVE_PWM = 160;
const int TURN_PWM = 150;
float CM_PER_S = 30.0;        // measured: distance covered per second at DRIVE_PWM
float DEG_PER_S = 150.0;      // measured: spin rate at TURN_PWM
float RIGHT_TRIM = 1.00;      // multiply right wheel PWM to fix a curve (try 0.97 to 1.03)

void driveCm(float cm) {
  unsigned long ms = (unsigned long)(fabs(cm) / CM_PER_S * 1000.0);
  int dir = cm >= 0 ? 1 : -1;
  drive(dir * DRIVE_PWM, dir * (int)(DRIVE_PWM * RIGHT_TRIM));
  delay(ms);
  drive(0, 0);
  delay(300);
}

void turnDeg(float deg) {                 // positive = clockwise (right)
  unsigned long ms = (unsigned long)(fabs(deg) / DEG_PER_S * 1000.0);
  int dir = deg >= 0 ? 1 : -1;
  drive(dir * TURN_PWM, -dir * TURN_PWM);
  delay(ms);
  drive(0, 0);
  delay(300);
}

void setup() {
  motorsInit();
  delay(3000);                            // time to place the robot
  for (int i = 0; i < 4; i++) {           // 50 cm square
    driveCm(50);
    turnDeg(90);
  }
}

void loop() {}

To measure CM_PER_S, drive for exactly 3 seconds on your test course, measure the distance, and divide by 3. To measure DEG_PER_S, spin for 2 seconds, measure the angle with a protractor drawn on the floor, and divide by 2. If the square does not close, change the numbers, not the logic, and repeat until it ends within a few centimetres of where it started. It will not close perfectly. The sketch has been proofread but not compiled on this site.

Test plan

  1. Straight line: drive 3 seconds along a taped metre line, measure the distance and any sideways drift, and compute CM_PER_S.
  2. Trim: adjust RIGHT_TRIM until the robot drives straight over the metre.
  3. Turn rate: spin for 2 seconds with a protractor on the floor, and compute DEG_PER_S.
  4. Square test: run the 50 cm square on a large sheet and measure how far from the start it ends.
  5. Repeat: run it five times and note how much the end point varies.
  6. Battery effect: repeat with a half-discharged pack and compare.

What usually goes wrong

SymptomLikely causeFix
Curves left or rightWheel speed mismatchAdjust RIGHT_TRIM
Square does not closeTurn angle wrong or driftRetune DEG_PER_S and the trim
Results change from day to dayBattery or floor changesRecalibrate on each use
Different results on carpetWheel slip changesCalibrate on the surface you will use
Turns are inconsistentStatic friction at startUse a short ramp-up and longer turns

Upgrades

  • Encoders: add wheel encoders to measure distance directly.
  • Gyro: use an MPU6050 for accurate turns.
  • Odometry: track position from encoders, as in the differential-drive worksheet.
  • Path list: store a list of moves and run them in sequence.

Cost and parts notes

The ₹1,440 total is an illustration and does not include shipping. Use the cost calculator to compare sellers.

Common questions

Why does the robot never drive exactly straight?

No two motors and wheels are identical, and even a 1 percent difference curves the path.

Why recalibrate when the battery changes?

Speed depends on voltage, so a drained pack makes the robot slower and the timed distances shorter.

Is it good enough for a maze?

For short, forgiving courses, yes. For anything longer, encoders or a gyro are needed.

What is open-loop?

Driving without measuring the result. The robot trusts its timing and never checks where it actually is.

Last reviewed: November 2026. This is a design worksheet with illustrative costs. It has not been bench-tested by this site; library names, pins and limits must be checked against your board and the current library versions, 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.