[RESOLU] MPU-6050 : Mesures qui dérivent dans le temps puis se stabilisent

Bonjour à tous,

Je suis en train de m'amuser avec mon nouveau MPU-6050 et pour le moment je me content de lire les valeurs des angles (en degré) sur les axes X, Y et Z.

Mais lorsque je met les mesures sous forme de graphique, voici ce que j'obtiens : cf pièce jointe

Pendant 10 à 20 secondes, on constate que la valeur du lacet augmente, puis, d'un coup, elle chute et fini par se stabiliser. Les roulis et tangage conserve leur valeur initiale pendant la même durée puis se stabilisent.

Ce comportement vous parait-il normal ?

J'ai refais les mesures plusieurs fois et j'obtiens à chaque fois le même résultat.
Ça m'ennuie un peu de devoir attente 20 secondes avant d'avoir des mesures exploitables...

Voici le code que j'utilise :

#include <Wire.h>
#include <I2Cdev.h>
#include "MPU6050_6Axis_MotionApps20.h"

MPU6050 mpu;

#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

// packet structure for InvenSense teapot demo
uint8_t teapotPacket[14] = { '

Merci de m'avoir lu.

Bonne journée :slight_smile:

, 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
int x = 0;

volatile bool mpuInterrupt = false;    // indicates whether MPU interrupt pin has gone high

void dmpDataReady() {
    mpuInterrupt = true;
}

void setup() {
    Wire.begin();
   
    Serial.begin(115200);

// initialize device
    Serial.println(F("Initializing I2C devices..."));
    mpu.initialize();

// verify connection
    Serial.println(F("Testing device connections..."));
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

// wait for ready
    Serial.println(F("\nSend any character to begin DMP programming and demo: "));
    while (Serial.available() && Serial.read()); // empty buffer
    while (!Serial.available());                // wait for data
    while (Serial.available() && Serial.read()); // empty buffer again

// load and configure the DMP
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();
   
    // make sure it worked (returns 0 if so)
    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);

// enable Arduino interrupt detection
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(0, dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();

// set our DMP Ready flag so the main loop() function knows it's okay to use it
        Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;

// get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } else {
        // ERROR!
        // 1 = initial memory load failed
        // 2 = DMP configuration updates failed
        // (if it's going to break, usually the code will be 1)
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
    }

// configure LED for output
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
    // if programming failed, don't try to do anything
    if (!dmpReady) return;

// wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) {
    }

// reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();

// get current FIFO count
    fifoCount = mpu.getFIFOCount();

// check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));

// otherwise, check for DMP data ready interrupt (this should happen frequently)
    } else if (mpuIntStatus & 0x02) {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

// read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
       
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;

// display Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        //Serial.print("ypr\t");
        Serial.print(ypr[0] * 180/M_PI);
        Serial.print(";");
        Serial.print(ypr[1] * 180/M_PI);
        Serial.print(";");
        Serial.print(ypr[2] * 180/M_PI);
        Serial.print(";");
        Serial.print(x);
        Serial.print("\n");

// blink LED to indicate activity
        blinkState = !blinkState;
        digitalWrite(LED_PIN, blinkState);
    }

x++;
}


Merci de m'avoir lu.

Bonne journée :)

