Calibrer le capteur MPU6050 avec un Arduino - Drone ch.5

Bonjour à tous,

Dans ce nouveau chapitre, nous allons voir comment fonctionne le capteur gyroscopique MPU5060 et comment l'utiliser avec un Arduino.

Comme pour toutes mesures à l'aide d'un capteur, nous allons passer par l'étape indispensable du calibrage.

La capteur gyroscopique étant au coeur du fonctionnement d'un drone, il mérite bien un chapitre dédié smiley

1. Qu'est-ce qu'un gyroscope ?

1.1 Gyroscope mécanique

gyroscope
Gyroscope en équilibre sur un fil

Un gyroscope mécanique est un système qui, lorsqu'il tourne, a pour particularité de conserver son inclinaison, même lorsque le support sur lequel il repose effectue une rotation.

Un peu comme une toupie ?

Exactement, une toupie se comporte comme un gyroscope.

Ainsi, quand une machine embarquant un gyroscope effectue une rotation, le gyroscope conserve son orientation. Il suffit alors de mesurer l'angle entre la machine et le gyroscope pour déduire l'angle de rotation de la machine.

Le gyroscope est un dispositif facinant que l'on pourrait étudier pendant des heures. Cet article n'a pas vocation à l'étudier en détail. Pour plus d'information à son sujet, je vous invite à étudier ces quelques références:

A l'ère du smartphone, ce dispositif présente plusieurs inconvéniants majeurs, dont sa taille. Je vous laisse imaginer la taille de votre téléphone s'il embarquait un gyroscope mécanique !
De plus, il faudrait une source d'alimentation pour entretenir la rotation, il serait sensible aux chocs... bref, pas très pratique.

Heureusement, il existe aujourd'hui des puces électroniques qui remplacent les gyroscopes mécaniques et c'est ce qui va nous intéresser aujourd'hui.

1.2 Accéléromètre électronique

1.2.1 Position, vitesse et accélération

Comme son nom l'indique, un accéléromètre sert à mesurer l'accélération qu'il subit. Mais au final, qu'est-ce qu'une accélération ?

L'accélération d'un corps est la variation de sa vitesse ($\vec{v}$) dans le temps ($t$).

Si une voiture passe de 0 km/h à 100km/h en 10 sec, elle subit une accélération positive. Si elle roule à une vitesse fixe de 100km/h, il n'y a pas variation de vitesse, son accélération est donc nulle.

Dans la vie courante, on distingue trois cas que les physiciens regroupent sous le seul concept d'accélération :

  • Aller plus vite : dans une voiture, le compteur de vitesse montre que la vitesse augmente.
    Du point de vue mathématique, l'accélération est positive, c'est-à-dire que le vecteur accélération possède une composante dans le sens de la vitesse
  • Aller moins vite : l'indication du compteur de vitesse diminue.
    L'accélération est négative, ou le vecteur accélération possède une composante opposée au sens de la vitesse.
  • Changer de direction : même si l'indication du compteur de vitesse ne change pas, le changement de direction implique une accélération.
    Le vecteur accélération comporte une composante perpendiculaire à la vitesse. On s'intéresse ici à la variation de la direction du vecteur vitesse, pas à la variation de sa norme.

D'un point de vue mathématique, l'accélération $\vec{a}$ est définit par la relation suivante:

$$ \vec{a} = \dfrac{d\vec{v}}{dt} = \dfrac{\Delta \vec{v}}{\Delta t}$$

Nous avons là une dérivée, ce qui est la traduction d'une variation dans le langage mathématique

Posons-nous maintenant la question (un peu plus évidente cette fois): qu'est-ce qu'une vitesse ?

La vitesse d'un objet est définit comme étant la variation de sa position ($\vec{p}$) dans le temps ($t$).
Si un objet se déplace de 1m en une seconde, sa vitesse est de $1m.s^{-1}$ (1m par seconde).

