Sensores de movimento no Android (Tradução da documentação oficial)

A plataforma Android fornece diversos sensores que permitem que você monitore os movimentos do dispositivo. Dois desses sensores são sempre baseados no hardware (o acelerômetro e o giroscópio), e três desses sensores podem ser tanto baseados em hardware quanto em software (gravidade, aceleração linear e rotação vetorial). Por exemplo, em alguns dispositivos os sensores baseados em software derivam seus dados do acelerômetro e do magnetrômetro, mas em outros pode-se usar também o giroscópio. Muitos dispositivos que rodam Android possuem um acelerômetro, e muitos agora incluem um giroscópio. A disponibilidade de sensores baseado em software é mais variada porque eles frequentemente dependem de um ou mais sensores de hardware para gerar os seus dados.

NESSE DOCUMENTO

  1. Usando o Acelerômetro
  2. Usando o Sensor de Gravidade
  3. Usando o Giroscópio
  4. Usando o Acelerômetro Linear
  5. Usando o Sensor de Rotação Vetorial

CLASSE E INTERFACES CHAVES

  1. Sensor
  2. SensorEvent
  3. SensorManager
  4. SensorEventListener

EXEMPLOS RELACIONADOS

  1. Accelerometer Play
  2. API Demos (OS – RotationVectorDemo)
  3. API Demos (OS – Sensors)

VEJA TAMBÉM

  1. Sensors
  2. Sensors Overview
  3. Position Sensors
  4. Environment Sensors

Sensores de movimento são úteis para  monitorar o movimento do dispositivo, como inclinação, vibração, rotação ou balanço. O movimento é normalmente um reflexo da entrada direta do usuário (por exemplo, um usuário dirigindo um carro ou um usuário controlando uma bola em um jogo), mas pode também ser um reflexo do ambiente físico onde o dispositivo está (por exemplo, o movimento enquanto dirige o carro). No primeiro caso, você estaria monitorando o movimento relativo ao quadro de referência do dispositivo  ou da sua aplicação; no segundo caso, você estaria monitorando o movimento relativo ao quadra de referência do mundo real. Sensores de movimento por si próprios tipicamente não são usados para monitorar a posição dispositivo, mas eles podem ser usados com outros sensores, como o sensor de campo magnético, para determinar a posição do dispositivo em relação ao quadro de referência do mundo real.

Todos os sensores de movimento retornam um array multi-dimensional de valores do sensor para cada SensorEvent. Por exemplo, durante um único evento do sensor o acelerômetro retorna os dados da força da aceleração para os três eixos coordenados, e o giroscópio retorna os dados da taxa de rotação para esses mesmos eixos. Esses dados são retornados em um array float (values) junto com outros parâmetros de SensorEvent. A Tabela 1 resume os sensores de movimento que estão disponíveis na plataforma Android.

Tabela 1. Sensores de movimento que são suportados pela plataforma Android

