Motion LabCapteurs

Centrale inertielle MPU6050

Un accéléromètre et un gyroscope sur une carte de deux centimètres. Le visualiseur 3D vous montre ce que chaque axe mesure, et les outils vous emmènent jusqu’à un angle stable.

  1. Comprendre
  2. Voir
  3. Manipuler
  4. Programmer
  5. Résoudre
  6. Construire

En bref

PuceInvenSense MPU-6050 : accéléromètre 3 axes, gyroscope 3 axes et capteur de température, sur 16 bits
InterfaceI²C, jusqu’à 400 kHz
Adresse I²C0x68 (broche AD0 à GND ou libre) ou 0x69 (AD0 au 3,3 V)
Accéléromètre±2, ±4, ±8 ou ±16 g
Gyroscope±250, ±500, ±1000 ou ±2000 °/s
Alimentation du module3,3 V ou 5 V sur la plupart des cartes GY-521 (régulateur intégré) ; la puce elle-même fonctionne en environ 3,3 V
Ce qu’il ne fait paspas de magnétomètre : il ne donne pas de cap (yaw) absolu

Ce guide vous emmène de la lecture des axes jusqu’au calcul d’angle stable. Le visualiseur 3D vous montre en direct ce que le capteur mesure quand vous l’inclinez.

Câblage

Broche GY-521ESP32Arduino Uno / Nano
VCC3V3 (ou 5V selon le module)5V
GNDGNDGND
SCLGPIO 22A5
SDAGPIO 21A4
AD0GND (adresse 0x68)GND
INTlibre (optionnel)libre
XDA, XCLlibreslibres
  • Le MPU6050 démarre en veille : il faut écrire 0 dans le registre PWR_MGMT_1 (0x6B) avant de lire quoi que ce soit.
  • Les modules courants portent déjà les résistances de rappel de l’I²C. Si vous partagez le bus avec un écran OLED, chaque appareil garde sa propre adresse (0x68 et 0x3C).
  • XDA et XCL servent à brancher un second capteur (magnétomètre) sur la puce : laisse-les libres.

Axes

Trois axes perpendiculaires, dessinés par des flèches sur la carte : X, Y et Z (Z sort de la face du composant). Les rotations sont comptées dans le sens direct autour de chaque axe. Pose la carte à plat, composant vers le haut : l’accéléromètre lit environ +1 g sur Z (il mesure la réaction à la pesanteur, dirigée vers le haut), et 0 sur X et Y.

Visualiseur 3D

Glisse sur le modèle pour l’incliner, ou utilise les curseurs. Flèches : X rouge, Y vert, Z bleu ; la flèche grise est la pesanteur.

Accél. X
Accél. Y
Accél. Z
Gyro X
Gyro Y
Gyro Z
Roll
Pitch

Accéléromètre

L’accéléromètre mesure l’accélération propre : le mouvement plus la pesanteur. Immobile, il ne voit que la pesanteur, ce qui donne l’inclinaison (roll et pitch) mais jamais l’orientation autour de la verticale. En mouvement ou sous vibration, ce chiffre n’est plus une inclinaison fiable.

  • La valeur brute est un entier signé de 16 bits ; on la divise par la sensibilité de la plage (16 384 LSB/g en ±2 g).
  • La norme √(ax² + ay² + az²) vaut environ 1 g à l’arrêt. Si elle s’en écarte nettement, le capteur accélère : ne fais pas confiance à l’angle calculé à cet instant.

Gyroscope

Le gyroscope mesure une vitesse de rotation en degrés par seconde, sur chaque axe. Pour obtenir un angle, on l’intègre : angle += vitesse × dt. C’est réactif et lisse, mais un petit défaut constant (l’offset) s’additionne à chaque pas et l’angle dérive. La calibration et la fusion avec l’accéléromètre servent à corriger cette dérive.

Température

