A drone can stop in the air and stay there while the wind pushes it about. An egg incubator keeps its eggs at 37.5 °C for three weeks, never much warmer or colder. Nobody is turning knobs inside either: a small computer measures, again and again, and corrects a little each time. How much it corrects turns out to be the whole secret.
Start with something simpler: a water tank. Water comes down a long pipe from a valve you control, so whatever you do at the valve reaches the tank 2 seconds later, and the house below uses water all the time. Keep the water at the dashed line, 60 cm, for a minute. The valve has only two settings, as many real switches do: shut, or fully open.
Before you play much: with a valve that is only ever shut or fully open, what do you think happens?
Pick an answer to see why.
You need: nothing to play with the tank. For the programs (Python, from Grade 8), loops, lists and functions, and the micro:bit board from Grade 8.
Try it
- a.Only shut or open. Play a few times with the tank above. Can you keep it within 2 cm of the line? Watch what happens just after you shut the valve.
- b.Any amount. This tank has a valve you can open part of the way. Fill it to the line, then look for a setting that keeps the water still.
How it works
Every machine that holds something steady goes round the same loop. It measures (the water’s level, the box’s temperature, the drone’s height). It compares that with the target, the value it should hold; the difference is the error, here 60 − the level. Then it corrects: it sets the valve, the heater or the motors. The world changes, and the loop goes round again. Acting on what was just measured is called feedback, and a program or device that runs it is a controller.
The only question is what to do in the third box. There are two simple answers.
- On/off control: if the error says too low, fully open; otherwise, shut. The water level controller in Grade 8’s Physical Computing switches its pump fully on or fully off; so does a simple electric iron’s thermostat.
- Proportional control: correct in proportion to the error. A long way below the line, open wide; a little below, open a little; on the line, shut. For example, open the valve 5% for every centimeter below the line: 40 cm below gives 200%, so fully open; 10 cm below, 50%; 1 cm below, 5%.
On/off wobbles for two reasons: water takes time to arrive, and the valve knows only two speeds while the house needs a third, 1 cm a second. A proportional rule slows down as it gets close, so little water is still in the pipe when it arrives.
With the proportional rule (5% for every centimeter below 60), how far open is the valve when the water is at 54 cm?
Pick an answer to see why.
Program it
The tank in Python (from Grade 8)
A list, pipe, holds the valve’s last 2 settings, oldest first. Each second the new setting joins the end (append) and the oldest leaves the front (pop(0)); the level goes up by what arrived (5 cm a second when fully open) and down 1 cm for the house. First, on/off:
level = 20 # cm of water in the tankpipe = [0, 0] # the valve's last 2 settings, still coming down the pipefor second in range(1, 41): if level < 60: valve = 100 else: valve = 0 pipe.append(valve) arriving = pipe.pop(0) level = level + 5 * arriving / 100 - 1 if second % 2 == 0: print(second, round(level, 1), valve)Output
2 18.0 1004 26.0 1006 34.0 1008 42.0 10010 50.0 10012 58.0 10014 66.0 016 69.0 018 67.0 020 65.0 022 63.0 024 61.0 026 59.0 028 57.0 10030 65.0 032 68.0 034 66.0 036 64.0 038 62.0 040 60.0 0Each line is the second, the level and the valve. In the first 2 seconds the level falls, though the valve is open: the water hasn’t arrived yet. It passes the line in second 13, the valve shuts at second 14, and the level goes on up to 70 cm at second 15 (the program prints only even seconds). Then it falls for 13 seconds, and the next round starts again from 57.
Let a function set the valve
A rule for the valve is a function: given the level, it gives back how far open, 0 to 100. The tank below calls your valve every second and does the same sums as the program above. Press Fill. Then change the rule (Edit) to the proportional one, return 5 * (60 - level), and fill again: the last two runs stay on the graph, faint.
def valve(level): if level < 60: return 100 return 0Three valves, one program
Now let one program try three rules and report how each did: on/off, the gentle proportional rule (5% a centimeter), and a strong one (30% a centimeter). fill takes the rule as its input, as the Moon lander’s fly took a pilot.
def on_off(level): if level < 60: return 100 return 0 def gentle(level): return 5 * (60 - level) def strong(level): return 30 * (60 - level) def fill(name, valve): level = 20 pipe = [0, 0] levels = [] for second in range(60): v = valve(level) if v > 100: v = 100 if v < 0: v = 0 pipe.append(v) arriving = pipe.pop(0) level = level + 5 * arriving / 100 - 1 levels.append(level) last = levels[30:] print(name, 'highest', round(max(levels), 1), '| last 30 s:', round(min(last), 1), 'to', round(max(last), 1)) fill('on_off', on_off)fill('gentle', gentle)fill('strong', strong)Output
on_off highest 70.0 | last 30 s: 57.0 to 69.0gentle highest 58.1 | last 30 s: 56.0 to 56.0strong highest 68.0 | last 30 s: 57.2 to 65.7The same idea on the micro:bit
An egg incubator is a box with a heater and a sensor. On the board below, the heater is wired to pin 0 (on a real board through a relay: as Grade 8 showed for a pump, a pin is far too weak to run it). pin0.write_digital(1) switches it fully on, pin0.write_digital(0) off, and temperature() reads the box in whole degrees.
A heater warms its coil, and the coil warms the air. When the air reaches 37 °C and the heater switches off, the hot coil goes on warming the box, as the water in the pipe kept arriving.
For a gentle heater, pin0.write_analog(n), with n from 0 to 1023, switches the pin on and off many times a second, on for that share of the time (PWM, pulse-width modulation). The coil only feels the average, so 511 is about half power. (A real heater would then be switched by a transistor: a relay can’t click that fast.)
What your program shows
Switched on and off, the tank went 10 cm past the line, to 70 cm, and in the last half minute was still swinging from 57 to 69 cm: nothing in the rule ever lets it settle. The incubator climbed to about 41 °C, 3.5 degrees above the 37.5 °C eggs do best at, and poultry experts warn that too hot does more harm than too cold.
The gentle rule never reached the line (highest 58.1 cm), and then the water held perfectly still. The gentle heater went just past 37, to 37.4 °C (the coil’s stored heat), then held steady. Correcting in proportion to the error settles them.
But look closely: the gentle tank settled at 56 cm, not 60, and the gentle heater a degree and a half short too, at about 35.5 °C. The house takes 1 cm a second, so the level only stays still with the valve 20% open, and the gentle rule opens 20% only when the error is 20 ÷ 5 = 4 cm. It needs to be off the line to open the valve at all.
The strong rule, 30% a centimeter, overshot nearly as far as on/off (68 cm) and was still swinging from 57.2 to 65.7: a big correction for a small error, arriving late, is nearly on/off again. The heater with error * 1 is exactly on/off: temperature() gives whole degrees, so a degree too cold already means full power, and the graph is the same, peak 41 °C. So a controller is tuned: strong enough to get there, gentle enough not to overshoot.
How a drone holds still
A drone is harder: its motors push it up against gravity, so the push changes the speed, and the speed changes the height, as the Moon lander’s burn did. Try the proportional rule on a drone that should hover 10 m up: push just hard enough to cancel gravity (9.8 m/s² on Earth), plus 1 m/s² more for every meter too low. The program flies it a tenth of a second at a time; brake is its second trick, 0 at first.
def hover(brake): height = 0 # m above the ground speed = 0 # m/s, upwards heights = [] for tenth in range(1, 201): # 20 seconds, a tenth of a second at a time thrust = 9.8 + (10 - height) - brake * speed if thrust < 0: thrust = 0 # the propellers can only push speed = speed + (thrust - 9.8) / 10 height = height + speed / 10 if tenth % 20 == 0: # every 2 seconds heights.append(round(height, 1)) print('brake', brake, ':', heights) hover(0)hover(2)Output
brake 0 : [14.6, 16.2, 0.3, 11.9, 18.2, 1.3, 9.1, 19.5, 3.1, 6.3]brake 2 : [6.2, 9.1, 9.8, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0]With brake 0 the drone bounces like a ball on a spring, from the ground to 20 m and back, for as long as it flies (the heights printed every 2 seconds catch it at 0.3 and 19.5): it reaches 10 m moving fast, and a rule that looks only at the height ignores the speed. The second run also pushes against the speed, 2 m/s² for every m/s, like a brake: it slows as it nears 10 m and holds there.
A real drone’s flight controller does this, much faster, for each motor, from three parts: one in proportion to the error (P), one against how fast the error is changing, as brake does (D, for derivative), and one that grows while an error lasts, to hold against a steady wind (I, for integral): a PID controller. The open-source PX4 autopilot runs such loops one inside another, for the drone’s position, speed, tilt and turning.
The tank, the heater and this drone are simple made-up models: a real house’s use changes all day, a real box loses heat faster on a cold night, and a real drone meets air resistance and gusts. Real controllers are tuned on the real machine, but the lessons hold.
Going further still
Fix the 4 cm with the I of PID: keep a running total of the error and add a little of it to the valve. While the water stays low the total grows and opens the valve wider, until the water reaches the line (the ★★★ challenge).
A gap in the middle. A pump that switches every few seconds wears out, so a roof tank’s controller starts it when the tank is low and stops it only when full (the ★★ challenge).
Challenges
★ The gentle valve (Python, from Grade 8)
Write valve(level) for the gentle rule: open the valve 5% for every centimeter below 60, but never more than 100 or less than 0.
def valve(level): # 5% for every cm below 60, from 0 to 100 return 0 print(valve(50))★★ A pump with a gap (Python, from Grade 8)
Write pump(level, on) for a pump with a gap, on saying whether it is running now: below 55 cm it runs, above 65 cm it stops, and in between it carries on as it is. Give back True to run it, False to stop it.
def pump(level, on): # True to run the pump, False to stop it return on print(pump(50, False))★★★ Hit the line (Python, from Grade 8)
The gentle valve settles short of 60, by more when the house uses more. Change only valve so the tank ends up steady on the line in every test, from different starts, with the house using 0.5 to 3 cm a second. valve is also given memory, a dictionary fill keeps from one second to the next for it to remember things in. fill(start, use) runs 2 minutes and says 'steady' (the last 20 seconds all within 0.5 cm of 60), 'wobbling' or 'off the line'.
def valve(level, memory): return 5 * (60 - level) def fill(start, use): level = start pipe = [0, 0] memory = {} levels = [] for second in range(120): v = valve(level, memory) if v > 100: v = 100 if v < 0: v = 0 pipe.append(v) level = level + 5 * pipe.pop(0) / 100 - use if level < 0: level = 0 if level > 100: level = 100 levels.append(level) last = levels[-20:] if max(last) - min(last) > 1: return 'wobbling' if all(abs(x - 60) <= 0.5 for x in last): return 'steady' return 'off the line' print(fill(60, 2))Questions
Why does a heater that switches fully on and off go past its target temperature?
Pick an answer to see why.
Name the four steps of a feedback loop, and say what each is for a drone holding its height.
Answer
The gentle tank settled at 56 cm with the house using 1 cm a second. Without a program, where would it settle if the house used 2 cm a second? Explain.
Answer
A drone controller that looks only at the height bounced between the ground and 20 m. What did the second controller add, and why did it help?
Answer
An incubator’s heater can only be on or off. Suggest one way to make its temperature swing less, and say what it costs.
Answer
Where to go next
- Can you land on the Moon? The drone’s sums, flown by your own autopilot.
- Grade 8, Physical Computing: the micro:bit board, its sensors, and the water level controller.
- Grade 8, Functions: functions that give back an answer, like
valve. - How did the Moon landing’s computer survive an overload? The computer that flew Eagle down (Grades 9–12). (Coming soon.)
