We do many things simultaneously in our day-to-day life. Chatting while walking, and eating while watching television are common examples. In computer science, this process is known as multi-tasking. A task is something that can be performed independently; multi-tasking is the process of performing many tasks in parallel.
Within each computer (and robot), there is a CPU that handles the processing of tasks. CPU stands for Central Processing Unit, and is where all the computational operations are processed in the computer.
How is multi-tasking done on a CPU? A CPU core can only execute one sequence of instructions at a time. One way to do multi-tasking is to use a multi-core CPU, so that multiple sequences of instructions can be executed concurrently.
But the NAO’s CPU has only a single core. Although only one sequence of program instructions can be executed at a time, the operating system is able to maintain an illusion of true concurrency. The different programs are split into threads. Threads are sequences of code to be executed. The operating system switches which thread is currently executing on the CPU thousands of times per second. This switching is faster than a human’s perception, and provides the illusion that all of the threads are executing simultaneously.
In the figure below, the operating system rapidly switches between three threads. As such, it appears as if all three threads are running in parallel. Notice that the amount of time each thread runs is not constant, and threads do not always run in the same order. The operating system decides when and how to switch between threads.
But we don’t need to worry about the underlying behavior of threads or the operating system on the NAO, since Choregraphe takes cares of this for us.
Behavior Layers in Choregraphe
Behavior Layers are a feature in Choregraphe similar to threads. Each layer (or behavior) defines a separate thread that will run on the NAO. Some behaviors could be moving an arm, turning the head, saying something, or turning the LEDs on and off.
In the following exercises, we will show how to create behavior layers in Choregraphe to have the NAO do the robot dance.
Basic Task: Arms and Head
In this lesson, we’ll learn how to use behavior layers on the NAO. Behavior layers allow the NAO to perform multiple tasks on the NAO at the same time. This is known as multi-tasking. Let’s implement the robot dance, and make the NAO move its arms up and down and shake its head at the same time.
1. In a new Choregraphe project, create a new “Timeline” box.
2. Connect a Motor on box, Stand Up box, an Init Pose box, your new timline box, and a second Init Pose box, as shown below. The robot will stand up, go to its initial position, dance, and return to the initial position. Recall that you can open the Pose Library by clicking View -> Pose library in the top menu bar.
3. Double click on your new timline box to edit it. Beneath the timeline and the word “Motion”, you will see the words “Behavior layers” with a plus sign next to it. Click the plus sign to add a new behavior layer. We will make this layer move the arms up and down.
4. Select the new layer, and add a custom timeline box. Connect it to the initial play arrow, and also connect it to itself. This causes the box to repeat forever.
5. double click on the box and create using the simulated robot arm movements. At this point you need to store the position and make sure the movement stops at 115 frames.
6. Do the same procedure in another behavior layer for the head, creating "yes" and " no" head movements.
7. Play the behavior. The NAO should move its arms and shake its head, doing the robot dance. If the motions appear unstable, try reducing the frame rate.
8. (Optional) Try experimenting with the frequencies of the different layers to find a combination that is visually pleasing.
Intermediate Task: Completing the Robot Dance
In the previous exercise, we finished the basic robot dance. Now we will add in leg motions, flashing lights, and speech.
1. First, add leg motions. As in the previous exercise, add a new behavior layer, Legs, with two keyframes. In one of the keyframes, make the robot squat, and in the second, make it stand up straighter. Be sure to only store angles for the legs in these keyframes
2. Play the entire dance to make sure that the robot doesn’t fall over. If the robot does fall, try slowing down the rate of the squat motion, or make the robot stand up less tall. Changing the frequency of motion of the arm and head may also help. Make a dance that is stable and visually appealing.
3. The beauty of using behavior layers is that you can change the speed of one aspect of the dance, while keeping the rest constant. Try changing the frequency of motion of the arm so that it matches the squats, and make the head bob at a different frequency.
4. We have finished with the dance motions. Add another layer for the NAO to speak. Place an infinitely repeating Say box in the layer, and have the robot say something of your choosing, such as “Nao.” You may also choose to add a Wait box to this layer, if you wish. Click the wrench on the Wait box to set how long the delay should be (in seconds) between saying words.
5. Now add the final behavior layer, which will control the LEDs. Place a bunch of loops with pairs of LED boxes, one to turn the LEDs on and one to turn them off.
6. Click on the wrenches to set each box’s parameters. Choose an appropriate duration (in seconds), and make the intensity 100% in the first box and 0% in the second box. This will make the lights blink continuously. For the eye LEDs, you can double click on the boxes to select colors.
7. (Optional) Add another layer to play music of your choosing.
8. Now play the entire dance. It should include arm, head and leg motions, along with speech and flashing lights.
Advanced Task: Walking in Circles
We have finished the robot dance. Now, let’s try doing something fancier, and have the NAO do part of the robot dance while walking in a circle. To do this, we will have the NAO use the omni-directional walk. We will record the odometry information as it walks to determine when we are finished.
1. First, remove the behavior layers for controlling the arms and the legs. The NAO cannot do these motions while walking stably.
2. Next, add a new behavior layer for the walk. Create a new box in this layer, connected to both the start and end arrows. Double click on the box to edit the python code. We will be adding functions into the code, bit by bit.
3. Before we begin editing and adding functions to the code, we need to import the relevant libraries. Add the following lines before the class MyClass line.
4. The first function we will edit is__init__.
__init__ is called only once (init stands for initialization), before the behavior starts executing. You have created a proxy to ALMotion in the past within the onInput_onStart function. The main difference here is that the variable name is self.motionProxy instead of just motionProxy. The addition of self. means that motionProxy is now a member variable of the class - it can be accessed in other functions.
5. Next, we will create a new function,simplifyAngle.
The while structure is a loop, similar to a for loop. However, instead of iterating over some value, it repeats until the condition in the loop evaluates to false. So the first loop will continue subtracting 2pi from theta until it is less than pi . Similarly, the second loop will continue adding 2pi until theta is more than -pi . Thus, simplifyAngle normalizes theta (in radians) to between -pi and pi .
6. We will now create another function, updateTheta.
The updateTheta function computes how much we have rotated so far (also known as odometry). ALMotion’s function, getRobotPosition, returns the pose of the robot
(x, y, theta) in global coordinates based on where the NAO first started. Since we are only interested in the amount of rotation, we access the third element in the array (array indices start from 0, so we retrieve element ).
Also, we only want the change in position since the Circle box started, not since the robot started, and the second line maintains this information.
Finally, we return the amount of rotation since the Circle box started (theta), and the current global angle of the robot (nextTheta) - that is used for future calculations of updateTheta.
7. Lastly, we will edit the onInput_onStart function - this function is called when the box executes, which we have done in previous exercises. We will use this function to have the NAO walk in a complete circle and then stop walking.
The first line calls setMoveArmsEnabled on ALMotion, which enables the swinging of the arms as the NAO walks. The swinging of the arms helps to keep the NAO balanced.
The next three lines initialize variables. globalTheta stores the global angle of the robot when the box starts executing; theta stores the rotation so far (0 since the robot hasn’t started walking yet); rotationSpeed is a value from -1 to 1 that controls how quickly the robot will rotate.
Thewhile loop continues executing until the odometry for theta exceeds 2pi , meaning that the circle is complete. Inside the loop, setWalkTargetVelocity is called to set the velocity of the NAO’s walk. The first 3 parameters are velocity in the x (forward), y (sideways, to the left), and theta (rotation, counter-clockwise) directions. Since we want the robot to walk in an arc, the walk velocity is set to 0.5 in the forward direction and 0.25 in the rotational direction. This makes the robot walk in an arc, where it walks forward and rotates at the same time.
The time.sleep function puts the thread to sleep, so the CPU can focus on other tasks. Without this line, the robot would execute this loop as fast as possible, but this is unnecessary and a waste of processing time.
Finally, we set the walk velocity back to zero. This stops the robot’s walk once the circle is complete. Otherwise, the function would end but the robot would continue walking.
8. The behavior will currently stop when it reaches the red flag you placed on the timeline previously. Right click on the red flag, and select “Reset end frame.” Now the behavior will end when the robot finishes walking in a circle.
9. Run the behavior. The robot should walk in a circle and stop.
WARNING: do not hit the red X button to stop the behavior. Otherwise, the robot may continue walking endlessly. If the NAO does walk endlessly, create a new project with a single Walk Toward box, and set the walk velocity in all directions (x, y and theta ) to 0.
10. Measure the odometry error at the end of the behavior. That is, how far does the robot’s final position differ from its initial position?
11. Does the robot walk in a clockwise or counter-clockwise circle? Change the code so that the robot walks in the opposite direction.
12. Measure the radius of the circle the robot walked in. How can you change the radius of the circle that the NAO walks? Make the NAO walk in circles of radii 0.25 m and 1 m.
13. Look again at the simplifyAngle method. This algorithm works, but it is terribly inefficient. What will happen if a very large number is passed to the function? How many times will the loop repeat? In computer science, this algorithm is called O(Ɵ) (“big-O of theta”), meaning that the runtime increases linearly with Ɵ. The simplifyAngle function can be written to be O(1) , meaning the runtime does not depend on Ɵ and the algorithm always runs in constant time. Rewrite the simplifyAngle algorithm to be O(1) . Your new function should not include any loops.
Hint: Useint(number) to truncate (cut off the parts after the decimal) the number to an integer. So, int(1.2) = 1, int(2.0) = 2, and int(1.9999) = 1.
- Modify the robot dance so that the NAO only bobs its head up and down each time you touch the head sensors.
Hint: add a Tactile Head box that links to the Head keyframe in the behavior layer.
- Make the NAO walk in a square (building off the earlier exercise) and perform a different combination of speech and/or motion as it traverses each edge of the square.
- Make the NAO walk in a figure eight while doing the robot dance (minus the arms).