没有地磁的Android设备定位

时间:2016-08-02 04:19:52

标签: android orientation magnetometer

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

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

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

7 个答案:

答案 0 :(得分:8)

方向可以分为三个欧拉角:俯仰,滚转和方位角。

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

你可以尝试这样的东西来了解你的俯仰和滚动:

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

答案 1 :(得分:3)

要在设备不平坦时获得旋转角度,请执行OrientationEventListeneronOrientationChanged将为设备提供方向。有关详细信息,请参阅https://developer.android.com/reference/android/view/OrientationEventListener.html

答案 2 :(得分:2)

我做了类似的事情:

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语句,请参阅此图片  image

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

我仍然需要做得更好,只是没有足够的时间。

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

答案 3 :(得分:1)

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

  

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

我知道这听起来很直观,但显然你需要的权限是:

from collections import defaultdict
import operator

# Binary counter
# (Current state, Current symbol) : (New State, New Symbol, Move)
rules = {
    (0, 1): (0, 1, 1),
    (0, 0): (0, 0, 1),
    (0, None): (1, None, -1),
    (1, 0): (0, 1, 1),
    (1, 1): (1, 0, -1),
    (1, None): (0, 1, 1),
}
# from here I don't really understand what's going on

def tick(state=0, tape=defaultdict(lambda: None), position=0):
    state, tape[position], move = rules[(state, tape[position])]
    return state, tape, position + move

system = ()
for i in range(255):
    system = tick(*system)
    if(system[2] == 0):
        print(map(operator.itemgetter(1), sorted(system[1].items())))

(或其中的子集)。

见这里:manifest.xml when using sensors

答案 4 :(得分:1)

答案 5 :(得分:1)

您可以尝试使用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);

从android docs中提取:https://developer.android.com/guide/topics/sensors/sensors_position.html

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

希望这有帮助。

答案 6 :(得分:0)

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

如果你只关心这个方向而你不关心北方的位置,这是我的建议,试试这个传感器,它在我的情况下很棒:

TYPE_GAME_ROTATION_VECTOR