Você está na página 1de 26

MEMS (Parte 1) - Guia para usar o acelermetro ADXL345

Recentemente eu estive jogando com mdulo barato GY-80, o mdulo mais precisamente 10DOF
com acelermetro, giroscpio, magnetmetro e barmetro. Eventualmente, eu vou escrever como
usar todos os quatro deles. Vou comear com acelermetro (acelerao). Este guia pode
potencialmente ser usado para interface maioria dos accels MEMS, e, definitivamente, como um
guia de como interpretar os dados provenientes da acelerao, no s de MEMS, mas tambm como
usar os dados provenientes de um Smartphone, wiimote etc. Eles so basicamente os mesma coisa.
No entanto, eu no vou estar a descrever caractersticas como sensor de toque e sensor de toque
duplo, esta ser apenas uma introduo sobre o acelermetro cru.
O que acelermetro de qualquer maneira?
Resposta curta, um dispositivo que mede a acelerao em uma direo especfica da gravidade e do
movimento. No nosso caso ADXL345 uma acelerao de 3 eixos, basicamente, ele pode medir a
acelerao em 3 direes simultaneamente. Na Terra, acelermetro quando colocado sobre uma
superfcie plana sempre medir 9,81m / s2.In maioria dos casos, voc encontrar que a acelerao
medida em g-foras, que basicamente a acelerao sentida como peso. Obviamente, na superfcie
da Terra e em condies de descanso que experimentamos 1G.
Na maioria dos casos, a acelerao usado como uma quantidade de vector, que pode ser usado
para detectar a orientao do dispositivo, mais precisamente pitch and roll. Porque quando voc
ligar o dispositivo, o componente 1G distribudo entre os 3 eixos. Com matemtica vector
simples, podemos calcular o ngulo do dispositivo.
inicializar ADXL345
Neste tutorial, eu vou estar usando o protocolo I2C para comunicar com o ADXL345. O princpio
bsico da comunicao com o dispositivo como se segue. Primeiro, voc enviar o endereo de
registrar, voc quer quer ler ou escrever. Em seguida, enviar os novos valores para escrever no

registo correspondente, ou solicitar quantidade especfica de bytes do dispositivo.


A inicializao do ADXL345, consiste em trs coisas, permitindo que o modo de medio em
POWER_CTL registo, especificando o formato de dados e configurar o deslocamento em registos OFSX, OFSY, OFSZ.
Para iniciar as medies que s precisa definir bit 3 no registo POWER_CTL, basicamente, ns
apenas escrever 0x08 a ele, assim:

writeTo(ADXL345_POWER_CTL, 0x08);

Depois de termos iniciado com xito a nossa ADXL345, agora podemos especificar o formato de
dados em outras palavras, a resoluo das medies. Veja a tabela abaixo para o DATE_FORMAT
registo

No nosso caso, estamos apenas interessados em 3 bits - D3, D1 e D0. Quando FULL_RES bit est
ativado, o dispositivo ser executado em modo de resoluo completa, em outras palavras, ele
sempre ir manter 4mg / LSB. No importa o intervalo for especificado, um pouco representar
4mg de acelerao. Se ele no est habilitado a ADXL345 ser executado no modo de 10 bits, e os
bits de gama ir determinar quantos mg / LSB.
Os bits Faixa basicamente define o intervalo das medies, consulte a tabela abaixo para obter as
configuraes possveis:

See table
below for
mG/LSB
in different
range

configurations:

No meu caso eu decidi que eu vou estar usando gama 16 g em resoluo mxima, de acordo com
minhas preferncias e da mesa que eu forneci acima, I'have para enviar 0x0B ao DATE_FORMAT
registo

writeTo(ADXL345_DATA_FORMAT, 0x0B);

O passo final opcional; voc pode facilmente ter uma acelerao de trabalho, sem especificar o
deslocamento. Para compensar o desvio, estou usando o construdo em registros no ADXL345 e eu
adicionei a capacidade de fazer o mesmo em software tambm.
Como mencionei anteriormente, o deslocamento especificado nos registos - OFSX, OFSY e
OFSZ. Os deslocamentos so armazenados em formato de dois elogios e so automaticamente
adicionados ao registo de sada. No entanto, h uma desvantagem usando compensado hardware,
limitado forma como precisa voc pode especificar o deslocamento, devido ao fator de escala de
15,6 mg / LSB. Devido a esta limitao na biblioteca I adicionado software compensados, bem
como, para que eu possa especificar o deslocamento em menos de 15,6 mG tambm. O
deslocamento calculado para cada acelermetro separadamente, no use minhas medidas, voc s
vai fazer a sua acelerao a ser ainda menos preciso.

