【问题标题】:Android device orientation without geomagneticAndroid 设备方向无地磁
【发布时间】:2016-12-07 07:01:43
【问题描述】:

我需要获取设备方向。据我所知,通常使用TYPE_ACCELEROMETERTYPE_MAGNETIC_FIELD 传感器。我的问题是SensorManager.getDefaultSensor 将地磁传感器返回给我null。它也为TYPE_ORIENTATION 传感器返回null

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

我需要另一种方法来获取设备方向。

【问题讨论】:

  • @Jas 添加到问题
  • 您可以利用屏幕宽度和高度来确定方向,在横向模式下通常宽度高于高度。
  • @ashutiwari4 仅仅知道是不是风景是不够的。我需要获取方位角。
  • 设备方向是指纵向角度吗?
  • @HoanNguyen Somehting 这样。是的。

标签: android orientation magnetometer


【解决方案1】:

方向可以分解为三个欧拉角:俯仰角、滚动角和方位角。

仅使用加速度计数据,您无法计算方位角,也无法计算俯仰符号。

您可以尝试这样的方法来了解您的俯仰和滚动:

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

【讨论】:

  • 正如 OP 所说,他不能依赖磁力计,而您的代码需要它。
  • 错了,我的代码会在它为空时做出反应并且仍然提供部分方向。
  • @J.Jacobs-VP 出色的解决方案。它应该被选为接受的答案
【解决方案2】:

要在设备不平坦时获得旋转角度,请实现OrientationEventListeneronOrientationChanged 将给出设备方向。详情请见https://developer.android.com/reference/android/view/OrientationEventListener.html

【讨论】:

  • 例如,这个解决方案可以让我达到 237 度吗?
  • 是的,请阅读文档,onOrientationChanged 为您提供从 0 到 360 的角度。
【解决方案3】:

我做了类似的事情:

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 语句,请查看此图片

为了我的使用,我将屏幕锁定为纵向模式,并使用 2 张图像在屏幕上显示角度,这是我的屏幕截图

我还得把它弄好一点,只是时间不够。

希望对您有所帮助,如果您需要完整代码,请告诉我。

【讨论】:

  • 您好,您能补充更多解释吗?
【解决方案4】:

您必须向清单添加一些权限。文档指出:

与请求的类型和唤醒属性匹配的默认传感器(如果存在)并且应用程序具有必要的权限,否则为 null

我知道这听起来违反直觉,但显然您需要的权限是:

<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"/>

(或其中的一个子集)。

请看这里:manifest.xml when using sensors

【讨论】:

  • 如果您在谈论地磁,我尝试了一些指南针应用程序 - 他们都说我的设备不需要传感器。
【解决方案5】:
【解决方案6】:

您可以像这样尝试 GEOMAGNETIC_ROTATION_VECTOR:

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

并以此计算传感器信息:

...
// 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);

摘自安卓文档:https://developer.android.com/guide/topics/sensors/sensors_position.html

在清单中添加适当的权限。

希望这会有所帮助。

【讨论】:

    【解决方案7】:

    对于还在为这个问题感到困惑的人,说:你想得到你手机的方位(方位角、俯仰角和滚动角),但有时磁场不稳定,所以你得到的方位也不稳定.上面的答案可能会帮助您获得俯仰角和滚动角,但您仍然无法获得方位角。他们说这是不可能的。然后你变得绝望。那么,你应该怎么做才能解决这个问题呢?

    如果你只关心方向而不关心北方在哪里,这是我的建议,试试这个传感器,它在我的情况下非常有用:

    TYPE_GAME_ROTATION_VECTOR
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-15
      • 2012-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多