Un peu de stabilité

Nous allons maintenant chercher à utiliser le capteur LSM6DSL qui intègre un accéléromètre et un gyroscope afin de mesurer, de manière stable, l'inclinaison de notre carte IoT-node dans l'axe de sa longueur. Cet exercice fonctionne de manière indépendante sur les différents axes et peut donc se généraliser sans complication.

Attention : faites simple. Une cinquantaine de lignes devrait suffire pour implémenter la totalité de ce qui est demandé ci-dessous. Si vous dépassez cette limite, demandez vous si vous ne pouvez pas factoriser ou simplifier votre code.

Principes

L'axe X du LSM6DSL se trouve à l'horizontale,dans la longueur de la carte, l'axe Y à l'horizontale dans la largeur de la carte et l'axe Z à la verticale. Nous allons faire des acquisitions des accéléromètres et des gyroscopes sur chacun de ces axes une centaine de fois par seconde et en déduire l'inclinaison de la carte dans le plan XZ (ou, dit autrement, sa rotation autour de l'axe Y).

À partir des données du vecteur d'accélération projeté sur le plan XZ et de la vitesse angulaire autour de l'axe Y, nous allons pouvoir obtenir une position stable.

Configuration

Nous allons configurer les registres du LSM6DSL de la manière suivante :

  • CTRL1_XL : accéléromètre à 104Hz, échelle de ±2g (nous n'avons pas besoin de plus, la position qui nous intéresse est statique)
  • CTRL2_G : gyroscope à 104Hz, échelle de ±125°/s
  • CTRL3_C : mode BDU (block data update) et auto-incrément

Nous utiliserons 0 pour tous les autres registres de configuration (cf. la documentation). En particulier, nous ne choisirons aucun filtre pour récupérer un signal de capteur bruité.

Programmez l'ensemble des registres de configuration (ceux commençant par CTRL_), y compris ceux qu'on laisse à 0.

Attention : si vous choisissez de configurer les registres en bloc (ce qui est conseillé, étant donné qu'ils sont situés à des adresses consécutives), assurez vous que le capteur est bien en mode auto-incrément en faisant une écriture isolée de CTRL3_C pour soit positionner ce mode au cas où il ait été déconfiguré soit faire un reset.

Récupération des données

Faîtes une boucle à 100Hz pour récupérer les données du capteur. Utilisez vTaskDelayUntil() pour que cette boucle ne dérive pas indépendamment du temps qu'elle met pour s'exécuter. Ce genre de fonctions est très utile lors de la conception de systèmes temps-réel. Il vous faudra peut-être activer cette fonctionnalité dans FreeRTOSConfig.h si ce n'est pas déjà le cas.

En déclarant votre structure de manière appropriée, vous pourrez lire en une seule fois le résultat des trois axes des deux capteurs. Voici une possible déclaration :

static struct {
  int16_t g[3];   // Gyroscope
  int16_t xl[3];  // Accelerometer
} sensors;

Vous avez alors la garantie que les déclarations se font dans l'ordre indiqué, sans trou entre les tableaux.

Vérification du temps disponible

A-t-on le temps de faire ces lectures I²C 100 fois par seconde ? Combien de temps prend chaque lecture ?

Pour lire ces 12 octets, il faut :

  • Envoyer une demande d'écriture pour indiquer l'index : 1 bit de start, 8 octets pour l'adresse du périphérique, 1 bit de ack, 8 octets pour l'adresse à lire, 1 bit de ack, 1 bit de stop ; soit 20 bits.
  • Lire le résultat : 1 bit de start, 8 octets pour l'adresse du périphérique, 1 bit de ack, 12 octets avec chacun un ack (ou un nack pour indiquer qu'on a fini la lecture), 1 bit de stop ; soit 119 bits.

Il faut donc échanger au total 139 bits. Si le bus I²C est configuré à 400kHz, cela prend donc 139 / 400000 = 0,0003475 seconde. Tout va bien, on pourrait même faire ces calculs en configurant les capteurs à 1,66kHz sans aucun problème. Par contre, pour augmenter la fréquence au delà (le LSM6DSL va jusqu'à 6,66kHz), il faudrait passer par le bus SPI qui permet des vitesses plus grandes.

Interprétation des données

Affichez les valeurs reçues pour l'accéléromètre et le gyroscope. Les valeurs s'interprètent ainsi : chaque valeur est comprise entre -32768 et 32767 (16 bits) et couvre la sensibilité programmée pour chaque capteur. Ainsi, une force égale à 1g appliquée sur un axe donnera, avec la sensibilité de ±2g que nous avons choisie, une valeur d'environ ±16384 selon le sens d'application.

Pour ne pas perturber trop les mesures par le temps de transmission sur le port série, nous n'enverrons ces informations qu'une fois sur 100 (une fois par seconde).

Détermination de l'inclinaison grâce à l'accéléromètre

En utilisant la fonction atan2(), déterminez la valeur de l'orientation autour de l'axe Y et affichez cette valeur multipliée par 100. On souhaite que 0 corresponde à la position de repos normale (horizontale) de la carte, ±9000 à la carte posée sur la tranche.

Détermination de l'inclinaison grâce au gyroscope

Vous vous souvenez du cours qu'un gyroscope a une dérive fixe et dérive lentement. Après avoir attendu qu'une mesure soit disponible (par exemple après un délai d'un centième de secondes), faîtes une première mesure du gyroscope et stockez la valeur comme étant son drift (dérive). Cette valeur sera soustraite aux mesures suivantes, en espérant que la dérive sera lente par rapport à notre expérience. Idéalement il faudrait un filtre passe-haut.

En partant d'une inclinaison initiale de 0°, stockée dans une variable en virgule flottante, ajoutez à chaque itération la variation relevée par le gyroscope. N'oubliez pas que les rotations pour les valeurs limites correspondent à un nombre de degrés par seconde. Il ne s'écoule qu'1/100ème de seconde entre chaque tour de boucle, il faut donc en tenir compte lors du calcul.

Affichez, une fois par seconde, la valeur de l'angle calculée à l'aide du gyroscope multiplié par 100. Assurez vous que vos deux valeurs sont orientées dans le même sens.

Filtre complémentaire

Maintenez séparément des autres valeurs une variable en virgule flottante représentant l'angle indépendante des capteurs individuels. À chaque itération :

  • Prenez 95% de la valeur de l'angle corrigé par la variation indiquée par le gyroscope (en soustrayant le drift).
  • Incorporez-y 5% de la valeur calculée à partir de l'accéléromètre.
  • Ceci donne le nouvel angle.

Toutes les secondes, affichez les trois valeurs multipliées par 100 : l'angle calculé à partir de l'accéléromètre, à partir du gyroscope, et après le filtre complémentaire. Les plus courageux afficheront de manière interactive les différentes orientations (en lisant /dev/ttyACM0 ou équivalent depuis un programme) afin de pouvoir en visualiser la stabilité.

Vous pouvez également configurer le filtre pour des coefficients respectifs de 98%/2% et observer l'influence sur la stabilité ainsi que sur le temps de réaction du système lorsque vous bougez la carte.

Amélioration de la précision

Nous avons vu qu'avec un bus I²C à 400kHz nous pouvions opter pour des mesures à une fréquence de 1,66kHz. Configurez les accéléromètres et gyroscopes pour cette fréquence, et faites un relevé toutes les millisecondes tout en affichant toujours les résultats toutes les secondes. Vous pourrez constater la stabilité conservée et l'amélioration du temps de réaction du dispositif face à des changements d'orientation.

Séparation du calcul et de l'affichage

Afin de rendre les mesures plus régulières :

  • Écrivez chaque seconde et de manière non bloquante les 3 mesures dans une file d'attente (queue) de FreeRTOS sans rien afficher et rendez cette tâche de calcul plus prioritaire.
  • Dans une autre tâche de priorité moindre, affichez les valeurs en provenance de la file d'attente au fur et à mesure de leur arrivée.