O cdigo completo para inicializar:


void ADXL345::init(char x_offset, char y_offset, char
1 z_offset)
2 {
3
writeTo(ADXL345_POWER_CTL, 8);
4
5
writeTo(ADXL345_DATA_FORMAT, 0x0B);
6
7
writeTo(ADXL345_OFSX, x_offset);
8
writeTo(ADXL345_OFSY, y_offset);
9
writeTo(ADXL345_OFSZ, z_offset);
10 }

Lendo a acelerao cru e convertendo a Gs


Neste ponto estamos prontos para ler os dados do acelermetro. As aceleraes em formato bruto
so armazenados em registos - DATAX0, DATAX1, DATAY0, DATAY1, DATAZ0 e DATAZ1.
Os resultados so divididos em dois 8 registos de bits que formam 16 registros de bit onde o
formato LSB primeiro, em seguida, seguido por MSB. Mais uma vez os dados so armazenados
no formato de dois de elogio. Com o protocolo I2C, possvel solicitar bytes mltiplos numa nica
sesso de leitura. Assim, para ler os dados, voc pode apenas fornecer o endereo de DATAX0 e
solicitar 6 bytes. Recomenda-se tambm por a folha de dados para evitar uma alterao nos dados
entre as leituras dos registos sequenciais.
Depois de termos ler dados brutos de acelerao, precisamos convert-los em Gs. Basicamente, ns
apenas como multiplicar os dados brutos com uma constante pr-calculados, que muda de acordo
com suas configuraes e "intervalo" "full Resoluo".
No meu caso, estou usando o modo de 16 bits e resoluo completa, o que me d em torno de 3,9
mg / LSB. Ento isso significa que eu tenho que multiplicar os dados com 0,0039 para converter os
dados em bruto para Gs. Veja a tabela acima para constantes de acordo com suas configuraes.
Veja o cdigo abaixo em como eu estou lendo e convertendo os dados brutos do acelermetro para
Gs:

1 AccelG ADXL345::readAccelG()
2 {
AccelRaw raw;
3
raw = readAccel();
4
5
6
double fXg, fYg, fZg;
7
fXg =raw.x * 0.00390625 + _xoffset;
8
fYg =raw.y * 0.00390625 + _yoffset;
9
10 fZg =raw.z * 0.00390625 + _zoffset;
11
12 AccelG res;
13
14 res.x = fXg * ALPHA + (xg * (1.0-ALPHA));
15 xg = res.x;
16
17 res.y = fYg * ALPHA + (yg * (1.0-ALPHA));
18 yg = res.y;
19
20 res.z = fZg * ALPHA + (zg * (1.0-ALPHA));
21 zg = res.z;
22
23 return res;
24

25 }
26
27 AccelRaw ADXL345::readAccel()
28 {
29 readFrom(ADXL345_DATAX0, ADXL345_TO_READ, _buff); //read the
30 acceleration data from the ADXL345
31
32 // each axis reading comes in 16 bit resolution, ie 2 bytes.
33 Least Significat Byte first!!
34 // thus we are converting both bytes in to one int
35 AccelRaw raw;
36 raw.x = (((int)_buff[1]) << 8) | _buff[0];
37 raw.y = (((int)_buff[3]) << 8) | _buff[2];
38 raw.z = (((int)_buff[5]) << 8) | _buff[4];
39
40 return raw;
}
No que na minha funo readAccelG eu tambm implementei um simples filtro passa-baixa:
1

res.x = fXg * ALPHA + (xg * (1.0-ALPHA));

Basicamente, eu s dou o componente dos dados do acelermetro atuais e a anterior e resumi-los


para produzir a nova acelerao. A ALPHA pode ser qualquer coisa entre 0 e 1. Quanto menor o
ALPHA as frequncias mais baixas sero filtrados.

Calculando pitch and roll


