Un robot analogique pour la coupe de France de robotique ?

L’équipe E.S.C.Ro.C.S. participe à la coupe de robotique chaque année depuis 2020. Nous avons présenté à cette compétition divers robots, avec plus ou moins de succès en fonction des années. Nous étions partie d’une base Arduino très simple en 2020-2021 puis avons progressivement évolué vers des architectures plus complexes à base d’ESP32 et de diverses cartes filles communiquant sur un bus.

Une discussion autour d’un café entre deux escrocs a fait naitre une idée un peu folle : et si on faisait un robot sans aucun ordinateur, sans micro-contrôleur, sans même la moindre électronique numérique, c’est possible ? On trouve de nombreux robots analogiques sur le net, mais ils sont en général limités à des fonctions très simples : suivie de ligne, de son, de lumière, etc. Peut-on faire un robot complexe, programmable, mais sans programmation d’ordinateur ?

Le retour de la cassette audio

Il semble assez évident qu’en l’absence de logique programmable, il sera très difficile de faire un robot capable de prendre des décisions à la volée et d’adapter sa trajectoire de manière dynamique. On choisit de se limiter à la conception d’un robot de type « automate » qui exécutera une succession linéaire d’actions de jeu. Bien que celà puisse sembler limitant, dans les faits, la majorité, et possiblement 80% des équipes de la coupe adoptent une stratégie similaire. Ça peut suffire même pour atteindre les huitièmes voire les quarts de finales.

Mais même un robot automate qui exécute de manière séquentielle une suite de mouvement nécessite une forme de mémoire pour stocker cette séquence. Tout comme l’orgue de barbarie utilise un carton perforé pour jouer les bonnes notes, il nous faut trouver un support. On le veut analogique, par principe. Il doit être lisible et réinscriptible facilement de manière électronique. Le support le plus évident est la bande magnétique : un magnétophone avec sa cassette audio semble parfaitement convenir à notre besoin. Nous avons donc acheté un magnifique Schneider 3605 sorti tout droit d’un « labo de langue » du siècle dernier.

Plutôt AM ou FM ?

Mais quoi stocker sur cette cassette, et comment ? Listons déjà l’ensemble des informations à enregistrer :

  • Notre robot est un robot différentiel à deux roues. Il a donc deux degrés de liberté (gauche/droite ou alors avancer/tourner, ce qui revient au même). Il y a donc 2 signaux analogiques à enregistrer sur la cassette.
  • Il doit être également capable d’actionner des moteurs, de lever des bras, de démarrer des pompes, etc. Le choix d’actionneurs à utiliser dépend du règlement de la coupe qui sera annoncé en septembre. Cependant, la plupart de ces actionneurs bougent entre deux états (haut/bas, ouvert/fermé, ventousé/lâché, etc.). On intuite donc qu’on aura besoin de stocker une information de type « tout ou rien » et d’avoir plusieurs canaux à disposition.

Notre magnétophone, comme la plupart des lecteurs/enregistreurs de cassettes de taille modeste est mono. On peut donc exclure la possibilité d’enregistrer deux ondes simultanément. Il faut donc attribuer des bandes de fréquences à ces signaux et les moduler. Notre choix se porte sur des modulations de fréquence : une modulation d’amplitude serait extrêmement sensible à des variations de gain de l’amplificateur, une modulation de phase serait facilement brouiller par l’effet « wow et flutter » inhérent aux lecteurs de cassettes. La modulation de fréquence est à la fois simple à mettre en œuvre et robuste. Le lecteur permet un rendu de la plage de fréquence de 100 Hz à 8 kHz environ. On attribue alors les fréquences suivantes :

  • Signal analogique de translation de 1kHz à 2kHz. La valeur 1.5kHz représentant le zéro, et les valeurs 1kHz et 2kHz représentent -5V et +5V.
  • Signal analogique de rotation de 4kHz à 8kHz.
  • Signaux tout-ou-rien à placer dans la bande 100Hz-1kHz.

Avant de se lancer tête baissée dans la réalisation de cartes électroniques, j’ai d’abord commencé par effectuer des essais sur ordinateur : un bout de code Python permet de générer un son, celui-ci est enregistré sur la cassette, celle-ci est rembobinée, relue et réenregistrée sur le PC. La suite GNURadio permet d’implémenter facilement une démodulation et vérifier que le signal est bien lisible. Les résultats initiaux semblent prometteurs et on arrive à reconstituer les signaux d’entrée.

Le plomb, matériau d’avenir

