Distance sensors compared: ultrasonic, infrared and time of flight

What ultrasonic, infrared and time-of-flight sensors each measure, where each one fails (soft surfaces, sunlight, glass) and what a beam width costs you. Three demos run on a live robot, comparing one distance reading with the 8 by 8 depth grid on the same scene.

Guidefree, runs in your browser

Three sensors turn up again and again on small robots, and they measure distance in three different ways. An ultrasonic sensor sends a click and times the echo. An infrared sensor shines a spot of light and watches where the reflection lands. A time of flight sensor fires a pulse of laser light and times that. All three hand your program a number in centimetres, and all three are wrong in their own way: one is fooled by a jumper, one by sunlight, one by a window. The other half of the story is that a single number is a thin description of the world in front of a robot, whichever sensor produced it. On this page a robot drives at a wall with a doorway in it, and each demo below is a real program you can change and run.

What each one actually measures

Ultrasonic (the HC-SR04 is the common one). It chirps at 40 kHz, well above hearing, and times the echo. Sound travels about 343 metres per second in air, so an object a metre away answers in about 5.8 milliseconds, which is why these sensors cannot be read much faster than 20 times a second. The beam is a cone of roughly 15 degrees for the part that matters and spills wider still. It works on glass, on white walls and in complete darkness, and it costs about two pounds.

Infrared, meaning the analogue Sharp sensors such as the GP2Y0A21. It shines an infrared spot and measures the angle the reflection comes back at, which is triangulation rather than timing. The beam is narrow. Its output is a voltage, and the curve is not a straight line: it climbs steeply close in, flattens out far away, and below about 10 cm it doubles back, so one voltage means two distances.

Time of flight (the VL53L0X, or the 8 by 8 VL53L5CX on this robot). It fires an invisible laser pulse and times the return with a photon counter. Light covers a metre in about 3.3 nanoseconds, so the timing is the hard part and the physics is the easy part. It reads in millimetres, it can be read 30 to 60 times a second, and the VL53L5CX version measures 64 distances at once in an 8 by 8 grid across a 45 degree view.

ultrasonic infrared (Sharp) time of flight
measures echo time of a 40 kHz click angle of a reflected IR spot flight time of a laser pulse
typical range 2 cm to 4 m 10 cm to 80 cm 5 cm to 2 m, more in the dark
beam wide cone, about 15 degrees narrow narrow, or a grid of narrow zones
readings per second about 20 25 to 50 30 to 60
fooled by soft and angled surfaces sunlight, dark surfaces, close range glass, shiny surfaces, sunlight
reads glass yes poorly no
works in the dark yes yes yes
The three sensors side by side: a wide ultrasonic cone, a narrow infrared spot and a fan of time-of-flight rays, with what fools each onerobotultrasonica cone about 15 degrees across26 cm wide at 1 m: it reports the nearestthing anywhere in the conefooled by soft and angled surfacesrobotout, and back at an angleinfrareda narrow spot, read by its angleone distance, and below about 10 cmone voltage means two of themfooled by sunlight and dark surfacesrobottime of flight45 degrees, in 8 columns9.9 cm between rays at 95 cm,3.7 cm at 33 cmfooled by glass and sunlight70 cm from the robot
The same patch of room, seen three ways, drawn to scale out to 70 cm. The ultrasonic cone is 26 cm across at a metre and answers with the nearest thing in all of it. The infrared spot is narrow and is read by the angle it comes back at. The time-of-flight grid splits its 45 degrees into 8 columns, which land 9.9 cm apart on a wall 95 cm away.

Where each one fails

Soft surfaces beat ultrasonic. A jumper, a curtain, foam or a cat absorbs a 40 kHz click instead of reflecting it, and no echo comes back. The sensor reports its maximum reading, which your program reads as "nothing there".

Angled surfaces beat ultrasonic too. Sound bounces off a smooth hard wall the way light bounces off a mirror. Meet the wall square on and the echo comes straight back. Meet it at more than about 30 degrees off square and the echo goes off sideways and never returns, and a robot driving obliquely at a wall sees clear space.

Sunlight beats both of the light-based sensors. Sunshine is full of infrared. An infrared sensor's detector is swamped by it, and a time of flight sensor's photon counter has to pick its own handful of returning photons out of that flood, so its range drops sharply outdoors. This is the main reason a robot that works in a corridor stops working by a window.

Glass and mirrors beat time of flight. The laser pulse goes straight through the window, or reflects off a shiny floor at an angle and never comes back, and the robot reads the tree in the garden or nothing at all. Ultrasonic sensors have no trouble with glass, which is why robot vacuums often carry both.