La puce embarque un thermomètre. La formule du constructeur : T (°C) = valeur brute ÷ 340 + 36,53. Il mesure la température de la puce, souvent plusieurs degrés au-dessus de l’air ambiant. Sert à compenser la dérive du capteur, pas à mesurer la pièce.

Convertisseur température

Plages de mesure

AccéléromètreLSB par gACCEL_CONFIG (0x1C)
±2 g16 3840x00
±4 g8 1920x08
±8 g4 0960x10
±16 g2 0480x18
GyroscopeLSB par °/sGYRO_CONFIG (0x1B)
±250 °/s1310x00
±500 °/s65,50x08
±1000 °/s32,80x10
±2000 °/s16,40x18

Une plage plus large mesure des mouvements plus violents mais perd en résolution. Choisissez la plus petite qui ne sature pas : ±2 g et ±250 °/s pour un niveau ou un robot lent, ±8 g et ±1000 °/s pour un drone ou un geste rapide. Les curseurs du visualiseur 3D vous laissent tester l’effet sur la valeur brute.

Calibration

Aucun capteur n’est parfait : à plat et immobile, les valeurs ne sont pas exactement 0, 0, +1 g pour l’accéléromètre et 0, 0, 0 pour le gyroscope. La calibration mesure ces écarts (les offsets) pour les retrancher ensuite.

  1. Pose la carte à plat, composant vers le haut, sur une surface stable. Ne touche à rien.
  2. Lis 500 mesures brutes de chaque axe et fais la moyenne.
  3. Saisis ces moyennes ci-dessous : l’outil calcule les offsets.

Calculateur d'offsets

offsets.h
// Le code apparaît ici.

Offset

Les offsets se retranchent de chaque mesure brute avant toute conversion : ax = brut_ax − AX_OFFSET. Pour l’axe Z de l’accéléromètre, la valeur de référence n’est pas 0 mais +1 g (16 384 en ±2 g), d’où le décalage calculé par l’outil. Le sketch d’angle plus bas contient déjà ces constantes.

  • Les offsets du gyroscope comptent le plus : sans eux, l’angle dérive de plusieurs degrés par minute.
  • Ils changent un peu avec la température : recalibre après quelques minutes de fonctionnement si vous cherchez la précision.

Filtrage

Les mesures brutes sont bruitées. Deux filtres simples :

  • Moyenne glissante sur N échantillons : efficace, mais retarde le signal d’environ (N − 1) ÷ 2 échantillons.
  • Moyenne exponentielle : y = α × x + (1 − α) × y. Un α petit lisse fortement mais réagit lentement.

Comparer les filtres

brut · moyenne glissante · moyenne exponentielle

Retard glissante
Retard exponentielle

Calcul d’angle

Avec l’accéléromètre, à l’arrêt : roll = atan2(ay, az) et pitch = atan2(−ax, √(ay² + az²)). Avec le gyroscope : angle = angle + vitesse × dt. Le calculateur ci-dessous applique les deux.

Calculateur d'angle

Roll
Pitch
Norme |a|

Intégration du gyroscope

Nouvel angle

Fusion des données

L’accéléromètre est juste en moyenne mais bruité ; le gyroscope est lisse mais dérive. Le filtre complémentaire combine les deux : angle = α × (angle + gyro × dt) + (1 − α) × angle_accéléromètre. Avec α proche de 1, on fait confiance au gyroscope à court terme, et l’accéléromètre ramène lentement l’angle dans le vrai. Pour aller plus loin : le filtre de Madgwick ou de Kalman.

Simulation du filtre complémentaire

angle réel · accéléromètre seul · gyroscope seul · fusion

Code prêt à l’emploi

Lecture brute (sans bibliothèque)

Ce sketch réveille le capteur, choisit les plages et affiche accélération, rotation et température.

mpu6050_raw.inoTélécharger
/*
 * MPU6050 : lecture brute sans bibliothèque — Kaihatsu Lab
 * Lit accélération, température et rotation directement dans les registres, et affiche le résultat converti.
 * Adresse I2C : 0x68 (0x69 si la broche AD0 est reliée au 3,3 V).
 */
