1. Introduction

Etant un passionné de jeux de cartes, j'ai eu l'idée de faire mon projet H sur cette thématique. En réfléchissant un peu, je suis arrivé à la conclusion que distribuer les cartes était toujours la partie la moins intéressante de n'importe que jeu de cartes et qu'il y avait certainement un moyen de s'aider de l'informatique pour se débarrasser de cette tâche pénible.
C'est comme cela que j'ai pensé a transformer mon Thymio en croupier.
Ne serait-it pas génial de déposer ses cartes dans une machines et qu'elle se charge de les distribuer pour vous ?

2. Matériel et méthodes

2.1 Matériel

Il ne faut pas beaucoup de matériel pour réaliser ce projet. Nous avons juste besoin de:

  • 2x Thymios
  • Paquets de cartes
  • Ordinateur avec logiciel Aseba Studio for Thymio
  • Papier
  • Ruban adhésif
  • poids
  • Boite à cartes

2.2 Méthode

Le projet nécessitera 2 Thymios, un pour se déplacer et l'autre, retourné sur lui même, pour éjecter les cartes une à une grace à ses roues. La réalisation de ce projet est constituée en grande partie par le codage des deux Thymio mais aussi une petite partie mécanique lors de la distribution des cartes.

2.2.1 sélection du nombre de joueurs (Thymio1)

Tout d'abord, il faut faire comprendre au Thymio du bas le nombre de tas de cartes qu'il doit créer. Pour cela nous allons utiliser les 7 capteurs de distances prox.horizontal qui nous permettront de définir le nombre de joueurs entre 2 et 8 et non 1 et 7, car je ne voyais pas l'intérêt de distribuer un paquet de cartes en seul tas.

var ZeitAnfang
var True
var Temps
var start
var NombreJoueurs
var modulo

start = 0
ZeitAnfang = 0
True = 1

sub vitesse
motor.left.target = 100
motor.right.target = -100
ZeitAnfang = 0
True = 0

onevent prox
if Temps < 800 then
if prox.horizontal0 > DETECTION then
NombreJoueurs = 2
callsub vitesse
start = 1
modulo = 6
True = 1
elseif prox.horizontal1 > DETECTION then
NombreJoueurs = 3
callsub vitesse
start = 1
modulo = 5
True = 1
elseif prox.horizontal2 > DETECTION then
NombreJoueurs = 4
callsub vitesse
start = 1
modulo = 4
True = 1
elseif prox.horizontal3 > DETECTION then
NombreJoueurs = 5
callsub vitesse
start = 1
modulo = 4
True = 1
elseif prox.horizontal4 > DETECTION then
NombreJoueurs = 6
callsub vitesse
start = 1
modulo = 3
True = 1
elseif prox.horizontal5 > DETECTION then
NombreJoueurs = 7
callsub vitesse
start = 1
modulo = 3
True = 1
elseif prox.horizontal6 > DETECTION then
NombreJoueurs = 8
callsub vitesse
start = 1
modulo = 3
True = 1
end
end

Cette partie du code s'occupera de la sélection du nombre de joueurs sous la variable NombreJoueurs ainsi que de lancer la première rotation du Thymio. Le code comporte une constante DETECTION qui est la distance à laquelle il faut approcher son doigt des capteurs de proximité pour qu'il exécute la suite du code. Je l'ai fixée à 4000 ce qui veut dire que l'on doit approcher son doigt très proche du capteur afin qu'il le prenne en compte mais cela réduit les erreurs de détections possibles.
La sous routine vitesse définit la vitesse de rotation du Thymio et fixe les variables True et ZeitAnfang à 0 ce qui sera crucial pour la prochaine étape du code.
Comme nous le verrons dans la prochaine étape, la variable Temps représente le temps écoulé depuis le lancement du programme et s'incrémente chaque centième de seconde. Dès lors, la sélection du nombre de joueurs n'est exécutable que dans les 8 secondes qui suivent le lancement du programme et ne pourra plus être modifié par la suite, ce qui empêche le Thymio de commencer avec un certain nombre de joueurs puis de changer en cours de route
La variable start est aussi fixée à 1 ce qui permettra d'effectuer la suite du code et la variable modulo se verra affecter une valeur en fonction du nombre de joueurs dont j'expliquerai l'utilité après.

2.2.2 Rotation du Thymio en fonction du nombre de joueurs (Thymio1)

