PWM and motor control explained
How a percentage in a program becomes power at a motor: pulse width modulation, duty cycle, the dead band at the bottom, the ceiling at the top and what an H bridge does for direction. Three demos measure the command against the speed the robot really reaches, and they all run in the browser.
A program says drive(40, 0, 0) and the robot goes at 40 percent. Nothing in the electronics understands 40 percent. The battery has one voltage and the motor has two wires, and the only thing the chip in between can do quickly and without wasting power is switch fully on and fully off. Pulse width modulation, PWM, is the trick that turns the one into the other: switch on and off thousands of times a second, and spend 40 percent of each cycle on.
This page follows a percentage all the way from a line of Python to how fast the robot actually moves, and each demo below is a real program you can change and run. You will find the two places where "40 percent" is not 40 percent of anything: the dead band at the bottom, where the robot does not move at all, and the ceiling at the top, where asking for more gets you nothing.
Duty cycle: the one number PWM has
A PWM signal is a square wave. It has a fixed period, and inside each period it is high for a while and low for the rest. The fraction of the period it spends high is the duty cycle, and that is the number the percentage becomes.
duty cycle = time on / (time on + time off)
average voltage = duty cycle × supply voltage
At 20 kHz the period is 50 microseconds. A duty cycle of 40 percent means 20 microseconds on and 30 off, over and over, 20,000 times a second. The motor cannot react anything like that fast: its coils and its own weight smooth the pulses out, so it behaves as though it were fed 40 percent of the battery voltage steadily. On a 3.7 volt cell that is about 1.5 volts.
The reason to do it this way rather than turning a knob down is heat. A part that sits half on, dropping half the voltage while the full current flows through it, turns that power into heat and needs a heatsink. A switch that is either fully on, with almost no voltage across it, or fully off, with almost no current through it, wastes almost nothing either way. The only losses are in the brief moments of switching, which is why PWM runs hardware motor drivers, LED dimmers, phone chargers and heating elements rather than variable resistors.
Choosing the frequency is a compromise. Too low and you hear it, because the motor's windings and the robot's body vibrate at the switching frequency, which is what the whine of a cheap drone or a 3D printer is. Above about 20 kHz most people cannot hear it. Too high and the switches spend more of their time in the middle, where they do get hot, and the motor's own inductance starts to limit the current before the pulse ends. Most small robot drivers run somewhere between 8 and 30 kHz.
In a program the duty cycle is usually a number in a register. On a microcontroller you set a timer's period and a compare value, and the hardware does the switching on its own with no help from the processor: the program writes one number and goes back to whatever it was doing. That is what makes PWM cheap enough to do on every motor at once.
The dead band: percentages the robot ignores
Here is the first demo. It asks for 12 percent, waits 2 seconds, then asks for 30 percent and waits 2 more. The chart shows the command and the speed the robot's optical flow sensor measures underneath it.
The program
from bugbot import *
connect()
for percent in (12, 30):
drive(percent, 0, 0)
before = position()
for tick in range(40): # 2 seconds
plot("command %", percent)
plot("speed cm/s", flow()[1])
wait(0.05)
after = position()
print("asked for", percent, "% : moved",
round(after[1] - before[1], 1), "cm in 2 s")
stop()
12 percent duty cycle is a real voltage arriving at a real motor. It is just not enough to overcome the friction holding the robot still, so nothing happens and the flow sensor reads its own noise, a few tenths of a centimetre a second either way. Every motor has a band like this near zero, and a motor that has to move something heavy has a wide one.
The second half shows something else worth noticing: the speed line does not jump to 6.3, it curves up to it over about a quarter of a second. A motor has weight and the robot has to be accelerated, so a step in the command gives a curve in the speed. That lag is exactly what makes a PID controller overshoot if you push it too hard.
The whole range at once
Now sweep the command from 10 percent up to 130, in steps of 10, and measure the settled speed at each one. Two things the sweep has to deal with: the robot would run out of mat if it went one way the whole time, so each step drives the opposite way to the one before and the program plots the size of the speed; and a reading taken the instant the command changes catches the motor still speeding up, so each step waits 0.8 seconds and then averages 10 readings.
The program
from bugbot import *
connect()
STEP = 10
TOP = 130 # ask for more than the robot can give
way = 1
for percent in range(STEP, TOP + 1, STEP):
drive(way * percent, 0, 0) # alternate, to stay on the mat
wait(0.8) # let the speed settle
total = 0.0
for i in range(10): # average out the sensor noise
total = total + abs(flow()[1])
wait(0.02)
speed = total / 10
plot("command %", percent)
plot("speed cm/s", speed)
print(percent, "% :", round(speed, 1), "cm/s")
way = -way
stop()
The two lines on the chart are both staircases, and the shape of the gap between them is the whole story.
- The dead band, 0 to 15 percent. The command line climbs and the speed line does not leave the floor. The printed numbers say 0.7 cm/s at 10 percent, which is the flow sensor's noise rather than movement.
- The straight part, 15 to 100 percent. The speed line climbs with the command: 4.0 cm/s at 20 percent, 8.5 at 50, 14.5 at 80, 18.2 at 100. Roughly 0.18 cm/s of speed for each percent of command, in a straight line. This is the part a program can rely on.
- The ceiling, above 100 percent. 110, 120 and 130 percent all measure about 18 cm/s, the same as 100. There is nothing above fully on. The duty cycle has already reached 1 and the command is quietly clipped.
Both ends matter to anyone writing a controller. A controller that works out a push of 8 percent near the target asks for something that does nothing, so the robot stops short and stays there: that is where the steady state error in the PID guide comes from. A controller that works out 140 percent is being ignored above 100, so it is not controlling anything any more, and whatever it is adding up quietly carries on adding up. That is saturation, and lesson Saturation and windup deals with what to do about it.
Try STEP = 1 with TOP = 25 to find the edge of the dead band exactly. Nothing moves at 14 percent and the robot is going at about 3 cm/s at 15, so the speed does not rise out of the dead band, it jumps out of it. This is why a program that needs a crawl slower than that cannot get one by asking for a small percentage. It has to pulse a usable command on and off instead, which is PWM again, at a much slower rate, on top of the PWM the hardware is already doing.
The H bridge: which way the current goes
A duty cycle is a number from 0 to 1. It has no sign, so it cannot say backwards. Direction is a separate job, and the part that does it is an H bridge: four switches arranged so that the motor sits in the middle of an H, with a switch above and below each of its two terminals.
Close the top left and the bottom right, and current runs through the motor left to right. Close the top right and the bottom left, and it runs right to left, so the motor turns the other way. The PWM is applied by switching one of the closed pair on and off, which gives you both direction and speed from the same four switches.
The other two combinations are useful too. Closing both bottom switches shorts the motor's terminals together, and a spinning motor is a generator, so its own current fights the spin and it stops quickly: that is a brake. Opening all four leaves the motor free to coast. What you must never do is close both switches on the same side, because that is a wire straight from the battery to ground through two switches, and it will destroy them in microseconds. It is called shoot-through, and driver chips put a few hundred nanoseconds of dead time between one switch opening and the other closing to make sure it cannot happen.
A motor driver chip is an H bridge with that protection built in, plus current limiting and usually a temperature cut-out. You give it a PWM pin and a direction pin, or two PWM pins, and it deals with the rest.
This last demo walks the command from full ahead down through zero to full astern, and plots the speed with its sign, so you can see the dead band from both sides at once.
The program
from bugbot import *
connect()
for percent in (100, 60, 30, 12, 0, -12, -30, -60, -100):
drive(percent, 0, 0)
wait(0.7)
total = 0.0
for i in range(10):
total = total + flow()[1] # keep the sign this time
wait(0.02)
plot("command %", percent)
plot("speed cm/s", total / 10)
print(percent, "% :", round(total / 10, 1), "cm/s")
stop()
The speed line is a rough mirror image of itself about zero, with a flat step across the middle three commands where the dead band sits. Nothing in the program changed direction: the sign of the number did, and the H bridge turned that into a different pair of switches.
Real motors are not perfectly symmetrical. This robot reaches 18.2 cm/s forwards and 18.3 backwards, which is close, but a gearbox, a brush motor or a robot whose weight is not centred can easily be several percent faster one way than the other. A controller that assumes the two directions match will steer slightly off, which is one of the reasons robots drift.
From a percentage to a robot, end to end
drive(40, 0, 0) a number in a program
-> 0.40 a duty cycle
-> 20 us on, 30 us off a square wave, 20,000 times a second
-> about 1.5 V average what the motor behaves as though it has
-> a pair of switches the H bridge, which also sets the direction
-> 7.4 cm/s what the flow sensor measures underneath
Every step in that chain is either a rule you can rely on or a measurement you have to make. The duty cycle, the square wave and the average voltage are arithmetic. What the motor does with that voltage is not: it depends on friction, on the battery's charge, on the surface and on that particular motor. That is why a robot program should close the loop on something measured rather than trusting the number it sent, and why measuring your own robot's dead band, top speed and slope is worth doing before you tune anything.
Where this is taught
- Speed and precision: what the speed number in a command actually does.
- A command is a request: why the robot does not do exactly what you asked.
- Time, rate and latency: the lag between a command and the motion.
- Measuring your robot: finding your own robot's top speed and dead band.
- Controlling speed: closing a loop round a measured speed instead of trusting the command.
- Saturation and windup: what a controller should do when the command hits the ceiling.
- Servos: a different use of pulse width, where the width is a position rather than a power.
- Embedded systems: inside BugBot: where the timer that makes the pulses lives.
Questions
What is PWM in simple terms?
Pulse width modulation is switching something fully on and fully off very fast, and changing how much of each cycle is spent on, to control how much power it gets on average. A motor fed a 40 percent duty cycle behaves as though it had 40 percent of the battery voltage, because it cannot react fast enough to follow the individual pulses.
What is duty cycle?
The fraction of each PWM cycle that the signal is on, usually written as a percentage. At 20 kHz the cycle is 50 microseconds long, so a 40 percent duty cycle is 20 microseconds on and 30 off. Duty cycle times supply voltage gives the average voltage the motor sees.
How does PWM control motor speed?
The average voltage across the motor sets roughly how fast it spins, and the duty cycle sets the average voltage. Doubling the duty cycle roughly doubles the speed, in the middle of the range. It stops being true at the bottom, where friction wins, and at the top, where the duty cycle cannot go past 1.
What is a motor dead band?
The range of small commands that do not move the motor at all, because the torque they produce is less than the friction holding it still. On the robot on this page nothing happens below about 15 percent, and the speed jumps straight to about 3 cm/s at 15 rather than easing up from zero. It is the main reason a proportional controller stops short of its target.
Why does my motor not move at low PWM?
Because the torque at that duty cycle is smaller than the friction in the bearings, the gearbox and whatever the motor has to push. Raise the command until it moves and you have measured the dead band. If you need slower motion than the dead band allows, pulse a command that does work on and off, or add a gearbox.
What happens above 100 percent duty cycle?
Nothing. 100 percent is the switch held fully on, and there is no more. A program that asks for 130 percent gets 100 and no warning, which matters because the controller that asked for it now has no authority left: its output changes and the robot does not. That is saturation, and it is why controllers with an integral term need protecting against windup.
What is an H bridge and what does it do?
Four switches around a motor, two above it and two below, in the shape of an H. Closing one diagonal pair drives current through the motor one way; closing the other pair drives it the other way, so the motor runs backwards. Closing both bottom switches brakes it, and opening all four lets it coast. Closing both switches on one side short circuits the battery, which is why driver chips insert dead time.
What is the difference between PWM and an analogue voltage?
An analogue voltage is steady at the level you want, which means something has to drop the difference and turn it into heat. PWM is always fully on or fully off and lets the load average it out, so almost no power is wasted. PWM is the usual choice for motors, LEDs and heaters; a steady analogue voltage is used where the ripple would matter, such as audio or a sensor reference.
What PWM frequency should I use for a motor?
Commonly between 8 and 30 kHz for a small robot. Below about 20 kHz the switching is audible as a whine. Much higher and switching losses rise and the motor's inductance limits the current in each pulse. If you can hear your robot buzzing and it bothers you, raising the frequency past 20 kHz is the usual fix.
Is PWM on the GCSE or A level syllabus?
Not by name on the computer science specifications, although embedded systems, microcontrollers and the idea of digital outputs are. Pulse width modulation does appear in GCSE and A level Design and Technology and in electronics courses, and in A level Physics through the average power of a square wave. It comes up constantly in any practical robot project, which is the usual place students meet it.
Learn it step by step
These lessons build the same ideas one at a time, each with tasks the simulator marks.
- 1.5 Speed and precision Driving, Robot club
- 3.4 Controlling speed Control, Robot club
- 7.1 Servos Hands and feet, Robot club
- F9.8 Embedded systems: inside BugBot Logic and computer systems, GCSE
- U1.2 A command is a request The robot as a system, University
- U1.4 Time, rate and latency The robot as a system, University
- U1.6 Measuring your robot The robot as a system, University
- U5.6 Saturation and windup Feedback control, University