如何基于时序GPS坐标检测铁路轨道直线路段与左右弯道
基于时序GPS数据的铁路轨道弯道检测方案
你当前采用的样条插值残差判定思路存在本质问题:插值残差仅能反映拟合曲线与原始点位的偏离程度,既无法区分弯道转向,也会受GPS噪声、重复点位、经纬度球面坐标畸变的干扰,完全不具备普适性。从你给出的测试结果也能看出问题:直线路段的计算残差(0.038)甚至高于2号弯道的残差(0.019),说明这个指标本身就无法区分直道和弯道。
你提供的弯道样例数据如下:
latitude longitude altitude 1 43.46724 -5.823470 145.0 2 43.46726 -5.823653 145.2 3 43.46728 -5.823837 145.4 4 43.46730 -5.824022 145.6 5 43.46730 -5.824022 145.6 6 43.46734 -5.824394 146.0 7 43.46738 -5.824768 146.3 8 43.46738 -5.824768 146.3 9 43.46742 -5.825146 146.7 10 43.46742 -5.825146 146.7 11 43.46746 -5.825527 147.1 12 43.46746 -5.825527 147.1 13 43.46750 -5.825910 147.3 14 43.46751 -5.826103 147.4 15 43.46753 -5.826295 147.6 16 43.46753 -5.826489 147.8 17 43.46753 -5.826685 148.1 18 43.46753 -5.826878 148.2 19 43.46752 -5.827073 148.4 20 43.46750 -5.827266 148.6 21 43.46748 -5.827458 148.9 22 43.46744 -5.827650 149.2 23 43.46741 -5.827839 149.5 24 43.46736 -5.828029 149.7 25 43.46731 -5.828212 150.1 26 43.46726 -5.828393 150.4 27 43.46720 -5.828572 150.5 28 43.46713 -5.828746 150.8 29 43.46706 -5.828914 151.0 30 43.46698 -5.829078 151.2 31 43.46690 -5.829237 151.4 32 43.46681 -5.829392 151.6 33 43.46671 -5.829540 151.8 34 43.46661 -5.829680 152.0 35 43.46650 -5.829816 152.2 36 43.46639 -5.829945 152.4 37 43.46628 -5.830066 152.4 38 43.46616 -5.830180 152.4 39 43.46604 -5.830287 152.5 40 43.46591 -5.830384 152.6 41 43.46579 -5.830472 152.8 42 43.46566 -5.830552 152.9 43 43.46552 -5.830623 153.2 44 43.46539 -5.830687 153.4 45 43.46526 -5.830745 153.6 46 43.46512 -5.830795 153.8 47 43.46499 -5.830838 153.9 48 43.46485 -5.830871 153.9 49 43.46471 -5.830895 154.0 50 43.46458 -5.830911 154.2 51 43.46445 -5.830919 154.3 52 43.46432 -5.830914 154.7 53 43.46418 -5.830896 155.1 54 43.46406 -5.830874 155.6 55 43.46393 -5.830842 155.9 56 43.46381 -5.830803 156.0 57 43.46368 -5.830755 155.5 58 43.46356 -5.830700 155.3 59 43.46332 -5.830575 155.1
你之前编写的样条插值测试代码如下:
from scipy.interpolate import make_interp_spline ## read csv file with pandas df = pd.read_csv("Curvas/Curva_2.csv") # take latitude and longitude columns df['latitude'].fillna(method='ffill',inplace=True) df['longitude'].fillna(method='ffill',inplace=True) # plot the data # df.plot(x='longitude', y='latitude', style='o') # plt.show() # using longitude and latitude data, use spline interpolation to create a new curve x = df['longitude'] y = df['latitude'] xnew = np.linspace(x.min(), x.max(), x.shape[0]) ynew = make_interp_spline(xnew, y)(x) plt.plot(xnew, ynew, zorder=2) plt.show() ## Error results using different coordinates/routes ## Curve_1 → Left (e = 0.04818886515888465) ## Curve_2 → Left (e = 0.019459215874292113) ## Straight_1 → Straight (e = 0.03839597167971931)
具体实现步骤
1. 数据预处理
- 首先剔除经纬度完全重复的点位,避免后续向量计算出现除零错误
- 将WGS84经纬度坐标投影到对应区域的平面投影坐标系(你提供的点位位于西班牙区域,可采用EPSG:25829 UTM投影),将角度单位的经纬度转换为米为单位的平面x/y坐标,禁止直接用经纬度角度值做几何计算
- 对投影后的坐标做3-5个点的滑动窗口均值平滑,抵消GPS的随机测量噪声
2. 转向判定核心逻辑
轨迹转向检测的通用高效方案是连续三点叉乘判别法,不需要复杂插值,计算逻辑简单可靠:
将轨迹点按时间顺序排列,取任意连续三个点$P_{i-1}、P_i、P_{i+1}$,计算两个相邻段的向量:
- 前一段向量:$\vec{a} = (x_i - x_{i-1}, y_i - y_{i-1})$
- 后一段向量:$\vec{b} = (x_{i+1} - x_i, y_{i+1} - y_i)$
计算两个向量的二维叉乘值:cross = a_x * b_y - a_y * b_x
叉乘值的符号直接对应转向方向: - 若
cross > 判定阈值:当前点位于左转弯路段 - 若
cross < -判定阈值:当前点位于右转弯路段 - 若
abs(cross) ≤ 判定阈值:当前点位于直线路段
3. 阈值标定与结果优化
- 阈值不需要硬编码:取你提前标注的直线路段上叉乘值的3倍标准差作为判定阈值即可,可自动适配你的GPS设备精度
- 对逐点得到的转向标签做滑动窗口投票:例如取20个点的窗口(对应铁路上约30-50米长度),窗口内超过70%的点判定为同一转向,就将整个窗口标记为对应路段,避免零星噪声点把连续弯道切割成碎片
- 长度小于10个点的短转向片段直接合并到相邻长路段,排除GPS跳点导致的误检
可直接运行的参考代码
import pandas as pd import numpy as np import pyproj # 初始化坐标转换器:WGS84经纬度转研究区域平面UTM坐标 transformer = pyproj.Transformer.from_crs("EPSG:4326", "EPSG:25829", always_xy=True) # 读取数据 df = pd.read_csv("Curvas/Curva_2.csv") # 缺失值填充+重复点位去除 df = df[['latitude', 'longitude', 'altitude']].ffill().drop_duplicates(subset=['latitude', 'longitude']) # 坐标投影,注意转换器输入顺序为经度、纬度 df['x'], df['y'] = transformer.transform(df['longitude'].values, df['latitude'].values) # 计算连续相邻点的坐标差 df['dx1'] = df['x'].diff() df['dy1'] = df['y'].diff() df['dx2'] = df['dx1'].shift(-1) df['dy2'] = df['dy1'].shift(-1) # 计算叉乘值 df['cross'] = df['dx1'] * df['dy2'] - df['dy1'] * df['dx2'] # 转向判定,经验阈值10(单位:平方米,适配1-2米间隔的GPS采样点) # 正式使用时请用自有直线路段数据重新标定阈值 turn_threshold = 10 df['turn_type'] = np.where( df['cross'] > turn_threshold, 'left_curve', np.where(df['cross'] < -turn_threshold, 'right_curve', 'straight') )
补充说明:铁路轨道曲率变化平缓,如果需要更高的检测精度,可以在平滑后的平面坐标上用滑动窗口拟合圆弧,计算连续曲率值与曲率方向,结果会比单点叉乘更稳定,但转向方向的核心判定逻辑依然基于叉乘符号。

内容的提问来源于stack exchange,提问作者Yassin
相关产品推荐
相关产品推荐

