提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
轨迹数据大多是由GPS设备从原始的嘈杂环境中采样而来,对 GPS 轨迹数据进行平滑滤波处理的主要目的是消除或减少由于信号采集和信号传输的过程中含有的不确定性和波动,从而减少噪声影响和提高精度和可靠性。
车辆轨迹数据的基本格式如下:
collect_time | id | lon | lat |
---|---|---|---|
时间 | 车辆标识 | 经度 | 纬度 |
本文提供了两种对时序经纬度的轨迹数据进行平滑滤波的python方法实现。
一、滑动平均滤波
1.基本介绍
滑动滤波器(Moving Average Filter)是一种常见的信号处理技术,用于平滑时间序列数据或信号。它的基本原理是利用窗口内的数据点进行加权平均,从而减少噪声和突发波动,得到更平滑的输出。
滑动滤波方法大致可分为:简单滑动平均、加权滑动平均、指数加权移动平均。其原理基本相同,区别在于:简单滑动平均的窗口中所有数据的权重相同,加权滑动平均的窗口中不同数据所占权重由用户指定,指数加权移动平均的窗口中不同数据所占权重由历史数据计算而来。
这里不介绍各种滑动滤波方法的具体数学原理,只提供简单滑动平均的实现代码。如有需要使用其他的滑动平均方法,可以自行搜索其数学原理,互联网上有海量的解释文章,进而对代码稍作更改即可。
2.代码
moving_average_with_padding函数使用了pandas和numpy库。输入数据data为pandas的series格式,表示需要平滑的那一列数据;输入数据window_size为整数格式,表示滑动窗口的大小。
对于大小为window_size的当前窗口,窗口位于最中间的数据用整个窗口的均值来代替,接着窗口向右移动一格,如此计算直至边界条件。
需要注意的是,整个数据集最左边的window_size // 2个数据是没有被平滑处理的,整个数据集最右边的window_size - left_window_size - 1个数据也是没有被平滑处理的。这是滑动窗口法自身的缺陷,因此代码单独平滑处理了左边缘和右边缘的数据。
# 定义平均滑动窗口函数
def moving_average_with_padding(data, window_size):
smoothed = np.zeros_like(data)
weights = np.repeat(1.0, window_size) / window_size
left_window_size = window_size // 2
right_window_size = window_size - left_window_size - 1
# 平滑左边缘数据
for i in range(left_window_size):
smoothed[i] = np.mean(data[:i + 1])
# 平滑右边缘数据
for i in range(len(data) - right_window_size, len(data)):
smoothed[i] = np.mean(data[i:i + right_window_size])
# 应用滑动平均
for i in range(left_window_size, len(data) - right_window_size):
smoothed[i] = np.sum(data[i - left_window_size:i + right_window_size + 1] * weights)
return smoothed
对于想要处理的列数据,只需这样调用函数即可平滑:
import pandas as pd
import numpy as np
#假设需要处理的数据已经用df读入,需要将data列平滑
window_size = 10 #窗口大小自行指定
smoothed_data = moving_average_with_padding(df["data"], window_size)
#将平滑后的数据加入新列
df['smoothed_data'] = smoothed_data
如果要平滑轨迹数据,只需按上面例子将经度和纬度列分别处理即可。
二、kalman滤波
1.基本介绍
卡尔曼滤波(Kalman Filter)是一种用于估计系统状态的强大算法,广泛应用于信号处理,在轨迹数据的滤波平滑中表现也十分出色。卡尔曼滤波的主要目标是通过观察到的数据,估计系统的状态,并且最小化预测和观测之间的误差的方差。其关键假设是系统状态和观测值之间服从高斯分布,并且系统的状态遵循线性动态规律。
这里同样不介绍各种kalman滤波的具体数学原理,只提供实现好的python代码,有需要则查询互联网上海量的解析文章即可。
2.代码
Kalman_traj_smooth函数使用了pandas、numpy和pykalman库。输入数据data为pandas的dataframe格式,包含collect_time、lon、lat三列,表示需要平滑的那一列数据;输入数据process_noise_std为浮点数格式,表示过程噪声标准差;输入数据measurement_noise_std为浮点数格式,表示观测噪声标准差。
def Kalman_traj_smooth(data, process_noise_std, measurement_noise_std):
'''
使用卡尔曼滤波器对轨迹数据进行平滑处理
返回
----
data : DataFrame
平滑后的轨迹数据
'''
# 拷贝数据,避免修改原始数据
data = data.copy()
# 轨迹数据转换为numpy数组
observations = data[['lon', 'lat']].values
timestamps = data['collect_time']
# F-状态转移矩阵
transition_matrix = np.array([[1, 0, 1, 0],
[0, 1, 0, 1],
[0, 0, 1, 0],
[0, 0, 0, 1]])
# H-观测矩阵
observation_matrix = np.array([[1, 0, 0, 0],
[0, 1, 0, 0]])
# R-观测噪声协方差矩阵
# 如果measurement_noise_std是list,则认为是观测噪声协方差矩阵的对角线元素
if isinstance(measurement_noise_std, list):
observation_covariance = np.diag(measurement_noise_std) ** 2
else:
observation_covariance = np.eye(2) * measurement_noise_std ** 2
# Q-过程噪声协方差矩阵
# 如果process_noise_std是list,则认为是过程噪声协方差矩阵的对角线元素
if isinstance(process_noise_std, list):
transition_covariance = np.diag(process_noise_std) ** 2
else:
transition_covariance = np.eye(4) * process_noise_std ** 2
# 初始状态
initial_state_mean = [observations[0, 0], observations[0, 1], 0, 0]
# 初始状态协方差矩阵
initial_state_covariance = np.eye(4) * 1
# 初始化卡尔曼滤波器
kf = KalmanFilter(
transition_matrices=transition_matrix,
observation_matrices=observation_matrix,
initial_state_mean=initial_state_mean,
initial_state_covariance=initial_state_covariance,
observation_covariance=observation_covariance,
transition_covariance=transition_covariance
)
# 使用卡尔曼滤波器进行平滑处理
# 先创建变量存储平滑后的状态
smoothed_states = np.zeros((len(observations), 4))
# 将初始状态存储到平滑后的状态中
smoothed_states[0, :] = initial_state_mean
# 从第二个状态开始,进行循环迭代
current_state = initial_state_mean
current_covariance = initial_state_covariance
for i in range(1, len(observations)):
# 计算时间间隔
dt = (timestamps.iloc[i] - timestamps.iloc[i - 1]).total_seconds()
# 更新状态转移矩阵
kf.transition_matrices = np.array([[1, 0, dt, 0],
[0, 1, 0, dt],
[0, 0, 1, 0],
[0, 0, 0, 1]])
# 根据当前状态的预测情况与观测结果进行状态估计
current_state, current_covariance = kf.filter_update(
current_state, current_covariance, observations[i]
)
# 将平滑后的状态存储到变量中
smoothed_states[i, :] = current_state
# 将平滑后的数据结果添加到原始数据中
data['lon'] = smoothed_states[:, 0]
data['lat'] = smoothed_states[:, 1]
return data
对于想要处理的轨迹数据,只需这样调用函数即可平滑:
import pandas as pd
import numpy as np
from pykalman import KalmanFilter
#假设需要处理的数据已经用df读入,需要将lon列和lat列平滑
#将时间、经度、纬度列的数据提取出来
selected_data = group[['collect_time', 'lon', 'lat']]
#用作存储平滑后的经度列和纬度列
smoothed_lon = []
smoothed_lat = []
# 滤波处理
smoothed_data = Kalman_traj_smooth(lon_lat_data, process_noise_std=0.01, measurement_noise_std=0.1)
# 将滤波后的数据作为新列插入原始DataFrame中
smoothed_lon.extend(smoothed_data['lon'].tolist())
smoothed_lat.extend(smoothed_data['lat'].tolist())
df['smoothed_lon'] = smoothed_lon
df['smoothed_lat'] = smoothed_lat
需要注意一点,process_noise_std和measurement_noise_std参数需要自己指定。kalman滤波是通过观测值(即原始数据值)和预测值(即模型预测出的值)来综合得到最后的一个平滑值。如果你更相信观测值,就让measurement_noise_std参数小一些;如果你更相信预测值,就让process_noise_std参数小一些。