目前,随着国内外相关从业人员的研究,研究者们提出了众多室内定位技术的理论与方法。在此仅讨论基于 Wi-Fi 的室内定位技术。
近年来Wi-Fi技术飞速发展,城市中的公共场所如大型超市商场、学校、企业等都已经广泛部署Wi-Fi。Wi-Fi室内定位技术已经出现了很多具有代表性的研究成果,如RADAR系统、Nibble系统、Weyes系统等室内定位系统。2012年,Google把Wi-Fi室内定位和室内地图引入了谷歌地图中,一年内已经覆盖了北美和欧洲一万大家大型场馆。我国的百度、高德、四维、智慧图等公司也在研发Wi-Fi室内定位产品。由于Wi-Fi网络的普及,Wi-Fi定位是目前比较流行的定位技术,定位精度能达到米级,定位成本低,定位信号收发范围大,适用性强,可以被普及推广。
采用基于 Wi-Fi 的室内定位方案,首先获取指定室内的结构图,然后在该结构图上进行打指纹 AP 点。什么是指纹 AP 点?
【位置指纹法】:“位置指纹”把实际环境中的位置和某种“指纹”联系起来,一个位置对应一个独特的指纹。这个指纹可以是单维或多维的,比如待定位设备在接收或是发送信息,那么指纹可以是这个信息或信号的一个特征或多个特征(最常见的是信号强度)。如果待定位设备是在发送信号,有一些固定的接收设备感知贷定位设备的信号或信息然后根据这些检测到的特征来估计自身的位置,这种方式可称远程定位或者网络定位。如果是待定位设备接受一些固定的发送设备的信号或信息,然后根据这些检测到的特征来估计自身的位置,这种被称为自身定位。待定位移动设备也许会把它检测到的特征传达给网络中的服务器节点,服务器可以利用它所获得的所有信息来估计移动设备的位置,这种方式也成为混合定位,在所有的这些方式中,都需要把感知到的信号特征拿去匹配一个数据库中的信号特征,这个过程可以看作一个模式识别的问题。——选自《室内定位系列(一)——Wi-Fi位置指纹(译)》
更多关于 Wi-Fi 位置指纹的内容可参考 室内定位系列(一)——Wi-Fi位置指纹(译)
微信小程序有专门获取 Wi-Fi 列表的 API,因此可以将 RSS(接收信号强度)来作为指纹 AP 的特征。RSS 不受信号带宽的影响,没必要高的带宽,是一个很受欢迎的信号特征。
有了指纹 AP 点以及位置指纹法的概念,室内定位问题实际上就转换为一个多分类问题。服务方事先在室内结构图上确定 N 个指纹 AP 点,然后分别在这 N 个指纹 AP 点上采集 Wi-FI 的 RSS 数据。接着运用机器学习的方式训练,获得一个用以预测分类的模型。最终,模型可以通过接受客户发送当前位置的 RSS 数据,来判断用户当前所属的指纹 AP 点。
然后,我们在需要的位置上打点,也就是标注指纹 AP 点。标注完指纹 AP 点之后,我们依次到刚才标注的指纹 AP 点上进行 Wi-Fi 信号强度采集。微信小程序有相应的获取 Wi-Fi 列表的接口,并且已经对 RSS 进行处理,因此我们无需再对 RSS 进行调整即可直接使用。
我们依次将各指纹 AP 点采集到的 Wi-Fi 信息存储到服务器的数据库中。
Wi-Fi 信息存储在数据库中的形式与我们真正训练所需的形式有一定的差别。因此,我们需要通过转换操作,将存储在数据库中的数据转换成训练所需的数据形式。
【实现代码】:
def drop_signal_data(dataset):
ap_dict = {}
for data in dataset:
bssid = data[0]
if bssid not in ap_dict:
ap_dict[bssid] = []
ap_dict[bssid].append((data[1], data[2]))
ap_count_list = []
for ap in ap_dict:
ap_count_list.append(len(ap_dict[ap]))
data_count = np.max(ap_count_list)
ap_result = {}
for ap in ap_dict:
if len(ap_dict[ap]) == data_count:
ap_result[ap] = ap_dict[ap]
return ap_result, data_count
def record2trainSet(ap_data, data_count):
matrix = np.zeros((data_count, len(ap_data) + 1))
ind, feature_dict = 0, {}
for ap in ap_data:
feature_dict[ind] = ap
if ind == 0:
matrix[:, -1] = [data[1] for data in ap_data[ap]]
matrix[:, ind] = [data[0] for data in ap_data[ap]]
ind += 1
return matrix, feature_dict
转换后的矩阵每一列为一个特定的 WiFi(后文以 BSSID 指代),现在我们的工作就是进行特征选择,挑选合适的特征。
需要注意的是,不同指纹 AP 点采集到的 BSSID 可能有所不同,因此我们取当前室内所有指纹 AP 点的 BSSID 交集,以确保在后续操作中每个指纹 AP 点的特征集都是相同的。
那么如何进行特征选择呢?最简单的做法就是判断每一个 BSSID 的方差,首先保证每一个 BSSID 在同一个指纹 AP 点的方差足够小,为什么?若 BSSID 在当前指纹 AP 点采集到的所有信号强度都是恒定不变,则表明该指纹 AP 点的 BSSID 很稳定,不会随着时间的变化而产生很大的抖动。接着,每一个 BSSID 在不同指纹 AP 点的方差足够大,这又是为什么?若 BSSID 在不同指纹 AP 点采集到的信号强度变化很大,则该 BSSID 对这些指纹 AP 点的区分程度也就越明显。例如在 A 点的信号强度为 30,B 点的信号强度为 60,此时用户接收到该 BSSID 的信号强度为 56,则可以很轻易地判断用户处在 B 点。基于上述考量,我们可以采用方差选择法来进行特征选择。
当然,除了方差选择法,我们还可以通过卡方检验、互信息、相关系数法等过滤式特征选择方法。若对训练的开销和时间的要求不高,则可以采用递归特征消除以及基于树模型的特征选择。我们可以使用 sklearn 的相应代码来进行特征选择,具体的知识可以参考:
确定了特征选择方案后,需要考虑特征选择的个数。考虑到不同地点的 BSSID 数量不同,因此我们需要视情况来选择特征数量。
最后将选择的特征存储到数据库指定表即可。
对于室内而言,Wi-Fi 分散在四周,例如东西两个 Wi-Fi,当用户从东走向西,则一个信号强度增加,另一个信号强度减弱,因此可以初步判断数据集应该是线性不可分,那么我们在选择模型时可以不用考虑线性分类模型。
对于这一类侧重空间向量计算的分类问题,KNN 的效果一般会比较好,因此我们可以将 KNN 纳入考虑,同时再选择 SVM。经过测试数据的训练后,两者的效果相差不大,因此都可作为最终的模型。至于选择哪一个作为模型,则可从数据集大小的角度来进行度量。
KNN 没有显式的训练过程,即使可以通过 K-D 树的方式减少计算量,但 K-D 树本身建立所需的时间以及存储开销都要比 SVM 大,因为 SVM 仅需要部分支持向量就可以进行预测工作。基于上述考虑,我的想法是当数据集过小时,直接使用 KNN 即可。若数据集较大时,则使用 SVM。
【异常情况】:作为指纹 AP 的 WiFi 出现故障或位置发生移动,此时会出现获取不到相应 WiFi 的信号强度,或信号强度与学习时的信号强度差距较大。
针对上述情况我们可以多设置几组指纹 AP,并分别为这几组指纹 AP 训练模型。当常用的指纹 AP 出现故障或位置发生移动时,系统能够自动调用其他指纹 AP 组。
基于上述考虑,最好能够可以多确定几组指纹 AP 组,并训练几组模型,以备不时之需。
完成模型训练后,我们可通过 sklearn.externals.joblib 包中的 dump 方法将模型缓存到服务器上,同时将模型在服务器上的位置存储到数据库中。
模型训练完成后,主要工作基本上已经完成。此时,只需要将用户当前位置的 Wi-Fi 信息经过处理后传递给服务器,服务器调用相应的模型进行预测,最终将预测结果返回即可。
软件测试以 505 寝室的室内定位作为案例。
首先,在 505 寝室内打 4 个指纹 AP 点,分别为四人的位置。接着,我们依次到 AP 点 1 号和 2 号处采集数据。
采集完数据后,点击训练按钮,等待一段时间直到训练完成的弹窗出现,同时下方信息栏的训练那一行也会出现“训练成功!”文字,标志着模型训练操作执行成功。然后,我们可以点击预测按钮来检验模型的训练成果。此时,我仍然站在 2 号位置处(因为刚刚在这采集数据),预测的结果如下图所示:
接着,我们回退到室内页面,在该页面内每隔5s 会自动进行定位,当然我们可以自行设置定位时间间隔,不过在这次测试过程中我没有特意去修改时间间隔,使用的是默认设置的 5s。
在 2 号位置等待 5s 后,定位图标从左上角移动到 2 号位置处(下方左图)。然后从 2 号位置走向 1 号位置,等待 5s 后,定位图标从 2 号位置移动到 1 号位置(下方右图)。
GitHub 项目地址:https://github.com/clvsit/Wi-Fi-based-indoor-positioning