5

方向センサーの読み取り値がうまく取得できません。センサーの読み取り値は信頼できないように見えたので、2 つの無料のセンサー テスト アプリ ( Sensor Tester (Dicotomica)Sensor Monitoring (R's Software) ) に対してコードをテストしました。私の測定値は多くの場合、センサー テスト アプリと一致していましたが、ピッチの測定値はほぼ一致していましたが、方位角/ヨーとロールの値が最大 40 度異なる場合がありました。2 つの無料アプリは常に一致しているように見えました。

コードを小さな Android アクティビティに入れましたが、同じ不整合が発生しました。コードは次のとおりです。

public class MainActivity extends Activity implements  SensorEventListener {

    private SensorManager mSensorManager;
    private float[] AccelerometerValues;
    private float[] MagneticFieldValues;
    private float[] RotationMatrix;
    private long nextRefreshTime;           // used to ensure dump to LogCat occurs no more than 4 times a second
    private DecimalFormat df;               // used for dumping sensors to LogCat

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSensorManager = (SensorManager)getSystemService(android.content.Context.SENSOR_SERVICE);
        Sensor SensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, SensorAccelerometer, SensorManager.SENSOR_DELAY_UI);  
        Sensor SensorMagField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        mSensorManager.registerListener(this, SensorMagField, SensorManager.SENSOR_DELAY_UI);
        AccelerometerValues = new float[3];
        MagneticFieldValues = new float[3];
        RotationMatrix = new float[9];
        nextRefreshTime = 0;
        df = new DecimalFormat("#.00");
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            System.arraycopy(event.values, 0, AccelerometerValues, 0, AccelerometerValues.length);
        else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
                System.arraycopy(event.values, 0, MagneticFieldValues, 0, MagneticFieldValues.length);

        if (AccelerometerValues != null && MagneticFieldValues != null) {
            if(SensorManager.getRotationMatrix(RotationMatrix, null, AccelerometerValues, MagneticFieldValues)) {
                float[] OrientationValues = new float[3];
                SensorManager.getOrientation(RotationMatrix, OrientationValues);

                // chance conventions to match sample apps
                if (OrientationValues[0] < 0) OrientationValues[0] += 2*(float)Math.PI;
                OrientationValues[2] *= -1;

                // dump to logcat 4 times a second
                long currentTimeMillis = System.currentTimeMillis();
                if (currentTimeMillis > nextRefreshTime) {
                    nextRefreshTime = currentTimeMillis+250;
                    Log.i("Sensors",    // arrange output so that numbers line up in columns :-)
                            "(" + AngleToStr(OrientationValues[0]) + "," + AngleToStr(OrientationValues[1]) + "," + AngleToStr(OrientationValues[2])
                            + ") ("+FloatToStr(AccelerometerValues[0]) + "," + FloatToStr(AccelerometerValues[1]) + "," + FloatToStr(AccelerometerValues[2])
                            + ") ("+FloatToStr(MagneticFieldValues[0]) + "," + FloatToStr(MagneticFieldValues[1]) + "," + FloatToStr(MagneticFieldValues[2])+")");
                }               
            }
        }               
    }

    private String AngleToStr(double AngleInRadians) {
        String Str = "   "+Integer.toString((int)Math.toDegrees(AngleInRadians));
        return Str.substring(Str.length() - 3);
    }
    private String FloatToStr(float flt) {
        String Str = "      "+df.format(flt);
        return Str.substring(Str.length() - 6);
    }   

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) { }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

}

Jelly Bean 4.1.1 を実行している Galaxy Note 2 を使用しています。誰が私が間違っているのか教えてもらえますか?

2013 年 3 月 24 日更新: 詳細情報。(1) マニフェストで縦向きと横向きの切り替えを無効にしたので、getWindowManager().getDefaultDisplay().getRotation() は常にゼロです。したがって、ここでは remapCoordSystem が役立つとは思いません。これは軸を切り替えるためのものですが、私が見ているエラーは大きなエラーではなく、はるかに微妙です。(2) 精度の感度を確認しましたが、両方のセンサーが高い精度を主張している場合に矛盾が発生します。

私が見ている不一致の例として、上記のコードで (azimuth,pitch,roll) = (235,-52,-11) が得られた場合、2 つの無料アプリは同様の値を示します。しかし、私が (278, -58, -52) を見ると、アプリは (256, -58, -26) を示しています。

4

4 に答える 4

6

