O acelerômetro está presente em diversos dispositivos nos dias de hoje. No celular ele responsável por informar a rotação, assim como em alguns joysticks. Portanto, vamos aprender a utilizar o acelerômetro MPU6050 para podermos fazer projetos interessantes.

O que é um acelerômetro

O acelerômetro é um dispositivo capaz de medir a aceleração de um objeto. Isto é, ele sabe qual é a direção e sentido da aceleração do objeto.

Os smartphones utilizam um acelerômetro em conjunto com um giroscópio para monitorar a orientação da tela. Outros aparelhos utilizam o acelerômetro para diversas funções, como o controle do wii que eu já expliquei neste post.

Existem sensores que utilizam diferentes propriedades físicas para fazer essas medições. Um tipo de acelerômetro é o piezoelétrico. Dentro desse tipo, existe um cristal que sofre uma força quando o objeto se movimenta. E, de acordo com as leis de Newton, força é igual a massa vezes a aceleração. Com isso, uma carga elétrica é gerada proporcional à aceleração. E durante a leitura dos sinais, essa carga elétrica é traduzida de volta para aceleração.

Se quiser entender melhor sobre a piezoeletricidade, leia o post sobre.

MPU6050

O MPU6050 (imagem abaixo) é um acelerômetro e giroscópio capaz de medir a aceleração e rotação nos três eixos coordenados (x,y,z).

Acelerômetro MPU 6050

Ele se comunica por I2C, que é um tipo de comunicação que utiliza apenas 2 canais (fios). Nestes dois canais, é possível conectar uma quantidade muito grande de dispositivos, e cada um utiliza um endereço específico.

Um desses canais (um fio) é responsável por transmitir e receber dados. O outro canal (fio) é responsável por fornecer o clock (controla a velocidade e sincronismo da comunicação). No caso da placa acima, o SCL é o pino do clock e o SDA o pino de transmissão e recepção dos dados.

Esse sensor possui um buffer de 1024 bytes que armazena os dados das medições. Se os dados forem medidos, o MPU coloca os valores dentro do buffer e envia um sinal pelo pino INT (interrupção).

Por meio do pino AD0 é possível mudar o endereço da placa, selecionando 0x68 ou 0x69 (essa é a forma como os endereços aparecem). Com isso, é possível conectar dois desses sensores ao mesmo tempo e comunicar com eles sem dar conflito. Pois, senão, os dois teriam o mesmo endereço e assim ocorreriam conflitos na comunicação. O padrão é utilizar o endereço 0x68, que é quando o pino AD0 está recebendo sinal de nível baixo (GND).

Por fim, os pinos XDA e XCL servem para conectar outro dispositivo que se comunica com I2C e controlá-lo a partir do MPU 6050 (complicado de fazer).

Vale mencionar que os pinos do MPU6050 tem um limite máximo de tensão de 3,46V. Exceto o Vcc, pois o circuito possui um regulador de tensão de 5V para 3,3V. Então, é preciso ter cuidado na hora de montar o circuito.

Circuito com um MPU6050

Vou utilizar um Arduino UNO, o qual opera em 5V, mas vou utilizar também um conversor de nível lógico para adequar os valores de tensão para o sensor. Se você for usar um Arduino que opere em 3,3V, poderá fazer a montagem sem o conversor de nível ligando os pinos diretamente.

Enfim, adiante está a ligação completa do sensor para evitar qualquer problema durante a utilização dele.

O Vcc conecta no 5V e o GND no GND. E é preciso alimentar o conversor de nível lógico também. No lado “High” dele (HV), alimente com 5V e GND e, no lado “Low” dele (LV), alimente com 3,3V e GND.

Além disto, conecte um pino bidirecional (TXI) no pino SDA do MPU6050 e o outro lado deste pino (TXO) no pino analógico A4. Agora, conecte um pino unidirecional (RXO – pode ser bidirecional também) no pino SCL do sensor e o outro lado deste pino (RXI) no pino analógico A5. Os pinos analógicos 4 e 5 do Arduino são justamente os pinos utilizados para comunicação I2C.

Por fim, o pino AD0 não precisa ser conectado no GND, pois por padrão o endereço I2C do MPU6050 já é 0x68. Mas ele pode ser alterado ligando o pino em 3,3V.

Observação

