15

デバイスの向きに応じてImageViewを配置する必要があるアプリケーションを作成しています。磁場センサーと加速度センサーの値を使用して、デバイスの向きを計算します。

SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);

私の問題は、ImageViewの配置が向きの変化に非常に敏感であるということです。imageviewを常に画面上でジャンプさせます。(度が変わるため)

これは、私のデバイスが磁場の読み取りに影響を与える可能性のあるものに近いためである可能性があることを読みました。しかし、これが唯一の理由ではありません。

いくつかのアプリケーションをダウンロードしてみたところ、「3Dコンパス」と「コンパス」の読み取り値が非常に安定していることがわかりました(ノイズフィルターを設定した場合)。アプリケーションでも同じ動作をしたいと思います。

「ローパスフィルター」を追加することで、測定値の「ノイズ」を微調整できることを読みましたが、これを実装する方法がわかりません(数学が不足しているため)。

誰かが私のデバイスでより安定した読み取りを作成するのを手伝ってくれることを願っています。デバイスへの少しの動きが現在の向きに影響を与えることはありません。今、私は小さなことをします

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }

ノイズのビットをフィルタリングします。しかし、それはあまりうまく機能していません:)

4

5 に答える 5

31

私はAndroidでコンパスを使用したことはありませんが、以下に示す基本的な処理(JavaScriptで)はおそらくあなたのために働くでしょう。

これは、 Windows Phoneチームが推奨する加速度計のローパスフィルターに基づいており、コンパスに合わせて変更が加えられています(360インチごとの周期的な動作)。

コンパスの読み取り値は度単位で、0〜360の範囲で変動し、出力も同様であると想定しています。

フィルタで2つのことを達成したい:

  1. 変化が小さい場合は、ガタつきを防ぐため、徐々にその方向に向けてください。
  2. 変化が大きい場合は、遅れを防ぐために、すぐにその方向に向けてください(コンパスをスムーズに動かしたい場合はキャンセルできます)。

そのために、2つの定数があります。

  1. 動きがどれだけスムーズになるかを定義するイージングフロート(1はスムージングなし、0は更新されない、私のデフォルトは0.5)。これをSmoothFactorCompassと呼びます。
  2. 距離がすぐに曲がるのに十分な大きさのしきい値(0は常にジャンプ、360はジャンプしない、私のデフォルトは30)。これをSmoothThresholdCompassと呼びます。

呼び出し全体で保存された1つの変数、oldCompassと呼ばれるfloatがあり、これはアルゴリズムの結果です。

したがって、変数の定義は次のとおりです。

var SmoothFactorCompass = 0.5;
var SmoothThresholdCompass = 30.0;
var oldCompass = 0.0;

関数はnewCompassを受け取り、結果としてoldCompassを返します。

if (Math.abs(newCompass - oldCompass) < 180) {
    if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
        oldCompass = newCompass;
    }
    else {
        oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass);
    }
}
else {
    if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
        oldCompass = newCompass;
    }
    else {
        if (oldCompass > newCompass) {
            oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360;
        } 
        else {
            oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360;
        }
    }
}

この問題は5か月前に開かれ、おそらくもう関係がないようですが、他のプログラマーが役立つと思うかもしれません。

OdedElyada。

于 2011-06-24T01:51:10.347 に答える
19

このローパスフィルターは、ラジアン単位の角度で機能します。コンパスの読み取りごとにadd関数を使用してから、averageを呼び出して平均を取得します。

public class AngleLowpassFilter {

    private final int LENGTH = 10;

    private float sumSin, sumCos;

    private ArrayDeque<Float> queue = new ArrayDeque<Float>();

    public void add(float radians){

        sumSin += (float) Math.sin(radians);

        sumCos += (float) Math.cos(radians);

        queue.add(radians);

        if(queue.size() > LENGTH){

            float old = queue.poll();

            sumSin -= Math.sin(old);

            sumCos -= Math.cos(old);
        }
    }

    public float average(){

        int size = queue.size();

        return (float) Math.atan2(sumSin / size, sumCos / size);
    }
}

Math.toDegrees()またはを使用Math.toRadians()して変換します。

于 2013-09-20T07:32:14.830 に答える
4

たとえば、350と10の平均は180ではないことに注意してください。私の解決策:

int difference = 0;
for(int i= 1;i <numberOfAngles;i++){
    difference += ( (angles[i]- angles[0] + 180 + 360 ) % 360 ) - 180;
}
averageAngle = (360 + angles[0] + ( difference / numberOfAngles ) ) % 360;
于 2013-08-05T14:44:51.823 に答える
3

ローパスフィルター(LPF)は、高速で変化する信号をブロック
し、信号のゆっくりとした変化のみを許可します。これは、小さな
突然の変更は無視されることを意味します。

これをソフトウェアに実装する標準的な方法
は、最後のN個のサンプルの移動平均を取り、その値を報告することです。Nを3から始めて
、アプリで十分に平滑化された応答が見つかるまでNを増やし続けます。

Nを高くすると、システムの応答が遅くなることに注意してください。

于 2011-03-05T03:58:51.893 に答える
2

この関連する質問に対する私の答えを参照してください:センサーからのデータの平滑化

ソフトウェアローパスフィルターは、基本的にその修正バージョンです。確かに、その回答では、別の関連する質問へのこのリンクを提供しました:ローパスフィルターソフトウェア?

于 2011-01-15T13:08:29.303 に答える