Roll, Pitch e Yaw com MPU6050 – Arduino

Tempo de leitura: 4 minutes

Um corpo pode girar no espaço ao longo de 3 eixos, referindo-se à dinâmica aplicada a aeronaves, falamos mais propriamente de roll (Roll), pitch (Pitch) e yaw (Yaw). Neste novo artigo sobre o acelerômetro/giroscópio MPU6050 irei falar sobre como é possível obter esses ângulos (Roll, Pitch e Yaw) usando o Arduino com uma fórmula simples.

Com um acelerômetro, os ângulos de rotação e inclinação podem ser determinados usando as seguintes fórmulas:

Ângulo de inclinação

Ângulo de rotação

Já para determinar o ângulo de guinada (Yaw) será necessário usar o giroscópio. O giroscópio é útil para medir a velocidade angular dos três eixos em graus/seg. Ao adquirir diferentes valores da velocidade angular do eixo Z do giroscópio para cada período de 1 segundo, será possível obter os graus de rotação em torno do eixo Z. O código utilizado para este fim é mostrado a seguir .

if (GyroAsseZ > 1 || GyroAsseZ < -1) {
  GyroAsseZ  /= 1000;
  yaw += GyroAsseZ;
}
delay(1);

Lista de materiais:

Arduino UNO (ou outro, veja a nota abaixo)
Módulo GY-521

ProtoBoard

MPU6050ARDUINO
VCC+3.3V
GNDGND
SDAA4
SCLA5

Para maiores esclarecimentos sobre as conexões a serem feitas, recomendo que você leia meu artigo sobre a IMU MPU6050.

Recentemente descobri uma biblioteca muito útil que pode facilitar (e não um pouco) o uso do MPU6050 para os menos experientes.

Em seguida, o sketch (sem o uso de bibliotecas externas) a ser carregado no Arduino, neste caso apenas os ângulos de Roll e Pitch serão calculados.

Desenhe sem usar a biblioteca

// Roll e Pitch com MPU6050

#include <SPI.h>
#include <Wire.h>
#define MPU 0x68  //Endereço I2C do MPU-6050

double AcX, AcY, AcZ;
int Pitch, Roll;

void setup() {
  Serial.begin(9600);
  init_MPU(); //Inicialização MPU6050
}

void loop() {
  FunctionsMPU(); // Eu adquiro eixos AcX, AcY, AcZ.

  Roll = FunctionsPitchRoll(AcX, AcY, AcZ);   //Cálculo do ângulo de rotação
  Pitch = FunctionsPitchRoll(AcY, AcX, AcZ);  //Cálculo do ângulo de inclinação

  Serial.print("Pitch: "); Serial.print(Pitch);
  Serial.print("\t");
  Serial.print("Roll: "); Serial.print(Roll);
  Serial.print("\n");

}

void init_MPU() {
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // definido como zero (ativa o MPU-6050)
  Wire.endTransmission(true);
  delay(1000);
}

// Função para calcular os ângulos de inclinação e rotação
double FunctionsPitchRoll(double A, double B, double C) {
  double DatoA, DatoB, Value;
  DatoA = A;
  DatoB = (B * B) + (C * C);
  DatoB = sqrt(DatoB);

  Value = atan2(DatoA, DatoB);
  Value = Value * 180 / 3.14;

  return (int)Value;
}

// Função para aquisição dos eixos X, Y, Z do MPU6050
void FunctionsMPU() {
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // request a total of 14 registers
  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
}

Ao abrir o Arduino Serial Monitor é possível consultar os ângulos de Roll e Pitch.

Já os interessados ​​em usar o MPU6050 por meio de uma biblioteca, terão que baixar a biblioteca aqui e inseri-la em /Documents/Arduino/libraries/ … e fazer o upload do seguinte sketch.

 

Sketch com o uso da biblioteca

// Roll, Pitch e Yaw com MPU6050

#include <Wire.h>
#include <MPU6050.h>

MPU6050 mpu;

// Valores de pitch, roll e yaw
int pitch = 0;
int roll = 0;
float yaw = 0;

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

  Serial.println("Initialize MPU6050");

  while (!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G)) {
    Serial.println("Não foi possível encontrar um sensor MPU6050 válido, verifique a ligação!");
    delay(500);
  }

  // Calibrar o giroscópio. A calibração deve estar em repouso.
  // Se você não quiser calibrar, comente esta linha.
  mpu.calibrateGyro();

  // Defina a sensibilidade do limite. Padrão 3.
  // Se você não quiser usar o limite, comente esta linha ou defina 0.
  mpu.setThreshold(1);

  // Verifique as configurações
  checkSettings();
}

