WiFi指纹+PDR融合的Android室内定位工程实现与调优 简介本资源是一个基于WiFi信号强度与行人航迹推算PDR融合算法的Android室内定位系统实现面向本科及硕士阶段的移动计算、智能感知与位置服务相关课程设计、毕业设计及科研入门学习者。项目完整包含可直接安装运行的APK、Android Studio工程源码含34个Java核心逻辑文件、UI资源26个PNG、构建脚本9个Gradle相关文件及配置文件52个XML共150个文件总大小2.49MB结构清晰、模块分离明确便于理解多源定位数据采集、特征提取、融合建模与可视化全流程。已有229人下载学习配套提供Matlab 2014a/2019a环境下的仿真验证代码与运行结果截图支持算法对比与参数调优同时涵盖Android端传感器数据实时处理、WiFi扫描调度、步态检测与航向积分等关键技术实现细节是开展室内外定位交叉研究的实用参考范例。 做室内定位的都知道一个场景GPS在室外是王者一进室内就基本成了摆设。卫星信号被楼板、玻璃幕墙和钢筋混凝土一挡手机上的定位点就开始乱飘指北针也像个喝醉了的指针。所以室内定位的工程方案和室外完全是两套逻辑。我最近在梳理一个WiFiPDR组合的室内定位Android程序这是一个打包好的完整工程跑起来之后能够在商场、办公楼、地下车库这类环境输出一条连续平滑的行人轨迹。它的核心思路不复杂WiFi指纹定位负责给绝对位置PDR行人航迹推算负责补上WiFi定位的跳变和延迟。如果你在做人导航App、人员定位系统、仓储巡检或者只是学生对室内定位感兴趣这个项目都值得拆开看看。整套方案落地之后精度大概能做到2~5米的水平比单独用WiFi定位平滑很多也比单独用PDR漂移小很多。关键它不是论文里的演示代码而是能在Android真机上跑起来的工程所用的硬件就是普通手机内置的WiFi模块和IMU传感器不依赖额外的Beacon基站或专用设备。这一篇我会把整个工程的架构、核心代码、参数标定和踩坑实录拆开来讲内容偏实操看完你也能搭一套属于自己的室内定位Demo。1. 为什么偏偏是WiFiPDR组合1.1 室内定位的传感器困境室外定位大家习惯用GPS因为卫星信号在开阔环境里能直接收到定位精度可以达到几米到十几米。但室内不行建筑结构对卫星信号的衰减非常严重手机在房间里往往搜不到足够的卫星或者收到的是经过多次反射的弱信号算出来的位置误差动辄几十米。所以室内定位必须换一条路。目前主流的室内定位方案其实不少UWB精度能做到厘米级但需要布设专门的基站一个房间至少两三个成本不低蓝牙Beacon方案部署相对简单但需要提前铺设信标维护也是麻烦事RFID定位只能覆盖特定区域适合资产追踪而不是人连续导航。相比之下WiFi定位有个天然优势——建筑里本来就有一堆无线路由器不需要额外加设备手机能扫到这些信号就能参与定位。PDR方案更直接它不依赖任何外部信号只靠手机里的加速度计、陀螺仪和磁力计推算人的位移属于纯自主定位。这两种方式放在一起刚好覆盖了室内定位里最关键的两个能力绝对位置校准和连续位移估计。WiFi在没有GPS的室内提供了“我大概在哪一片区域”的绝对参考PDR提供了“我从上一步走到了哪一步”的相对轨迹。两者单独用都有硬伤合在一起却能形成不错的闭环。1.2 WiFi单独用最大的问题不是精度而是“跳”WiFi定位在工程里最常用的是指纹定位法。原理其实很简单先在室内网格点上采集一遍WiFi信号强度RSSI把每个位置的“信号指纹”存到数据库里在线定位时手机扫到当前一組RSSI去指纹库里找最相似的位置算出来就是结果。听起来很顺但实际用起来有一个特别头疼的问题RSSI这玩意儿太不稳定了。人体本身会挡信号你站在同一个点身体朝向不同扫描到的RSSI可能差10dBm以上。开门关门、有人路过、环境里的微波炉电磁干扰都会让WiFi信号上下起伏。结果是WiFi定位出来的坐标会前后跳动静止站着不动定位点也可能在两三米范围内来回晃。而且Android系统的WiFi扫描频率有限制一两秒才能扫一轮这就导致定位结果不仅有噪声还有延迟。我把这个工程单独跑WiFi模式的时候在走廊里走一趟轨迹明显是“跳格子”的感觉——位置从左边闪到右边再跳到前面完全不像一条连续的走路路线。这就是WiFi定位的典型特征绝对坐标大致靠谱但连续性和平滑性很差。1.3 PDR单独用短时很准长走就飘PDRPedestrian Dead Reckoning行人航迹推算的核心思路是已知起点通过检测步数、推算步长、获取航向一步一步累加出人的位置。这个方法在短时间内的表现非常好尤其是二三十米以内的短距离定位轨迹非常平滑自然能真实反映人的行走路径。但PDR有一个致命缺陷——累积误差。每一步的步长估计未必准航向传感器也会受环境磁干扰和手机姿态变化影响产生漂移。假设每一步的误差只有1%走100步之后位置误差就可能累积到好几米。更麻烦的是PDR完全依赖起点起点如果给错后面所有位置就全偏了。我在这个工程里测试过单独PDR模式刚开始100米轨迹还算有模有样走了三四百米之后轨迹已经明显偏向墙里甚至“穿墙而过”了。所以PDR像是一个“自信但固执”的导航员短时间它记得住自己每一步但时间一长就开始迷茫。这时候就需要WiFi定位时不时拉它一把把累积的漂移修正回来。1.4 组合定位的核心逻辑WiFi定位和PDR正好互补WiFi定位绝对位置可信但噪声大、不平滑PDR平滑连续但会累积漂移。组合起来的思路就是“用PDR看趋势用WiFi做校正”。每一帧定位结果里WiFi给一个绝对坐标PDR给一个相对位移通过滤波算法把两者融合输出的轨迹既保持PDR的连续平滑又有WiFi的绝对参考不会越走越偏。这个工程采用的是松耦合融合架构也就是WiFi和PDR先各自计算位置结果再通过融合模块合并。相比紧耦合架构直接把WiFi原始观测值塞进滤波器松耦合实现简单、调试方便对Android设备性能要求也低。对一个小型室内定位工程来说这是很务实的选型。2. 程序整体架构与模块划分2.1 六个核心模块拆开这个zip工程Android项目结构非常清晰主要分成六个模块各自职责单一方便单独调试和替换WiFiScanModule负责扫描周围的WiFi热点封装WifiManager的扫描逻辑把扫描到的BSSID、SSID和RSSI整理成标准指纹格式。FingerprintDatabase指纹库管理基于SQLite的离线指纹存储支持指纹数据采集、查询、更新在线定位时从这个库里拿参考数据做匹配。WiFiLocalizerWiFi定位引擎负责把当前扫描到的实时指纹和指纹库里参考点做匹配计算核心算法是KNNK近邻。PDRModule行人航迹推算模块读取加速度计和方向传感器数据完成步数检测、步长估计、航向统计和坐标更新。FusionEngine融合引擎把WiFi定位结果和PDR的位移结果整合在一起实现位置修正和轨迹平滑。MapView室内地图显示模块负责把融合后的坐标渲染在平面图上实时显示行走轨迹。我在解这个工程的时候觉得模块划分的节奏感很好。每个模块都只负责一件事情数据通过简单的接口传递后续调参、替换算法都不需要大面积改代码。比如你想把KNN换成别的算法只动WiFiLocalizer就够。2.2 数据流走向整个程序的数据流是双向的离线阶段采集指纹数据入库在线阶段实时定位输出坐标。离线采集时WiFiScanModule扫描周围AP把BSSID、RSSI等信息连同当前人工标注的位置坐标一起写入FingerprintDatabase。这个过程需要在室内按网格逐点采集每个点至少停留几十秒保证样本量充足。在线定位时WiFiScanModule再次开始扫描把实时RSSI交给WiFiLocalizerWiFiLocalizer从FingerprintDatabase里取参考指纹做KNN匹配得出一个绝对坐标。与此同时PDRModule持续读取传感器数据检测到一步就更新一次相对位移。两个结果传递到FusionEngine融合之后输出最终位置再丢给MapView渲染。这个流程每个周期大概2秒左右因为WiFi扫描频率受限。PDR的更新频率要高得多一步一更所以在两帧WiFi定位之间PDR会输出好几个中间点融合引擎需要用这些中间点把WiFi的轨迹“补”得连续。2.3 关键设计取舍这个工程选择松耦合而不是紧耦合主要原因是工程实现代价小、稳定性好。松耦合下WiFi和PDR各算各的互不干扰即使某个定位源暂时失效另一个还能撑住。比如WiFi信号在某段走廊特别弱扫描结果很差这时可以降低WiFi的置信度让PDR主导输出反过来在WiFi信号好的区域让WiFi多拉一拉PDR的漂移。紧耦合虽然理论上能利用更多信息但需要把WiFi的观测模型精确建模RSSI信号本身非线性强、NLOS非视距干扰多建模不好反而容易让滤波器发散。指纹库用SQLite存储也很合理。WiFi AP的RSSI值是典型的键值对结构SQLite索引方便查询效率高而且做数据增删改查都在App内部完成不需要额外的网络服务端。如果指纹库做得大离线采集几百个点每个点几十个AP的指纹也就几万条记录SQLite完全扛得住。这里面有个容易被忽略的设计点工程里把“采集模式”和“定位模式”做成了两个独立接口。采指纹和定位是两套流程如果混在一起会导致在线定位时误把实时数据写进指纹库后期定位结果会越来越脏。单独拆开更安全。3. Android端核心代码与实现细节3.1 WiFi指纹采集与实时扫描WiFi扫描这部分最容易踩的坑是Android版本差异。Android 8之后对后台WiFi扫描的限制非常严格前台应用扫描间隔也有限制。代码里必须做好权限检查和扫描请求失败的兜底。下面是工程里实现WiFi扫描的核心部分我加上了注释方便理解public class WifiScanner { private WifiManager wifiManager; private BroadcastReceiver scanReceiver; private Context context; private WifiScanCallback callback; public WifiScanner(Context context, WifiScanCallback callback) { this.context context; this.callback callback; this.wifiManager (WifiManager) context.getApplicationContext() .getSystemService(Context.WIFI_SERVICE); } public void startScan() { // Android 12以下扫描大约需要1-2秒13以上更快一些 IntentFilter filter new IntentFilter(); filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); context.registerReceiver(scanReceiver, filter); // 关键很多手机上位置服务没打开扫描会直接失败 boolean success wifiManager.startScan(); if (!success) { callback.onError(WiFi扫描启动失败请检查定位权限); } } private BroadcastReceiver scanReceiver new BroadcastReceiver() { Override public void onReceive(Context context, Intent intent) { ListScanResult results wifiManager.getScanResults(); ListWifiFingerprint fingerprints new ArrayList(); for (ScanResult result : results) { WifiFingerprint fp new WifiFingerprint(); fp.bssid result.BSSID; fp.ssid result.SSID; fp.rssi result.level; fingerprints.add(fp); } if (callback ! null) { callback.onScanResult(fingerprints); } } }; }这里需要特别提醒的是WiFi扫描结果必须拿到ACCESS_FINE_LOCATION权限否则在Android 6以后拿不到任何ScanResult。而且就算权限给了如果系统位置开关没打开扫描队列也只会返回空结果。我在工程调试时遇到过这种情况权限全部授权了但就是扫不到AP最后发现是模拟器上的GPS开关是关的WiFi扫描需要“定位服务”总开关处于开启状态。拿到原始WiFi扫描结果后需要把它转成指纹向量。指纹向量的格式一般是[AP1的RSSI, AP2的RSSI, ..., APn的RSSI]其中AP的排列顺序需要固定。工程里是先把所有指纹库里出现过的BSSID收集成一个AP列表然后每一帧扫描结果映射到这个列表上扫描不到的AP用-100dBm填充。这样做的目的是保证指纹向量的维度一致KNN计算欧氏距离时才有意义。3.2 KNN指纹匹配算法WiFi定位引擎里用的KNN匹配算法实现并不复杂。计算当前指纹和指纹库每一个参考点的欧氏距离排序取距离最近的K个点然后按距离倒数加权平均得到最终定位坐标。精髓就在于K值的选择和加权方式。public Position knnLocalize(ListWifiFingerprint realtimeFp, ListReferencePoint database, int K) { // 构建当前指纹向量维度与指纹库一致 double[] queryVector buildVector(realtimeFp); ListRefPointDistance distances new ArrayList(); for (ReferencePoint rp : database) { double[] refVector rp.getVector(); double distance 0; for (int i 0; i queryVector.length; i) { double diff queryVector[i] - refVector[i]; distance diff * diff; } // 这里用的是欧氏距离工程上也可以用曼哈顿距离或者余弦相似度 distances.add(new RefPointDistance(rp, Math.sqrt(distance))); } // 按距离从小到大排序取前K个 Collections.sort(distances, new ComparatorRefPointDistance() { Override public int compare(RefPointDistance a, RefPointDistance b) { return Double.compare(a.distance, b.distance); } }); double totalWeight 0; double x 0, y 0; int count Math.min(K, distances.size()); for (int i 0; i count; i) { RefPointDistance item distances.get(i); // 用距离的倒数作为权重距离越近权重越大 double weight 1.0 / (item.distance 0.01); x item.rp.getX() * weight; y item.rp.getY() * weight; totalWeight weight; } if (totalWeight 0) { return new Position(x / totalWeight, y / totalWeight); } return null; }实际跑起来K值太大会导致定位结果“向中间靠拢”因为取的点多了以后加权平均会压缩位置差异定位轨迹看起来就是缩在地图中间一小块区域K值太小比如K1则容易受单点噪声影响定位结果一帧一个样。工程里默认K设的是8~10这个范围内定位精度相对稳定。另外指纹库参考点的RSSI向量需要提前做归一化因为不同手机接收器的灵敏度不一样同一部手机在移动过程中RSSI绝对值波动也可能很大。归一化后主要保留的是“相对强度”而不是“绝对强度”匹配会更鲁棒。3.3 PDR核心计算链步数检测、步长估计、航向推算PDR模块是整个工程里最容易调得头疼的部分。我先把三个核心环节逐一拆开讲。步数检测的主流方法是峰值检测法。手机内置加速度计输出三轴加速度先计算合成加速度的模值mag sqrt(x² y² z²)然后去掉重力分量通过高通滤波剩余的部分就是行走过程中的身体震动。每走一步震动信号会形成一个明显的波峰波谷检测到波峰就计一步。public class StepDetector { private float lastMagnitude 0; private float peakThreshold 1.2f; // 可调参数 private long lastStepTime 0; private int stepCount 0; public void onSensorChanged(float[] accelerometerValues, long timestamp) { float x accelerometerValues[0]; float y accelerometerValues[1]; float z accelerometerValues[2]; // 计算合成加速度 float magnitude (float) Math.sqrt(x * x y * y z * z); // 高通滤波去除重力常量 float highPass magnitude - lastMagnitude; lastMagnitude magnitude; // 峰值检测高通过零后由正转负 if (highPass peakThreshold timestamp - lastStepTime 300) { stepCount; lastStepTime timestamp; } } }需要特别注意的是peakThreshold不能设得太低否则手臂的晃动、路面颠簸都会被当成一步步数检测结果会成倍增长。工程里还加入了一个步频限制两次计数间隔不能低于300毫秒因为正常成年人最快步频也就每秒3步左右再快就是误检。另外手机放在口袋、拿在手里、放在耳朵旁打电话三种姿态下加速度信号的特征完全不同。工程里默认是手持模式其他姿态需要切换不同的检测参数。步长估计常用的是Weinberg模型行人迈出一步的长度与这一脚跨出时身体重心起伏的加速度峰值差有关公式为stepLength k * (accMax - accMin)^(1/4)其中k是一个需要标定的人体系数不同身高、不同步速的人都不一样。工程里提供了一组默认参数实际使用前最好做一次简单的步长标定实验让人直线走20米统计步数用真实距离除以步数算出实际步长作为模型的校准值。航向推算是整个PDR里误差最大的环节。工程里用的是Android提供的旋转矢量传感器TYPE_ROTATION_VECTOR它通过融合加速度计、陀螺仪和磁力计的输出输出设备当前的姿态转换成欧拉角后能拿到航向角。但注意航向角描述的是“手机朝向”不是“人的行走方向”。手机屏幕朝上的手持模式还好说如果手机放在裤子口袋里朝向和人的行进方向可能有很大的固定偏转需要先做一次方向校准。得到步数、步长和航向之后位置更新就是简单的极坐标累加double heading Math.toRadians(pdrHeading); x stepLength * Math.sin(heading); y stepLength * Math.cos(heading);这里坐标系需要统一工程里地图采用“上北下南”的几何坐标系所以y方向是北方向x方向是东方向。这一步看着简单实际调试时因为坐标系混乱导致的轨迹翻转、镜像问题我遇到过不止一次。3.4 融合引擎让两个定位源互相纠正融合引擎的实现工程里用的是带噪声协方差调的卡尔曼滤波简化版状态向量是二维坐标(x, y)PDR的位移作为状态转移驱动WiFi定位结果作为观测值来修正状态。卡尔曼滤波的基本流程是两步预测和更新。预测阶段用上一时刻的位置加上PDR检测到的位移得到预测位置更新阶段把WiFi定位的观测值和预测位置做加权平均权重由噪声协方差矩阵决定。当WiFi信号稳定时观测噪声R小WiFi对最终结果的拉动作用强当WiFi信号抖动厉害时R增大系统更信任PDR的运动趋势。public class FusionKalman { private double x 0, y 0; private double px 0, py 0; // 预测误差协方差 private double q 0.1; // 过程噪声 private double r 5.0; // 观测噪声根据WiFi置信度调整 public void predict(double stepX, double stepY) { x stepX; y stepY; px q; py q; } public void update(double wifiX, double wifiY, double wifiConfidence) { double noise r / wifiConfidence; double kx px / (px noise); double ky py / (py noise); x kx * (wifiX - x); y ky * (wifiY - y); px * (1 - kx); py * (1 - ky); } }工程里把r值设成了动态的当一帧WiFi扫描结果和指纹库匹配度很高距离近、最近邻和次近邻的差距明显时说明定位置信度高就把noise调小让WiFi提供更强的修正当WiFi匹配度差比如周围AP很少或者RSSI普遍偏低时就反着来。这个动态调整策略很有用实测下来能让轨迹在WiFi信号好的区域快速收敛在信号差的区域不会因为WiFi不良观测而乱跳。4. 参数标定与调优经验4.1 WiFi指纹库的建立指纹库质量直接决定WiFi定位的天花板。工程里默认的采集网格间距是1.5米这个密度在走廊里够用但在开阔大厅或者拐角密集的区域可以加密到1米。指纹库太密采集成本高太疏定位精度又上不去1~1.5米是我试过比较省力且效果均衡的间距。每个参考点采集时间至少要20秒以上。原因很简单RSSI是统计量一个样本完全不可信一段时间的统计平均才能反映该位置的真实信号特征。采集时人需要站在参考点上尽量以正常使用手机的姿态举着手机不要快速转动身体。我最初采集指纹时犯过懒每个点停留时间太短导致定位时参考指纹和实测指纹的偏差很大定位误差直接翻倍。AP筛选也很关键。一个参考点周围能扫到几十个AP但很多AP信号极弱或者时有时无如果全带进指纹向量维度高、噪声大反而把有效信号稀释了。工程里做了个简单筛选只保留在全部参考点中扫描到次数超过80%的AP其他AP剔除。这个筛选能显著提高匹配稳定性。4.2 PDR参数标定PDR参数里最值得花时间的是三个步数检测阈值、步长系数和方向偏移。步数检测阈值peakThreshold默认1.2但不同手机的加速度计噪声水平不一样。较便宜的机器加速度计噪声大这个阈值可以提高到1.5否则静止时也会误报步数。调节的方法是让测试者拿着手机走20步看App统计的步数是多少如果明显多出或少了就相应调阈值。步长系数k的标定方法前面提过就是已知距离测步数。我这边标定出来身高175cm、正常步速的人步长在0.6~0.7米之间小步慢走大概0.5米大步快走能到0.8米以上。如果程序需要适应不同人的使用可以在设置页留一个步长校准入口让用户填身高和性别用经验公式估算步长。方向偏移的问题更隐蔽。手机的磁力计会受到环境磁场干扰尤其是走廊里的配电箱、钢制门框会明显干扰航向。工程里加了一个“方向预校准”步骤走到已知方向的直走廊上按一次校准按钮记录当前航向和真实方向的差值后续PDR航向都减去这个差值。这个校准操作实测能减少一半以上的轨迹漂移。4.3 滤波器参数调节卡尔曼滤波里的q和r是两个不可回避的参数。q表示过程噪声也就是PDR位移的置信度q越大表示系统认为PDR每一步的位移误差越大r表示观测噪声也就是WiFi定位的置信度r越大表示系统认为WiFi定位结果越不可信。工程里默认q0.1、r5.0这套参数在普通办公环境跑起来问题不大。如果定位轨迹看起来“太跳”说明r设小了WiFi的噪声被过度信任如果轨迹“太飘逸”、长时间不收敛到正确位置说明q设大了或者r设的太大WiFi根本拉不住PDR。调参时建议先把r固定然后从小到大调q观察轨迹的平滑度再反过来固定q调r观察轨迹和真实路径的贴合度。这是个反复试错的过程没有捷径。5. 常见问题与避坑指南5.1 定位点乱跳和轨迹穿墙问题如果融合后的坐标还是频繁跳变首先要怀疑WiFi定位结果的置信度是否被高估。可以加日志打印每一帧的WiFi匹配距离如果距离值很大且波动明显说明当前WiFi信号环境很差这时融合引擎应该增大r、减小WiFi的影响权重。另一个常见原因是指纹库里的参考点和实时扫描的RSSI绝对值存在系统性偏差不同手机厂商的信号接收灵敏度不一样。解决办法是做一次信号强度补偿在已知位置同时采集一遍实测值和指纹库参考值求平均差值在线匹配前把实时RSSI减去这个差值。轨迹穿墙的问题根源在于定位结果是纯坐标点没有做地图约束。室内地图通常有墙体轮廓如果定位点落在了墙里面明显是错的。工程里可以加一个简单的“墙体碰撞检测”判断定位点是否穿过了墙线如果是就把位置拖到墙体最近的边缘。这个约束做起来不复杂但能让轨迹看起来正常很多。5.2 步数检测不稳定的排查步骤步数检测出现问题时排查方向不要一上来就调算法先确认硬件数据有没有问题。打印加速度计原始数据和合成模值曲线看看峰值是否明显。如果波形是平的那可能是传感器采样率太低代码里需要显式设置采样频率为SENSOR_DELAY_GAME或更高。如果波形正常但步数仍然误检再看阈值和时间间隔条件。手机佩戴位置改变之后步数检测就会明显受影响。放在裤兜里时竖向加速度分量更大峰值更明显检测反而更准拿在手里摆动时摆臂产生的加速度分量会干扰检测。解决方案是使用“同侧”检测把手机放在稳定位置避免在行走中频繁切换握持姿势。这也是为什么工程里在设置页里加了一个“手机佩戴位置”选项不同位置对应不同的检测参数组。5.3 Android系统层面的坑Android 9API 28以后系统对后台WiFi扫描限制非常严格。如果App退到后台WifiManager.startScan()会被静默拒绝导致定位直接停止刷新。解决方案是启动一个前台服务并且在服务通知里保持定位状态让系统认为App正在前台使用。还有一点是关于分区存储的。Android 11以后的存储权限规则更严格指纹库文件如果导出到外部存储需要用MediaStore或者应用专属目录不能直接访问根目录。工程里把指纹库默认放在App内部数据库目录里绕开了这一层权限问题如果你自己扩展导出功能务必处理好这个点。Android设备碎片化也是个不可忽视的问题。不同厂商对传感器数据的处理方式略有差异同一套步数检测参数在小米和三星设备上跑出来的步数误差可能差5个百分点。工程里预留了“设备校准参数”的数据库表可以针对设备型号保存不同的参数组实测中很有用。5.4 几个值得单独说的独家经验整个项目跑下来我最想分享的几条经验都堆在这里每一条都是用实际测试换来的。第一WiFi指纹库不要在人多的时候采集。工作日下午的办公室和晚上十点的办公室WiFi信号环境是两套完全不同的场景。采指纹时如果人流量大人体遮挡会导致RSSI统计值偏低且波动大。工程里建议采集时间安排在环境相对安静的时候并且定期做指纹库刷新。第二PDR航向不要直接使用方向传感器。Android早期版本的TYPE_ORIENTATION已经废弃它的输出在快速转身时会有一段明显的延迟。工程里用的旋转矢量传感器也需要加低通滤波我是对航向角做了滑动平均窗口取5帧效果很好既保住了转向灵敏度又把抖动抹平了。第三融合结果输出前加一个低通平滑。即使有卡尔曼滤波坐标输出依然可能带一些高频抖动。工程里对最终坐标做了简单的指数平滑pos alpha * newPos (1 - alpha) * oldPosalpha取0.6左右。代价是轨迹会有轻微滞后但换来的是肉眼可见的平滑效果在导航场景里体验提升非常明显。第四调试时一定要实现一个“回放”功能。工程里把每一帧的WiFi扫描原始数据、传感器数据和融合结果按时间戳实时记录到本地文件。回放时可以把这些数据重新跑一遍融合算法方便定位算法调试。这个功能开发时多花半天时间后面调试能省好几天的功夫。没有回放功能直接在真机上改参数、反复走路线人会走到崩溃。第五精度评估必须区分场景。同一套系统在空旷大厅、狭长走廊、电梯厅附近的误差表现完全不同。电梯旁的定位误差动不动能到十几米因为金属箱体严重反射信号走廊里只要不走到底PDR主导的轨迹精度反而不错。评估精度时建议按场景分类统计不要只看一个总体平均值否则你会被平均数据迷惑不知道真正该优化哪里。我在实际使用这个工程的过程中最大的体会是不要把WiFi和PDR当成两个定位器而是把它们当成两个“各有性格”的传感器。WiFi很聪明但每次开口都带着颤动PDR很稳健但久了一定会跑偏融合算法就是那个听两边意见再做决定的协调者。协调得好不好全看你对两个传感器的脾气了解多少。做室内定位这个方向没有一劳永逸的参数只有不断采集、不断校准、根据场景调参数的过程。如果你刚拿到这个zip工程建议先跑通流程再逐步调整参数最后再往工程里加入你自己的改进点。慢慢来室内定位这口饭急不得。本文还有配套的精品资源点击获取