Le programme principal

Le programme de détection de balle est donné en exemple dans le kit de développement de Sony (SDK OPEN-R) et réalise les actions suivantes: lorsqu'une balle se trouve en face du robot, ce dernier va la suivre en bougeant la tête. Autrement, il va la chercher en bougeant la tête en cercles.

Orienter la tête vers la balle peut être écrit très simplement en URBI avec les deux lignes suivantes:

headPan  = headPan  + camera.xfov * ball.x &
headTilt = headTilt + camera.yfov * ball.y;

Cela aura pour effet de bouger en même temps (c'est ce que veut dire le séparateur &) les deux moteurs de tête pan et tilt, d'une quantité proportionnelle aux positions x et y de la balle dans l'image de sa caméra. Les coefficients camera.xfov et camera.yfov viennent du device camera que nous découvrirons dans le chapitre suivant. Ils représentent l'angle x et l'angle y du champ de vision de la caméra de l'Aibo qui sont utilisés pour convertir l'intervalle normalisé [-1/2;1/2] de ball.x et ball.y en degrés.

Pour suivre la balle et non plus s'orienter une fois dans sa direction, nous allons utiliser la commande whenever:

whenever (ball.visible) {
  headPan  = headPan  + camera.xfov * ball.x &
  headTilt = headTilt + camera.yfov * ball.y;
};

Ce programme fait seulement trois lignes et réalise le comportement de suivi de balle que l'on souhaitait. Cependant, avec un Aibo, cela risque d'être trop réactif et conduira à de faibles oscillations de la tête autour de l'orientation de la balle. Pour éviter cela, une technique robotique simple est d'utiliser un coefficient d'atténuation, ball.a, pour limiter la réactivité du système. Par exemple:

ball.a = 0.8;
whenever (ball.visible) {
  headPan  = headPan + ball.a * camera.xfov * ball.x &
  headTilt = headTilt+ ball.a * camera.yfov * ball.y;
};

La prochaine étape est de basculer de ce comportement vers le comportement de recherche quand la balle n'est pas visible. Le comportement de recherche peut être exprimé avec un simple mouvement sinusoïdal sur à la fois headPan et headTilt. Nous utilisons dans l'exemple suivant l'extension de variable 'n [6] qui indique que l'on travaille avec une valeur normalisée de la variable, comprise entre 0 et 1, calculée à partir des propriétés rangemin (la limite inférieure) et rangemax (la limite supérieure). Cela s'avère très pratique d'éviter de contrôler la valeur actuelle d'un device et de le manipuler d'une façon générique:

periode = 10s;
headPan'n  = 0.5 sin:periode ampli:0.5 &
headTilt'n = 0.5 cos:periode ampli:0.5,

Le modificateur cos est identique à sin mais avec un décalage de phase de pi/2. Remarquez comme la veleur centrale 0.5 avec l'amplitude 0.5 permet de couvrir toute l'étendue du device: [0..1].

La commande précédente réalise le mouvement circulaire requis mais lorsque ce comportement est lancé, la position initiale sera atteinte brusquement depuis l'emplacement où se trouvant la tête avant le lancement de la commande. Pour éviter cela, nous pouvons précéder notre commande avec une transition douce d'une seconde vers la position initiale du cercle qui est headPan'n = 0.5 et headTilt'n = 1:

headPan'n  = 0.5 smooth:1s &
headTilt'n = 1   smooth:1s;

Le modificateur smooth est similaire à time, à la différence qu'il ajoute la douceur du tracé d'un "S", au lieu de faire un mouvement linéaire.

Désormais, nous pouvons tout connecter en un seul comportement, en utilisant la capteur d'évenement at comme ciment. Afin d'éviter de basculer du balayage circulaire au suivi de la balle trop souvent, nous ajoutons également un test coulé, et nous utilisons la fonction loadwav pour précharger deux fichiers sonores que l'on affecte au device speaker (le haut-parleur, nous décrirons ce device plus tard) pour jouer un son lorsque la balle est trouvée ou perdue:

// Parameters initialization
ball.a = 0.9;
period = 10s;
trouvee = loadwav("found.wav");
perdue  = loadwav("lost.wav");
// Comportement principal
whenever (ball.visible ~ 100ms) {
  headPan  = headPan  + ball.a * camera.xfov * ball.x &
  headTilt = headTilt + ball.a * camera.yfov * ball.y;
};

at (!ball.visible ~ 100ms)
recherche: {
          { 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 recherche;

// Comprtement sonore
at (ball.visible ~ 100ms) speaker = trouvee
onleave speaker = perdue;

Vous pouvez aussi utiliser la construction onleave pour regrouper les deux commandes at (ball.visible), mais vous devez alors employer la commande at&, pour placer la commande recherche en tâche de fond (car c'est une commande sans fin et donc at ne rendrait jamais la main).



[6] D'autres extensions sont disponibles dans URBI. Les extensions sont de puissants outils pour moduler l'évaluation d'une variable. Consultez le document "URBI Language Specification" pour de plus amples détails.