void loop() {
  // Lê valores normalizados
  Vector normAccel = mpu.readNormalizeAccel();
  Vector normGyro = mpu.readNormalizeGyro();

  // Calcular Pitch & Roll
  pitch = -(atan2(normAccel.XAxis, sqrt(normAccel.YAxis * normAccel.YAxis + normAccel.ZAxis * normAccel.ZAxis)) * 180.0) / M_PI;
  roll = (atan2(normAccel.YAxis, normAccel.ZAxis) * 180.0) / M_PI;

  // Ignore o giroscópio se nossa velocidade angular não atingir nosso limite
  if (normGyro.ZAxis > 1 || normGyro.ZAxis < -1) {
    normGyro.ZAxis /= 100;
    yaw += normGyro.ZAxis;
  }

  // Mantenha nosso ângulo entre 0-359 graus
  if (yaw < 0)
    yaw += 360;
  else if (yaw > 359)
    yaw -= 360;

  // Output
  Serial.print("Pitch = ");
  Serial.print(pitch);
  Serial.print("\tRoll = ");
  Serial.print(roll);
  Serial.print("\tYaw = ");
  Serial.print(yaw);

  Serial.println();

  delay(10);
}

void checkSettings() {
  Serial.println();

  Serial.print(" * Sleep Mode:        ");
  Serial.println(mpu.getSleepEnabled() ? "Enabled" : "Disabled");

  Serial.print(" * Clock Source:      ");
  switch (mpu.getClockSource()) {
    case MPU6050_CLOCK_KEEP_RESET:     Serial.println("Para o relógio e mantém o gerador de cronometragem reiniciado"); break;
    case MPU6050_CLOCK_EXTERNAL_19MHZ: Serial.println("PLL with external 19.2MHz reference"); break;
    case MPU6050_CLOCK_EXTERNAL_32KHZ: Serial.println("PLL with external 32.768kHz reference"); break;
    case MPU6050_CLOCK_PLL_ZGYRO:      Serial.println("PLL with Z axis gyroscope reference"); break;
    case MPU6050_CLOCK_PLL_YGYRO:      Serial.println("PLL with Y axis gyroscope reference"); break;
    case MPU6050_CLOCK_PLL_XGYRO:      Serial.println("PLL with X axis gyroscope reference"); break;
    case MPU6050_CLOCK_INTERNAL_8MHZ:  Serial.println("Internal 8MHz oscillator"); break;
  }

  Serial.print(" * Gyroscope:         ");
  switch (mpu.getScale()) {
    case MPU6050_SCALE_2000DPS:        Serial.println("2000 dps"); break;
    case MPU6050_SCALE_1000DPS:        Serial.println("1000 dps"); break;
    case MPU6050_SCALE_500DPS:         Serial.println("500 dps"); break;
    case MPU6050_SCALE_250DPS:         Serial.println("250 dps"); break;
  }

  Serial.print(" * Gyroscope offsets: ");
  Serial.print(mpu.getGyroOffsetX());
  Serial.print(" / ");
  Serial.print(mpu.getGyroOffsetY());
  Serial.print(" / ");
  Serial.println(mpu.getGyroOffsetZ());

  Serial.println();
}

Como pode ser visto na linha 20, é possível definir a escala total do DPS e a sensibilidade em G.

Para definir a escala total no DPS, basta substituir MPU6050_SCALE_2000DPS por:

  • MPU6050_SCALE_2000DPS
  • MPU6050_SCALE_1000DPS
  • MPU6050_SCALE_500DPS
  • MPU6050_SCALE_250DPS

Quanto à faixa de sensibilidade, MPU6050_RANGE_2G deve ser substituído por:

  • MPU6050_RANGE_2G
  • MPU6050_RANGE_4G
  • MPU6050_RANGE_8G
  • MPU6050_RANGE_16G

NB: Eu dividi o valor do eixo z do giroscópio por 100 (e não por 1000), pois adicionei um atraso de 10 ms, portanto 1000/10 = 100.

Abrindo o monitor serial você deve ver os três cantos. É tudo por agora.

Se você tiver alguma dúvida, não hesite em deixar um comentário. Olá a todos, até breve!