MPU9250 9 axes

Bonsoir,

J’ai reçu ce midi (enfin) mon gyro, acceleromètre, magnétomètre donc je me suis empressé de le tester.
Voici mon matos:

  • MPU9250
  • Arduino nano

Branchements:

Arduino —> MPU9250
5V VCC (régulateur)
GND GND
SCL A5
SDA A4

Quand je lance le scanner I2C, il me retourne ça:

 Scanning...
I2C device found at address 0x68  !
done

Donc j’ai essayé une multitude de codes aujourd’hui (mon IDE est pollué de bibliothèques :smiley: )
Et celle-ci a l’air d’être la meilleure (code concis, précis, beaucoup utilisé sur Internet, etc)

"MPU9250.h" de Brian R Taylor

Et voici le code:

 /*
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(115200);
  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);
}

Dans le port Serial, il me retourne ça: (Bon, j’ai RESET la carte nano 4x pour essayer)

 IMU initialization unsuccessful
Check IMU wiring or try cycling power
Status: -2
IMU initialization unsuccessful
Check IMU wiring or try cycling power
Status: -5
IMU initialization unsuccessful
Check IMU wiring or try cycling power
Status: -1
IMU initialization unsuccessful
Check IMU wiring or try cycling power
Status: -4

Si quelqu’un a déjà eu affaire avec ce capteur, qu’il se manifeste ! :smiley:

Merci beaucoup !

J’ai des news !

Alors j’ai trouvé un code, sans bibliothèque, qui fonctionne parfaitement ! Le voici:

#include "Wire.h"

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);

#define MPU9250_ADDRESS 0x68
#define ACC_FULL_SCALE_2_G 0x00
uint8_t Buf[14];
float yangle;

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();
}

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();
}

void setup() {
  lcd.init();
  lcd.backlight();
  Wire.begin();
  I2CwriteByte(MPU9250_ADDRESS, 29, 0x06);
  I2CwriteByte(MPU9250_ADDRESS, 28, ACC_FULL_SCALE_2_G);
  I2CwriteByte(MPU9250_ADDRESS, 108, 0x2F);
  Serial.begin(115200);
}

void loop() {
  I2Cread(MPU9250_ADDRESS, 0x3B, 14, Buf);
  int16_t ay = -(Buf[2] << 8 | Buf[3]);
  yangle = ay / 182.04;
  lcd.setCursor(5, 2);
  lcd.print(yangle, 3);
  delay(100);

}

Je l’ai piqué d’un projet de niveau numérique sur Youtube de GreatScott.

Je me suis bien perdu avec cette histoire de buffer, de registre etc, je ne les retrouve pas dans le code.
D’ailleurs, dans sa vidéo, il explique qu’il a désactivé certaines fonctionnalités du MPU, pour ne laisser que le gyro avec l’axe Y (logique). Donc comment appeler les autres registres pour avoir les autres axes du gyro + l’acceléromètre ?

J’ai trouvé ceci qui semble pertinent, mais je ne sais pas du tout l’utiliser:

Quelques questions me viennent à l’esprit:
quelle est la référence de votre carte
a-t-elle un adaptateur 5v <->3v?
Quand à savoir ce que fait le logiciel complet dont vous donnez le lien, j’ai quelques généralités:

a) il est recommandé par sparkfun (qui aime à gâter ses clients et les garder)
b) vous avez choisi, chez github, le logiciel gérant le capteur de pression (10 DoF). C’est un peu incompatible avec votre titre (9 axes). Il y en a une version plus simple…MPU9250BasicAHRS.ino a 400 lignes de moins (30%)…

c) il gère un afficheur nokia… et la jonction série. Pour commencer, vous devriez vous contenter de la jonction série(que vous avez). Ca réduirait le code (ainsi que la remarque b)… et le nokia est ennuyeux à configurer, surtout si, à la place, vuis avez un LCD O2C…

d) Setup fait appel à deux fonctions, readByte, readBytes, et writeByte qui gèrent un I2C avec une “subadress” (l’I2C normal n’a pas la notion de subadresse, IIRC)…
Ol commence par vérifier qu’on a bien un MPU9250 : recherche, à l’adresse 0x68 -qui marche pour vous- et à une sous adresse 75 (je suppose que les sous adresses définissent des registres) une cle, qui doit être 0x71; il l’affiche et , alors, daigne continuer. Alors, il inflige un auto-test, lit les corrections d’usine, les affiche, d’abord pour l’acceleromètre et le gyromètre, ensuite pour le magnétomètre.
Les constantes (en usine) du magnétomètre sont affichées sur le port série -

Tout ça a l’air sympa…
e) loop a deux options:
afficher au cours du temps les valeurs brutes… ou afficher un cap, un angle de tangage et de roulis

Oulàlà, c'est compliqué tout ça !

Oui, j'ai un régulateur sur mon MPU9250 pour le VCC, donc je peux directement le brancher sur le 5V. En fait, le lien Github montrait surtout les différents registres que je ne maitrise pas du tout ! Moi, je voudrais simplement afficher les valeurs du gyro en x, y, z, et acceleromètre en x, y, z à partir du code donné ci dessus (GreatScott) avec un code relativement simple !

D'où l'utilisation de registres, là ou, je présume, on récupère les différentes données

Bon eh ben j’ai trouvé un super code! Au début, ça ne fonctionnait pas, mais après l’avoir branché en 3,3V c’est parfait (Je ne l’ai pas grillé apparemment ! OUF)

 #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);
 
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;

// ____________________________________
// ::: 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");
 
// End of line
Serial.println("");
// delay(100); 
}

Là, vous gérez 6 axes (3 acceleros, 3 gyros) ai lieu de 9 (manque la boussole) ... ce n'est pas compatible avec votre titre (mais si ça marche, tant mieux). En plus, en dehors de faire clignter une LED toutes les demis secondes, TimerOne ne vous sert à rien...

dbrion06: Là, vous gérez 6 axes (3 acceleros, 3 gyros) ai lieu de 9 (manque la boussole) ... ce n'est pas compatible avec votre titre (mais si ça marche, tant mieux). En plus, en dehors de faire clignter une LED toutes les demis secondes, TimerOne ne vous sert à rien...

D'accord, je vais alors le supprimer. Merci !

J'ai un problème avec le filtre Kalman... Il me renvoie des valeurs qui se stabilisent en fonction du temps, mais de façon très lente ... J'ai remarqué que sur tous les axes xyz, l'accéléromètre me donne une valeur de -110 environ quand le capteur ne bouge absolument pas. Ce qui doit influer la dessus

On est d’accord que j’ai un régulateur qui me permet de le brancher en 5V ici ?
Peut être qu’il faut que j’utilise des convertisseur logiques 5v vers 3,3V, mais comment les câbler ?

Bonjour, on est d'accord, voir "mpu9050 arduino wiring" -> images

Le problème c'est que parfois c'est en 5V parfois en 3,3.. quoi qu'il en soit, ça fonctionne bien en 3,3V :D

Bon, en x et en y ça fonctionne plutôt bien, mais vu que je suis en train de faire l’axe des lacets (z) de mon drone, bah j’ai aussi besoin d’avoir une mesure précise de cet axe sur mon Gyro, sauf qu’en utilisant le même code, mon Gyro en Z est totalement faux!

iFrostizz67:

 #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);

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;

// ____________________________________
// ::: 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");

// End of line
Serial.println("");
// delay(100);
}

J’ai quand même une valeur, mais pas du tout précise.

Ce qui est curieux, c’est que mon module ne fonctionne avec Aucune librairie, j’ai littéralement passé des heures à les tester

J’ai entendu parler aussi d’un registre “Who am I” I2C

Sinon, vous connaissez des Gyro qui fonctionnent bien sans trop s’embêter ? C’est un vrai casse tête …

Bonjour,

Ce lien me semble plutôt complet. Il n'y a pas de raison que tu n'y arrives pas en dehors d'un souci de composant défectueux.

Oui, c'est vraiment sur cette librairie (et ce youtubeur) que je plaçais toutes les chances, même lui n'a pas su m'expliquer le problème. Je pense plutôt à un composant défectueux alors

Voici un lien vers la datasheet du MPU9250, et l’utilisation des registres.

  • Les valeurs de l’accéléromètre sont aux adresses 59 à 64 (0x3B à 0x40)
  • La température à 65 et 66
  • Le gyromètre de 67 à 72
  • Le magnétomètre aux adresse 03 à 08

Quand le code exécute

uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);

on voit qu’il stocke dans le tableau Buf les 14 valeurs (octets) lus aux adresses 0x3B à 0x48 (59 à 72 en décimal). Donc il ne lit pas le magnétomètre. Pour obtenir les valeurs du magnétomètre, il faudrait faire

uint8_t BufMag[6];
I2Cread(MPU9250_ADDRESS,0x03,6,BufMag);

Ensuite, les lignes du genre

int16_t ax=-(Buf[0]<<8 | Buf[1]);

servent à reconstituer une valeur sur 16 bits à partir de deux octets.

Pour la valeur fausse du gyromètre, le code fait ça :

// 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];

On voit qu’il y a un signe ‘-’ pour les axes x et y mais pas pour le z : est-ce la raison ?

Sinon, ça peut venir de la configuration du gyromètre. Le registre 27 sert à ça :

BIT NAME FUNCTION
[7] XGYRO_Cten X Gyro self-test
[6] YGYRO_Cten Y Gyro self-test
[5] ZGYRO_Cten Z Gyro self-test
[4:3] GYRO_FS_SEL[1:0] Gyro Full Scale Select:
00 = +250dps
01= +500 dps
10 = +1000 dps
11 = +2000 dps
[2] - Reserved
[1:0] Fchoice_b[1:0]
Used to bypass DLPF as shown in table 1 above. NOTE:
Register is Fchoice_b (inverted version of Fchoice), table 1 uses
Fchoice (which is the inverted version of this register).

Pour ce que je vois dans le code, la config est faite ici :

#define GYRO_FULL_SCALE_1000_DPS 0x10
// Configure gyroscope range
I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_1000_DPS);

Donc on aurait
Registre 27, bit 7 6 5 4 3 2 1 0
0 0 0 1 0 0 0 0

Il y a aussi ces lignes :

// Set gyroscope low pass filter at 5Hz
I2CwriteByte(MPU9250_ADDRESS,26,0x06);

C’est (assez mal) expliqué aux pages 12 à 14 de la référence citée plus haut.

Ça m'étonnerait qu'un simple - fausse mes mesures mais autant essayer ! Quoi qu'il en soit, on m'a dit que le Gyro en Z n'était pas vraiment "lisible" directement, mais qu'il fallait passer passer par de la Trigo à l'aide des accélérations sur les axes. J'essaie et je vous tiens au courant.

Merci beaucoup pour votre message très intéressant !

Non ca ne fonctionne pas :/

J'aurais bien utilisé de la Trigo, mais vu que lorsque mon module est immobile, j'ai une accélération égale à 0, je ne peux rien faire ...

Et si je remplaçais cette ligne :

 uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);

par ça :

 uint8_t Buf[14];
I2Cread(MPU9250_ADDRESS,0x3B,15,Buf);

À mon avis, il faut aussi compter le "0" du tableau, peut être que le z n'est pas compté dedans

Qu'en pensez-vous ?