Android SensorManager strange how to remapCoordinateSystem

前端 未结 5 1751
感情败类
感情败类 2020-12-12 16:57

API Demos -> Graphics -> Compass

It works properly only, until you don\'t change the device natural orientation. In most phones is the portrait and most 10 inch tabl

相关标签:
5条回答
  • 2020-12-12 17:12

    This is how I do the magic in my application:

            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);
    
    0 讨论(0)
  • 2020-12-12 17:20

    Thanks keianhzo, your answer works great with phones flat on the ground. For AR-applications where you look "through" the display, I found this to work: use the proper axis:

    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;
    }
    

    Get the orientation degrees:

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-12 17:22

    To complete the switch branches I just try to think following the remapCoordinateSystem method javadoc:

    X defines on which world axis and direction the X axis of the device is mapped.
    Y defines on which world axis and direction the Y axis of the device is mapped.

    So take your device rotate it from its natural orientation (90, 180 or 270 degrees) and ask yourself: The X positive axis in the original device orientation to which axis corresponds in the current device orientation?. And same for the Y axis.

    So in case your device is rotated 90 degrees you will see that the original X positive axis corresponds to the current positive Y axis and the original positive Y axis corresponds to the current orientation negative X axis.

    So It should be:

    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;
    }
    

    That worked for me, hope that helps.

    0 讨论(0)
  • 2020-12-12 17:25

    What I do is

    1. remapping coordinate system like keianhzo suggested in his answer, to remap coordinates according to screen-rotation
    2. then I remap resulting coordinate system again with
      SensorManager.remapCoordinateSystem(rotationMatrixScreenRemapped, SensorManager.AXIS_X, SensorManager.AXIS_Z, rotationMatrixCameraRemapped);
      to remap coordinates according to camera (AR-like), as suggested in documentation

    So far I hope it works well!

    0 讨论(0)
  • 2020-12-12 17:26

    If the phone UI locked to the rotation 0, I am getting the following values without remapCoordinateSystem()

    Pitch (phone) = -Pitch   (API)
    Roll  (phone) =  Roll     (API)
    Yaw   (phone) =  Azimuth  (API)
    
    • at least near 0,0,0 values.

    If the phone UI forced to rotation 90:

    Yaw value has -90 degree ( - PI/2 ) at old orientation!!! => I will go to East in reality instead of North.

    If I take phone to 0,0,0 position:

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

    If the phone UI forced to rotation 180:

    Yaw value has +/-180 degree ( +/- PI ) at old orientation!!! => I will go to South in reality instead of North.

    If I take phone to 0,0,0 position:

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

    If the phone UI forced to rotation 270:

    Yaw value has +90 degree ( + PI/2 ) at old orientation!!! => I will go to West in reality instead of North.

    If I take phone to 0,0,0 position:

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

    I wrote a little fix, and tested with: 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  
    }
    

    In most cases is working. When you rotate quickly 180 degree around 1 axis, than the system will be screwed!

    The full code available at Github

    0 讨论(0)
提交回复
热议问题