Quando comprei o MPU6050 cheguei a usar o Arduino UNO para comunicar com ele. Entretanto, devido a falta de atenção e de informação, não sabia que os pinos do sensor eram sensíveis ao 5V, então realizei a ligação abaixo:

O circuito mostrado abaixo não é recomendável.

circuito com acelerômetro Arduino

O sensor funcionou, consegui ler os dados dele e controlá-lo sem problemas. Pelo menos era o que parecia, mas a comunicação parava de responder depois de um tempo que o programa estava rodando.

Gastei horas pesquisando o problema e até achei que era mau contato da ligação. E fui descobrir tempos mais tarde que o problema era de incompatibilidade de tensão dos pinos. Mas até que eu descobrisse, utilizei bastante o MPU6050 com a ligação acima e ele continuou funcionando. Não sei se ele estragou alguma coisa, pois não tenho outro para comparar os sinais.

Enfim, se você for seguir o mesmo caminho, siga por sua conta e risco.

Programação

Código base

Peguei o código de exemplo deste site e adicionei comentários para facilitar o entendimento de como o Arduino extrai os dados do sensor. Basicamente o Arduino inicia a comunicação no endereço do sensor, faz um “pedido” de leitura de 14 registradores (contem os dados medidos) e armazena em variáveis. Por fim, o código manda imprimir cada variável separadamente.

Para ter uma boa visualização dos dados, abra o plotter serial (gráfico) ao invés do monitor serial.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Codigo adaptado de: Usuário do Arduino JohnChi

#include<Wire.h>//Biblioteca para comunicação I2C

const int MPU_addr=0x68; //Endereço do sensor

int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; //Variaveis para pegar os valores medidos

void setup(){
  Wire.begin(); //Inicia a comunicação I2C
  Wire.beginTransmission(MPU_addr); //Começa a transmissao de dados para o sensor
  Wire.write(0x6B); // registrador PWR_MGMT_1
  Wire.write(0); // Manda 0 e "acorda" o MPU 6050
  Wire.endTransmission(true);

  Serial.begin(9600); //Inicia a comunicaçao serial (para exibir os valores lidos)
}
void loop(){
  Wire.beginTransmission(MPU_addr); //Começa a transmissao de dados para o sensor
  Wire.write(0x3B); // registrador dos dados medidos (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true); // faz um "pedido" para ler 14 registradores, que serão os registrados com os dados medidos
  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(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read(); // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read(); // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read(); // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read(); // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

  //Agora escreve os valores no monitor serial
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53); //Equação da temperatura em Cº de acordo com o datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);
  delay(333);
}

Interpretação dos valores medidos

Os valores lidos estarão sempre na faixa de -32768 e +32767. Agora, o que esses valores irão representar depende da escala escolhida. A escala pode ser escolhida alterando os registradores de configuração do sensor (consultar datasheet).

A escala padrão da aceleração é de +/- 2g, sendo que g é a força g (equivale a aproximadamente 9,8 m/s²). E para a rotação, a escala padrão é de +/- 250 graus/segundo. Portanto, o valor de +32767 para a aceleração, representa 2g e -32768 representa -2g. Os valores intermediários são lineares. Isto é, a metade de +32767 representa 1g.

Repare que também há uma leitura de temperatura, mas ela, dentro do código, já está sendo convertida para graus celsius.

Obs.1: Se o sensor estiver para cima apoiado em uma mesa horizontal, a aceleração em z será cerca de 16300, que corresponde a 1g. Isto se deve à gravidade da terra.

Obs.2: Como na vida real nada é perfeito, as leituras com o acelerômetro parado em uma mesa horizontal terão pequenas variações devido à vibrações que vêm de todo lugar possível (carro passando na rua, alguém esbarrando na mesa, a própria vibração do ar etc).

Dois sensores MPU6050

Circuito

A ligação com os dois acelerômetros será exatamente a mesma, com a diferença que o AD0 de um fica conectado no GND (nível baixo) e o AD0 do outro fica no 3,3v (nível alto). Isso serve para garantir que o endereço I2C dos dois seja diferente. No primeiro caso é 0x68 e no segundo 0x69.

Circuito com dois MPU6050

Circuito

Leia os comentários para entender os passos do código.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<Wire.h>//Biblioteca para comunicação I2C

const int MPU1 = 0x68; // Endereço do sensor 1
const int MPU2 = 0x69; // Endereço do sensor 2

