
1. 方案选型为什么用 Wi-Fi 做室内定位而不是蓝牙或UWB这两年出门导航已经成了条件反射但一到商场、地下车库、大型展馆GPS 信号被建筑结构挡得七七八八手机上的定位点要么飘到隔壁街区要么干脆转圈圈。室内定位的需求就是这么被逼出来的。而 Wi-Fi 室内定位系统说到底就是利用现成的无线网络基础设施通过测量手机收到的路由器信号强度反推出人所在的位置。很多人会问室内定位方案这么多蓝牙 Beacon、UWB超宽带、地磁、视觉为什么偏偏选 Wi-Fi我的答案很直接低成本、零额外硬件、手机直接兼容。商场、机场、写字楼、医院本来就有大量的 AP无线接入点在跑Wi-Fi 定位不需要像蓝牙方案那样再部署几百个 Beacon也不像 UWB 那样需要专用芯片和配套基站只需要在服务器上架一套定位引擎前端用浏览器或 App 调接口就能把定位能力用起来。1.1 Wi-Fi 定位的底层逻辑与精度预期Wi-Fi 定位的核心原理并不复杂主要分三类思路第一种叫RSSI 测距法根据信号强度随距离衰减的规律反推手机到 AP 的距离。拿到三个以上 AP 的距离后用三边定位法算出坐标。这个方法实现简单但室内环境多径效应严重信号反射、折射、吸收都会让 RSSI 和距离的对应关系变得很不稳定实际精度通常只有 5 到 15 米只适合对精度要求不高的场景。第二种叫指纹定位法分两个阶段。离线阶段把定位区域划分成网格在每个网格点记录手机收到的各个 AP 的信号强度建成一张“指纹地图”在线阶段手机实时上报它收到的信号强度数组后台拿这个数组去指纹库里比对用最近邻算法找出最匹配的网格点。这个方案的精度可以做到 2 到 5 米是目前 Wi-Fi 定位的主流做法。第三种是信道状态信息CSI测量需要网卡和驱动支持能拿到更细粒度的振幅和相位数据可以做人体活动识别甚至手势识别但对硬件要求高没法和普通手机直接配合使用基本只停留在研究和定制项目阶段。做项目前一定要对精度有一个清醒的认知。Wi-Fi 定位的上限就在那里指纹方案能做到 3 米左右已经非常不错。如果你的需求是“找车位精确到哪一辆车”那老老实实上 UWB 或者地磁方案Wi-Fi 做不了这种厘米级的事情。但如果是 “在商场里找到某家店铺”“在园区里确认识别在哪栋楼”Wi-Fi 定位完全够用。1.2 RSSI 信号模型与衰减公式再深入一点RSSI 测距法背后是经典的对数距离路径损耗模型RSSI(d) RSSI(d0) - 10 * n * lg(d / d0) X其中d0是参考距离通常取 1 米n是路径损耗指数在空旷走廊里约 2在穿透多面墙的办公室可能到 3.5 甚至 4X是随机噪声项。这个公式在室外很准但在室内一个很小的位置偏移比如转了 30 度方向就可能导致 5dBm 以上的波动换算成距离就是几米的偏差。所以我在项目里几乎不做纯测距法只把它用于粗略判断“用户在哪个区域”。指纹法因为没有明显依赖距离公式反而受环境影响更小。但指纹法有个前提场景不能大改。如果你今天采集指纹库时货架是这样摆的明天全部换了位置那指纹库直接废掉必须维护更新。2. 核心细节指纹定位的建模与实际部署要点2.1 区域网格划分与AP信号覆盖勘察指纹定位的第一步是划分参考点。这个步骤需要亲自到现场拉尺子不能只在 CAD 图上画。建议在定位区域用 1 到 1.5 米的网格密度划点走廊可以放宽到 2 米开阔大厅保持 1 米。每个网格中心就是一个采样点Reference Point简称 RP。在开始采集前先要对现场的 AP 部署情况做一次勘察。拿手机装一个 Wi-Fi 分析仪Android 上可以用 WiFiAnalyzeriOS 上可以用 AirPort Utility 的 Wi-Fi 扫描功能走一遍全场记录以下信息每个位置能收到的 AP 数量以及对应的 BSSIDAP 的 MAC 地址信号强度范围如果很多位置信号低于 -85dBm说明覆盖有空洞需要临时补充 AP 或改用邻近参考点AP 的信道配置2.4G 频段 1、6、11 信道是否存在严重重叠注意AP 数量并不是越多越好。我实测过一个位置能稳定收到 5 到 8 个 AP 信号定位效果最好。信号太多但大多都在 -80dBm 以下反而会引入大量噪声降低匹配准确度。2.2 指纹库采集数据质量决定定位下限指纹库的质量直接决定在线定位的下限。这里有一个关键指标叫RSSI 稳定性。同一位置同一 AP静止不动的手机30 秒内信号强度也可能波动 3 到 6dBm。如果只是站在某个点采一次数据这个指纹的代表性很差。我采用的采集策略是每个参考点静止采集 30 到 60 秒每秒扫描一次Android 的WifiManager.startScan()加上ScanResult回调实际扫描频率受系统限制约 1 到 2 秒一次拿到一个 AP 的多组 RSSI 样本后取中位数而不是平均值。为什么不取平均因为 RSSI 的噪声分布不是标准正态分布偶尔会出现特别极端的值比如 -30dBm 的瞬时峰值平均值会被这些异常值严重拉偏中位数则稳得多。采集工具的选择上不用自己做复杂的 App初期可以用现成的 WiFi 指纹采集开源工具但正式项目中我建议自己写一个原因是现成工具的数据格式和导出方式往往不满足你的网格管理需求。我自己的采集程序核心逻辑很简单# 伪代码基于 Android ScanResult 的回调 fingerprint {} while time 30s: results wifiManager.getScanResults() for r in results: bssid r.BSSID rssi r.level if bssid not in fingerprint: fingerprint[bssid] [] fingerprint[bssid].append(rssi) sleep(1) # 每个 AP 取中位数 fp_vector {bssid: sorted(vals)[len(vals)//2] for bssid, vals in fingerprint.items()}采集时要注意身体朝向。我会在同一个参考点分别采集东南西北四个方向的样本或者至少做 360 度缓慢旋转采集。因为人体含水对 2.4G 信号有明显的吸收手机朝向不同会让部分 AP 的 RSSI 下降 5 到 10dBm。如果只朝一个方向采用户使用时其他方向对着 AP定位误差会显著增加。2.3 AP 选择策略和 RSSI 截断处理在建模阶段不是所有扫到的 AP 都要用。我一般会做一个预处理剔除信号强度低于 -85dBm 的 AP因为弱信号抖动剧烈携带的位置辨别信息很低剔除样本中出现率低于 80% 的 AP一个 AP 在这个参考点只出现过几次说明它在覆盖边缘不稳定对 BSSID 做去重部分企业 AP 同一射频可能有多个 BSSID要过滤掉虚拟 BSSID还有一个容易被忽略的点MAC 地址随机化。现代智能手机尤其 Android 10 以上在扫描 Wi-Fi 时会使用随机 MAC 地址。这导致采集指纹时手机上报的 BSSID 集合和用户手机上报的 BSSID 集合可能不一致。不过 BSSID 是 AP 侧的信息通常不会受影响受影响的是客户端 MAC而室内定位服务端只关心 AP 的 BSSID所以这个问题在 Wi-Fi 室内定位中问题不大。真正需要留意的是部分 AP 开启了隐藏 SSID但 BSSID 依旧可被扫描到不影响定位。3. 实操过程从零搭建一套可用的 Wi-Fi 定位系统3.1 系统架构与模块划分一个完整的 Wi-Fi 室内定位系统我拆成了四个模块模块职责技术选型数据采集端采集指纹样本关联网格坐标Android App / Python Scapy指纹库管理存储、预处理射频特征数据MySQL 定时任务在线定位引擎接收实时 RSSI 数据计算位置Python FastAPI Redis 缓存可视化前端地图展示位置标注浏览器端 Leaflet / Web Canvas这里最核心的是定位引擎。定位引擎要解决两个问题一是用哪个算法做位置估算二是如何验证精度达标。3.2 在线定位算法从 KNN 到 WKNN我当时用的是WKNN加权 K 最近邻。基本逻辑是拿到用户上报的 RSSI 向量和指纹库里的每个参考点向量计算距离取最接近的 K 个点按距离倒数加权平均得到最终坐标。指纹向量距离的度量可以用欧氏距离也可以用余弦相似度。但实测下来欧氏距离在 RSSI 这种偏差值上更直观效果也好一些。且在做距离计算前需要统一向量维度。因为用户实时上报的 AP 集合和指纹库的 AP 集合不一定完全重合需要做向量对齐import numpy as np from scipy.spatial.distance import euclidean # rssi_query: dict {bssid: rssi} # fp_db: list of dict {bssid: rssi, x: x, y: y} def wknn_position(rssi_query, fp_db, k5): distances [] common_bssids list(rssi_query.keys()) query_vector np.array([rssi_query[b] for b in common_bssids]) for fp in fp_db: fp_vector np.array([fp[rssi_map].get(b, -100) for b in common_bssids]) d euclidean(query_vector, fp_vector) distances.append((d, fp[x], fp[y])) distances.sort(keylambda t: t[0]) neighbors distances[:k] # 距离倒数加权 weights [] for d, _, _ in neighbors: if d 1e-6: weights.append(1e6) else: weights.append(1.0 / d) w_sum sum(weights) x sum(w * x for w, (_, x, _) in zip(weights, neighbors)) / w_sum y sum(w * y for w, (_, _, y) in zip(weights, neighbors)) / w_sum return x, y这段代码里有个细节对于指纹库中存在但用户当前没测到的 AP我用-100dBm补齐。为什么不跳过这个 AP因为“用户在这个位置收不到某个 AP”本身就是一种位置信息。一个在 A 点能收到 5 个 AP、在 B 点只能收到 2 个 AP 的场景缺失本身就能帮助算法区分区域。k值的选择需要调参。我试过 3、5、7、9在 800 平方米的办公区里k5 时 CDF累积分布函数的 50 分位误差最小约 2.8 米k 太大反而会把远处不相关的点拉进来增加误差k 太小则抗噪能力差单点误差跳变严重。建议在项目启动时做一轮 k 值网格搜索找到最优参数。3.3 滤波与平滑应对 RSSI 抖动的组合拳定位引擎算出来的原始坐标通常抖动明显。即使手机完全静止连续两次定位结果可能相差 3 米以上。原因还是 RSSI 的时变波动。我在线上部署时加了两个平滑层第一层是RSSI 时间平滑对同一 AP 的连续测量值做移动平均或卡尔曼滤波。我选了一阶低通滤波smooth_rssi alpha * raw_rssi (1 - alpha) * prev_smooth_rssialpha取 0.3 比较合适太小则响应迟钝人走了 5 米位置还在原地太大则滤波器基本失效。但低通滤波有个副作用定位结果会“粘滞”适合安防巡检这种慢速场景不适合实时导航。第二层是坐标空间平滑采用带最大移动速度约束的滑动窗口。假设人每秒最多走 2 米上一帧位置到当前帧原始位置的直线距离如果超过 2.5 米就把当前帧位置沿连线方向截断在 2.5 米处。这个策略在实际项目里非常有效能把静态场景的定位漂移从 4 米压到 2 米以内。3.4 精度评估怎么判断定位系统“能用”做定位系统最怕拍脑袋说“差不多能用”一定要量化为指标。行业里核心指标是CDF 误差分布曲线横轴是定位误差米纵轴是“误差小于该值的测试点占比”。我在测试时会在现场选 50 个均匀分布的测试点和指纹参考点不重合的独立点位每个点测试 20 次统计误差。验收标准可以这样定50% 的测试点误差 ≤ 3 米80% 的测试点误差 ≤ 5 米90% 的测试点误差 ≤ 8 米如果达不到先从指纹库质量查起大部分情况是采集密度不够或者 AP 覆盖不均匀。还有一个经常被忽略的指标是定位稳定性——同一个点连续定位 20 次输出的坐标点集应该落在一个半径 2 米左右的圆内。如果定位点绕着一整条走廊乱跳说明算法或输入数据有问题。稳定性影响的是体验比绝对误差更难优化。4. 常见问题与排查技巧实录4.1 信号跳变严重定位点乱飘这是一个高频问题。排查看三个方面检查手机系统Android 的 Wi-Fi 扫描频率受系统节电策略影响。部分国产 ROM 在灭屏后会自动停止扫描或者把扫描周期拉到 30 秒以上。处理办法是前台保持亮屏或者申请CHANGE_WIFI_STATE权限并保证 App 在前台运行。检查 AP 信道干扰2.4G 频段的信道重叠非常严重。如果现场同时有十几个 AP 挤在信道 6 上互相干扰会造成 RSSI 剧烈波动。有条件的话把相邻 AP 分配到 1、6、11并开启 5.8G 频段优先让支持 5G 的手机连接。检查算法参数k 值太小或者距离阈值设置不合理也会导致结果跳跃。我习惯先把原始定位点打印在图上区分是“RSSI 抖动”还是“算法不稳定”。如果原始 RSSI 不抖但坐标抖就调算法如果 RSSI 本身就抖先处理信号。4.2 AP 信号出现忽强忽弱定位结果“跳区”项目上线一周后某个区域的定位结果频繁在 A 区和 B 区之间跳跃。最后排查原因是 B 区新增了一台微波炉微波炉工作时对 2.4G 频段有很强的干扰。这种事情在食堂、茶水间特别常见。对策是双管齐下一方面在定位引擎中加入区域置信度机制只有连续 3 帧定位到同一区域才切换显示区域另一方面建一个信号质量的长期监控任务定时统计各 AP 的 RSSI 均值发现某个 AP 均值异常下降超过 10dBm自动告警提醒现场排查。4.3 设备兼容性差异iPhone 和 Android 的 RSSI 天差地别这是行业内公认的坑。iPhone 和 Android 手机使用的网卡芯片不同天线增益不同RSSI 上报方式也有差异。同一位置iPhone 报的 -63dBm某品牌 Android 可能报 -70dBm。如果用一组设备采集指纹库另一组设备做实时定位误差会显著增加。缓解这个问题的常见做法是设备归一化在采集阶段拿多台主流设备在同一参考点采集数据计算设备间 RSSI 的固定偏差统一校正到某个基准设备上。我实际使用中还有一个土办法比较实用采集指纹库时准备两台设备一台 iPhone、一台 Android分别采集生成两份指纹库在线定位时根据用户设备类型选择对应的指纹库。虽然维护成本翻倍但精度提升立竿见影。4.4 指纹库失效场景变化了怎么办这个问题的核心是“指纹库是有保质期的”。金属隔断、货架堆满/清空、人流量高峰期、会议室门开着/关着都会改变信号场。建议方案是AP 级指纹失效检测在定位区域内设立 3 到 5 个校准点每个校准点记录标准 RSSI 向量。后台定期下发指令让本地巡检人员或保洁人员携带一个带有定位 SDK 的终端路过这些点时自动上报当前 RSSI和标准向量比对。如果偏差超过阈值触发对应区域的指纹库局部更新用新数据替换旧数据。不要想着整个指纹库重建工作量太大了。局部更新加定期全面重采两者结合才可持续。5. 精度优化进阶比“能用”更好的实战调整5.1 多频段融合2.4G 和 5G 的信号特征分开用不少人在指纹建模时只收集 2.4G 的信号因为 5G 信号穿墙能力差、容易被忽略。但 5GHz 频段的信号在无阻挡的环境里RSSI 稳定性比 2.4G 好很多多径效应更小。如果现场 AP 支持双频建议在同一采集点上分别记录 2.4G 和 5G 的指纹向量。在线定位时把两个频段作为独立的特征拼接起来。但要注意同一 BSSID 同时发 2.4G 和 5G 时BSSID 不同可以直接拼接不需要额外处理。几轮实践下来多频段融合的平均误差大约能比单用 2.4G 减少 10% 到 15%。5.2 高密度区域的细分子区域分类器在开放办公区几十个工位挨在一起只用指纹库做 WKNN 分辨不出来相邻工位。这时加一层粗定位 细分类的二级结构第一级用全量指纹库做粗匹配把用户定位到某个大区域比如 10 米乘 10 米的区块第二级只在这个区块内做细分类用更密集的指纹点0.5 米间隔配合支持向量机SVM或随机森林做分类把用户进一步细分到工位或座位实际效果非常显著。在一个 200 平方的开放式工区里粗定位级别区分度不高加了细分类后“这个人在 3 排 5 号工位”这种粒度可以实现约 85% 的准确率。代价是采集工作量大了不少因为细分类阶段的指纹点密度要求高很多。5.3 多源融合用惯性传感器修正 Wi-Fi 位置在手机上做室内定位Wi-Fi 不是唯一可用信息。手机上的加速度计、陀螺仪、磁力计也能提供步数、方向信息。把 Wi-Fi 定位结果和行人航位推算PDR融合是工程上性价比极高的做法。PDR 的思路是通过加速度计检测步数通过磁力计判断方向每一步大约 0.6 到 0.7 米累加就能得到相对位移。问题在于它随时间漂移走 50 米后可能偏了 10 多米。把 Wi-Fi 定位结果作为“锚点”定期校正 PDR 的漂移同时用 PDR 填补两次 Wi-Fi 定位之间的轨迹最终效果是又平滑又准确。融合的方式最简单的是卡尔曼滤波或粒子滤波。扩展卡尔曼滤波在这个场景下实现不算复杂状态变量是 (x, y, 朝向)观测变量是 Wi-Fi 定位坐标预测方程是 PDR 的步进位移。我跑通后的效果在线动态行走测试中50% 误差从 Wi-Fi 纯定位的 3.2 米降到 1.8 米体验提升非常明显。6. 踩坑记录与部署提醒做 Wi-Fi 室内定位不是只写算法就行部署阶段有大量和算法无关的坑这里集中梳理一下我实际踩过的电源和交换机问题定位系统上线后某个走廊区域定位全挂查了一圈发现管理该区域 AP 的 PoE 交换机被误重启部分 AP 离线了。AP 离线对指纹定位是致命的因为它移除了一部分指纹签名。所以每次发布前一定要检查 AP 在线列表。Wi-Fi 漫游策略在密集部署的 AP 环境下手机可能频繁在多个 AP 之间漫游。如果定位引擎采集的是“当前连接的 AP”而不是“所有可见 AP”那定位结果会跟着连接切换跳变。解决方案是扫描所有 AP 的信号而不是只用连接中的 AP。时间同步如果多个采集设备同时做指纹采集需要保证手机时间和服务器一致。指纹数据带的时间戳用于后期分析时间不同步会给调试带来很多麻烦。别笑我一开始就吃过这个亏。还有一个特别实用的小经验现场测试手机不要太高档。低端手机的 Wi-Fi 天线性能差采集到的 RSSI 波动更大、灵敏度更低。如果现场所有测试机都用 iPhone 14 Pro用户手里普遍是各价位段的安卓机精度数据会和你实际交付后的体验相差很多。多准备几台不同价位的手机做交叉验证是最接近真实使用情况的测试方式。对定位引擎的日志建议保存每一次定位请求的原始输入RSSI 向量、AP 列表和输出坐标至少保留一周。这样用户在群里反馈“这里定位飘了”你可以拉出日志回放对照当时 AP 状态和信号质量。没有日志的定位项目出了问题就是两眼一抹黑根本没法定位 bug。最后想分享一个经验Wi-Fi 室内定位项目真正的难点不在算法而在工程化。RSSI 的数据质量问题、硬件兼容性问题、场景变化问题每一个都比算法调参要命。不要迷信高深的深度学习算法先把数据采集做扎实、把可视化调试工具做顺手比你多试十个 SOTA 模型都有用。定位精度不够时先问数据质量好不好再问模型复不复杂。这套流程跑通之后你在这个领域的积累才算真正有了根基。