A proportional controller pushes by the error
A proportional controller is a rule that sets the push on a motor in proportion to the error. The error is the gap between where a joint is and where it should be. It is the simplest way to make a joint reach a target. It also shows the trade-off between speed and overshoot that every controller has.
In this lesson you simulate one joint, watch its step response and measure how far it overshoots.
By the end of this lesson
Note
I can simulate a proportional controller that reaches a target and measure its overshoot.
Key words
| Word | What it means |
|---|---|
| Sensor | a device that measures something, such as a joint angle |
| Error | the target minus the measured position |
| Gain | how strongly the controller reacts to the error |
| Overshoot | how far the joint goes past the target |
| Step response | how the position changes after the target jumps |
The cells students fill in are left blank in this preview.
A controller measures, compares and pushes
A sensor measures the joint angle. The controller subtracts it from the target to get the error, and sets the motor push in proportion: push equals gain times error.
Then the joint moves a little and the sensor measures again. This repeating loop, a few times a second or faster, is the whole of a proportional controller.
The error shrinks by a fixed fraction
Take a joint with no inertia, that moves exactly as the push says. Each second the position moves the gain times the error closer.
With a gain of 0.5 the error halves every second: 10, 5, 2.5 and so on. It gets very near the target but never overshoots.
Print the error each second with NumPy
docker exec jupyter python3 -c "import numpy as np; print(np.round(10 * np.exp(-0.5 * np.arange(0, 5.0)), 2))"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A smooth approach has no overshoot
The Run above uses a smooth version of the same idea. The error falls as 10, 6.07, 3.68, 2.23 and 1.35.
It never changes sign, so the joint never passes the target. Real joints have mass, and that changes the picture.
Watch the error shrink
target = 10
position = 0
gain = 0.5
for second in range(5):
error = target - position
print("second", second, "error", round(error, 2))
position = position + gain * error * 1
# Change gain to 1.5 and press Run.
# The error now changes sign every second. A gain that is too high overshoots.
A common mistake
Warning
Many people think a bigger gain is always better, because the joint arrives faster. A joint with weight keeps moving after the push has gone, so a high gain makes it go past the target. Too high a gain makes it swing back and forth.
A heavy joint keeps moving, so it overshoots
A real joint has inertia, so the push changes its speed rather than its position. The simulation keeps a speed. Each step adds (gain times error minus damping times speed) times dt to the speed, then adds speed times dt to the position.
The damping, the drag that slows the joint, is what stops it overshooting.
Worked example: one joint, three steps
| Step | Error | Speed | Position |
|---|---|---|---|
| Start | 10 | 0 | 0 |
| 1 | 10 | 4 | 0.4 |
| 2 | 9.6 | 7.44 | 1.144 |
| 3 | 8.856 | 10.238 | 2.168 |
The cells students fill in are left blank in this preview.
Simulate one joint with inertia
target = 10
kp = 4
kd = 1
dt = 0.1
position = 0
speed = 0
best = 0
for step in range(1, 61):
error = target - position
speed = speed + (kp * error - kd * speed) * dt
position = position + speed * dt
if position > best:
best = position
if step % 10 == 0:
print("step", step, "position", round(position, 3))
print("highest position:", round(best, 3))
# Change kd to 5 and press Run.
# The joint now creeps up and never goes past 10.
Overshoot and settling time describe a response
Two numbers summarise a step response. The overshoot is the highest position minus the target, or 0 if it never passes. The settling step is the first step after which the position stays within a small tolerance of the target.
A good controller has a small overshoot and settles quickly. You usually trade one against the other.
Repeat the first three steps of the table
docker exec jupyter python3 -c "exec('p = 0\nv = 0\nfor k in range(3):\n v += (4 * (10 - p) - v) * 0.1\n p += v * 0.1\nprint(round(p, 3))')"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Going further
In start.ipynb, run the simulation for gains of 1, 4 and 16 with damping fixed at 1. Print the overshoot of each.
Which gain gives the biggest overshoot, and which would you choose for a joint carrying a fragile load?