Cette partie du code s'occupera d'arrêter la rotation du Thymio à l'angle voulu afin de séparer les tas de cartes. Aseba Studio for Thymio ne permettant pas de calculer des angles en degrés, j'ai dû trouver le temps nécessaire au Thymio afin qu'il fasse une rotation de 360 degrés à une vitesse de 100 puis j'ai diviser ce temps par le nombre de joueurs afin de trouver le temps voulu pour répartir les tas équitablement autour des robots. Après de multiples essais, j'ai trouvé que le temps nécessaire pour une rotation de 360 degrés était de 8,97s et j'en ai créé la constante UNE_ROTATION = 897 car le timer0 tourne en centièmes de secondes.

var Cartes
var Paquets
var Aika

timer.period0 = 10
timer.period1= 1000

onevent timer1
if start == 1 then
Aika ++
end

onevent timer0
Temps ++
if start == 1 then
ZeitAnfang ++
if ZeitAnfang > 0 then
if Cartes < Paquets then
if ZeitAnfang > UNE_ROTATION/NombreJoueurs then
motor.left.target = 0
motor.right.target = 0
if Aika % modulo == 0 then
callsub vitesse
True = 1
end
end
end
end
end

C'est dans cette partie du code que la valeur Temps s'incrémente. Cette variable commence son incrémentation dès le lancement du programme tandis que la variable Aika elle, est en secondes et commence seulement quand start = 1, soit quand le nombre de joueurs a été choisis et que le Thymio commence sa première rotation.
ZeitAnfang, qui est aussi une variable de temps, commencera aussi son incrémentation à ce moment et sera remise à 0 à la fin de l'événement quand la sous-routine vitesse sera appelée. l'intérêt de redéfinir ZeitAnfang à 0 pour le ré-incrémenter par la suite est que elle commence à 0 à chaque début de l'événement. Maintenant que ZeitAnfang nous donne la durée de temps écoulé depuis le début d'une rotation, il suffit d'arrêter la rotation du robot quand ZeitAnfang devient plus grand que UNE_ROTATION divisé par le nombre de joueurs présents. Par example si il y 6 joueurs, Les moteurs s'arrêteront après 149 centièmes de secondes (897/6).
C'est à ce moment la que la valeur attribuée à modulo au début du programme devient utile. Maintenant que les robots ont effectué leur quartier de rotation, il faut activer le Thymio du dessus pour qu'il fasse tomber une carte puis il faut relancer le Thymio du bas pour effectuer une nouvelle rotation etc...
A chaque fois que aika modulo la variable modulo sera égale à 0, la sous routine vitesse sera appelée, réinitialisant ZeitAnfang et True à 0 et relancera le robot pour une nouvelle rotation. Par example pour 4 joueurs, modulo = 4. Cela implique que chaque 4 secondes depuis la détection du nombre de joueurs, l'expression Aika/modulo sera égale à 0 car aika sera un multiple de 4 et la sous-routine vitesse sera appelée. Le Thymio du bas mettant 2.24 secondes pour tourner de 90 degrés, il restera 1,76 secondes (4-2,24) pour que le Thymio du dessus jette une carte avant de repartir à l'emplacement du 2ème tas.
Cette partie du code sert aussi à fixer True = 1 après la sous-routine vitesse dont je vais expliquer l'utilité dans la prochaine partie de la méthode.
Notons aussi que pour que tout l'événement se déclenche, il faut qu'il reste des cartes à distribuer sinon les robots arrêterons de tourner mais j'y reviendrai dans la 5ème partie de la méthode.

2.2.3 connexion entre les robots et comptage de cartes (Thymio1)

Dans cette partie du code, on comprend l'utilité de la variable True. Si True = 1 et que les moteurs sont éteints, cela veux dire que le Thymio du bas a effectué sa rotation et qu'il est temps pour le second Thymio de distribuer sa carte. La commande "emit befehl 200" sera envoyée au Thymio du dessus et servira à créer un événement befehl avec une valeur de 200. Toutefois la transmission entre les machines est hasardeuse à cause du bruit engendré par les autres appareils et c'est pourquoi j'ai décidé d'envoyer 3 messages, afin d'être quasiment assuré que le message arrive à bon port.
Maintenant que l'ordre de distribuer une carte a été donné, on peut incrémenter la variable Cartes afin de tenir le compte des cartes distribuées.
Cela étant fait, on va fixer True = 0 afin de rompre l'événement.

