Acelerómetro Implementação de um sensor de orientação no Arduino e no Processing
youtube quick preview: http://www.youtube.com/watch?v=yAlQlf6zgPg
Neste tutorial iremos desenvolver, a partir de um acelerómetro de 3 eixos, um sensor de Attitude, ou por outras palavras, um sistema que determina em tempo real a orientação de um objecto no espaço.
Este post subdivide-se em 3 partes. Na primeira, faremos uma introdução descritiva ao modo de funcionamento do acelerómetro. São abordados diferentes cenários de utilização e discutidas as suas limitações.
Num segundo momento, explicaremos de forma detalhada a sua implementação no arduino,
Na última parte, será apresentado um exemplo em Processing e outro em Unity3D (próximo post).
O acelerómetro que serve de base a este tutorial é a unidade de baixo custo ADXL335.
No entanto,quer os textos de apoio quer os exemplos são válidos genericamente para qualquer outro acelerómetro, desde que este possua saídas analógicas e o leitor tenha acesso à sua folha técnica.
.
.
Como funciona um acelerómetro de 3 eixos (3D)?
O acelerómetro digital de baixo custo é um sensor que mede indirectamente a aceleração através da força a que o sensor está sujeito em cada um dos três eixos. Se uma força actuar sobre um eixos do sensor, a aceleração é proporcional e de sentido inverso ao valor obtido.
Quando o acelerómetro está em repouso, por exemplo pousado numa superfície plana, o seu valor para o eixo Z é igual a -1g (e zero os eixos X e Y). Este valor (x= 0,y=0, z=1) corresponde ao vector da força da gravidade. Se rodarmos o acelerómetro de modo a ficar com outro eixo voltado para baixo, digamos o eixo X, o valor comunicado correspodenrá ao vector (x=1,y=0,z=0).
.
.
Cenário 1. Attitude Sensor
Assim, sempre que o acelerómetro estiver em repouso é possível, através de cálculos trignométricos, determinar com exactidão a orientação tridimensional do sensor, o que em si é tarefa simples.
O primeiro gráfico representa uma situação em que o acelerómetro se encontra em repouso (pousado numa superfície). O sensor medirá o vector da força de gravidade a actuar sobre o eixo Z. Se rodarmos o acelerómetro, digamos, ao longo do eixo Z, essa rotação irá reprecutir-se no sistema de coordenadas (gráfico do centro). Repare-se que o vector da força da gravidade, face ao mundo, permanece na vertical. O output do acelerómetro está representado no gráfico à direita. Calculando o ângulo entre este vector e os 3 eixos obtemos a orientação 3D do acelerómetro, ou a sua atitude.
.
.
Cenário 2. TILT Sensor
Um sensor TILT mede as forças que agem sobre um obejcto e que, geralmente, provocam a sua translação no espaço. A partir da aceleração é possível inferir a velocidade e a aceleração.
O acelerómetro, além de medir a força da gravidade, mede também todas as forças a que o sensor está sujeito. Este conjunto de forças, chamada força de rede, é o resultado da soma algébrica de todas as forças. Se por exemplo empurrarmos o sensor, que se encontrava pousado numa superfície plana e em repouso (gráfico da esquerda), ao longo do X (gráfico do centro), o output do acelerómetro corresponde à força de rede, ou seja, a soma de todas as forças (gráfico da direita). Também, neste caso, é fácil do ponto de vista matemático inferir o valor da força que o deslocou (Fp), bastando para isso subtrair ao output do sensor (Fn) o vector gravidade (Fg).
.
.
IMU. Inertial Motion Unity
A principal limitação do acelerómetro consiste na impossibilidade de conjugação dos dois cenários. Um sistema de sensoriamento inercial, composto unicamente por um acelerómetro, pode estimar com exactidão a orientação de um objecto caso este se encontre imóvel ou em repouso (cenário 1. Atitude Sensor), e consegue estimar a translação sempre que o movimento não envolva rotações (cenário 2. Tilt sensor). Infelizmente estas duas situações raramente descrevem os cenários reais que envolvem desde aparelhos voadores, quer gestos humanos realizados no manuseamento de objectos.
Quando um objecto (que possua um acelerómetro incorporado) é manuseado, está sujeito a movimentos de translação e simultaneamente de rotação. Reportando ao exemplo anterior no qual um objecto em repouso é movido lateralmente ao longo do eixo X (gráfico à esquerda), se acrescentarmos uma rotação (gráfico da direita) torna-se impossível a partir do vector obtido do acelerómetro ( o vector agregado da força de rede Fn) calcular as componentes individuais de rotação (Fg) e de translação (Fp) que lhes deream origem.
Um sistema que permita discriminar estas componentes é capaz de estimar simultaneamente a posição e a orientação de um objecto. Tais sistemas denominam-se por IMU – Inertial Motion Units, e geralmente requerem, além do acelerómetro, outras sensores como o giroscópio, o magnetómetro (bússula digital) ou até mesmo GPS, quando estese encontra diposponível e é adequado ao contexto de aplicação. Combinando o output de várias unidades é possível estimar não só a orientação como também a posição de um objecto.
Para mais informação acerca do funcionamento do acelerómetro, do giroscópio e de como combinar ambos para desenvolver um IMU, consulte o excelente guia de Starlino.
.
.
Implementação no Arduino de um sensor de orientação
O próximo passo é desenvovler um programa no Arduino cujo obejctivo é converter os valores dos acelerómetro em unidades físicas, tais como a aceleração medida em Força g ou a orientação medida em graus. Inspirando-nos no guia de Starlino iremos implementar o código no Arduino e explicar passo-a-passo as várias etapas.
– O nosso programa irá comunicar os ângulos da orientação do acelerómetro por via da porta de comunicação. No bloco Setup necessitamos de inicializar o objecto Serial.
void setup(){ Serial.begin(9600); }
.
– No início do progrma, criamos 3 variáveis globais para guardar o output do sensor.
int ax,ay,az;
.
– Em seguida, já no interior do bloco loop, vamos declarar e ler o output analógico do sensor.
void loop(){ ax=analogRead(0); ay=analogRead(1); az=analogRead(2);
.
– Tendo em consideração que estamos a alimentar a unidade com 3.3V e o valor de referência do cenversor analógico-digital é de 5V, teremos de efectuar uma simples conversão.
Este procedimento não é necessário caso conecte 3.3V ao pino AREF ou caso esteja a usar um acelerómetro alimentado a 5V (atenção que a maior parte dos acelerómetros trabalham a 3~3.3V e podem ser danificados se os alimentar a 5V).
ax=ax*5/3.3; ay=ay*5/3.3; az=az*5/3.3;
.
– As folhas técnicas dos acelerómentros costumam representar as grandezas em volts. Uma simples operação permitirá converter os valores 10 bits (0-1023) do conversor analógico do arduino para valores em volts.
float vx = ax*3.3/1023; float vy = ay*3.3/1023; float vz = az*3.3/1023;
.
– De acordo com a folha técnica do acelerómetro, o valor da voltagem que corresponde a uma aceleração nula, denominada Zero g, é de 1.5V quando a unidade é alimentada com 3V (Vs).
Como o output do Zero g é proporcional (ratiometric) podemos calcular a relação para 3.3V
Assim:
float z= 1.5*3.3/3; //z=1.65
.
– Subtraímos o Zero v e obtemos os dados centrados na origem.
vx-=z; vy-=z; vz-=z;
.
– Finalmente estamos em condições de calcular a aceleração medida em g’s (por exemplo, gravidade g=1, ou seja 9.8 m/s^2).
PAra converter para g’s os valores medidos em Volts necessitamos de procurar na folha técnica os parâmetros de sensibilidade, geralmente expressos em mV/g. Também neste caso, os valores são ratiometric.
No nosso caso, a sensibilidade é de 300/g para uma Vs=3V. Ou seja, quando o acelerómetro é alimentado com 3V, cada 300mv equivalem a uma aceleração de 1g.
Iremos num primeiro momento ajustar a sensibildidade ao nosso Vs, que como sabemos é de 3.3V, e em seguida dividir por 1000 porque precisamos ajustar a sensibilidade para Volts e não em mVolts.
Por fim, multiplicamos os valores obtidos nos passos anteriores pelo coeficiente de sensibilidade e obtemos os valores expresso em força g.
float sensitivity = 300*3.3/3; //sensitivity=330 sensitivity=sensitivity/1000; float gx = vx*sensitivity; float gy = vy*sensitivity; float gz = vz*sensitivity;
Recorremos à seguinte fórmula para a conversão de força g em ângulos:
angleX = arccos(gx/m)
em que m é o comprimento do vector da força (de gravidade).
float m=sqrt(gx*gx+gy*gy+gz*gz); float angleX=acos(gx/m); float angleY=acos(gy/m); float angleZ=acos(gz/m);
.
– Finalmente, após converter os ângulo radianos para graus, iremos enviá-los pela porta série.
angleX=angleX*180/3.14; angleY=angleY*180/3.14; angleZ=angleZ*180/3.14; Serial.print(int(angleX)); Serial.print(","); Serial.print(int(angleY)); Serial.print(","); Serial.print(int(angleZ)); Serial.print(","); Serial.println(); delay(50);
.
.
Exemplo no arduino e processing
Ligue o acelerómetro ao arduino.Juntando tudo num único sketch
#include <math.h> int zx0=335; int zy0=335; int zz0=335; int ax,ay,az; void setup(){ Serial.begin(9600); } void loop(){ // read accelerometer output ax=analogRead(3); ay=analogRead(4); az=analogRead(5); // arduino analog maps 0v-5v into 0-1023 // most accelerometers work at ~3v // skip this part if: // 1.you connected 3.3v to arduino pin AREF // 2.your acelerometer is working at 5v // - remember, most of units doesnt work with 5v ax=ax*5/3.3; ay=ay*5/3.3; az=az*5/3.3; // calculate voltage float vx = ax*3.3/1023; float vy = ay*3.3/1023; float vz = az*3.3/1023; //from accelerometer's datasheet, get zero g voltage value. //most of the time value is for 3v power supply. //we can easly calculate for 3.3v // since the output typically is ratiometric (proportional) // in this case, datasheet shows Zg=1.5v for Vs=3 float z= 1.5*3.3/3; //z=1.65 // set the origin to Zero-g voltage level vx-=z; vy-=z; vz-=z; // now we can convert voltage to force g (for instance gravity: 9.8 m/s^2) // find in the datasheet sensitivy, usually expressed in mV/g. // for our case sensitivity equals 300/g for Vs=3V. // again the former is ratiometric to power supply (Vs=3.3V) // so we need to transform this number first float sensitivity = 300*3.3/3; //sensitivity=330 // and convert it from milli-voltage to Voltage // since our readings are in voltage sensitivity=sensitivity/1000; // now we can aply the formula to our readings // and get force (g) float gx = vx*sensitivity; float gy = vy*sensitivity; float gz = vz*sensitivity; // Determine the angle between the accelerometer and axis xyz // using the formula // angleX = arccos(gx/m) // where m (magnitude or length) is // m = Sq.root(x*x + y*y + z*z) float m=sqrt(gx*gx+gy*gy+gz*gz); float angleX=acos(gx/m); float angleY=acos(gy/m); float angleZ=acos(gz/m); // convert angles from radians to degrees angleX=angleX*180/3.14; angleY=angleY*180/3.14; angleZ=angleZ*180/3.14; Serial.print(int(angleX));Serial.print(","); Serial.print(int(angleY));Serial.print(","); Serial.print(int(angleZ));Serial.print(","); Serial.println(); delay(50); }
Para termos uma representação visual do algoritmo desenvolvemos uma aplicação simples no Processing. Esta úlitma, por sua vez, recebe os dados do Arduino e aplica em tempo real a orientação (attitude) a uma caixa 3D.
import processing.serial.*; Serial porta; int x,y,z; PFont fonte; void setup() { size(500, 500, P3D); noStroke(); fill(255, 120,0); stroke(255); strokeWeight(4); background(0,100,70); String portName = Serial.list()[0]; porta = new Serial(this, portName, 9600); porta.bufferUntil(10); } void draw() { background(0); text(x + " , " + y ,40,45); translate(width/2, height/2); rotateX(x*(TWO_PI)/360); rotateY(y*(TWO_PI)/360); box(100, 100, 100); } void serialEvent(Serial p) { String buffer = (porta.readString()); int[] val = int(split(buffer, ',')); println(buffer); x=val[0]; y=val[1]; z=val[2]; }
.
.
.
.
.
Pingback: Comunicação entre Arduino e Unity3D – Fazer Lab