Плавные данные GPS

Я работаю с данными GPS, получая значения каждую секунду и отображая текущую позицию на карте. Проблема в том, что иногда (особенно когда точность низкая) значения сильно меняются, что делает текущую позицию «прыгать» между удаленными точками на карте.

Мне было интересно узнать какой-нибудь простой способ избежать этого. В качестве первой идеи я подумал о том, чтобы отбросить значения с точностью до определенного порога, но я думаю, что есть и другие лучшие способы. Как обычно, программы выполняют это?

То, что вы ищете, называется фильтром Калмана . Он часто используется для сглаживания навигационных данных . Это не обязательно тривиально, и вы можете много настраивать, но это очень стандартный подход и работает хорошо. Доступна библиотека KFilter, которая является реализацией на C ++.

Мой следующий запасной вариант был бы наименьшим квадратом . Фильтр Калмана сгладит данные, принимая во внимание скорости, тогда как подход с наименьшими квадратами подходит только для использования позиционной информации. Тем не менее, это определенно проще реализовать и понять. Похоже, что у Научной библиотеки GNU может быть реализация этого.

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

Общая теория фильтра Калмана касается оценок для векторов с точностью оценок, представленных ковариационными matrixми. Однако для оценки местоположения на устройствах Android общая теория сводится к очень простому случаю. Поставщики местоположения Android предоставляют местоположение в виде широты и долготы вместе с точностью, которая определяется как единое число, измеренное в метрах. Это означает, что вместо ковариационной матрицы точность фильтра Калмана может быть измерена одним числом, хотя местоположение в калмановском фильтре измеряется двумя числами. Также можно игнорировать тот факт, что широта, долгота и метры фактически все разные единицы могут быть проигнорированы, потому что если вы поместите коэффициенты масштабирования в фильтр Калмана, чтобы преобразовать их все в одни и те же единицы, то эти коэффициенты масштабирования в конечном итоге будут отменены при преобразовании результатов обратно в исходные единицы.

Код может быть улучшен, поскольку он предполагает, что наилучшая оценка текущего местоположения является последним известным местоположением, и если кто-то движется, должно быть возможно использовать датчики Android для получения более точной оценки. Код имеет единственный свободный параметр Q, выраженный в метрах в секунду, который описывает, как быстро точность уменьшается при отсутствии каких-либо новых оценок местоположения. Более высокий параметр Q означает, что точность уменьшается быстрее. Фильтры Kalman в целом работают лучше, когда точность затухает немного быстрее, чем можно было бы ожидать, поэтому для прогулки по телефону с Android я считаю, что Q = 3 метра в секунду отлично работает, хотя я обычно хожу медленнее, чем это. Но если вы путешествуете в быстром автомобиле, очевидно, следует использовать гораздо большее число.