Cartes = 0

if True == 1 then
if motor.left.target == 0 then
emit befehl 200
emit befehl 200
emit befehl 200
Cartes ++
True = 0
end
end

2.2.4 Sélection du nombre de paquets de cartes (Thymio2)

En plus de distribuer les cartes, le 2ème Thymio se charge de savoir le nombre de cartes à distribuer. Pour cela nous allons à nouveau utiliser les capteurs de proximité prox.horizontal afin de faire comprendre au Thymio le nombre de paquets qu'il devra distribuer.
A nouveau, ce choix est possible seulement pendant les 8 secondes après le lancement du programme pour ne pas avoir de risque que cette variable change durant la distribution.

var Tid
var Paquet

timer.period1 = 1000

onevent timer1
Tid ++

onevent prox
if Tid < 8 then
if prox.horizontal2 > DETECTION then
emit Nb_Paquet 1
elseif prox.horizontal5 > DETECTION then
emit Nb_Paquet 2
elseif prox.horizontal6 > DETECTION then
emit Nb_Paquet 3
end
end

Durant ces 8 secondes, si le capteur prox.horizontal2 détecte un objet très proche, il enverra un événement Nb_Paquet de valeur 1 au Thymio du bas. le même événement sera envoyé s'il détecte quelque chose au capteurs 5 ou 6 mais la valeur envoyée sera respectivement 2 ou 3.
Pour des raisons de place, au maximum 3 paquets peuvent tenir sur le Thymio du dessus mais il serait facilement envisageable de rajouter du code selon le même schéma pour des paquets supplémentaires en utilisant plus de capteurs de proximité.

2.2.5 Interpretation du nombre de cartes (Thymio1)

Le Thymio du bas doit maintenant récupérer l'information du nombre de paquet pour savoir combien de cartes à distribuer et s'arrêter à la fin.

var Paquet

onevent Nb_Paquet
if event.args0 == 1 then
Paquets = 1
elseif event.args0 == 2 then
Paquets = 2
elseif event.args0 == 3 then
Paquets = 3
end

Paquets = Paquets*52

Pour cela il suffit de tester la valeur envoyée et de définir la variable Paquet nombre entre 1 et 3 en fonction de cette valeur envoyée.
Ayant le nombre de paquets, on obtient le nombre de cartes en multipliant Paquet par 52.
Durant la distribution, il faudra stopper les Thymios une fois que la variable Cartes atteindra Paquet, et c'est l'utilité de la ligne de code dont je n'avais pas parlé dans la section 2.2.2

2.2.6 Déclenchement de la distribution (Thymio2)

Finalement il reste à activer les roues du 2ème Thymio pour jeter les cartes.
Dans la partie 2.2.3 on avait envoyé une valeur (200) depuis le Thymio du bas à celui du dessus lorsqu'il est à l'arrêt. Il faut maintenant l'interpréter pour activer les moteurs.

var Timi

timer.period0 = 10

onevent befehl
Timi = 0
onevent timer0
Timi ++
if Timi < 66 then
motor.left.target = event.args0
motor.right.target = event.args0
elseif Timi > 66 then
motor.left.target = 0
motor.right.target = 0
end

A chaque fois que l'événement befehl est lancé, Timi qui est une variable de temps est fixée à 0 et recommence son incrémentation. Après plusieurs tests, j'ai trouvé qu'à une vitesse de 200, le temps idéal pendant lequel les moteurs doivent tourner pour éjecter une carte est de 66 centièmes de secondes. En conséquent, tant que Timi est plus petit que 66, les moteurs tourneront à 200 et s'éteindront après 66 centièmes, en attendant que l'événement befelf soit rappelé par le Thymio du bas.

2.2.7 Montages

Le montage de ce projet et relativement simple. Tout d'abord il faut disposer le 2ème robot à l'envers sur le premier pour que l'on puisse disposer les cartes sur ses roues. Ce qui pose un peu plus problème est de faire tenir les cartes sur les roues et que seule la première carte soit éjectée à la fois. Pour cela il faut augmenter la pression des cartes sur la roue en mettant des poids dessus (les jetons de poker sur la photo).
Le moyen que j'ai trouvé pour que les cartes ne partent pas dans n'importe quelles directions et restent bien alignées sur les roues est de les coincer à l'intérieur de leur boîte qui sera elle meme fixée au Thymio avec du ruban adhésif. De plus, j'ai mis du papier à l'intérieur de la boite à cartes afin d'exercer une pression sur la tranche des cartes à nouveau dans l'idée d'augmenter la pression sur les roues et d'empêcher les certes de partir.

