Thymio II control with python

As part of my final year project I've been working with the Thymio II robot in an attempt to integrate it with Scratch. After significant searching I have basically figured it out and for the sake of recording it I'll write what I did here.

Here is a video of the robot in action:

First, this only works on Linux so far as there is very limited support for dbus (the communication middle-ware) on windows and it seems due to this there isn't a version of asebamedulla. this software allows for multiple connections to the thymio as well as connections using dbus.

You will need to install the aseba studio in order to get asebamedulla, this can be done with the usual apt-get install aseba. with this done asebamedulla can be run. with the thymio plugged in you need to type sudo asebamedulla "ser:name=Thymio-II". If done right it will find the thymio, say what port its on and then continue to run.

At this point we're ready for the python to communicate with it. The dbus libraries I have found, work best with python2.7 so when installing python I would recommend using that version. Dbus on python might come pre installed but if not then apt-get dbus as well as this you will need gobject for python.

create a file and open and prepare to write some python

first of all we're going to want to sort out our imports, for this we'll be using:

import dbus
import dbus.mainloop.glib
import gobject
import sys
import time
from optparse import OptionParser

Dbus will allow python to communicate with the thymio through asebamedulla.
Gojbect is used to run and handle the loop.
Sys will be used to receive key presses.
Time will be used to make python wait while an operation takes place.

Wow we need to define the function we want to loop for the thymio.

def control():
    #get the values of the sensors
    network.GetVariable("thymio-II", "prox.horizontal", reply_handler = get_variables_reply , error_handler = get_variables_error)
 
    #print the proximity sensors value in the terminal
    print proxSensorsVal[0], proxSensorsVal[1], proxSensorsVal[2], proxSensorsVal[3], proxSensorsVal[4]
 
    #read in a key press
    char = sys.stdin.read(1)
    #if a key is pressed
    if char == 'w':
       network.SetVariable("thymio-II", "motor.left.target", [300])
       network.SetVariable("thymio-II", "motor.right.target", [300])
       time.sleep(1)
       network.SetVariable("thymio-II", "motor.left.target", [0])
       network.SetVariable("thymio-II", "motor.right.target", [0])
    if char == 's':
       network.SetVariable("thymio-II", "motor.left.target", [-300])
       network.SetVariable("thymio-II", "motor.right.target", [-300])
       time.sleep(1)
       network.SetVariable("thymio-II", "motor.left.target", [0])
       network.SetVariable("thymio-II", "motor.right.target", [0])
    if char == 'a':
       network.SetVariable("thymio-II", "motor.left.target", [-300])
       network.SetVariable("thymio-II", "motor.right.target", [300])
       time.sleep(0.2)
       network.SetVariable("thymio-II", "motor.left.target", [0])
       network.SetVariable("thymio-II", "motor.right.target", [0])
    if char == 'd':
       network.SetVariable("thymio-II", "motor.left.target", [300])
       network.SetVariable("thymio-II", "motor.right.target", [-300])
       time.sleep(0.2)
       network.SetVariable("thymio-II", "motor.left.target", [0])
       network.SetVariable("thymio-II", "motor.right.target", [0])
    return True

we will also need to define the get_variable_reply and get_variable_error

def get_variables_reply(r):
    global proxSensorsVal
    proxSensorsVal=r
 
def get_variables_error(e):
    print 'error:'
    print str(e)
    loop.quit()

The above code will wait for a key press, when you press w, a, s, or d and then enter it will tell the thymio what to set the motor speeds to and then wait and set the speeds back to 0. Commands can also be queued as the sys.stfin.read(1) will only read one key at a time, for instance [waawsdds] would move the thymio forward, then turn it and then move forward again, and then reverse the process back to where it started.

All we need to do now is to actually create the network and the loop for the code to run in. To do this we will use the following

if __name__ == '__main__':
    parser = OptionParser()
    parser.add_option("-s", "--system", action="store_true", dest="system", default=False,help="use the system bus instead of the session bus")
 
    (options, args) = parser.parse_args()
 
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
    if options.system:
        bus = dbus.SystemBus()
    else:
        bus = dbus.SessionBus()
 
    #Create Aseba network
    network = dbus.Interface(bus.get_object('ch.epfl.mobots.Aseba', '/'), dbus_interface='ch.epfl.mobots.AsebaNetwork')
 
    #print in the terminal the name of each Aseba NOde
    print network.GetNodesList()  
    #GObject loop
    #print 'starting loop'
    loop = gobject.MainLoop()
    #call the callback of Braitenberg algorithm
    handle = gobject.timeout_add (100, Control) #every 0.1 sec
    loop.run()

Here we create a parser, the options variable and the bus variable. with these we can define the network. The 'ch.epfl.mobots.Aseba' is the name on asebamedulla for the code to connect to. After this we define the loop, tell it what to run and run it.

the full script is available here