Android Compass ориентация на ненадежный (фильтр нижних частот) - PullRequest
15 голосов
/ 15 января 2011

Я создаю приложение, в котором мне нужно расположить ImageView в зависимости от ориентации устройства. Я использую значения датчиков MagneticField и Accelerometer для расчета ориентации устройства с помощью

SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerValues, magneticFieldValues)
SensorManager.getOrientation(rotationMatrix, values);
double degrees = Math.toDegrees(values[0]);

Моя проблема в том, что расположение ImageView очень чувствительно к изменениям ориентации. Создание изображения просмотра постоянно прыгает по экрану. (потому что градусы меняются)

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

Я попытался загрузить некоторые приложения и обнаружил, что " 3D-компас " и " Компас " остаются очень стабильными в своих показаниях (при установке фильтра шума), я хотел бы такое же поведение в моем приложении.

Я прочитал, что могу настроить «шум» моих показаний, добавив « Фильтр нижних частот », но я понятия не имею, как это реализовать (из-за отсутствия математики).

Я надеюсь, что кто-то может помочь мне создать более устойчивое чтение на моем устройстве, где небольшое движение к устройству не повлияет на текущую ориентацию. Прямо сейчас я делаю маленький

if (Math.abs(lastReadingDegrees - newReadingDegrees) > 1) { updatePosition() }

Для фильтрации шума. Но это не очень хорошо работает:)

Ответы [ 5 ]

30 голосов
/ 24 июня 2011

Хотя я не использовал компас на Android, базовая обработка, показанная ниже (в JavaScript), вероятно, будет работать для вас.

Он основан на низкочастотном фильтре акселерометра, рекомендованном Команда Windows Phone с изменениями, подходящими для компаса (циклическое поведение каждые 360 ").

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

Вы хотите выполнить в фильтре 2 вещи:

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

Длячто у нас будет 2 константы:

  1. Поплавок замедления, который определяет, насколько плавным будет движение (1 - не сглаживание, а 0 - никогда не обновляется, по умолчанию - 0,5). Мы будем вызыватьэто SmoothFactorCompass.
  2. Порог, при котором расстояние достаточно велико для мгновенного поворота (0 - это прыжок всегда, 360 - никогда не прыгает, по умолчанию 30).Мы назовем его SmoothThresholdCompass.

У нас есть одна переменная, сохраненная в вызовах, с плавающей точкой oldCompass, и она является результатом алгоритма.

Итак, определение переменнойis:

var SmoothFactorCompass = 0.5;
var SmoothThresholdCompass = 30.0;
var oldCompass = 0.0;

и функция получает newCompass и возвращает oldCompass в качестве результата.

if (Math.abs(newCompass - oldCompass) < 180) {
    if (Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
        oldCompass = newCompass;
    }
    else {
        oldCompass = oldCompass + SmoothFactorCompass * (newCompass - oldCompass);
    }
}
else {
    if (360.0 - Math.abs(newCompass - oldCompass) > SmoothThresholdCompass) {
        oldCompass = newCompass;
    }
    else {
        if (oldCompass > newCompass) {
            oldCompass = (oldCompass + SmoothFactorCompass * ((360 + newCompass - oldCompass) % 360) + 360) % 360;
        } 
        else {
            oldCompass = (oldCompass - SmoothFactorCompass * ((360 - newCompass + oldCompass) % 360) + 360) % 360;
        }
    }
}

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

Одед Эльяда.

19 голосов
/ 20 сентября 2013

Этот фильтр нижних частот работает для углов в радианах. Используйте функцию add для каждого показания компаса, затем вызовите Среднее , чтобы получить среднее значение.

public class AngleLowpassFilter {

    private final int LENGTH = 10;

    private float sumSin, sumCos;

    private ArrayDeque<Float> queue = new ArrayDeque<Float>();

    public void add(float radians){

        sumSin += (float) Math.sin(radians);

        sumCos += (float) Math.cos(radians);

        queue.add(radians);

        if(queue.size() > LENGTH){

            float old = queue.poll();

            sumSin -= Math.sin(old);

            sumCos -= Math.cos(old);
        }
    }

    public float average(){

        int size = queue.size();

        return (float) Math.atan2(sumSin / size, sumCos / size);
    }
}

Используйте Math.toDegrees() или Math.toRadians() для преобразования.

4 голосов
/ 05 августа 2013

Имейте в виду, что, например, в среднем 350 и 10 не 180. Мое решение:

int difference = 0;
for(int i= 1;i <numberOfAngles;i++){
    difference += ( (angles[i]- angles[0] + 180 + 360 ) % 360 ) - 180;
}
averageAngle = (360 + angles[0] + ( difference / numberOfAngles ) ) % 360;
3 голосов
/ 05 марта 2011

Фильтр нижних частот (ФНЧ) блокирует быстро меняющиеся сигналы и
позволяет только медленные изменения в сигналах. Это означает, что любой маленький
внезапные изменения будут игнорироваться.

Стандартный способ реализовать это в программном обеспечении - взять среднее значение
из последних N образцов и сообщить это значение. Начните с N всего 3 и
продолжайте увеличивать N, пока не найдете достаточно сглаженный ответ в своем приложении.

Имейте в виду, что чем выше значение N, тем медленнее реакция системы.

2 голосов
/ 15 января 2011

См. Мой ответ на этот связанный вопрос: Сглаживание данных с датчика

Программный фильтр нижних частот в основном является модифицированной версией этого. Действительно, в этом ответе я даже предоставил эту ссылку на другой связанный вопрос: Программное обеспечение фильтра низких частот?

...