Team:NAWI Graz/Robot

The Thymio Robot


The robot, a Thymio II, is a robot designed for educational purposes. All components (hardware and software) are open source. The robot consists of more than 20 sensors, many LEDs, two motors, a microphone and a loudspeaker. To make the already very powerful tool even more superior we connected a Raspberry Pi to it, allowing us to not only take full control over every aspect of the robot, but also using Python as a programming language for the device.


Thymio robot
Fig. 1: Our Thymio robot in action. On its top resides a Raspberry Pi, which is powered by an external power bank.


The code

The program controlling our robot is relatively simple in principle. It can not only be addressed from the perspective of the robot, but also be considered in its entirety with the reactions of the bioreactor. First, the robot connects to a network to link to the bioreactor. After that, it initializes its sensors and switches into the main loop. Here the robot iterates some commands: The robot queries the sensor values ​​of the distance sensors and, depending on a predetermined threshold, rules if there is an object in front of it or not. If an object has been detected, this information is sent to the reactor (which then pumps a certain amount of bacterial culture into the reaction chamber, where the bacteria are either heated or the pH of their medium is changed). If no object is detected, this information is also transmitted to the reactor (which then pumps a certain amount of bacterial culture into the reaction chamber, but in this case does not heat or change the pH of the medium). Thereafter, the robot must wait for a response from the reactor. The reactor pumps the bacterial culture into the measuring chamber and checks whether fluorescent proteins can be detected. In that case, this information is transmitted to the robot and depending on whether fluorescent proteins were detected (results in a rotation) or not (resulting in a movement forward), the robot reacts. After this reaction a new iteration of the main loop starts.

This is our code controlling the Thymio:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/python2
import dbus
import dbus.mainloop.glib
import gobject
import sys
import socket
import time
import logging

#from w1thermsensor import W1ThermSensor

from optparse import OptionParser

# proximity sensor readaout values
proxSensorsVal = [0, 0, 0, 0, 0]

# server ip and port
host = '192.168.43.150'
port = 4343

logger = logging.getLogger('colibot')

debug = False
counter = 0

SENSOR_THRESHOLD = 3300
DRIVE_DIST = 400
TURN_DIST = 180

def dbus_reply():
    pass


def dbus_error(e):
    # logger.info 'error %s'
    # logger.info str(e)
    pass


def control():
    # get the values of the sensors
    network.GetVariable("thymio-II", "prox.horizontal",
                        reply_handler=get_variables_reply, error_handler=get_variables_error)

    # send proximity sensor reading to colis
    reply = coli_com()

    if reply == 0:
        for i in range(0, DRIVE_DIST):
            network.SetVariable("thymio-II", "motor.left.target", [100])
            network.SetVariable("thymio-II", "motor.right.target", [100])

    if reply == 1:
        for i in range(0, TURN_DIST):
            network.SetVariable("thymio-II", "motor.left.target", [100])
            network.SetVariable("thymio-II", "motor.right.target", [-100])

        network.SetVariable("thymio-II", "motor.left.target", [0])
        network.SetVariable("thymio-II", "motor.right.target", [0])

    return True


def get_variables_reply(r):
    global proxSensorsVal
    proxSensorsVal = r


def get_variables_error(e):
    logger.info('error: ')
    logger.info(str(e))
    loop.quit()


def coli_com():
    global debug

    sum_prox = proxSensorsVal[0] + proxSensorsVal[1] + proxSensorsVal[2] + proxSensorsVal[3] + proxSensorsVal[4]
    sides = proxSensorsVal[0] + proxSensorsVal[4]

    global counter
    counter += 1
    if (counter % 2):
    # print("dirrrrty hack to take every second measurement only")
        return 6

    logger.info('# # # # # # # # # # # # # # # # #')
    logger.info('                         cycle {}'.format(counter/2))
    
    logger.info("Proximity sensor sum: " + str(sum_prox))


    try:
        if (sum_prox > SENSOR_THRESHOLD):
            logger.info("Obstacle detected!")
            if not debug:
                # send data to server
                s.sendto(str.encode('obstacle'), (host, port))
                logger.info("Sent 'obstacle' to reactor.")
            if debug == True:
                reply = 'turn' 
        else:
            logger.info("No obstacle")
            if not  debug:
                # send data to server
                s.sendto(str.encode('clear'), (host, port))
                logger.info("Sent 'clear' to reactor.")
            if debug == True:
                reply = 'drive' 


        if debug == False:
            # receive data
            d = s.recvfrom(1024)
            reply = d[0]
            addr = d[1]

        logger.info('Server reply : ' + str(reply))
#        logger.info('Executing ...')
        logger.info('                                #')
        logger.info('# # # # # # # # # # # # # # # # #')
        logger.info('')

        if (reply.decode("utf-8") == 'turn'):
#            s.sendto(str.encode('thanks'), (host, port))
            return 1
        if (reply.decode("utf-8") == 'drive'):
#            s.sendto(str.encode('thanks'), (host, port))            
            return 0
        else:
            return 6

    except socket.error as msg:
        logger.info('Error Code : ' + str(msg[0]) + ' Message ' + msg[1])


def cam_sync_at_start():
    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])    
    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 __name__ == '__main__':
    # format
    FORMAT = "[%(asctime)s ]   %(message)s"
    fileHandler = logging.FileHandler('log.txt')
    fileHandler.setFormatter(logging.Formatter(FORMAT))
    logger.addHandler(fileHandler)
    logging.basicConfig(level=20, format=FORMAT, datefmt='%d.%m.%Y %H:%M:%S')
	

    parser = OptionParser()
    parser.add_option("-s", "--system", action="store_true", dest="system",
                      default=False, help="use the system bus instead of the session bus")
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
                      default=False)

    (options, args) = parser.parse_args()	
	
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    # use system bus instead of session bus if option is passed
    if options.system:
        bus = dbus.SystemBus()
    else:
        bus = dbus.SessionBus()

    if options.debug:
        debug = True
    
    # Create Aseba network
    network = dbus.Interface(bus.get_object(
        'ch.epfl.mobots.Aseba', '/'), dbus_interface='ch.epfl.mobots.AsebaNetwork')

    # logger.info in the terminal the name of each Aseba NOde
    #logger.info(network.GetNodesList())

    # to sync camera:
    logger.info("cam-sync:")
    cam_sync_at_start()
    logger.info("Start logging")
    logger.info("###############################")
    logger.info("   ColiBot Thymio controller   ")
    logger.info("-------------------------------")
    logger.info("...        starting:        ...")


    # create dgram udp socket
    try:
        logger.info("Creating socket.")
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    except socket.error:
        logger.info('Failed to create socket')
        sys.exit()



    # GObject loop
    # logger.info 'starting loop'
    loop = gobject.MainLoop()
    # call the callback of control flow
    handle = gobject.timeout_add(1000, control)  # every 0.1 sec
    loop.run()