Um dos usos mais comuns de acelermetro medir a inclinao de um eixo particular. Lembre-se,
anteriormente mencionei que acelermetro sobre uma superfcie plana iria produzir 1 g em um de
seu eixo (o mais
provvel Z). A sada
do acelermetro,
no mais uma
onda senoidal
linear, mas, ento
voc no pode
simplesmente
converter foras-g
proporcionalmente
para inclinar em
graus.

Medio de inclinao com um eixo


A maneira mais simples para medir a inclinao, a utilizao de apenas um eixo. Basicamente o
inverso da funo seno lhe dar o ngulo.

Ento = sin-1 (x). No entanto, devido natureza da onda senoidal possvel medir a inclinao de
forma confivel a partir de 45 a -45 . Passado esses pontos a sensibilidade das medies so
significativamente reduzidos.

Medio de inclinao com dois eixos


Um mtodo ligeiramente mais fivel para o clculo da inclinao, a utilizao de dois eixos. Ento
voc pode medir de 90 a -90 , sem qualquer perda de sensibilidade. Lembre-se a geometria do
ensino mdio:

Usando dois eixos melhora significativamente a preciso da medio do ngulo. No entanto, se o


seu acelermetro ligeiramente rodado na direco do eixo Y, suas medidas ser novamente
imprecisa, uma vez que alguns dos componentes do vector a partir do eixo Z ser "perdido" ao eixo
Y.
Medio de inclinao com trs eixos
Para ter a melhor preciso na medio de inclinao, voc deve usar todos os trs eixos para
determinar o ngulo. Basicamente, a mesma equao arctan utilizado, mas em vez de
simplesmente dividindo por um eixo, que calcular a magnitude entre os outros dois eixos.

Com a equao acima, seria calcular o ngulo entre o vector de gravidade e o eixo X. Dependendo
de como voc acelermetro colocado na placa; Ela pode ser passo ou rolo.
Basicamente, voc tem que determinar em qual eixo para voc rolo e em que campo.
No meu caso eu calcular o pitch and roll como este:

Para implementar essas duas equaes no cdigo que eu usei atan2 funo, que fornecido pela
biblioteca de matemtica em C e C ++. A funo atan2 retorna o ngulo em radianos, ento lembrese novamente de matemtica do ensino mdio que 1 radiano = 180 / , que de cerca de ~ 57 .

1 rot.pitch = (atan2(accel.x,sqrt(accel.y*accel.y+accel.z*accel.z)) * 180.0) / PI;


2 rot.roll = (atan2(accel.y,(sqrt(accel.x*accel.x+accel.z*accel.z))) * 180.0) / PI;

Clculo de velocidade e distncia percorrida (para fins educacionais)


Primeiro de tudo, eu no aconselho a usar o acelermetro para calcular nem a velocidade nem a
distncia, devido a ser integrante apenas aproximado no cdigo. Dado que a integral ser apenas
aproximao voc vai obter rapidamente um erro em sua distncia e velocidade. Especialmente na
distncia, uma vez que para calcular isso, voc tem que usar integral dupla.
Primeiro acelerao a variao de velocidade na unidade de tempo:

A partir deste podemos deduzir que:

Para calcular a velocidade, voc tem que tomar periodicamente medies a partir do acelermetro e
multiplicar exatamente pela diferena de tempo e adicion-lo ao actual acelerao:

Quanto mais frequentemente voc vai ter as amostras, menos de erro que voc ter. No entanto,
porque voc no pode tomar uma quantidade infinita de medies entre duas unidades de tempo, a
velocidade, eventualmente, ir gerar um erro. O mais provvel que no vai mesmo voltar ao
estado inicial 0.
Para calcular a distncia, s usar a seguinte
equao:

E, novamente, periodicamente, voc deve calcular a distncia percorrida e adicionar distncia


previamente calculada:

O mesmo problema se aplica a calcular a velocidade ao calcular a distncia. Porque voc no pode
ter medies infinita entre, o integrante ser uma aproximao e ir gerar um erro. Na prtica,
porque voc usaria integral dupla, a distncia ir gerar o erro muito rapidamente, principalmente
porque, a velocidade nunca vai chegar 0. Ser sempre algo prximo a 0, ea distncia s vai flutuar
em qualquer direo. Quanto mais amostras voc vai tomar e o integral mais precisa, voc ter a
menos deriva voc ter.
Para evitar a deriva na distncia e velocidade, voc pode querer considerar o uso de GPS em
conjunto com o acelermetro. Para fundir os sensores voc pode usar filtro complementar simples
ou filtro de Kalman. Ou simplesmente no usar o acelermetro para a distncia ou velocidade de
medio