int16_t AcX1,AcY1,AcZ1,Tmp1,GyX1,GyY1,GyZ1; //Variaveis para pegar os valores medidos
int16_t AcX2,AcY2,AcZ2,Tmp2,GyX2,GyY2,GyZ2; //Variaveis para pegar os valores medidos

void setup(){
  Wire.begin(); // Inicia a comunicação I2C
  Wire.beginTransmission(MPU1); //Começa a transmissao de dados para o sensor 1
  Wire.write(0x6B); // registrador PWR_MGMT_1
  Wire.write(0); // Manda 0 e "acorda" o sensor 1
  Wire.endTransmission(true);

  Wire.beginTransmission(MPU2); //Começa a transmissao de dados para o sensor 2
  Wire.write(0x6B); // registrador PWR_MGMT_1
  Wire.write(0); // Manda 0 e "acorda" o sensor 2
  Wire.endTransmission(true);

  Serial.begin(9600); //Inicia a comunicaçao serial (para exibir os valores lidos)
}
void loop(){
  // Sensor 1
  Wire.beginTransmission(MPU1); //Começa a transmissao de dados do sensor 1
  Wire.write(0x3B); // Registrador dos dados medidos (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU1,14,true); // Faz um "pedido" para ler 14 registradores, que serão os registrados com os dados medidos
  AcX1 = Wire.read()<<8|Wire.read();
  AcY1 = Wire.read()<<8|Wire.read();
  AcZ1 = Wire.read()<<8|Wire.read();
  Tmp1 = Wire.read()<<8|Wire.read();
  GyX1 = Wire.read()<<8|Wire.read();
  GyY1 = Wire.read()<<8|Wire.read();
  GyZ1 = Wire.read()<<8|Wire.read();
  Wire.endTransmission(true); // Se der erro tente tirar esta linha

  // Sensor 2
  Wire.beginTransmission(MPU2); // Começa a transmissao de dados do sensor 2
  Wire.write(0x3B); // Registrador dos dados medidos (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU2,14,true); // Faz um "pedido" para ler 14 registradores, que serão os registrados com os dados medidos
  AcX2 = Wire.read()<<8|Wire.read();
  AcY2 = Wire.read()<<8|Wire.read();
  AcZ2 = Wire.read()<<8|Wire.read();
  Tmp2 = Wire.read()<<8|Wire.read();
  GyX2 = Wire.read()<<8|Wire.read();
  GyY2 = Wire.read()<<8|Wire.read();
  GyZ2 = Wire.read()<<8|Wire.read();
  Wire.endTransmission(true); // Se der erro tente tirar esta linha

  // Agora escreve os valores no monitor serial
  // Sensor 1
  Serial.print("AcX1 = "); Serial.print(AcX1);
  Serial.print(" | AcY1 = "); Serial.print(AcY1);
  Serial.print(" | AcZ1 = "); Serial.print(AcZ1);
  Serial.print(" | Tmp1 = "); Serial.print(Tmp1/340.00+36.53); // Equação da temperatura em Cº de acordo com o datasheet
  Serial.print(" | GyX1 = "); Serial.print(GyX1);
  Serial.print(" | GyY1 = "); Serial.print(GyY1);
  Serial.print(" | GyZ1 = "); Serial.println(GyZ1);
  // Sensor 2
  Serial.print("AcX2 = "); Serial.print(AcX2);
  Serial.print(" | AcY2 = "); Serial.print(AcY2);
  Serial.print(" | AcZ2 = "); Serial.print(AcZ2);
  Serial.print(" | Tmp2 = "); Serial.print(Tmp2/340.00+36.53); // Equação da temperatura em Cº de acordo com o datasheet
  Serial.print(" | GyX2 = "); Serial.print(GyX2);
  Serial.print(" | GyY2 = "); Serial.print(GyY2);
  Serial.print(" | GyZ2 = "); Serial.println(GyZ2);

  delay(500);
}

Aplicações

O limite de aplicações é a imaginação, mas posso citar alguns exemplos interessantes:

  • Fazer um algoritmo para detecção de gestos utilizando o acelerômetro em uma luva.
  • Com a aplicação acima é possível ligar uma lâmpada, um ventilador ou qualquer outra coisa apenas com gestos.
  • Detectar a rotação de um objeto.
  • Detectar se o objeto está em queda.
  • Medir a distância deslocada ou a velocidade com base na aceleração.

Descobrindo distância com acelerômetro