나침반을 포함한 Android 휴대 전화 방향 개요


107

나는 한동안 Android 방향 센서를 둘러 보려고 노력했습니다. 이해했다고 생각했습니다. 그런 다음 나는 그렇지 않다는 것을 깨달았습니다. 이제는 (희망) 다시 한 번 더 좋은 느낌을 받았지만 아직 100 %는 아닙니다. 나는 그것에 대한 나의 고르지 못한 이해를 시도하고 설명 할 것이며, 내가 부분적으로 잘못되었거나 빈칸을 채우면 사람들이 나를 바로 잡을 수 있기를 바랍니다.

나는 경도 0도 (프라임 자오선)와 위도 0도 (적도)에 서 있다고 상상합니다. 이 위치는 실제로 아프리카 해안의 바다에 있지만 저를 참아주세요. 나는 전화기 바닥이 내 발을 가리 키도록 내 얼굴 앞에 전화기를 들고있다. 나는 북쪽 (그리니치를 바라보고 있음)을 향하고 있으므로 전화의 오른쪽이 아프리카를 향해 동쪽을 가리 킵니다. 이 방향 (아래 다이어그램 참조)에서 X 축은 동쪽을 가리키고 Z 축은 남쪽을 가리키고 Y 축은 하늘을 가리 킵니다.

이제 전화기의 센서를 사용하여이 상황에서 장치의 방향 (위치 아님)을 파악할 수 있습니다. 이 부분은 항상 저를 혼란스럽게 만들었습니다. 아마도 그것이 제대로 작동한다는 것을 받아들이 기 전에 어떤 것이 어떻게 작동했는지 이해하고 싶었 기 때문일 것입니다. 전화기는 두 가지 다른 기술을 조합하여 방향을 조정하는 것 같습니다.

그것에 도달하기 전에 위도 및 경도 0도에서 위에서 언급 한 방향으로 서있는 가상의 땅에 다시 서 있다고 상상해보십시오. 당신이 눈가리개를하고 신발이 놀이터 로터리에 고정되어 있다고 상상해보십시오. 누군가가 당신을 뒤에서 밀면 앞으로 (북쪽으로) 넘어지고 양손을 내밀어 넘어집니다. 마찬가지로 누군가가 왼쪽 어깨를 밀면 오른손으로 넘어 질 것입니다. 내 이에는 "중력 센서" (유튜브 클립) 가있어 앞으로 / 뒤로 떨어지는 지, 왼쪽 / 오른쪽으로 떨어지는 지 또는 아래로 (또는 위로 !!) 떨어지는지를 감지 할 수 있습니다. 따라서 인간은 전화기와 동일한 X 및 Z 축을 중심으로 정렬 및 회전을 감지 할 수 있습니다.

이제 누군가가 원형 교차로에서 당신을 90도 회전시켜 당신이 이제 동쪽을 향하도록한다고 상상해보십시오. Y 축을 중심으로 회전하고 있습니다. 이 축은 생물학적으로 감지 할 수 없기 때문에 다릅니다. 우리는 우리가 어느 정도 각을 이루고 있다는 것을 알고 있지만 행성의 자기 북극과 관련하여 방향을 알지 못합니다. 대신 외부 도구 인 자기 나침반을 사용해야합니다. 이를 통해 우리가 어떤 방향을 향하고 있는지 확인할 수 있습니다. 우리 전화도 마찬가지입니다.

이제 휴대 전화에는 3 축 가속도계도 있습니다. 나는이 없다 NO그들이 실제로 어떻게 작동하는지 생각하지만 내가 시각화하는 방식은 중력을 하늘에서 떨어지는 일정하고 균일 한 '비'로 상상하고 위 그림의 축을 통해 흐르는 비의 양을 감지 할 수있는 튜브로 상상하는 것입니다. 전화기를 똑바로 세우면 모든 비가 Y '튜브'를 통해 흐릅니다. 전화기가 점차적으로 회전하여 화면이 하늘을 향하면 Y를 통해 흐르는 비의 양은 0으로 감소하고 Z를 통한 음량은 최대 비가 통과 할 때까지 꾸준히 증가합니다. 마찬가지로 이제 전화기를 옆으로 기울이면 X 튜브는 결국 최대 비를 모을 것입니다. 따라서 전화기의 방향에 따라 3 개의 튜브를 통해 흐르는 빗물의 양을 측정하여 방향을 계산할 수 있습니다.

