资讯动态

用 WiFi 信号数人头:howmanypeoplearearound 项目深度解析

发布时间:2026/8/13 1:53:39 来源:尧图企业网站定制
在不依赖摄像头、蓝牙信标或红外传感器的前提下仅凭一块支持监听模式的 WiFi 适配器就能估算你周围有多少人——这就是 GitHub 上获得 7100 Star 的开源项目howmanypeoplearearound所做的事情。一、项目概述基本信息属性内容仓库地址schollz/howmanypeoplearearoundStar 数7100Fork 数381开发语言Python 2.7 / Python 3许可证MIT版本v0.5.0核心依赖tshark (Wireshark CLI)、click、netifaces、pick核心理念项目的核心思路极其简洁智能手机是人的代理信号。当今约 70% 的人随身携带智能手机而智能手机的 WiFi 模块会周期性地向外发送Probe Request探测请求帧。这些帧用于发现周围的 WiFi 网络即使手机没有连接任何 AP接入点也会持续发出。howmanypeoplearearound 就是利用这一特性通过监听这些 Probe Request 来统计周围出现的不同 MAC 地址数量再结合智能手机普及率换算出估算人数。应用场景监控家庭/办公室的人流密度搭配树莓派判断室友是否在家商铺客流粗略统计任何需要感知附近是否有人但不想部署摄像头的场景二、技术原理WiFi Probe Request 嗅探2.1 什么是 Probe Request在 802.11 协议中WiFi 设备活跃地寻找可用网络时有两种方式被动扫描设备静默等待 AP 定期广播的 Beacon 帧主动扫描设备主动发送 Probe Request 帧AP 收到后回复 Probe Response绝大多数智能手机为了更快发现可用网络会采用主动扫描。即使屏幕熄灭、WiFi 未连接手机仍会以 1~10 分钟为周期发送 Probe Request。这些帧包含了发送方的 MAC 地址这就是项目得以工作的物理基础。2.2 监听模式Monitor Mode普通的 WiFi 适配器工作在 Managed managed模式只接收发往自己的帧。要捕获空气中所有的 802.11 帧包括别人的 Probe Request需要将适配器切换到Monitor Mode监听模式。并非所有 WiFi 芯片都支持监听模式。README 中列出了推荐芯片Atheros AR9271Ralink RT3070Ralink RT3572Ralink RT5572常见的 Alfa 系列AWUS036NHA 等和 Panda 系列PAU5/PAU6/PAU9适配器都基于上述芯片。2.3 tshark抓包引擎项目并不自己实现底层抓包而是借助 Wireshark 的命令行工具tshark。tshark 是业界标准的网络协议分析工具支持将适配器切换到监听模式并捕获原始 802.11 帧。整个抓包流程分为两步捕获阶段tshark -I -i adapter -a duration:seconds -w dump_file-I启用监听模式-i指定网络适配器-a duration设置捕获时长-w将原始数据写入 pcap 文件解析阶段tshark -r dump_file -T fields -e wlan.sa -e wlan.bssid -e radiotap.dbm_antsignal-r读取之前保存的 pcap 文件-T fields以字段格式输出-e wlan.sa提取源 MAC 地址Source Address-e wlan.bssid提取 BSSID-e radiotap.dbm_antsignal提取信号强度 RSSI三、代码结构解析项目代码结构非常精简核心包目录下仅 6 个文件howmanypeoplearearound/ ├── __init__.py # 包初始化空文件 ├── __main__.py # 主程序入口CLI 参数解析 抓包 MAC 分析 人数计算 ├── analysis.py # 数据可视化读取 JSON 日志生成 Plotly HTML 图表 ├── colors.py # 终端 ANSI 颜色常量 ├── oui.py # OUI 数据库加载与下载 └── plotlyjs.py # Plotly.js 可视化模板代码3.1__main__.py核心逻辑这是整个项目最重要的文件包含 CLI 入口和完整的扫描分析逻辑。CLI 参数设计使用click框架定义了丰富的命令行参数click.command()click.option(-a,--adapter,default,helpadapter to use)click.option(-s,--scantime,default60,helptime in seconds to scan)click.option(-o,--out,default,helpoutput cellphone data to file)click.option(-n,--nearby,helponly quantify signals that are nearby (rssi -70),is_flagTrue)click.option(--nocorrection,helpdo not apply correction,is_flagTrue)click.option(--loop,helploop forever,is_flagTrue)click.option(-j,--jsonprint,helpprint JSON of cellphone data,is_flagTrue)click.option(--sort,helpsort cellphone data by distance (rssi),is_flagTrue)click.option(-m,--manufacturers,default,helpread list of known manufacturers from file)click.option(--allmacaddresses,helpdo not check MAC against OUI database,is_flagTrue)click.option(-f,--pcap,helpread a pcap file instead of capturing)# ...关键参数说明参数作用--adapter/-a指定 WiFi 适配器名称--scantime/-s扫描时长秒默认 60--nearby/-n只统计 RSSI -70 的近距离信号--nocorrection不应用 70% 智能手机普及率修正--loop循环扫描模式--out/-o将结果以 JSON 追加写入文件--analyze/-z分析已有 JSON 文件并可视化--pcap/-f直接读取 pcap 文件而非实时抓包--allmacaddresses不过滤 OUI统计所有 MAC 地址--sort按 RSSI距离排序输出scan() 函数核心扫描流程scan()是整个项目的主逻辑函数工作流程如下┌─────────────────────────────────────────────────────┐ │ 1. 加载 OUI 字典若无则自动下载 │ │ └─ oui.py: download_oui() 从 IEEE 官网拉取 │ ├─────────────────────────────────────────────────────┤ │ 2. 检查 tshark 是否安装 │ │ └─ which(tshark) 遍历 PATH 查找可执行文件 │ ├─────────────────────────────────────────────────────┤ │ 3. 选择 WiFi 适配器交互式 pick 或 -a 指定 │ ├─────────────────────────────────────────────────────┤ │ 4. 启动 tshark 抓包监听模式 指定时长 │ │ └─ subprocess.Popen([tshark, -I, -i, ...]) │ │ 同时启动倒计时线程 showTimer() │ ├─────────────────────────────────────────────────────┤ │ 5. 用 tshark 解析 pcap提取字段 │ │ wlan.sa (源MAC) wlan.bssid radiotap.dbm_antsignal │ ├─────────────────────────────────────────────────────┤ │ 6. 逐行解析输出构建 foundMacs 字典 │ │ { MAC: [rssi1, rssi2, ...] } │ │ 对同一 MAC 的多次 RSSI 取平均值 │ ├─────────────────────────────────────────────────────┤ │ 7. OUI 过滤只保留手机厂商的 MAC 地址 │ │ └─ mac[:8] 查 OUI 字典匹配手机厂商列表 │ ├─────────────────────────────────────────────────────┤ │ 8. 人数计算 │ │ num_people len(cellphone_people) / 0.7 │ │ 70% 智能手机普及率修正 │ ├─────────────────────────────────────────────────────┤ │ 9. 输出结果终端 / JSON 文件 / 循环追加 │ └─────────────────────────────────────────────────────┘关键代码片段解读RSSI 聚合——对同一个 MAC 地址的多次信号强度取平均foundMacs{}forlineinoutput.decode(utf-8).split(\n):ifline.strip():continuemacline.split()[0].strip().split(,)[0]datsline.split()iflen(dats)3:if:notindats[0]orlen(dats)!3:continueifmacnotinfoundMacs:foundMacs[mac][]dats_2_splitdats[2].split(,)iflen(dats_2_split)1:# 多天线场景取平均值rssifloat(dats_2_split[0])/2float(dats_2_split[1])/2else:rssifloat(dats_2_split[0])foundMacs[mac].append(rssi)# 对每个 MAC 的所有 RSSI 取平均forkey,valueinfoundMacs.items():foundMacs[key]float(sum(value))/float(len(value))这段代码处理了 tshark 输出中可能出现多天线信号值的情况逗号分隔对同一 MAC 在扫描周期内的多次出现取平均 RSSI作为该设备的信号强度。OUI 过滤——通过 MAC 地址前缀识别手机厂商cellphone[Motorola Mobility LLC, a Lenovo Company,GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD,Huawei Symantec Technologies Co.,Ltd.,Microsoft,HTC Corporation,Samsung Electronics Co.,Ltd,BlackBerry RTS,LG ELECTRONICS INC,Apple, Inc.,OnePlus Tech (Shenzhen) Ltd,Xiaomi Communications Co Ltd,# ...]项目内置了一份手机厂商列表通过 OUIOrganizationally Unique Identifier查询 MAC 地址前 3 字节前 8 位含分隔符对应的厂商名称。只有在手机厂商列表中的 MAC 才会被计入。用户也可以通过-m参数自定义厂商列表文件。人数计算与修正percentage_of_people_with_phones0.7ifnocorrection:percentage_of_people_with_phones1num_peopleint(round(len(cellphone_people)/percentage_of_people_with_phones))默认将检测到的手机数量除以 0.7假设 70% 的人携带智能手机得到估算人数。使用--nocorrection可关闭这一修正直接以手机数量作为人数。3.2oui.pyOUI 数据库管理OUIOrganizationally Unique Identifier是 MAC 地址的前 3 字节由 IEEE 分配并公开维护。该模块负责从 IEEE 官网下载 OUI 数据并解析为字典defload_dictionary(file):oui{}withopen(file,r)asf:forlineinf:if(hex)inline:dataline.split((hex))keydata[0].replace(-,:).lower().strip()companydata[1].strip()oui[key]companyreturnouidefdownload_oui(to_file):urihttp://standards-oui.ieee.org/oui/oui.txtoui_dataurlopen(uri,timeout10).read()withopen(to_file,wb)asoui_file:oui_file.write(oui_data)IEEE 的 oui.txt 文件格式为00-00-00 (hex) Apple, Inc.。代码按(hex)分割将前缀统一为冒号分隔的小写格式作为字典 key厂商名作为 value。首次运行时若本地无此文件会自动从 IEEE 官网下载。3.3analysis.py数据可视化当用户使用--loop模式持续收集数据后可以通过--analyze读取 JSON 日志文件并生成可视化 HTML 页面。分析模块的逻辑逐行读取 JSON 格式的扫描记录每条含cellphones数组和time时间戳按时间排序筛选 RSSI -80 的 MAC 地址作为有效信号为每个 MAC 构建时间序列RSSI 随时间变化生成内嵌 Plotly.js 的 HTML 文件启动本地 HTTP 服务器默认端口 8001提供访问可视化包含两个图表总人数趋势图展示每个扫描周期检测到的手机数量随时间变化个体信号追踪图每个 MAC 地址的 RSSI 时间序列可双击高亮某条轨迹3.4setup.py打包发布entry_points{console_scripts:[howmanypeoplearearound howmanypeoplearearound.__main__:main,]},通过 setuptools 注册为全局命令行工具pip install后即可直接在终端使用howmanypeoplearearound命令。3.5Dockerfile容器化方案FROM python:3 RUN apt-get update \ apt-get upgrade --yes \ DEBIAN_FRONTENDnoninteractive apt-get install -y tshark \ yes | dpkg-reconfigure -f noninteractive wireshark-common \ addgroup wireshark \ usermod -a -G wireshark ${USER:-root} \ newgrp wireshark \ pip install howmanypeoplearearound CMD [ howmanypeoplearearound ]Docker 方案将 tshark 安装、权限配置和 Python 包安装一体化。运行时需要--nethost来共享宿主机网络栈使容器内的 tshark 能直接访问 WiFi 适配器。四、完整使用流程4.1 环境准备# 安装 tshark (Linux)sudoapt-getinstalltshark# 配置非 root 用户可运行sudodpkg-reconfigure wireshark-common# 选择 YESsudousermod-a-Gwireshark${USER:-root}newgrp wireshark# 安装 Python 包pipinstallhowmanypeoplearearoundmacOS 需通过 Homebrew 安装 Wireshark且扫描前需断开当前 WiFi 连接brewinstallwireshark brew caskinstallwireshark-chmodbpfsudo/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport-z4.2 基本使用# 交互式选择适配器扫描 60 秒howmanypeoplearearound# 指定适配器和扫描时长howmanypeoplearearound-awlan1-s120# 只输出人数数字适合脚本调用howmanypeoplearearound--number# 输出 JSON 格式的详细手机数据howmanypeoplearearound-j-awlan1# 只统计近距离信号howmanypeoplearearound-n-awlan1# 循环扫描并持续记录到文件howmanypeoplearearound-otest.json-awlan1--loop# 分析历史数据并可视化howmanypeoplearearound--analyzetest.json4.3 JSON 输出示例[{rssi:-86.0,mac:90:e7:c4:xx:xx:xx,company:HTC Corporation},{rssi:-84.0,mac:80:e6:50:xx:xx:xx,company:Apple, Inc.},{rssi:-49.0,mac:ac:37:43:xx:xx:xx,company:HTC Corporation}]RSSI 值越高表示设备越近注意 RSSI 是负值-49 比 -86 更近。作者提到其中一个 -49 的是自己的手机另两个是室友楼上手机的信号。五、技术选型分析5.1 优势依赖 tshark 而非自研抓包项目没有从零实现 802.11 帧的捕获和解析而是直接调用 tshark。这是一个明智的工程决策tshark 经过数十年打磨协议解析覆盖面广且稳定tshark 支持跨平台Linux/macOS/Windows项目自动获得跨平台能力开发者只需关注业务逻辑MAC 去重、OUI 匹配、人数计算无需处理底层协议细节OUI 过滤策略通过 MAC 地址前缀识别设备厂商只统计手机厂商的 MAC 地址有效排除了笔记本电脑、路由器、IoT 设备等非手机 WiFi 设备的干扰提高了人数估算的准确性。RSSI 多天线均值处理tshark 在多天线适配器上可能返回逗号分隔的多个 RSSI 值。代码对这种情况取平均值体现了对实际抓包场景的考虑。循环模式 可视化--loop--analyze的组合形成了一个轻量级的持续监控系统循环采集数据写入 JSON 日志随后生成交互式 Plotly 图表。这对于长期人流监控非常实用。5.2 局限性MAC 随机化问题这是项目面临的最大技术挑战。自 iOS 82014 年和 Android 62015 年起主流移动操作系统在发送 Probe Request 时默认使用随机化 MAC 地址而非设备的真实 MAC。这意味着同一部手机在不同时间可能使用不同的 MAC 地址OUI 过滤可能失效因为随机化 MAC 不一定使用真实厂商前缀MAC 去重策略导致同一部手机被多次计数项目代码中的 OUI 过滤在 MAC 随机化场景下的效果会大打折扣。虽然--allmacaddresses选项可以绕过 OUI 过滤统计所有 MAC但这又引入了非手机设备的噪声。70% 修正系数的粗放性percentage_of_people_with_phones0.7这个固定值来自 2016 年的一条 Twitter 数据针对的是美国/加拿大的智能手机普及率。在不同地区、不同人群如儿童比例较高的场所、不同时间段这个值可能差异很大。扫描窗口的局限性README 提到手机发送 Probe Request 的周期为 1~10 分钟。默认 60 秒的扫描时长可能错过部分处于休眠周期的设备导致低估人数。延长扫描时间可以提高覆盖率但实时性会下降。无去重机制--loop模式下每次扫描独立计算同一部手机在不同扫描周期中被重复计数。虽然 MAC 地址可用于跨周期去重但当前代码并未实现这一逻辑各周期的cellphone_people列表是独立的。OUI 数据的时效性OUI 数据库在首次运行时从 IEEE 官网下载但之后不会自动更新。新厂商的新 OUI 前缀不会自动纳入可能漏检较新的手机品牌。不过用户可以手动删除oui.txt触发重新下载。5.3 安全与法律风险README 中明确标注了法律警告It may be illegal to monitor networks for MAC addresses, especially on networks that you do not own. Please check your country’s laws.在美国监听网络通信可能触犯 18 U.S.C. § 2511联邦窃听法。即使在自己拥有的网络上操作也应注意隐私合规。在许多国家和地区未经授权嗅探 WiFi 流量可能构成违法行为。六、架构亮点与不足总结架构亮点设计决策价值tshark 作为抓包后端避免重复造轮子获得工业级协议解析能力OUI 厂商过滤有效区分手机与其他 WiFi 设备Click CLI 框架参数设计清晰支持丰富的使用模式JSON 日志 Plotly 可视化轻量级数据持久化与可视化方案Docker 容器化降低环境配置门槛多天线 RSSI 均值针对真实抓包场景的细节处理架构不足问题影响MAC 随机化未处理核心准确性受现代手机系统影响70% 修正系数固定不适配不同地区和人群循环模式无跨周期去重长期监控场景精度不足OUI 数据不自动更新可能漏检新厂商设备串行 subprocess 调用抓包与解析分两步中间存在 I/O 开销错误处理较粗放tshark 异常、适配器问题等场景处理不够健壮七、适用场景与改进建议适用场景低精度人流感知如判断办公室是否有人、家中室友是否回来等定性场景树莓派 DIY 项目低成本、低功耗的客流监测方案安全研究与教学理解 802.11 协议和 WiFi 嗅探原理的实践项目原型验证在投入更复杂的传感器方案前快速验证人流感知的可行性改进建议应对 MAC 随机化可以研究 Probe Request 帧中的其他特征如序列号 Sequence Number 的连续性、帧间间隔模式来识别同一设备而非仅依赖 MAC 地址。学术界已有 OUI-based 与 sequence-number-based 的去随机化研究。动态修正系数允许用户根据所在地区/场景配置智能手机普及率或提供常见地区的预设值。跨周期去重在--loop模式下维护一个带 TTL过期时间的 MAC 缓存避免同一设备被多次计入。需结合 MAC 随机化策略使用。实时流式处理当前方案是先抓包再解析的批量模式。可以改为流式处理 tshark 输出实时更新计数降低延迟。信号强度距离估算利用 RSSI 进行粗略的距离估算path-loss 模型结合--nearby选项提供更灵活的 proximity 感知。OUI 数据自动更新添加版本检查机制定期刷新 OUI 数据库。HTTPS 下载 OUIoui.py中使用的是http://而非https://下载 OUI 数据存在中间人攻击风险。八、结语howmanypeoplearearound 是一个小而美的项目。它用不到 300 行核心代码巧妙地将 WiFi 协议特性、OUI 数据库和统计修正组合在一起实现了数人头这一看似复杂的任务。项目最大的价值不在于精度而在于展示了一种极简的感知思路不需要摄像头、不需要蓝牙信标一块 WiFi 适配器就够了。然而随着现代移动操作系统普遍采用 MAC 随机化项目面临的核心挑战日益严峻。这也反映了技术演进中的一个普遍现象基于特定时代技术特征的方案会随着底层技术的变化而逐渐失效。项目作者也提到了它的前身 find-lf一个使用树莓派集群进行室内定位的更复杂方案——howmanypeoplearearound 正是 find-lf 的简化版。对于想深入理解 802.11 协议、WiFi 嗅探和被动感知技术的开发者来说这个项目是一个极佳的学习起点。代码简洁、依赖清晰、原理明确非常适合作为从零到一的实践教材。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价