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.

Schéma 1 : Concept de la première méthode

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 : Schéma 2 : problème de moyennes 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 : Schéma 3 : Illustration de la deuxième méthode 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 : Partition Tetris 1.tif Partition Tetris 2.tif Partition Tetris 3.tif Partition Tetris 4.tif

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)