Un retour sur la création d'une animation avec les cartes électroniques Micro:bit dans laquelle les participants recréent le comportement des lucioles.

Cet article détaille le processus de création de l'animation. Pour accéder directement au déroulé de l'animation et aux différents supports qui vont avec, rendez-vous sur le Wiki.

Émergence

Il y a quelques temps déjà, nous avions évoqué le principe de fonctionnement des lucioles, lorsque nous parlions de l'émergence. L'idée était de montrer des exemples de systèmes complexes dont l'organisation "émerge" d'une simple poignée de règles simples. Nous montrions des exemples comme la Fourmi de Langton ou le Jeu de la Vie, ainsi que des comportements animaux, comme les nuées d'oiseaux, les troupeaux de moutons, ou encore, le clignotement des lucioles. En effet, ces insectes ont la particularité de clignoter à l'unison, et ce, à partir d'une règle simple pour chaque individu : lorsqu'une luciole voisine clignote, réduire son temps d'attente avant le prochain clignotement.

Si ce sujet vous intéresse, nous vous invitons à consulter la page de Fil d'étincelles, une émission où nous discutions de sujets scientifiques et informatiques. Notamment, les épisodes suivants :

Par ailleurs, depuis plus d'un an, la médiathèque est dotée d'une malle Micro:bit fournie par la fondation CGénial en partenariat avec astu'sciences. Nous avions déjà publié quelques déroulés d'animations exploitant cette malle, comme la création d'un robot télécommandé ou la mise en place d'une chasse au trésor. Chaque année, une quinzaine est organisée pour valoriser les activités mises en place avec ces malles, cette année sur la thématique de "l'art de coder ou coder de l'art".

Une idée est alors venue : utiliser les cartes Micro:bit pour imiter le comportement des lucioles qui clignotent à l'unisson, et réaliser une petite vidéo les mettant en scène.

Un tutoriel existe déjà pour simuler le comportement des lucioles avec ces cartes, disponible sur le site Micro:bit. Malheureusement, ce tutoriel implémente un mécanisme arbitraire qui n'a pas la magie des lucioles, certes relavitement simple à exprimer en blocs mais tout de même hors de portée pour des débutants, surtout dans le cadre d'une animation d'une heure et demie.

Ainsi, nous avons tenté d'élaborer notre propre animation, dont le déroulé est disponible sur ce site. Cet article présente notre démarche, dans un but de transparence et de partage de connaissances et de pratiques !

Modélisation

Pour modéliser les lucioles, nous nous inspirons d'un modèle décrit dans un article de John Buck, chercheur de l'université John Hopkins à Baltimore, Synchronous Rhythmic Flashing of Fireflies. II., publié en 1988.

Dans cet article, Buck explique que les lucioles clignotent naturellement de façon régulière, à la manière d'une batterie qui se charge continuellement et se décharge d'un coup lorsqu'elle est pleine. Comme Buck le soulignait dans un article précédent, de tels phénomènes biologiques cycliques sont déjà connus, comme le rythme cardiaque, la contraction de certains muscles ou la respiration. Pour faire simple, chaque luciole possède une horloge interne et clignote lorsqu'elle indique midi. Différentes espèces peuvent avoir des séquences de clignotement différentes, permettant de les identifier. En guise d'exemple, on trouve un tableau présentant les motifs de seize espèces de lucioles nord-américaines dans un article de Herbert Spencer Barber, North American fireflies of the Genus Photuris, publié en 1951.

Tableau de 16 lignes, une ligne par espèce, avec un dessin des motifs de clignotement de chaque espèce
Tableau des séquences de clignotement des lucioles males de l'espèce Photuris (Barber, 1951)

Buck rapporte ensuite deux modèles pour expliquer la synchronisation des lucioles : le modèle d'avance de phase et le modèle de retard de phase. Dans le premier, lorsqu'une luciole voit une voisine clignoter alors qu'elle-même s'apprête à le faire, elle n'attend pas et clignote directement (d'où l'avance de phase), et remet son horloge à zéro. Dans le second, lorsque la luciole voit une voisine clignoter, elle ne clignote pas mais remet son horloge à zéro ; ainsi, elle attend le prochain cycle pour clignoter en même temps que sa voisine, d'où le retard de phase.

Diagramme temporel tiré de l'article de Buck de 1988
Diagrammes présentant le modèle d'avancement de phase (Buck, 1988). En bas, la ligne MODEL correspond aux cycles internes de la luciole. Au centre, la ligne SIG correspond aux stimulations des lucioles environnantes. En haut, la ligne FIREFLY correspond aux clignotements réels de la luciole, qui marquent un petit délais avec le cycle interne.

