19H — Thymio musicien
Par admin le mardi, février 5 2019, 12:00 - 2018-2019 - Lien permanent
Ce projet a été réalisé par Baptiste Maquignaz.
1. Introduction
Les robots Thymio ont été conçu par des étudiants de l’EPFL pour servir de supports didactiques permettant d’approcher d’une façon ludique la programmation et la robotique. Ces robots comportent de nombreuses entrées, dont, notamment, une dizaine de capteurs répartis à l’avant, à l’arrière et au dessous du robot, ainsi qu'un micro qui lui permet de capter et d'émettre des sons. Le but de ce projet est de permettre à un robot Thymio de lire des partitions musicales et de les rejouer à mesure que les notes défilent. Bien évidemment, il est impossible pour Thymio de lire une partition conventionnelle ; une partition est bien trop complexe pour être déchiffrée tel quel. Il s'agira donc de trouver un moyen efficace de transcrire des partitions pour les rendre lisibles par un robot Thymio ainsi qu'une manière de lui faire déchiffrer ces nouvelles partitions.
Pour ce faire, il existe plusieurs méthodes ; la méthode que j’ai utilisée consiste à faire avancer le robot grâce à ses deux roues sur la partition et à utiliser ses deux capteurs au sol pour lui permettre de lire, avec l’un, la longueur de la note et avec l’autre sa fréquence. Pour ce faire, j’utiliserai le logiciel Aseba Studio for Thymio, un logiciel gratuit mis à disposition par les concepteurs du Thymio, ce qui me permettra de donner des instructions au robot en langage Aseba. De cette manière j’aurai l’occasion de me familiariser un peu plus avec ce langage et d’approfondir mes connaissances en robotique.
2. Matériel et méthodes
2.1 Matériel
1 x robot Thymio
1 x câble USB - mini USB
1 x Imprimante
Aseba Studio for Thymio
Partitions pour Thymio
2.2 Méthode
2.2.1 Première méthode : concept
Pour transformer le robot en musicien, il a fallu régler plusieurs problème et passer par différentes méthodes. Ma première idée était en fait d’utiliser chacun des capteurs au sol pour détecter une nuance de gris. La longueur de la note était alors définie par la largeur des rectangles.
Cette méthode était, d'après moi, la bonne : elle permettait de reconnaître un nombre conséquent de notes et permettait de gérer facilement la longueur des notes.
2.2.2 Première méthode : code
Avant de commencer à regarder le code, il faut savoir que le robot avance tout droit à vitesse constante durant toutes ses opérations. De plus, il faut savoir que les deux capteurs au sol, respectivement prox.ground.delta 0 et 1, renvoient des valeurs entre, plus ou moins, 250 (noir) et 1000 (blanc) lorsqu'ils roulent sur la partition.
Afin de reconnaître les notes, j’ai donné une série de conditions à mon robot pour lui permettre de « choisir » une notes dans une liste ‘gamme’ en fonction des valeurs que renvoyaient ses capteurs.
var gamme[] = [1047,1175,1319,1397,1568,1760,1976,2093,1109,1245,1480, 1661,1865,0]
J’ai utilisé 2 nuances de gris(claire et foncée), du blanc et du noir afin d’avoir 15 notes différentes à jouer (notez qu'ici, ‘gamme’ n’en contient que 13 pour les tons et les demi-tons d’un octave). Les deux capteurs reconnaissent chacun 4 nuances. Il y a donc 16 possibilités au total. Cependant , si les deux capteurs lisent du blanc, le robot n’émet pas de son. Ainsi il est possible de lui faire lire des silences.
Ces 16 conditions étaient initialement contenues dans une sous-routine ‘change’. Ici, j’utilise une autre sous-routine ‘son’ De plus j’utilise trois constantes ‘LOW’, ‘MID’ et ‘HIGH’(respectivement 300, 550 et 800) pour différencier les nuances de gris.
sub change if prox.ground.delta[0] > HIGH or prox.ground.delta[1] > HIGH then frequence = gamme[13] callsub son elseif prox.ground.delta[0] < LOW and prox.ground.delta[1] < LOW then frequence = gamme[0] timer = 0 callsub son elseif prox.ground.delta[0] < MID and prox.ground.delta[1] < LOW then frequence = gamme[1] timer = 0 callsub son elseif prox.ground.delta[0] < LOW and prox.ground.delta[1] < MID then frequence = gamme[2] timer = 0 callsub son ...
La sous-routine ‘son’ a pour fonction de demander à Thymio de jouer une note. Il utilise la routine ‘sound.freq’ (routine existante) qui permet de faire jouer un son d’une certaine hauteur (en Hz) et d’une certaine longueur (en 60ème de seconde). La hauteur de la note est prélevée dans la variable ‘fréquence’ et sa durée dans la variable ‘longueur’ :
var longueur = 600 sub son call sound.freq(frequence,longueur)
Les notes durent donc 10s. Ce qui nous permet de jouer des notes de la bonne longueur si elles durent moins de dix secondes ; en effet, ‘change’ ne se déclenche que si les capteurs lisent un changement conséquent de nuance de gris :
var curent[]=[prox.ground.delta[0],prox.ground.delta[1]] onevent prox if abs(prox.ground.delta[0]-curent[0])> EPSILON or abs(prox.ground.delta[1]-curent[1])> EPSILON then callsub change curent[0]= prox.ground.delta[0] curent[1]= prox.ground.delta[1] end
Pour constater la différence de ton, j'utilise une liste nommée 'current' afin de "sauvegarder" les valeurs des deux capteurs au sol avant leur changement.Ici, le robot va vérifier si les valeurs des capteurs au sol qu’a « copié » la variable current ont changé d’une valeur supérieur à la constante ‘EPSILON’ (qui vaut 100 dans le cas présent) auquel cas on lancera ‘change’ et on actualisera ‘current’. De cette manière, on évite que le robot joue la note qu’il lit actuellement à chaque fois que les capteurs s’actualisent (soit 10x par secondes), comme dans l'exemple ci-dessous :
onevent prox if prox.ground.delta[0] > HIGH or prox.ground.delta[1] > HIGH then frequence = gamme[13] callsub son
2.2.3 Première méthode : problème
Bien que cette méthode me semblait théoriquement correct, elle ne fonctionne pas en pratique. Ce problème est dû au fait que les capteurs au sol renvoie une moyenne de la zone qu’ils sont en train de lire. Ainsi, les capteurs ne feront pas la différence entre du gris sur l’entier du capteur ou une moyenne de noir et de blanc : Ce problème rentre en compte lorsque les capteurs se situent entre deux notes ; au lieu de lire distinctement les deux notes, les capteurs peuvent passer par plusieurs autres notes en renvoyant des valeurs intermédiaires durant la transition. La fréquence de mise à jour des capteurs étant particulièrement rapide (10 x par seconde), j’ai préféré chercher une méthode légèrement différente plutôt que de régler ce problème qui me paraissait insoluble.
2.2.4 Deuxième méthode : concept
La deuxième méthode est très similaire à la première ; le seul changement d’idée a été d’utiliser un des deux capteurs pour lire la durée des notes et l’autre pour lire leur hauteur. Le capteur responsable de la durée varie ainsi entre du noir et du blanc. De cette manière il n’y a plus d’ambiguïté sur les moyennes ; Les notes sont toutes séparées par des blancs pendant lequel le Thymio joue un son de 0 Hz, ce qui revient à stopper la note lorsque le capteur lit du blanc. Le second capteur lit, lui, une des 5 nuances de gris. Les rectangles gris, blanc ou noirs sont centrés sur le début de la note de manière à éviter que le robot doive faire une moyenne et que le problème revienne. Les notes prennent donc cette forme : Sur ce schéma, le capteur 1 lit la durée des notes et le capteur 2 la hauteur
2.2.5 Deuxième méthode : code
La fonction ‘change’ de la première méthode n’a pas beaucoup changé. Elle s’appelle maintenant ‘read_note’ et la condition pour que le robot effectue la sous-routine est similaire :
sub read_note if blind == 0 then if prox.ground.delta[0] < 400 then if prox.ground.delta[1] < 300 then note = 0 callsub play_sound elseif prox.ground.delta[1] < 450 then note = 1 callsub play_sound elseif prox.ground.delta[1] < 600 then note = 2 callsub play_sound elseif prox.ground.delta[1] < 950 then note = 3 callsub play_sound elseif prox.ground.delta[1] > 950 then note = 4 callsub play_sound end else callsub sound_mute end end onevent prox if abs(current - prox.ground.delta[0])> DELTANB then callsub read_note current = prox.ground.delta[0]
‘DELTANB’ correspond à la constante ‘EPSILON’ de la première version, à l’exception qu’elle est beaucoup plus grand car elle représente la différence entre du noir et du blanc (‘DELTANB’ vaut 650 au lieu de 100). De plus on retrouve la variable ‘current’ qui a le même rôle mais qui, cette fois, ne « copie » que la valeur du premier capteur.
Le robot vérifie la note et la joue grâce à la sous-routine ‘play_sound’ (la nouvelle sous-routine ‘son’) lorsque le premier capteur renvoie du noir, sinon il arrête le son grâce à la sous-routine ‘sound_mute’ :
var gamme[]= [523,587,659,698,784] sub play_sound call sound.freq(gamme[note]*octave,360) sub sound_mute call sound.freq(0,1)
La sous-routine sound_mute permet de jouer un son de 0Hz très rapidement et ainsi arrêter la note qui est en train d’être jouée lorsque le premier capteur renvoie du blanc. J’ai rajouté une variable ‘octave’ pour permettre au robot de jouer des sons sur deux octaves. Les octaves sont contrôlés par les boutons droite et gauche du Thymio :
onevent button.left octave = 2 onevent button.right octave = 1
La variable ‘blind’ permet d’empêcher le robot de lire et jouer des sons lorsque ses moteurs sont arrêtés. En effet, j’ai ajouté une fonctionnalité au bouton central pour permettre de faire avancer ou de stopper le robot :
var marche_arret = 0 var vitesse = 60 onevent button.center marche_arret+=1 if marche_arret % 4 == 2 then blind = 0 motor.left.target = vitesse motor.right.target = vitesse else callsub sound_mute blind = 1 motor.left.target = 0 motor.right.target = 0 end
Il faut savoir que les événements ‘buttons’ sont déclenchés lorsque l’on appuie sur un bouton mais aussi lorsqu’on le relâche. Ainsi, la variable ‘marche_arret’ augmente de 2 à chaque fois que l’on appuie sur le bouton, d’où la présence d’un modulo 4 (et non 2) qui permet de distinguer si l’on a appuyé sur le bouton un nombre pair de fois ou non : pair équivaut à arrêter le robot et impair à le mettre en marche.
Pour mettre le Thymio en marche, si l’on a appuyé un nombre impair de fois sur le bouton central, on désactive le mode ‘blind’ et on active les moteurs pour le faire avancer. Autrement, on active le mode ‘blind’ pour empêcher au robot de lire des notes et on arrête les moteurs. La vitesse du robot n'est plus constante. C'es pourquoi j'utilise une variable 'vitesse' au lieu d'un simple nombre.
Cette variable peut être modifiée avec les boutons avant et arrière :
onevent button.forward if vitesse < 190 then vitesse += 10 if marche_arret % 4 == 2 then motor.left.target = vitesse motor.right.target = vitesse end end onevent button.backward if vitesse > 10 then vitesse -= 10 if marche_arret % 4 == 2 then motor.left.target = vitesse motor.right.target = vitesse end end
Ici, on ajoute ou on enlève 20 (2 x 10) à la vitesse (qui vaut initialement 60) car, pour rappel, les événements liés aux boutons se déclenche lorsque l’on appuie mais aussi lorsque l’on relâche. La vitesse est comprise entre 10 et 190 pour éviter que le robot n’aille en arrière ou qu’il n’aille trop vite pour lire les notes. De plus, la vitesse du Thymio n’augmente que s’il avance déjà, afin de permettre de régler la vitesse du robot lorsqu’il est immobile sans qu’il se mette à avancer.
3. Résultats
Avec la première méthode, les résultats laissent à désirer... En effet le Thymio joue beaucoup de notes qu'il ne devrait pas jouer et il n'atteint pas toujours la bonne note. Voici la vidéo d'un test effectué avec la première version du code pendant lequel le robot devait jouer tous les demi-tons d'une gamme dans l'ordre croissant (entre C4 et C5 ):
Cependant, la deuxième version du code semble fonctionner! Même si l'on entend beaucoup le bruit des moteurs, toutes les notes ont la bonne tonalité et la bonne longueur. Voici un test du deuxième code sur une partition reprenant le thème principal du jeu Tetris :
La variable gamme permet de choisir les 5 notes que le robot a la capacité de jouer. Ici, la variable gamme contient les fréquences en Hz des notes A4, B4, C5, D5 et E5 :
var gamme[]= [880,988,1047,1175,1319]
Et la partition prend cette forme :
Tous les ajouts semblent fonctionner ; le bouton central pour mettre en marche et stopper le robot, les boutons avant et arrière pour faire varier la vitesse et les boutons droite et gauche pour changer l'octave.
Cependant, une des améliorations qu'il faudrait ajouter serait un dispositif afin que le Thymio avance parfaitement droit sur la partition et qu'on ne doive pas le rediriger légèrement avec la main pour qu'il continue à lire correctement la partition.
4. Discussion
Bien que mon objectif initial semble fonctionner, je pense qu'il serait nécessaire d'apporter certaines améliorations à mon projet. Premièrement, l'ajout d'un dispositif (code ou matériel) afin de rectifier la trajectoire du Thymio serait utile pour un meilleur fonctionnement. Bien que cet ajout ne soit pas nécessaire, il empêcherait beaucoup de petites erreurs dues à l'alignement du robot.
Une autre limite qui mérite d'être soulevée est la petite portée du robot en terme de notes. Sur ce point, la première méthode surpassait la première : le Thymio avait accès à 16 notes au lieu de 5 sur la nouvelle version. Une piste qu'il faudrait explorer serait de faire lire au deuxième capteur plusieurs cases qui composeraient une sorte de code barre. Le Thymio reconnaitrait alors la combinaisons comme une note précise et le robot augmenterait de façon conséquente sa portée. Une autre solution à ce problème serait de lancer plusieurs robots simultanément de sorte à ce qu'ils se complètent et joue une partition à plusieurs.
Je ne me suis pas non plus focalisé sur la polyphonie (plusieurs Thymio qui jouent en même temps) pour une raison précise : ma mauvaise gestion du temps. Je pense cependant qu'il serait possible "d'accorder" plusieurs robots ensemble sans apporter d'ajouts au code, simplement en les activant à l'aide du bouton central simultanément et à la même vitesse sur des partitions différentes. La limite de la portée et de la polyphonie pourraient donc trouver une solution. Pour favoriser une bonne synchronisation entre les robots, il serait aussi imaginable de donner l'impulsion de départ à l'aide d'une télécommande ; en effet les robots Thymio sont munis de capteurs infrarouges, ce qui leur donne la capacité de recevoir des données d'une télécommande.
Finalement j'avais espéré avoir le temps de créer plusieurs partitions, ce que je n'ai pas eu le temps de faire à cause de ma mauvaise gestion du temps.
5. Conclusion
Pour conclure, je suis globalement satisfait des résultats obtenus. Bien que le projet ne soit pas parfait et que plusieurs améliorations puissent être apportées (à savoir la mise en place d'un dispositif pour assurer un déplacement linéaire, la portée de notes, la polyphonie et la création de partitions), j'ai atteint la majorité des objectifs que je m'étais fixés : Le Thymio est capable de lire une partition simple sans difficultés. Ce projet m'a permis de mieux comprendre le fonctionnement des robots Thymio et de me familiariser avec le langage ASEBA et la programmation en général. Il m'a aussi permis de constater qu'un travail plus régulier sera nécessaire pour les projets futurs. Les mises à jour régulières sur le serveur GIT m'ont aussi permis de mettre en pratique certaines connaissances du langage bash et la rédaction de ce rapport a servi de très brève introduction au langage Wiki.
Références
Site officiel du robot Thymio : https://www.thymio.org (dernière consultation le 04.02.19 à 19h33)