3. Résultats

Je suis globalement content des résultat obtenus. Le projet marche bien dans l'ensemble mais rencontre quand même quelques soucis.
Le problème majeur est certainement les transmissions d'informations entre les robots. Il arrive que l'événement "befehl" ne se déclenche pas et cela veut dire que la carte ne sera pas distribuée, ce qui est évidement un gros problème pour une distribution qui se veut équitable.
Un autre problème est la distribution des cartes qui ne fonctionne pas à 100%. Il arrive encore trop fréquemment que plusieurs ou aucune cartes partent en même temps malgré tous mes tests pour l'optimiser.
Hormis ces faits là, le projet ne rencontre pas trop de problèmes. La partie informatique ne pose techniquement pas de soucis et elle représente la grosse majorité du projet. En m'y étant pris plus à l'avance j'aurais peut être réussi à optimiser la partie mécanique et à réduire les erreurs de distribution mais je pense que le projet est déjà viable comme cela.

4. Discussion

La forme finale de mon projet remplit les attentes de mon préprojet mais de façon simplifiée. A la place de devoir utiliser un RaspberryPi pour contrôler un moteur externe comme je pensais faire au début, on m'a fait remarquer qu'un 2ème Thymio pouvait très bien remplir cette fonction et que cela simplifierait la réalisation du projet.
Il existe quand même une maladresse dans mon code lors de la sélection du nombre de joueurs et du nombre de paquet. Il faut en effet lancer les 2 Thymios ensemble le plus rapidement possible et d'abord choisir le nombre de paquet puis choisir le nombre de joueurs sans quoi le programme ne fonctionnera pas. Il existe certainement un moyen de pallier à cela mais comme dit précédemment je m'y suis pris trop tard.
Concernant les communications entre les robots qui ne fonctionnent pas toujours, elles surviennent seulement à la réception du signal. Le fait d'émettre 3 fois le signal befehl garantit quasiment à coup sûr que le signal soit envoyé mais il n'est pas toujours lu.
La seule façon qui m'est venue à l'esprit pour régler ce problème de lecture est de créer un nouvel événement qui ordonnerait au Thymio du bas de renvoyer le signal jusqu'à ce qu'il soit lu par l'autre Thymio si l'événement befehl ne de déclenche pas. Mais le problème serait que pour cela, il faudrait recréer des nouveaux signaux ce qui créerait encore plus de bruit et qui auraient, eux aussi, des chances de ne pas être lus.
Il n'a pas non plus été si simple de trouver la constante UNE_ROTATION et les 0,66 seconde pour les motors. J'ai dû beaucoup procéder par tâtonnement et je ne serais pas surpris que les nombres que j'ai trouvés ne sont pas les plus optimaux, ce qui expliquerait par ailleurs une partie des erreurs.
Un autre élément que je n'avais pas prévu est le temps que dure le programme. Cela peut prendre jusque à 5 minutes juste pour distribuer un seul paquet de cartes, ce qui rend le projet pas très efficace...
Une façon simple d'améliorer l'efficacité de la distribution est de légèrement faire ressortir la première carte qui vas être éjectée. Cela va permettre d'être sur que toutes les premières cartes soient distribuées car elles tomberont avant la fin de rotation de la roue et cels réduit les erreurs de distribution.

5. Conclusion

En conclusion, je pense pouvoir dire que j'ai réussi mon projet et j'en suis content. Ce projet m'a appris à me débrouiller à partir de quasiment rien et à réaliser une idée que j'avais en tête avec les moyens présents. J'ai appris beaucoup de choses sur Aseba Studio for Thymio et je suis désormais beaucoup plus à l'aise avec ce langage, notamment dans la gestion d'événements. J'ai aussi eu du plaisir à réaliser ce projet car j'étais impatient de voir le résultat final.
Ce projet H m'a motivé pour le Projet P à venir et cette fois, je m'y prendrai à l'avance !

Références

configuration Thymio sans fil
Site officiel de Thymio
langage Aseba