1

私が作成しているアプリケーションには、カメラコンパスが必要です。アプリケーションは、マニフェストでランドスケープ モードに設定されています。
まず、コンパスを実装しました。Android Developers で提案されているように、AccelerometerMagnetic Fieldの 2 つのセンサーを使用しました。これが私がやった方法です:
私は自分の活動を実装していSensorEventListenerます。onCreate()私は以下を使用して初期化しますsensorManager:

sManager = (SensorManager) getSystemService(SENSOR_SERVICE);

リスナーを次のonResume()ように登録します。

sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
sManager.registerListener(this, sManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),SensorManager.SENSOR_DELAY_NORMAL);

もちろん、で登録を解除しますonPause()。使いませんonAccuracyChanged()。これは私がすることですonSensorChanged()

@Override
public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
        case Sensor.TYPE_MAGNETIC_FIELD:
            mags = event.values.clone();
            break;
        case Sensor.TYPE_ACCELEROMETER:
            accels = event.values.clone();
            break;
    }

    if (mags != null && accels != null) {
        gravity = new float[9];
        magnetic = new float[9];
        SensorManager.getRotationMatrix(gravity, magnetic, accels, mags);
        float[] outGravity = new float[9];

        float inclination = (float) Math.acos(gravity[8]);
        if (inclination < Math.toRadians(25)
                || inclination > Math.toRadians(155)) {
            // device is close to flat. Remap for landscape.
            SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_Y,SensorManager.AXIS_MINUS_X, outGravity);
            SensorManager.getOrientation(outGravity, values);
        } else {
            // device is not flat. Remap for landscape and perpendicular
            SensorManager.remapCoordinateSystem(gravity, SensorManager.AXIS_X,SensorManager.AXIS_Z, outGravity);
            SensorManager.getOrientation(outGravity, values);
        }
        azimuth =  Math.round(Math.toDegrees(values[0]));
    }
}

ご覧のとおり、電話がテーブルの上に平らに置かれているときと、ユーザーが電話を持っているとき (写真を撮るときのように) を区別します。このコードを単独で使用すると、多かれ少なかれすべてがうまく機能します。電話がテーブルの上に置かれているときと、テーブルに対して垂直に保持されているときの両方で、正しい方位角の値を取得しています (約 5 ~ 10 度の差ですが、それで問題ありません)。

アプリケーションにカメラ プレビューを追加すると、問題が発生します。私は自分の活動を実装していSurfaceHolder.Callbackます。カメラを次のように初期化しますonCreate()

SurfaceView cameraView = (SurfaceView)findViewById(R.id.camera_view);
surfaceHolder = cameraView.getHolder();
surfaceHolder.addCallback(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

これは私がインターフェースを実装する方法です:

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    camera = Camera.open();
    camera.setDisplayOrientation(0);
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    if (isCameraOn) {
        camera.stopPreview();
        isCameraOn = false;
    }

    if (camera != null) {
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
            isCameraOn = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    camera.stopPreview();
    camera.release();
    camera = null;
}

プロジェクトにカメラ コードを追加し、電話の画面にカメラを表示すると、電話が突然垂直になるとセンサーが正しく動作しません。電話がテーブルの上に平らに置かれている場合、取得している方位角の値は正しいです。電話がテーブルに対して垂直に保持されている場合、方位角の値は約 40 度ずれています (安定していますが)。

私は解決策を探してみました (自分自身とオンラインの両方で) が、これまでのところ私の努力は無駄でした. この問題に対処する方法について、何らかの方向性を示したいと思います。
ありがとう!

4

1 に答える 1

0

最初の TYPE_MAGNETIC_FIELD センサーは、すべてのデバイスで利用できるわけではありません。TYPE_ACCELEROMETER センサーを単独で使用して、要件を達成できます。

加速度計センサーを取得する

Sensor accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

センサー変更イベント呼び出し時に値を比較してコピーするだけです

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
    mGravity = event.values;
}

次に、以下の関数を使用して、すべての軸のセンサー値を取得できます。

public int[] getDeviceAngles() {
  float[] g = mGravity.clone();
  double normOfG = Math.sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);

  // Normalize the accelerometer vector
  g[0] = (float) (g[0] / normOfG);
  g[1] = (float) (g[1] / normOfG);
  g[2] = (float) (g[2] / normOfG);

  int x = (int) Math.round(Math.toDegrees(Math.atan2(g[1], g[0])));
  int pitch = (int) Math.round(Math.toDegrees(Math.atan2(g[1], g[2])));
  int rollValue = (int) Math.round(Math.toDegrees(Math.atan2(g[2], g[0])));
  int pitchValue = pitch * -1;
  int[] values = new int[3];
  values[0] = x;
  values[1] = pitchValue;
  values[2] = rollValue;
  //values contains: azimut, pitch and roll
  return values;
}
于 2016-12-21T19:34:41.980 に答える