Visão geral da orientação do telefone Android, incluindo bússola

107

Estou tentando entender os sensores de orientação do Android há um tempo. Achei que tinha entendido. Então eu percebi que não. Agora acho (espero) que tenha um sentimento melhor de novo, mas ainda não estou 100%. Vou tentar explicar meu entendimento irregular sobre isso e espero que as pessoas sejam capazes de me corrigir se eu estiver errado em algumas partes ou preencher alguns espaços em branco.

Eu imagino estar em 0 graus de longitude (meridiano principal) e 0 graus de latitude (equador). Este local é na verdade no mar ao largo da costa da África, mas tenha paciência comigo. Eu seguro meu telefone na frente do meu rosto para que a parte inferior do telefone aponte para os meus pés; Estou voltado para o norte (olhando para Greenwich), portanto, o lado direito do telefone aponta para o leste em direção à África. Nesta orientação (com referência ao diagrama abaixo), tenho o eixo X apontando para o leste, o eixo Z apontando para o sul e o eixo Y apontando para o céu.

Agora, os sensores do telefone permitem que você calcule a orientação (não a localização) do dispositivo nessa situação. Essa parte sempre me confundiu, provavelmente porque eu queria entender como algo funcionava antes de aceitar que simplesmente funcionava. Parece que o telefone determina sua orientação usando uma combinação de duas técnicas diferentes.

Antes de chegar a isso, imagine estar de volta naquele pedaço de terra imaginário a 0 graus de latitude e longitude, na direção mencionada acima. Imagine também que você está com os olhos vendados e seus sapatos fixados em uma rotatória de playground. Se alguém o empurrar pelas costas, você cairá para frente (em direção ao norte) e estenderá as duas mãos para amortecer a queda. Da mesma forma, se alguém lhe empurrar o ombro esquerdo, você cairá sobre a mão direita. Seu ouvido interno possui "sensores gravitacionais" (clipe do youtube) que permitem detectar se você está caindo para frente / para trás, ou para a esquerda / direita ou caindo (ou para cima !!). Portanto, os humanos podem detectar o alinhamento e a rotação em torno dos mesmos eixos X e Z do telefone.

Agora imagine alguém girando você 90 graus na rotatória, de modo que você esteja voltado para o leste. Você está sendo girado em torno do eixo Y. Este eixo é diferente porque não podemos detectá-lo biologicamente. Sabemos que estamos inclinados em certa medida, mas não sabemos a direção em relação ao pólo norte magnético do planeta. Em vez disso, precisamos usar uma ferramenta externa ... uma bússola magnética. Isso nos permite determinar a direção que estamos enfrentando. O mesmo se aplica ao nosso telefone.

Agora, o telefone também possui um acelerômetro de 3 eixos. Tenho NOTenho ideia de como eles realmente funcionam, mas a maneira como eu visualizo isso é imaginar a gravidade como uma "chuva" constante e uniforme caindo do céu e imaginar os eixos na figura acima como tubos que podem detectar a quantidade de chuva que flui. Quando o telefone é colocado na posição vertical, toda a chuva flui pelo tubo em Y. Se o telefone for girado gradualmente de forma que sua tela fique voltada para o céu, a quantidade de chuva que flui em Y diminuirá a zero, enquanto o volume em Z aumentará continuamente até que a quantidade máxima de chuva esteja fluindo. Da mesma forma, se agora inclinarmos o telefone para o lado, o tubo X eventualmente coletará a quantidade máxima de chuva. Portanto, dependendo da orientação do telefone, medindo a quantidade de chuva que flui através dos 3 tubos, você pode calcular a orientação.

O telefone também possui uma bússola eletrônica que se comporta como uma bússola normal - sua "agulha virtual" aponta para o norte magnético. O Android mescla as informações desses dois sensores para que, sempre que um SensorEventde TYPE_ORIENTATIONfor gerado, a values[3]matriz tenha
valores [0]: Azimute - (a bússola que aponta a leste do norte magnético)
valores [1]: inclinação, rotação em torno do eixo x (é o telefone inclinado para frente ou para trás)
valores [2]: Roll, rotação em torno do eixo y (é o telefone inclinado sobre seu lado esquerdo ou direito)