Le choix de la technologie de batterie est évident. Les batteries au plomb ont un facteur « cool » indéniable. En plus de ça, les batteries lithium, pour des raisons de sécurité nécessite d’utiliser un BMS pour suivre les niveaux de courant, les températures et les tensions de cellule. Les BMS du commerce sont tous basés sur des microcontrôleurs et réaliser un BMS analogique serait une « quête annexe » coûteuse. Le plomb, c’est très bien.

Les batteries au plomb ont une tension de 13.8V lorsqu’elles sont chargées et environ 12V lorsqu’elles ne sont plus bonnes à rien pour un robot. Dans un soucis de simplicité, tous les moteurs et actionneurs seront pris de sorte à pouvoir être connectés directement à ces niveaux de tension. Nous utilisons depuis des années des moteurs EMG30, motoréducteurs 12V avec encodeurs à effet Hall, et ils marchent très bien pour la coupe. L’électronique analogique a quant à elle besoin d’une tension régulée très propre. De plus, une alimentation à rails positifs et négatifs simplifieront la conception de circuits à amplificateurs opérationnels (mes composants préférés). Le choix se porte donc sur une alimentation +5V / -5V avec un rail 0V de référence de tension. Cela permet d’utiliser un régulateur linéaire depuis la tension de batterie jusqu’à 10V, puis de générer le rail médian. Pas d’alimentations à découpage, pas de bruit.

Beaucoup trop de cartes

L’achitecture électronique du robot est centrée autour du lecteur cassette. Celui-ci s’interface avec le robot via sa sortie audio « line out ». L’entrée « line in » pour l’enregistrement permettra d’enregistrer les stratégies sur la cassette. L’idée est la suivante : sur une table d’essai, une télécommande spécialement conçue est branché sur l’entrée audio du magnétophone. Nous pouvons ensuite piloter le robot avec le télécommande, à la façon d’un robot pour la coupe junior. L’ensemble des mouvements du robot sera alors enregistré sur la bande. En match, il n’y aura plus qu’à rejouer la séquence pour obtenir un robot autonome.

Le sortie audio est ensuite envoyée vers deux cartes de démodulation analogique, une pour le signal de translation, une pour la rotation. Le seul but de ces cartes est de générer des signaux analogiques de vitesse entre -5V et +5V (0V signifiant arrêté, une valeur positive avancer, une valeur négative reculer). Ces signaux sont donné à des cartes d’asservissement PID, qui, à partir du retour de vitesse des codeurs rotatifs, calculent la commande de rotation et de translation. Les commandes gauche et droite sont définies par addition et soustraction des commandes de rotation et de translation. Ça permet également de gérer le choix du côté où jouer, en intervertissant les canaux gauche et droite. La carte moteur génère ensuite un signaux PWM dont le rapport cyclique est proportionnel à la commande et qui commute l’alimentation des moteurs dans un pont en H. Les encodeurs à effet Hall génèrent un signal dont la fréquence est proportionnel à la vitesse de rotation et la carte moteur effectue cette conversion fréquence vers tension.

Les différents signaux tout-ou-rien pour les actionneurs permettent de commander les différents moteurs via d’éventuelles logiques à relayage si des comportement plus complexes sont nécessaire, par exemple pour couper le moteur si un fin de course est atteint.

Enfin, on n’oubliera pas qu’une condition d’homologation des robots est la faculté du robot à éviter les collisions avec l’adversaire. La détection peut se faire de manière classique avec un capteur analogique à infrarouge ou à ultrason. On évitera le beau lidar qui ferait tâche sur un tel robot. Le magnétophone est équipé d’un port fort sympathique : le contrôle à distance « rem » qui permet de forcer la pause de la cassette en fermant un circuit entre deux contacts d’un connecteur Jack. Ça permet de réaliser simplement l’évitement « du pauvre » (ou plutôt celui des 3/4 des équipes) : tout couper en cas d’obstacle et reprendre quand la voie est libre.

Conclusion

Tout ça n’est évidement que théorique, tant que le robot n’est pas construit et ne roule pas, on ne peut pas savoir si ce projet est réellement viable. On en saura plus dans les mois à venir.

Et on espère bien avoir à nouveau le stand « K7 » l’an prochain…

Developing a replacement for the FTS-8 CTCSS sub-tone encoder/decoder

I recently purchased a brand new older than me Yaesu FT-736 ham radio transceiver on eBay from a Japanese seller. It is a nice VHF/UHF rig, with true full duplex and “all mode”s (FM, SSB, CW, packet) capabilities and a nice “facade full of dials and buttons” ergonomics that I missed a bit coming from a computer-controlled SDR.

