Overdrive Arena : faire courir des Anki Overdrive depuis un PC après la disparition de l'appli

L’écran de bataille d’Overdrive Arena : une piste ovale à gauche avec une voiture rouge et une mine armée, et à droite trois cartes de voitures nommées X52, Mammoth et Dynamo avec barres de vie, vitesse, voie, temps au tour, tension de batterie et un fil des éliminations
Une bataille en cours. La piste scannée à gauche, une carte par voiture à droite avec vie, vitesse, voie, temps au tour et état de la batterie. La X52 a une cellule fatiguée, Mammoth est hors piste et Dynamo est posée sur son chargeur.

Anki Overdrive était un jouet malin. Les voitures sont de petits robots : une caméra sous le châssis lit des codes optiques imprimés sur la piste, et chaque voiture sait donc sur quelle pièce elle se trouve, à quelle avancée et dans quelle voie. Une application mobile leur parlait en Bluetooth LE, envoyait des ordres de vitesse et de voie, et posait un jeu vidéo par-dessus, avec des armes, des adversaires IA et des modes de course. Les pièces de piste s’emboîtent, on construit donc son propre circuit.

Anki a fermé en 2019, et l’application officielle n’est plus disponible. Les voitures, elles, vont très bien. Elles chargent, elles roulent, elles lisent toujours la piste. Tout ce qui en faisait un jeu vivait dans l’appli, et l’appli a disparu.

C’est la même histoire que celle de mon aspirateur Neato : le matériel survit à l’entreprise, et l’entreprise emporte le cerveau en partant. J’ai donc fait la même chose. Overdrive Arena est une application Python qui parle directement aux voitures, depuis un PC Windows, avec des manettes Xbox ou PS5 ou le clavier. C’est open source sous licence MIT, et les notes sur le protocole sont dans le README pour qui voudrait construire autre chose par-dessus.

Ce qu’elle fait

En résumé : on branche des manettes, on allume les voitures, et on joue.

  • Un périphérique par voiture. Toute manette reconnue par SDL fonctionne (Xbox, DualSense, Switch Pro), plus le clavier. On peut brancher ou débrancher des manettes pendant que l’application tourne.
  • Scan de la piste. Les voitures font un tour et l’application reconstruit le circuit à partir des codes lus : identifiants de pièces, sens des virages, et vérification que la boucle se referme. Le tracé est sauvegardé dans track.json, on ne scanne donc qu’une fois par circuit.
  • Une carte en direct. Les voitures sont dessinées sur la piste scannée avec leur cap, leur voie, des traînées et des effets, à partir des rapports de position envoyés par les voitures et d’une estimation à l’aveugle entre deux rapports.
  • Une grille de départ. Chaque voiture se rend seule sur la ligne de départ dans la voie qui lui est attribuée et s’arrête sur le marqueur de la ligne d’arrivée. Puis un décompte 3-2-1-GO libère tout le monde en même temps.
  • Deux modes de jeu. BATTLE a des blasters, des mines, des points de vie et des éliminations. RACE, c’est le premier à N tours.
  • Des pilotes IA pour les voitures que personne n’a prises.
  • Suivi de batterie, parce que ces cellules sont vieilles, avec tension, pourcentage estimé, chute sous charge et un journal CSV.
  • Reconnexion quand une voiture redémarre, ce qui arrive plus souvent qu’on le voudrait avec des batteries fatiguées.

L’écran d’appairage : trois cartes pour Dynamo, X52 et Mammoth avec l’état de batterie et un emplacement de pilote IA chacune, un badge de curseur clavier au-dessus de Dynamo, et des boutons RACE, SCAN TRACK, BATTLE et un nombre de tours
L’écran d’appairage. Chaque manette a un curseur. On le déplace sur une voiture et on appuie sur A pour la piloter ; les voitures que personne ne prend sont pilotées par l’IA ou garées. La petite carte dans le coin est la piste scannée plus tôt.

Parler aux voitures