デバイスが平らでない場合に方向角を定義する最良の方法は、から得られる標準のオイラー角よりも適切な角度座標系を使用することだと思いますSensorManager.getOrientation(...)ここ math.stackexchange.com で説明したものをお勧めします。また、それを実装するいくつかのコードを回答 hereに入れました。方位角の適切な定義とは別に、ピッチ角のより適切な定義もあります。この方法論では、回転がどの軸に沿って発生するかに関係なく、水平面からの回転として定義されます。

最初の段落で示した 2 つのリンクから完全な詳細を取得できます。ただし、要約すると、SensorManager.getRotationMatrix(...) からの回転行列Rは

回転行列 R の定義

ここで、(E x、E y、E z )、(N x、N y、N z )、および (G x、G y、G z ) は、真東、真北、および重力の方向を指すベクトルです。次に、必要な方位角は次の式で与えられます

方位角ファイの定義

于 2013-05-07T11:36:50.340 に答える
5

それはすべて、デバイスの向きに依存します。デバイスが平らではなく、ある程度の回転がある場合、つまりPortraitまたはLandscapeではない場合、方位角は間違っています。この場合、電話remapCoordSystemする前に電話する必要がありますgetOrientation。また、少なくとも加速度計の値をフィルタリングする必要があります。
詳細については、磁場 X、Y、Z 値をデバイスからグローバル リファレンス フレームに変換する
での私の回答を参照してください。 また、平坦性の意味については、Android で加速度計を使用して XY 平面で電話の傾きを測定する方法を参照してください。

次のようにアプリをテストする必要があります。

  1. デバイスを平らにしてアプリを起動し、自分の価値を他のアプリと比較します。読み取りごとに約 30 秒かかります。このステップの間、デバイスを動かさないでください。
    このステップの私の推測: あなたの値は他のアプリの値とほとんど変わらないはずですが、最初は他のアプリより安定性が低いかもしれません.

  2. アプリの実行中に、デバイスを新しい位置に回転させ、値が安定するまで待ちます (同じ値である必要はありませんが、変動は約 1 ~ 2 度です)。同じ位置で、自分の値を他のアプリの値と比較します。同じことを繰り返しますが、回転時に他のアプリを実行します。
    このステップの私の推測: 安定した値は他のアプリの値とほとんど変わらないはずですが、値が安定するまでには時間がかかります。また、新しい位置で停止すると、アプリの最初のいくつかの値と安定した値の差が他の値よりも大きくなります。

  3. //dump to logcat のコメントの前に次のコードを追加し、向きの場合と同様にログに記録します

    デバイスが回転しないように、デバイスを箱に立てて入れます。上記の回転の値ができるだけ 0 に近いことを確認してください (少しジャンプします)。
    デバイスを直立させて、ステップ 1 と 2 を繰り返します。
    私の推測では、あなたの結果は上記のステップ 1 と 2 の私の推測と同じになるでしょう。

  4. それ以上回転しないように、デバイスを箱に斜めに直立させて置きます。上記の回転の値ができるだけ 25 または -25 に近いことを確認してください (少しジャンプします)。
    デバイスを直立させて、ステップ 1 を繰り返します。
    私の推測では、あなたの結果は他のアプリとはかなり異なるものになるでしょう。
    別の回転値で手順 4 を繰り返すことができます。

私の推測が正しければ、戻ってきて説明します。それまでの間、 Magnetic Fields, Rotation Matrix And global coordinatesで私の回答を読むことができます

于 2013-03-21T00:20:04.923 に答える
3

このパズルの一部に答えることができます。デバイスがテーブルの上に平らに置かれている場合はすべて問題ありませんが、直立していてピッチがプラスまたはマイナス 90 度の場合、ジンバ​​ル ロックが発生します。この場合、方位角とロールは未定義です。その状況では、方位角とロールは同じことを意味し、数学を調べると、azumith+roll が定義されている (pitch = +90 度の場合) か、azimuth-roll が定義されている (pitch = -90 度の場合) ことがわかります。 )、しかし、それらの 2 つは独自に定義されていません。あなたが示した例では、あなたのアプリと他のアプリの合計方位角+ロールがほぼ同じであることに注意してください。

また、加速度計の読み取り値にローパス フィルターを使用する必要がないように、android Sensor.TYPE_GRAVITY を使用することもできます。Sensor.TYPE_GRAVITY は、加速度計センサーからの加速度の影響を排除しようとするために、他のセンサーから計算されるセンサーです。ただし、Sensor.TYPE_GRAVITY が導入される前の古いバージョンの Android でアプリを実行する場合は、自分で作業を行う必要があります。

于 2013-03-26T00:12:38.177 に答える