L'IMU (Inertial measurement unit ou centrale inertielle) MPU-9250 est considéré comme l'un des meilleurs composants de sa catégorie, dans le monde des Makers.
Je n'ai pas la prétention de vous faire un exposé sur le composant, mais de vous faire part de mon retour, sur ma tentative d'utilisation de ce composant.
Pour de plus amples informations, voici quelques liens documentaires :
Ayant une petite expérience du Gyro Lego, je me disais que la mise en oeuvre de ce composant allait être une formalité.
Je pensais qu'il suffisait d'appeler une fonction pour récupérer les valeurs X, Y et Z du gyroscope. Et bien, non !
Le PMU-9250 possède un Accéléromètre 3 axes, un Gyroscope 3 axes, une Boussole(magnètomètre) 3 axes et un thermomètre.
Le plus simple, c'est le thermomètre qui fonctionne immédiatement. Rien à dire.
Mon objectif était d'utilisé le Gyroscope pour diriger mon quadrupède en ligne droite. Malheureusement, le Gyroscope ne donne pas les valeurs des 3 angles d'inclinaison X, Y et Z, mais la vitesse angulaire, exprimé en degré/s. Je ne sais même pas de quoi, il s'agit.
J'imaginais que j'allais trouver un grand nombre d'exemples utilisant la bibliothèque MPU9250. Et bien non, je n'ai trouvé aucun exemple mettant en oeuvre les fonctions de cette bibliothèque.
Hors l'utilisation de cette bibliothèque, il existe, en particulier, le Blog de Lulu https://lucidar.me/f...ino-9-axis-imu/ qui propose un code assez complexe qui donne la valeur de tous les capteurs dans chaque axes. Ce code ne donne rien de plus que l'exemple Basic_I2C.ino de la bibliothèque MPU9250.h.
Ce code est repris par GreatScott! dans une vidéo très populaire, voir code et lien, ci-dessous.
Malheureusement, il utilise une astuce en utilisant l'axe Y de l'Accéléromètre pour mesurer l'inclinaison d'un Spirit Level (niveau à bulle) plutôt que le Gyroscope. Promettant l'utilisation du Gyroscope ultérieurement, cela fait déjà, 3 ans . . .
// https://lucidar.me/fr/inertial-measurement-unit/mpu-9250-and-arduino-9-axis-imu/
#include <Wire.h>
#include <TimerOne.h>
#define MPU9250_ADDRESS 0x68
#define MAG_ADDRESS 0x0C
#define GYRO_FULL_SCALE_250_DPS 0x00
#define GYRO_FULL_SCALE_500_DPS 0x08
#define GYRO_FULL_SCALE_1000_DPS 0x10
#define GYRO_FULL_SCALE_2000_DPS 0x18
#define ACC_FULL_SCALE_2_G 0x00
#define ACC_FULL_SCALE_4_G 0x08
#define ACC_FULL_SCALE_8_G 0x10
#define ACC_FULL_SCALE_16_G 0x18
// This function read Nbytes bytes from I2C device at address Address.
// Put read bytes starting at register Register in the Data array.
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.endTransmission();
// Read Nbytes
Wire.requestFrom(Address, Nbytes);
uint8_t index=0;
while (Wire.available())
Data[index++]=Wire.read();
}
// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
// Set register address
Wire.beginTransmission(Address);
Wire.write(Register);
Wire.write(Data);
Wire.endTransmission();
}
// Initial time
long int ti;
volatile bool intFlag=false;
// Initializations
void setup()
{
// Arduino initializations
Wire.begin();
Serial.begin(115200);
// Set accelerometers low pass filter at 5Hz
I2CwriteByte(MPU9250_ADDRESS,29,0x06);
// Set gyroscope low pass filter at 5Hz
I2CwriteByte(MPU9250_ADDRESS,26,0x06);
// Configure gyroscope range
I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_1000_DPS);
// Configure accelerometers range
I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_4_G);
// Set by pass mode for the magnetometers
I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);
// Request continuous magnetometer measurements in 16 bits
I2CwriteByte(MAG_ADDRESS,0x0A,0x16);
pinMode(13, OUTPUT);
Timer1.initialize(10000); // initialize timer1, and set a 1/2 second period
Timer1.attachInterrupt(callback); // attaches callback() as a timer overflow interrupt
// Store initial time
ti=millis();
}
// Counter
long int cpt=0;
void callback()
{
intFlag=true;
digitalWrite(13, digitalRead(13) ^ 1);
}
// Main loop, read and display data
void loop()
{
while (!intFlag);
intFlag=false;
// Display time
Serial.print (millis()-ti,DEC);
Serial.print ("\t");
// _______________
// ::: Counter :::
// Display data counter
// Serial.print (cpt++,DEC);
// Serial.print ("\t");
// ____________________________________
// ::: accelerometer and gyroscope :::
// Read accelerometer and gyroscope
uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);
// Create 16 bits values from 8 bits data
// Accelerometer
int16_t ax=-(Buf[0]<<8 | Buf[1]);
int16_t ay=-(Buf[2]<<8 | Buf[3]);
int16_t az=Buf[4]<<8 | Buf[5];
// Gyroscope
int16_t gx=-(Buf[8]<<8 | Buf[9]);
int16_t gy=-(Buf[10]<<8 | Buf[11]);
int16_t gz=Buf[12]<<8 | Buf[13];
// Display values
// Accelerometer
Serial.print (ax,DEC);
Serial.print ("\t");
Serial.print (ay,DEC);
Serial.print ("\t");
Serial.print (az,DEC);
Serial.print ("\t");
// Gyroscope
Serial.print (gx,DEC);
Serial.print ("\t");
Serial.print (gy,DEC);
Serial.print ("\t");
Serial.print (gz,DEC);
Serial.print ("\t");
// _____________________
// ::: Magnetometer :::
// Read register Status 1 and wait for the DRDY: Data Ready
uint8_t ST1;
do
{
I2Cread(MAG_ADDRESS,0x02,1,&ST1);
}
while (!(ST1&0x01));
// Read magnetometer data
uint8_t Mag[7];
I2Cread(MAG_ADDRESS,0x03,7,Mag);
// Create 16 bits values from 8 bits data
// Magnetometer
int16_t mx=-(Mag[3]<<8 | Mag[2]);
int16_t my=-(Mag[1]<<8 | Mag[0]);
int16_t mz=-(Mag[5]<<8 | Mag[4]);
// Magnetometer
Serial.print (mx+200,DEC);
Serial.print ("\t");
Serial.print (my-70,DEC);
Serial.print ("\t");
Serial.print (mz-700,DEC);
Serial.print ("\t");
// End of line
Serial.println("");
delay(100);
}
Et voici le code de l'exemple Basic_I2C. ino de la bibliothèque.
Spoiler
/*
Basic_I2C.ino
Brian R Taylor
brian.taylor@bolderflight.com
Copyright (c) 2017 Bolder Flight Systems
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <MPU9250.h>
// an MPU9250 object with the MPU-9250 sensor on I2C bus 0 with address 0x68
MPU9250 IMU(Wire,0x68);
int status;
void setup() {
// serial to display data
Serial.begin(9600);
while(!Serial) {}
// start communication with IMU
status = IMU.begin();
if (status < 0) {
Serial.println("IMU initialization unsuccessful");
Serial.println("Check IMU wiring or try cycling power");
Serial.print("Status: ");
Serial.println(status);
while(1) {}
}
}
void loop() {
// read the sensor
IMU.readSensor();
// display the data
Serial.print(IMU.getAccelX_mss(),6);
Serial.print("\t");
Serial.print(IMU.getAccelY_mss(),6);
Serial.print("\t");
Serial.print(IMU.getAccelZ_mss(),6);
Serial.print("\t");
Serial.print(IMU.getGyroX_rads(),6);
Serial.print("\t");
Serial.print(IMU.getGyroY_rads(),6);
Serial.print("\t");
Serial.print(IMU.getGyroZ_rads(),6);
Serial.print("\t");
Serial.print(IMU.getMagX_uT(),6);
Serial.print("\t");
Serial.print(IMU.getMagY_uT(),6);
Serial.print("\t");
Serial.print(IMU.getMagZ_uT(),6);
Serial.print("\t");
Serial.println(IMU.getTemperature_C(),6);
delay(100);
}
Voilà, vous l'aurez compris, je suis en galère. Moi, ce qui m'intéresse, c'est de connaitre la valeur de l'axe Z du Gyroscope.
Mais, peut-être pourriez-vous répondre à un petite question. Pourquoi, dans son code, GreatScoth! divise t-il la valeur de ay par 182.04 ?
Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir
Posté 26 janvier 2021 - 04:53
16384 / 90 = 182.04
16384 est la valeur d'accélération que tu obtiens lorsque tu es incliné à 90° par rapport à l'horizontale sur l'axe y. ( axe choisi par rapport à son projet qui mesure que l'inclinaison sur cet axe.)
( revoir dans sa vidéo à 3m36 si besoin )
Après malheureusement c'est une approximation linéaire d'une règle sinusoïdale ....
Pour son besoin c'est pas forcément grave mais c'est pas top ...
Pour le reste si tu pose une question plus précise je suis sûr que je peux d'avantage t'aider.
Car la valeur de l'axe z du gyroscope est affichée en brut ...
C'est Gz dans le code basic.
Mais il faut savoir dans quelle unité le résultat est donné et savoir que ça ne mesure pas un angle mais une vitesse angulaire ... Contrairement au magnétomètre mais que je ne recommande pas forcément d'utiliser comme boussole et encore moins seul ( car facilement perturbable par des éléments extérieurs )
16384 est la valeur d'accélération que tu obtiens lorsque tu es incliné à 90° par rapport à l'horizontale sur l'axe y. ( axe choisi par rapport à son projet qui mesure que l'inclinaison sur cet axe.)
( revoir dans sa vidéo à 3m36 si besoin )
Merci Mike.
J'avais bien vu à 3'36", mais je n'avais pas du tout fait la relation.
Oui, j'ai bien compris que Gz était une vitesse angulaire. La valeur varie fortement en fonction de la vitesse à laquelle tu tournes l'axe. Malheureusement, je n'en vois pas l'intérêt dans mon projet, mais j'ai quand même fait quelque chose qui pourrait servir.
Ce soir, c'est trop tard. J'essaye d'avancer demain et je poserai des questions plus précises.
je penses que ta déception vient en partie de la confusion entre le gyroscope et le gyromètre (qui sont malheureusement tous deux abrégés en "gyro"):
- le gyroscope, c'est un engin mécanique avec une pièce centrale qui tourne et qui reste toujours dans la même orientation absolue même si le boîtier est incliné. C'est gros, lourd et cher, mais ça a l'avantage de donner directement l'inclinaison (sur les 3 axes)
- le gyromètre donne une vitesse de rotation (selon un axe ou selon les 3 axes si on utilise un composant qui contient 3 gyromètres). L'avantage c'est qu'on en trouve des pas du tout cher, dans des tout petits circuits intégrés (on en trouve aussi des super précis, qui sont gros et cher). En soit un gyromètre ne permet pas d'avoir l'orientation.
Néanmoins, avec un gyromètre, si on intègre correctement les données, on peut mesurer les changements d'orientation (par exemple par rapport à l'orientation initiale). A noter néanmoins qu'il y a inévitablement une dérive dans le temps (proportionnelle au temps le plus souvent, au mieux proportionnelle à la racine carrée du temps si l'offset est calibré à la perfection).
Le cas "facile" est celui où le capteur reste parfaitement horizontal : il suffit d'intégrer la vitesse de rotation autour de l'axe Z. Néanmoins, si le capteur se retrouve à un moment légèrement incliné, ça fausse significativement les résultats.
Dans le cas contraire, il faut faire l'intégration des 3 angles, ce qui est bien plus complexe.
Si on veut limiter les dérives, on peut éventuellement combiner avec un accéléromètre (qui donne la verticale si le capteur est immobile, ou un indice sur la verticale si le capteur bouge (mais ce cas est bien plus dur à traiter)).
Donc ton capteur Légo, s'il te donnait une orientation, était probablement en fait un gyromètre relié à un petit microcontroleur qui faisait les calculs pour estimer l'orientation (relative à l'orientation de départ), éventuellement utilisant des références absolues via un accéléromètre (verticale) et/ou un magnétomètre (nord, très bruité en intérieur).
Si tu ne veux pas te casser la tête, et avoir un module "tout intégré", qui se charge de faire les fusions de données et de te donner une estimation d'angle absolut, ça existe, mais c'est un peu plus cher. Par exemple https://www.gotronic...a2472-23896.htm d'adafruit.
Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...
Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.
Comme tu as pu le voir dans mes vidéos, mon quadrupède est très sautillant.
L'idée, c'était de faire au plus simple, c'est à dire utiliser un IMU plutôt que 2 ou 3 télémètres laser, https://www.robot-ma...l53l0x-161.htmlpour parcourir les 10m de la TRR.
La description du module ADA2472 d'Adafruit, m'a conduit au BNO055 (qui ne vaut que quelques euros sur Ali) et sur quelques tutoriaux sur YouTube.
Si ton robot est à peu près immobile, ça devrait assez bien marcher.
Par contre, fait attention une fois que ton robot bouge : ce que tu mesures, c'est l'angle entre ton capteur et le vecteur (accélération - g) où g est le vecteur gravité (g=9.81 m/s^2, orienté vers le bas).
Donc si l'angle entre -g et (accélération-g) n'est plus négligeable, tu aura une erreur du même ordre de grandeur dans ton estimation de l'inclinaison.
Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...
Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.
Comme évoqué plus haut, le divisé par 182.04 est une approximation grossière ...
Normalement l'effort en du vecteur gravité projeté sur l'axe y est censé s'exprimée en K*sin(Teta ) ( ou K * cos(Teta) en fonction d'où tu considère ton 0° )
De sorte à ce que à 0° on se retrouve avec un effort nulle sur l'axe ( ou à 90° si on prend la formule avec le cosinus ) et à inversement à 90° on a la valeur la plus importante ...
Tu peux tester d'incliner ton robot à 45° et regarder si ça t'affiche bien 45° sur le moniteur série pour t'en convaincre ...
Donc en théorie tu devrais pouvoir améliorer la précision en faisant :
angle = arcSin( accelY / K ) (ou arcCos en fonction de où tu veux ton 0 ... )
AccelY c'est la valeur que tu mesure en Y , K c'est la valeur maximum que tu as ( ça représente la constante de gravité à l'échelle prêt... car doit être exprimé dans les même unités que accelY)
Sinon pour le reste ce qui devrait t'intéresser c'est d'utiliser le filtre de magwick. ça marche très bien avec L'imu mpu9250. ça te permet d'avoir la position angulaire de ton imu sur ses 3 axes. C'est un peu compliqué pour un novice complet mais tout se trouve tout fait sur internet =) .
Et tu verras ce que sont les quaternions ... Mais c'est peut être un tout petit peu complexe pour toi pour le moment et tu seras peut être d'abord intéressé par faire de l'intégration continue de le vitesse de rotation sur l'axe z. Ce qui normalement devrait être suffisant pour ton besoin.
Le principe :
Au départ ton angle = 0;
Tu lis la valeur de gz ( qui représente une rotation / secondes) en boucle, tout en notant le temps écoulé entre 2 lectures que l'on note DeltaT .
Et tu fais angle += gz * deltaT.
Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir
Posté 27 janvier 2021 - 07:47
Bon eh bien quand tu voudra essayer à nouveau tu pourras essayer ça:
#include <MPU9250.h>
// an MPU9250 object with the MPU-9250 sensor on I2C bus 0 with address 0x68
MPU9250 IMU(Wire,0x68);
void setup() {
// serial to display data
Serial.begin(9600);
while(!Serial) {}
// start communication with IMU
int status = IMU.begin();
if (status < 0) {
Serial.println("IMU initialization unsuccessful");
Serial.println("Check IMU wiring or try cycling power");
Serial.print("Status: ");
Serial.println(status);
while(1) {}
}
for (uint8_t i = 0; i < 100; i++)
computeHeading(false); // on initialise le biais, ne pas bouger l'IMU pendant ce temps là
}
void loop() {
Serial.print("heading : ");
Serial.print(computeHeading(true),6);
Serial.print("°\t gyro : ");
Serial.print(IMU.getGyroZ_rads(),6);
Serial.println("rad /s");
}
float computeHeading( bool isMoving) {
// Variables static qui seront réutilisée d'une itération sur l'autre
static uint32_t previousTime = micros();
static float heading = 0;
static float biais = 0;
uint32_t now = micros();
IMU.readSensor(); // On lit les données du capteurs
float gyroSpeedZ = degrees(IMU.getGyroZ_rads()); // On récupère la vitesse de rotation en Rad /s selon l'axe z et on la convertie en degrées
if( isMoving && abs(gyroSpeedZ) > 1) {
uint32_t deltaTime = now - previousTime; // On calcul le delta de temps depuis la dernière mesure en microsecondes (µs)
heading += (gyroSpeedZ - biais) * deltaTime * 1e-6; // On intègre la vitesse de rotation pour obtenir l'angle avec 1e-6 permet de convertir les µs en secondes
//On remet l'angle entre -/+ 180 °
if(heading > 180)
heading -= 180;
else if(heading < -180)
heading += 180;
} else {
biais = (99 * biais + gyroSpeedZ) / 100; // On calcul le biais avec une moyenne de 100 valeurs ...
}
previousTime = now; // On sauvegarde notre référence de temps pour la prochaine itération du calcul.
return heading;
}
J'ai testé ton programme. Il fonctionne bien, malgré une dérive au bout de quelques déplacements. Je ne pense pas que cela provienne de mon banc de tests.
Tu utilises une variable "biais", y a t-il un rapport avec les fonctions bias de la bibliothèque ?
Qu'elle est la signification de ce terme, en dehors du sens premier ?
Les gyromètre, comme beaucoup de capteurs, ont un biais (aussi appelé offset), c'est à dire que toutes les valeurs affichées sont décalées de cette valeur.
Ainsi, si le biais de ton gyromètre est (par exemple) de +1°/s, alors :
- si ton robot est immobile, le gyromètre renverra 0+1=1°/s
- si ton robot tourne de +10°/s, il renverra 10+1=11°/s
- si ton robot tourne de -3°/s, il renverra -3+1=-2°/s
...
Le gros problème, est que si tu intègre la vitesse angulaire pour connaître ton orientation, tu intègres aussi l'offset : tu auras donc une dérive de biais*durée. Si l'offset est de 1°/s, alors au bout de 3 minutes, tu aura une erreur de 180° sur l'orientation de ton robot.
La solution, c'est d'estimer ce biais, pour le soustraire.
Certains capteurs ont un biais vraiment fixe, donc on peut calculer la valeur une fois pour toute (par exemple un capteur de distance très précis, mais qui est montée 1cm derrière l'endroit où il aurait du être : il affichera toujours 1cm de trop). Mais la plupart des capteurs électroniques ont une biais qui change (entre autre avec la température, la tension d'alimentation, ...). C'est pourquoi le mieux est en général de calibrer juste avant l'utilisation. Pour ça, le plus facile, c'est que quand on sait que le robot est immobile (moving==false), on prend la moyenne des valeurs lues : c'est ce que fait Mike.
Le "problème", c'est que beaucoup de capteurs ont des biais qui malgré tout varient lentement : donc la calibration n'est plus parfaite au bout de quelques secondes, d'où un dérive (beaucoup plus lente néanmoins que sans calibration du tout)
Aidez-nous à vous aider : partagez toutes les informations pertinentes : description précise du problème, contexte, schéma de câblage, liens vers la documentation des composants, votre code (ou encore mieux un code minimal reproduisant le bug), ...
Vous recevrez ainsi plus de réponses, et elles seront plus pertinentes.
Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir
Posté 28 janvier 2021 - 05:20
Sandro a très bien répondu à la question, il y a en effet plusieurs méthodes pour corriger ce biais ( filtrage), là j'ai proposé une méthode simple sans prendre les outils qui sont présent dans la librairie. Le but étant de comprendre ce qui était derrière et de voir le problème ... J'avais même hésite à poster d'abord un code qui ne prendrait pas en compte le biais ... Mais je me suis ravisé en me disant que sandro viendrait sans doute me rappeler à l'ordre
Bien entendu il existe des filtres beaucoup plus performant. Dont le filtre de magwik AHRS.
100 données c'est pas énorme pour le biais ...
Mais du coup est ce que ça t'intéresse de creuser un peu plus ce thème maintenant que tu vois un peu les tenants et aboutissant ?
Mais du coup est ce que ça t'intéresse de creuser un peu plus ce thème maintenant que tu vois un peu les tenants et aboutissant ?
Merci à vous deux pour votre aide et vos explications. Non, je ne poursuivrai pas avec ce type de capteur. J'ai commandé d'autres capteurs, j'en reparlerai quand je les recevrai et si cela fonctionne correctement. En attendant de les recevoir, je passe à autre chose.