1. ホーム
  2. android

[解決済み] コンパスを含むAndroid携帯のオリエンテーションの概要

2022-10-13 15:49:46

質問

Androidの方位センサーについて、しばらく頭を悩ませていました。 私はそれを理解したと思っていました。その後、理解していないことに気づきました。今、私は再びそれについてより良い感覚を持っていると思いますが (希望)、まだ 100% ではありません。私は、それについての私のつたない理解を説明し、私が部分的に間違っている場合、または空白を埋めるために、人々が私を修正できることを望みます。

私は、経度0度(本初子午線)、緯度0度(赤道)の位置に立っていると想像しています。この場所は実際にはアフリカの沖合にあるのですが、ご容赦ください。携帯電話の底が足元を指すように顔の前に構え、私は北(グリニッジ方向)を向いているので、携帯電話の右側はアフリカに向かって東を向いています。この向き (下の図を参照) では、X 軸は東を、Z 軸は南を、Y 軸は空を指しています。

現在、携帯電話のセンサーによって、この状況でのデバイスの向き (位置ではない) を計算することができます。この部分は常に私を混乱させてきました。おそらく、何かが機能することを受け入れる前に、それがどのように機能するかを理解したかったからでしょう。携帯電話は、2 つの異なる技術の組み合わせでその方向を計算しているようです。

その前に、緯度経度 0 度の架空の土地に戻り、上記の方向に立っていることを想像してみてください。また、目隠しをして、靴を運動場のロータリーに固定していると想像してください。もし誰かがあなたの背中を押したら、あなたは前方(北の方向)に倒れ、両手を出してその倒れを止めようとするでしょう。同様に、誰かがあなたの左肩を押したら、あなたは右手をついて倒れるでしょう。あなたの内耳には重力センサーがあります。 (YouTubeの動画) というセンサーがあり、自分が前後に倒れているか、左右に倒れているか、下に倒れているか(上に倒れているか!)を感知することができるのです。したがって、人間は携帯電話と同じ X 軸と Z 軸を中心とした位置合わせと回転を検出することができます。

今、誰かがあなたをロータリーで90度回転させ、あなたが今東を向いていると想像してください。このとき、あなたは Y 軸を中心に回転させられています。この軸は、生物学的に検出できないため、異なっています。私たちは、自分がある角度で回転していることは知っていますが、地球の磁北極との関係では、その方向を知ることはできません。 そのため、外部の道具である磁気コンパスを使う必要があります。これを使えば、自分の向いている方向がわかる。携帯電話も同じです。

さて、携帯電話には3軸の加速度センサーも搭載されています。私は NO が、私がそれを視覚化する方法は、空から降ってくる一定で均一な「雨」として重力を想像し、流れる雨の量を検出できるチューブとして上の図の軸を想像することです。携帯電話を直立させると、すべての雨はYの「管」を通って流れます。携帯電話の画面が空に向くように徐々に回転させると、Yを流れる雨の量はゼロになり、Zを流れる雨の量は最大量になるまで着実に増加します。同様に、今度は携帯電話を横向きにすると、X管には最終的に最大量の雨が集まります。したがって、3つのチューブを流れる雨の量を測定することによって、携帯電話の向きによって向きを計算することができます。

携帯電話には電子コンパスも搭載されており、通常のコンパスのように仮想の針が磁北を指しています。Android はこれら 2 つのセンサーの情報を統合し、携帯電話上で SensorEventTYPE_ORIENTATION が生成されると values[3] の配列には

値[0]を持ちます。方位 - (磁北より東のコンパスの方位)

値[1]: ピッチ、X軸の周りの回転(携帯電話は前方または後方に傾いている)。

値[2]。ロール、Y軸周りの回転(携帯電話は左側または右側に傾いている)。

Android が 3 つ目の加速度センサーの値ではなく方位 (コンパスの方位) を示すのは、コンパスの方位の方がより便利だからだと思います (つまり私は知らない)。なぜこのタイプのセンサーを非推奨にしたのかはよくわかりません。 SensorEvent というタイプの TYPE_MAGNETIC_FIELD . イベントの value[] の配列に渡される必要があります。 SensorManger.getRotationMatrix(..) メソッドに渡して回転行列を取得し (下記参照)、それを SensorManager.getOrientation(..) メソッドに渡されます。 Android チームがなぜ非推奨としたのか、誰か知っていますか? Sensor.TYPE_ORIENTATION ? それは効率化のためでしょうか?それは、同じような 質問 に対するあるコメントで示唆されていることですが、それでもあなたは 開発/サンプル/Compass/src/com/example/android/compass/CompassActivity.javaに登録する必要があります。 の例を見てください。

ここからは回転行列の話をしたいと思います。(ここが一番わからないところです) 上の図はAndroidのドキュメントにあった3つの図で、A,B,Cと呼ぶことにします。

A = SensorManger.getRotationMatrix(...)メソッドの図形で、Worldの座標系を表しています。

B = SensorEvent APIで使用される座標系。

C= SensorManager.getOrientation(...)メソッド図形

私の理解では、Aはquot;世界の座標系を表し、これは地球上の位置が(緯度、経度)カップルとオプション(高度)として与えられる方法を指していると推測されます。Xは 東経(easting)です。 の座標で、Y は "northing"。 の座標です。Z は空を指し、高度を表します。

図Bに示す電話座標系は固定されています。そのY軸は常に上部を指しています。回転行列は携帯電話によって常に計算されており、2 つの座標系をマッピングすることができます。つまり、回転行列が B の座標系を C に変換していると考えてよいのでしょうか? ということは SensorManager.getOrientation(..) メソッドを使用すると values[] 配列に図 C に対応する値を指定します。 携帯電話を空に向けると、回転行列は等値行列(行列の数学的な値が1)になり、デバイスが世界の座標系と一致するためマッピングの必要がないことを意味します。

さて、そろそろ終わりにしましょう。前に言ったように、私がどこで失敗したか、または人々を助けたか (あるいはさらに人々を混乱させたか!) を人々が教えてくれることを望みます。

どのように解決するのですか?

を確認するのがよいでしょう。 1 回のスクリーン ターンでもう 1 回のスクリーン ターンに値する の記事をご覧ください。なぜ回転行列が必要なのかが説明されています。

簡単に言うと、携帯電話のセンサーは、デバイスを回転させても常に同じ座標系を使用します。

単一の方向にロックされていないアプリケーションでは、デバイスを回転させると画面の座標系が変化します。 したがって、デバイスをデフォルトのビューモードから回転させると、センサー座標系はスクリーン座標系と同じではなくなります。 この場合の回転行列は、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]);
        }
    }
}