Implementando tudo em cdigo (Arduino)


Finalmente, toda a teoria necessria foi coberto sobre como o acelermetro ADXL345 funciona e
como interpretar e utilizar os dados fornecidos pelos acelermetros. Eu no vou estar indo em
detalhes atravs do cdigo, porque eu acredito que auto-explicativo, especialmente devido a me
destacar e dando exemplos de cdigo ao escrever a teoria.
Exemplo de uso:

#include "ADXL345.h"
ADXL345 accel;
void setup()
{
Serial.begin(115200);
Serial.println("Ready.");
Wire.begin();
accel.init(-1, 0, 8);
accel.setSoftwareOffset(-0.023, 0, 0.03577027);
accel.printCalibrationValues(40);
}
void loop()
{
//ACCEL
AccelRotation accelRot;
accelRot = accel.readPitchRoll();
Serial.print("{P0|Pitch|127,255,0|");
Serial.print(accelRot.pitch);
Serial.print("|Roll|255,255,0|");
Serial.print(accelRot.roll);
Serial.println("}");
AccelG accelG;
accelG = accel.readAccelG();
Serial.print("{P1|Xg|255,0,0|");
Serial.print(accelG.x);
Serial.print("|Yg|0,255,0|");
Serial.print(accelG.y);
Serial.print("|Zg|0,0,255|");
Serial.print(accelG.z);
Serial.println("}");
//END ACCEL
}

ADXL345.h file
#ifndef ADXL345_h
#define ADXL345_h
#include <Wire.h>
#include "Arduino.h"

#define ADXL345_DEVICE 0x53


#define ADXL345_TO_READ 6
#define ADXL345_POWER_CTL 0x2d
#define ADXL345_DATAX0 0x32
#define ADXL345_DATA_FORMAT 0x31
#define ADXL345_OFSX 0x1E
#define ADXL345_OFSY 0x1F
#define ADXL345_OFSZ 0x20
#define ALPHA 0.5
struct AccelRaw
{
int x;
int y;
int z;
};
struct AccelG
{
double x;
double y;
double z;
};
struct AccelRotation
{
double pitch;
double roll;
};
class ADXL345
{
public:
ADXL345();
void init(char x_offset=0, char y_offset=0, char z_offset=0);

void writeTo(byte address, byte val);


AccelRaw readAccel();
AccelG readAccelG();
void readFrom(byte address, int num, byte _buff[]);
void printAllRegister();
void print_byte(byte val);
void printCalibrationValues(int samples);
AccelRotation readPitchRoll();
void setSoftwareOffset(double x, double y, double z);
private:
byte _buff[6];
double xg;
double yg;
double zg;
double _xoffset;
double _yoffset;
double _zoffset;
};
#endif

ADXL345.cpp file