Então eu acho (ou seja, eu não sei) que a razão do Android fornecer o azimute (direção da bússola) em vez da leitura do terceiro acelerômetro é que a direção da bússola é apenas mais útil. Não sei por que eles descontinuaram esse tipo de sensor, pois agora parece que você precisa registrar um ouvinte no sistema para SensorEvents do tipo TYPE_MAGNETIC_FIELD. O value[]array do evento precisa ser passado para o SensorManger.getRotationMatrix(..)método para obter uma matriz de rotação (veja abaixo) que é então passada para o SensorManager.getOrientation(..)método. Alguém sabe por que a equipe do Android suspendeu o uso Sensor.TYPE_ORIENTATION? É uma questão de eficiência? Isso é o que está implícito em um dos comentários a uma pergunta semelhante, mas você ainda precisa registrar um tipo diferente de ouvinte nodevelopment / samples / Compass / src / com / example / android / compass / CompassActivity.java example.

Agora gostaria de falar sobre a matriz de rotação. (É aqui que estou mais inseguro) Portanto, acima, temos as três figuras da documentação do Android, vamos chamá-las de A, B e C.

A = SensorManger.getRotationMatrix (..) figura do método e representa o sistema de coordenadas do mundo

B = Sistema de coordenadas usado pela API SensorEvent.

C = SensorManager.getOrientation (..) figura do método

Portanto, meu entendimento é que A representa o "sistema de coordenadas do mundo" que presumo se refere à maneira como as localizações no planeta são dadas como um par (latitude, longitude) com uma (altitude) opcional. X é a coordenada "leste" , Y é a coordenada "norte" . Z aponta para o céu e representa a altitude.

O sistema de coordenadas dos telefones é mostrado na figura B é fixo. Seu eixo Y sempre aponta para o topo. A matriz de rotação está sendo calculada constantemente pelo telefone e permite o mapeamento entre as duas. Então, estou certo em pensar que a matriz de rotação transforma o sistema de coordenadas de B em C? Então, quando você chama o SensorManager.getOrientation(..)método, você usa a values[]matriz com valores que correspondem à figura C. Quando o telefone é apontado para o céu, a matriz de rotação é a matriz de identidade (a matriz matemática equivalente a 1) o que significa que nenhum mapeamento é necessário porque o dispositivo está alinhado com o sistema de coordenadas do mundo.

Está bem. Acho melhor parar agora. Como eu disse antes, espero que as pessoas me digam onde eu baguncei ou ajudei as pessoas (ou confundi as pessoas ainda mais!)

Tim
fonte
25
Eu realmente gosto dessa pergunta. Não posso responder, mas gosto.
Octavian A. Damiean
4
Tim, você já obteve uma resposta? Eu estive coçando minha cabeça ao mesmo tempo. Esta é uma das APIs mais mal documentadas que já vi.
Pierre-Luc Paour
Não estou realmente com medo. Eu tive que seguir em frente. Algum dia voltarei a esse assunto.
Tim
1
Aqui tenho a mesma pergunta, quase? E a resposta, solução também. Tornou público meu código no Github.
a mesma coisa que estou pensando, eu implementei uma bússola em um dispositivo Android e está funcionando corretamente se eu recebesse ajuda da internet, está funcionando bem, mas o que é confuso é ... Suponha que meu dispositivo esteja no chão em minha direção e apontando para o norte, agora pego meu celular e coloco-o verticalmente acima da minha cabeça, e o rosto ainda está voltado para mim. Primeiramente a agulha deveria mudar sua direção e por quê. Estou pensando que não deveria, pois não mudei minha direção, mas está mudando no meu aplicativo e em todos os outros aplicativos que baixei. Alguém pode explicar por quê?
Syed Raza Mehdi

Respostas:

26

Você pode querer verificar o artigo Uma volta de tela merece outro . Isso explica por que você precisa da matriz de rotação.

