Explorer behaviour (Yellow)

thymioII-yellow.jpg

Thymio explorer

When you start this behaviour, Thymio moves forwards. If the Thymio detects an object in front of its front central sensor, it moves backwards. If it detects an object with its two front left sensors, it turns right until it no longer detects the object and then it begins to move forward again. Similarly, an object on the right will cause the Thymio to turn left. If it reaches the edge of a table, it will stop. By pressing the forward or backward arrow button, Thymio will accelerate or decelerate. You can even make it go backwards. When going backwards, it will turn away from an object detected by its two back sensors. Note that when going backwards, Thymio cannot detect the edge of a table in time for it to stop (see the explanation in the detailed description of the code which follows below). The yellow circle leds form an animated circle but this is just for decoration.

This Aseba .aesl file contains the source code implementing this behaviour.


Dive into the code

The variables declaration

The variables list has to be placed at the beginning of the code. The # symbol in a line signifies that it and the rest of the line are comments which will be ignored by the compiler; it will not affect the robot's behaviour, it is there just for us humans. Putting more than one # in a row will just emphasize the comment and allow it to be found again more easily.

In this part of the code, we see the keyword var before each variable. This indicates to the compiler that the following word is a variable. The variables are global (they are accessible from anywhere in the code), so try to use unique names! If you choose variables named temp1, temp2, temp3, etc., it will not be so easy to remember which one does what.

The variables can be initialized to a specific value, as the speed one is on line 9, with the declaration var speed = 200. If a variable is not initialized, its value is undefined (it may contain anything).

On line 12, we can see that the variable led is declared as var led[8]. This simply means that led is not a single variable but an array containing 8 values. It will be possible to access these values in the code through led[0], led[1], led[2] up to led[7]. Arrays are indexed from 0 to their size minus one.

Finally, the array var weight_braitenberg[] = [1,2,3,2,1] shows another way to declare an array. The double bracket indicates that weight_braitenberg is an array, but does not give the size; then the = [1,2,3,2,1] fills this array with 5 elements of value 1, 2, 3, 2, 1.

These variable names are probably obscure for you now but we will clarify them when we explore the different parts of the code. You can learn more about variables in the Aseba language reference documentation.

The timer set up

We want to show both a glowing and a circle animation. For that, we need to update the lights at regular intervals. To do so, the code above enables timer 0 with a period of 20 ms (milliseconds, 1 second = 1000 ms), meaning that every 20 ms the event timer0 will be triggered. Thymio has two timers, the 0 and the 1. These timers can be configured for periods between 1 and 32767 ms. They can be set independently and will each trigger an event, timer0 respectively timer1. This is particularly useful if you want a certain action to be performed at regular time intervals.

If you had written timer.period[1]=100, it would have meant that the event timer1 would have been triggered every 100 ms. You can learn more about timers in the programming interface reference documentation.

The timer event

The first line which is not a comment is onevent timer0. This means that every time the event timer0 is triggered, the robot will do what is written in the block which follows. In this case, this part of the code will be executed every 20 ms as it has been set as described in the previous part.

This code block can be separated into two parts, the first one starts with the comment #Led ring animation and the second one with #Body color pulse.

The led ring animation

In a few words, this part will make the led ring lights rotate as in the video.

Now, let's dive into the code. On line 23 there is: call math.fill(led, 0). This is a call to a native function (you can find them at the bottom left of the Aseba Studio window). These are predetermined sequences of commands which are stored in the memory of Thymio. This one is related to mathematics as it has the syntax math.. Specifically, this function will fill the array led with zeros, it is an efficient way to initialize an array. You will find all the native functions here.

Then the variable led_state is modified. The statement led_state = (led_state + 2) & 0xff may seem complex, so we will decompose it step by step. The expression led_state + 2 is within parentheses, which means that this operation will be performed first, so that the contents of the variable led_state are incremented by 2, and the result is stored temporarily. Then there is a & 0xff. This is a mask operation. 0xff is a number written in hexadecimal, it is equal to 15 * 16 + 15 = 255 which is represented internally by 8 consecutive 1-bits. The & is the logical AND operator. In fact, performing & 0xff is equivalent to comparing the result of incrementing led_state to 255 and, if it is bigger, subtracting 256 by removing the correct bit in the number. Then the final result is stored in the variable led_state. Taken altogether, the statement led_state = (led_state + 2) & 0xff allows the value of led_state to be incremented by 2 and makes sure that the resultant value stays between 0 and 255. The variable will grow each time the timer0 event is triggered until it exceeds 255, then it will go back to zero and start growing again.