D'un point de vue mathématique, la vitesse est définit par la relation suivante:

$$v = \dfrac{d \vec{p}}{dt} = \dfrac{\Delta \vec{p}}{\Delta t}$$

Nous avons au final l'accélération qui est la dérivée de la vitesse, qui est elle-même la dérivée de la position. L'accélération est donc la dérivée seconde de la position. Son unité dans le Sytème International est logiquement en $m.s^{-2}$.

Son expression en fonction de la position est définit par la relation suivante:

$$ \vec{a} = \dfrac{d^{2} \vec{p}}{dt^{2}}$$

L'accélération terreste, définie par la troisième conférence générale des poids et mesures de 1901, est de $9,80665 m.s^{-2}$. On la note généralement avec la lettre $g$ (pour "gravité").
C'est de ce $g$ là qu'on parle quand on dit que les pilotes de courses subissent une accélération de 3g dans les virages.

C'est tout pour les rappels de physique laugh

1.2.2 Mesurer l'accélération

Pour mesurer l'accélération qu'il subit, un accéléromètre électronique va mesurer une variation de capacité électrique. Voici à quoi ressemble sa structure interne:

Accéléromètre électronique

On distingue deux parties: une structure fixe (en vert) et une structure mobile (en rouge). Ces deux structures, bien souvent en silicium, sont chargées électriquement.
Au repos, ces deux structures sont immobiles.

Lorsque le capteur subit une accélération, la structure en peigne (en rouge) se déforme et se déplace par inertie. La distance entre les lamelles rouges et vertes varie, entrainant une variation de capacité électrique. Cette variation de capacité est mesurée et finalement convertie pour obtenir la valeur de l'accélération subie.

Avec un seul de ces systèmes, on ne peut mesurer l'accélération que sur un seul axe. C'est pourquoi les accéléromètres de smartphone sont constitués d'un minimul de 3 de ces systèmes, chacun mesurant l'accélération d'un plan (X, Y & Z). C'est ensuite une unité de calcul dédiée qui envoie les informations au CPU du capteur.

Un capteur tel que celui-ci fait partie de la catérgorie des Micro-Electro-Mechanical-Systems (MEMS). Les MEMS sont de très petits systèmes, composés de microcomposants de l'ordre du dixième de nanomètre. Ces composants sont faits de silicon, polymères, métaux et/ou céramiques et sont très souvent combinés avec un CPU.

1.3 Gyroscope électronique

Les gyroscopes électroniques utilisent le même principe pour mesurer une rotation à ceci près qu'une autre loi physique entre en jeu: l'effet Coriolis.

C'est quoi l'effet Coriolis ?

Si un corps se déplace dans un milieu qui lui-même est en rotation uniforme, une force perpendiculaire à la direction du mouvement est appliqué audit corps.

Autrement dit, une bille lancée en ligne droite sur un plateau tournant va avoir une trajectoire en arc de cercle du point de vue d'un point fixe du plateau. Pourtant, la force de poussée de la bille est bien orientée dans une seule direction.
La rotation du plateau a egendré une force perpendiculaire à celle de poussée de la bille, ce qui a pour conséquence de la dévier dans cette direction, d'où la trajectoire en arc de cercle.

Effet Coriolis
Trajectoire d'une bille à vitesse constante sur un dique en rotation

Pour un observateur extérieur immobile, c'est une ligne droite (en haut). Pour un observateur qui serait sur le point rouge (en bas), elle épouse la courbe inverse de la rotation.

Un gyroscope électronique va se baser sur ce principe pour mesurer les rotations. Voici une vidéo illustrant ce principe:

On retrouve une structure en peigne similaire à celle de l'accéléromètre, mais cette fois la masse oscille régulièrement. Lorsqu'une rotation est appliquée à l'ensembe du système, une force va apparaitre (due à l'effet Coriolis), ce qui entraine un déplacement latéral de la grille rouge. La distance entre les lamelles rouges et bleues s'en trouve changée, entrainant là encore une variation de capacité électrique.
Et sur le même principe que précédemment, le capteur va interprêter cette variation de capacité pour la traduire en rotation.

