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.
En bref
| Puce | InvenSense MPU-6050 : accéléromètre 3 axes, gyroscope 3 axes et capteur de température, sur 16 bits |
|---|---|
| Interface | I²C, jusqu’à 400 kHz |
| Adresse I²C | 0x68 (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 module | 3,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 pas | pas 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-521 | ESP32 | Arduino Uno / Nano |
|---|---|---|
| VCC | 3V3 (ou 5V selon le module) | 5V |
| GND | GND | GND |
| SCL | GPIO 22 | A5 |
| SDA | GPIO 21 | A4 |
| AD0 | GND (adresse 0x68) | GND |
| INT | libre (optionnel) | libre |
| XDA, XCL | libres | libres |
- 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).
XDAetXCLservent à 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.
Le modèle intègre le gyroscope et se recale sur l’accéléromètre (filtre complémentaire, α = 0,98).
- 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ètre | LSB par g | ACCEL_CONFIG (0x1C) |
|---|---|---|
| ±2 g | 16 384 | 0x00 |
| ±4 g | 8 192 | 0x08 |
| ±8 g | 4 096 | 0x10 |
| ±16 g | 2 048 | 0x18 |
| Gyroscope | LSB par °/s | GYRO_CONFIG (0x1B) |
|---|---|---|
| ±250 °/s | 131 | 0x00 |
| ±500 °/s | 65,5 | 0x08 |
| ±1000 °/s | 32,8 | 0x10 |
| ±2000 °/s | 16,4 | 0x18 |
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.
- Pose la carte à plat, composant vers le haut, sur une surface stable. Ne touche à rien.
- Lis 500 mesures brutes de chaque axe et fais la moyenne.
- Saisis ces moyennes ci-dessous : l’outil calcule les offsets.
Calculateur d'offsets
// 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 : 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 : 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ôme | Cause probable | Solution |
|---|---|---|
| Toutes les valeurs à 0 | Le capteur est resté en veille | Écris 0 dans PWR_MGMT_1 (0x6B) au démarrage. |
| Lecture impossible, ou erreur I²C | SDA/SCL inversés, mauvaise adresse, mauvaise alimentation | Lance un scanner I²C (voir le guide SSD1306) : attends 0x68 ou 0x69. |
| WHO_AM_I ne renvoie pas 0x68 | Puce 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êt | Offsets du gyroscope non corrigés | Calibre le capteur à plat et immobile, puis utilise le filtre complémentaire. |
| Az ne vaut pas 1 g à plat | Offset ou mauvaise plage | Vérifie 16 384 LSB/g en ±2 g et retranche l’offset de Z. |
| Les angles sont faux pendant un mouvement | L’accéléromètre voit le mouvement, pas seulement la pesanteur | Baisse la confiance dans l’accéléromètre (α plus proche de 1) pendant les mouvements. |
| Valeurs très bruitées | Filtre interne désactivé, vibrations | Active le filtre interne (registre CONFIG), ajoute une moyenne, fixe mieux la carte. |
| Le yaw dérive toujours | Aucun capteur de cap absolu | Ajoute 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.
/*
* 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.