Projet

Général

Profil

Lib yarp APP » Historique » Version 9

Frederic Elisei, 22/01/2025 18:34

1 1 Frederic Elisei
h1. Objectif
2
3
Pouvoir réaliser rapidement une maquette, en python, d'une interaction avec le robot Furhat, en faisant intervenir :
4
* synthèse et reconnaissance de parole via Furhat
5
* détection des interlocuteurs avec la caméra de Furhat
6
* détection visuelle avec YOLO (cartes vues de dessus par exemple) et une caméra externe
7
* gestion du regard/orientation de la tête vers les interlocuteurs ou les cartes détectées par YOLO
8 4 Frederic Elisei
9
10 1 Frederic Elisei
On découpera l'application sous forme d'un automate réactif, en états simples. 
11 9 Frederic Elisei
L'environnement d'exécution s'appuie aussi sur YARP [https://yarp.it/latest/], pour permettre la répartition des tâches entre plusieurs machines (windows, Linux...) qui communiqueront par messages.
12 1 Frederic Elisei
13
h1. Squelette minimal :
14
15 4 Frederic Elisei
On va s'appuyer sur lib_yarp_APP.py (et indirectement sur my_yarp.py) pour créer nos états et programmer les actions réactives et les transitions entre états.
16 1 Frederic Elisei
17
<pre><code class="python">
18
#! /usr/bin/env python3
19
20
# import app, furhat, State, AnyKeywords, yolo_center, yolo_target
21
from lib_yarp_APP import *
22
23
# create and run initial state, through its name
24
State("main")
25
app.run("main")
26
</code></pre>
27
28
h1. Gestion des évènements :
29
30 5 Frederic Elisei
Les états, comme celui nommé "main" dans l'exemple précédent, sont des objets dérivés de la classe State (ou d'une sous-classe). *Les états doivent avoir des noms différents.*
31
Pour implémenter des réactions à des évènements, il faut qu'ils fournissent une implémentation de tout ou partie des prototypes :
32 1 Frederic Elisei
33 7 Frederic Elisei
| <code class="python">do_in(self)</code>| appelée lorsqu'on rentre nouvellement dans l'état. Par exemple pour dire un message de bienvenue.|
34
| <code class="python">do_out(self)</code>| appelée lorsqu'on quitte l'état.|
35
| <code class="python">do_reco(self, key, msg)</code>| *msg* contient la chaîne de parole reconnue par le robot.
36
*key* peut faciliter la détection de certaines classes d'intention, indépendamment de leur formulation exacte. Il n'y en a pas beaucoup par défaut : "Oui{}" ou "Non{}" ou "Bonjour{}" "Fini{}"
37
...
38
  Oui{} "je suis d'accord"
39
  Oui{}"oui"
40
  Oui{}"ok"
41
  Unknown{} "C'est complètement l'idée que je me faisais de la chose."
42
|
43
| <code class="python">do_detect(self, data,yins,ydel)</code>| Cette méthode est appelée lorsque yolo détecte des apparitions ou disparations de cartes ou de la main du sujet. Ou régulièrement s'il y a des détections. 
44
*yins* et *ydel* sont des sets python (itérables mais non indexables comme une liste ou un tableau !), possiblement vides. Par exemple {} ou {0,4} ou yins.pop() si yins n'est pas vide. 
45
...
46 6 Frederic Elisei
Les labels sont accessibles en indexant *app.yolo_classes[]* : 0 correspond à HAND (une main), 1 à CARD (une carte inconnue), les suivants sont des cartes identifiées ("Bouilloire verte"...). 
47 7 Frederic Elisei
Les coordonnées de tout ce qui est visible sont dans la chaîne data. Si deux mains sont visibles, le set ne contiendra qu'une fois 0 au maximum (au moment de l'insertion ou de la disparition), mais data contiendra les références multiples.
48
...
49
Le format de la chaîne (str) *data* est visible sur cet exemple avec 2 détections (séparées par le +) :
50 6 Frederic Elisei
*"id1 confidence x y w h+id2 conf2 x2 y2 w h"*
51
<code class="python">len(data.split("+"))</code> vous donne donc le nombre d'objets détectés (peut-être 0...)
52 7 Frederic Elisei
pour retrouver s'il y a une/des mains : <code class="python">(d for d in data.split("+") if d.startswith("0 ") )</code>|
53 6 Frederic Elisei
|
54 3 Frederic Elisei
#
55
| <code class="python">do_user_in(self, user)</code> 
56 8 Frederic Elisei
| un nouvel utilisateur a été détecté par le robot en face de lui.
57
...
58
l'objet de type User a plusieurs champs:
59
*user.id*
60
*user.location* une chaine (str), directement utilisable avec *look3D()*
61
*user.visible* vaudra True
62
|
63 1 Frederic Elisei
#
64
| <code class="python">do_user_out(self, user)</code> 
65 8 Frederic Elisei
| un utilisateur précédemment détecté n'est plus visible.
66
...
67
Les champs sont les mêmes que précédemment, à part *user.visible* qui vaudra False |
68 1 Frederic Elisei
69 2 Frederic Elisei
on peut sous-classer State, comme ici:
70
<pre><code class="python">
71
class StateParrot(State):
72
73 1 Frederic Elisei
  def do_reco(self,key,msg):
74
      app.say(msg)
75
76
StateParrot("repeat_as_parrot")
77
app.run("repeat_as_parrot")
78
</code></pre>
79
ou attacher une méthode, sans sous-classer ou parce qu'elle sert dans plusieurs états :
80
<pre><code class="python">
81
State("main").set_behaviour(State.do_reco,catch_default_msg)
82
</code></pre>
83
84
h1. Actions possibles
85
86 3 Frederic Elisei
Dans un état, au moment où on y entre ou à réception d'un évènement, il est possible de générer certaines actions. 
87
On ne peut *pas* générer d'action régulière (idle), ou au bout d'un certain temps sans passer par ces évènements ou transitions. C'est un choix lié à la vitesse d'exécution sous Python et éviter de se retrouver avec beaucoup d'évènements en retard non traités.
88
89 1 Frederic Elisei
Voici les actions possibles :
90
91 3 Frederic Elisei
| <code class="python">app.switch("etat2"</code>) 
92
| prépare la transition vers un autre état. Elle ne sera pas instantanée, laissant aux évènements déjà en attente d'être dépilés. En clair, c'est la même file d'attente pour les évènements et le changement d'état.|
93
#
94
| <code class="python">app.sayNB("texte à prononcer")</code>
95
| fait prononcer au robot le texte voulu. La suite du traitement des évènements va reprendre dès que la phrase commencera à être prononcée ( _NB = non blocking)_  |
96
#
97
| <code class="python">app.say("texte à prononcer")</code>
98
| fait prononcer au robot le texte voulu. *Attention, l'appel est bloquant* et des évènements risquent de s'empiler dans la pile de traitement, surtout si la phrase est longue... |
99
#
100
| <code class="python">app.look("x y z",duration=Xms)</code>
101
| demande au robot de regarder aux coordonnées correspondant à la *chaine (str)* en paramètre. 
102
Celle-ci peut être obtenue par yolo ou construite à partir d'un triplet de flottants: "%d %d %d"%(1,2,3), en mètres (x-axis to the robot's left, the y-axis up, and z-axis to the front). Le repère est centré sur les yeux au repos.  
103 7 Frederic Elisei
Si *duration* est spécifié (en millisecondes), le regard reviendra sur sa cible initiale une fois ce délai écoulé. Sans ce paramètre, le regard du robot sera changé de façon plus définitive.|