Mealy machines: FSMs with output
Output on every transition, tracing a Mealy machine, and a search, approach and stop controller for the robot.
Do this lesson in the simulatorThe machines in the last lesson only said yes or no at the end. A robot controller has to do something on every tick: spin, drive, stop. A finite state machine with output produces an output as it goes. In a Mealy machine, the output is produced on each transition, so it depends on both the current state and the input just read.
Labelling transitions with output
A Mealy machine has states, an input alphabet, a start state and a transition function just like before, plus an output alphabet. Each arrow is labelled input/output: the symbol that causes the transition, then the symbol it outputs. There are no accepting states. A Mealy machine is not deciding yes or no; it is translating a string of inputs into a string of outputs of the same length.
Here is a machine for a bump sensor that reads 1 while the bumper is pressed. The robot should react once per bump, not on every tick the bumper stays pressed, so the machine outputs 1 only on a rising edge: the tick where the input changes from 0 to 1.
| Current state | Input | Next state | Output |
|---|---|---|---|
| LOW | 0 | LOW | 0 |
| LOW | 1 | HIGH | 1 |
| HIGH | 0 | LOW | 0 |
| HIGH | 1 | HIGH | 0 |
LOW means "the last input was 0" and HIGH means "the last input was 1". The only transition that outputs 1 is LOW to HIGH.
Tracing a Mealy machine
A trace now records the output of every step as well as the state. Starting in LOW with the input 0110111:
| Input | 0 | 1 | 1 | 0 | 1 | 1 | 1 | |
|---|---|---|---|---|---|---|---|---|
| State | LOW | LOW | HIGH | HIGH | LOW | HIGH | HIGH | HIGH |
| Output | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
The output is 0100100: two bumps, each reported exactly once. Seven inputs give seven outputs.
In Python, each value in the table becomes a pair: the next state and the output.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
table = {
("LOW", "0"): ("LOW", "0"),
("LOW", "1"): ("HIGH", "1"),
("HIGH", "0"): ("LOW", "0"),
("HIGH", "1"): ("HIGH", "0"),
}
def translate(inputs):
state = "LOW"
outputs = ""
for symbol in inputs:
state, out = table[(state, symbol)]
outputs = outputs + out
return outputs
print(translate("0110111"))
state, out = table[...] unpacks the pair, so the state and the output are updated together, exactly as one transition does both.
Mealy and Moore
There is a second kind of machine with output, the Moore machine, where the output belongs to the state rather than to the transition: every time the machine is in a state it outputs that state's symbol. Anything one can do, the other can too, although a Moore machine may need more states. For AQA you only need Mealy machines, and a question that says "FSM with output" means one.
A robot controller as a Mealy machine
A robot's behaviour is a Mealy machine running forever. Each tick, the sensors are turned into one input symbol, the machine takes one transition, and the output is the action for that tick. For a robot that must find a marker, drive up to it and stop, the input alphabet is:
none: the marker is not in view;far: the marker is in view and more than 15 cm away;near: the marker is in view and 15 cm away or less.
The output alphabet is spin, drive and halt.
| Current state | Input | Next state | Output |
|---|---|---|---|
| SEARCH | none | SEARCH | spin |
| SEARCH | far | APPROACH | drive |
| SEARCH | near | STOP | halt |
| APPROACH | none | SEARCH | spin |
| APPROACH | far | APPROACH | drive |
| APPROACH | near | STOP | halt |
The table makes you face the awkward cases. What if the marker drops out of view while approaching, because the robot steered too hard? The row (APPROACH, none) answers that: go back to searching. A hand-written chain of if statements often forgets a case like this; a table with one row for every state and input cannot.
STOP has no transitions out: when the machine reaches it, the run is over. On the real robot, this is the design you would draw before writing any code, and the table is what you would show a teammate to check.
Task: search, approach, stop
Marker 4 is somewhere on the mat, out of view. Run the controller above as a table-driven Mealy machine so the robot finds it, drives up and stops.
sense()is written for you. It returns a pair: the input symbol ("none","far"or"near") and a steering value in degrees thatactuses.act(output, steer)is written for you. It carries out one output symbol ("spin","drive"or"halt").- Write
tableas a dictionary: each key is(state, input)and each value is(next_state, output), with all six rows from the table above. - Start in
"SEARCH". Each tick: sense, look up the transition, act on the output, move to the next state, thenwait(0.1). Stop looping when the state is"STOP". - Whenever the state changes, print the old and new state in the form
SEARCH -> APPROACH.
The robot must stop within 20 cm of the marker.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def bearing_of(cx):
return (cx - 160) * 120 / 320 # pixels to degrees
def sense():
tags = [t for t in apriltags() if t[0] == 4]
if not tags:
return "none", 0
if tags[0][3] <= 15:
return "near", 0
return "far", bearing_of(tags[0][1])
def act(output, steer):
if output == "spin":
turn_right(40)
elif output == "drive":
drive(60, 0, steer * 3)
elif output == "halt":
stop()
set_cv("apriltag")
table = {}
state = "SEARCH"
Challenges
- Add an input symbol
bumpedand a stateBACK_OFFthat reverses for a moment. Write the new rows of the table first. - Draw a Mealy machine that outputs 1 on a falling edge, when the bumper is released.
- Rewrite the edge detector as a Moore machine. How many states does it need?