我与GPS数据的工作,获取值每秒,并在地图上显示当前位置。 的问题是,有时(特别是当精度低)的值有很大的差异,使得当前位置在地图上遥远点之间“跳跃”。
我想知道一些简单的方法足以避免这种情况。 作为第一个想法,我想和超过一定阈值精度丢弃值,但我想有一些其他更好的方法来做到。 什么是程序执行这一通常的方式?
我与GPS数据的工作,获取值每秒,并在地图上显示当前位置。 的问题是,有时(特别是当精度低)的值有很大的差异,使得当前位置在地图上遥远点之间“跳跃”。
我想知道一些简单的方法足以避免这种情况。 作为第一个想法,我想和超过一定阈值精度丢弃值,但我想有一些其他更好的方法来做到。 什么是程序执行这一通常的方式?
你所寻找的被称为卡尔曼滤波器 。 它经常被用来平滑导航数据 。 它不一定是微不足道的,而且有很多调整,你可以做的,但它是一个非常标准的做法和行之有效的。 有一个KFilter库可这是一个C ++实现。
我的下一个回退将最小二乘法拟合 。 卡尔曼滤波器将平滑数据取速度考虑在内,而最小二乘拟合方法将只使用位置信息。 不过,这绝对是易于实施和理解。 它看起来像GNU科学图书馆可能有这个实施。
这里有一个简单的卡尔曼滤波器可用于正是这种情况。 它来自一些工作,我在Android设备上一样。
一般卡尔曼滤波器理论是一回事估计向量,由协方差矩阵代表的估计的准确性。 然而,对于在Android设备估计位置的一般理论减少到一个非常简单的情况下。 机器人位置提供给所述位置作为纬度和经度,以被指定为以米为单位测得的单数的精度在一起。 这意味着,代替的协方差矩阵,在所述卡尔曼滤波器的准确度可以通过一个单一的数字测量,即使在卡尔曼滤波器的位置被一由两个数字测量。 另外一个事实,即纬度,经度和米是有效的所有不同的单位,可以忽略不计,因为如果你把缩放因素卡尔曼滤波将它们全部转换为相同的单位,然后将这些比例因子最终转换结果时抵消回到原单位。
该代码可以改进,因为它假设的当前位置的最佳估计是最后已知位置,如果有人动它应该是可以使用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;
}
}
}
这可能来晚了一点......
我写这篇KalmanLocationManager为Android,它包装的两种最常见的位置提供商,网络和GPS,卡尔曼滤波器的数据,并提供更新的LocationListener
(如两个“真正”的供应商)。
我用它主要是为了读数之间的“插值” - 接收更新(位置预测)每100个米利斯例如(而不是一秒的最大GPS速度),动画我的位置的时候,给了我一个更好的帧速率。
实际上,它使用了三个卡尔曼滤波器,对每个维度:经度,纬度和海拔高度。 他们是独立的,反正。
这使得矩阵数学更容易:而不是使用一个6x6的状态转移矩阵,我用3点不同的2x2矩阵。 实际上,在代码中,我完全不使用矩阵。 解决了所有公式和所有值都是基元(双人)。
源代码工作,并有一个演示活动。 对不起,在一些地方缺乏的Javadoc,我会赶上来。
你不应该从每个时间位置的变化来计算速度。 GPS可以具有不准确的位置,但它有精确的速度(5km以上/小时)。 因此,使用从GPS位置标记的速度。 进一步,你不应该这样做当然有,但它的工作时间最多。
GPS位置,如交付,已经卡尔曼滤波,你可能无法提高,在后处理通常你没有像GPS芯片相同的信息。
你能顺利,但是,这个还引入了错误。
只要确保你删除的位置时,该设备静止,这消除跳跃位置,一些设备/配置不删除。
使用较少的数学/理论的一种方法是在一个时间到样品2,5,7,或10个数据点,并确定那些离群值。 比卡尔曼滤波器离群的不太准确的措施是使用下面的算法取分之间的所有成对的距离,并抛出了一个最远离其他人。 通常,这些值与最接近的值替换为您更换偏离值
例如
平滑在五个取样点A,B,C,d,E
距离AD和AE的α总计= SUM
b总=距离的SUM AB BC BD BE
AC BC CTOTAL =距离SUM CD CE
DTOTAL =距离SUM DA DB DC DE
E总=距离之和EA EB EC ON
如果b总是最大的,你会用d代替B点,如果BD = {分AB,BC,BD,BE}
这种平滑确定离群值,可以通过使用BD代替点d的中点以平滑位置线来增强。 你的情况可能会有所不同,更严格的数学解决方案存在。
我通常使用加速度计。 的地位在短期内的突然变化意味着高加速度。 如果这不是在加速度传感器遥测反映它几乎肯定是由于用于计算位置的“最佳三”卫星的变化(我所指的GPS意念移物)。
当资产是在休息,跳来跳去由于GPS意念移物,如果逐步计算重心您可以有效地交叉越来越大集弹,提高测量精度。
要做到这一点,当资产不是在休息,你必须根据速度,航向和线性和旋转估计其未来可能的位置和方向(如果你有陀螺仪)加速度数据。 这或多或少是著名·K滤波做什么。 你可以得到整个事情在硬件约150 $含一切,但GPS模块AHRS的,并用千斤顶连接一个。 它有自己的CPU和主板卡尔曼滤波; 结果是稳定的,相当不错。 惯性制导对抖动高度耐药,但随着时间的漂移。 GPS很容易出现抖动,但不随时间漂移,他们几乎以补偿对方。
你也可以使用一个样。 饲料中你的价值观和你的已知点之间插入点。 用最小二乘法拟合,移动平均或卡尔曼滤波器(在其他的答案中提到)把这个给你计算出其间的点你的“已知”点的能力。
如果能够你的已知,之间插入的值给你一个很好的平稳过渡,是什么,如果你有一个更高保真度的数据将存在于/合理/近似。 http://en.wikipedia.org/wiki/Spline_interpolation
不同的花键具有不同的特点。 我见过的最常用的一个人是阿基玛和三次样条。
另一个要考虑的算法是拉默 - 道格拉斯 - 普克线简化算法,它是在GPS数据的简化十分常用。 ( http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm )
让我们再回到卡尔曼滤波器...我发现一个C实施卡尔曼滤波器在这里GPS数据: http://github.com/lacker/ikalman我还没有尝试过了还,但它似乎有希望的。
至于最小二乘法拟合,这里有一些其他的东西来试验:
只是因为它的最小二乘法拟合并不意味着它必须是线性的。 你可以最小二乘法,拟合数据的二次曲线,那么这将适合用户正在加速的情况。 (注意,通过最小二乘法拟合我的意思使用的坐标作为因变量和时间作为独立变量。)
您也可以尝试加权根据报告准确性的数据点。 当精度低重量的那些数据点低。
你可能想尝试的另一件事是,而不是显示一个单点,如果精度低显示了一圈什么指示,用户可根据报告的准确度的范围内。 (这是iPhone的内置谷歌地图应用程序一样。)
如果任何人有兴趣映射到CoffeeScript的。 **编辑 - 使用骨干>也很抱歉,但你的想法。
略作修改,以接受attribs一盏明灯
{纬度: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]