轨迹匹配算法学习总结

为什么要做轨迹匹配

我们知道地图是通过GPS来实现定位的,但是在现实中GPS获取的位置信息受多方因素影响,尤其是在天气不好或者高大建筑密集的情况下,有可能会定位在水里,建筑里,这些会成为计算轨迹的重要干扰因素。
比如,下图的蓝点就是GPS记录的车行驶一段路程的点集。人眼很容易看到这辆车行驶的轨迹是红色和绿色组成的路线,但是如何让计算机来识别并精确的匹配到道路上,是急需解决的问题。
轨迹匹配算法学习总结

几何方法

我们第一时间想到的方法就是计算点到最近路段的垂足,这种方法简单粗暴,且可以解决问题。但是现实的道路比上个例子复杂得多,有可能存在几条道路交错的情况,当点定位在离两条甚至三条路段都很近的情况下,这种方法的正确率就是低很多了。
轨迹匹配算法学习总结

隐式马尔科夫模型(HMM)

现在大部分的轨迹匹配算法都是基于隐式马尔科夫模型来实现的,这个模型主要是在概率的基础上实现的。

简单的例子

假设我有三种类型的骰子:
1、正四面体的骰子,我们将其简称为 D4,可以随机投出 1,2,3,4 中的任意一个数字。
2、正六面体的骰子,也就是立方体的骰子,我们将其简称为 D6,可以随机投出 1,2,3,4,5,6 中的任意一个数字。
3、正八面体的骰子,我们将其简称为 D8,可以随机投出 1,2,3,4,5,6,7,8 中的任意一个数字。
由于骰子是均匀的,所以我们认为每次投出来每个数字的概率都是相等的。也就是说,D4 投出 1 到 4 中的每个数字的概率都是 1/4。D6 投出 1 到 6 中的每个数字的概率都是 1/6。D8 投出 1 到 8 中的每个数字的概率都是 1/8。

好了,现在,我们要以一定的规则来投掷出一串数字,具体的规则如下:
刚开始的时候,我随机选择任意一个骰子来投掷出一个数字,也就是说,每个骰子被选中的概率都是 1/3。
以后的每一次投掷,我都以 1/2 的概率选择沿用上一次选择的骰子,或者分别以 1/4 的概率选择另外两个骰子。
那么,对应于这么一个场景,我们可以提出下面这三类问题:
1、已知我每次选用的骰子是哪一个,比如这个序列是 “D4-D6-D6-D8”,并且已知我投掷出的数字序列,比如是 “2-6-4-6”,那么我想知道,我投掷出这个序列的概率是多少?
2、已知我最终记录下来的数字序列,比如是 “2-6-4-6”,我想知道,投出这个序列的最大可能的骰子序列是哪一组?
3、问题可以更夸张一些,我不知道骰子是不是均匀的(意思就是,我不知道骰子投出每个数字的概率是多少),也不知道我每次投掷都是以什么样的概率去选择骰子的,但是我有很多组记录下来的数字序列,我想根据这些数字序列来反推出我所有不知道的概率值是多少?

问题定义

上面提到的骰子序列,我们称之为隐藏序列
上面提到的数字序列,我们称之为观测序列
上面提到的骰子的投掷概率,我们称之为观测概率
上面提到的骰子的选择概率,我们称之为转换概率
观测概率和转换概率我们将其称之为模型参数。

那么,上面提到的三类问题,可以抽象为:
已知隐藏序列和模型参数,求观测序列的概率。
已知观测序列和模型参数,求最大可能的隐藏序列。
已知大量的观测序列,确定模型参数。

Viterbi算法求解最大可能序列

我们知道,要应用维特比算法的话,除了要知道观测序列之外,还需要知道模型参数。但是和骰子问题不一样,我们并不知道准确的模型参数。于是,我们遇到的一个问题就是,观测概率和转换概率应该怎么计算?

如果需要知道准确的模型参数的话,可以参考上面提到过的第三类问题的解决方法,用一些神经网络或者机器学习的方法,就可以训练出比较靠谱的参数。但是对于我们的地图匹配目标而言,完全没有必要弄的这么麻烦。

实际上,仔细看下我们的维特比算法的实现,我们发现,整个计算过程中,这个概率值我们只关心它们之间的大小比较,并不关心数值范围!

那么问题就可以简单处理了,我们在设定观测概率和转换概率的时候,只需要指定一个符合趋势的“代价值”就好了。

所以,我们都不需要用浮点数来计算了。直接用一个正整数来比较代价,而在运用维特比算法的时候,我们将代价小的点作为概率高的点来处理就可以了。
至于代价如何设定,可以考虑这几方面的因素。
GPS 点到路段的距离;
GPS 点前进方向和路段的方向差;
GPS 点的速度与道路限速值的差异(低于限速值代价为 0,超过限速值就累加插值)。

现在,观测序列有了,模型参数也有了,是时候该用维特比算法来计算最有可能的行车路线,也就是隐藏序列了。具体步骤如下:
1、初始化第一个观测点的所有可能状态(对应于骰子问题,状态就指选用哪个骰子)概率。
2、从前往后遍历每一个状态,对于每一个状态,用以下方法计算当前观测点的所有可能状态的概率:
a、遍历当前观测可能对应的所有状态;
b、对每个状态,遍历所有上一状态,通过公式 P(当前状态) = P(上一状态) * P(上一状态转移到当前状态) * P(当前状态观测) 来计算当前状态的概率。
3、当所有观测点都遍历完之后,查找概率值最大的那个状态,然后查找这个概率值对应的上一状态,就这样一路往回找,所找到的序列(倒序的)就是最大概率的隐藏序列。

具体实现——map-matching

目前有很多地图匹配的开源代码,我参考的是GraphHopper的map-matching算法。