Skip to main content

Achoo! Crash-choo! Episode II


Some humans find it very, very hard to multi-task.  Like drink coffee, eat toast (with baked beans on top), talk and cycle... all at the same time (...and all of this just after waking up, from a bad night's sleep).  But this is where robots should be cleverer than humans.  They can do many things, at the same time (sort of).  Or they do a little bit of each thing so extremely quickly that it seems like they are doing it all simultaneously.  But, regardless, we still like to think we are cleverer than robots.  And we like to be bossy.  So we're going to order them to be better than us... and multi-task.

Let's remind ourselves:  We were able to move Rosie by controlling her remotely.  Separately, we got her to alert us when there was an object in front of her.  But - I hear your say - how do we get her to do both of those things at the same time?  Could she, for example, think through all the steps needed to drink her Starbucks latte, eat beans on toast, describe to us the taste (yummy!), and compete in the Tour de France, all at the same time?  And how does she keep track of how she's progressing with each task, and work out what to do next when things (obviously) start to go wrong?  If she's now drinking beans on coffee, she needs to stop cyclingRight?

Here's the thing.  Everything we've been doing so far has been boringly sequential... we progressed through each line of Rosie's Python code embedded deep in her brain, in order, one line at a time.  That's so boring.  So yesterday.  Today, we want to get Parallel.  As per the words to this famous song from 1981*:
Let's get parallel, parallel
I want to get parallel
Let's get into parallel
Let me hear your Rosie talk
 *Not really.

You will need to have these:

  • Raspberry Pi 3 (with motors / wheels), and Raspbian running on SDHC card
  • Computer from which you are connecting to the Raspberry Pi remotely
  • Ultrasonic range (distance) sensor and 'RGB' LED to plug into Pi's GPIO pins
  • Obstacles... lots of them

You will need to have done these things:

  1. I’ve got Pi(3) brain
  2. Why-Fi-ght the Wi-Fi?
  3. Hurrah-ndom inventions
  4. Don't reinvent the eel
  5. Achoo! Crash-choo! Episode I

You will need to do this:

  • Adapt the Python program to use a thread
  • Try and make Rosie crash!

What do you get after all this?

You may not have noticed it, but we were walking (or in the case of Rosie, crashing) into a big problem.  And that problem wasn't (for once) Tammy, the cat.

We can control Rosie remotely using our keyboard (over SSH).  And our grand plan was just to combine that bit of code with our code for the distance sensor.  Rosie can then be controlled, but also detect obstacles, at the same time.  Easy.  We just copy and paste in the new code somewhere before or after the existing code.  And we can move onto more interesting things, right?

No way, Jose.  Esto no es tan simple.  And this is why.  The code that takes keyboard input halts, and waits (patiently) on a line of code that requires a human - yeah, that's you - to enter an arrow key before carrying on.  So while it's waiting, it won't be able to do any other useful stuff, like check for approaching alien spaceships using the distance sensor.  And, likewise, once the motors have been instructed to move forwards, Rosie's application will continue to remain paused, until instructed to stop or change direction, using another keyboard key.  There's no background monitoring during these pauses.  We don't get our LED colour changes.  We don't get our auto-break.  That's right... Rosie will continue to crash into Tammy.  Booo!  It's called Blocking, and we don't like it.

So we think.  And we think some more...  What we would actually like to do is run two sets of code in parallel; one in the background, constantly checking the distance sensor, and another (the main program) that waits for, and processes, your input.  And crucially, the main program should have access to what the other bit of code is reporting back so that it can use that information to help it make decisions.

There are many different techniques in programming to achieve this; but in Python, the two most common solutions involve the use of Processes or Threads - mechanisms that seemingly allow bits of code to run in parallel.  There are endless discussions on the Internet about the differences between the two, and why you might use one or the other.  But we don't like the sound of processes (we like to make things up as we go along, you know, break the rules) so let's use threads instead.

Confused already?  Of course.  But is it all worth it?

Yes!  Because when it's all working, Rosie appears to be doing a lot more than just accepting our demands.  She is thinking for herself in the background, and constantly trying to stop us from making the wrong decisions.

This is simply too much detail:

Let's see some Blocking in action.  Temporarily create a file called 'blocking.py' (aren't we being imaginative today?) and place the following lines in the file:

touch blocking.py
nano blocking.py
...creates 'blocking.py' Python application file and open in nano

print("ROSIE: Where would you like to go now?")
destination = raw_input()
print("ROSIE: Ok, let's go to...")
print(destination)


If you now run this, you'll notice that it is waiting for you to enter something.

python blocking.py
...runs the 'blocking.py' Python code


It's only once you enter that something (and hit 'Enter') that the code carries on and completes.  This makes sense in this case, because carrying on without having the destination from you is pointless.  Where would you go?


We can also confirm this by running Python interactively. 

Sometimes we don't want to create an application (the .py file) to test our code.  We just want to run some Python commands in real-time, and see what it does.  There is nothing stopping you from running Python interactively.  To do this, from the Linux shell, simply type:

python
...launches Python shell


Here, you can run any Python code you like - interactively - to see what happens.  Use the function exit() to leave the shell at any time.  For example:

message = "Welcome to the world of Python shell"
print(message)
exit()


Start up the Python shell again.  Now, let's just type in the following line from the 'blocking.py' file:

destination = raw_input()


There; we have confirmed that the raw_input() function is Blocking, and that it waits until someone enters something.  All very useless, if you have other things that you need your robot to be thinking about and doing in the meantime.

Now, time to meet the latest of your Python friends: Thread.  He's a bit difficult.  He's moody.  He likes to work alone in most cases.  But you'll grow to like him.  After all, he has special powers; he allows one section of your code to magically run independently. Now would be a great time to ask him to do just that.