Sensor Dados do evento do Sensor Descrição Unidades de medida
TYPE_ACCELEROMETER SensorEvent.values[0] Acceleration force along the x axis (including gravity). m/s2
SensorEvent.values[1] Acceleration force along the y axis (including gravity).
SensorEvent.values[2] Acceleration force along the z axis (including gravity).
TYPE_GRAVITY SensorEvent.values[0] Force of gravity along the x axis. m/s2
SensorEvent.values[1] Force of gravity along the y axis.
SensorEvent.values[2] Force of gravity along the z axis.
TYPE_GYROSCOPE SensorEvent.values[0] Rate of rotation around the x axis. rad/s
SensorEvent.values[1] Rate of rotation around the y axis.
SensorEvent.values[2] Rate of rotation around the z axis.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] Acceleration force along the x axis (excluding gravity). m/s2
SensorEvent.values[1] Acceleration force along the y axis (excluding gravity).
SensorEvent.values[2] Acceleration force along the z axis (excluding gravity).
TYPE_ROTATION_VECTOR SensorEvent.values[0] Rotation vector component along the x axis (x * sin(θ/2)). Unitless
SensorEvent.values[1] Rotation vector component along the y axis (y * sin(θ/2)).
SensorEvent.values[2] Rotation vector component along the z axis (z * sin(θ/2)).
SensorEvent.values[3] Scalar component of the rotation vector ((cos(θ/2)).

O sensor de rotação vetorial e o sensor de gravidade são os sensores frequentemente mais usados para detecção e monitoramento de movimento. O sensor de rotação vetorial é particularmente versátil e pode ser usado para uma ampla variedade de tarefas relacionadas ao movimento, como detectar gestos, monitoramento de mudanças no ângulo, e monitoramento de mudanças na orientação. Por exemplo, o sensor de rotação vetorial é ideal se você estiver desenvolvendo um jogo, uma aplicação de realidade aumentada, uma bussola de 2 ou 3 dimensões, ou uma aplicação de estabilização da câmera. Em muitos casos, o uso desses sensores é uma escolha melhor do que usar o acelerômetro e sensores de campo magnético ou de orientação.

Sensores do Android Open Source Project

O Android Open Source Project (AOSP) fornece três sensores de movimento baseados em software: um sensor de gravidade, um sensor de aceleração linear e um sensor de rotação vetorial. Esses sensores foram atualizados no Android 4.0 e agora usam um giroscópio do dispositivo (além de outros sensores) para melhorar a estabilidade e performance. Se quiser testar esses sensores, pode identifica-los através do uso dos métodos getVendor() e getVersion() (o fabricante – vendor – é o Google Inc.; o número da versão é 3). Identificar esses sensores por fabricante e número de versão é necessário porque o sistema Android considera esses três sensores como secundários. Por exemplo, se o fabricante do aparelho fornecer o seu próprio sensor de gravidade, então o sensor de gravidade do AOSP será mostrado como um sensor secundário. Todos esses três sensores dependem de um giroscópio,  e não estarão disponíveis caso não exista um para uso.

Usando o Acelerômetro


Um sensor de aceleração mede a aceleração aplicada ao dispositivo, incluindo a força da gravidade. O código a seguir mostra como você consegue uma instância do sensor de aceleração padrão:

private SensorManager mSensorManager;
private Sensor mSensor;
  ...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Conceitualmente, um sensor de aceleração determina a aceleração que é aplicada a um dispositivo (Ad) pela medição das forças aplicadas sobre o próprio sensor (Fs) usando a seguinte relação:

Porém, a força da gravidade está sempre influenciando a medição da aceleração de acordo com a seguinte relação:

Por essa razão, quando o dispositivo está sobre uma mesa (e não sendo acelerado), o acelerômetro lê uma magnitude de g = 9.81 m/s2. De forma similar, quando o dispositivo está em queda livre e assim sendo acelerado rapidamente em direção ao chão a 9.81 m/s2, o acelerômetro lê uma magnitude de g = 0 m/s2. Dessa forma, para medir a aceleração real do dispositivo, a contribuição da força gravitacional precisa ser removida dos dados do acelerômetro. Isso pode ser conseguido aplicando um filtro de high-pass. Por outro lado, um filtro de low-pass pode ser usado para isolar a força da gravidade. O exemplo a seguir mostra como fazer isso:

public void onSensorChanged(SensorEvent event){
  // In this example, alpha is calculated as t / (t + dT),
  // where t is the low-pass filter's time-constant and
  // dT is the event delivery rate.

  final float alpha = 0.8;

  // Isolate the force of gravity with the low-pass filter.
  gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
  gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
  gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

  // Remove the gravity contribution with the high-pass filter.
  linear_acceleration[0] = event.values[0] - gravity[0];
  linear_acceleration[1] = event.values[1] - gravity[1];
  linear_acceleration[2] = event.values[2] - gravity[2];
}

Nota: Você pode usar muitas técnicas diferentes para filtras os dados do sensor. O exemplo acima usa um simples filtro constante para criar um filtro low-pass. Esse filtro é derivado de uma constante de tempo (t), que é uma representação aproximada da latência que o filtro adiciona aos eventos do sensor, e a taxa de entrega do evento do sensor (dt). O código exemplo usa uma constante de valor 0.8 para proposito de demonstração. Se você usar esse método de filtragem pode precisar de uma valor diferente.

Acelerômetros usam o sistema de coordenadas padrão. Na prática, isso significa que as seguintes condições se aplicam quando um dispositivo está sobre uma mesa em sua orientação natural:

  • Se você empurra o dispositivo do lado esquerdo (move-o para a direita) o valor da aceleração é positivo.
  • Se você empurra o dispositivo na parte inferior (move-o para longe de você), o valor é positivo.
  • Se você empurra o dispositivo na direção do céu com aceleração A m/s2, o valor z da aceleração será igual a A + 9.81, que corresponde à aceleração do dispositivo (+A m/s2) menos a força da gravidade (-9.81 m/s2).
  • O dispositivo estando parado terá uma valor de aceleração de +9.81, que corresponde a aceleração do dispositivo (0 m/s2) menos a força da gravidade, que é -9.81 m/s2.

Em geral, o acelerômetro é um bom sensor para usar se você estiver monitorando o movimento do dispositivo. Quase todos os dispositivos Android possuem um acelerômetro, e ele usa aproximadamente 10 vezes menos energia do que outros sensores de movimento. Uma desvantagem é que você pode ter que implementar filtros low-pass e high-pass para eliminar a força gravitacional e reduzir o ruído.

O SDK do Android fornece uma aplicação exemplo que mostra como usar esse sensor (Accelerometer Play).

Usando o sensor de Gravidade


O sensor de gravidade fornece um vetor de três dimensões indicando a direção e magnitude da gravidade. O código a seguir mostra como você pode obter uma instância do sensor de gravidade padrão:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

A unidade é mesma da usadas pelo sensor de aceleração (m/s2), assim com o sistema de coordenadas.

Nota: Quando o dispositivo está em repouso, a saída do sensor de gravidade deve ser idêntica a do acelerômetro.

Usando o Giroscópio


O giroscópio mede a variação da rotação em rad/s em torno dos eixos x, y e z. O código a seguir mostra como obter uma instância do giroscópio padrão:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

The sensor’s coordinate system is the same as the one used for the acceleration sensor. Rotation is positive in the counter-clockwise direction; that is, an observer looking from some positive location on the x, y or z axis at a device positioned on the origin would report positive rotation if the device appeared to be rotating counter clockwise. This is the standard mathematical definition of positive rotation and is not the same as the definition for roll that is used by the orientation sensor.

Usually, the output of the gyroscope is integrated over time to calculate a rotation describing the change of angles over the timestep. For example:

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
  // This timestep's delta rotation to be multiplied by the current rotation
  // after computing it from the gyro sample data.
  if (timestamp != 0) {
    final float dT = (event.timestamp - timestamp) * NS2S;
    // Axis of the rotation sample, not normalized yet.
    float axisX = event.values[0];
    float axisY = event.values[1];
    float axisZ = event.values[2];

    // Calculate the angular speed of the sample
    float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

    // Normalize the rotation vector if it's big enough to get the axis
    // (that is, EPSILON should represent your maximum allowable margin of error)
    if (omegaMagnitude > EPSILON) {
      axisX /= omegaMagnitude;
      axisY /= omegaMagnitude;
      axisZ /= omegaMagnitude;
    }

    // Integrate around this axis with the angular speed by the timestep
    // in order to get a delta rotation from this sample over the timestep
    // We will convert this axis-angle representation of the delta rotation
    // into a quaternion before turning it into the rotation matrix.
    float thetaOverTwo = omegaMagnitude * dT / 2.0f;
    float sinThetaOverTwo = sin(thetaOverTwo);
    float cosThetaOverTwo = cos(thetaOverTwo);
    deltaRotationVector[0] = sinThetaOverTwo * axisX;
    deltaRotationVector[1] = sinThetaOverTwo * axisY;
    deltaRotationVector[2] = sinThetaOverTwo * axisZ;
    deltaRotationVector[3] = cosThetaOverTwo;
  }
  timestamp = event.timestamp;
  float[] deltaRotationMatrix = new float[9];
  SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
   }
}

