尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESP32蓝牙Beacon测距实战:RSSI转距离原理与校准滤波指南

ESP32蓝牙Beacon测距实战:RSSI转距离原理与校准滤波指南 搞 ESP32 开发的朋友应该都有这种感觉从板子到手到真正让它按自己的想法跑起来中间要翻过的山头真不少。之前几讲把环境搭建、GPIO、Wi-Fi 联网这些基础打完后很多人在蓝牙这块又卡住了尤其是想用蓝牙做点实际应用的时候比如室内定位、防丢器、靠近检测这类。这篇第六讲主题就是 ESP-IDF 加 VSCode 环境下用 ESP32 做蓝牙 beacon 测距也就是让设备通过低功耗蓝牙收发 beacon 广播用 RSSI 信号强度把距离给估算出来。这篇内容适合两类人看一是已经把 ESP-IDF 环境跑通、想往蓝牙方向深入的同学二是正在做室内定位、防丢、展馆导览、智能门禁靠近解锁这类需求想评估蓝牙 beacon 方案是否可行的工程师。我会把方案选型、协议格式、发射端配置、接收端解析、RSSI 转距离的数学原理和校准方法、滤波处理以及实际调试中踩过的坑和排查思路全部过一遍。整个过程基于 ESP32 常规开发板用 ESP-IDF 的官方蓝牙栈 Bluedroid 实现VSCode 作为编辑器代码会给出关键部分都是可以直接抄走改改就能用的。1. 蓝牙 beacon 测距的方案选型与整体思路1.1 为什么是 beacon它能解决什么问题先说结论beacon 测距解决的是“你这设备大概离我多远”的问题精度在米级胜在成本低、部署简单、终端兼容性好。Beacon 本质是一种单向广播设备它不断向外发送固定格式的蓝牙广播包自己不关心有没有人收到。接收端比如手机、另一个 ESP32通过监听这些广播包获取到信号强度 RSSI然后利用无线信号在空间中传播衰减的规律反推出距离。这种机制和 GPS 完全不是一回事不需要卫星、不需要联网、不需要回传通道非常适合室内场景。实际项目里我用 beacon 测距做过几类东西一个是展馆里的大屏互动观众手机靠近展品 1 米以内自动弹介绍靠的就是 beacon 广播加手机端测距另一个是仓库物料防丢提醒物料上挂一个 beacon网关用 ESP32 扫描它信号强度低于阈值就报警还有朋友在做宠物防丢项圈手机 App 扫到 beacon 后大致判断宠物在房间哪个区域。这些场景的共同特点是不需要厘米级精度但需要低功耗、低成本、能大规模部署。对比一下同类无线测距方案就很清楚了方案频段典型精度成本功耗适用场景UWB超宽带3.1~10.6GHz厘米级高中汽车钥匙、人员高精度定位Wi-Fi RTT5GHz1~2米中需AP支持高商场、机场手机定位蓝牙 beacon RSSI2.4GHz1~5米很低极低防丢、导览、室内粗略定位ZigBee2.4GHz2~5米低低物联网传感网很少用于测距所以如果你的需求是“知道个大概位置”就行那蓝牙 beacon 就是性价比之王。但也要事先有心理准备RSSI 测距天然有噪声环境一变精度就飘方案设计时必须把这一点考虑进去后面第 5 章我会专门讲怎么校准和滤波。1.2 硬件和软件栈选型ESP32、ESP-IDF、VSCode为什么选 ESP32因为它一颗芯片就把双模蓝牙经典蓝牙 BT 和低功耗蓝牙 BLE、Wi-Fi、双核 CPU、丰富外设全集成在一起了价格还便宜。做 beacon 测距时ESP32 既可以当 beacon 发射端也可以当扫描接收端甚至同时作为网关把测距结果用 Wi-Fi 发到服务器一颗芯片全搞定。我最早是用 Nordic 的 nRF52832 做低功耗 beacon后来发现很多项目还需要联网上云ESP32 的双模加 Wi-Fi 优势就出来了整个系统的元件数量也少很多。软件栈为什么用 ESP-IDF 而不是 ArduinoArduino 生态写个简单 beacon 确实快但到了要精细调 BLE 广播参数、扫描窗口、做低功耗管理、跟 Wi-Fi 共存这些环节Arduino 的库封装把底层细节藏得太深反而不顺。ESP-IDF 是乐鑫官方给的完整开发框架BLE 这块用的是 Bluedroid 蓝牙协议栈虽然接口风格偏底层但控制粒度很细广播间隔、扫描窗口、发射功率、厂商数据内容全部可以自己定义。对于 beacon 测距这种需要精确控制协议字段的应用用 ESP-IDF 是更踏实的路子。开发环境选 VSCode 加 Espressif IDF 插件纯粹是效率问题。VSCode 的代码补全、Git 集成、终端体验加上乐鑫官方插件把构建、烧录、串口监视、内存分析都集成到图形界面里对新手友好对老手也顺手。整个开发调试在 VSCode 里闭环不用反复切换工具。这一讲的示例我都按 VSCode 下的操作来讲但底层命令就是 idf.py 那几个在别的环境也没差别。2. 开发环境搭建与第一个蓝牙工程2.1 VSCode 下配置 ESP-IDF 开发环境如果你是从零开始装环境建议直接在 VSCode 的扩展商店里搜“Espressif IDF”安装乐鑫官方的插件。插件首次启动时会把 ESP-IDF 工具链、Python 虚拟环境、OpenOCD、QEMU 等全部下载下来路径默认放在用户目录的 esp 文件夹下。有几个细节值得注意安装 ESP-IDF 时版本选择建议直接选最新 stable 版本。我之前在一个老项目上用了 v4.4新项目想迁移到 v5.x发现 API 变动不小尤其是 BLE 相关的头文件和结构体都有调整。从 v5.0 开始ESP-IDF 启用了新的组件依赖管理构建系统更严格虽然学习曲线略微陡一点但长远看值得。选择目标芯片ESP32 和 ESP32-C3 这类芯片在框架里是不同的 target工程创建后要执行一次 set-target。在 VSCode 插件里命令面板CtrlShiftP输入 ESP-IDF: Set Espressif Device Target选择自己的芯片型号。最常遇到的环境报错插件提示 “The path for ESP-IDF is not valid: /tools/idf.py not found”。这个错误九成是 ESP-IDF 没有正确安装或者路径配置错误。如果手动下载过 ESP-IDF必须在 VSCode 设置的 “Idf: Espressif Idf Path” 中指向包含 idf.py 的那个目录的父级目录比如你下载在 D:\esp-idf那这里填 D:\esp-idf 而不是 D:\esp-idf\tools。配完之后建工程有两条路一是用 VSCode 命令面板的 “ESP-IDF: Show Examples Projects”从官方示例库里找模板二是用 idf.py create-project 自己建一个干净工程。对于讲蓝牙这种需要改官方示例的情况我更推荐直接从示例工程派生省得在 CMakeLists 里手动链接一堆组件包。另外建议在 VSCode 里把 C/C 插件也装好并让它根据 ESP-IDF 生成的头文件路径自动配置智能提示这需要执行一次 “ESP-IDF: Add vscode Configuration Folder” 命令会在工程目录下生成 .vscode/c_cpp_properties.json这样跳转定义、补全才会好使。2.2 官方 iBeacon 示例工程编译与烧录乐鑫在 ESP-IDF 里放了一个很关键的参考工程examples/bluetooth/bluedroid/ble/ble_ibeacon。这个工程就是用 ESP32 广播一个 iBeacon 格式的 beacon 包非常适合作为我们这一讲的起点。在 VSCode 里通过 “ESP-IDF: Show Examples Projects” 找到它选择一个工作目录复制一份出来。打开工程后目录结构核心就三个文件main/ibeacon_demo.cbeacon 广播主逻辑定义并配置广播数据。main/ibeacon_demo.hiBeacon 数据结构的定义和广播包组装的辅助函数。main/CMakeLists.txt组件构建配置一般不用改。在 VSCode 底部状态栏选好串口和目标芯片点击火焰图标开始编译。编译过程中如果看到类似于 “idf.py build 失败” 的情况九成是环境变量、Python 包版本或者工具链路径的问题可以打开终端手动跑一下idf.py build看完整报错日志信息往往比 IDE 里截断的提示更明确。烧录跑起来后用手机下载一个通用的 BLE 扫描工具比如 nRF Connect扫描能看到一个名字为空、厂商数据为 Apple 0x004C 的广播包那大概率就是 ESP32 发出的 iBeacon 了。到这一步说明蓝牙广播已经通接下来就是改协议内容和加扫描测距逻辑。3. beacon 广播数据格式与发射端实现3.1 iBeacon 协议格式拆解iBeacon 是苹果定义的 beacon 广播格式它不基于标准 GATT 服务而是把数据塞在广播包的厂商特定字段里。一个完整的 iBeacon 广播包结构分几层数据段长度字节内容说明PDU 头10x02表示后续广播数据长度Flags1 10x01 0x06广播类型标志LE General DiscoverableAD Length10x1A厂商数据段总长度26 字节AD Type10xFF厂商特定数据Company ID20x004CApple 公司标识符iBeacon Type20x02 0x15标识这是一个 iBeacon 广播UUID16自定义区分不同应用或厂商Major2自定义通常表示区域编号Minor2自定义通常表示具体设备编号TX Power1自定义1 米处的参考 RSSI 值有符号这里的 TX Power 是整个测距功能的关键。它表示接收者在距离 1 米处理论上测到的 RSSI 数值单位是 dBm因为有符号所以用 int8_t 表示。广播端把这个值写进包里接收端拿这个作为参考基准。也就是说如果广播端把发射功率调高了TX Power 值也应该同步调大否则接收端算出来的距离会产生系统性偏差。顺带提一句iBeacon 是苹果家的格式如果做 Android 端或者更通用的场景也可以用 Google 推出的 Eddystone 格式。Eddystone 除了有类似 iBeacon 的 UID 帧还支持 URL 帧和 TLM遥测帧比如广播温湿度数据。我自己实际项目里两种格式都遇到过但从测距角度看核心都是读取参考 RSSI 和实测 RSSI 做衰减计算协议帧不同不影响本讲思路我就以 iBeacon 为例展开掌握了之后换 Eddystone 也就是改解析逻辑的事。3.2 在 ESP32 上配置自定义 iBeacon 广播官方示例工程里已经把广播数据组织的部分写好了我们只需要改几个参数。核心代码逻辑在ibeacon_demo.c里示例会调用esp_ble_gap_config_adv_data配置广播数据。关键修改点UUID改成自己的应用标识16 字节。Major通常用于表示楼层或区域。Minor表示同一区域里的具体设备编号。TX Power需要和实际发射功率匹配多数 ESP32 默认配置下参考值可以写 -59 dBmiOS 设备实测经验值后续校准后再调整。广播参数上示例里用的是系统默认但实际项目中建议自己设置。重点调两个参数广播间隔adv_int_min / adv_int_max单位是 0.625 毫秒。间隔越短接收端发现越快、测距刷新率越高但功耗越大。我在做防丢器时广播间隔设在 100~200ms兼顾发现速度和续航做静态定位点名时甚至可以拉到 1 秒以上。发射功率tx_powerESP32 的 BLE 发射功率可以通过esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, level)设置level 范围从 -12dBm 到 9dBm 左右。功率调高覆盖距离远但功耗也上去而且 TX Power 字段要同步改。默认 9dBm 在室内空旷场景大概能覆盖 30 到 50 米。下面给出一个自定义广播参数和数据的核心代码段基于 v5.x API#include esp_gap_ble_api.h #include esp_bt.h #include esp_bt_main.h #include esp_gap_bt_api.h // 在 ble_app_init() 中做配置 esp_ble_adv_params_t adv_params { .adv_int_min 0x20, // 32 * 0.625ms 20ms .adv_int_max 0x20, // 保持和 min 一致方便观察 .adv_type ADV_TYPE_IND, .own_addr_type BLE_ADDR_TYPE_PUBLIC, .channel_map ADV_CHNL_ALL, .adv_filter_policy ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; esp_ble_gap_config_adv_params(adv_params); // 设置发射功率为 9dBm esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);注意广播包中所有数据必须一次性打包成一个连续 buffer 供esp_ble_gap_config_adv_data使用官方示例在ibeacon_demo.c里有一个set_ibeacon的辅助函数把esp_ble_ibeacon_t结构体转换成匹配广播协议栈的字节序列。如果你要自己改结构务必记得把结构体的字节序和广播包的排列对齐Intel 小端模式下 Major、Minor 这些字段要手动做字节序转换否则手机上扫出来数值是反的。4. ESP32 扫描 beacon 与 RSSI 测距实现4.1 BLE 扫描器配置与广播包解析测距的核心是作为接收端的 ESP32 要能扫描到蓝牙广播包并读出每个包的 RSSI 值和厂商数据内容。ESP-IDF 里 BLE 扫描的启动非常简单但有几个参数会影响测距效果必须认真设置。初始化 BLE 后注册 GAP 回调然后设置扫描参数并开始扫描static esp_ble_scan_params_t ble_scan_params { .scan_type BLE_SCAN_TYPE_ACTIVE, // 主动扫描可拿到额外响应数据 .own_addr_type BLE_ADDR_TYPE_PUBLIC, .scan_filter_policy BLE_SCAN_FILTER_ALLOW_ALL, .scan_interval 0x50, // 80 * 0.625ms 50ms .scan_window 0x30, // 30 * 0.625ms 18.75ms .scan_duplicate BLE_SCAN_DUPLICATE_DISABLE // 关闭去重每个广播都回调 }; esp_ble_gap_set_scan_params(ble_scan_params);重点说scan_duplicate这个参数。系统默认会开启去重同一个设备的广播包在去重周期内只上报一次这对普通扫描场景是好事但对测距是灾难因为 RSSI 需要连续多次采样才能滤波。实测中我通常把它设为BLE_SCAN_DUPLICATE_DISABLE这样每个广播包都会触发回调RSSI 采样密度足够滤波效果才上得来。代价是回调频率高CPU 占用上升这在后面低功耗优化里再讨论。在 GAP 回调中扫描结果的 event 是ESP_GAP_BLE_SCAN_RESULT_EVT拿到scan_rst里包含bda设备地址、rssi、adv_data和adv_data_len。iBeacon 的过滤逻辑就是查找adv_data中是否包含 0x02 0x15 这个标志序列以及厂商 ID 是否是 0x004C。找到之后从偏移位置取出 UUID、Major、Minor 和 TX Power。代码思路如下case ESP_GAP_BLE_SCAN_RESULT_EVT: { esp_ble_gap_cb_param_t *param (esp_ble_gap_cb_param_t *)arg; if (param-scan_rst.search_evt ESP_GAP_BLE_SCAN_RESULT_EVT) { uint8_t *adv param-scan_rst.adv_data; uint8_t len param-scan_rst.adv_data_len; int rssi param-scan_rst.rssi; // 自行实现查找 0x02 0x15 的逻辑并提取字段 if (is_ibeacon(adv, len)) { // 记录到滑动窗口缓冲区交给滤波和距离换算 } } break; }这里特别提醒一个坑adv_data指针指向的数据在回调返回后就失效了如果要把广播数据缓存到自己的结构体里做后续处理必须用memcpy复制一份不能直接保存指针。否则回调返回后你拿到的可能是被覆盖或随机的内容。4.2 RSSI 转距离的原理与参数校准方法RSSI 换算距离的经典模型是数理统计里的对数距离路径损耗模型公式是RSSI A - 10 * n * lg(d)反解出距离d 10 ^ ((A - RSSI) / (10 * n))其中A距离 1 米时测得的 RSSI 绝对值负值比如 -59相当于 iBeacon 里的 TX Power。n路径损耗指数自由空间约 2室内有遮挡时 2.5 到 4 不等。d估算距离单位米。为什么距离公式里必须预先知道 A 和 n因为无线信号在现实环境里不是理想自由传播墙壁反射、人体吸收、多径衰落都会让同样的距离呈现出完全不同的 RSSI 值。A 回答了“刚开始有多强”n 回答了“衰减有多快”。这两个参数如果不经过实测校准直接拍脑袋填测距结果可能偏差五米以上。我在室内走廊做的一组实测数据很能说明问题ESP32 做接收端iPhone 做发射端实际距离米第一次 RSSI第二次第三次平均值1-57-60-58-58.32-65-68-63-65.33-71-76-69-72.05-83-85-80-82.78-93-90-97-93.3如果是理想环境n2A-595 米处理论 RSSI 应该是约 -73但实测是 -82 到 -85差了快 10 个 dB按公式一算直接成了 9 到 12 米。所以强调一句任何抛开现场校准的 RSSI 测距都是玩具。初版实现可以先用 A-59、n2 跑通流程但要认真用的话必须做一次校准。校准方法很简单在目标环境里拉一条卷尺从 1 米开始每隔 0.5 米或 1 米采集几十个 RSSI 样本求均值然后用最小二乘把 A 和 n 拟合出来再把参数回填。具体拟合方法我在第 5 章展开。4.3 滤波处理滑动平均和简易卡尔曼就算校准做好单次 RSSI 采样依然跳得厉害直接算距离就会看到数值上下乱蹦。所以实际工程里必须加滤波。最常用的两个第一个是滑动平均。维护一个 N 个采样的环形缓冲区每来一个新值取平均值作为当前 RSSI 观测量。N 太小滤波效果差N 太大会导致距离变化响应迟钝。我的经验是小范围静态测距 N 取 5 到 8移动目标跟踪N 取 3 到 5不然目标都走过去了显示的距离还没跟上。第二个是简易一维卡尔曼滤波。如果嫌滑动平均太“钝”可以试试这个思路。卡尔曼本质上是在“预测值”和“观测值”之间做一个带权重的折中权重由系统噪声和测量噪声共同决定。对于 RSSI 这种随机噪声占主导的信号效果通常优于滑动平均。一个极简的卡尔曼实现如下typedef struct { float state; // 当前估计值 float cov; // 估计误差协方差 float q; // 过程噪声 float r; // 测量噪声 } kalman_1d_t; float kalman_filter(kalman_1d_t *kf, float measurement) { // 预测 float pred_cov kf-cov kf-q; // 更新 float gain pred_cov / (pred_cov kf-r); kf-state kf-state gain * (measurement - kf-state); kf-cov (1.0f - gain) * pred_cov; return kf-state; }参数调节技巧如果发现滤波结果跟着原始数据抖得很厉害说明测量噪声 r 设得太小如果距离真实移动时反应慢半拍说明过程噪声 q 设太小。默认可以取 q0.05、r2.0 作为起点再根据波形微调。我实际项目的做法是滑动平均加卡尔曼串起来先用滑动平均去掉高频毛刺再把平滑结果喂给卡尔曼整体曲线既平滑又不至于反应太慢。代价是多一点 CPU但 ESP32 双核 240MHz 完全跑得动。5. 实测调试记录与误差标定5.1 实测环境与数据采集过程为了让大家对 beacon 测距有直观认识我特意搭了一套实测环境ESP32 开发板放在走廊一端固定位置用 USB 供电另一端用另一块 ESP32 作为可移动 beacon 发射端沿走廊每隔 1 米采集 100 个 RSSI 样本分别记录均值和标准差。走廊宽度约 2 米两侧有金属门和墙面属于典型的室内非理想环境。采集程序里我开了 4.1 节说的扫描参数不做滤波原始 RSSI 直接通过串口打印出来用 Python 脚本收集整理。这里要提醒的是如果串口波特率太低大量广播回调会导致打印数据丢失建议串口波特率设为 115200 或 2000000并且适当降低广播频率500ms 间隔来缓解数据洪峰。100 个样本统计下来几个关键位置的数据是这样的距离米RSSI 均值标准差最小最大1-58.22.1-54-652-66.42.5-61-723-72.83.2-67-805-83.54.1-76-958-92.75.7-85-104能看到一个规律距离越远标准差越大这正好对应着 log 模型里距离和 RSSI 的非线性关系。0~3 米内的测距区分度很好超过 5 米后 RSSI 波动加剧距离分辨率明显下降。这个问题不是 ESP32 特有的所有 RSSI 测距方案都逃不掉。5.2 标定流程把实测数据拟合成可用参数拿到一组 (RSSI, d) 数据之后需要反推出 A 和 n。手工算比较麻烦我一般用 Python 的 numpy polyfit 或 scipy.optimize.curve_fit 来做最小二乘拟合。原理就是让模型预测值和实测值的误差平方和最小。上面那组数据拟合的结果大致是 A ≈ -58.5n ≈ 2.35。把它回代到代码里做个效果验证实际距离米用默认参数估算用校准参数估算11.11.022.32.133.93.257.85.4813.58.7校准后误差从动不动几米缩到多数时候一米以内近距离误差基本在半米内。这个精度做防丢提醒、区域判定足够了做高精度定位就得叠加多个锚点做三边测量。三边测量的思路是用至少三个已知坐标的接收端或 beacon各自测出到目标的距离然后以各自坐标为中心画三个圆圈交点就是目标位置。考虑到 RSSI 有噪声实际用最小二乘求解而不是纯几何求交。我把代码逻辑放在一个结构体里思路就是用一个目标函数表示“目标点假设坐标与三个距离的吻合度”然后用梯度下降或高斯牛顿迭代优化。这部分如果你项目需要可以继续深耕这一讲先把单点测距做扎实。5.3 提升精度的实用小技巧实测做得多了总结出几个能明显改善精度的土办法第一多天线、多接收点冗余。一个接收点 RSSI 跳得厉害就把接收点布置成阵列取多个点的加权平均。我在一个房间里放过 4 个 ESP32 接收端对同一个 beacon 的测量结果做加权融合误差能再缩小 30% 左右。第二对采样数据做样本剔除。RSSI 偶尔会冒出一个极端值比如瞬间低 20 个 dB这是广播碰撞或遮挡造成的。用滑动窗口内的中位数替代平均值或者把偏离均值超过 2 倍标准差的样本丢掉都能减少偶发毛刺。第三记录环境变化因素。人站在中间、金属门开关、雨水天气都会让 RSSI 产生几 dB 的偏移。项目上线之后如果要长期稳定需要在代码里做一个自适应背景校准的机制比如定期测量一个已知距离上的 RSSI 均值发现整体漂移就修正 A 参数。6. 实战常见问题与排查技巧实录6.1 扫不到 beacon先把排查顺序理顺实际开发中问得最多的就是“我的 ESP32 怎么扫不到设备”这个问题在 beacon 场景下尤其常见。我建议按照下面这个顺序排查第一确认广播端在正常工作。用手机上的 nRF Connect 先扫一下如果能扫到说明广播没问题问题在接收端如果手机也扫不到回头看广播端的esp_ble_gap_start_advertising是否被调用广播间隔和发射功率设置是否合理。第二检查接收端扫描参数。扫描窗口和扫描间隔如果配得太小比如窗口只有几毫秒很可能刚好错过广播包尤其是广播间隔在 100ms 以上的情况。直接把scan_window调到接近或大于scan_interval的一半能显著提高扫描命中率。代价是功耗上升看项目取舍。第三检查回调里的过滤逻辑。很多同学扫不到是过滤条件写错了比如判断adv_data里某个偏移位置的字节值结果偏移算错或者用大小写敏感的方式比较厂商 ID。调试时最简单的办法是先把所有扫描结果连同原始adv_data以十六进制格式打印出来肉眼看广播数据再反推解析逻辑。6.2 RSSI 跳变严重、测距结果忽远忽近这是 RSSI 测距最让人头疼的问题但大部分原因是没做滤波或者滤波参数不合适。我的经验是按这个优先级排查是否已经关掉系统去重如果没关一秒只能采到一两个样本滤波窗口形同虚设。滑动窗口长度是否合适窗口太短滤波效果不明显太长距离变化时响应太慢。建议先从 N5 起步观察串口输出曲线再调整。广播端是否在移动如果 beacon 本身在移动任何滤波都只能延迟响应需要从算法上做外观测量比如速度估计但这属于另外一个复杂度层级一般不轻易上。另外一个非常容易被忽略的问题回调里的处理耗时过长。BLE 扫描回调是在蓝牙协议栈的任务上下文中执行的如果在里面做了大量日志打印、字符串格式化、甚至动态内存分配会直接拖垮协议栈导致广播包丢失或 RSSI 采样率严重下降。正确做法是回调里只做数据拷贝和标志位更新解析、滤波、算距离全部放到应用任务里做。6.3 蓝牙和 Wi-Fi 共存时的互相干扰ESP32 上 Wi-Fi 和 BLE 共用 2.4GHz 频段和同一天线同时工作会互相抢占资源。实测中开 Wi-Fi 传大文件时BLE 扫描的 RSSI 会明显跳动甚至出现广播包丢失。乐鑫的方案是两者分时复用框架底层会自动做仲裁但优先级分配得不好就会导致 BLE 响应变慢。我的实践建议测距场景尽量避免高吞吐 Wi-Fi 传输同时进行。如果架构上避不开可以把 Wi-Fi 的调制方式和带宽调低比如用 20MHz 频宽减少占用的空口时间或者降低 Wi-Fi 数据上行频率让 BLE 有更多机会跑。另一个方案是换用 ESP32-C3 或 ESP32-C6它们虽然也是单天线但共存调度上比老 ESP32 要顺滑一些。如果项目要求测距和联网并发这个因素在选型阶段就要考虑到。6.4 低功耗优化beacon 模式下把电流压到最低beacon 测距的典型部署场景是电池供电功耗是绕不开的话题。先说广播端beacon 广播端功耗主要由广播间隔决定。间隔 100ms 时ESP32 的 BLE 广播平均电流大概在几十微安到几百微安之间间隔拉到 500ms 以上可以有效降低功耗。若不需要实时响应甚至可以在广播之间进 light sleep定时唤醒发一个包再睡过去平均功耗能做到极低。再说接收扫描端如果接收端不需要 7x24 小时监听可以设置一个占空比方案比如扫描 1 秒、深度睡眠 10 秒这样平均电流能大幅下降。如果要求持续性监测把扫描窗口和间隔同时拉长也有帮助但要注意过长的扫描间隔会导致发现延迟增大。我做过一个低功耗传感器节点beacon 扫描占空比 10%1s 扫描 9s 睡眠平均电流压到了 1mA 以下还能保证 1 到 2 秒内响应目标靠近事件。6.5 几个容易踩的坑速查表把这段时间踩过的坑整理成一张表方便大家排查现象可能原因解决办法编译报错找不到蓝牙头文件CMake 里没加 bluedroid 组件在 CMakeLists.txt 的 REQUIRES 里加上 bt 和 bluedroid手机扫描设备名显示乱码广播包数据排列错误检查字节序和 adv_data 长度逐字节对照协议接收端 RSSI 始终固定不变扫描去重没关闭设置BLE_SCAN_DUPLICATE_DISABLE测距结果整体偏大路径损耗指数 n 设太小按现场校准室内一般 2.2~3.5测距结果整体偏小参考 A 值偏小重新标定 1 米处 RSSI 均值打日志后系统变卡回调里做耗时操作回调内只复制数据任务里处理同时开 Wi-Fi 后 BLE 丢包射频共存调度分时使用或降低 Wi-Fi 空口占用这些坑里最隐蔽的是第一个编译组件问题ESP-IDF 的组件依赖是显式声明的用例程派生工程没关系但自己建工程时漏掉REQUIRES bt bluedroid就会得到一堆找不到头文件的报错。另外提醒一下ESP32 的 NVS flash 分区必须存在蓝牙初始化时协议栈会读取 NVS 里保存的蓝牙配置如果分区表里没配 NVS初始化会直接失败。默认分区表没问题但如果你为了省 flash 改了分区表这一步很容易漏掉。这个系列玩到这我个人最大的体会是beacon 测距看起来只是读几个 RSSI 值套个公式但真要落地到一个产品、一个具体场景核心工作量全在“把不确定的东西变得尽量确定”上——环境标定、参数拟合、滤波调参、共存避让一个都不能省。跳过了这些你拿到的就是一个数字玩具把它们做好了这套测距方案才算是真正从 demo 变成了可用的功能。下一步如果你想接着深入可以试着在这套单点测距基础上做多 beacon 的三边定位再把结果通过上一讲的 MQTT 通道发到云端做位置展示那就是一个完整的室内定位应用原型了。
返回列表