ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32-Paxcounter:用WiFi和BLE被动扫描实现低成本客流统计

ESP32-Paxcounter:用WiFi和BLE被动扫描实现低成本客流统计 如果你开了一家店、负责一个展位或者单纯想搞明白会议室一天里到底有多少人进出又不愿意架摄像头、不想碰人脸识别那cyberman54/ESP32-Paxcounter这个开源项目会是个很对胃口的方案。它用一块几十块钱的 ESP32 开发板靠 WiFi 和蓝牙的被动扫描把周围手机、手环发出的无线广播帧统计成一条实时流量曲线。项目基于 ESP-IDF 编写不依赖云平台数据可以走串口、OLED 屏也能通过 LoRaWAN 传到远端服务器。这篇文章适合准备做客流统计、空间占用检测或者环境人数监测的开发者我会从原理、硬件、配置到实际部署一路讲下去顺带把我在现场踩过的坑也交代清楚。1. 先搞清 Paxcounter 的原理它靠什么统计人数1.1 为什么不是摄像头方案而是无线嗅探常见的人数统计方案大概有三类红外对射、摄像头加视觉算法、以及 WiFi 探针。红外对射只能统计单通道穿越两个人并排走就会漏摄像头方案成本高还要考虑隐私和存储WiFi 探针则是很多商场和连锁店已经用过的老办法核心在于手机在后台会不断寻找可连接的 WiFi 热点这个过程会主动向外发送一种叫 Probe Request 的报文里面带有设备自身的 MAC 地址。Paxcounter 的思路就是让 ESP32 当一个迷你版 WiFi 探针同时再把 BLE 蓝牙广播也一起收进来。它不连接任何设备也不解析除了 MAC 和信号强度以外的内容只是在某个信道上监听空气里的广播帧然后对设备 MAC 去重算出这一时刻我看到了多少个无线设备。项目名称里 Paxcounter 直译过来就是乘客计数器最早确实带着公共交通场景的影子不过用在店铺客流、展会人流、工位占用检测上也非常合适。这种方案最大的优点是便宜、安装简单、没有摄像头那种隐私压力。缺点也很明显手机 MAC 随机化会带来计数波动它本质上是一个相对趋势统计器不是航空级精确计数器。理解了这一点后面调参和看数据的时候就不会心态崩坏。1.2 ESP32 如何实现被动扫描ESP32 的 WiFi 芯片可以被配置成混杂模式promiscuous mode意思是网卡不管收到的帧是不是发给自己的只要在同一个信道里就全部接住。Paxcounter 在 ESP-IDF 里调用esp_wifi_set_promiscuous(true)然后注册一个 sniffer 回调函数每收到一帧就解析出 IEEE 802.11 MAC 头里的发送者地址和信号强度 RSSI再丢进计数模块。蓝牙侧用的是 BLE 观察者模式ESP32 的蓝牙控制器会接收周围设备主动广播的 ADV 数据包。现在几乎所有人身上的手机、手表、手环、TWS 耳机都在以不同频率向外广播广播包里同样带 MAC 地址。两侧数据汇合之后会进入一个按时间窗口划分的去重结构同一个时间片内同一个 MAC 只保留一次。输出的时候你拿到的就是类似timestamp, wifi_devices, ble_devices, total这样的结构。这里有个概念容易混淆WiFi 探针和 BLE 广播不是一回事。WiFi 探针是手机主动找网络时发出的而 BLE 广播是设备周期性向外宣告自己存在。前者更依赖手机厂商和当前网络状态后者更像是我在这里的小广告。Paxcounter 把两者都抓下来互补性很强尤其在手机不主动扫 WiFi 的时间段BLE 广播还能继续补数据。1.3 为什么选 ESP32 而不是树莓派或 Arduino很多人会问树莓派也能做嗅探为什么用 ESP32答案其实很直白树莓派贵、功耗高、启动时间长放在店门口长期运行不划算。Arduino Uno 没有 WiFi 也没有蓝牙得外挂一个 ESP8266 或单独的蓝牙模块绕一圈回来还不如直接用 ESP32。ESP32 集成 2.4GHz WiFi 和双模蓝牙官方 ESP-IDF 提供了完整混杂模式接口价格不到一杯咖啡Flash 和 RAM 也足够跑去重逻辑。更关键的是它能耗可控ESP32 可以在扫描间隙进入 light sleep用电池或者太阳能板也有机会支撑长期部署。如果要做远距离数据传输还可以在 SPI 总线上接一片 SX1276 LoRa 模块把统计结果发送到几公里外的网关这也是 Pico 或 Arduino 生态里很少能一口气给你集成完整方案的地方。2. 动手前准备硬件清单与 ESP-IDF 开发环境2.1 硬件选型从 ESP32 开发板到 LoRa/GPS 模块先给一份我在实际项目中验证过的硬件清单。基础款不需要 LoRa只需要一块 ESP32 开发板和一个串口调试器。如果后面要走无线远程上报再考虑扩展模块。部件建议规格用途ESP32 开发板ESP32-WROOM-324MB Flash推荐板载天线或不带天线的型号运行 Paxcounter 主程序LoRa 扩展SX1276/SX1278频率根据当地频段选择 433/470/868/915 MHz远程数据上报OLED 屏SSD13060.96 英寸或 1.3 英寸I2C 接口现场直接看实时人数GPS 模块可选串口输出 NMEA移动场景下的时间同步电池方案18650 锂电池TP4056 充电板LDO 稳压无市电环境部署外置天线2.4GHz 外置棒状天线或玻璃钢天线带 IPEX 转接头提高扫描灵敏度选开发板时特别注意两点一是尽量选板载 USB 转串口芯片为 CP2102 或 CH340 的常见型号驱动好找烧录稳定二是如果计划长期固定安装最好不要用带一堆排针和跳线的裸板直接选带外壳或自己打一个 ABS 盒子更省心。Paxcounter 对 Flash 要求不高4MB 够用但别买那些只有 1MB Flash 的老旧模组编译后固件加上配置分区容易放不下。散热和天线布局很容易被忽略。ESP32 全开扫描时射频前端和 PA 会发热放在密闭金属盒里温度会很高最后导致频繁重启。外壳尽量用塑料或带散热孔的铝盒天线位置要保证辐射方向朝向人流通道。2.2 搭建 ESP-IDF 环境为什么不能用 Arduino IDE很多新手第一次接触这个项目会问能不能用 Arduino IDE 加离线包直接编译答案是不建议而且官方也不支持。Paxcounter 用到了 ESP-IDF 里的 WiFi promiscuous 模式、蓝牙 Controller 的 observer 能力、低功耗管理、以及 LoRaWAN 协议栈这些在 Arduino core 里要么没有封装要么封装得不完整。你用 Arduino IDE 强行改造会遇到大量底层 API 缺失和编译错误最后还是要回到 ESP-IDF。推荐的开发环境是 ESP-IDF v5.x支持 Windows、Linux 和 macOS。拉取和编译流程大致是安装工具链然后idf.py set-target esp32再idf.py build。项目仓库里有完整的子模块依赖拉代码时记得带上--recursive参数否则components下面会缺一堆东西。如果以前没碰过 ESP-IDF建议先跑一个通用的 Hello World 例程确认自己的编译链和开发板通信正常再开始编译 Paxcounter。这样能把环境问题和项目问题分开后面排错会少很多。2.3 编译提速与离线包使用经验ESP-IDF 首次编译要下载工具链和 Python 依赖几 GB 的下载量很常见。Windows 上编译慢多半是杀毒软件实时扫描占用了 IO或者工具链目录放在 OneDrive、同步盘这种会频繁同步的路径下。我的做法是把整个 ESP-IDF 和项目放在纯英文路径的本地 SSD加入杀毒软件白名单然后开启 ccache。如果网络下载不稳定可以尝试在白天网络空闲时段慢慢把依赖拉完或者找国内代码托管平台的镜像。PlatformIO 用户也有离线包方案先在能联网的机器上把platformio/espressif32平台和工具链包预下载好然后复制到目标机器的~/.platformio/packages目录之后离线编译就不会反复卡下载。编译命令可以指定并行度idf.py build -j8会比默认快不少。不过并行度也不是越高越好内存小的机器可能编译到一半 OOM。我的经验是 8GB 内存的笔记本用-j4到-j6比较稳16GB 内存可以用-j8。3. 核心配置逐项拆解让计数器按你的场景工作3.1 配置文件与菜单配置项Paxcounter 把大量参数放到了 Kconfig 里也就是用idf.py menuconfig打开的那个图形配置界面。你可以通过搜索PAXCOUNTER直接把所有相关项过滤出来。重点看这几类WiFi 扫描开关、BLE 扫描开关、扫描周期和休眠周期、RSSI 阈值、去重时间窗口、以及 LoRaWAN 相关配置。不同场景推荐配置差异很大。如果做展会门口的人流趋势统计WiFi 和 BLE 都要开扫描周期短一点5 到 10 秒扫一轮RSSI 阈值设在 -75 dBm 左右。如果做办公室工位占用检测设备离人非常近扫描周期可以长到 30 秒RSSI 阈值提高一些避免把隔着走廊的人也算进来。这里不推荐照搬任何默认参数。先以默认配置编译刷机跑一天看原始数据再做针对性调整。每一次改动只需要重新idf.py build flash monitor整个流程几分钟就能完成调参成本很低。3.2 去重与 RSSI 阈值数值虚高怎么压Paxcounter 统计的是当前哪些设备在附近而不是今天总共有多少人来过。所以你会看到同一个 MAC 在多个时间片里反复出现这没问题因为它每个时间窗口内做了一次去重。但手机厂商这些年推行 MAC 随机化手机在发出 Probe Request 时可能每次都用不同 MAC这就导致同一个真实的人被当成好几个设备。实际解决思路有三个。第一合理设置 RSSI 阈值只统计信号较强的设备过滤掉穿过玻璃墙的路人。第二控制去重时间窗口。窗口太短设备挪动一下就被重复计窗口太长短暂停留的人会被漏掉。第三把 Paxcounter 当趋势指标用不要纠结单次绝对值。我在办公室实测时发现更稳定的做法是统计一个时间段内的平均可见设备数而不是某个秒级瞬间的最大值。我见过不少部署翻车场景都是因为把阈值调得过于激进。RSSI 阈值设在 -90 dBm结果把隔壁店的人和街上路过的车都算进来了数值直接爆表。建议先用默认值跑一天用串口记录原始 RSSI 分布再决定砍到多少。3.3 省电与扫描节奏长期部署的关键如果你打算用电池供电扫描节奏就是整个项目里最需要算计的地方。ESP32 在 WiFi 和蓝牙同时扫描时电流很容易跑到 150 到 200mA连续扫一天18650 电池也撑不了太久。但扫描本身又是 Paxcounter 的灵魂没法省掉所以只能靠间歇性扫描来降功耗。一个常见的节奏是扫描 10 秒休眠 50 秒这样平均电流能降到 20 到 30mA配合 3000mAh 电池理论续航能到几天甚至一周以上具体取决于休眠配置和模块静态功耗。ESP32 在扫描间隙可以进入 light sleep但注意 WiFi 混杂模式和 BLE 观察者模式都不支持深度睡眠所以别指望一次睡到天荒地老。真要超低功耗只能用定时器唤醒醒来扫一阵再睡。市电供电的部署反而要小心过热。连续 24 小时全速扫描模块温度比我预想高不少最后我给开发板加了个小散热片温度和稳定性立刻好了。如果设备安装在户外或者阳光直射的地方最好还是选择外置天线方案把射频部分和发热元件分开。3.4 数据上报方式串口、OLED 与 LoRaWANPaxcounter 的默认输出非常朴素但实用UART 串口以 CSV 格式打印每个统计周期的结果大概包括时间戳、WiFi 设备数、BLE 设备数、总数等字段。你只需要用 USB 线连接电脑或者用另一个单片机/USB 转串口模块去读就能直接在终端看到实时数据。现场需要直接看数字时可以开 OLED 显示。SSD1306 通过 I2C 接到 ESP32配置好引脚之后屏幕上会滚动显示当前计数和累计计数非常适合放在店门口或者展位内侧不需要额外显示器。OLED 的刷新频率不高主要起到现场可视化的作用。远程部署或者数据要进云端的话LoRaWAN 是官方的首选方案。接上 SX1276 后可以在 menuconfig 里填入 OTAA 或 ABP 参数。LoRaWAN 的优点是功耗低、距离远城市环境里到网关几百米到几公里都算正常。缺点是配置比串口麻烦网络里需要网关、服务器和 payload 解析器。我建议前期调试阶段先用串口把计数逻辑跑稳再把 LoRa 加进来不然问题叠加起来你根本分不清是计数出错还是网络链路出错。4. 实操记录从拉取代码到上线部署4.1 拉取项目并配置目标芯片先从仓库拉最新稳定代码注意带上子模块git clone --recursive https://github.com/cyberman54/ESP32-Paxcounter.git cd ESP32-Paxcounter idf.py set-target esp32然后打开配置菜单idf.py menuconfig在菜单里找到 Paxcounter 的配置项按需调整。第一次建议保持所有默认选项只用串口输出等确认硬件没问题再扩展。如果编译报缺少子模块多半是 clone 时漏了--recursive在项目目录里补一句git submodule update --init --recursive即可。4.2 编译、烧录与串口验证配置完成后进入构建循环idf.py build idf.py -p /dev/ttyUSB0 flash monitorWindows 下串口号一般是COM3或COM4Linux 下常见/dev/ttyUSB0。如果开发板进入下载模式失败先按住板上的 BOOT 键再插 USB 或按复位键很多时候能强制进入烧录状态。启动后串口输出会先显示固件版本、设备 MAC 地址和当前配置摘要。正常情况下几秒钟后就开始按设定周期打印计数数据。你可以拿一部手机开关一次 WiFi或者开启蓝牙在开发板附近走动观察数值变化。这里有个心态上的提醒第一次测试数值偏低不代表设备坏了因为手机在屏幕熄灭状态下不一定马上主动扫描。多切换几次手机 WiFi 开关很容易看到计数器响应。4.3 现场部署的摆放与天线细节安装位置直接影响数据质量。我的建议是离地面 1.5 到 2 米面向人流通道周围不要有大型金属物体。如果开发板是 PCB 天线天线方向要尽量对着人流来的方向但不要让整块 PCB 紧贴墙面或者贴着铁架。实测下来同样的板子放在塑料支架上和不加任何固定直接挂在铁皮配电箱旁边计数差异能到 20% 以上信号弱的人的设备直接就被 RSSI 阈值滤掉了。多个 Paxcounter 同时部署时保持设备间距 5 米以上避免同一个人的手机同时被两台设备非常强地看到造成数据冗余。你可能会觉得这又不是冲突但现场做趋势分析时要维护多个点位重复看到会让不同入口的数据相关性变得模糊。最好的方案是每个入口单独一台后台再对总量做归一化和去重处理。5. 常见问题与排查速查表5.1 编译与刷写问题我第一次编译 Paxcounter 时最常碰到的几个报错基本都能归到环境问题。下面这个表可以帮你快速定位。现象常见原因排查与解决idf.py: command not found没有 source 环境脚本在 ESP-IDF 目录执行. ./export.sh或source export.sh编译时找不到头文件子模块缺失git submodule update --init --recursive烧录时报连接超时驱动没装或 BOOT 模式没进入换数据线按住 BOOT 键再烧录Windows 编译极慢杀毒软件扫描工具链目录把 ESP-IDF 和项目加入白名单开启 ccache编译到最后内存不足老版本 ESP-IDF 与项目不匹配切到官方要求的 ESP-IDF v5.x 分支给新手一个额外建议不要使用项目仓库的 master 分支直接跑生产除非你已经很清楚自己在干什么。开发分支可能包含新功能也意味着更频繁的接口变动编译报错会多。找一个稳定的 release 版本按官方文档推荐的方式拉取能少走很多弯路。5.2 计数异常排查如果你发现设备跑起来了但数据一直不对先对照下面几种情况。一直为 0先看启动日志里 WiFi 和 BLE 扫描有没有被开启再确认 RSSI 阈值是不是设得太高。可以在 menuconfig 里把阈值临时调到很低比如 -100 dBm如果还是没有数据就要怀疑天线和射频部分了。数值非常低手机屏幕熄灭且 WiFi 关闭时Paxcounter 能收到的信号本来就少。测试时记得把手机 WiFi 打开或者让手机保持亮屏模拟真实活跃用户。数值明显虚高大概率是 MAC 随机化把同一个人算成了多个人或者 RSSI 阈值太低把外围信号都收进来了。逐步调高 RSSI 阈值配合观察原始数据里的信号强度分布会好很多。数据一直不稳定附近 WiFi/蓝牙干扰严重。ESP32 支持把 WiFi 扫描限定到特定信道比如 1、6、11 这三个常用信道中的一个减少和其他无线路由器的串扰。不要一上来就怀疑 LoRa 或 OLED 模块先把串口输出的底层数据看明白再往上一层排查。5.3 LoRaWAN 入网/数据上云问题LoRaWAN 配置出错是最容易让人抓狂的因为很多参数需要在网关、网络服务器和设备端三处保持一致。最常见的坑包括LoRa 模块频率和当地网关频率不匹配、SPI 引脚接错、OTAA 的 AppEUI/DevEUI/AppKey 填错、以及 payload 格式没有在服务器端做解析。先确认硬件引脚。Paxcounter 默认对 SX1276 的 NSS、SCLK、MOSI、MISO、RST、DIO0 都是有定义和可以在 menuconfig 里改的。如果你用非默认的 LoRa 模块型号务必对照模块原理图逐项检查尤其是 DIO0 中断引脚接错会导致发送事件永远不来。LoRaWAN 入网时建议先用一个已经跑通过的通用 LoRaWAN 示例代码验证模块硬件再切回 Paxcounter。这样可以排除模块本身是坏的还是配置问题。Payload 解析在 TTN 或 ChirpStack 这类服务器上处理时记得使用项目提供的 decoder 函数而不是自己猜测字节序。5.4 排障心得最后聊几句我的实操心得。用 Paxcounter 做采集最忌讳的是数据链路太长才排查。先串口再 OLED最后再上 LoRaWAN这个顺序不要乱。LoRaWAN 一旦加入数据流里多了一段无线传输出错面立刻扩大前期没调稳的话问题会被层层掩盖。我在现场测试时习惯先记录一段十余分钟的原始数据观察信号强度波动再去修改阈值和扫描周期。另一个好用的小技巧是把手机固定放在测试点不动开着屏幕看计数器是否稳定为一个较小的波动值再让人带着手机走动看计数器是否明显上升。通过这种 A/B 测试能快速确认部署方向和参数是否合理。如果你是准备做一个长期运行的部署建议每隔几天拉一次串口日志备份同时记录设备温度和网络情况。这个项目本身很稳但现场环境的复杂度远超实验室定期回头看数据才能在问题扩大之前发现端倪。合规方面Paxcounter 只处理公开广播帧中的 MAC 和信号强度不解析用户数据但公共场所部署时还是要提前告知相关人员给使用者留出知情权。我自己最深的感受是别把它当高精度人口传感器它更擅长告诉你趋势发生了什么变化这已经能解决很多实际问题了。
返回列表