public class KalmanLatLong { private final float MinAccuracy = 1; private float Q_metres_per_second; private long TimeStamp_milliseconds; private double lat; private double lng; private float variance; // P matrix. Negative means object uninitialised. NB: units irrelevant, as long as same units used throughout public KalmanLatLong(float Q_metres_per_second) { this.Q_metres_per_second = Q_metres_per_second; variance = -1; } public long get_TimeStamp() { return TimeStamp_milliseconds; } public double get_lat() { return lat; } public double get_lng() { return lng; } public float get_accuracy() { return (float)Math.sqrt(variance); } public void SetState(double lat, double lng, float accuracy, long TimeStamp_milliseconds) { this.lat=lat; this.lng=lng; variance = accuracy * accuracy; this.TimeStamp_milliseconds=TimeStamp_milliseconds; } ///  /// Kalman filter processing for lattitude and longitude ///  /// new measurement of lattidude /// new measurement of longitude /// measurement of 1 standard deviation error in metres /// time of measurement /// new state public void Process(double lat_measurement, double lng_measurement, float accuracy, long TimeStamp_milliseconds) { if (accuracy < MinAccuracy) accuracy = MinAccuracy; if (variance < 0) { // if variance < 0, object is unitialised, so initialise with current values this.TimeStamp_milliseconds = TimeStamp_milliseconds; lat=lat_measurement; lng = lng_measurement; variance = accuracy*accuracy; } else { // else apply Kalman filter methodology long TimeInc_milliseconds = TimeStamp_milliseconds - this.TimeStamp_milliseconds; if (TimeInc_milliseconds > 0) { // time has moved on, so the uncertainty in the current position increases variance += TimeInc_milliseconds * Q_metres_per_second * Q_metres_per_second / 1000; this.TimeStamp_milliseconds = TimeStamp_milliseconds; // TO DO: USE VELOCITY INFORMATION HERE TO GET A BETTER ESTIMATE OF CURRENT POSITION } // Kalman gain matrix K = Covarariance * Inverse(Covariance + MeasurementVariance) // NB: because K is dimensionless, it doesn't matter that variance has different units to lat and lng float K = variance / (variance + accuracy * accuracy); // apply K lat += K * (lat_measurement - lat); lng += K * (lng_measurement - lng); // new Covarariance matrix is (IdentityMatrix - K) * Covarariance variance = (1 - K) * variance; } } } 

Это может немного запоздать …

Я написал этот KalmanLocationManager для Android, который обертывает двух наиболее распространенных поставщиков местоположения: сети и GPS, kalman-фильтрует данные и доставляет обновления в LocationListener (например, два «реальных» провайдера).

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

Фактически, он использует три фильтра калмана, для каждого измерения: широту, долготу и высоту. Во всяком случае, они независимы.

Это значительно упрощает математическую матрицу: вместо использования одной матрицы перехода состояния 6×6 я использую 3 разные матрицы 2×2. Фактически в коде я вообще не использую матрицы. Решенные все уравнения и все значения являются примитивами (double).

Исходный код работает, и есть демонстрационная активность. Извините за отсутствие javadoc в некоторых местах, я догоню.

Вы не должны рассчитывать скорость смены позиции за раз. GPS может иметь неточные позиции, но имеет точную скорость (выше 5 км / ч). Поэтому используйте скорость от отметки местоположения GPS. И дальше вы не должны делать это с курсом, хотя он работает большую часть времени.

Позиции GPS, поставляемые, уже отфильтрованы Kalman, вероятно, вы не можете улучшить, в постобработке обычно у вас нет той же информации, что и чип GPS.

Вы можете сгладить его, но это также приводит к ошибкам.

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

Обычно я использую акселерометры. Резкое изменение положения за короткий период подразумевает быстрое ускорение. Если это не отражается в телеметрии акселерометра, то это почти наверняка связано с изменением «лучших трех» спутников, используемых для вычисления положения (к которому я отношусь как телепортация GPS).

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

Чтобы сделать это, когда актив не находится в состоянии покоя, вы должны оценить его вероятную следующую позицию и ориентацию, основанную на данных ускорения, заголовка и линейности и вращения (если у вас есть гироскопы). Это более или менее то, что делает знаменитый фильтр K. Вы можете получить все это на аппаратных средствах около 150 долларов США на AHRS, где есть все, кроме модуля GPS, и с разъемом для подключения. Он имеет собственный процессор и фильтрацию Kalman на борту; результаты стабильны и неплохи. Инерционное наведение очень устойчиво к дрожанию, но со временем дрейфует. GPS склонен к дрожанию, но не дрейфует со временем, они были практически сделаны, чтобы компенсировать друг друга.

Один метод, который использует меньше математики / теории, состоит в выборке 2, 5, 7 или 10 точек данных за раз и определяет те, которые являются выбросами. Менее точная мера выброса, чем фильтр Калмана, состоит в том, чтобы использовать следующий алгоритм для того, чтобы принимать все парные расстояния между точками и выбрасывать ту, которая наиболее удалена от других. Обычно эти значения заменяются значением, самым близким к исходному значению, которое вы заменяете

Например

Сглаживание в пяти точках выборки A, B, C, D, E

ATOTAL = СУММ расстояний AB AC AD AE

BTOTAL = СУМ расстояний AB BC BD BE

CTOTAL = СУМ расстояний AC BC CD CE

DTOTAL = СУМ расстояний DA DB DC DE

ETOTAL = СУММ расстояний EA EB EC DE

Если BTOTAL является самым большим, вы замените точку B на D, если BD = min {AB, BC, BD, BE}

Это сглаживание определяет выбросы и может быть увеличено с использованием средней точки BD вместо точки D, чтобы сгладить позиционную линию. Ваш пробег может варьироваться, и существуют более математически строгие решения.

Вы также можете использовать сплайн. Подавайте значения, которые у вас есть, и интерполируйте точки между вашими известными точками. Связывание этого с подгонкой наименьших квадратов, скользящим средним или калмановским фильтром (как упоминалось в других ответах) дает вам возможность вычислять точки между вашими «известными» точками.

Возможность интерполировать ценности между вашими известными людьми дает вам приятный плавный переход и / разумное / приближение того, какие данные будут присутствовать, если бы у вас была более высокая точность. http://en.wikipedia.org/wiki/Spline_interpolation

Различные сплайны имеют разные характеристики. Чаще всего я использовал Akima и Cubic сплайны.

Еще один алгоритм, который следует учитывать, – это алгоритм упрощения линии Рамера-Дугласа-Пиккера, он довольно часто используется для упрощения данных GPS. ( http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm )

Возвращаясь к фильтрам Калмана … Я нашел реализацию C для фильтра Калмана для данных GPS здесь: http://github.com/lacker/ikalman Я еще не пробовал, но кажется многообещающим.

Что касается наименьших квадратов, то вот еще несколько вещей, которые можно поэкспериментировать с:

  1. Просто потому, что это наименьшие квадраты подходят не означает, что он должен быть линейным. Вы можете наименьших квадратов – соответствовать квадратичной кривой для данных, тогда это соответствует сценарию, в котором пользователь ускоряется. (Обратите внимание, что по методу наименьших квадратов я имею в виду использование координат как зависимую переменную и время как независимую переменную.)

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

  3. Еще одна вещь, которую вы, возможно, захотите попробовать, вместо того, чтобы отображать одну точку, если точность низкая, отображает круг или что-то, указывающее диапазон, в котором пользователь может основываться на заявленной точности. (Это то, что делает встроенное приложение Google Maps для iPhone).

Прислал в CoffeeScript, если кто-то заинтересован. ** edit -> извините, используя магистраль тоже, но вы получите эту идею.

Изменено немного, чтобы принять маяк с атрибутами

{широта: item.lat, longitude: item.lng, дата: новая дата (item.effective_at), точность: item.gps_accuracy}

 MIN_ACCURACY = 1 # mapped from http://stackoverflow.com/questions/1134579/smooth-gps-data class v.Map.BeaconFilter constructor: -> _.extend(this, Backbone.Events) process: (decay,beacon) -> accuracy = Math.max beacon.accuracy, MIN_ACCURACY unless @variance? # if variance nil, inititalise some values @variance = accuracy * accuracy @timestamp_ms = beacon.date.getTime(); @lat = beacon.latitude @lng = beacon.longitude else @timestamp_ms = beacon.date.getTime() - @timestamp_ms if @timestamp_ms > 0 # time has moved on, so the uncertainty in the current position increases @variance += @timestamp_ms * decay * decay / 1000; @timestamp_ms = beacon.date.getTime(); # Kalman gain matrix K = Covarariance * Inverse(Covariance + MeasurementVariance) # NB: because K is dimensionless, it doesn't matter that variance has different units to lat and lng _k = @variance / (@variance + accuracy * accuracy) @lat = _k * (beacon.latitude - @lat) @lng = _k * (beacon.longitude - @lng) @variance = (1 - _k) * @variance [@lat,@lng] 
Interesting Posts

Прозрачная заставка для Linux?

Каков современный метод установки общих флагов компиляции в CMake?

jQuery – плохо иметь несколько $ (document) .ready (function () {});

Windows 7 UFD не реагирует после жесткого выброса

Добавление локального принтера через создание стандартного порта TCP / IP и добавление сетевого принтера

emberjs – как пометить активный элемент меню с помощью инфраструктуры маршрутизатора

Проверка FileExtension с помощью выборочной проверки создает дубликаты и недопустимые атрибуты data- *

Изменение пароля, который Windows 7 сохранил для беспроводной сети

В чем разница между «$ @» и «$ *» в Bash?

Расширение ошибки в Javascript с синтаксисом ES6 и Babel

Vim удалить все строки, которые НЕ содержат определенное слово

Какое лучшее программное обеспечение для сшивания фотографий доступно?

Различия между HashMap и Hashtable?

Более безопасная, но простая в использовании и гибкая альтернатива C ++ для s ++ ()

Отключить «Ctrl» + масштабирование колесика мыши в Chrome?

Давайте будем гением компьютера.