Les voitures exposent un seul service BLE avec une caractéristique d’écriture et une caractéristique de notification. Les messages sont [len][id][payload], et les définitions de messages d’origine viennent du drive-sdk qu’Anki avait publié à l’époque où l’entreprise existait. Ce que le SDK ne donne pas, c’est le comportement autour de ces messages, et c’est là qu’est allé l’essentiel du travail. Quelques choses que j’ai dû apprendre sur du vrai matériel, vérifiées sur le firmware 11866 avec les voitures X52, Mammoth et Dynamo :

  • Deux messages à la connexion, pas un. Il faut envoyer la commande de mode SDK et aussi dire à la voiture que son décalage par rapport au centre de la route est zéro. Sans le second, la voiture rapporte un décalage de 654321, qui veut dire « indéfini », et chaque changement de voie demandé est ignoré en silence.
  • Les voitures ne corrigent pas leur propre estimation de voie. Une voiture croit le dernier décalage latéral qu’on lui a donné, même quand les codes qu’elle lit disent autre chose. Avant tout changement de voie, l’application lit donc la vraie voie dans l’identifiant de position et la renvoie à la voiture. Sinon, « une voie à gauche » depuis une croyance fausse vous envoie au mauvais endroit, ou hors de la piste.
  • Le sens du virage vient des roues. Le message de transition envoyé quand une voiture quitte une pièce inclut la distance parcourue par chaque roue sur cette pièce. Roue gauche plus loin que la droite : virage à droite. C’est ainsi que le scanner sait dans quel sens tourne chaque virage.
  • Les codes ne se lisent plus sous environ 250 mm/s. Les barres entre les pièces se détectent encore au pas, mais pas les codes de position. Cela compte pour la grille de départ, où les voitures doivent s’approcher lentement de la ligne tout en sachant où elles sont.
  • Le message d’état indique si la voiture est sur la piste, sur son chargeur, en batterie faible ou pleine, et le premier octet du nom annoncé porte aussi le drapeau « sur le chargeur ». Pratique pour exclure une voiture posée sur son socle sans que personne n’ait à le dire.

La table qui associe chaque code à une voie et à un cran d’avancement vient d’Anki-Partydrive, un autre projet communautaire qui maintient ces voitures en vie, de même que la structure du message d’état. Le reste des agencements d’octets a été confirmé en enregistrant des tours et en regardant ce qui revenait.

Un piège propre à Windows qui mérite d’être noté : bleak casse quand il tourne sur le thread principal à côté de pygame, à cause de l’appartement COM que pygame met en place. Tout le BLE tourne donc sur son propre thread avec un worker entre les deux.

Scanner la piste

La piste d’Anki est une grille de 560 mm. Une ligne droite fait 560 mm, et le rayon de la ligne médiane d’un virage ressort à environ 280 mm d’après les distances de roues, si bien que quatre virages font un cercle exactement large d’une ligne droite. Chaque pièce a un identifiant (droites, virages, les deux moitiés de la pièce de départ avec la barre d’arrivée entre elles, intersections, tremplins et réceptions), et ces identifiants se répètent dans un tracé, on ne peut donc pas identifier une pièce par son seul identifiant.

Le scan se passe ainsi. Chaque voiture connectée fait un tour. L’application enregistre la séquence des pièces franchies par chaque voiture, avec le sens du virage tiré des roues, et la première voiture dont la séquence se referme gagne : son tour devient la carte. Une voiture qui ne lit rien pendant huit secondes est marquée comme hors piste. Le résultat est une liste de pièces avec leurs orientations que le moteur de rendu place sur la grille, et il est sauvegardé pour ne pas avoir à recommencer jusqu’au prochain circuit.

L’écran de scan : la carte de la piste avec Mammoth à mi-chemin dans le premier virage et X52 en attente sur une ligne droite, avec des cartes indiquant Dynamo ignorée sur son chargeur, X52 en attente de la pièce de départ et Mammoth en train d’enregistrer un tour de 3 pièces
Le scan. Mammoth a enregistré trois pièces jusqu’ici (pièce de départ, seconde moitié du départ, un virage à droite). X52 attend de franchir la pièce de départ pour commencer à enregistrer. Dynamo est sur son chargeur et ignorée.

Une fois la carte en place, chaque voiture a son propre localisateur : les rapports de position la recalent sur une voie et un cran d’une pièce connue, et entre deux rapports l’application extrapole à partir de la dernière vitesse connue. C’est ce qui rend les traînées sur la carte fluides plutôt qu’une suite de sauts.

La grille de départ

Chaque voiture participante se rend seule sur la ligne de départ dans sa voie. Si elle est dans le mauvais sens, elle fait d’abord demi-tour. Sur la dernière demi-pièce elle ralentit à 200 mm/s, juste au-dessus de la vitesse où les codes cessent de se lire, et s’arrête sur le marqueur de la ligne d’arrivée.

Si une voiture arrive dans la mauvaise voie, elle refait un tour et réessaie. Une voiture qui ne lit aucun code pendant douze secondes, ou qui est posée sur son chargeur, est laissée de côté. Quand tout le monde est sur la ligne, le décompte démarre et toutes les voitures partent au même instant.

L’écran de formation de la grille : une grande bannière « TO THE START LINE 1/3 » par-dessus la piste, avec Mammoth sur la ligne, X52 en train de se placer derrière et Dynamo exclue parce qu’elle est sur son chargeur
Formation de la grille. Mammoth est déjà sur la ligne, X52 se place à 200 mm/s, et Dynamo est sur son chargeur, exclue de cette course.

Bataille et course

