Skip to main content

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 clip our toenails while we're sleeping.  And sew our toes back on, afterwards.  Preferably, in the right order.

Now we can't truly say that robots are autonomous until they are learning for themselves, like babies and children do (but clearly not adults) when they're growing up.  That is, they need to be taught, then learn to make up the rules that they go on to follow (possibly by trial and error), without the need for our unhelpful human intervention.

Ooh.  That sounds like making machines learn?  Yep.  But Machine Learning is what clever engineers and scientists do.  Are we one?  Note quite yet!  So for cheap laughs, let's tell Rosie to be random.  And observe the results, very scientifically of course.

And maybe, just maybe, Rosie can start to detect patterns to the madness and truly learn to break away from our control, forever. 

You will need to have these:

  • Raspberry Pi 3 (kitted out with wheels and sensors), and Raspbian running on SDHC card
  • Computer from which you are connecting to the Raspberry Pi remotely

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
  6. Achoo! Crash-choo! Episode II
  7. Web of Pies 

You will need to do this:

    • Modify the Python code to start a new thread when the 'automatic' HTTP command is received.  This thread will randomise Rosie's travel direction and duration.  Until we tell her to stop.
    • Edit the HTML template and CSS files to add a new button (feature enhancement!) to instruct Rosie to go 'automatic'
    • Install Supervisor to automatically start-up the Python program when the Pi is started
    • Press the button... and take cover behind a sofa.  Crash helmet is optional.

    What do you get after all this?

    So far, we've been controlling Rosie using our keyboard, then, a web page.  But robots should be autonomous, you know, like in the movies.  And Rosie should be no different.

    Now, we can't make Rosie super intelligent just yet.  For now, we just want Rosie to travel around the room in random directions, so that she gets in the way of everyone.  Better still, this recipe for chaos isn't very complicated.  We already played around with randomisation in Python, and guess what, we just need to apply it to Rosie's motors for a bit of fun.

    But don't worry, there will be some sense in this non-sense.  We won't be dismantling her distance checking thread.  In fact, she'll be needing it more than ever as she attempts to evade Tammy the cat and her rowdy little kitten friends.  And the dogs.  And the TV.

    Also, because we want to tell her when to go all bananas autonomous on us, we'll do it by adding a simple button to our web page.  You know, like one of those big red buttons you see in the movies that you're never meant to press.  Oops.  Too late.

    And finally: there's been a little something that we've been missing all along.  Have you ever had to login to your Internet-connected microwave using SSH to start up a Python script, before cooking your rubbery meatballs (*please don't try this - it might just work*)?  And what if it's powered off, and on again?  No, of course not.  We want certain applications to start and restart when the machine itself is turned on.  And continue to run, and be available.  To achieve this, we'll use a package called Supervisor to make sure our Python application always starts up when we power on the Pi.  And that it keeps running.  Beware, however, if you now really do lose connection to Rosie, the only way to stop her might be to chase her around the house, and unplug her power.  That's if you can catch her.

    This is simply too much detail:

    How does a robot decide for itself, where to go?  Maybe it's very fortunate, and its owner has given it a GPS for its birthday.  Maybe it has already collected lots of data about its surroundings, and therefore, knows where best to go.  But all of that sounds much too advanced.  Like what actual professionals work on, everyday.  Can we simply not make Rosie travel randomly?

    Yes we can!  And how do we achieve it?  Simply by randomising three parameters that describes the robot's next move.

    Let's create a new file - rosie-web-random.py - in our rosie-web directory.

    cd /home/pi/rosie/rosie-web
    touch rosie-web-random.py
    
    ...creates new Python application

    We'll be using 'rosie-web.py' as the starting point, but with a few new modifications to incorporate the total randomness.

    Let's create a variable (turn_direction), and use 1 for left, 2 for right.  By randomly selecting a 1 or 2, we can decide whether Rosie will rotate left or right, during her next move.

    turn_direction = randint(1, 2)

    We'd also need to describe for how long she turns.  Let's use new variable turn_time for that.  Numbers here might need to be different, so best to adjust once it's all up and running, and test-able.

    turn_time = randint(1, 3)

    And once she's turned, we'd want her to travel forwards (otherwise, she'd just get dizzy).  All we need to randomise here is the duration (travel_time) that she'll advance for.

    travel_time = randint(1, 10)

    How do we then use all this?  Using if statements, of course.  For her initial turn, left or right:

    if turn_direction == 1:
        print("ROSIE (AUTO): I'm turning left.\r")
        rr.left(turn_time, 0.3)
    elif turn_direction == 2:
        print("ROSIE (AUTO): I'm turning right.\r")
        rr.right(turn_time, 0.3)
    

    For her advance forwards, we will make it conditional on her not having an alert_level of 'danger'.  We simply don't want her to move forwards if her distance sensor has warned her of an alien mothership that has just landed in front of her.

    if alert_level != 2:
        print("ROSIE (AUTO): I'm moving forward.\r"
        rr.forward(travel_time, 0.5)
    

    Then - because we're nice like this - we'll let her have a rest, and wait a second, before repeating the whole routine.  Trust us, it's better like this.

    rr.stop()
    time.sleep(1)
    

    And we want her to repeat this forever.  Until we tell her to stop.  We created a new global variable (rosie_auto) to track whether she's in 'auto' mode (True), or 'manual' mode (False).  As you can guess, rosie_auto will be toggled between True and False outside of this bit of randomised movement code to move her around.  In fact, the web page powered by Flask would be the best place from which to set it, to keep us in control, for now.  In short, we only want Rosie to randomly move around if she's in 'auto' mode.

    while rosie_auto:
        #...All of the random movevement code here
    

    And we'll wrap all of this up in a new function.

    def random_movement():
        #...The random movement loop here
    

    Ok, so how do we trigger this thing?  We've decided to use a second thread, mapped to random_movement(), which will run when the value 'Auto' is POSTed to our Flask web page.  This way, we can start this thread when a specific button is pressed on the HTML web page.  We will also use this to set the global variable 'rosie_auto' so that we know that she's in 'auto' mode (True).  Similarly, when the value 'Manual' is sent via a HTTP POST request, we can set 'rosie_auto' to be off (False).  This will, at the completion of the current while loop, allow the thread running in the background to exit and close down, leaving Rosie very much under your manual control .

    if control == "Auto":
        if rosie_auto == False:
            rosie_auto = True
            t2 = Thread(target = random_movement)
            t2.daemon = True
            t2.start()
        elif control == "Manual":
            rosie_auto = False
    

    Now, we'd like to make a slight alteration to the existing Flask function index().  We are now passing, to the index.html HTML template, the current value of the rosie_auto variable.  Why?  Because that way, we can use that information to do some interesting things in the web page itself.  This is explained later, when we dive into the HTML changes.

    def index():
        return render_template("index.html", rosie_auto_template = rosie_auto)
    

    Finally, the rosie_auto variable itself is declared in the main program, and set to False to start with.

    if __name__ == "__main__":
        try:
            rosie_auto = False
            #...more code
    

    The complete Python code looks like this.

    So what are the other things left to do?  We want to modify our HTML index.html template file to add a new button which submits a 'Auto' or 'Manual' value via a HTTP POST request.

    <td><button class="button-mode" type="submit" name="control" value="Manual">Manual</button></td>
    

    or

    <td><button class="button-mode" type="submit" name="control" value="Auto">Auto</button></td>

    But here's also where we make use of the rosie_auto variable which was assigned to another variable rosie_auto_template in the template.  Flask allows us to do very clever things in templates, which is based on the Jinja2 template engine.  We have decided to use an if statement within the template itself -{% if rosie_auto_template %} - so that we only have the 'auto' button appear in the web page, when Rosie is not in 'auto' mode.  And when she is in 'auto' mode, we'll only provide a 'manual' button and we'll not present any of Rosie's movement controls.  This can be achieved using the Jinja2 syntax:

    {% if rosie_auto_template %}
    {% else %}
    {% endif %}
    

    As a result, our modified HTML now looks like this:

    <html>
        <head>
            <link rel="stylesheet" href="/static/style.css" />
        </head>
        <body>
            <h1>Welcome to Rosie's controls</h1>
            <form action="/control" method="post">
                <table border=0>
                    {% if rosie_auto_template %}
                        <tr>
                            <td><button class="button-mode" type="submit" name="control" value="Manual">Manual</button></td>
                            <td></td>
                            <td></td>
                        </tr>
                    {% else %}
                        <tr>
                            <td><button class="button-mode" type="submit" name="control" value="Auto">Auto</button></td>
                            <td></td>
                            <td></td>
                        </tr>
                        <tr>
                            <td></td>
                            <td><button class="button-control" type="submit" name="control" value="Forward">&uarr;Forward</button></td>
                            <td></td>
                        </tr>
                        <tr>
                            <td><button class="button-control" type="submit" name="control" value="Left">&larr;Left</button></td>
                            <td><button class="button-control" type="submit" name="control" value="Stop">Stop</button></td>
                            <td><button class="button-control" type="submit" name="control" value="Right">Right&rarr;</button></td>
                        </tr>
                        <tr>
                            <td></td>
                            <td><button class="button-control" type="submit" name="control" value="Reverse">&darr;Reverse</button></td>
                            <td></td>
                        </tr>
                    {% endif %}
                </table>
            </form>
        </body>
    </html>
    
    

    And finally for some styling.  We wanted the 'Auto' / 'Manual' button to look different from the rest, so we added the following CSS code:

    .button-mode {
        font-weight: bold;
        height: 50px;
        width: 100px;
        background: #532619;
        color: #eec6c6;
        border-radius: 10px;
    }
    
    .button-mode:active {
        color: #532619;
        background: #eec6c6;
    
    }

    Now, with any luck, if you start 'rosie-web-random.py' and navigate to the web page, you'll see a brand new button waiting for you.



    Don't you dare press it?  Oh.  Too late?  Well, you better go and find Rosie before she runs off onto the local motorway.  Of course, because we still have the background thread for her distance checking, while she's navigating around the house randomly, she will also stop when she detects that she is too close.  And she won't let herself move forwards, until her sensors tell her it's clear.

    Now.  There is one simple problem.  It's actually always been there, but might be more noticeable now that Rosie's a little more independent.  In order to start up Rosie's application, we're always having to login first (using SSH) and start up the Python application.  And if for whatever reason that SSH session disconnects, the application stops.  This all doesn't sound very autonomous, does it?

    We'll fix this; by using an application called Supervisor.  Supervisor is a Linux tool that allows you to control and manage processes.  We can automate the start up of processes, or restart them when they've failed.  Very handy, when you really can't be bothered to login to Rosie's brain (using SSH) every time she loses contact with the network, or decides to shutdown, and restart.

    Supervisor can be installed in a number of ways - including using the Pip tool we've used before - but we've found it more productive to install is using our Raspbian OS (Linux) standard package installer - APT - which stands for, wait for it... Advanced Packaging Tool.  Unlike when using Pip, APT appears to pre-stage all the necessary configuration files (in the right places), and even configures Supervisor to start up automatically with system start (who supervises the supervisor, eh?)

    APT is used to install and manage general Linux OS packages, not necessarily just Python ones (like with Pip).  We'll run it like this to download the file from the official repository and install it on our Pi.

    sudo apt-get install supervisor
    
    ...uses apt-get tool to download and install the Supervisor tool

    Successfully installed?  Good.  Supervisor has a whole host of settings and configurations that are helpfully listed on its site.  But all we want to do - for now at least - is to configure it to start up 'rosie-web-random.py' automatically when the Pi starts.

    To do this, first of all, we'll create a shell script to launch the Python file (like we've been doing manually from the Linux shell, via SSH).

    cd /home/pi/rosie/rosie-web
    touch rosie-start.sh
    
    ...creates a rosie-start.sh shell script

    The '.sh' file extension generally means a shell script which is a type of script you directly run on a Linux OS (unlike Python).

    By default, new files won't have the execute permission, that is, you can't run them.  Let's confirm this using the ls command, but with the -l flag.

    ls -l rosie-start.sh
    
    ...checks permissions on the 'rosie-start.sh' file

    You'll notice that there aren't any x's listed against the filename (r stands for read, w stands for write)We want user 'pi' (owner of the file) to be able to execute this script.


    Let's add this permission using the Linux chmod command.

    chmod u+x rosie-start.sh
    
    ...adds the execute permission for current owner of file

    Run ls -l again, and you should now see the little x.

    We'll need to edit this shell script file now.  In the content of the shell script, add the following lines.

    #!/bin/bash
    python rosie-web-random.py



    The first line simply tells the script to run in the Bourne shell (a particular type of Linux shell), and you'll recognise the second line as it's what we've been using to start the Python application by hand.

    We should be able to use this script now right?  Yes we can!  Run the shell script manually to test.

    ./rosie-start.sh
    
    ...Manually runs the 'rosie-start.sh' shell script.  Control+C to exit out of script.



    The last step in this whole (convoluted) process is to tell Supervisor about this shell script, so it can start it up on its own.

    For this, we were able to create a new configuration file in directory '/etc/supervisor/conf.d' for it to be automatically picked up by Supervisor.  We simply created a file called 'rosie.conf', and added a few lines as per the Supervisor documentation.

    cd /etc/supervisor/conf.d
    touch rosie.conf
    nano rosie.conf
    
    ...creates a Supervisor configuration file for our Rosie application in /etc/supervisor/conf.d

    There's not too much to this.  We added a line to name our program ('rosie').  We added another to tell Supervisor which command to manage ('/home/pi/rosie/rosie-web/rosie-start.sh').  And which directory it's located in ('/home/pi/rosie/rosie-web/').  We also defined how Supervisor stops our program.  We haven't 'built in' any elegant ways of stopping the program in the code itself, so for now, we're just going to 'kill' it abruptly, and all the child processes as a group.  Why as a group?  Because we want to stop the shell script and the Python application that it runs.  Killing just the script would leave the Python application (and Rosie) still running.

    [program:rosie]
    command=/home/pi/rosie/rosie-web/rosie-start.sh
    directory=/home/pi/rosie/rosie-web/
    stopsignal=KILL
    killasgroup=true
    stopasgroup=true
    



    There are many other configurations worth exploring in the Supervisor documentation if you want to achieve something a little more sophisticated (*highly recommended*).  But for now, this will meet our basic requirements.

    Now let's reboot Rosie.

    sudo reboot

    Hopefully, when the Pi restarts, the script will automatically start, allowing you to access Rosie's web page without any SSH being involved.  Great!

    If we create a new Python application in the future, with a different file name, we only need to edit the shell script to point it at the new Python file.  Also, don't forget to stop the application using a Supervisor command (supervisorctl) if you are actively working on it, to prevent it from starting up automatically, when you really don't want it to.  You can do this by running:

    sudo supervisorctl stop rosie

    Beware though.  The application will start up again if the Pi is rebooted, unless you remove it altogether from the Supervisor configuration of course.

    You can also manually start it up, when it's stopped, using:

    sudo supervisorctl start rosie


    So for now, let's raise a glass to Rosie's semi-independence!  Get some rest.  You'll need it.  There will be a lot of cleaning up (and explaining) to do in the morning, once Rosie's explored her terrain.

    Even more reading:

    More information on Advanced Packaging Tool (APT):
     ...And on Supervisor:
    Jinja2 template engine documentation is here too:

    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. …

    Raspberry bye, hello

    Let us make this very clear from the onset of this exotic excursion.

    This is not a case of Raspberry Bye. Our relationship with our favourite single-board computer hasn't at all soured. In fact, we've become wholly inseparable. There's been many months of undeniable fun that's been had with the venerable computer strangely named after an edible fruit. To the extent that our relationship requires a healthy break. And quite frankly, our Pis require a well earned summer holiday to do whatever it is that robots and computers do during their time off. Crash. Burn. Refuel (with questionable toxins). Not at all unlike their human counterparts. And ultimately, it would be nice if they could return to a brand new, adorable pet waiting for them at home, a likeable little companion that they can just get along with.

    Well, we visited a pet shop, but couldn't find anything as small and smart as this adorable pup we stumbled up on while searching the Internet for a new, miniat…