Pour notre animation, le premier modèle est plus intéressant car plus visuel : il implique plus de clignotements. Malheureusement, à petite échelle, il est rare d'obtenir une synchronisation parfaite. Si la fenêtre de temps durant laquelle la luciole se synchronise avec sa voisine est trop courte, seules des lucioles déjà proches pourront clignoter ensembles. Si elle est trop longue - ce qui ne correspond pas trop au principe biologique de charge et décharge - la synchronisation sera instantanée.

Ainsi, nous allons nous autoriser une petite modification : au lieu d'avancer l'horloge brutalement et seulement lorsque le clignotement approche, nous allons avancer l'horloge légèrement mais sans condition. La valeur du décalage va varier selon le temps restant avant le clignotement. S'il reste toute l'horloge, on ne l'avancera qu'un peu. S'il reste une moitié de tour, on avancera un peu plus. S'il ne reste qu'un quart de tour, on avance beaucoup, jusqu'au clignotement. Il s'agit du modèle implémenté dans la simulation de Nicky Case, qui nous sert de démonstration pour l'introduction de l'animation.

Capture d'écran de la simulation de Nicky Case montrant les lucioles se synchroniser petit à petit jusqu'à atteindre la synchronisation parfaite.

Pour comprendre pourquoi faut-il un décalage non constant, considérons deux lucioles clignotant avec une période T et décalées de T/2. L'idée, c'est que lorsque la première clignote, la seconde avance son horloge de Δt, ramenant le décalage entre les deux à T/2 - Δt. Mais lorsque la seconde luciole clignote, la première luciole avance elle aussi son horloge de Δt et le décalage revient à T/2. Ce problème n'a pas lieu avec un décalage Δt variable. Le premier décalage est important car il reste un demi tour à la luciole, mais le second est beaucoup plus court car la luciole vient juste de clignoter. Ainsi, petit à petit, les lucioles parviendront à se synchroniser.

Simulation du déplacement des lucioles sur le plateau. À gauche, le décalage est constant, d'une seule case. La synchronisation n'est pas atteinte. À droite, le décalage est variable, selon les règles décrites ci-dessous. La synchronisation est atteinte.

Bien entendu, tout ceci est assez abstrait, et cela peut être assez difficile à concevoir, notamment pour les participants de l'animation. Pour expliquer ces principes, nous pouvons nous appuyer sur une idée de l'article Fireflies: A paradigm in synchronization (Ramírez-Ávila et al.,2018)., qui est d'introduire un plateau circulaire sur lequel chaque luciole est représentée par un pion, qui se déplace de case en case à chaque avancée de l'horloge. L'article expose différents modèles et variantes. Pour simplifier, nous avons reproduit ce plateau circulaire en fixant les règles : la case du midi est la case du clignotement, et lorsqu'une luciole clignote, on bouge les autres de la façon suivante :

  • case clignotement : ne pas bouger,
  • cases 1 à 5 : avancer d’une case,
  • cases 6 à 10 : avancer de deux cases,
  • cases 11 à 15 : avancer de trois cases,
  • cases 16 à 19 : avancer de quatre cases, sans dépasser la case de clignotement (ce qui revient à aller directement sur la case clignotement).

Plateau circulaire avec 20 cases. La case la plus haute est la case de clignotement. Le reste du plateau est divisé en quatre zones.
Plateau de simulation de l'horloge des lucioles. La case 0 (ou midi) est la case de clignotement. Les zones de couleur marquent les règles à suivre lors d'un clignotement : avancer d'une case pour la zone bleu, deux pour la zone jaune, trois pour l'orange et quatre pour la rouge.

Il s'agit d'une version simplifiée du modèle d'avance de phase modifié que nous avons décrit plus tôt. Normalement la synchronisation survient rapidemment. Il faut tout de même faire attention à ne pas trop se perdre avec les différents pions, et à ne pas oublier de comptabiliser chaque clignotement : lorsqu'une luciole arrive sur la case clignotement du fait d'une autre luciole, il faut à nouveau déplacer toutes les autres selon les règles citées plus haut. Puisqu'une luciole déjà sur la case clignotement ne peut être artificiellement avancée, toutes finiront pas s'y retrouver, et à se déplacer ensemble.

Implémentation

Le modèle étant en place, reste à l'implémenter sur les cartes Micro:bit. Plus précisément, il faut :

  1. permettre aux lucioles de clignoter, de façon régulière,
  2. être tenu au courant du clignotement d'une luciole voisine,
  3. ajuster l'horloge d'une luciole suite au clignotement d'une voisine.