The next line (28) sets the variable fixed to led_state divided by 32. As long as Aseba does not support decimal numbers, fixed will vary between 0 (when led_state is between 0 and 31) and 7 (when led_state is between 224 and 255), that is to say, it is a round down operation. This will be used to set one led to a fixed level of brightness during the time in which 32 timer0 events occur. Knowing that the event timer0 occurs every 20 milliseconds, the variable fixed will stay the same during 32*20 = 640 milliseconds.

The three next lines will set the brightness of three leds, the fixed one, the one before it (fixed-1) and the one after it (fixed + 1). The idea is to set the fixed led to full brightness while increasing the brightness of the next led and reducing the brightness of the previous one. Let's have an example:

Cercle_led.svg

Led 6 is the fixed one, fully lit. The previous one, the 5, is also lit while the 7 is off. Led 5 will then become less and less bright while the 7 will become brighter. Once the 7 is completely lit, the next one, led 0, will become brighter while the 6 will slowly turns off… etc.

We will then go round the circle of leds, lighting them on and off. We can see the indices of the array led here:

led[fixed]
led[(fixed - 1) & 0x7]
led[(fixed + 1) & 0x7]

The & 0x07 is another mask operation. It ensures that the result of (fixed - 1) and (fixed + 1) never leaves the 0 to 7 range. Indeed, if fixed = 0, then (fixed - 1) = -1, yet the previous led in this case is 7, not -1. The mask operation will correct that and make sure that the leds are lit on and off continually.

The last line of the led ring animation part (32) is call leds.circle(led[0],…). This will set the brightness value of each led. Indeed, so far, we only manipulated the array of variable led[ ], now we have to make the robot effectively change the brightness of the leds !

The body colour pulse

This part will make Thymio pulse in a yellow colour.

The line 33 is, as for the circle leds, an increment of the variable led_pulse. It will just add 1 to the variable every time the timer0 event is triggered.

Next we find another conditional test but this time in two parts. This one is in the common form of:

if … then … else … end

Let's break this down. It is done in four key words:

  • if:** we test the variable (here the question is: is led_pulse bigger than 0?)
  • then:** if the condition is true (if led_pulse is bigger than 0), we execute the following code (call leds.top(led_pulse…)…)
  • else:** if the condition is false (if led_pulse is smaller than or equal to 0), we execute the following code (call leds.top(-led_pulse…))
  • end:** this is simply the end of the test.

Let's take an example. Let's say that, at the beginning, led_pulse is equal to 0. Then, it will be incremented on line 36 and will become 1. The test if led_pulse > 0 will be true, so the leds on the top of Thymio will be set to (1, 1, 0). The next test on line 39 will be false as led_pulse is not bigger than 32, it will thus stay unmodified. Finally, as the first condition was true (line 37), the else condition will not be true.

This will continue like that 32 times in a row, then when led_pulse is equal to 33, the second condition (line 39) will be true. led_pulse will then be set to -32. The next time the timer0 event is triggered, the first condition (line 37) will be false, thus the else code will be executed. It will just set the top led brightnesses equal to -led_pulse in order to make them glow less and less each time the timer0 timer is triggered.

The buttons event

This part will make the robot go faster when the forward button is touched, slower when the backward button is touched and stop when the central button is touched.

We again find the line onevent buttons. This means that whenever any button is pressed, the event is triggered.

In the code, we can see three similar blocks. The structure is based on the use of when … do … end. This structure is similar to the if … then … end with one exception. The when condition executes the code only if the previous value was false and the current value is true. It allows an action to be performed only if something changed !

The first block of when … do … end will be executed only when the forward button is touched, the second block when the backward button is touched and the last one when the central button is touched.

In the first block, the speed of the robot is increased by adding 50. speed += 50 is equivalent to speed = speed + 50, it is just shorter. In the second block the speed is decreased by subtracting 50 and in the last one, the robot is simply stopped by setting its speed to 0 (and also the target speed to 0).