Resumindo, os sensores do telefone sempre usam o mesmo sistema de coordenadas, mesmo quando o dispositivo é girado.

Em aplicativos que não estão bloqueados em uma única orientação, o sistema de coordenadas da tela muda quando você gira o dispositivo. Assim, quando o dispositivo é girado de seu modo de visualização padrão, o sistema de coordenadas do sensor não é mais o mesmo que o sistema de coordenadas da tela. A matriz de rotação, neste caso, é usada para transformar A em C (B sempre permanece fixo).

Aqui está um trecho de código para mostrar como ele pode ser usado.

SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE);

// Register this class as a listener for the accelerometer sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_NORMAL);
// ...and the orientation sensor
sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_NORMAL);

//...
// The following code inside a class implementing a SensorEventListener
// ...

float[] inR = new float[16];
float[] I = new float[16];
float[] gravity = new float[3];
float[] geomag = new float[3];
float[] orientVals = new float[3];

double azimuth = 0;
double pitch = 0;
double roll = 0;

public void onSensorChanged(SensorEvent sensorEvent) {
    // If the sensor data is unreliable return
    if (sensorEvent.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE)
        return;

    // Gets the value of the sensor that has been changed
    switch (sensorEvent.sensor.getType()) {  
        case Sensor.TYPE_ACCELEROMETER:
            gravity = sensorEvent.values.clone();
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            geomag = sensorEvent.values.clone();
            break;
    }

    // If gravity and geomag have values then find rotation matrix
    if (gravity != null && geomag != null) {

        // checks that the rotation matrix is found
        boolean success = SensorManager.getRotationMatrix(inR, I,
                                                          gravity, geomag);
        if (success) {
            SensorManager.getOrientation(inR, orientVals);
            azimuth = Math.toDegrees(orientVals[0]);
            pitch = Math.toDegrees(orientVals[1]);
            roll = Math.toDegrees(orientVals[2]);
        }
    }
}
Octavian A. Damiean
fonte
4
apenas mencione que azimute, pitch e roll NÃO são o mesmo que sair do OrientationSensor obsoleto. orientation[0] = orientation[0] >= 0 ? orientation[0]: orientation[0] + 360;vai normalizar o azimute e if (orientation[1] <= -90) { orientation[1] += (-2*(90+orientation[1])); } else if(orientation[1] >= 90){ orientation[1] += (2*(90 - orientation[1])); }vai normalizar o tom
Rafael T
@RafaelT e normalizar roll? Ou isso não faz sentido?
Matthias
@RafaelT: Sua normalização do azimute parece ter efeito: os valores vão de [-180,180] a [0, 360]. Mas os valores de pitch que recebo já estão em [-90,90], então a normalização que você propõe não tem efeito.
Matthias
O que significa se após verificar (gravity! = Null && geomag! = Null), o valor de geomag é sempre 0, não importa como eu movo o tablet? Pode ser um tablet sem sensor geomag?
Avançado de
3

Roll é uma função da gravidade, um roll de 90 graus coloca toda a gravidade no registro x.

A inclinação é a mesma, uma inclinação de 90 graus coloca todo o componente da gravidade no registro y.

Yaw / Heading / azimuth não tem efeito sobre a gravidade, está SEMPRE perpendicular à gravidade, portanto, não importa para que lado você esteja voltado para a gravidade, ela será imensurável.

É por isso que você precisa de uma bússola para avaliar, talvez isso faça sentido?

Craig
fonte
0

Eu estava tendo esse problema, então mapeei o que acontece em diferentes direções. Se o dispositivo for montado em formato de paisagem, por exemplo, em um carro, os 'graus' da bússola parecem correr de 0-275 (no sentido horário) acima de 269 (entre oeste e norte), conta regressivamente de -90 a 0, então avança de 0 a 269. 270 torna-se -90

Ainda em paisagem, mas com o dispositivo deitado de costas, meu sensor dá 0-360. e no modo retrato ele roda de 0 a 360 tanto deitado de costas quanto em pé no retrato.

Espero que ajude alguém

bob sem esperança
fonte