18

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 轴在桌子上)

设备方向

只有且仅当

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 度)

看来,如果 Rotation 0 不变(axisX = SensorManager.AXIS_X;axisY = SensorManager.AXIS_Y;),则 90 度应该是:

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

比谷歌文档说的值错误!问题在哪里?!

getRotationMatrix返回:

在此处输入图像描述

X 定义为向量积 YZ(它与设备当前位置的地面相切,大致指向东方)。

Y 在设备当前位置与地面相切,并指向磁北极。

Z指向天空并垂直于地面。

看上面的电话!我想从左到右,后置摄像头接地。

getOrientation返回:

在此处输入图像描述

X 定义为向量积 YZ(它与设备当前位置的地面相切,大致指向西)。

Y 在设备当前位置与地面相切,并指向磁北极。

Z指向地球的中心并且垂直于地面。

values[0]:方位角,绕 Z 轴旋转。

values[1]:俯仰,绕X轴旋转。

values[2]:滚动,绕 Y 轴旋转。

电话应该如何?

最后我想有像飞机这样的角度值。我的电话(我)前往北方:(偏航是方位角)

在此处输入图像描述

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

5 回答 5

27

为了完成切换分支,我只是尝试遵循 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;
}

这对我有用,希望有帮助。

于 2013-12-24T11:12:07.697 回答
4

谢谢 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;
        }
    }
}
于 2016-10-19T08:09:31.907 回答
3

这就是我在应用程序中施展魔法的方式:

        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);
于 2015-02-06T03:48:20.337 回答
2

如果手机 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 上提供的完整代码

于 2013-09-13T18:34:22.420 回答
1

我所做的是

  1. 像keianhzo在他的回答中建议的重新映射坐标系,根据屏幕旋转重新映射坐标
  2. 然后我再次重新映射结果坐标系
    SensorManager.remapCoordinateSystem(rotationMatrixScreenRemapped, SensorManager.AXIS_X, SensorManager.AXIS_Z, rotationMatrixCameraRemapped);
    按照文档 中的建议,根据相机(类似 AR)重新映射坐标

到目前为止,我希望它运作良好!

于 2018-11-13T13:10:37.373 回答