The ball tracking program is given as an example in the Sony SDK (OPEN-R) and does the following: when there is a ball in front of the robot, it will track it by moving the head in the ball direction, otherwise it will scan the surrounding environment by moving the head in circles.
Moving the head in the direction of the ball can be written very simply in URBI with these two lines of code:
headPan = headPan + camera.xfov * ball.x & headTilt = headTilt + camera.yfov * ball.y;
The effect is to move at the same time (this is the meaning of the & separator) the head motors in pan and tilt directions, by an amount proportional to the x and y position of the ball in the image. The camera.xfov and camera.yfov coefficients are coming from the camera device that we will discover in the next chapter. They represent the x-angular and y-angular field of view of the Aibo camera, which are used here to convert the [-1/2;1/2] unit segment of ball.x and ball.y into actual angles in degrees.
To actually track the ball, and not simply move once in its direction, we will use a whenever command:
whenever (ball.visible) {
headPan = headPan + camera.xfov * ball.x &
headTilt = headTilt + camera.yfov * ball.y;
};
This program is only three lines long and does the ball tracking behavior expected. However, on the Aibo, it might be too reactive and lead to small oscillations of the head around the ball position. To avoid this, a simple technique from robotic control is to use an attenuation coefficient, ball.a, to limit the reactivity of the system. For example:
ball.a = 0.8;
whenever (ball.visible) {
headPan = headPan + ball.a * camera.xfov * ball.x &
headTilt = headTilt+ ball.a * camera.yfov * ball.y;
};
The next step is to switch from this behavior to the scanning behavior when the ball is not visible. The scanning behavior can be expressed with a simple sinusoidal movement on both headPan and headTilt. We use in the following example the 'n variable extension[6] which indicates that we are working with the normalized value of the variable, between 0 and 1, calculated from the known rangemin and rangemax properties. It is very convenient to avoid checking the actual range of a device and use it in a more general way:
period = 10s; headPan'n = 0.5 sin:period ampli:0.5 & headTilt'n = 0.5 cos:period ampli:0.5,
The cos modifier is identical to sin with a phase shift of pi/2. Note how the central value of 0.5 with the amplitude of 0.5 allows to cover the full range of the device: [0..1]
The above command does the circular movement required but when this behavior is started, the first position in the circle will be reached abruptly from wherever the head was before the command starts. To avoid this, we can precede the command with a smooth transition in one second towards the initial position in the circle, which is headPan'n = 0.5 and headTilt'n = 1:
headPan'n = 0.5 smooth:1s & headTilt'n = 1 smooth:1s;
The smooth modifier is similar to time but with a smooth S-shaped movement, instead of a linear movement.
Now, we can connect everything into one single behavior, using the 'at' event catcher as a glue. To avoid switching from the circular sweeping to the ball tracking too often, we also add a soft test, and we use the loadwav function to preload two wav files that we assign to the speaker device (described later) to play a sound when the ball is found or lost:
// Parameters initialization
ball.a = 0.9;
period = 10s;
found = loadwav("found.wav");
lost = loadwav("lost.wav");
// Main behavior whenever (ball.visible ~ 100ms) { headPan = headPan + ball.a * camera.xfov * ball.x & headTilt = headTilt+ ball.a * camera.yfov * ball.y; }; at (!ball.visible ~ 100ms) search: { { headPan'n = 0.5 smooth:1s & headTilt'n = 1 smooth:1s } | { headPan'n = 0.5 sin:period ampli:0.5 & headTilt'n = 0.5 cos:period ampli:0.5 } }; at (ball.visible) stop search; // Sound behavior at (ball.visible ~ 100ms) speaker = found onleave speaker = lost;
You can also use the onleave construct to group the two at (ball.visible) commands, but you must use the at& command in that case, to put the search command in background (because it is a never-ending command and at would never get the hand again otherwise).
[6] Other extensions are available in URBI. Extensions are a powerful way to modulate the evaluation of a variable. Check the URBI Language Specification for more details