Voici une vue au microscope d'un vrai capteur gyroscopique:

Vue au microscope d'un capteur gyroscopique

C'est d'autant plus impressionant quand on se rend compte de l'échelle !

2. Le capteur MPU6050

Le capteur que nous allons utiliser, le MPU6050 de InvenSense, utilise ces principes et embarque un accéléromètre 3 axes et un gyroscope 3 axes.

Pourquoi tu nous fais utiliser ce capteur plutôt qu'un autre ?

Il faut en effet toujours aborder un problème dans le bon sens: partir d'une problématique pour arriver à une solution technique et surtout pas s'imposer une solution technique pour remonter à la problématique.

Après une phase de recherche préliminaire, je suis arrivé à la conclusion que ce capteur offrait tout ce dont j'avais besoin (gyroscope et accéléromètre) pour une somme modique (~2€). De plus, la documentation à son sujet étant très riche sur le web, j'ai décidé d'utiliser ce capteur.

N'hésitez donc pas à faire vos propres recherches et à comparer les capteurs qui existent. Peut-être qu'entre le moment où j'écris ces lignes et aujourd'hui, de nouveaux capteurs plus adaptés auront vu le jour.

Voici à quoi resemble ce capteur:

MPU5060

En consultant la documentation technique du constructeur, on constate que:

  • Sa tension d'alimentation est comprise entre 2.375V et 3.45V
  • Il utilise le bus I2C
  • Il est constitué d'un accéléromètre 3 axes ET d'un gyroscope 3 axes
  • Il utilise les interruptions
  • Le gyroscope a une précision allant jusqu'à $\pm 2000 °/sec$
  • Le gyroscope a une consommation nominale de $3.6mA$
  • L'accéléromètre a une précision allant jusqu'à $\pm 16g$
  • L'accéléromètre a une conommation nominale de $500\mu{A}$

Il a tout ce qu'il faut pour fonctionner avec un Arduino et nous assurer des relevés adaptés à notre cas d'usage !

N'hésitez pas à consulter la documentation, on y trouve tout ce qu'il y a savoir sur ce capteur.

3. Calibrage

Maintenant qu'on sait de quoi on parle, on va pouvoir passer à l'étape qui nous intéresse: le calibrage. Et pour que celui-ci ait du sens, on va le faire directement sur le drone !

3.1 Réalisation d'un shield pour Arduino

Pour que nos mesures aient du sens, il va falloir que le capteur soit fixe par rapport à la structure du drone. Nos mesures seraient inexploitables si le capteur se baladait librement, pendu au bout de ses fils.

Afin de se simplifier la vie (si, si, je vous assure), nous allons réaliser un shield pour Arduino sur lequel sera fixé le capteur.

C'est quoi un shield ?

Il s'agit d'un circuit imprimé qui peut être pluggué sur l'Arduino pour étendre ses fonctionnalités. Tous les shields Arduino suivent la même philosophie que le kit original: ils sont faciles à monter et pas chers à produire.

Shield 1 Shield 2 Shield 3
Exemple de shields Arduino

Voici le schéma de câblage pour relier le capteur à l'Arduino:

Schéma de câblage
Schéma de câblage

Ayant formaté mon PC un peu trop rapidement, je n'ai plus le fichier de routage de mon shield.
Pour cette fois, je vous laisse le soin de le réaliser par vous-même wink

Quoi qu'il en soit, voici à quoi ressemble mon shield une fois monté sur l'Arduino. Ça a de la gueule !

Shield MPU6050
Shield MPU6050 pour Arduino

Avec un shield, le capteur va être surélevé par rapport au centre du drône. Ca ne risque pas de fausser les mesures ?

