web-dev-qa-db-ja.com

Android SensorManagerのremapCoordinateSystemの奇妙な方法

APIデモ->グラフィックス-> コンパス

デバイスの自然な向きを変更しない限り、正しく機能します。ほとんどの携帯電話では縦向きで、ほとんどの10インチタブレットは横向きです。変更する場合は、これを90度回転させる必要があります。そのシステムの3D修正を見たいのですが。

100%確実に使用する必要があります remapCoordinateSystem() メソッド。

それらの軸マッピング(理論数学)がどのように計算されるかについての説明を見ることができれば、どのように(コード)それが素晴らしいかを見たいと思います。

私は理解しようとしましたが、すべての線形代数を忘れました。

ここ なぜ使用しなければならないのかはわかりますが、方法はわかりません。

float R[] = new float[9];
// X  (product of Y and Z) and roughly points East
// Y: points to Magnetic NORTH and tangential to ground
// Z: points to SKY and perpendicular to ground
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);

これらの座標はこの位置にあるようです:-デバイスはテーブルで言います(x軸とy軸はテーブル上にあります)

device orientation

のみ

getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_0

問題は、このコードをどのように完成させるかです:-それらのケースブランチ

switch (mScreenRotation) {
    case Surface.ROTATION_0:
    Log.v("SurfaceRemap", "0 degree");
    axisX = SensorManager.AXIS_X;// is this valid?
    axisY = SensorManager.AXIS_Y;// is this valid?
    break;

    case Surface.ROTATION_90:
    Log.v("SurfaceRemap", "90 degree");
    // examples says remapCoordinateSystem(inR, AXIS_Y, AXIS_MINUS_X, outR);
    axisX = SensorManager.AXIS_Y;
    axisY = SensorManager.AXIS_MINUS_X;
    break;

    case Surface.ROTATION_180:
    Log.v("SurfaceRemap", "180 degree");
    break;

    case Surface.ROTATION_270:
    Log.v("SurfaceRemap", "270 degree");
    break;

    default:
    Log.v("SurfaceRemap", "don't know the mScreenRotation value: "+mScreenRotation+" you should never seen this message!");
    break;
}


boolean remapped = SensorManager.remapCoordinateSystem(R, axisX, axisY, R);

float orientation[] = new float[3];

SensorManager.getOrientation(R, orientation);// All three angles above are in radians and positive in the counter-clockwise direction.
inclination = SensorManager.getInclination(I);

編集:私は小さなテストアプリケーションを作成しました。画面上に画面の回転が表示されます:0、90、270度(現在180度にすることはできません)

回転0が変更されていない場合(axisX = SensorManager.AXIS_X;axisY = SensorManager.AXIS_Y;)90度よりも次のようになります。

axisX = SensorManager.AXIS_MINUS_Y;
axisY = SensorManager.AXIS_X;

グーグルのドキュメントがどこか間違った値を言っているよりも!質問はどこですか?

getRotationMatrix これを返します:

enter image description here

Xは、ベクトル積Y.Zとして定義されます(デバイスの現在の位置で地面に接し、おおよそ東を指します)。

Yは、デバイスの現在の位置で地面に接しており、磁北極を指しています。

Zは空を指し、地面に垂直です。

上記の電話を参照してください!バックカメラを地面に向けて、左から右に行きたいです。

getOrientation これを返します:

enter image description here

Xは、ベクトル積Y.Zとして定義されます(デバイスの現在の位置で地面に接し、おおよそ西を指します)。

Yは、デバイスの現在の位置で地面に接しており、磁北極を指しています。

Zは地球の中心を指し、地面に垂直です。

values[0]:方位角、Z軸を中心とした回転。

values[1]:ピッチ、X軸を中心とした回転。

values[2]:ロール、Y軸を中心とした回転。

電話はどうあるべきですか?

最後に、航空機のような角度の値が欲しいです。北に向かっている私の電話(私):(ヨーは方位角です)

enter image description here

              if ScreenRotation = 0 degree
Pitch axis = -orientationAxisX  =  rotationAxisX
Roll axis  =  orientationAxisY  =  rotationAxisY 
Yaw axis   =  orientationAxisZ  = -rotationAxisZ
18
user529543

スイッチブランチを完了するには、remapCoordinateSystemメソッドjavadocに従って考えてみます。