I plan to use it to setup a satellite base station at home. The trick is that nowadays most if not all FM satellites require CTCSS tones. This transceiver can do that with a little plug-in board that was sold by Yaesu as an option back in the day. Mine does not have it unfortunately.

Original parts are nowhere to be found (or sell for a significant fraction of the price I purchased the radio…) and Piexx sells a somewhat expensive 79$ replacement board that is a modern redesign. Considering the triviality of the function of this board, I looked for homebrew schematics. Homo ludens has already done so using a PIC micro-controller but not having a PIC programmer at home, and finding the design a bit flawed I decided to design my own.

Encoding CTCSS tones

I wanted to keep the design simple and use components I was already familiar with. I pick the ATmega328P micro-controller as it was cheap, powerful enough and I already had the programmer for it. Sub-tone decoding is done using the onboard ADC and encoding is done through PWM. The schematics of the board is the following:

Contrary to homo ludens’ design, the PWM does not output a square wave at the CTCSS frequency. It would have made the filtering of the tone harmonics tedious for two reasons. At first, square waves have strong third harmonics, requiring sharp low-pass filters of high orders to eliminate them. Furthermore, the highest CTCSS frequency, 254.1Hz is more than thrice the lowest CTCSS frequency: the proper elimination of the third harmonics would require filter banking, in a similar way as amplifiers work when working over several bands.

In my implementation, the output frequency is 201 times the CTCSS frequency, with a PWM modulation that samples a sine wave period over 201 points. The low-pass filter must simply be designed to let the highest CTCSS tone (254.1Hz) pass, while blocking the PWM frequency of the lowest tone (13.4kHz) which is much simpler to achieve. A simple second order active filter using a Sallen-Key topology does the trick.

Decoding CTCSS tones

I don’t really need decoding capabilities in my usage of my radio. However, for the sake of the challenge of performing a feature complete re-implementation of the FTS-8 board, I wanted my board to have that.

Decoding CTCSS consist on detecting the strength of a single frequency in the audio input and compare it to a detection threshold. This can be done by computing the Fourier transform at a single frequency value, the one of the CTCSS subtone. The Goertzel algorithm provides an elegant solution to this problem. By sampling the audio input x at a sampling rate of a multiple p of the tone frequency, and over a buffer length of N period (thus sampling N.p points), the Goertzel algorithm computes a sequence s such as:

s n = x n + 2. c o s ( 2 π p ) . s n 1 s n 2

We can then calculate the power term as:

P = s [ N . p ] 2 + s [ N p 1 ] 2 2 c o s ( 2 π p ) . s [ N . p ] . s [ N p 1 ]

The tricky thing to do on a 8-bit micro-controller with no floating-point arithmetic (and no machine instruction for division for that matter) is the multiplication by the constant cos(2π/p). Fixed point arithmetic is out of question because for p much larger than one – which is required to avoid potential aliasing caused by higher frequency tones, cos(2π/p) is fairly closed to 1 and the error would accumulate. The trick to speed up the calculation is to find a couple of integers p and Ω such as:

c o s ( 2 π p ) 1 1 2 Ω

Values of p=201 and Ω=11 are a surprisingly good approximation (the correct value of p is 201.054). This allows the computation to be done solely with integer addition, subtraction and bit shifting:

#define COEFF_MULT(x)       (((x) << 1) - ((x) >> (OMEGA_FACTOR - 1)))

volatile int32_t sprev, sprev2;
volatile uint16_t sample_no = 0;
ISR(ADC_vect) {
    int32_t s = (int32_t)ADC + COEFF_MULT(sprev) - sprev2;
    sprev2 = sprev;
    sprev = s;
    sample_no++;

    if (sample_no >= NUMBER_OF_PERIODS * P_FACTOR) {
        // Power term calculation...

        sprev = sprev2 = 0;
        sample_no = 0;
    }
}

Note that’s also the reason why the encoder output frequency is 201 times the CTCSS tone: both encoding and decoding use the same timer in the same configuration. The decoding is performed over 32 tone periods (478ms at the lowest tone and 126ms at the highest one), as a compromise between tone separation, signal/noise separation and detection speed.

End result

Here is the little board:

I first check the quality of the generated tone, to check that the generated frequency was correct and the waveform had a good sine shape. The result was quite satisfying, with no measurable harmonics.

Once properly installed on my Yaesu FT-736M, I checked the proper deviation of the encoder by checking the emission of the transmitter using a RTL-SDR dongle and Gqrx software. The following screenshot show the transmitter output with CTCSS encoding enabled (bottom waterfall) and without (top). The deviation is about 15-20% of the signal range, which is what is expected for a CTCSS tone.