Le capteur est surélevé par rapport au centre de gravité du drone, mais est parallèle à celui-ci. Ainsi, lorsque le drone effectue une rotation d'un angle $\theta$, le capteur effectue la même rotation. Il subit en plus un déplacement horizontal par rapport au centre du cadre, ce qui n'affecte en rien son inclinaison.

Rotation 1 Rotation 2
Rotation avec capteur surélevé

Rien n'oblige a positionner le capteur au dessus du centre de gravité du drone. Dans l'absolue on pourrait le placer n'importe où sur le drone, pourvu que ses axes X, Y & Z soient orientés dans le même sens que ceux du quadricoptère.

3.2 Implémentation du code

Maintenant que le capteur tient bien en place, nous allons pouvoir mettre les mains dans le code.

Vous allez avoir besoin des libraries I2Cdev et MPU6050, disponibles ici. Copiez les répertoires Arduino/I2Cdev et Arduino/MPU6050 dans le dossier libraries de votre IDE Arduino.

Uploadez ensuite le code suivant dans votre Arduino:

// I2Cdev and MPU6050 must be installed as libraries
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

///////////////////////////////////   CONFIGURATION   /////////////////////////////
//Change this 3 variables if you want to fine tune the skecth to your needs.
int buffersize=1000;     //Amount of readings used to average, make it higher to get more precision but sketch will be slower  (default:1000)
int acel_deadzone=8;     //Acelerometer error allowed, make it lower to get more precision, but sketch may not converge  (default:8)
int giro_deadzone=1;     //Giro error allowed, make it lower to get more precision, but sketch may not converge  (default:1)

// default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
//MPU6050 accelgyro;
MPU6050 accelgyro(0x68); // <-- use for AD0 high

int16_t ax, ay, az,gx, gy, gz;

int mean_ax,mean_ay,mean_az,mean_gx,mean_gy,mean_gz,state=0;
int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;

///////////////////////////////////   SETUP   ////////////////////////////////////
void setup() {
  // join I2C bus (I2Cdev library doesn't do this automatically)
  Wire.begin();
  // COMMENT NEXT LINE IF YOU ARE USING ARDUINO DUE
  TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz). Leonardo measured 250kHz.

  // initialize serial communication
  Serial.begin(115200);

  // initialize device
  accelgyro.initialize();

  // wait for ready
  while (Serial.available() && Serial.read()); // empty buffer
  while (!Serial.available()){
    Serial.println(F("Send any character to start sketch.\n"));
    delay(1500);
  }                
  while (Serial.available() && Serial.read()); // empty buffer again

  // start message
  Serial.println("\nMPU6050 Calibration Sketch");
  delay(2000);
  Serial.println("\nYour MPU6050 should be placed in horizontal position, with package letters facing up. \nDon't touch it until you see a finish message.\n");
  delay(3000);
  // verify connection
  Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  delay(1000);
  // reset offsets
  accelgyro.setXAccelOffset(0);
  accelgyro.setYAccelOffset(0);
  accelgyro.setZAccelOffset(0);
  accelgyro.setXGyroOffset(0);
  accelgyro.setYGyroOffset(0);
  accelgyro.setZGyroOffset(0);
}

///////////////////////////////////   LOOP   ////////////////////////////////////
void loop() {
  if (state==0){
    Serial.println("\nReading sensors for first time...");
    meansensors();
    state++;
    delay(1000);
  }

  if (state==1) {
    Serial.println("\nCalculating offsets...");
    calibration();
    state++;
    delay(1000);
  }

  if (state==2) {
    meansensors();
    Serial.println("\nFINISHED!");
    Serial.print("\nSensor readings with offsets:\t");
    Serial.print(mean_ax); 
    Serial.print("\t");
    Serial.print(mean_ay); 
    Serial.print("\t");
    Serial.print(mean_az); 
    Serial.print("\t");
    Serial.print(mean_gx); 
    Serial.print("\t");
    Serial.print(mean_gy); 
    Serial.print("\t");
    Serial.println(mean_gz);
    Serial.print("Your offsets:\t");
    Serial.print(ax_offset); 
    Serial.print("\t");
    Serial.print(ay_offset); 
    Serial.print("\t");
    Serial.print(az_offset); 
    Serial.print("\t");
    Serial.print(gx_offset); 
    Serial.print("\t");
    Serial.print(gy_offset); 
    Serial.print("\t");
    Serial.println(gz_offset); 
    Serial.println("\nData is printed as: acelX acelY acelZ giroX giroY giroZ");
    Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0");
    Serial.println("If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)");
    while (1);
  }
}