[〜#〜] x [〜#〜]デバイスのX軸がマップされるワールド軸と方向を定義します。
[〜#〜] y [〜#〜]デバイスのY軸がマップされるワールド軸と方向を定義します。

したがって、デバイスを自然な方向(90、180、または270度)から回転させて、次のことを自問してください。元のデバイス方向のX正軸は、現在のデバイス方向でどの軸に対応しますか。そして、Y軸についても同じです。

したがって、デバイスを90度回転させると、元のXの正の軸が現在の正のY軸に対応し、元の正のY軸が現在の方向の負のX軸に対応することがわかります。

したがって、次のようになります。

switch (mScreenRotation) {
    case Surface.ROTATION_0:
        axisX = SensorManager.AXIS_X;
    axisY = SensorManager.AXIS_Y;
        break;

    case Surface.ROTATION_90:
        axisX = SensorManager.AXIS_Y;
    axisY = SensorManager.AXIS_MINUS_X;
        break;

    case Surface.ROTATION_180:
        axisX = SensorManager.AXIS_MINUS_X;
    axisY = SensorManager.AXIS_MINUS_Y;
        break;

    case Surface.ROTATION_270:
        axisX = SensorManager.AXIS_MINUS_Y;
    axisY = SensorManager.AXIS_X;
        break;

    default:
        break;
}

それは私のために働いた、それが役立つことを願っています。

26
keianhzo

Keianhzoに感謝します、あなたの答えは地面に平らな電話でうまくいきます。ディスプレイを「透視」するARアプリケーションの場合、これが機能することがわかりました。適切な軸を使用してください。

int screenRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
//use the correct axis
int axisX = SensorManager.AXIS_X;
int axisY = SensorManager.AXIS_Y;
switch (mMode) {
    case LOOK_THROUGH: {
        // look through always uses x and z
        axisX = SensorManager.AXIS_X;
        axisY = SensorManager.AXIS_Z;
        break;
    }
    case FLAT: {
        // flat changes the x axis depending on rotation state
        switch (screenRotation) {
            case Surface.ROTATION_0:
                axisX = SensorManager.AXIS_X;
                axisY = SensorManager.AXIS_Y;
                break;
            case Surface.ROTATION_90:
                axisX = SensorManager.AXIS_Y;
                axisY = SensorManager.AXIS_MINUS_X;
                break;
            case Surface.ROTATION_180:
                axisX = SensorManager.AXIS_MINUS_X;
                axisY = SensorManager.AXIS_MINUS_Y;
                break;
            case Surface.ROTATION_270:
                axisX = SensorManager.AXIS_MINUS_Y;
                axisY = SensorManager.AXIS_X;
                break;
            default:
                break;
        }
        break;
    }
    default:
        break;
}

方向度を取得します。

boolean success = SensorManager.remapCoordinateSystem(getQuaternion().getMatrix4x4().getMatrix(), axisX, axisY, mRotationMatrixTransformed);
if (success) {
    SensorManager.getOrientation(mRotationMatrixTransformed, mOrientationValues);

    for (int i = 0; i < 3; i++) {
        mOrientationDegrees[i] = (float) Math.toDegrees(mOrientationValues[i]);
    }
//And for look through, add the rotation state
    if (mMode == MODE.LOOK_THROUGH) {
    // look through has different angles depending on rotation state
    switch (screenRotation) {
        case Surface.ROTATION_90: {
            mOrientationDegrees[2] += 90;
            break;
        }
        case Surface.ROTATION_180: {
            mOrientationDegrees[2] += 180;
            break;
        }
        case Surface.ROTATION_270: {
            mOrientationDegrees[2] += 270;
            break;
        }
    }
}
4
Björn Kechel

これが私のアプリケーションで魔法を使う方法です:

        float[] rotationMatrixOrig = new float[9];
        SensorManager.getRotationMatrix(rotationMatrixOrig, null, lastAccelerometerValue, lastMagnetometerValue);

        int screenRotation = app.getCurrentActivity().getWindowManager().getDefaultDisplay().getRotation();
        int axisX, axisY;
        boolean isUpSideDown = lastAccelerometerValue[2] < 0;

        switch (screenRotation) {
            case Surface.ROTATION_0:
                axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X);
                axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ? 
                        (isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) :
                        (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y));
                break;
            case Surface.ROTATION_90:
                axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y);
                axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ? 
                        (isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) :
                        (isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X));
                break;
            case  Surface.ROTATION_180:
                axisX = (isUpSideDown ? SensorManager.AXIS_X : SensorManager.AXIS_MINUS_X);
                axisY = (Math.abs(lastAccelerometerValue[1]) > 6.0f ? 
                        (isUpSideDown ? SensorManager.AXIS_Z : SensorManager.AXIS_MINUS_Z) :
                        (isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y));
                break;
            case Surface.ROTATION_270:
                axisX = (isUpSideDown ? SensorManager.AXIS_Y : SensorManager.AXIS_MINUS_Y);
                axisY = (Math.abs(lastAccelerometerValue[0]) > 6.0f ? 
                        (isUpSideDown ? SensorManager.AXIS_MINUS_Z : SensorManager.AXIS_Z) :
                        (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X));
                break;
            default:
                axisX = (isUpSideDown ? SensorManager.AXIS_MINUS_X : SensorManager.AXIS_X);
                axisY = (isUpSideDown ? SensorManager.AXIS_MINUS_Y : SensorManager.AXIS_Y);
        }

        float[] rotationMatrix = new float[9];
        SensorManager.remapCoordinateSystem(rotationMatrixOrig, axisX, axisY, rotationMatrix);
