Android getOrientation Азимут загрязняется, когда телефон наклонен

У меня действительно раздражающая проблема, когда вид AR ведет себя как компас. Поэтому, когда я держу телефон в портретном режиме (чтобы экран был направлен на мое лицо), тогда я вызываю remapCoordinateSystem что высота тона равна 0, когда он находится в портретном положении. Тогда азимут (функция компаса) идеален, но как только я наклоняю телефон, азимут разрушается, если я наклоняюсь вперед, азимут увеличивается, а если я наклоняюсь назад, он уменьшается.

Я использую 2 датчика, чтобы получить показания, Sensor.TYPE_MAGNETIC_FIELD а также Sensor.TYPE_GRAVITY,

Я использую lowpassfilter, который довольно прост, он реализован с альфа- константой и используется непосредственно для считывания значений с датчиков.

Вот мой код:

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

Как видите, здесь нет магии. Я вызываю этот кусок кода, когда параметры gravitymeterValues ​​и magnetometerValues ​​готовы к использованию.

У меня вопрос: как я могу остановить сумасшедший азимут, когда я наклоняю телефон?

Я проверил бесплатное приложение в Google Play Store, Compass, и оно не решило эту проблему, но я надеюсь, что есть решение.

Я имею в виду 2 решения:

  1. Заставьте AR работать только в очень ограниченных углах наклона, сейчас у меня есть что-то вроде pitch >= -5 && pitch <= 30, Если это не заполнено, пользователю показывают экран, который просит, чтобы он / она повернул телефон к портрету.

  2. Каким-то образом используйте тональность для подавления азимута, хотя это кажется довольно специфичным для устройства решением, но, конечно, я открыт для предложений.

Я также могу добавить, что я искал подходящее решение в течение нескольких часов, и я не нашел ни одного, который дал бы мне лучшие решения, чем 2) здесь.

Заранее спасибо!

1 ответ

Решение

Полный код см. https://github.com/hoananguyen/dsensor
Сохраняйте историю и усредняйте, я не знаю правильной интерпретации шага и крена, поэтому следующий код предназначен только для азимута.

Члены класса

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;
}
Другие вопросы по тегам