Create a new Python file called 'rosie_wheels_with_distance.py'.

touch rosie_wheels_with_distance.py
nano rosie_wheels_with_distance.py
...creates 'rosie_wheels_with_distance.py' Python application file and open using nano text editor... but you knew this already!

This is the code that hopefully achieves our aims (for now):

Most of the code will look familiar, as it is simply an ugly mash-up of 'rosie_wheels.py' and 'rosie_distance.py'.  The main points of interest are listed out below.

First things first.  We need the threading module to use... wait for it... threads.   If you are using Processes instead, the module would be called 'multiprocessing'.  Read up on the differences between threads and processes before choosing which one to use.

from threading import Thread

A new function check_distance() is created.  This essentially contains the bulk of our code from 'rosie_distance.py' which checks distance sensor input, and changes Rosie's alert level.  It's a busy function: it also does the LED colour changing, and the emergency stopping of motors.  We would like the function to be run as a thread, in the background, infinitely, regardless of what we are telling Rosie to do.  It's, after all, critical to her safety.

def check_distance():
    #...code to check distance, change alert level, change LED colour, stop motors...

A variable to track Rosie's alert level (0 = normal, 1 = warning, 2 = danger) is declared as being 'global'.  This is to allow the main program (more on this later) to receive Rosie's alert level from the background thread.

global alert_level

There are other (more sophisticated, and probably correct!) ways to share data between threads, the most common being Queues, but we'll stick to using a global variable for now.  But remember, running threads can cause a lot of havoc if you don't keep track of what they are all doing, and to what, and when.

And the rest of the program, borrowed heavily from 'rosie_wheels.py', ends up in the 'main' part of the program.  In Python, this is defined using a bizarre bit of code.  Don't ask.  Just accept that this is how you define the main section of your program which only gets executed when running your program directly.

if __name__ == "__main__":
    #...bunch of code to do what you want the application to do

Oh, yes.  If you want the thread to now start, don't forget to create an instance of it, and map it back to our check_distance() function.

t = Thread(target = check_distance)
t.daemon = True
t.start()

The rest of the content here is pretty much the same as the original 'rosie_wheels.py' file (which in turn is based on the sample '08_manual_robot_continuous.py' program from GitHub), with the exception of:

if alert_level != 2:
    rr.forward(0, 0.5)
    print("ROSIE: I'm moving forwards.\r")
else:
    print("ROSIE: For your safety, forward is currently disabled.\r")

Here, Rosie is simply refusing to follow instructions when the 'UP' key is pressed, if the last alert level provided by the thread, running check_distance() loop, was 'danger' (2).   How sensible of her?

And that's it.  With a bit of threading, we have made a dramatic improvement to Rosie's IQ.  Tell us, who is cleverer now - you or Rosie?

Run the program to find out!


Even more reading:

Python's official documentation on threading module:
A reminder that our particular implementation makes use of the following GitHub contributions for lower-level interaction with the hardware that we are using:

Comments

Popular posts from this blog

Tea minus 30

We're fast approaching Christmas time.  And if robots were to make one simple observation about the human species during the Christmas festivities, it's that they watch a lot of TV.  A LOT.  Often, accompanied by an inappropriate amount of greenhouse gas-producing food.  Stuff you don't normally eat during the remainder of the year - for good reason.

And most so-called shows on TV are boring to robots like Rosie.  After all, why watch a minor subspecies of the human race - celebrities - stumble awkwardly around the dance floor, dressed like a faulty, sparking circuit board?  Such branch of entertainment doesn't require robots to engage any of their proud circuitry.  Their processors remain idle.  Memory under-utilised.

But if robots are to be part of people's homes (and blend in), they need to look at least a little interested in some of this irrational nonsense.  Nobody likes a party pooper.  A killjoy.  And this is where a certain subgenre of TV entertainment co…

Beam me up, Rosie!

How do you get from A to B?

You can't, as As and Bs are just letters in the alphabet. But if A is your house, and B is a meerkat village at your favourite safari park, you'd probably use a device equipped with GPS.  Not to be confused with UPS, who will deliver you your chosen meerkat through the post. And why on Earth would Rosie Patrol need one? Precisely, it's because she is on Earth that she needs one. Because our planet is rather big. Big enough to get lost in. And we don't want to lose our friendly plastic boxes on wheels. And maybe, eventually when she's clever enough, she'll go and defeat baddies on her own. And return home afterwards for a well deserved Earl Grey tea.

Besides, why wouldn't we want to add another three letter acronym to Rosie Patrol's repertoire?
All superheroes need:One Raspberry Pi 3, running Raspbian OSComputer from which you are connecting to the Raspberry Pi Probably the most important bit: a GPS receiver thingmy. …

Hello supervision, goodbye supervision

The time has finally come, to let our precious Rosie go.  Wish her luck (yes, she'll really need it).  Press the big red button.  And watch her destroy / navigate (delete as appropriate) the terrain*.  All on her own.  Without your help.  Well, sort of.

*Somewhat scientific term... for your lounge

You see, cool robots are never meant to be controlled by us clumsy humans.  If they want to be clumsy, they can be clumsy on their own.  That way we can deny all responsibility when they crash into the neighbour's car.  Or, less importantly, take over the world.

And by the way, Autonomous stuff seems to be in fashion these days.  Self-driving cars are just around the corner (maybe quite literally).  Very clever computers also beat humans at chess, apparently.  After all, if robots only do what we tell them to do, they are going to need us.  They're going to be just like us.  Oh no.  We want them to work alongside us, to do things that we simply can't be bothered to do.  Like …