#include "Arduino.h"
#include "ADXL345.h"
#include <math.h>
ADXL345::ADXL345()
{
xg =0;
yg=0;
zg=0;
}
void ADXL345::init(char x_offset, char y_offset, char z_offset)
{
writeTo(ADXL345_POWER_CTL, 8);

writeTo(ADXL345_DATA_FORMAT, 0x0B);
writeTo(ADXL345_OFSX, x_offset);
writeTo(ADXL345_OFSY, y_offset);
writeTo(ADXL345_OFSZ, z_offset);
}
void ADXL345::setSoftwareOffset(double x, double y, double z)
{
_xoffset = x;
_yoffset = y;
_zoffset = z;
}
AccelRotation ADXL345::readPitchRoll()
{
//http://developer.nokia.com/community/wiki/How_to_get_pitch_and_roll_from_accelerometer_dat
a_on_Windows_Phone
//http://www.hobbytronics.co.uk/accelerometer-info
AccelG accel;
accel = readAccelG();
AccelRotation rot;
rot.pitch = (atan2(accel.x,sqrt(accel.y*accel.y+accel.z*accel.z)) * 180.0) / PI;
rot.roll = (atan2(accel.y,(sqrt(accel.x*accel.x+accel.z*accel.z))) * 180.0) / PI;
return rot;
}
void ADXL345::printCalibrationValues(int samples)
{
double x,y,z;
double xt,yt,zt;
xt = 0;
yt = 0;
zt = 0;
Serial.print("Calibration in: 3");
delay(1000);
Serial.print(" 2");
delay(1000);
Serial.println(" 1");
delay(1000);
for(int i=0; i<samples; i++)
{

AccelG accel = readAccelG();


xt += accel.x;
yt += accel.y;
zt += accel.z;
delay(100);
}
Serial.println("Accel Offset (mg): ");
Serial.print("X: ");
Serial.print(xt/float(samples)*1000,5);
Serial.print(" Y: ");
Serial.print(yt/float(samples)*1000,5);
Serial.print(" Z: ");
Serial.println( zt/float(samples)*1000,5);
delay(2000);
}
// Writes val to address register on device
void ADXL345::writeTo(byte address, byte val)
{
Wire.beginTransmission(ADXL345_DEVICE); // start transmission to device
Wire.write(address); // send register address
Wire.write(val); // send value to write
Wire.endTransmission(); // end transmission
}
AccelG ADXL345::readAccelG()
{
AccelRaw raw;
raw = readAccel();
//Scale = (16*2)/2^13
double fXg, fYg, fZg;
fXg =raw.x * 0.00390625 + _xoffset;
fYg =raw.y * 0.00390625 + _yoffset;
fZg =raw.z * 0.00390625 + _zoffset;
AccelG res;
res.x = fXg * ALPHA + (xg * (1.0-ALPHA));
xg = res.x;
res.y = fYg * ALPHA + (yg * (1.0-ALPHA));
yg = res.y;
res.z = fZg * ALPHA + (zg * (1.0-ALPHA));
zg = res.z;

return res;
}
AccelRaw ADXL345::readAccel()
{
readFrom(ADXL345_DATAX0, ADXL345_TO_READ, _buff); //read the acceleration data from
the ADXL345
// each axis reading comes in 16 bit resolution, ie 2 bytes. Least Significat Byte first!!
// thus we are converting both bytes in to one int
AccelRaw raw;
raw.x = (((int)_buff[1]) << 8) | _buff[0];
raw.y = (((int)_buff[3]) << 8) | _buff[2];
raw.z = (((int)_buff[5]) << 8) | _buff[4];
return raw;
}
// Reads num bytes starting from address register on device in to _buff array
void ADXL345::readFrom(byte address, int num, byte _buff[])
{
Wire.beginTransmission(ADXL345_DEVICE); // start transmission to device
Wire.write(address); // sends address to read from
Wire.endTransmission(); // end transmission
Wire.beginTransmission(ADXL345_DEVICE); // start transmission to device
Wire.requestFrom(ADXL345_DEVICE, num); // request 6 bytes from device Registers: DATAX0,
DATAX1, DATAY0, DATAY1, DATAZ0, DATAZ1
int i = 0;
while(Wire.available()) // device may send less than requested (abnormal)
{
_buff[i] = Wire.read(); // receive a byte
i++;
}
Wire.endTransmission(); // end transmission
}
void ADXL345::printAllRegister()
{
byte _b;
Serial.print("0x00: ");
readFrom(0x00, 1, &_b);
print_byte(_b);
Serial.println("");
int i;

for (i=29;i<=57;i++)
{
Serial.print("0x");
Serial.print(i, HEX);
Serial.print(": ");
readFrom(i, 1, &_b);
print_byte(_b);
Serial.println("");
}
}
void ADXL345::print_byte(byte val)
{
int i;
Serial.print("B");
for(i=7; i>=0; i--){
Serial.print(val >> i & 1, BIN);
}
}

Aqui voc pode ver alguns grficos sobre os dados do acelermetro:

E isso. Na parte 2, vou descrever como usar o giroscpio e como usar filtro complementar para
fundir os dados de acelerao com os dados do giroscpio. Parte 2 em breve.
biblioteca download aqui

References
HobbyTronics. (NA). Accelerometers. Available: http://www.hobbytronics.co.uk/accelerometerinfo. Last accessed 31/05/2014.
Analog Devices. (NA). Digital Accelerometer ADXL345. Available:
http://www.forkrobotics.com/wp-content/uploads/2013/05/ADXL345.pdf. Last accessed
31/05/2014.
bildr. (2011). Tap, Tap, Drop. ADXL345 Accelerometer + Arduino . Available:
http://bildr.org/2011/03/adxl345-arduino/. Last accessed 31/05/2014.

Você também pode gostar