#include <Wire.h>

const uint8_t MPU_ADDR = 0x68;
const int PIN_SDA = 21;
const int PIN_SCL = 22;

// Sensibilités pour les plages choisies plus bas : ±2 g -> 16384 LSB/g, ±250 °/s -> 131 LSB/(°/s)
const float LSB_PER_G   = 16384.0;
const float LSB_PER_DPS = 131.0;

void writeReg(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

int16_t read16() {                        // deux octets, poids fort en premier
  uint8_t hi = Wire.read();
  uint8_t lo = Wire.read();
  return (int16_t)((hi << 8) | lo);
}

bool readAll(int16_t &ax, int16_t &ay, int16_t &az, int16_t &tmp, int16_t &gx, int16_t &gy, int16_t &gz) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);                       // ACCEL_XOUT_H : début des 14 octets de mesure
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom(MPU_ADDR, (uint8_t)14) != 14) return false;
  ax = read16(); ay = read16(); az = read16();
  tmp = read16();
  gx = read16(); gy = read16(); gz = read16();
  return true;
}

void setup() {
  Serial.begin(115200);
  Wire.begin(PIN_SDA, PIN_SCL);
  Wire.setClock(400000);

  writeReg(0x6B, 0x00);                   // PWR_MGMT_1 : sort du mode veille (le capteur démarre endormi)
  writeReg(0x1C, 0x00);                   // ACCEL_CONFIG : ±2 g
  writeReg(0x1B, 0x00);                   // GYRO_CONFIG  : ±250 °/s
  writeReg(0x1A, 0x03);                   // CONFIG : filtre passe-bas interne à environ 44 Hz

  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x75);                       // WHO_AM_I : 0x68 attendu
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_ADDR, (uint8_t)1);
  Serial.print(F("WHO_AM_I = 0x"));
  Serial.println(Wire.read(), HEX);
}

void loop() {
  int16_t ax, ay, az, tmp, gx, gy, gz;
  if (!readAll(ax, ay, az, tmp, gx, gy, gz)) {
    Serial.println(F("Lecture impossible : verifie SDA, SCL et l'adresse"));
    delay(500);
    return;
  }
  Serial.print(F("acc (g) "));
  Serial.print(ax / LSB_PER_G, 2); Serial.print(' ');
  Serial.print(ay / LSB_PER_G, 2); Serial.print(' ');
  Serial.print(az / LSB_PER_G, 2);
  Serial.print(F(" | gyro (deg/s) "));
  Serial.print(gx / LSB_PER_DPS, 1); Serial.print(' ');
  Serial.print(gy / LSB_PER_DPS, 1); Serial.print(' ');
  Serial.print(gz / LSB_PER_DPS, 1);
  Serial.print(F(" | temp "));
  Serial.print(tmp / 340.0 + 36.53, 1);
  Serial.println(F(" C"));
  delay(100);
}

Angles roll et pitch

Filtre complémentaire à 98 % gyroscope. Colle vos offsets dans les constantes en haut du fichier.

mpu6050_angle.inoTélécharger
/*
 * MPU6050 : angles roll et pitch par filtre complémentaire — Kaihatsu Lab
 * Le gyroscope donne la réactivité, l'accéléromètre corrige la dérive.
 * Copie ici les offsets trouvés avec l'outil de calibration (capteur posé à plat, immobile).
 * Le yaw n'est pas calculé : sans magnétomètre, il dérive.
 */
#include <Wire.h>

const uint8_t MPU_ADDR = 0x68;

const float LSB_PER_G   = 16384.0;        // ±2 g
const float LSB_PER_DPS = 131.0;          // ±250 °/s
const float ALPHA       = 0.98;           // poids du gyroscope (0 à 1)

// Offsets bruts à retrancher (valeurs de l'outil de calibration)
const int16_t AX_OFFSET = 0, AY_OFFSET = 0, AZ_OFFSET = 0;
const int16_t GX_OFFSET = 0, GY_OFFSET = 0, GZ_OFFSET = 0;