Dark, matt surfaces shorten the light-based sensors. Black cloth reflects very little infrared, so a time of flight sensor that reads 2 metres against a white wall may manage well under a metre against a black one.

Narrow beams miss things, wide beams smear them. That one applies to every sensor here, and it is what the demos below are about. The simulator models the time of flight sensor, so the failures above are described here rather than demonstrated. The beam is a different matter: that you can measure.

What one number hides

The mat has a wall across it, 95 cm in front of the robot, with a doorway 20 cm wide in the middle. The robot slides sideways across the doorway and prints distance(), one number, the whole way.

Sliding 27 cm sideways past a 20 cm doorway: the single reading jumps from about 95 cm to about 176 and back, and it finds the gap over a window only 12.2 cm wide.
The program
from bugbot import *
connect()

# change this number and press Run
SWEEP = 4            # how fast the robot slides sideways, cm/s

START = (100, 25)    # where the robot starts on the mat, cm

def where():
    x, y = position()
    return START[0] + x, START[1] + y

left(30, distance=12)                # start to the left of the doorway
wait(0.5)
far = []
for tick in range(65):               # 6.5 seconds
    d = distance()
    x, y = where()
    plot("distance cm", d)
    plot("x on the mat cm", x)
    if d > 130:
        far.append(x)
    drive(0, SWEEP * 100 / 15, 0)
    wait(0.1)
stop()
x, y = where()
print("slid from", round(START[0] - 12), "to", round(x), "cm")
print("it read past the wall from x =", round(far[0], 1), "to", round(far[-1], 1),
      "cm: a window", round(far[-1] - far[0], 1), "cm wide in a doorway 20 cm wide")
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

The chart is a plateau at about 95 cm, a cliff up to about 176, and a cliff back down. The number is not wrong at any point. It is answering a narrower question than the one the program wants to ask: not "is there a way through", but "what is straight ahead of this one line".

Two things follow. The first is that a robot with one distance reading can only find a gap by driving until the gap happens to be in front of it. The second is the size of that window: the doorway is 20 cm wide and the reading found it over 12.2 cm. The missing 8 cm is the beam.

distance() on this robot is the two middle columns of the depth sensor, which look 2.8 degrees to each side of straight ahead. At 95 cm those two lines of sight are 9.3 cm apart, so both of them are inside a 20 cm doorway only while the robot is within about 5 cm of its middle. A real sensor is worse than this, because each of its zones is a cone rather than a line, and the number it returns is the nearest thing anywhere in that cone.

The two middle lines of sight are 9.3 cm apart at the wall, so both of them are inside a 20 cm doorway only over a narrow window of robot positionswallwall20 cm doorwayrobotthe two middlecolumns, 5.6 degrees apart9.3 cm apart here10.7 cm of standing room where both raysget through: the demo measured 12.2
Why a 20 cm doorway showed up over only 12.2 cm of sliding. distance() is the two middle columns of the grid, 5.6 degrees apart, which is 9.3 cm apart by the time they reach a wall 95 cm away. Both have to be inside the doorway for the reading to say the way is clear, which leaves 10.7 cm of standing room in the middle.

The same scene as 64 numbers

Nothing about the scene changed. This demo reads the whole 8 by 8 grid instead, plots the nearest reading in the left three columns, the middle two and the right three, and draws on the mat where every reading landed.

The same slide with tof_grid(): the middle two columns find the doorway on 29 of the 65 readings, and at least one of the eight columns finds it on all 65.
The program
from bugbot import *
import math
connect()

# change this number and press Run
SWEEP = 4            # how fast the robot slides sideways, cm/s

START = (100, 25)    # where the robot starts on the mat, cm
AZ = [-19.7, -14.1, -8.4, -2.8, 2.8, 8.4, 14.1, 19.7]   # where each column looks

def where():
    x, y = position()
    return START[0] + x, START[1] + y

left(30, distance=12)
wait(0.5)
grid = tof_grid()
for row in range(8):
    print("row", row, grid[row * 8:row * 8 + 8])

seen, middle, any_of_eight = [], 0, 0
for tick in range(65):               # 6.5 seconds
    grid = tof_grid()
    row = grid[24:32]                # row 3, the one that looks straight ahead
    x, y = where()
    for col in range(8):
        if row[col] < 300:
            a = math.radians(heading() + AZ[col])
            seen.append((x + row[col] * math.sin(a), y + row[col] * math.cos(a)))
    draw("what the grid saw", seen[-400:], "red", "dots")
    plot("left three cm", min(row[0:3]))
    plot("middle two cm", min(row[3:5]))
    plot("right three cm", min(row[5:8]))
    if min(row[3:5]) > 130:
        middle += 1
    if max(row) > 130:
        any_of_eight += 1
    drive(0, SWEEP * 100 / 15, 0)
    wait(0.1)