///////////////////////////////////   FUNCTIONS   ////////////////////////////////////
void meansensors(){
  long i=0,buff_ax=0,buff_ay=0,buff_az=0,buff_gx=0,buff_gy=0,buff_gz=0;

  while (i<(buffersize+101)){
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    
    if (i>100 && i<=(buffersize+100)){ //First 100 measures are discarded
      buff_ax=buff_ax+ax;
      buff_ay=buff_ay+ay;
      buff_az=buff_az+az;
      buff_gx=buff_gx+gx;
      buff_gy=buff_gy+gy;
      buff_gz=buff_gz+gz;
    }
    if (i==(buffersize+100)){
      mean_ax=buff_ax/buffersize;
      mean_ay=buff_ay/buffersize;
      mean_az=buff_az/buffersize;
      mean_gx=buff_gx/buffersize;
      mean_gy=buff_gy/buffersize;
      mean_gz=buff_gz/buffersize;
    }
    i++;
    delay(2); //Needed so we don't get repeated measures
  }
}

void calibration(){
  ax_offset=-mean_ax/8;
  ay_offset=-mean_ay/8;
  az_offset=(16384-mean_az)/8;

  gx_offset=-mean_gx/4;
  gy_offset=-mean_gy/4;
  gz_offset=-mean_gz/4;
  while (1){
    int ready=0;
    accelgyro.setXAccelOffset(ax_offset);
    accelgyro.setYAccelOffset(ay_offset);
    accelgyro.setZAccelOffset(az_offset);

    accelgyro.setXGyroOffset(gx_offset);
    accelgyro.setYGyroOffset(gy_offset);
    accelgyro.setZGyroOffset(gz_offset);

    meansensors();
    Serial.println("...");

    if (abs(mean_ax)<=acel_deadzone) ready++;
    else ax_offset=ax_offset-mean_ax/acel_deadzone;

    if (abs(mean_ay)<=acel_deadzone) ready++;
    else ay_offset=ay_offset-mean_ay/acel_deadzone;

    if (abs(16384-mean_az)<=acel_deadzone) ready++;
    else az_offset=az_offset+(16384-mean_az)/acel_deadzone;

    if (abs(mean_gx)<=giro_deadzone) ready++;
    else gx_offset=gx_offset-mean_gx/(giro_deadzone+1);

    if (abs(mean_gy)<=giro_deadzone) ready++;
    else gy_offset=gy_offset-mean_gy/(giro_deadzone+1);

    if (abs(mean_gz)<=giro_deadzone) ready++;
    else gz_offset=gz_offset-mean_gz/(giro_deadzone+1);

    if (ready==6) break;
  }
}

Ce code n'est pas de moi, un grand merci à Luis Rodenas pour son travail.

3.3 Mode opératoire

Le but du calibrage est de donner un point de référence au capteur et le point qu'on va lui donner correspond à une inclinaison de 0°.

