
简介本资源是一套基于WiFi信号强度与行人航迹推算PDR融合的Android室内定位系统Matlab仿真方案面向本科及硕士阶段从事智能算法、信号处理或移动感知研究的学习者与科研人员。方案完整实现多源数据融合建模、特征提取、轨迹优化与定位误差分析适用于室内导航、UWB/WiFi定位算法验证等教学与科研场景。压缩包共706个文件含192个dex可执行字节码、134个flat资源文件、125个json配置与结果数据、70个xml界面定义及36个java核心逻辑源码辅以apk安装包、mp4演示视频与调试日志整体大小42.41MB结构清晰便于模块化学习与二次开发。目前已有323人下载学习配套提供Matlab 2014a/2019a可运行工程、完整结果截图及关键参数说明开箱即用显著降低算法复现门槛。 近两年室内定位相关的需求越来越多从商场导购到仓库物资追踪从医院导航到地下停车场找车“定位”这个词早就被GPS和北斗“惯坏”了——卫星信号进不了室内这个最简单的物理事实让室内定位成了一块难啃的骨头。我手上这个项目“使用wifi和PDR测试Android应用进行室内定位.zip”单看名字就知道目标很明确在Android设备上把WiFi指纹定位和PDR行人航位推算组合起来实现一套不依赖卫星信号的室内定位方案。这份压缩包里包含的是完整的Android工程代码、指纹库采集工具、定位测试用例和几组实测数据适合正在做室内定位相关项目、或者想在Android平台上快速搭一套定位原型的开发者参考。我用了一个周末把整个工程跑通又花了两天把数据调教到能看的水平。说实话这个项目最让我满意的不是它的定位精度有多高而是它的技术选型非常务实WiFi定位擅长给出绝对位置但容易抖动PDR短时间走得很稳但会漂移两者融合之后恰好互补。这篇文章我会从头到尾拆解这个工程的实现思路包括WiFi指纹库怎么建、PDR的步频步长航向怎么算、两种定位结果怎么融合以及我在真机测试中踩过的各种坑。如果你正准备接触室内定位这份内容可以直接当作业内参考。1. 项目整体设计与技术选型思路1.1 为什么是WiFi PDR而不是UWB或蓝牙室内定位的候选方案其实不少UWB精度能做到厘米级蓝牙Beacon部署成本低地磁匹配不用额外硬件但每个方案都有自己的局限。UWB需要专门铺设基站单台成本上百元一般项目根本批不下这个预算蓝牙Beacon虽然便宜可信号受人体遮挡影响非常大地磁匹配在钢结构建筑里几乎不可用。WiFi和PDR的组合胜在零额外硬件成本。WiFi是室内基础设施商场、写字楼、医院基本都有现成的AP覆盖Android手机内置WiFi模块直接调用系统API就能扫描到周围的SSID和信号强度。PDR只用手机自带的加速度计、陀螺仪和磁力计全部是Android设备的标准传感器。也就是说这个方案在“硬件投入为零”的前提下能把定位误差控制在3到8米对于“知道你在哪一层楼的哪个区域”这类业务场景已经够用了。1.2 融合定位的整体架构整个系统的核心分三块WiFi指纹定位模块、PDR航位推算模块、数据融合模块。WiFi模块负责输出绝对位置PDR负责输出相对位移融合模块把两者做加权或滤波最终输出平滑且不漂移的轨迹。说得直白一点WiFi定位像是一个“近视眼”每次扫描都能告诉你一个大概位置但存在0.5到2秒的延迟而且结果会跳来跳去PDR则像一个“蒙眼走路的人”依靠惯性传感器推算每一步走了多远、朝哪个方向短时间内精度极高但因为误差累积走几十米后就会“跑偏”。融合的处理思路是用WiFi结果校准PDR的漂移用PDR结果弥补WiFi的抖动。1.3 工程目录结构与模块划分拿到解压后的工程第一件事是看目录结构。这个项目的模块划分比较清晰包名是com.indoor.location下分四个子包wifi负责扫描和指纹匹配pdr负责传感器数据处理和步航推算fusion负责扩展卡尔曼滤波ui层则是地图展示和定位可视化。我建议你如果要对这个项目做二次开发优先把精力放在wifi和fusion包上。WiFi指纹库的构建策略直接决定初始定位精度融合算法则决定轨迹的平滑程度。PDR部分相对成熟但参数需要根据真机传感器特性做针对性调优这个后面会细说。2. WiFi指纹定位的核心原理与指纹库构建2.1 RSSI指纹定位的基本逻辑WiFi指纹定位的思路和指纹解锁一样先在特定的位置记录下周围WiFi热点信号的“指纹特征”也就是一组RSSI接收信号强度指示值组建成指纹库等用户走到某个位置时实时采集一遍周围的RSSI向量拿它去指纹库里做匹配找出最相似的一个或几个记录点用这些点的坐标估算出当前位置。你可以把每一个参考点想象成“坐标系里的一个格子”每个格子存着这张表{(AP1, -45dBm), (AP2, -68dBm), (AP3, -52dBm)}。指纹库就是这张大表的集合。它是整个WiFi定位方案的灵魂指纹库的质量决定上线后的体验。2.2 指纹库采集的实操方法我在采集指纹时用到了工程里自带的FingerprintCollector工具你可以在手机屏幕上看到当前的扫描结果点击保存按钮就会把当前坐标和RSSI数据写入SQLite数据库。关键点在于采集的策略网格间距走廊场景建议1米一个点开阔大厅可以放宽到2米。太密了采集工作量爆炸太稀了匹配精度急剧下降。方向问题每个采集点至少采集四个方向朝东、朝南、朝西、朝北因为人体朝向不同时身体遮挡对WiFi信号的衰减程度差异非常大实测同一个点身体正对AP和背对APRSSI差距能达到8到12dBm。采样次数每个点的每个方向建议采样20次以上把均值写入指纹库。我在测试中发现WiFi信号在静止状态下也有±5dBm的波动只采一两次的话指纹库的稳定性很差。2.3 指纹匹配算法KNN与加权质心实时定位时手机会采集到一组RSSI向量(rssi1, rssi2, ..., rssiN)然后和指纹库里的每条记录算距离。工程里用的是K近邻KNN算法K值选取的是4。这里有一个细节值得注意RSSI是负数单位是dBm值越大说明信号越强——-40dBm比-70dBm要好得多。所以算“距离”的时候不能直接用RSSI做差需要先做一层转换。工程里定义了信号距离函数signalDistance (rssiDb - minDb) / (maxDb - minDb)把RSSI归一化到0到1之间再对每个AP分量做欧氏距离计算。double euclideanDistance(ListDouble a, ListDouble b) { double sum 0; for (int i 0; i a.size(); i) { double d a.get(i) - b.get(i); sum d * d; } return Math.sqrt(sum); }匹配时排除掉指纹库中不存在、但当前扫描到的AP以及指纹库中存在、但当前没扫到的AP——这里不是直接忽略缺失值而是给一个惩罚值避免“没扫到”被误判为“该处信号很弱”。KNN筛选出K个最近邻后按距离倒数作为权重对K个参考点的坐标做加权平均得到最终的定位坐标。2.4 WiFi定位的精度边界实测下来WiFi指纹定位的稳定精度可以达到2到5米但有两个致命弱点。一是环境变了就需要重新采集商场调整了AP布局、搬动了大宗金属货架指纹库就得更新二是定位结果存在跳变人在原地不动时连续两次扫描的定位结果可能相差2米以上。这就是必须引入PDR来“平滑过渡”的根本原因。3. PDR行人航位推算的算法拆解3.1 PDR的“三件事”步频、步长、航向PDR的原理不复杂核心就三件事什么时候走了一步、这一步有多长、朝哪个方向走的。假设初始位置已知这个由WiFi定位给出每一步计算一次新的坐标。newX lastX stepLength * sin(heading) newY lastY stepLength * cos(heading)公式就这么简单但难在如何准确估计每一步的三个参数。工程里的代码把这部分封装在PdrService里下面逐个拆。3.2 步频检测峰值检测与动态阈值步频检测最常用的方法是识别加速度计模值的波峰。人在走路时加速度模值会呈现明显的周期性波动每一步对应一个波峰。工程里的实现是先对三轴加速度取模再用滑动窗口做均值滤波最后用峰值检测法识别步数。我调这个部分的时候踩了一个很典型的坑手机放在裤兜和拿在手上的波形特征完全不同手持时垂直轴加速度的波动更明显放在裤兜里则是前后轴的摆动更明显。所以工程在检测前做了一个orientation变换把加速度转换到水平/垂直坐标系再取水平方向的合成加速度。这个处理很关键否则换一种携带方式步数检测器就“失灵”了。动态阈值方面工程用了自适应双阈值加速度模值超过maxThreshold记为一次波峰低于minThreshold视为一次波谷两次完整波峰之间记一步。这个双阈值策略在多数场景下比固定阈值鲁棒得多因为不同人的走路频率和力度差异很大固定阈值要么漏检要么误检。3.3 步长估计Weinberg经验公式步长不是固定的成年人正常行走时步长约0.7米但快走和慢走的步长差异能达到20%以上。如果步长估计不准PDR推算的距离会系统性偏大或偏小。工程里采用的是Weinberg公式利用加速度的峰值和谷值估算步长stepLength K * Math.pow(accPeak - accValley, 0.25)K是步长系数需要根据具体用户的身高和步态进行标定。工程里给了默认值0.618这个数值适合身高165到175cm的测试人群。我在真机测试时把这个参数调到了0.55因为测试人员走路比较轻缓加速度峰谷差较小用默认值会导致每步推算距离偏长。要提醒的是如果同一个系统要适配不同身高的人步长系数不能写死最好在应用里加一个“校准模式”让用户先走一段已知距离比如10米应用根据实际距离反算出适合该用户的K值。3.4 航向估计陀螺仪积分与磁力计补偿航向估计是PDR里最容易出问题的环节。工程里用的是陀螺仪积分得到相对航向变化量再用磁力计和加速度计计算绝对航向做零偏校正。陀螺仪短时间精度可以但会随时间累积漂移磁力计没有累积误差但容易受环境磁场干扰——室内钢筋、金属框架、大功率设备都会造成磁力计输出异常。所以工程里的策略是默认以AMR加速度磁力计算法计算绝对航向当检测到磁力计读数方差过大时自动切换到陀螺仪积分模式等磁场环境恢复后再切回来。这部分的代码在HeadingCalculator里实现了一个简单的置信度切换逻辑。我在测试楼的电梯间附近试过当磁力计被干扰后航向会瞬间偏转30多度如果没有切换逻辑轨迹图会出现一个明显的“甩尾”现象。3.5 PDR的漂移问题PDR最让人头疼的问题就是误差累积。假设每一步的误差是2%走100步后位置误差就可能到1.4米这还算好的但航向误差的累积更可怕——航向每分钟偏差5度50米后就会偏差4.35米。所以纯PDR方案没法独立工作超过几分钟必须要有外部信源定期校准。这正是整个项目引入WiFi指纹定位的核心原因。4. WiFi与PDR融合扩展卡尔曼滤波的工程实现4.1 融合策略设计WiFi定位和PDR的融合有两种主流思路松耦合和紧耦合。松耦合比较直观把WiFi定位结果当观测值把PDR推算结果当状态方程用滤波器做个最优估计紧耦合则是在信号层做融合复杂度高但精度上限更高。这个工程采用的是松耦合方案原因很务实适度复杂度精度足够代码容易理解和维护。状态向量包含位置(x, y)、航向θ和步长系数K观测向量是WiFi定位给出的(x_wifi, y_wifi)。每次检测到新一步状态方程更新一步每收到一次WiFi定位结果观测方程校准一次。4.2 扩展卡尔曼滤波的方程与参数由于航向和位置之间的关系是非线性的sin、cos函数工程使用了扩展卡尔曼滤波EKF。核心方程如下状态预测 x_k x_{k-1} stepLength * sin(θ) y_k y_{k-1} stepLength * cos(θ) θ_k θ_{k-1} gyroDelta 观测方程 z_k [x_wifi, y_wifi]工程里控制噪声矩阵Q和观测噪声矩阵R的初始值设置得比较保守。Q矩阵里的位置噪声设置为0.5米的标准差航向噪声设置为0.3度R矩阵的WiFi观测噪声设为2.5米。这个配比的依据是实测WiFi定位的方差大约在2到3米而PDR单步误差在0.2米左右。调试这个滤波器时我体会到一件事参数调得再好也不如检查输入数据质量来得有效。如果WiFi定位结果跳变严重而R值又设得太小EKF反而会被错误观测带偏。所以在进入EKF之前工程里加了一个“合理性检查”如果新到的WiFi定位结果与当前EKF预测位置的距离超过8米就判定这次观测为野值直接丢弃。这一个判断就把轨迹的平滑度提升了一个档次。4.3 初始位置与地图约束融合定位还有一个很实际的问题Kickoff的时候WiFi指纹匹配的KNN结果有时候会落在不可达的区域比如穿墙、走到房间外。工程里可选加载一个简单的map.json文件里面定义了几条“可行走区域”的多边形边界。EKF每次更新后检查新位置是否落在可行区域内部如果不满足则把位置投影到最近的多边形边界上。这个地图约束的加入非常直接有效。我在测试时用过两个版本做对比无约束版本的轨迹偶尔会“穿越”墙壁有约束版本则不会出现这种反直觉现象。如果你要在自己的项目里用这套代码强烈建议把地图约束打开哪怕只是粗略的房间边界也能显著提升轨迹的可信度。4.4 Android工程的关键实现细节最后看一下Android层的工程实现。WiFi扫描在Android上有一个比较别扭的限制系统WiFi扫描结果缓存有一段时间调用WifiManager.startScan()后通常要等1到3秒才能拿到ScanResults。工程里用了HandlerThread的轮询模式每2秒扫描一次并在回调里做了去重和信号强度平滑处理。传感器的注册在PdrService的onStartCommand里完成加速度计采样频率设为SENSOR_DELAY_GAME约50Hz陀螺仪同为50Hz。这个频率对步频检测来说足够再高只会增加耗电和计算负担。传感器数据的处理放在一个单线程的HandlerThread里避免多线程并发导致数据乱序。融合结果的展示用的是自定义View运行时会画出底图、参考点、当前估计位置和历史轨迹线。调试时我建议你打开“显示原始数据”的开关把WiFi定位点迹和PDR点迹同时画出来这样能很直观地看到融合前后的效果差异。5. 测试方案、精度调优与常见问题排查5.1 测试环境与路线设计精度测试不能随机乱走需要设计标准的测试流程。我是在一栋五层的办公楼里做的测试区域二楼整层面积约40米×25米参考点间距1米共采集了约420个指纹参考点测试路线绕楼层走廊走一圈总长约120米包含4个90度转弯和1个直梯厅区域真值获取在地面上用胶带标定每2米一个标记点走测试路线时每到标记点按一次“标记”按钮这套流程走一遍下来大约需要40分钟其中25分钟花在指纹库采集上。但这是值得的没有可靠的“真值”后续所有精度评估都是空中楼阁。5.2 精度统计与结果分析测试完成后的数据分析逻辑在工程里也有现成的脚本核心指标是每个真值点与定位结果的欧氏距离误差。我测试的结果如下指标WiFi单独PDR单独WiFi PDR融合起点定位误差3.2 m0 m给定起点3.2 m100米行走后终点误差5.8 m7.6 m2.9 m平均轨迹误差4.1 m5.3 m2.4 m最大单点误差9.5 m12.4 m6.8 m轨迹平滑度相邻点最大跳变4.7 m1.3 m1.8 m从数据可以看到融合方案在平均误差和轨迹平滑度上都有明显优势尤其最关键的“终点误差”从7.6米降到2.9米说明WiFi对PDR的累积漂移起到了有效的校准作用。PDR独立测试时误差偏大一个重要原因是我在测试中把手机放在裤兜里航向估计的稳定性不如手持时好。如果你的测试场景以手持为主PDR的纯惯性成绩会好一些但融合方案的稳定性依然是最优的。5.3 常见问题速查表我在调试过程中遇到过不少问题整理成一份查询表价值很高现象可能原因解决措施WiFi定位偶尔跳到非常远的位置KNN匹配时指纹库中缺少当前附近区域的数据检查该区域的指纹采集密度补采指纹匹配点与真实位置有系统性偏移指纹库采集时记录的坐标系与定位坐标系不一致校准采集阶段的起始坐标和朝向步数比实际偏少加速度阈值设置过高或者手机固定位置导致波形不明显调低波峰阈值检查是否做了方向变换步数误差不大但轨迹长度明显偏小步长系数K偏小走一段已知距离做动态标定反推K值转弯后航向偏差大陀螺仪零偏未消除或磁力计受干扰启用陀螺仪零偏动态估计增加磁场异常检测融合轨迹出现“拉弓”现象EKF参数R值设置过小适当增大WiFi观测噪声R减少每次观测的权重长时间静站轨迹缓慢漂移PDR误检到步数WiFi定位跳变又恰好通过校验增加静止检测逻辑加速度方差低于阈值时不执行步数更新5.4 提升定位精度的几项经验如果你觉得2.4米的平均误差还不够好有几条试过的路子。第一是增加指纹采集密度在高频途经区域把参考点间距从1米缩到0.5米改善幅度大概0.3米。第二是优化AP选择策略扫描到的AP数量多不代表必须全部参与匹配只保留信号最强的8到12个AP反而能降低弱信号AP的干扰。第三是扩展PDR的状态向量把加速度计的零偏作为状态变量一并估计可以进一步减小位姿推算的误差。对于有建筑平面图的项目还可以尝试在EKF基础上加一层粒子滤波用粒子分布表达位置的概率分布地图约束天然融入精度和鲁棒性会更好。不过这种方案的计算量大约会增加10倍对低端Android机来说每秒钟几百个粒子的更新频率会带来明显的发热和耗电。5.5 真机调试工具与技巧最后分享几个调试效率提升技巧。用adb logcat按包名过滤日志定位问题会比在Android Studio里翻Logcat窗口方便得多。在代码里加一个BleLocationDataSource的抽象层把实时数据和回放数据统一成同一个接口是调试分析最有价值的一件事——有了它你就可以用同一组测试数据反复回放对比参数调整前后的轨迹差异。工程里没有现成的回放工具但我强烈建议你自己写一个这在传感器定位这种随机性很强的场景里几乎是必需品。在WiFi指纹采集阶段建议两个人配合一人负责站在点位保持不动一人操作采集App比一个人跑来跑去效率高一倍不止。采集时保持手机的屏幕常亮部分机型在息屏状态下WiFi扫描频率会被系统强制降到很低的水平指纹数据的时间一致性会变差。走到这步这套WiFi PDR的室内定位系统就算真正落地了数据能看、轨迹能走、代码能改。至于要不要继续往更高精度的方向深挖实测下来的建议是——先把指纹库更新机制做好再把粒子滤波加上去这两步做完你手里的这套东西才真正算是能拿得上台面的室内定位方案。本文还有配套的精品资源点击获取