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

    MOST VISITED (APPARENTLY)

    LoRa-Wan Kenobi

    In the regurgitated words of Michael BublĂ©: It's a new dawn .  It's a new day .  It's a new Star Wars film .  For me .  And I'm (George Lucas, and I'm) feeling good .  Unfortunately for Canadian Mike, the Grammy that year was won by the novelty disco classic with the famous refrain: We love IoT, even in Planet Tatooine * . *Not true. Clearly, the Star Wars producers didn't sincerely mean the last Jedi the previous time around.  Return of the Jedi, released during the decade that spearheaded cultural renaissance 2.0 with the mullet and hair-metal , was less economic with the truth.  Either way, we're going to take inspiration from the impressive longevity of the money-spinning space-opera and reboot our franchise with some Jedi mind tricks.  Except this particular flick doesn't require an ever-growing cast of unrecognisable characters, unless ASCII or UTF counts.  In place of an ensemble gathering of Hollywood stars and starlets, we will b

    Battle of BLEtain

    The trolling . The doxing . An army of perplexing emojis. And endless links to the same - supposedly funny - viral video of a cat confusing a reflection from a dangling key for a golden hamster, while taking part in the mice bucket challenge. Has social media really been this immense force for good? Has it actually contributed significantly to the continued enlightenment of the human (or feline) race? In order to answer these poignant existential questions about the role of prominent platforms such as Critter, StinkedIn and Binterest, employing exceptional scientific rigour equal to that demonstrated by Theranos , we're going to set up a ground-breaking experiment using the Bluetooth Low Energy feature of MicroPython v1.12, and two ESP32 development boards with inexplicable hatred for one another.  And let them hurl quintessentially British expressions (others call them abuse) at each other like two Wiltshire residents who have had their internet access curbed by the co

    Hard grapht

    You would all be forgiven for assuming that bar , pie and queue line are favourite pastimes of the British .  Yet, in fact – yes, we did learn this back in GCSE maths – they are also mechanisms through which meaningless, mundane data of suspect origin can be given a Gok Wan -grade makeover, with the prime objective of padding out biblical 187-page PowerPoint presentations and 871-page Word reports (*other Microsoft productivity tools are available).  In other words, documents that nobody has the intention of ever reading.  But it becomes apparent over the years; this is perhaps the one skill which serves you well for a lifetime in certain careers.  In sales.  Consultancy.  Politics.  Or any other profession in which the only known entry requirement is the ability to chat loudly over a whizzy graph of dubious quality and value, preferably while frantically waving your arms around. Nevertheless, we are acutely conscious of the fact that we have spent an inordinate amount