전화기에는 또한 일반 나침반처럼 작동하는 전자 나침반이 있습니다. "가상 바늘"은 자북을 가리 킵니다. 마다 안드로이드 병합 이러한 두 개의 센서로부터의 정보가되도록 SensorEventTYPE_ORIENTATION생성되고 values[3]배열 갖는
값 [0] : 방위각 - (자북의 나침반 베어링 이스트)는
값 [1] 피치, X 축 주위의 회전 (전화입니다 앞으로 또는 뒤로 기울임)
값 [2] : 롤, y 축을 중심으로 회전 (휴대 전화가 왼쪽 또는 오른쪽으로 기울어 짐)

그래서 안드로이드가 세 번째 가속도계를 읽는 것보다 방위각 (나침반 방위)을 제공하는 이유는 나침반 방위가 더 유용하기 때문이라고 생각합니다. SensorEvents 유형에 대해 시스템에 리스너를 등록해야하는 것처럼 보이므로 이러한 유형의 센서를 더 이상 사용하지 않는 이유를 잘 모르겠습니다 TYPE_MAGNETIC_FIELD. 이벤트의 value[]배열은 SensorManger.getRotationMatrix(..)메서드로 전달되어 회전 행렬 (아래 참조)을 얻은 다음 SensorManager.getOrientation(..)메서드 로 전달되어야합니다 . 누구든지 Android 팀이 지원 중단 된 이유를 알고 Sensor.TYPE_ORIENTATION있습니까? 효율성 문제입니까? 이것이 비슷한 질문 에 대한 의견 중 하나에 암시되어 있지만 여전히 다른 유형의 청취자를 등록해야합니다.development / samples / Compass / src / com / example / android / compass / CompassActivity.java 예제.

이제 회전 행렬에 대해 이야기하고 싶습니다. (여기가 가장 확실하지 않습니다.) 위의 Android 문서에있는 세 가지 그림이 있습니다.이를 A, B, C라고 부릅니다.

A = SensorManger.getRotationMatrix (..) 메서드 그림이며 세계의 좌표계를 나타냅니다.

B = SensorEvent API에서 사용하는 좌표계.

C = SensorManager.getOrientation (..) 메서드 그림

그래서 내 이해는 A가 "세계의 좌표계"를 나타낸다는 것인데, 내가 생각하기에 행성상의 위치가 선택적인 (고도)와 함께 (위도, 경도) 커플로 주어지는 방식을 언급한다고 생각합니다. X는 "동쪽" 좌표이고 Y는 "북쪽" 좌표입니다. Z는 하늘을 가리키며 고도를 나타냅니다.

전화 좌표계는 그림 B에 표시되어 있습니다. Y 축은 항상 상단을 가리 킵니다. 회전 행렬은 전화기에 의해 지속적으로 계산되며 둘 사이의 매핑을 허용합니다. 그래서 회전 행렬이 B의 좌표계를 C로 변환한다고 생각하는 것이 맞습니까? 따라서 SensorManager.getOrientation(..)메서드 를 호출 values[]할 때 그림 C에 해당하는 값이 있는 배열 을 사용합니다 . 전화기가 하늘을 가리킬 때 회전 행렬은 단위 행렬 (수학적 1에 해당하는 행렬)이므로 장치가 정렬되어 있으므로 매핑이 필요하지 않습니다. 세계의 좌표계와 함께.

확인. 이제 그만두는 게 좋을 것 같아요. 내가 전에 말했듯이 사람들이 내가 어디를 엉망으로 만들 었는지 또는 사람들을 도왔는지 (또는 사람들을 더 혼란스럽게 했습니까!)


25
이 질문이 정말 마음에 듭니다. 대답 할 수 없지만 마음에 들어요.
Octavian A. Damiean 2011 년

4
팀, 답은 받았어? 나는 동시에 내 머리를 긁적이었다. 이것은 내가 본 것 중 가장 잘 문서화되지 않은 API 중 하나입니다.
Pierre-Luc Paour 2011-06-23

정말 두렵지 않습니다. 나는 계속 나아가 야했다. 언젠가이 문제로 돌아올 것입니다.
Tim

