我正在处理GPS数据,每秒获取一次值并在地图上显示当前位置。问题是有时(特别是精度低时),值会发生很大变化,导致当前位置在地图上“跳跃”到远离的点。
我想了解一些足够简单的方法来避免这种情况。作为第一个想法,我考虑将精度超过某个阈值的值丢弃,但我想可能还有其他更好的方法可以实现。程序通常如何执行此操作?
我正在处理GPS数据,每秒获取一次值并在地图上显示当前位置。问题是有时(特别是精度低时),值会发生很大变化,导致当前位置在地图上“跳跃”到远离的点。
我想了解一些足够简单的方法来避免这种情况。作为第一个想法,我考虑将精度超过某个阈值的值丢弃,但我想可能还有其他更好的方法可以实现。程序通常如何执行此操作?
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;
}
}
}
这可能有点晚了...
我为Android编写了KalmanLocationManager,它包装了两个最常见的位置提供程序:网络和GPS,对数据进行卡尔曼滤波,并向LocationListener
(就像两个“真正”的提供者)提供更新。
我主要用它来“插值”读数-例如每100毫秒接收更新(位置预测),而不是最大GPS速率的一秒钟,这给我的位置动画更好的帧率。
实际上,它使用三个卡尔曼滤波器,一个用于每个维度:纬度、经度和海拔高度。无论如何,它们是独立的。
这使得矩阵数学变得更容易:我使用了3个不同的2x2矩阵,而不是使用一个6x6状态转移矩阵。实际上,在代码中,我根本不使用矩阵。解决了所有方程式,所有值都是原始的(double)。
源代码可以工作,并且有一个演示活动。对于某些地方缺乏javadoc,我很抱歉,我会赶上的。
我通常使用加速度计。在短时间内位置的突然变化意味着高加速度。如果这在加速度计遥测中没有反映出来,几乎可以确定是由于计算位置所用的“最佳三颗”卫星发生了变化(我称之为GPS瞬间跳跃)。
当资产静止不动并因GPS瞬间跳跃而跳动时,如果逐步计算重心,您实际上正在相交越来越大的一组外壳,从而提高了精度。
在资产不静止的情况下,您必须根据速度、航向和线性和旋转(如果有陀螺仪)加速度数据估计其可能的下一个位置和方向。这基本上就是著名的K滤波器所做的。您可以在AHRS上以约150美元的价格得到整个硬件,其中包含除GPS模块之外的所有内容,并带有连接插孔。它具有自己的CPU和Kalman滤波板;结果稳定且相当好。惯性导航高度抵抗抖动但随时间漂移。GPS容易受到抖动的影响,但不会随时间漂移,它们实际上是相互补偿的。
回到卡尔曼滤波器...我在这里找到了一个针对GPS数据的Kalman滤波器的C实现:http://github.com/lacker/ikalman 我还没有尝试过,但看起来很有前途。
仅因为是最小二乘拟合,并不意味着它必须是线性的。你可以对数据进行二次拟合,这将适用于用户加速的情况。(请注意,我所说的最小二乘拟合是使用坐标作为因变量,时间作为自变量。)
您还可以尝试根据报告的准确度对数据点进行加权。当准确度较低时,将这些数据点的权重降低。
你可能想尝试的另一件事是,如果准确度低,不要显示单个点,而是显示一个圆或其他指示用户基于报告准确度可能处于的范围的东西。(这就是iPhone内置的Google地图应用程序所做的。)
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)
}