float roll = 0, pitch = 0;
uint32_t lastUs = 0;

void writeReg(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

int16_t read16() {
  uint8_t hi = Wire.read();
  uint8_t lo = Wire.read();
  return (int16_t)((hi << 8) | lo);
}

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  Wire.setClock(400000);
  writeReg(0x6B, 0x00);
  writeReg(0x1A, 0x03);
  delay(100);
  lastUs = micros();
}

void loop() {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);
  if (Wire.endTransmission(false) != 0) return;
  if (Wire.requestFrom(MPU_ADDR, (uint8_t)14) != 14) return;
  int16_t ax = read16() - AX_OFFSET;
  int16_t ay = read16() - AY_OFFSET;
  int16_t az = read16() - AZ_OFFSET;
  read16();                               // température ignorée
  int16_t gx = read16() - GX_OFFSET;
  int16_t gy = read16() - GY_OFFSET;
  read16();                               // gz inutile pour roll et pitch

  uint32_t now = micros();
  float dt = (now - lastUs) / 1e6;
  lastUs = now;

  float axg = ax / LSB_PER_G, ayg = ay / LSB_PER_G, azg = az / LSB_PER_G;
  float rollAcc  = atan2(ayg, azg) * RAD_TO_DEG;
  float pitchAcc = atan2(-axg, sqrt(ayg * ayg + azg * azg)) * RAD_TO_DEG;

  roll  = ALPHA * (roll  + (gx / LSB_PER_DPS) * dt) + (1.0 - ALPHA) * rollAcc;
  pitch = ALPHA * (pitch + (gy / LSB_PER_DPS) * dt) + (1.0 - ALPHA) * pitchAcc;

  Serial.print(F("roll "));
  Serial.print(roll, 1);
  Serial.print(F("  pitch "));
  Serial.println(pitch, 1);
  delay(10);
}

Des bibliothèques existent aussi (Adafruit MPU6050, MPU6050_light), qui cachent les registres derrière getEvent() ou update(). Lire les registres soi-même, comme ici, aide à comprendre ce que le capteur envoie.

Dépannage

SymptômeCause probableSolution
Toutes les valeurs à 0Le capteur est resté en veilleÉcris 0 dans PWR_MGMT_1 (0x6B) au démarrage.
Lecture impossible, ou erreur I²CSDA/SCL inversés, mauvaise adresse, mauvaise alimentationLance un scanner I²C (voir le guide SSD1306) : attends 0x68 ou 0x69.
WHO_AM_I ne renvoie pas 0x68Puce compatible différente (MPU-6500 par exemple)Le code de lecture des registres reste presque identique, mais vérifie la fiche de votre puce.
L’angle dérive sans arrêtOffsets du gyroscope non corrigésCalibre le capteur à plat et immobile, puis utilise le filtre complémentaire.
Az ne vaut pas 1 g à platOffset ou mauvaise plageVérifie 16 384 LSB/g en ±2 g et retranche l’offset de Z.
Les angles sont faux pendant un mouvementL’accéléromètre voit le mouvement, pas seulement la pesanteurBaisse la confiance dans l’accéléromètre (α plus proche de 1) pendant les mouvements.
Valeurs très bruitéesFiltre interne désactivé, vibrationsActive le filtre interne (registre CONFIG), ajoute une moyenne, fixe mieux la carte.
Le yaw dérive toujoursAucun capteur de cap absoluAjoute un magnétomètre, ou accepte la dérive lente du gyroscope.

Projets

Niveau à bulle : MPU6050 et SSD1306

La bulle affichée sur l’écran OLED se déplace du côté le plus haut, comme sur un vrai niveau. Incline la carte virtuelle avec les curseurs. Le sketch réutilise la lecture des registres de ce guide et l’affichage du guide SSD1306.

La carte est réputée à plat quand les deux angles sont proches de 0 : la bulle se centre dans la cible.