stop()
print("the middle two columns found the doorway on", middle, "of", tick + 1, "readings")
print("at least one of the eight columns found it on", any_of_eight)
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

The red dots on the mat draw the wall out as the robot slides: two runs of dots with a gap between them, which is the doorway. The left and right lines on the chart sit at about 95 cm the whole time, because the outer columns are looking at wall wherever the robot stands. Only the middle line jumps.

The printed grid is worth a look as well:

row 0 [400, 400, 400, 400, 400, 400, 400, 400]
row 1 [400, 400, 400, 400, 400, 400, 400, 400]
row 2 [400, 400, 400, 400, 400, 400, 400, 400]
row 3 [101,  99,  94,  92, 174, 177,  97, 101]
row 4 [ 23,  28,  25,  27,  23,  26,  27,  26]
row 5 [ 17,  17,  14,  10,  15,  13,  12,  16]
row 6 [ 11,  12,   6,  10,  13,  12,  11,   8]
row 7 [  6,   9,   7,   7,   6,   8,   9,   8]

64 numbers, and only 8 of them are about the wall. The sensor sits 3 cm above the mat and is tilted so that rows 2 and 3 look level. The top three rows pass over an 8 cm wall and see nothing, which is why they read 400. The bottom four rows are looking at the mat itself, 6 to 28 cm in front of the robot. Row 3 is the row with the wall in it, and the 174 and 177 in the middle of it are the doorway.

That is the shape of the job with any grid sensor: most of the picture is floor and sky, and the program has to know which part of it is the part it cares about.

What a beam width means in practice

The 45 degree view is a fixed angle, so the patch of world it covers grows with distance. This demo drives at the wall and works out how far apart the eight columns land, from the sensor's own readings.

Driving from 95 cm to 33 cm from the wall: the eight columns cover 69.5 cm of it at the start, with 9.9 cm between neighbouring rays, and 26.2 cm at the end, with 3.7 cm between them.
The program
from bugbot import *
import math
connect()

# change this number and press Run
CREEP = 8            # how fast the robot drives at the wall, cm/s

START = (100, 25)    # where the robot starts on the mat, cm
AZ = [-19.7, -14.1, -8.4, -2.8, 2.8, 8.4, 14.1, 19.7]

def where():
    x, y = position()
    return START[0] + x, START[1] + y

left(40, distance=45)                # move across, away from the doorway
wait(0.5)
print("standing at", [round(v) for v in where()])
landed = []
for tick in range(90):               # 9 seconds
    grid = tof_grid()
    row = grid[24:32]
    x, y = where()
    pts = []
    for col in range(8):
        if row[col] < 300:
            a = math.radians(heading() + AZ[col])
            pts.append((x + row[col] * math.sin(a), y + row[col] * math.cos(a)))
    draw("the eight rays", pts, "red", "squares", 4)
    if len(pts) == 8:
        landed = pts
        plot("fan width cm", pts[7][0] - pts[0][0])
        plot("step between rays", (pts[7][0] - pts[0][0]) / 7)
        plot("wall ahead cm", distance())
    drive(CREEP * 5, 0, 0)
    wait(0.1)
stop()
x, y = where()
print("stopped", round(120 - y, 1), "cm from the wall")
print("the eight rays landed", round(landed[7][0] - landed[0][0], 1), "cm apart")
Put this demo on your own site

Paste it into a school website, Moodle, Google Sites or a blog. More options on the embed page.

At 95 cm the eight readings are spread over 69.5 cm of wall, which is 9.9 cm between one ray and the next. At 33 cm they cover 26.2 cm, 3.7 cm apart. The picture gets finer as the robot gets closer, and it is coarse exactly when the robot most wants warning.

That is what a beam width costs you, and it cuts both ways:

  • A narrow beam misses things. Anything smaller than the gap between the rays can sit between two of them and never be seen. A chair leg 4 cm across, a metre away, fits comfortably between two of this robot's rays.
  • A wide beam smears things. An ultrasonic sensor with a 15 degree cone reports the nearest thing anywhere in that cone as though it were straight ahead. A door frame off to one side reads as an obstacle in front of you, and a 20 cm doorway a metre away is invisible, because the cone is 26 cm wide by the time it gets there.

Neither is a fault to be fixed. It is the reason robots carry more than one sensor, sweep them about, or use a grid.

