Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android getOrientation Azimuth gets polluted when phone is tilted

I'm having a really annoying problem with a AR view acting like a compass. So when I hold the phone in portrait (so that the screen is pointing to my face), then I call the remapCoordinateSystem that the pitch is 0 when holding it portrait. Then the azimuth (compass functionality) is perfect, but as soon as I tilt the phone the azimuth gets ruined, if I bend forward the azimuth increases and if I bend backwards it decreases.

I use 2 sensors to get the readings, Sensor.TYPE_MAGNETIC_FIELD and Sensor.TYPE_GRAVITY.

I use a lowpassfilter which is pretty basic, it's implemented with an alpha constant and is used directly on the read values from the sensors.

Here is my code:

float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrix(rotationMatrix, null, gravitymeterValues,
    magnetometerValues);

float[] remappedRotationMatrix = new float[9];

SensorManager.remapCoordinateSystem(rotationMatrix, SensorManager.AXIS_X,
    SensorManager.AXIS_Z, remappedRotationMatrix);

float results[] = new float[3];
SensorManager.getOrientation(remappedRotationMatrix, results);

float azimuth = (float) (results[0] * 180 / Math.PI);
if (azimuth < 0) {
    azimuth += 360;
}

float pitch = (float) (results[1] * 180 / Math.PI);
float roll = (float) (results[2] * 180 / Math.PI);

As you see there is no magic here. I call this piece of code when the gravitymeterValues and the magnetometerValues are ready to be used.

My question is how do I stop the azimuth from going crazy when I tilt the phone?

I checked a free app on the Google Play Store, Compass and it hasn't solved this problem, but I hope there is a solution.

I have 2 solutions in mind:

  1. Make the AR view only work in very constrainted pitch angles, right now I have something like pitch >= -5 && pitch <= 30. If this isn't fullfilled the user is shown a screen that asks him/her to rotate the phone to portrait.

  2. Somehow use the pitch to suppress the azimuth, this seems like a pretty device-specific solution though, but of course I'm open for suggestions.

I can also add that I've been searching for a couple of hours for a decent solution and I haven't found any that has given me any better solutions than 2) here.

Thanks in advance!

like image 295
Johan S Avatar asked Jul 31 '13 19:07

Johan S


People also ask

What is azimuth and pitch in Android?

Azimuth (angle around the z-axis). Pitch (angle around the x-axis). Roll (angle around the y-axis). 1 This sensor was deprecated in Android 2.2 (API level 8), and this sensor type was deprecated in Android 4.4W (API level 20).

How does the proximity sensor work on Android?

The Android platform also provides a sensor that lets you determine how close the face of a device is to an object (known as the proximity sensor ). The geomagnetic field sensor and the proximity sensor are hardware-based.

How do I determine the orientation of a mobile phone?

Likewise, handset manufacturers usually include a proximity sensor to determine when a handset is being held close to a user's face (for example, during a phone call). For determining a device's orientation, you can use the readings from the device's accelerometer and the geomagnetic field sensor.

What is a position sensor on an Android device?

Position sensors. The Android platform provides two sensors that let you determine the position of a device: the geomagnetic field sensor and the accelerometer. The Android platform also provides a sensor that lets you determine how close the face of a device is to an object (known as the proximity sensor ).


1 Answers

For complete code see https://github.com/hoananguyen/dsensor
Keep a history and average out, I do not know the correct interpretation of pitch and roll so the following code is for azimuth only.

Class members

private List<float[]> mRotHist = new ArrayList<float[]>();
private int mRotHistIndex;
// Change the value so that the azimuth is stable and fit your requirement
private int mHistoryMaxLength = 40;
float[] mGravity;
float[] mMagnetic;
float[] mRotationMatrix = new float[9];
// the direction of the back camera, only valid if the device is tilted up by
// at least 25 degrees.
private float mFacing = Float.NAN;

public static final float TWENTY_FIVE_DEGREE_IN_RADIAN = 0.436332313f;
public static final float ONE_FIFTY_FIVE_DEGREE_IN_RADIAN = 2.7052603f;

onSensorChanged

@Override
public void onSensorChanged(SensorEvent event)
{
     if (event.sensor.getType() == Sensor.TYPE_GRAVITY)
     {
         mGravity = event.values.clone();
     }
     else
     {
        mMagnetic = event.values.clone();
     }

     if (mGravity != null && mMagnetic != null)
     {
          if (SensorManager.getRotationMatrix(mRotationMatrix, null, mGravity, mMagnetic))
          {
              // inclination is the degree of tilt by the device independent of orientation (portrait or landscape)
              // if less than 25 or more than 155 degrees the device is considered lying flat
              float inclination = (float) Math.acos(mRotationMatrix[8]);
              if (inclination < TWENTY_FIVE_DEGREE_IN_RADIAN 
                      || inclination > ONE_FIFTY_FIVE_DEGREE_IN_RADIAN)
              {
                  // mFacing is undefined, so we need to clear the history
                  clearRotHist();
                  mFacing = Float.NaN;
              }
              else
              {
                  setRotHist();
                  // mFacing = azimuth is in radian
                  mFacing = findFacing(); 
              }
          }
     }
}

private void clearRotHist()
{
    if (DEBUG) {Log.d(TAG, "clearRotHist()");}
    mRotHist.clear();
    mRotHistIndex = 0;
}

private void setRotHist()
{
    if (DEBUG) {Log.d(TAG, "setRotHist()");}
    float[] hist = mRotationMatrix.clone();
    if (mRotHist.size() == mHistoryMaxLength)
    {
        mRotHist.remove(mRotHistIndex);
    }   
    mRotHist.add(mRotHistIndex++, hist);
    mRotHistIndex %= mHistoryMaxLength;
}

private float findFacing()
{
    if (DEBUG) {Log.d(TAG, "findFacing()");}
    float[] averageRotHist = average(mRotHist);
    return (float) Math.atan2(-averageRotHist[2], -averageRotHist[5]);
}

public float[] average(List<float[]> values)
{
    float[] result = new float[9];
    for (float[] value : values)
    {
        for (int i = 0; i < 9; i++)
        {
            result[i] += value[i];
        }
    }

    for (int i = 0; i < 9; i++)
    {
        result[i] = result[i] / values.size();
    }

    return result;
}
like image 187
Hoan Nguyen Avatar answered Oct 19 '22 20:10

Hoan Nguyen