Le point (1) est presque à portée des débutants, car il ne requiert que des fonctions d'affichage et d'attente, mais doit tout de même être lié à une horloge interne dont l'écoulement peut être bousculé. Le point (2) est très simple grâce aux cartes Micro:bit : ces dernières étant dotées d'antennes radio, il est possible de communiquer et de diffuser un message lorsqu'une luciole clignote. La portée de l'antenne définit en quelques sortes le champ de vision de la luciole. Enfin, le point (3) demande d'appliquer une formule de proportion, ce qui peut être difficile voire hors de portée pour un jeune public.

Ainsi, les points (1) et (3) demandent une implication soutenue des participants. Heureusement, l'éditeur MakeCode permet de créer ses propres blocs, ce qui va nous permettre de dissimuler certains comportements complexes en ne laissant que la logique générale aux participants. Brièvement, il faut créer un nouveau projet Micro:bit, utiliser l'éditeur en Javascript, et éditer le fichier custom.ts trouvable dans l'explorateur de fichiers, situé en bas à gauche de l'espace de travail. Le code est rédigé en TypeScript, une version un peu plus stricte de JavaScript nécessitant de déclarer les types des variables manipulées. Chaque module personnalisé supplémentaire fait l'objet d'un namespace dédié. Dedans, on peut exposer publiquement une ou plusieurs fonctions. Pour en faire des blocs, il faut utiliser certains mots clés en commentaire, juste avant de déclarer la fonction. Voici un exemple très minimal de ce que nous pourrions écrire comme module personnalisé :

namespace luciole {

    //% block="clignoter pendant $duree millisecondes"
    //% duree.min=0 duree.max=10000 duree.defl=1000
    export function clignoterPendant(duree: number) {
        basic.showIcon(IconNames.Heart);
        basic.pause(duree);
        basic.clearScreen();
    }

}

Ce module ne propose qu'un seul bloc, "clignoter pendant X millisecondes".

Notre module, que nous nommerons "luciole", fonctionne avec deux variables globales. La première enregistre la période de clignotement de la luciole, c'est-à-dire la durée entre chaque clignotement. La seconde est un horodatage du dernier clignotement. Avec cela, on peut déjà (presque) implémenter le point (1) : une boucle tourne en arrière-plan de la luciole, et dès qu'il s'est écoulé assez de temps depuis le dernier clignotement, on clignote à nouveau tout en enregistrant la nouvelle heure. Ce qui se passe durant le clignotement, ce sera à l'utilisateur, aux participants de le définir.

Capture d'écran de 11 blocs Scratch du module personnalisé Luciole
Les blocs de notre module personnalisé

Le point (2) est également assez simple à implémenter. Nous ajoutons un bloc d'initialisation de la luciole, qui initialise en réalité la communication radio. On règle ainsi toutes les lucioles sur le même canal, afin que toutes puissent se parler (même si on pourrait faire autrement, et faire coexister deux espèces par exemple). On règle également la puissance d'émission à 0, la plus faible valeur. Physiquement, cela correspond à une portée d'environ 1m50 (très variable selon l'environnement). Juste avant de déclencher le clignotement au point (1), une luciole émet un signal radio, signalant ainsi à ses voisines qu'elle clignote.

Test de la portée de communication des cartes Micro:bit. Ici, les deux cartes émettent un message toutes les secondes, avec une puissance de 0 (minimale). Lorsqu'elles reçoivent elles-mêmes un message, elles clignotent. Le mètre jaune scotché sur la table mesure 1m50. Au-delà de cette valeur, peu de messages semblent passer. En dessous d'un mètre, la communication n'échoue presque plus. À noter que ces tests sont très variables. En conditions réelles, les cartes étaient capables de se synchroniser à plus de quatre ou cinq mètres de distance, petit à petit.

Ce signal radio est capté par ses voisines les plus proches, qui peuvent alors s'ajuster. Pour cela, les participants n'ont qu'à choisir le pourcentage d'avancement de l'horloge. Avec un petit calcul de proportion, on peut alors reculer l'horodatage du dernier clignotement, et ainsi réaliser le point (3) : la prochaine boucle de clignoter sera réalisée plus tôt. Concrètement, si t est l'instant présent, t0 l'instant du dernier clignotement et k le pourcentage d'avancée de l'horloge (valeur entre 0 et 1), la nouvelle valeur (artificielle) t1 de l'instant du dernier clignotement est :

t1 = t - (k + 1) * (t - t0)

Afin de pouvoir tester d'autres scénario, des fonctions d'avance de phase constante ou selon les modèles décrits par Buck ci-dessus sont également implémentées. Mais dans le temps limité de notre animation, nous ne les utiliserons probablement pas. Elles sont donc placées dans le menu "plus" de MakeCode.

