Android device orientation without geomagnetic

江枫思渺然 提交于 2019-12-18 13:01:44

问题


I need to get device orientation. As I know usually used TYPE_ACCELEROMETER and TYPE_MAGNETIC_FIELD sensors. My problem is that SensorManager.getDefaultSensor returns me null for geomagnetic sensor. It returns null for TYPE_ORIENTATION sensor too.

manager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensorAcc = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), //normal object
        sensorMagn = manager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); //null
orientationListener = new OrientationSensorListener();
manager.registerListener(orientationListener, sensorAcc, 10);
manager.registerListener(orientationListener, sensorMagn, 10);

I need another ways to get device orientation.


回答1:


Orientation can be decomposed in three Euler angle : Pitch, Roll and Azimuth.

With only accelerometer datas, you cannot compute your Azimuth, neither the sign of your pitch.

You can try something as this to know something about your pitch and roll :

    private final float[] mMagnet = new float[3];               // magnetic field vector
    private final float[] mAcceleration = new float[3];         // accelerometer vector
    private final float[] mAccMagOrientation = new float[3];    // orientation angles from mAcceleration and mMagnet
    private float[] mRotationMatrix = new float[9];             // accelerometer and magnetometer based rotation matrix

    public void onSensorChanged(SensorEvent event) {
    switch (event.sensor.getType()) {
        case Sensor.TYPE_ACCELEROMETER:
            System.arraycopy(event.values, 0, mAcceleration, 0, 3);   // save datas
            calculateAccMagOrientation();                       // then calculate new orientation
            break;
        case Sensor.TYPE_MAGNETIC_FIELD:
            System.arraycopy(event.values, 0, mMagnet, 0, 3);         // save datas
            break;
        default: break;
    }
}
public void calculateAccMagOrientation() {
    if (SensorManager.getRotationMatrix(mRotationMatrix, null, mAcceleration, mMagnet))
        SensorManager.getOrientation(mRotationMatrix, mAccMagOrientation);
    else { // Most chances are that there are no magnet datas
        double gx, gy, gz;
        gx = mAcceleration[0] / 9.81f;
        gy = mAcceleration[1] / 9.81f;
        gz = mAcceleration[2] / 9.81f;
        // http://theccontinuum.com/2012/09/24/arduino-imu-pitch-roll-from-accelerometer/
        float pitch = (float) -Math.atan(gy / Math.sqrt(gx * gx + gz * gz));
        float roll = (float) -Math.atan(gx / Math.sqrt(gy * gy + gz * gz));
        float azimuth = 0; // Impossible to guess

        mAccMagOrientation[0] = azimuth;
        mAccMagOrientation[1] = pitch;
        mAccMagOrientation[2] = roll;
        mRotationMatrix = getRotationMatrixFromOrientation(mAccMagOrientation);
    }
}
public static float[] getRotationMatrixFromOrientation(float[] o) {
    float[] xM = new float[9];
    float[] yM = new float[9];
    float[] zM = new float[9];

    float sinX = (float) Math.sin(o[1]);
    float cosX = (float) Math.cos(o[1]);
    float sinY = (float) Math.sin(o[2]);
    float cosY = (float) Math.cos(o[2]);
    float sinZ = (float) Math.sin(o[0]);
    float cosZ = (float) Math.cos(o[0]);

    // rotation about x-axis (pitch)
    xM[0] = 1.0f;xM[1] = 0.0f;xM[2] = 0.0f;
    xM[3] = 0.0f;xM[4] = cosX;xM[5] = sinX;
    xM[6] = 0.0f;xM[7] =-sinX;xM[8] = cosX;

    // rotation about y-axis (roll)
    yM[0] = cosY;yM[1] = 0.0f;yM[2] = sinY;
    yM[3] = 0.0f;yM[4] = 1.0f;yM[5] = 0.0f;
    yM[6] =-sinY;yM[7] = 0.0f;yM[8] = cosY;

    // rotation about z-axis (azimuth)
    zM[0] = cosZ;zM[1] = sinZ;zM[2] = 0.0f;
    zM[3] =-sinZ;zM[4] = cosZ;zM[5] = 0.0f;
    zM[6] = 0.0f;zM[7] = 0.0f;zM[8] = 1.0f;

    // rotation order is y, x, z (roll, pitch, azimuth)
    float[] resultMatrix = matrixMultiplication(xM, yM);
    resultMatrix = matrixMultiplication(zM, resultMatrix);
    return resultMatrix;
}
public static float[] matrixMultiplication(float[] A, float[] B) {
    float[] result = new float[9];

    result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6];
    result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7];
    result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8];

    result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6];
    result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7];
    result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8];

    result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6];
    result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7];
    result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8];

    return result;
}