![mesures.png|791x470](upload://2A2lfdKIpDGZT9zK5p5AGqQyohx.png)

Je vois que tu utilises le DMP du MPU.

Est-ce que par hasard il ne fait pas la calibration du gyroscope et de l'accéléromètre ?
Si oui, il faut que tu récupères les offsets une bonne fois pour toute, et que tu les insères dès le début (et que tu repositionnes le MPU tout le temps de la même manière - les axes dirigés dans le même sens-).

je crois qu'il faut que tu utilises les filtres complémentaires ou de Kalman, mais n'ayant pas fini avec mon gyro je ne peux encore rien te dire :confused:

Salut.
Alors en tout franchise j'ai pas réfléchi longtemps sur le code, j'ai pris celui ce démo de la librairie MPU6050. Donc le DMP j'ai pas la moindre idée de ce que c'est. IL va falloir que j'étudie un peu le fonctionnement de ce capteur plus en profondeur.

En ce qui concerne le filre de Kalmann j'ai trouvé une implémentation qui fonctionne mais le signal de sortie est très bruité je trouve, d'autant que je n'ai que les axes X et Y.

lobodol:
En ce qui concerne le filre de Kalmann j'ai trouvé une implémentation qui fonctionne mais le signal de sortie est très bruité je trouve, d'autant que je n'ai que les axes X et Y.

Ouep c'est vraiment la galère ce truc :confused: Moi il me faut un truc vraiment stable mais j'ai toujours pas compris comment intégrer ce filtre dans mon code...à vrai dire je ne le comprends même pas je ne vois pas ce qu'il fait...si jamais tu sais comment avoir un truc qui marche préviens moi stp !^^"

Salut, de ce que j'ai compris, un filtre de Kalmann sert à filtrer des données en se basant sur plusieurs mesures afin d'éliminer les mesures erronées. Par exemple, tu utilise un capteur gyroscopique et un accéléromètre. Si ton capteur d'angle t'indique le passage de 0° à 45° entre deux mesures et que l'accéléromètre n'a relevé aucune accélération -> on est dans le cas d'une mesure foireuse du gyroscope, donc on considère cette mesure = à 0 (par exemple).
C'est vraiment très simplifié comme explication, si quelqu'un de plus calé lit ce sujet n'hésitez pas à nous éclairer.

Quoi qu'il en soit, je ne comprend pas l'aspect mathématique de ce filtre et donc je suis incapable de proposer ou de corriger une implémentation.

Comme l'a dit @Creuvard, il est fort possible que cette période de transition corresponde à la calibration du capteur, chose qu'il n'est forcément nécessaire de faire à chaque fois.
Je vais donc me renseigner sur le sujet et reviendrai vers vous lorsque j'aurai plus d'informations.

lobodol:
Quoi qu'il en soit, je ne comprend pas l'aspect mathématique de ce filtre et donc je suis incapable de proposer ou de corriger une implémentation.

Pareil, j'ai essayé de trouver des explications sur le net mais personne d'explique l'algorithme...Je travaille sur ces foutus filtres en ce moment et je galère...

Si, il y a un type qui explique son implémentation sur son blog, après il faut avoir le package mathématique pour comprendre : TKJ Electronics » A practical approach to Kalman filter and how to implement it

Après avoir fouillé sur le net et dans la doc de la librairie (I2Cdevlib: MPU6050 Class Reference) je ne suis pas parvenu à trouver une explication. J'ai trouvé un tuto qui explique comment calibrer les offset son MPU : I2Cdevlib: MPU6050 Class Reference

Mais dans mon code actuel, je n'ai pas trouvé où il y aurait un calibrage automatique, que ce soit dans le setup() ou dans la loop()...

Peut-être dans ce code se trouverait une solution ? (écriture de quelques valeurs de démarrage en 'dur' ... )
Balancing bot
En tout cas si on en juge par la vidéo : le MPU6050 a l'air très bien exploité ! J'ai rarement vu un balancing bot aussi stable sans utilisation de filtre Kalman ou autre semble-t-il !?

Merci pour le lien, je vais étudier le code source de ce projet et je vous ferai un retour.
Mais effectivement, le fait de fixer les offset en dur dans le code semble être une solution...

Je confirme ! j'ai mis les offsets à 0 et j'ai des valeurs très précises pour le pitch et le roll

arduinopizza:
Je confirme ! j'ai mis les offsets à 0 et j'ai des valeurs très précises pour le pitch et le roll

Ah ! Super nouvelle :slight_smile: Je test ça ce soir !
Et pour le Yaw ça donne quoi ?

Comme quoi, on va peut-être pouvoir se passer de Kalmann et de son filtre :wink:

Bon bah en ce qui me concerne c'est l'échec total. J'ai d'abord tenté de mettre les offsets à 0 : les valeurs dérivent encore plus.
J'ai ensuite essayé de calibrer avec cette méthode : je n'ai jamais réussi à obtenir des valeurs proches de 0...
Du coup je veux bien que tu me montre ton code source stp :'D

[EDIT] Sur ce site la personne a exactement le même problème "d'auto-calibrage" au démarrage. Sa solution : attendre 40sec au démarrage avant d'attaquer le code principal... ça pue un peu comme solution ça : /

J'ai finalement trouvé une méthode qui a l'air de fonctionner, sur ce site.

J'obtiens des valeurs proches de 0, lorsque je trace le graphe, les courbes sont beaucoup plus stables (cf pièce jointe).

[EDIT] ouais globalement les valeurs sont stables dès le début, problème résolu !

Example self balance robot : Arduino Kendini Dengeleyen Robot - Proje Hocam