3
Orka

電話UIが回転0にロックされている場合、次の値なしで取得していますremapCoordinateSystem()

Pitch (phone) = -Pitch   (API)
Roll  (phone) =  Roll     (API)
Yaw   (phone) =  Azimuth  (API)
  • 少なくとも0,0,0の値に近い。

電話UIが強制的に回転90された場合:

ヨー値は古い方向で-90度(-PI/2)です!!! =>私は実際には北ではなく東に行きます。

電話を0,0,0の位置に持っていくと:

Pitch (phone) = -Roll    (API)
Roll  (phone) = -Pitch   (API)
Yaw   (phone) =  Azimuth (API)

電話UIが180回転を強制された場合:

ヨー値は古い方向で+/- 180度(+/- PI)です!!! =>私は実際には北ではなく南に行きます。

電話を0,0,0の位置に持っていくと:

Pitch (phone) =  Pitch   (API)
Roll  (phone) = -Roll    (API)
Yaw   (phone) =  Azimuth (API)

電話UIが強制的に回転270の場合:

ヨー値は古い方向で+90度(+ PI/2)です!!! =>私は実際には北ではなく西に行きます。

電話を0,0,0の位置に持っていくと:

Pitch (phone) =  Roll    (API)
Roll  (phone) =  Pitch   (API)
Yaw   (phone) =  Azimuth (API)

私は少し修正を書き、次のようにテストしました:Android:screenOrientation="fullSensor"

public static final void fixRotation0(float[] orientation) { //azimuth, pitch, roll
    orientation[1] = -orientation[1]; // pitch = -pitch
}

public static final void fixRotation90(float[] orientation) { //azimuth, pitch, roll
    orientation[0] += Math.PI / 2f; // offset
    float tmpOldPitch = orientation[1];
    orientation[1] = -orientation[2]; //pitch = -roll
    orientation[2] = -tmpOldPitch; // roll  = -pitch    
}

public static final void fixRotation180(float[] orientation) { //azimuth, pitch, roll
    orientation[0] = (float)(orientation[0] > 0f ? (orientation[0] - Math.PI) : (orientation[0] + Math.PI)); // offset
    orientation[2] = -orientation[2]; // roll = -roll
}

public static final void fixRotation270(float[] orientation) { //azimuth, pitch, roll
    orientation[0] -= Math.PI / 2; // offset
    float tmpOldPitch = orientation[1];
    orientation[1] = orientation[2]; //pitch = roll
    orientation[2] = tmpOldPitch; // roll  = pitch  
}

ほとんどの場合、機能しています。 1軸を中心に180度すばやく回転すると、システムがねじ込まれます。

利用可能な完全なコード Githubで

2
user529543

私がしていることは

  1. 画面の回転に従って座標を再マッピングするために、 彼の答え で提案されたkeianhzoのような座標系の再マッピング
  2. 次に、結果の座標系をで再度マップします。
    SensorManager.remapCoordinateSystem(rotationMatrixScreenRemapped, SensorManager.AXIS_X, SensorManager.AXIS_Z, rotationMatrixCameraRemapped);
    ドキュメント で提案されているように、カメラ(ARのような)に従って座標を再マップします

これまでのところ、うまくいくことを願っています!

0
Berťák