回答2:


To get the angle of rotation when the device is not flat, implements OrientationEventListener. onOrientationChanged will give the device orientation. See https://developer.android.com/reference/android/view/OrientationEventListener.html for detail.




回答3:


i did something like :

public class MainActivity extends AppCompatActivity
{
    SensorManager sensorManager;
    Sensor sensor;
    ImageView imageViewProtractorPointer;

    /////////////////////////////////////
    ///////////// onResume //////////////
    /////////////////////////////////////
    @Override
    protected void onResume()
    {
        super.onResume();
        // register sensor listener again if return to application
        if(sensor !=null) sensorManager.registerListener(sensorListener,sensor,SensorManager.SENSOR_DELAY_NORMAL);
    }
    /////////////////////////////////////
    ///////////// onCreate //////////////
    /////////////////////////////////////
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageViewProtractorPointer = (ImageView)findViewById(R.id.imageView2);
        // get the SensorManager
        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        // get the Sensor ACCELEROMETER
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }
    /////////////////////////////////////
    ///////////// onPause ///////////////
    /////////////////////////////////////
    @Override
    protected void onPause()
    {
        // Unregister the sensor listener to prevent battery drain if not in use
        super.onPause();
        if(sensor !=null) sensorManager.unregisterListener(sensorListener);
    }

    /////////////////////////////////////////////
    /////////// SensorEventListener /////////////
    /////////////////////////////////////////////
    SensorEventListener sensorListener = new SensorEventListener()
    {
        @Override
        public void onSensorChanged(SensorEvent sensorEvent)
        {
            // i will use values from 0 to 9 without decimal
            int x = (int)sensorEvent.values[0];
            int y = (int)sensorEvent.values[1];
            int angle = 0;

            if(y>=0 && x<=0) angle = x*10;
            if(x<=0 && y<=0) angle = (y*10)-90;
            if(x>=0 && y<=0) angle = (-x*10)-180;
            if(x>=0 && y>=0) angle = (-y*10)-270;

            imageViewProtractorPointer.setRotation((float)angle);
        }
        @Override
        public void onAccuracyChanged(Sensor sensor, int i){}
    };
}

if you want to understand about my if statements see this image

for my use i lock the screen in a portrait mode , and a use 2 images to show the angle on screen , this is my screenshot

i'm still have to make it little better , just not enough time for it.

i hope this help , if you need a full code let me know.




回答4:


You have to add some permissions to the manifest. The docs states:

the default sensor matching the requested type and wakeUp properties if one exists and the application has the necessary permissions, or null otherwise

I know it sounds counter intuitive, but apparently the permissions you need are:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

(or a sub-set of those).

See here: manifest.xml when using sensors




回答5:


https://developer.android.com/guide/topics/sensors/sensors_position.html

https://developer.android.com/guide/topics/sensors/sensors_motion.html

https://developer.android.com/guide/topics/sensors/sensors_overview.html

Which sensor for rotating android phone?




回答6:


You can try the GEOMAGNETIC_ROTATION_VECTOR like this:

private SensorManager mSensorManager;
private Sensor mSensor;
...
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR)

And compute the sensor info with this:

...
// Rotation matrix based on current readings from accelerometer and magnetometer.
final float[] rotationMatrix = new float[9];
mSensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);

// Express the updated rotation matrix as three orientation angles.
final float[] orientationAngles = new float[3];
mSensorManager.getOrientation(rotationMatrix, orientationAngles);

Extracted from android docs: https://developer.android.com/guide/topics/sensors/sensors_position.html

add the proper permissions in the manifest.

Hope this helps.




回答7:


For anyone who is still confused by this problem, say: you want to get the orientation of your phone (the azimuth, pitch and roll), but sometimes the magnetic field is unstable, so that the orientation you get is also unstable. The answers above may help you get the pitch and roll angle, but you still cannot get the azimuth angle. They said it is impossible. Then you become desperate. So, what should you do to solve this problem?

If you only care about the orientation and you don't care about where north is, here is my suggestion, try this sensor, it works awesome in my case:

TYPE_GAME_ROTATION_VECTOR


来源:https://stackoverflow.com/questions/38711705/android-device-orientation-without-geomagnetic

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!