1
여기 에 거의 같은 질문이 있습니다. 그리고 응답, 솔루션 도. 만들어 내 코드 공개 Github에서에서.

내가 궁금해하는 것과 똑같은 것, 나는 안드로이드 장치에 나침반을 구현했으며 인터넷에서 도움을 받으면 제대로 작동하지만 혼란스러운 점은 ... 내 장치가 바닥면에 있다고 가정 나를 향하고 북쪽을 향한 이제 나는 휴대폰을 들고 머리 위에 수직으로 올려 놓고 얼굴은 여전히 ​​나를 향하고 있습니다. 먼저 바늘이 방향과 이유를 바꿔야 했습니까? 내가 내 방향을 변경하지 않았기 때문에 내가 생각하지 않아야한다고 생각하지만 내 앱과 내가 다운로드 한 다른 모든 앱에서 변경되고 있습니다. 아무도 이유를 설명 할 수 있습니까?
Syed Raza Mehdi 2014 년

답변:


26

당신은 체크 아웃 할 수 있습니다 하나 개의 화면 전원을 켜고는 또 다른 의하여 가치가 기사를. 회전 행렬이 필요한 이유를 설명합니다.

간단히 말해서 휴대 전화의 센서는 기기가 회전하더라도 항상 동일한 좌표계를 사용합니다.

단일 방향으로 잠기지 않은 응용 프로그램에서는 장치를 회전하면 화면 좌표계가 변경됩니다. 따라서 장치가 기본보기 모드에서 회전 할 때 센서 좌표계는 더 이상 화면 좌표계와 동일하지 않습니다. 이 경우 회전 행렬은 A를 C로 변환하는 데 사용됩니다 (B는 항상 고정되어 있음).

다음은 사용 방법을 보여주는 코드 스 니펫입니다.

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]);
        }
    }
}

4
방위각, 피치 및 롤은 더 이상 사용되지 않는 OrientationSensor에서 나오는 것과 동일하지 않습니다. orientation[0] = orientation[0] >= 0 ? orientation[0]: orientation[0] + 360;방위각 if (orientation[1] <= -90) { orientation[1] += (-2*(90+orientation[1])); } else if(orientation[1] >= 90){ orientation[1] += (2*(90 - orientation[1])); }을 정규화하고 피치를 정규화합니다
Rafael T

@RafaelT 및 롤 정규화? 아니면 말이 안 되나요?
Matthias

@RafaelT : 방위각의 정규화가 영향을 미치는 것 같습니다. 값은 [-180,180]에서 [0, 360]으로 이동합니다. 그러나 내가 얻은 피치 값은 이미 [-90,90]이므로 제안한 정규화는 효과가 없습니다.
Matthias

(gravity! = null && geomag! = null) 확인 후 태블릿을 어떻게 움직여도 geomag의 값이 항상 0이면 무엇을 의미합니까? Geomag 센서가없는 태블릿 일 수 있습니까?
고급 '

3

롤은 중력의 함수이며 90도 롤은 모든 중력을 x 레지스터에 넣습니다.

피치는 동일합니다. 90도 피치 업은 중력의 모든 구성 요소를 y 레지스터에 넣습니다.

Yaw / Heading / azimuth는 중력에 영향을 미치지 않으며 항상 중력에 직각을 유지하므로 중력에 직면하는 방향에 관계없이 측정 할 수 없습니다.

이것이 평가할 나침반이 필요한 이유입니다.



0

나는이 문제를 겪고 있었기 때문에 다른 방향에서 일어나는 일을 매핑했습니다. 장치가 가로 방향으로 장착 된 경우, 예를 들어 자동차 마운트에서 나침반의 '도'가 0-275 (시계 방향)에서 269 (서쪽과 북쪽 사이) 위의 0-275 (서쪽과 북쪽 사이)에서 실행되는 것처럼 보이면 -90에서 0까지 거꾸로 계산합니다. 0에서 269로 전달됩니다. 270은 -90이됩니다.

여전히 풍경에 있지만 장치가 뒤쪽에 누워 있으면 센서가 0-360을 제공합니다. 세로 모드에서는 등을 대고 서서 세로로 서서 0-360을 실행합니다.

누군가를 돕는 희망

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.