- 1 Project Card
- 2 Updating middleware and exercises
- 3 Jupyter
- 4 Blocking functions
- 5 Remote control
- 6 An autonomous Pi-mbot
- 7 Pi-MBot
- 8 Scratch in Mbot
Author: Eva García Domingo
Academic Year: 2017/2018
Degree: Degree in Telecommunications Technologies Engineering
GitHub Repository: TFG-Eva_García
Updating middleware and exercises
Until now, the middleware has four different methods for moving the robot. Deciding this is quite useless, now we use just one function: move(), with two parameters, one lineal velocity and another one for turning it. The aim is being able to advance and turn at the same time.
In order to this, a few considerations:
-if the lineal velocity is zero, move(0, v), we will want to just turn the robot
-if the angular velocity is zero, move(v, 0), the robot have to go ahead with no tuning
-a negative lineal parameter, means go back.
-a positive angular velocity means to the rigth, and a negative one, to the left.
-when advancing, for turning right, the left wheel will go faster, and vice versa. However, when going back, for turning right, the right wheel will be the faster.
All this specifications and funcionality, will be hide in the middleware. Kids will only use lineal and angular velocity, and the middleware will do all the work.
Also, I thought about to do a decider. My idea, thinking about the kids' behaviour, and due to the fact that the mbot only accept a few values of velocities -and it does not difference between, for example, 80 and 90- was to control the ranges of the velocities. Even if a kid'd decide to write "1000", the mbot understand 255 as a maximum.
In order to facilitate things to kids, let's start to use Jupyter to program in python, instead de python's IDLE or some editor that would be necessary to install.
Need to know about Jupyter
-The file JdeRobotKids.yml.old would be edited depending if you are going to use the robot in real or simulated.
When working with the sensors I realised that calling for the sensors were not blocking, and sometimes the process were continuing without update the value of the parameter. Because of this, first thing I did was a loop, forcing the program to wait to the sensor, but that was not an "elegant" solution.
First approximation to the "perfect" solution is to block the function-calling, forcing the program to wait to the sensor response. The idea is that, due the fact the Arduino board is slower than the raspberry, waiting to the Arduino response will secure us the last updated value.
Learning to use Python's events and threads
Let's try with Python's Event objects, which has a wait function by itself for making a thread be blocked until the other thread (the sensor's response) set the flag to true. This way keep clear of having to use blocking callings in the low level of the firmware.
Using first with a synthetic example, just Python.
def pulsador(): for i in range(11): time.sleep(0.01) print i if i == 10: botonTouch.set() pulsador = threading.Thread(target=pulsador) pulsador.start() botonTouch.wait() print "finish the counting"
Be notice that a different thread is used for each control function (pulsador function). That is, whatever we need to control, will be a thread. Thus, each robot's sensor will be controlled with an event (the number 10, in this example), and the main thread (the main program) will wait to the events to do separate things.
0 1 2 3 4 5 6 7 8 9 10 finish the counting
Having tried this simple example in the Raspberry, and having the same result, lets move on and try threads with actuators; waiting to a red led to move.
import sys import time import threading from ArduinoJdeRobot import MBot robot = MBot("/dev/ttyUSB0") # Conectamos con el robot pulsado = threading.Event() def control(): robot.encenderLedPlaca(0, 255,0,0) pulsado.set() #flag del evento a 1 time.sleep(0.2) threadControl = threading.Thread(target=control) threadControl.start() pulsado.wait() robot.avanzar(100) time.sleep(0.2)
=> Tested and checked.
Next, using threads in the "start button".
pulsado = threading.Event() def pulsador(): time.sleep(0.2) but = robot.leerBoton() if (but == 1): pulsado.set() #flag del evento a 1 return else: pulsador() threadPulsador = threading.Thread(target=pulsador) threadPulsador.start() pulsado.wait()
Once this is tested and working, we focus on the idea that all this implementation, threads and events, is quite complicated and kids won't be able (they don't need to) to program it. So, next step is to introduce the implementation in the firmware. The aim is to hide the implementation inside JdeRobot, making it work in any circumstance with no need of programming threads (and killing these threads) each time.
First problem: creating events
As I will need, a priori, an event for each sensor, I've created a dictionary, for being able to find and get easily the event I'd need.
Second problem: another raze condition
The idea is the middleware will be always reading from the sensor -from the first time we ask it for the sensor-. If this middleware is setting the "sensor_readed" event to True (1), the API will be reading the True when ask for the sensor, so wont wait for the new value.
So, we'll use two events, one for the sensor and another one for "the security" of the middleware, and a True/Flase flag.
Before integrate the schema in the middleware, I've tried with a simple example -just python- for checking that the razer conditions are fixed. The aim is that, even if one thread is faster than the other and writes more often than the other reads -in the same resource-, the second (the application) always has to read an updated value, no matter if it misses some values.
The "-----C: " is the writer and the "A: " is the reader.
-----------C: 1 A: 1 -----------C: 2 A: 2 -----------C: 3 A: 3 -----------C: 4 A: 4 -----------C: 5 ----------C: 6 A: 6 -----------C: 7 A: 7 -----------C: 8 A: 8 -----------C: 9 -----------C: 10 A: 10 -----------C: 11 A: 11 -----------C: 12 A: 12 -----------C: 13 -----------C: 14 A: 14
First time I tried to copy the example in the middleware, it did not work on the robot. I failed in the loop which writes on the sensor value.
Second time, I think I did well. The non-ending loop who writes in the US value, which only starts when the API calls for the sensor the first time, is the "requestsensor". The protected function is the "readsensor".
Keep fighting, noticed that, to be able to changing the value (setting, clearing) of the flags and events, and be sure of not working with a "copy" of them, I have to do in just one place, and asking for changing them to this class. Another thing is, python character, these events/flags have to be in the self, in order to use them as global.
Working and updating the values, let's try it in the robot:
Rest of the sensors
IR line follower and sound sensor: the idea and the implementation, is the same than with the US sensor.
IR remote control: this one is a bit diferent, bocause we've decided that the API function shoud only return a value if the readed value is not 0 (the 'null'). The reason is because the control is sending values all the time; if no key is pressed, the value readed is 0. So, the aim is to actualize the variable only if a key is pressed.
As the remote control were not in the firmware, I am working to add it. It will be so useful to the PimBot: I want not to need the keyboard for sending orders to the robot.
First "problem" I found was to find wich ID belongs to the remote control; all of them was listed on the Arduino firmware, only thing to do was to try which one was the good one.
As keys in the control have not the same value than the signal the robot receives, and kids will understand better if the value is recognizable, I've chose a pyhton's dictionary: now the middleware responds directly the same value that you see in the control. A useful thing is algo to know the exact values of the dicctionay; if I'd wont to use a specific key to order something, I'll need to write it exactly like in the dictionary.
Just for trying it:
Once the remote is working, let's do an exercise for kids, for example to program a controlled car. First thing important to notice is that, as we have seen, if no switch is pressed, a string "null" is receive; another thing is once a engine has received the "advance" command, it wont stop until the "stop" order. So, the car will continue walking until another switch is pushed.
while True: valor_mando = robot.leerMandoIR() if (valor_mando == "DERECHA"): robot.girarDerecha(vel) elif (valor_mando == "IZQUIERDA"): robot.girarIzquierda(vel) elif (valor_mando == "ARRIBA"): robot.avanzar(vel) elif (valor_mando == "ABAJO"): robot.retroceder(vel) elif (valor_mando == "0"): robot.parar()
Just for trying it:
An autonomous Pi-mbot
Firstly, we want the robot to move by itself, so we need no cables, no monitor... We put the raspberry and the power in the robot:
Trying to control the start of the programs
As the final aim is that the programs run by themselves in the robot, with no need of ssh, the first thing needed is to control the beginning of the program. I have choose to use the button of the mbot's mainboard only for this purpose.
In the firts try, as the physical button does not work, we cannot control the start of the robot, but we can show that he moves by itself.
Once the Arduino mainboard has been changed and the button works, we can start to convert all the already-existed exercises:
Do not crush
Replacing the monitor and keyboard
As the aim is no need anything else except the robot, the first thing to do is get rid of the monitor, so anything the robot ask us, it will do it in the Led panel. As the panel is in front of the button, we do without the star button for now.
In order to have a more autonomous robot, we want to connect the MBot and a Raspberry-pi. Doing this, we will be able to use a camera with the Mbot, to run complex exercises (more complex than before) and we will not need a computer for connecting with the robot.
Preparing the Raspberry-pi 3
We choose this version of the Raspberry because the Wi-Fi is integrated in the baseplate; not needing a USB-wifi will make us everything more easy.
1. Download the Raspbian image from the oficial website.
2. Format the microSD card.
3. Burn the image in the SD card. For example, with Etcher.
Until the Raspberry has a suitable Wifi connection, we need to use it as a computer, with a keyboard, mouse, monitor and (always needed) a power supply. As we want to be able to connect us by SSH all times, with no need of the "computer-pack", we will need a password for the Rasp, and a static IP for the lab-network (we don't want to find averytime which IP it has). As the network in the lab has no output to the Internet, we also want a static IP for the Ethernet connection, in case we need install some packages on the Raspbery.
The files of the system /etc/network/interfaces y /etc/wpa_suplicant/wpa_supplicant.conf have to be edited, adding the specifications of the networks.
Once we have connection on the Raspberry, we install:
1. Download Arduino IDE from the Arduino_web. Installing it sh arduino.sh
2. Download Makeblock libraries . Copy the makeblock folder to the arduino default library.
3. Download the JDeRobot-Kids firmware from 
4. Install the dependencies:
aptitude install python3-all python3-pip
pip3 install cython
aptitude install libusb-1.0-0-dev libudev-dev
pip3 install hidapi
pip3 install pyserial
pip3 install pymata
5. Copy secundaria-libro/codigos/infraestructura/ArduinoJderobot into the /usr/lib/python3.5
6. Open the mbot_jderobot.ino into the Arduino IDE, and uploading it into the connected MBot. Herramientas. Placa: Arduino/Genuino Uno; Puerto: /dev/ttyUB0. Programa: Subir. Compilar.
Testing the previous exercises in the Pi-mBot
Testing the already existing exercises in the raspberry:
Scratch in Mbot
The aim of this section is to create diferents examples and exercises for the MBot, using the graphic programming language Scratch. Using this language has the purpose of having a basis of basic concepts of programming, due the fact the high level languages are quite complicated for people with no previous knowledge.
Moving the robot
Simple example, with only movements.