I checked the tone squelch feature of two ways communications by using an handheld transceiver and checking that both and trigger the CTCSS tone detection of the other.

The code is uploaded on my Github page and is licensed under the GPL v3 license (opensource).

I had to build 5 of those (which was the minimum order quantity), so I have 4 of those for sale on eBay for anyone interested.

EDIT: I ordered some more since it appears that those board were of interest to several OM. They are for sales on eBay.

EDIT2: New eBay link : https://www.ebay.fr/itm/186906777747

References

F4VQG

Tutoriel: installer une machine virtuelle OpenBSD sur la Freebox Delta

Nouvellement abonné chez Free, j’ai commandé la dernière Freebox, la Delta. Celle-ci est appelée « serveur » et non « routeur » dans la documentation, et ce, à juste titre puisque Free ont mis le paquet pour permettre l’auto-hébergement. En effet, une des fonctionnalités de l’OS de la Freebox est d’exécuter des machines virtuelles KVM directement sur la box – évitant ainsi d’avoir à faire tourner H24 un PC à la maison. C’est d’ailleurs sur une VM dans ma Freebox que ce blog est hébergé.

Free propose un certain nombre de distributions Linux (Debian, Ubuntu, CentOS notamment) mais j’ai souhaité faire tourner mon serveur personnel sur OpenBSD, parce que c’est cool™. Il y a cependant quelques difficultés à surmonter pour que tout fonctionne convenablement.

Démarrage de l’installateur et installation de l’OS

Le gros de l’installation se fait via l’interface d’administration de la Freebox http://mafreebox.freebox.fr, accessible sur le réseau local. On peut alors explorer le menu « VMs » et commencer la création de la machine virtuelle.

La première difficulté est que la Freebox tourne sur architecture ARM64. Il faut donc se procurer l’installateur OpenBSD adéquat qui, allez savoir pourquoi, n’est pas disponible au format CD-ROM (ISO) mais seulement sous la forme d’image disque. On peut tout à fait contourner ce problème :

  • en choisissant une image ISO bidon dans l’interface de la Freebox, puis en la supprimant après coup dans les paramètres de la machine virtuelle ;
  • en téléchargeant l’installateur pour clé USB, et créant une clé USB amorçable qu’on pourra ensuite brancher sur un des ports USB de la Freebox (la machine virtuelle pourra démarrer depuis l’USB !).

La configuration finale ressemblera à ça sur la Freebox. Notez l’absence d’image CD-ROM et l’ajout du port USB dans le bloc « système ».

On peut alors créer un disque USB d’installation selon la procédure standard, puis l’insérer dans la Freebox :

$ wget https://cdn.openbsd.org/pub/OpenBSD/7.2/arm64/install72.img
# dd if=install72.img of=/dev/<disque_usb> bs=4M

On démarre la VM, et ça boote…

Contourner le crash au démarrage

…et puis ça plante au bout que quelques secondes. Le démarrage reste bloqué sur un message un peu cryptique.

virtio3: couldn't map interrupt

Une petite recherche sur les Internets montre que le problème semble être lié au générateur de nombres aléatoires de QEMU que OpenBSD ne semble pas bien gérer. Après un peu de bidouille, on se rend également compte qu’il y a des problèmes avec le pilote du memory balloon. La solution : mettre sur liste noire ces deux pilotes. On redémarre donc la machine virtuelle, et au démarrage on tape boot -c pour entrer dans le BOOT_CONFIG de OpenBSD. On désactive les pilotes en question :

boot> disable viornd
boot> disable viomb
boot> quit

Et là, miracle, l’installateur démarre et on peut installer le système d’exploitation puis démarrer le nouveau système en ré-effectuant la manipulation à chaque démarrage.

Rendre la bidouille permanente

C’est non seulement pénible de devoir désactiver les pilotes à chaque démarrage, mais également problématique puisqu’en cas de coupure de courant ou de redémarrage inopiné, la machine virtuelle plantera au démarrage et le serveur restera inaccessible.

On va donc rendre cette manipulation permanente en configuration un noyau avec ces pilotes désactivés. C’est là qu’intervient le fichier de configuration /etc/bsd.re-config qui permet de correctement configurer le noyau à chaque démarrage. On y insère alors les deux commandes disable viornd et disable viomb.

On teste ensuite en redémarrant le système non pas une, mais deux fois : en effet, les commandes du fichier /etc/bsd.re-config étant prises en commande au démarrage du système pour le démarrage suivant, le premier redémarrage est garanti de poser problème.