level_bubble.inoTélécharger
/*
 * Niveau à bulle numérique — Kaihatsu Lab
 * MPU6050 + écran OLED SSD1306, tous deux sur le même bus I2C (SDA 21, SCL 22).
 * La bulle va du côté le plus haut, comme sur un vrai niveau. Bibliothèques : Adafruit GFX et SSD1306.
 *
 * Monte le MPU6050 à plat avec ses axes dans le même sens que l'écran : X vers la droite, Y vers le haut.
 * Chiffres affichés : P = pitch (autour de Y), R = roll (autour de X).
 */
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_W  128
#define SCREEN_H  64
#define OLED_ADDR 0x3C
const uint8_t MPU_ADDR = 0x68;

Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, -1);

const int   CX = 40, CY = 32, R_OUT = 28;   // cadran à gauche, chiffres à droite
const float PIXELS_PER_DEG = 2.0;           // sensibilité de la bulle
float roll = 0, pitch = 0;                  // degrés, lissés

int16_t read16() {
  uint8_t hi = Wire.read();
  uint8_t lo = Wire.read();
  return (int16_t)((hi << 8) | lo);
}

bool readAngles(float &r, float &p) {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);
  if (Wire.endTransmission(false) != 0) return false;
  if (Wire.requestFrom(MPU_ADDR, (uint8_t)6) != 6) return false;
  float ax = read16() / 16384.0;
  float ay = read16() / 16384.0;
  float az = read16() / 16384.0;
  r = atan2(ay, az) * RAD_TO_DEG;
  p = atan2(-ax, sqrt(ay * ay + az * az)) * RAD_TO_DEG;
  return true;
}

void setup() {
  Wire.begin(21, 22);
  Wire.setClock(400000);
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x6B);
  Wire.write(0x00);
  Wire.endTransmission();
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
}

void loop() {
  float r, p;
  if (readAngles(r, p)) {
    roll  += 0.2 * (r - roll);              // lissage exponentiel : la bulle ne tremble pas
    pitch += 0.2 * (p - pitch);
  }

  float bx = CX - pitch * PIXELS_PER_DEG;   // tangage positif : le côté droit descend, la bulle va à gauche
  float by = CY - roll * PIXELS_PER_DEG;    // roulis positif : le haut monte, la bulle monte
  float dx = bx - CX, dy = by - CY;
  float dist = sqrt(dx * dx + dy * dy), maxD = R_OUT - 5;
  if (dist > maxD) { bx = CX + dx * maxD / dist; by = CY + dy * maxD / dist; }

  display.clearDisplay();
  display.drawCircle(CX, CY, R_OUT, SSD1306_WHITE);       // cadran
  display.drawCircle(CX, CY, 7, SSD1306_WHITE);           // cible centrale
  display.drawFastHLine(CX - R_OUT, CY, 6, SSD1306_WHITE);
  display.drawFastHLine(CX + R_OUT - 5, CY, 6, SSD1306_WHITE);
  display.fillCircle((int)bx, (int)by, 5, SSD1306_WHITE); // bulle

  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(82, 14);
  display.print(F("P "));
  display.print(pitch, 1);
  display.setCursor(82, 34);
  display.print(F("R "));
  display.print(roll, 1);
  display.display();
  delay(20);
}

Idées à construire

  • Stabilisateur de caméra ou de plateau avec deux servos qui compensent les angles.
  • Détecteur de chute ou de choc : réagit quand |a| dépasse un seuil.
  • Manette de jeu ou télécommande par inclinaison, en radio avec le nRF24L01+.
  • Mesure d’angle d’une porte, d’un bras articulé ou d’une rampe.

Le PCB et les composants pour reproduire les projets arriveront avec la boutique.

Recevoir les prochaines sorties

Un email quand un projet, un guide, un PCB ou un kit est publié. Pas de spam, désinscription à tout moment.

Être prévenu

Recevez un email quand une nouveauté est publiée.