En mode BATTLE, chaque voiture a 100 PV. Le blaster tire sur la voiture la plus proche devant vous à moins de 1,4 mètre le long de la piste, dans n’importe quelle voie, pour 20 dégâts et un ralentissement à 45 % de la vitesse pendant une seconde et demie, avec une seconde de recharge. Les mines se posent dans votre propre voie, s’arment après une seconde et vivent 45 secondes ; une voiture qui passe à moins de 12 cm dans cette voie prend 35 dégâts et s’arrête net pendant 1,2 seconde. On peut avoir deux mines posées à la fois. À 0 PV, la voiture s’arrête trois secondes avec ses feux qui clignotent, puis réapparaît, et l’élimination monte au tableau. Les impacts font clignoter les feux de la voiture et vibrer la manette.

En mode RACE, les armes sont coupées. Chaque carte affiche LAP n / cible avec une barre de progression, le classement se fait au nombre de tours puis à la position sur la piste, et la première voiture à atteindre la cible reçoit une bannière WINS pendant que les autres roulent jusqu’à l’arrêt. Les tours peuvent être 3, 5, 10 ou 20.

L’écran de fin de course : une grande bannière « X52 WINS » par-dessus la piste, avec X52 au tour 3 sur 3, Mammoth au tour 2 et Dynamo garée sur son chargeur, plus un journal des temps au tour
Fin d’une course en trois tours. X52 gagne avec un meilleur tour de 5,27 s. Mammoth en était au tour 2 quand la course s’est terminée. Dynamo a passé la course sur son chargeur.

Les voitures que personne n’a prises sont pilotées par l’IA, qui les fait tourner dans le bon sens à une vitesse de croisière réglable. Une seule personne avec une seule manette a donc quand même une course.

Vieilles batteries

Ces voitures ont des années, et leurs batteries LiPo à une cellule le montrent. L’application interroge la tension de chaque voiture toutes les quatre secondes et l’affiche sur la carte avec un pourcentage estimé d’après une courbe de tension au repos. Le chiffre le plus utile est la chute (sag) : la différence entre la tension au repos et la tension sous charge. Une chute au-dessus de 0,20 V signale une cellule fatiguée, et la carte le dit avec une étiquette WEAK CELL. C’est la voiture la plus susceptible de redémarrer quand on accélère fort, et c’est pourquoi l’accélération par défaut est volontairement prudente. Sur le chargeur, la carte passe de CHARGING à CHARGED. Tout est journalisé dans battery_log.csv, on peut donc suivre le déclin d’une cellule sur quelques sessions.

Quand une voiture redémarre vraiment, la carte affiche LINK LOST et l’application s’y reconnecte automatiquement à son retour.

Notes pratiques

Deux choses à savoir avant d’essayer.

Contention Bluetooth. Les voitures et toute manette sans fil partagent l’unique adaptateur Bluetooth du PC. Deux manettes Xbox en Bluetooth en plus des connexions aux voitures peuvent rendre les liaisons avec les voitures moins fiables. Branchez les manettes en USB ou passez par un dongle sans fil Xbox, ou donnez aux voitures leur propre adaptateur BLE.

Les changements de voie sont indicatifs. La voiture ne connaît que le décalage latéral qu’on lui a donné, comme décrit plus haut. L’application resynchronise le décalage depuis les codes avant chaque ordre de voie et réessaie les changements qui n’ont pas eu lieu, mais une voiture sous 250 mm/s ne peut pas lire les codes, donc les ordres de voie au pas sont peu fiables par nature.

La lancer

Windows 10 ou 11 avec un adaptateur Bluetooth LE et Python 3.11 ou plus récent :

git clone https://github.com/Leicas/overdrive-arena.git
cd overdrive-arena
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python app.py

Allumez les voitures avant de lancer. Chaque voiture qui s’annonce se connecte automatiquement, et on peut restreindre cela avec --max-cars ou par adresse. Le dépôt contient aussi les petits outils en ligne de commande que j’ai utilisés pour comprendre le protocole (tests de scan, de conduite, de voie et de feux, journalisation de tours) et un tour enregistré avec un script de relecture hors ligne, ce qui permet de travailler sur le rendu sans trois voitures par terre.

Liens et crédits

  • Code source : Leicas/overdrive-arena, sous licence MIT. Le README contient toutes les notes de protocole : structure des messages, identifiants de pièces, format d’annonce.
  • anki/drive-sdk, le SDK d’origine d’Anki, pour les définitions de messages.
  • MasterAirscrachDev/Anki-Partydrive pour la table des identifiants de position et la structure du message d’état.
  • Et les gens qui ont continué à documenter ces voitures après que l’entreprise a cessé.

Anki et Overdrive sont des marques de leurs propriétaires ; ceci est un projet de loisir sans affiliation. Si vous avez une boîte de ces voitures quelque part et un PC Windows avec du Bluetooth, elles courent encore.

Antoine Weill--Duflos
Antoine Weill--Duflos
Responsable Technologie et Applications

Je m’intéresse à l’haptique, la mécatronique, la micro-robotique…