Avec ces mécanismes, tous les points cruciaux de la luciole sont implémentés. Les participants devront donc initialiser la luciole, définir sa période, la faire clignoter lorsque son horloge interne fait un tour, et réagir au clignotement d'une luciole voisine. Cependant, il reste un détail à régler, plutôt cosmétique. En l'état, les participants doivent définir le clignotement de la luciole au sein du bloc "lorsque l'horloge interne sonne midi" à l'aide des blocs basiques "montrer l'icône", "pause" et "effacer l'écran". Malheureusement, cela ne permet pas de facilement reproduire la séquence de clignotement de certaines espèces, encore moins si au lieu d'utiliser la matrice de LED on s'essaye aux rubans de LED avec Neopixel.

Ainsi, nous avons également rajouté deux blocs "clignoter les LED" et "clignoter le ruban" (qui fonctionne après avoir utilisé le bloc "associer la luciole au ruban LED"). Ces blocs fournissent trois avantages majeurs :

  1. ils intègrent directement les fonctions d'affichage, d'attente et de nettoyage de l'écran, diminuant le nombre de blocs nécessaire à la programmation de la luciole,
  2. ils intègrent un effet de fondu (ou de vague sur le ruban de LED), en diminuant l'intensité des clignotements graduellement, pour reproduire la lueur d'une luciole,
  3. mais surtout, ils peuvent en se chevauchant.

Démonstration de l'effet de vague sur les rubans de LED. Un tel résultat serait assez difficle à obtenir en n'utilisant que les blocs de base.

En effet, une des difficultés principales dans l'utilisation des Micro:bit est qu'il est difficile d'effectuer certaines actions en parallèle. Hors, mettons que l'on souhaite démarrer un nouveau clignotement alors que le premier n'est pas totalement terminé : cela n'est pas faisable avec les blocs basiques. L'astuce, ici, est que ces blocs sont simplement déclaratifs : ils enregistrent dans une mémoire la liste des motifs de clignotements à reproduire. Ensuite, une fonction interne prend tous ces motifs, et calcule au fur et à mesure l'état dans lequel contrôler la matrice de LED ou le ruban. Ainsi, le choix (ou l'invention) d'une espèce de lucioles par les participants peut désormais tout à fait rentrer dans le cadre de notre animation !

Avec ces briques élémentaires, les participants devraient désormais pouvoir implémenter leurs propres lucioles rapidemment, sans nécessairement d'expérience en programmation. Afin de rendre ce code disponible au public, il faut le publier. MakeCode fournit pour cela un guide de création de sa propre extension. L'extension luciole est hébergée sur GitHub. Pour la charger dans un nouveau projet, il suffit d'ajouter une nouvelle extension, et de rechercher « ychalier-rlv/luciole » (ou de simplement copier-coller l'URL du dépôt dans la barre de recherche). Le nouveau menu, orange, s'ajoute alors à ceux de MakeCode.

Animation et réalisation

Comme mentionné au départ, ces éléments ont permis la rédaction d'un déroulé d'animation, disponible sur le Wiki.

Dans le cadre de la quinazine Yes We Code 2024, nous avons testé cette animation avec 7 participants entre 9 et 11 ans. Ils étaient très intéressés, ont rapidemment compris le fonctionnement des lucioles et du programme. Le plus compliqué fut, comme souvent dans les ateliers avec les cartes Micro:bit, de téléverser les fichiers HEX sur les cartes.

L'animation s'est terminée par la réalisation d'une petite vidéo mettant en scène les lucioles conçues par les participants. L'idée est de recréer, de façon mi-réaliste mi-artistique ce que produirait le clignotement de nos lucioles dans la nature. Pour mettre en scène cela, les participants sont placé en ligne dans une pièce sombre, afin qu'en baissant l'exposition de la caméra, la scène paraisse complètement noir, et seules les lueurs des lucioles apparaissent. Les participants se déplacent en tenant leur luciole face à la caméra, qui se synchronisent petit à petit. Pour rendre le tout plus facile à organiser, nous avons dissimulé une porte dérobée dans le code des lucioles, permettant à une télécommande d'activer ou désactiver à distance la synchronisation, pour éviter que toutes les lucioles ne se soient déjà synchronisées avant le démarrage de l'enregistrement.

Vidéo réalisée avec les participants de l'atelier du 21 février 2024
Même vidéo avec un effet de traînée appliqué, pour renforcer la visibilité des lucioles

Si vous aussi réalisez l'expérience, n'hésitez-pas à nous le partager !

Article précédent