GPS 데이터로 작업하고 있으며 매 초마다 값을 가져오고지도에 현재 위치를 표시합니다. 문제는 때때로 (특히 정확도가 낮을 때) 값이 많이 달라져 현재 위치가지도의 먼 지점 사이에서 "점프"하도록하는 것입니다.
이것을 피할 수있는 쉬운 방법이 궁금합니다. 첫 번째 아이디어로 특정 임계 값을 초과하여 정확하게 값을 버리는 것에 대해 생각했지만 더 좋은 방법이 있다고 생각합니다. 프로그램이 이것을 수행하는 일반적인 방법은 무엇입니까?
GPS 데이터로 작업하고 있으며 매 초마다 값을 가져오고지도에 현재 위치를 표시합니다. 문제는 때때로 (특히 정확도가 낮을 때) 값이 많이 달라져 현재 위치가지도의 먼 지점 사이에서 "점프"하도록하는 것입니다.
이것을 피할 수있는 쉬운 방법이 궁금합니다. 첫 번째 아이디어로 특정 임계 값을 초과하여 정확하게 값을 버리는 것에 대해 생각했지만 더 좋은 방법이 있다고 생각합니다. 프로그램이 이것을 수행하는 일반적인 방법은 무엇입니까?
답변:
이 상황에 정확히 사용할 수있는 간단한 칼만 필터가 있습니다. Android 기기에서 수행 한 작업에서 비롯된 것입니다.
일반적인 칼만 필터 이론은 공분산 행렬로 표시되는 추정치의 정확도와 함께 벡터에 대한 추정치에 관한 것입니다. 그러나 Android 기기의 위치를 추정하기 위해 일반적인 이론은 매우 간단한 경우로 줄어 듭니다. Android 위치 제공 업체는 위치를 위도와 경도로 미터 단위로 측정 한 단일 숫자로 지정된 정확도와 함께 제공합니다. 즉, 칼만 필터의 위치가 두 개의 숫자로 측정 되더라도 공분산 행렬 대신 칼만 필터의 정확도를 단일 숫자로 측정 할 수 있습니다. 또한 위도, 경도 및 미터가 서로 다른 모든 단위라는 사실은 무시할 수 있습니다. 스케일링 계수를 Kalman 필터에 넣어 모두 동일한 단위로 변환하면,
코드는 현재 위치의 최고 추정치가 마지막으로 알려진 위치라고 가정하기 때문에 개선 될 수 있으며, 누군가가 이동하는 경우 Android 센서를 사용하여 더 나은 추정치를 생성 할 수 있어야합니다. 이 코드에는 초당 미터 단위로 표현되는 단일 자유 매개 변수 Q가 있으며, 새로운 위치 추정치가 없을 때 정확도가 얼마나 빨리 저하되는지를 설명합니다. Q 파라미터가 높을수록 정확도가 더 빨리 감쇠합니다. 칼만 필터는 일반적으로 정확도가 예상보다 약간 빨리 쇠퇴 할 때 더 잘 작동하므로 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;
}
/// <summary>
/// Kalman filter processing for lattitude and longitude
/// </summary>
/// <param name="lat_measurement_degrees">new measurement of lattidude</param>
/// <param name="lng_measurement">new measurement of longitude</param>
/// <param name="accuracy">measurement of 1 standard deviation error in metres</param>
/// <param name="TimeStamp_milliseconds">time of measurement</param>
/// <returns>new state</returns>
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;
}
}
}
Q_metres_per_second
해당 sigma
Wikipedia 기사의 "관련 프로세스"섹션 에있는 변수 에 해당합니다 . Q_metres_per_second
표준 편차이며 미터 단위로 측정되므로 미터 / 초가 아닌 미터가 단위입니다. 1 초가 경과 한 후 분포의 표준 편차에 해당합니다.
찾고있는 것을 Kalman Filter 라고합니다 . 탐색 데이터 를 매끄럽게 하는 데 자주 사용됩니다 . 반드시 사소한 것은 아니며 많은 조정이 가능하지만 매우 표준적인 접근 방식이며 잘 작동합니다. 있습니다 KFilter 라이브러리 는 C ++ 구현 가능합니다.
내 다음 폴백은 최소 제곱에 적합 합니다. 칼만 필터는 속도를 고려하여 데이터를 부드럽게하는 반면 최소 자승법은 위치 정보 만 사용합니다. 그럼에도 불구하고 구현하고 이해하는 것이 훨씬 간단합니다. GNU Scientific Library가 이것을 구현 한 것으로 보입니다 .
조금 늦을 수도 있습니다 ...
필자는 Android와 KalmanLocationManager 를 작성 했는데 ,이 두 곳은 가장 일반적인 위치 제공 업체 인 네트워크와 GPS를 감싸고 데이터를 필터링하고 LocationListener
( '실제'제공 업체와 같은) 업데이트를 제공합니다 .
나는 주로 판독 사이에 "보간"하기 위해 사용합니다-예를 들어 100 밀리 초마다 업데이트 (위치 예측)를 수신합니다 (예 : 최대 gps 속도 대신 1 초). 내 위치를 애니메이션 할 때 더 나은 프레임 속도를 제공합니다.
실제로 각 차원마다 위도, 경도 및 고도의 세 가지 칼만 필터를 사용합니다. 어쨌든 그들은 독립적입니다.
이것은 6x6 상태 전이 행렬 하나를 사용하는 대신 3 개의 다른 2x2 행렬을 사용하여 행렬 수학을 훨씬 쉽게 만듭니다. 실제로 코드에서는 행렬을 전혀 사용하지 않습니다. 모든 방정식을 풀고 모든 값은 프리미티브 (이중)입니다.
소스 코드가 작동하고 있으며 데모 활동이 있습니다. 어떤 곳에서는 javadoc이 없어서 유감입니다.
시간당 위치 변경으로 속도를 계산해서는 안됩니다. GPS의 위치가 정확하지 않을 수 있지만 정확한 속도 (5km / h 이상)가 있습니다. 따라서 GPS 위치 스탬프의 속도를 사용하십시오. 또한 대부분의 경우 작동하지만 물론 그렇게해서는 안됩니다.
배송 된 GPS 위치는 이미 Kalman으로 필터링되었으므로 후 처리 과정에서 일반적으로 GPS 칩과 동일한 정보가없는 경우 개선 할 수 없습니다.
부드럽게 할 수 있지만 오류가 발생합니다.
장치가 정지 상태 일 때 위치를 제거하고 점프 위치를 제거하고 일부 장치 / 구성이 제거되지 않도록하십시오.
나는 보통 가속도계를 사용합니다. 단기간에 갑작스런 위치 변화는 높은 가속도를 의미합니다. 이것이 가속도계 원격 측정에 반영되지 않으면 위치를 계산하는 데 사용되는 "최고의 3 개"위성이 변경 되었기 때문일 것입니다 (GPS 텔레 포팅이라고 함).
GPS 텔레 포팅으로 인해 자산이 정지되고 도약 할 때 중심을 점진적으로 계산하면 더 큰 쉘 세트를 효과적으로 교차시켜 정밀도를 향상시킬 수 있습니다.
자산이 정지되어 있지 않을 때이를 수행하려면 속도, 방향 및 선형 및 회전 (자이로가있는 경우) 가속도 데이터를 기반으로 다음 위치와 방향을 추정해야합니다. 이것은 유명한 K 필터의 기능입니다. GPS 모듈을 제외한 모든 것을 포함하는 AHRS와 하나를 연결하는 잭으로 하드웨어에서 약 150 달러에 구입할 수 있습니다. 자체 CPU 및 Kalman 필터링이 내장되어 있습니다. 결과는 안정적이고 꽤 좋습니다. 관성 가이던스는 지터에 대한 내성이 강하지 만 시간이지나면서 표류합니다. GPS는 지터가 발생하기 쉽지만 시간이 흐르면서 표류하지는 않으며 실제로 서로를 보상하기 위해 만들어졌습니다.
더 적은 수학 / 이론을 사용하는 한 가지 방법은 한 번에 2, 5, 7, 10 개의 데이터 포인트를 샘플링하고 이상치 인 것을 결정하는 것입니다. 칼만 필터보다 특이점을 덜 정확하게 측정하려면 다음 알고리즘 을 사용하여 점 사이의 모든 쌍의 현명한 거리를 취하고 다른 것보다 가장 먼 거리를 버리십시오. 일반적으로 이러한 값은 대체하려는 외부 값에 가장 가까운 값으로 바뀝니다.
예를 들어
5 개의 샘플 포인트 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이 가장 큰 경우 BD = min {AB, BC, BD, BE} 인 경우 B 지점을 D로 바꿉니다.
이 평활화는 특이 치를 결정하며 점 D 대신 BD의 중간 점을 사용하여 위치 선을 평활화하여 보강 할 수 있습니다. 마일리지는 다를 수 있으며보다 수학적으로 엄격한 솔루션이 존재합니다.
최소 자승법에 따라 실험해야 할 몇 가지 사항이 있습니다.
최소 제곱 맞춤이라고해서 반드시 선형이어야한다는 의미는 아닙니다. 2 차 곡선을 데이터에 최소 제곱합 할 수 있으며, 이는 사용자가 가속하는 시나리오에 적합합니다. (적어도 제곱에 적합하다는 것은 좌표를 종속 변수로 사용하고 시간을 독립 변수로 사용한다는 것을 의미합니다.)
보고 된 정확도를 기준으로 데이터 포인트에 가중치를 부여 할 수도 있습니다. 정확도가 낮 으면 데이터 포인트가 낮아집니다.
정확도가 낮은 경우 원 또는 사용자가보고 된 정확도를 기반으로 할 수있는 범위를 나타내는 것이 표시되는 경우 단일 지점을 표시하는 것이 아니라 시도 할 수도 있습니다. (이것은 iPhone의 내장 Google Maps 응용 프로그램의 기능입니다.)
스플라인을 사용할 수도 있습니다. 가지고있는 값을 입력하고 알려진 점 사이에 점을 보간하십시오. 이것을 최소 제곱 적합, 이동 평균 또는 칼만 필터 (다른 답변에서 언급 한 바와 같이)와 연결하면 "알려진"점 사이의 점을 계산할 수 있습니다.
알려진 값 사이의 값을 보간 할 수 있으면 충실도가 높으면 어떤 데이터가 존재할지에 대한 부드러운 전환과 / 합리적인 / 근사치를 얻을 수 있습니다. http://en.wikipedia.org/wiki/Spline_interpolation
스플라인마다 특성이 다릅니다. 내가 가장 일반적으로 사용하는 것은 Akima와 Cubic 스플라인입니다.
고려해야 할 또 다른 알고리즘은 Ramer-Douglas-Peucker 라인 단순화 알고리즘이며 GPS 데이터 단순화에 일반적으로 사용됩니다. ( http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm )
칼만 필터에 다시가는 ... 여기 GPS 데이터에 대한 칼만 필터의 C 구현을 발견 http://github.com/lacker/ikalman 나는 아직 그것을 시도하지 않은,하지만 약속 보인다.
관심있는 사람이 있으면 CoffeeScript에 매핑됩니다. ** 편집-> 백본을 사용하여 죄송하지만 아이디어를 얻습니다.
아트 리브가있는 비콘을 받도록 약간 수정 됨
{위도 : item.lat, 경도 : 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]
@lat
및 @lng
설정은. 해야 +=
보다는=
Java 코드를 @Stochastically에서 Kotlin으로 변환했습니다.
class KalmanLatLong
{
private val MinAccuracy: Float = 1f
private var Q_metres_per_second: Float = 0f
private var TimeStamp_milliseconds: Long = 0
private var lat: Double = 0.toDouble()
private var lng: Double = 0.toDouble()
private var variance: Float =
0.toFloat() // P matrix. Negative means object uninitialised. NB: units irrelevant, as long as same units used throughout
fun KalmanLatLong(Q_metres_per_second: Float)
{
this.Q_metres_per_second = Q_metres_per_second
variance = -1f
}
fun get_TimeStamp(): Long { return TimeStamp_milliseconds }
fun get_lat(): Double { return lat }
fun get_lng(): Double { return lng }
fun get_accuracy(): Float { return Math.sqrt(variance.toDouble()).toFloat() }
fun SetState(lat: Double, lng: Double, accuracy: Float, TimeStamp_milliseconds: Long)
{
this.lat = lat
this.lng = lng
variance = accuracy * accuracy
this.TimeStamp_milliseconds = TimeStamp_milliseconds
}
/// <summary>
/// Kalman filter processing for lattitude and longitude
/// /programming/1134579/smooth-gps-data/15657798#15657798
/// </summary>
/// <param name="lat_measurement_degrees">new measurement of lattidude</param>
/// <param name="lng_measurement">new measurement of longitude</param>
/// <param name="accuracy">measurement of 1 standard deviation error in metres</param>
/// <param name="TimeStamp_milliseconds">time of measurement</param>
/// <returns>new state</returns>
fun Process(lat_measurement: Double, lng_measurement: Double, accuracy: Float, TimeStamp_milliseconds: Long)
{
var accuracy = accuracy
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
val 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.toFloat() * 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
val 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
}
}
}
다음은 @Stochastically의 Java 구현이 필요한 사람을위한 Javascript 구현입니다.
class GPSKalmanFilter {
constructor (decay = 3) {
this.decay = decay
this.variance = -1
this.minAccuracy = 1
}
process (lat, lng, accuracy, timestampInMs) {
if (accuracy < this.minAccuracy) accuracy = this.minAccuracy
if (this.variance < 0) {
this.timestampInMs = timestampInMs
this.lat = lat
this.lng = lng
this.variance = accuracy * accuracy
} else {
const timeIncMs = timestampInMs - this.timestampInMs
if (timeIncMs > 0) {
this.variance += (timeIncMs * this.decay * this.decay) / 1000
this.timestampInMs = timestampInMs
}
const _k = this.variance / (this.variance + (accuracy * accuracy))
this.lat += _k * (lat - this.lat)
this.lng += _k * (lng - this.lng)
this.variance = (1 - _k) * this.variance
}
return [this.lng, this.lat]
}
}
사용 예 :
const kalmanFilter = new GPSKalmanFilter()
const updatedCoords = []
for (let index = 0; index < coords.length; index++) {
const { lat, lng, accuracy, timestampInMs } = coords[index]
updatedCoords[index] = kalmanFilter.process(lat, lng, accuracy, timestampInMs)
}