Pour ce faire, vous allez avoir besoin (en plus de l'Arduino et de son shield) d'un niveau à bulle, de 4 supports et... d'un jeu de cartes !

Un jeu de cartes, sérieusement ?

Yep ! Vous allez voir smiley

Commencez par fixer l'arduino et son shield sur le drone. Ensuite, placez le drone sur 4 supports de même hauteur. Ca peut être n'importe quoi, des verres, des boites, des livres, ...etc, pourvu qu'ils aient tous la même hauteur (à peu de chose près).

Placez ensuite un niveau à bulle sur le drone pour mesurer l'inclinaison. Et c'est là qu'intervient le jeu de cartes: corrigez les défauts d'inclinaison en glissant des cartes sous les supports. Ca permattra de corriger très finement l'inclinaison sur chaque axe.

niveau 1 niveau 2
Mise à niveau du capteur pour calibrage

Prenez le temps de vérifier l'inclinaison de chaque axe. Lorsque vous êtes satisfaits, vous pouvez lancer le sketch Arduino et ouvrir une console.

Après quelques secondes, le programme vous retourne 6 valeurs d'offsets: 3 pour l'accéléromètre et 3 pour le gyroscope, respectivement X, Y & Z pour chaque.

Send any character to start sketch.


MPU6050 Calibration Sketch

Your MPU6050 should be placed in horizontal position, with package letters facing up. 
Don't touch it until you see a finish message.

MPU6050 connection successful

Reading sensors for first time...

Calculating offsets...
...
...
...

FINISHED!

Sensor readings with offsets:	7	3	16391	1	0	-1
Your offsets:	365	-4046	1554	105	96	26

Data is printed as: acelX acelY acelZ giroX giroY giroZ
Check that your sensor readings are close to 0 0 16384 0 0 0
If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)

Gardez-les bien de côté car elles sont propres à votre capteur. 

Pour info, voici les valeurs que j'ai obtenues:

Accel Gyro
X Y Z X Y Z
365 -4046 1554 105 96 26

4. Mesures angulaires

Bien ! Il est maintenant temps de vérifier que le calibrage a fonctionné.

Balancez le code suivant dans votre Arduino en prenant soin de remplacer les valeurs d'offset (l.59) par celles que vous avez trouvées précédemment:

#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"
// ---------------------------------------------------------------------------
#define YAW      0
#define PITCH    1
#define ROLL     2
// --------------------- MPU650 variables ------------------------------------
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
// 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
VectorFloat gravity; // [x, y, z]            gravity vector
float ypr[3];        // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector

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

/**
 * Interrup détection routine
 */
void dmpDataReady() {
    mpuInterrupt = true;
}

/**
 * Setup configuration
 */
void setup() {
    Wire.begin();
    TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)

    Serial.begin(57600);

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

    // Load and configure the DMP
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();

    // MPU calibration: set YOUR offsets here.
    mpu.setXAccelOffset(365);
    mpu.setYAccelOffset(-4046);
    mpu.setZAccelOffset(1554);
    mpu.setXGyroOffset(105);
    mpu.setYGyroOffset(96);
    mpu.setZGyroOffset(26);

    // Returns 0 if it worked
    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 : #pin2)..."));
        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(")"));
    }
}

/**
 * Main program loop
 */
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) {
        // Do nothing...
    }

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

        // Convert Euler angles in degrees
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);

        // Print angle values in degrees.
        Serial.print(ypr[YAW] * (180 / M_PI));
        Serial.print("\t");
        Serial.print(ypr[PITCH] * (180 / M_PI));
        Serial.print("\t");
        Serial.println(ypr[ROLL] * (180 / M_PI));
    }
}

Ouvrez un terminal et admirez le résultat !

Vous devriez normalement avoir un relevé de mesures qui défile, dans l'odre: lacet, tangage, rouli. Commencez par vérifier que vous avez bien des valeurs proches de 0 lorsque l'Arduino est posé à plat.

Amusez-vous ensuite à vérifier des inclinaisons simples à reproduire, 90°, 45°...etc.

Quand je lance le programme, les valeurs mettent quelques secondes à se stabiliser, c'est normal ?

Oui et non. J'ai le même résultat mais je n'ai pas réussi à corriger ce comportement. Il faut donc attendre quelques secondes avant que les relevés de mesures soient correctes. Ça n'est pas vraiment génant mais ça pourrait être optimisé.