Giroscópios padrão fornecem  dados de rotação brutos sem nenhuma filtragem ou correção de ruído e vento. Na prática, o ruído do giroscópio produz erros e precisa ser compesado. Você normalmente determina o vento e o ruído pelo monitoramento de outros sensores, como o sensor de gravidade ou o acelerômetro.

Usando o Acelerômetro Linear


O sensor de aceleração linear fornece a você uma vetor de três dimensões que representa a aceleração em cada eixo do dispositivo, excluindo a gravidade. O código a seguir mostra como obter uma instância do sensor de aceleração linear padrão:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

Conceitualmente, o sensor fornece a você dados da aceleração de acordo com a seguinte relação:

linear acceleration = acceleration - acceleration due to gravity

Você tipicamente usa esse sensor quando quer obter dados da aceleração sem a influência da gravidade. Por exemplo, você pode usar esse sensor para ver quão rápido seu carro está se movendo. O sensor de aceleração linear sempre tem um deslocamento, que você precisa remover. A maneira mais simples de fazer isso é criar um passo de calibração em sua aplicação. Durante a calibração, você pode que usuário para colocar o dispositivo sobre a mesa, e lê o deslocamento dos três eixos. Você pode então subtrair esse deslocamento das leituras diretas do sensor para obter a aceleração linear correta.

Usando o Sensor de Rotação Vetorial


A rotação vetorial representa a orientação do dispositivo como uma combinação de uma ângulo e um eixo, no qual o dispositivo é rotacionado por um ângulo θ em torno de um eixo (x, y, or z). O código a seguir mostra como obter uma instância do sensor de rotação vetorial padrão:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

Os três elementos do vetor de rotação são expressos a seguir:

x*sin(θ/2)
y*sin(θ/2)
z*sin(θ/2)

Onde a magnitude do vetor de rotação é igual a sin(θ/2), e a direção do vetor de rotação é igual a direção do eixo de rotação.

Figure 1. Coordinate system used by the rotation vector sensor.
Figure 1. Coordinate system used by the rotation vector sensor.

 

Os três elementos do vetor de rotação são iguais aos últimos três componentes de uma unidade Quaternião (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). Os elementos do vetor de rotação não possuem unidade. Os eixos x, y e z são definidos da mesma forma que o sensor de aceleração. O sistema de coordenadas é definido como uma base octogonal direta (veja a figura 1). Esse sistema de coordenada possui as seguintes características:

  • X é definido como o produto vetorial Y x Z. É tangencial ao chão na localização atual do dispositivo e aponta aproximadamente para o Leste.
  • Y é tangencial ao chão na posição atual do dispositivo e aponta para o Pólo Norte magnético.
  • Z aponta para o céu e é perpendicular ao plano do chão.

O SDK do Android fornece uma aplicação exemplo que mostra como usar o sensor de rotação vetorial ( OS – RotationVectorDemo).