The fan of rays spreads with distance, so the gaps between the readings grow and a thin object can sit between two of themrobot3.7 cm apart at 33 cm9.9 cm apart at 95 cma 4 cm chair leg,missed between two rays
The 45 degrees are fixed, so the picture is fine close in and coarse far off: the eight readings land 3.7 cm apart at 33 cm and 9.9 cm apart at 95 cm. Anything thinner than that gap, such as a 4 cm chair leg, can sit between two rays and be missed. A wide ultrasonic cone has the opposite fault: it sees the leg, and reports it as though it were straight ahead.

Choosing one

  • Indoors, cheap, and the obstacles are big and hard: ultrasonic. Watch for soft furnishings and for walls met at an angle.
  • A short, fixed range with a fast answer, such as a table edge or a gripper's jaws: infrared, or a small time of flight sensor. Keep sunlight off it.
  • Millimetres, or a whole grid at once: time of flight. Keep it away from glass, and remember what happens outdoors.
  • Anything that matters: two different kinds, and a program that notices when they disagree. A robot vacuum carries a bumper as well, because the only reading you can trust completely is the one that says you have already hit something.

Where this is taught

Questions

Which is better, ultrasonic or infrared?

Neither, in general. Ultrasonic reads further, works on glass and in the dark, and is fooled by soft or angled surfaces. Infrared is quicker and narrower, and is fooled by sunlight, dark surfaces and anything closer than its minimum range. Pick by the surfaces and the lighting you expect.

How accurate is an HC-SR04 ultrasonic sensor?

Within a centimetre or two on a hard flat surface met square on, out to about 4 metres. The accuracy depends on the speed of sound, which changes with temperature by about 0.6 metres per second per degree, so a sensor calibrated in a cold room reads a few per cent out in a warm one. Its bigger errors are not accuracy at all: they are the readings it misses entirely.

Why does my ultrasonic sensor give wrong or random readings?

Usually the echo is not coming back. A soft surface absorbs the click, and an angled one reflects it away. Both show up as a maximum reading or a wild one. The other common cause is reading it too often: the echoes of the last ping are still in the room, so leave about 60 milliseconds between readings.

Does sunlight affect a time of flight sensor?

Yes. Sunlight is full of infrared, and the sensor has to pick its own returning photons out of that background, so its range falls sharply outdoors. A sensor that reads 2 metres indoors may manage a fraction of that in direct sun.

Why can't a time of flight sensor see glass?

The laser pulse goes through it. What comes back is whatever is on the other side, or nothing, so the robot reads past the window and drives into it. An ultrasonic sensor handles glass well, since it is a hard flat surface for sound.

What is the beam width of a distance sensor, and why does it matter?

It is the angle the sensor looks over. Because it is an angle, the patch it covers grows with distance: the sensor on this page covers 69.5 cm of a wall 95 cm away and 26.2 cm of one 33 cm away. A wide beam reports the nearest thing anywhere in the cone, so things off to the side look as though they are straight ahead. A narrow beam can miss a thin object entirely.

What is a VL53L5CX, and how is it different from a VL53L0X?

Both are time of flight sensors. A VL53L0X gives one distance, straight ahead. A VL53L5CX gives 64 at once, an 8 by 8 grid across a 45 degree view, which is enough to tell a doorway from a wall without turning the robot. The second demo on this page reads exactly that grid.

How many distance sensors does a robot need?

One will follow a wall or stop before a bump. Finding a way through things needs either a grid, several sensors pointing different ways, or one sensor that the robot sweeps by turning. Most real robots use more than one kind, because their failures are different: what fools sound does not fool light.

Are distance sensors on the GCSE or A level specification?

Not as hardware. GCSE and A level Computer Science specifications (AQA, OCR, Edexcel, Eduqas) cover sensors in general terms, as the input side of an embedded or control system, and that is where this fits. The programming, a loop that reads a value and decides, is squarely on the GCSE specification.

Learn it step by step

These lessons build the same ideas one at a time, each with tasks the simulator marks.

  1. F2.8 Project: parking sensor Decisions and loops, GCSE
  2. 2.2 The distance sensor Sensing, Robot club
  3. 2.3 The depth grid Sensing, Robot club
  4. 2.4 Getting round things Sensing, Robot club
  5. 2.6 Project: the maze Sensing, Robot club
  6. U4.1 A reading is a distribution Noise and filtering, University
  7. U4.4 Outliers Noise and filtering, University
  8. U8.2 The inverse sensor model Mapping, University
  9. U8.5 Ray casting the map Mapping, University
Open the lessons