5. Conclusion

Bravo, vous avez calibré votre capteur ! Vous êtes maintenant capables d'effectuer des mesures au 100ème de degré près !

Dans le prochain article nous verrons comment utiliser une radiocommande avec un Arduino. En attendant, vous pouvez vous amuser avec votre capteur, ne serait-ce que pour vérifier que vos meubles sont bien posés cheeky

 

A bientôt sur fire-DIY !

Sources: [1] [2] [3] [4] [5] [6]

Vos réactions (8) :

AxelLaurent

Bonjour,
ou avez vous acheter votre shield, je n'en trouve pas des biens

18/12/2017 à 22:14

lobodol

Salut, je ne l'ai pas acheté, je l'ai fait

20/12/2017 à 09:20

caways

Bonjour,

Votre projet avance et m'intéresse de plus en plus.
Vivement la suite.
Quelle va être la prochaine étape : asservissement de la position ?

Bonne continuation et merci pour le partage.

Caways

01/02/2018 à 13:34

lobodol

Salut @caways, content que ça te plaise :)
Le prochain article est en cours d'écriture. Terminé à 90% donc il devrait bientôt voir le jour.
L'asservissement sera le dernier chapitre de cette série, on n'y est pas encore tout à fait ;)

02/02/2018 à 10:01

Ricoco

Très belle description étape par étape, pour moi la partie programme c’est vraiment du chinois je suis largué.

13/02/2018 à 22:13

doe38

Bonjour, et mille félicitations pour ces 6 premiers articles.
Je développe un projet de nano-blimp (emport max de 50 g pour tout compris 4 moteurs, batterie 3V7, etc) avec une carte promini et un MPU6050, un récepteur graupner GR12sh.
J'ai eu deux problèmes avec les codes du chapitre 5. Pour la mise au point de mon code, j'utilise une carte uno.
1) calibration des offsets : le code fonctionne très bien la première fois, mais si je rentre les six valeurs indiquées dans les offset du code, la calibration devient extrêmement lente (> à 5 min). Pour revenir à une vitesse normale de calibration (< 30 s), il faut que je laisse les 3 offsets de gyro à 0. Avec cela, les 6 valeurs indiquées restent quand même très proche des valeurs attendues, toutes à zéro sauf la gravité.
2)valeurs YPR : le code fonctionne, mais il y a beaucoup de dérive sur la valeur du lacet Y quand on ramène le gyro dans la position initiale.
En suivant les références du site arduino, j'ai modifié l'instruction attachinterrupt avec les trois lignes de code suivantes :
* #define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards
* pinMode(INTERRUPT_PIN, INPUT); //dans le setup
* attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
Avec cette modification, il semble y avoir beaucoup moins de dérive lors des retours à la position initiale.
Merci beaucoup pour votre travail extraordinaire.
Salutations
Denis

17/02/2018 à 16:48

tech68

bonjour, je narrive pas a copier les répertoires Arduino/I2Cdev et Arduino/MPU6050,je dois prendre juste le fichier.h dedans? et pour le <wire.h> présent en début de programme il ne faut rien télécharger?

18/02/2018 à 16:59

mts

Bonjour,
Je viens de débuter la programmation Arduino et je souhaite implémenter ce même accéléromètre. Votre programme m'est d'une grande aide ! Cependant quand je l'exécute je ne reçois en sortie que les vraies valeurs ( c'est à dire obtenues en prenant en compte l'offset) du gyroscope et pas celles provenant de l'accéléromètre.. Pourriez-vous m'expliquer pourquoi je ne parviens pas à afficher ces valeurs s'il vous plaît ?
Merci d'avance et encore bravo pour ce tuto !
Cordialement.

07/03/2018 à 17:12

Vous avez besoin d'aide ? Utilisez le Forum plutôt que les commentaires.

Un commentaire ?

* Champs obligatoires