You can see two calls to native functions on lines 48 and 53. These will simply limit the variable speed. The line 48 call math.min(speed, speed, 500) will store the minimum (i.e. smaller) of speed and 500 in the variable speed (the first argument of the math.min call).

The prox event

This part is the obstacle avoidance algorithm used by Thymio. It is based on a Braitenberg vehicle. It also contains a second part which is the table border detection.

The Braitenberg obstacle avoidance

This part is divided into two smaller ones. The first one is executed if the speed of Thymio is greater than 0 (e.g. if it is going forwards) and the second one if its speed is less than 0 (e.g. if it is going backwards). This introduces another type of conditional test, again quite close to the traditional if … then … end.

if … then … elseif … then … end

The only difference lies in the elseif. This simply means that there are more than two possibilities. Until now, we tested something saying: If it is not this, it is that. Now, we would say If it is not this, it could be that.

The robot will test the first condition on line 65 if speed > 0 then and if speed is not greater than 0, it will test the second condition of line 76 elseif speed < 0 and if speed is not less than 0 then the robot does not execute any of the code.

In the case where Thymio is going forwards, its speed is adapted according to the values of its frontal proximity sensors. This is the concept of the Braitenberg vehicle:

The values of the proximity sensors are used, after passing through a weighting process, to change directly the speed of each wheel of Thymio

Braitenberg.png

As you can see in the picture, the closer Thymio gets to an obstacle, the bigger its proximity sensor value on one side becomes, the smaller the speed of the wheel on the opposite side will be. As Thymio use a differential drive (the two wheels are driven independently), if the speed of the left wheel gets smaller, the robot will turn left and avoid the obstacle to its right !

This is exactly what lines 66 to 69 do.

Two coefficients are computed using a weighted sum of the horizontal proximity sensors, temp_braitenberg and temp_braitenberg_turn. One of them will take care of slowing down the robot in case of a frontal obstacle, the other will make the robot turn in case it detects an obstacle to either side.

The target speeds of both wheels are adapted using these coefficients. The functions used to calculate the coefficients are native functions of the mathematical library.

In the second case (line 76 to 80), when Thymio is going backwards, the only sensors capable of sensing an obstacle are the back ones. There are only two sensors at the back of Thymio, looking directly backwards, thus the obstacle avoidance will not be as effective as for forward movements. The concept is exactly the same as for the forward mode.

In lines 82 to 85, you will find four call in a row. Each one of them uses a native function of the mathematical library. These are limiting functions. Let's take the first one. The function will see which of motor.left.target and vmax is the smaller and will save the result in motor.left.target. It means that if motor.left.target gets bigger than the maximum speed Thymio can reach, motor.left.target is set to vmax. This way, you can ensure that motor.left.target never exceeds vmax (vitesse max in French meaning maximal speed). The three other calls are here for the same reason, to limit the left and right wheel speeds to an upper or lower value.

The table border detection

Here, the idea is to stop Thymio if it reaches the edge of a table.

We find another simple if … then … else … end conditional test. The test is about the prox.ground.reflected[0] and prox.ground.reflected[1] variables. If you put your Thymio on a table and look at the values of these variables, they should be between 300 and 1000. Now, if you take your robot and lift it from the table, these sensors should indicate approximately 0. Knowing that, we just need to test if the ground proximity sensors are smaller than a certain threshold. If so, the robot is not on a table, it has to stop (and the bottom leds are lit in red thanks to call leds.bottom.left(32,0,0) and call leds.bottom.right(32,0,0)). If not, the robot is on a table, we just turn off the leds and let the speed be managed by the Braitenberg controller. Note that the ground sensors are at the front of Thymio, so are no help when the robot is going backwards!

Thymio_IR_Ground_en.png

A small note concerning the ground sensors. You can see that there are three different values for the ground sensors: prox.ground.ambiant, prox.ground.reflected and prox.ground.delta. The first one measure the ambiant light without emitting any IR pulse, the second one measure the reflected IR pulse and the last one make the difference between the two firsts. This way, we can have a value of the reflected IR pulse without the pollution of the ambiant light.

That's it !

Have fun with your Thymio !

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License