☰
物联网粉尘监测预警系统全栈实现:从传感器到小程序
2026/10/11 6:29:15 网站建设 项目流程

简介:面向物联网方向毕业设计与工程实训的粉尘危害监测预警系统源码包,围绕作业场所粉尘浓度采集、传输、展示与预警闭环设计,适合中高级学习者直接运行练习,也适合作为毕设、课程设计或初期项目立项基础。压缩包共952个文件、约5.07MB,以453个PNG图片、276个JavaScript脚本、82个CSS样式、60个HTML页面和24个JSON配置为主,页面采用layui等前端框架组织界面,另有地图、字体、说明文档、许可证等辅助内容,前端展示与交互逻辑相对完整,便于按目录结构定位和修改。目前已有32人浏览学习,源码经过测试可直接运行。读者可获得可复刻的物联网前端界面、预警展示逻辑与项目组织方式,并能在此基础上扩展传感器数据接入、报警策略或后台管理功能,减少从零搭建的时间成本;相关技术与模块拆分方式对课程设计、工程实训和二次开发都有较高参考价值。

1. 粉尘监测预警系统到底在做什么:一次从0到1的物联网毕设全景

车间里切割、打磨、搅拌一开,扬尘肉眼可见,但大多数中小工厂连一台固定式粉尘检测仪都没有。基于物联网的作业场所粉尘危害监测预警系统,就是把激光粉尘传感器、ESP32/STM32网关、MQTT上报链路、MySQL存储和微信小程序展示串成一条完整链路,浓度超标自动推送预警。它不只是一个单片机点灯项目,而是一套“采集—传输—存储—展示—报警”的闭环,适合正在找物联网毕业设计题目、想展示全栈能力的同学,也适合已经做过云平台Demo、想自己从底层搭一套系统的从业者。整套东西一个学期能做完,预算控制在三百元以内,硬件工程、服务端、小程序和论文都能对应上。

2. 硬件选型与系统架构:传感器、网关和平台怎么搭才不翻车

2.1 粉尘传感器怎么选:GP2Y1010、SDS011、PMS5003 与 SPS30 的取舍

粉尘传感器的选择直接决定数据有没有说服力。最便宜的是夏普 GP2Y1010AU0F,红外散射原理,输出电压模拟量,几十块钱一片,Arduino 教程一大堆,但它有个硬伤:对 0.5μm 以下的颗粒几乎不响应,重复性也一般,适合做原理验证,不适合放在论文里宣称“监测环境粉尘”。想做可靠一点的系统,从 SDS011 或 PMS5003 开始更稳妥,它们都是激光散射原理,串口输出,内置风扇主动进气,能直接给出 PM2.5 和 PM10 数值。

传感器型号输出方式量程适合场景大致成本
GP2Y1010AU0FADC 模拟电压0~0.5 mg/m³原理验证、低成本教学20 元以内
SDS011UART TTL0~999 μg/m³宿舍、办公室、一般车间80 元左右
PMS5003UART TTL0~1000 μg/m³工厂、工地、扬尘监测120 元左右
SPS30I2C/UART0~1000 μg/m³高精度长期监测200 元以上

SDS011 和 PMS5003 的协议格式很接近,都是主动上报和被动查询两种模式,代码可以复用。毕设场景我一般推荐 SDS011,量程够用,数据稳定度在中低浓度下表现不错,而且它内部有标定系数,不需要像 GP2Y1010 那样自己搭光学校准平台。要注意的是,SDS011 需要 5V 供电,3.3V 单片机要用串口电平转换或者选带电平兼容的板子,否则读出来的数据全是乱码。

2.2 网关主控选型:ESP32 还是 STM32+ESP8266,什么时候上 4G 模块

网关是这套系统的“腰”,腰不行,传感器再准也白搭。最常见的两种做法:一是直接用 ESP32,双核、自带 WiFi 和蓝牙、ADC 和串口都够用,Arduino 框架下几行代码就能连 MQTT,非常适合时间紧的毕设;二是 STM32F103 + ESP8266,这是经典的 STM32 物联网网关组合,单片机负责采集和控制,ESP8266 负责透传,论文里能多写一层“单片机+通信模块”的架构图,答辩时也更撑场面。

如果作业场所没有 WiFi 覆盖,比如工地、室外堆场,就要用 4G 模块。4G 物联网模块常见的是 EC20、SIM7600 这类,通过串口 AT 指令拨号上网,然后走 MQTT。很多同学上来就买 4G 模块,结果发现天线没接好、SIM 卡没插紧、电源纹波大,模块要么连不上网要么发烫掉线。我的经验是:能先用 WiFi 调通业务逻辑,再换 4G 模块做现场测试,不然排查问题时会同时面对“通信链路问题”和“业务代码问题”两座大山。

无论选哪条路,网关层都要考虑本地缓存。WiFi 断了几分钟,数据不能直接丢,用一块 MicroSD 卡把记录写成 CSV,等连接恢复再补传,这个功能在论文里是非常加分的可靠性设计。

2.3 平台端怎么搭:MQTT Broker + MySQL + 轻量后端

平台端是整个项目的“黑匣子”,很多同学在这里翻车。最省事的方案是用云厂商的物联网平台,但毕设要的是你能讲清楚数据从哪来到哪去,所以我更推荐本地部署一套:EMQX 或 Mosquitto 做 MQTT Broker,MySQL 8 做数据库,后端用 Python FastAPI 或者 Spring Boot 都行,再加一个微信小程序做展示端。

数据库表不要贪多,两张核心表够了。dust_record存监测原始数据,字段包括设备 ID、PM2.5、PM10、温度、湿度、采集时间;dust_alert存预警记录,字段包括设备 ID、预警级别、触发值、处理状态。后端服务订阅 MQTT 的dust/data主题,收到 JSON 后解析入库,同时跑一个预警判定任务。这套架构的好处是每一层都能单独调试:先用 MQTT 客户端手动发一条假数据,看数据库进没进;再关掉传感器,看预警逻辑能不能正常触发。

3. 数据链路打通:从传感器采集到 MQTT 上报的完整实现

3.1 传感器数据采集:SDS011 串口解析与滑动平均滤波

SDS011 工作在主动上报模式时,每秒吐一帧数据,帧长 10 字节:AA C0 PM25_L PM25_H PM10_L PM10_H 校验位 校验位 FF AB。被动查询模式要发一组指令,返回格式相同。用 ESP32 的 HardwareSerial 接口读取,代码很直接。

// ESP32 + SDS011 串口采集代码(Arduino 框架) #include <HardwareSerial.h> HardwareSerial dustSerial(2); // ESP32 的 UART2,默认 RX=16, TX=17 uint8_t frame[10]; int frameIndex = 0; void setup() { Serial.begin(115200); dustSerial.begin(9600, SERIAL_8N1, 16, 17); // SDS011 固定波特率 9600 } void loop() { while (dustSerial.available()) { uint8_t b = dustSerial.read(); // 按帧头 AA C0 对齐,避免误读 if (frameIndex == 0 && b != 0xAA) continue; if (frameIndex == 1 && b != 0xC0) { frameIndex = 0; continue; } frame[frameIndex++] = b; if (frameIndex == 10) { // 校验字节不匹配则丢弃整帧 if (frame[6] != frame[0] + frame[1] + frame[2] + frame[3] + frame[4] + frame[5]) { frameIndex = 0; continue; } float pm25 = frame[2] | (frame[3] << 8); // PM2.5 低字节在前 float pm10 = frame[4] | (frame[5] << 8); // PM10 低字节在前 Serial.printf("PM2.5=%.1f ug/m3, PM10=%.1f ug/m3\n", pm25 / 10.0, pm10 / 10.0); frameIndex = 0; } } delay(200); // 降低 CPU 占用 }

这段代码的逻辑核心是“按帧头对齐 + 按校验位把关”。SDS011 的校验和是前 6 个字节的累加,用 8 位溢出后的结果跟第 7 字节比对,能过滤掉大部分串口噪声。波特率必须用 9600,SDS011 不支持更高速率。

滤波这一步不能省。原始数据每秒跳一次,波动很大,直接把瞬时值上报会让预警系统反复误报。我一般做“滑动平均 + 中值滤波”:连续取 10 个有效帧,去掉最大值和最小值,剩下 8 个求平均。这个值既跟得上浓度变化趋势,又能吃掉单个异常尖刺。

3.2 数据上云:MQTT 主题设计与 JSON 封装格式

采集到有效数据后,下一步是通过 MQTT 上报到服务端。MQTT 主题设计要有“设备维度 + 数据类型”的概念,不要所有设备都往一个主题里塞。我常用的主题规则是dust/{device_id}/data,预警用dust/{device_id}/alert,这样订阅dust/+/data就能收到全部设备的数据,单设备排错也方便。

// ESP32 MQTT 上报代码(PubSubClient 库) #include <WiFi.h> #include <PubSubClient.h> const char* ssid = "your_wifi"; const char* password = "your_password"; const char* mqttServer = "192.168.1.100"; // 本地 EMQX 或 Mosquitto 地址 const int mqttPort = 1883; WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); void publishDust(float pm25, float pm10, float temp, float humidity) { // 按固定顺序拼接 JSON,避免浮点转字符串的不确定性 String payload = String("{\"device_id\":\"D001\",") + "\"pm25\":" + String(pm25, 1) + "," + "\"pm10\":" + String(pm10, 1) + "," + "\"temp\":" + String(temp, 1) + "," + "\"humidity\":" + String(humidity, 1) + "}"; mqttClient.publish("dust/D001/data", payload.c_str(), true); // QoS 1 + retain }

这里两个参数要特别说明。true表示 retain 标志,Broker 会保留这条消息,新订阅的客户端上线立刻拿到最新值,而不是干等下一帧数据,对小程序首屏加载很友好。QoS 选 1,保证消息至少到达一次,避免预警数据在链路中丢失。

3.3 服务端落库:Python 订阅 MQTT 并写入 MySQL

服务端用 Python 的paho-mqtt订阅主题,收到 JSON 后解析入库。这里有个容易被忽略的点:MottT 回调函数里不要直接做重活,比如查询或复杂校验,只做数据清洗和人库,否则消息积压会越来越大。

# 服务端 MQTT 订阅 + MySQL 写入(Python + paho-mqtt + mysql-connector) import json import mysql.connector import paho.mqtt.client as mqtt conn = mysql.connector.connect( host="127.0.0.1", user="dust", password="dust_pass", database="dust_db" ) cursor = conn.cursor() def on_message(client, userdata, msg): try: data = json.loads(msg.payload.decode()) cursor.execute( "INSERT INTO dust_record(device_id, pm25, pm10, temp, humidity) " "VALUES(%s, %s, %s, %s, %s)", (data["device_id"], data["pm25"], data["pm10"], data["temp"], data["humidity"]) ) conn.commit() except Exception as e: print("入库失败:", e) # 不要吞异常,留着日志排查 client = mqtt.Client() client.on_message = on_message client.connect("127.0.0.1", 1883) client.subscribe("dust/+/data") client.loop_forever()

这段代码要注意的是数据库连接是长连接,不要每条消息都新建连接。MySQL 默认wait_timeout是 8 小时,长时间空闲后连接可能被服务端断开,程序启动后如果报MySQL Connection not available,就在on_message里加一个重连判断。另外client.loop_forever()是阻塞式的,如果后续要同时跑预警检测,需要用loop_start()起后台线程。

4. 预警逻辑与可视化端:从阈值判定到微信小程序展示

4.1 预警判定逻辑:三级阈值为什么要用“连续 N 次”而不是单点触发

预警是这个系统的灵魂,但也是最容易做假的地方。很多毕设的预警就是 ifpm25 > 100弹个提示,实际生产中浓度波动很大,单次超阈值会频繁误报。参考 GBZ 2.1 的思路,预警应该分三级:黄色预警对应短时接触限值的 60%,橙色对应 80%,红色直接对标短时接触限值,并且用“连续 3 个采样点超阈值”或“3 分钟内均值超阈值”来判定。

# 预警判定逻辑:滑动窗口均值 + 三级阈值 import time from collections import deque LIMITS = {"yellow": 60, "orange": 160, "red": 300} # 单位 μg/m³,按车间标准调整 WINDOW = 60 # 最近 60 条记录,约 5 分钟 records = deque(maxlen=WINDOW) def judge_level(pm25): records.append((time.time(), pm25)) recent = [r[1] for r in records if time.time() - r[0] < 300] # 只取近 5 分钟 avg = sum(recent) / len(recent) if avg >= LIMITS["red"]: return "red" if avg >= LIMITS["orange"]: return "orange" if avg >= LIMITS["yellow"]: return "yellow" return "normal"

预警级别不能只看 PM2.5,还要结合 PM10。作业场所的扬尘大多是粗颗粒,PM10 比 PM2.5 更敏感。实际做法是“PM2.5 和 PM10 任一触发就按高等级输出”,同时把温湿度同步入库,排查超标原因时能看出是不是高湿环境导致的传感器误报。消抖也很关键:颜色从黄色回到正常,要等滑动窗口均值降到阈值的 80% 以下,这能避免浓度在阈值边界震荡时短信轰炸。

4.2 微信小程序展示端:调后端接口渲染图表与预警列表

展示端优先考虑微信小程序,微信小程序天然适合做扫码即看的工具型界面,而且毕设答辩时用小程序演示比 PC 网页更有话题性。小程序不需要自己画图表,用 ec-canvas 封装好的 ECharts 或 ucharts 就行。数据流向是:小程序wx.request请求后端 REST 接口,后端查 MySQL 最近 N 条记录返回 JSON,前端渲染折线图和预警列表。

// 小程序端请求后端接口并渲染(微信小程序 + ECharts) Page({ data: { pm25Data: [], alertList: [] }, onLoad() { this.loadData(); }, loadData() { wx.request({ url: 'http://192.168.1.100:8000/api/dust/recent', method: 'GET', data: { deviceId: 'D001', limit: 100 }, success: (res) => { const records = res.data.records; const pm25Data = records.map(r => r.pm25); const alertList = res.data.alerts; this.setData({ pm25Data, alertList }); this.renderChart(); }, fail: (err) => { wx.showToast({ title: '数据加载失败', icon: 'none' }); } }); }, renderChart() { // 这里用 ec-canvas 初始化折线图,传入 this.data.pm25Data } });

小程序开发的一个常见坑是本地调试时后端跑在电脑上,手机预览小程序访问不到localhost。要么把后端地址改成电脑的局域网 IP,要么用真机调试时勾选“不校验合法域名”。生产环境建议后端接口统一走 HTTPS,但毕设阶段没备案域名时,HTTP + IP 是可行方案,只是小程序上线审核过不了,所以本地和演示用开发者工具即可。

微信小程序端还要处理“空数据”状态。传感器离线或数据库没数据时,页面不能白屏,要显示“设备离线”占位图,预警列表为空时也要有提示。这些细节在答辩演示时很加分,因为它证明你考虑过真实运行场景。

5. 粉尘监测系统常见问题与避坑:5 个必须知道的踩坑点

5.1 湿度过高导致读数虚高

现象:下雨天或车间喷淋开启时,PM2.5 数值突然全线飘红,有时甚至冲到四五百。

原因:激光散射传感器对水汽敏感,高湿环境下,水雾滴和吸湿膨胀的颗粒会被误认为粉尘颗粒,读数虚高。这不是传感器坏了,而是物理原理决定的。

解决:在高湿环境里,对读数做湿度补偿。简单做法是湿度超过 60%RH 时按经验系数压缩读数,并给传感器进气口加装除湿加热装置。论文里要把补偿公式写成拟合函数,不要用固定系数一笔带过。

5.2 网关断电或断网导致数据空白

现象:服务端数据库里某几个时段出现整段缺失,小程序的折线图断崖。

原因:网关只负责实时上报,没有本地缓存。WiFi 断线期间的数据等于彻底丢了,这对监测系统的完整性是致命的。

解决:网关加一块 MicroSD 卡,实时数据先写 CSV,MQTT 重连成功后扫描本地文件按时间顺序补传。补传时每条消息带一个原始时间戳ts,服务端入库时用这个时间戳而不是接收时间,才能保证时序正确。

5.3 预警频繁误报,一会黄色一会红色

现象:预警记录一天几十条,全是瞬时超标的假警报,值班人员直接把消息免打扰了。

原因:单点判定 + 阈值边界抖动。粉尘浓度本来就在波动,某个瞬时尖峰就会触发预警。

解决:改成滑动窗口均值判定,并且加“保持时间”——同一级别持续两分钟才算有效预警。预警恢复也要滞后,等浓度降到阈值 80% 以下才解除。这样预警记录会从一天 50 条降到两三条,可信度完全不同。

5.4 4G 物联网模块发烫、频繁掉线

现象:EC20 模块上电几秒后烫手,网络注册成功但隔几分钟就掉线,甚至完全无法拨号。

原因:大多数 4G 模块峰值电流能达到 2A,直接从 ESP32 的 3.3V 引脚取电必死。天线悬空或者天线接口松动会导致驻波比飙升,模块自我保护式降功率甚至重启。SIM 卡卡座接触不良也会间歇性断网。

解决:4G 模块改用独立的 DC-DC 降压模块(比如 LM2596)从 12V 电源取电,地线要粗,天线必须用 IPEX 转外置棒状天线并拧紧,SIM 卡卡座选自弹式并使用标准卡。上电前先用电流表确认模块待机电流、联网峰值电流和短信/电话唤醒电流都在规格书范围内。

5.5 MySQL 8 的 zip 安装与项目导入常见报错

现象:下载mysql-8.x-winx64.zip解压后执行mysqld提示缺少配置文件或直接闪退;mysql命令提示“不是内部或外部命令”;Eclipse 导入 Spring Boot 后端项目时报invalid zip archive: could not find eocd或failed to copy spatial iop zip。

原因:MySQL 的 zip 包不是解压即用,需要手动初始化数据目录;没配 PATH 环境变量命令当然找不到。invalid zip archive则是下载的依赖 jar 包损坏,通常是下载工具断点续传或者网络丢包导致的。

解决:MySQL zip 安装三步走:解压到纯英文无空格路径,用mysqld --initialize-insecure初始化,再用mysqld --console启动并mysql -u root登录改密码。后端依赖报错时,把本地 Maven 仓库里的对应 jar 删掉重新下载即可。毕设代码包用 zip 分发时也一样——解压路径如果带中文或空格,很多框架的解析器会出问题,统一规范到全英文路径是最省心的解法。

6. 最后一公里:湿度补偿与低功耗补传这两个技巧让系统更抗造

6.1 零点标定和湿度补偿是数据可信度的分水岭

如果你只做一个能跑通的闭环,系统也能毕业,但数据稍微被专业老师一问就站不住。做得更扎实一点的关键是标定。拿到传感器后不过我建议先做零点测试:在确认干净的空气中读一组数据,算出零点偏移量,后续所有读数减掉这个偏移。

湿度补偿我实际使用时会这样处理:湿度低于 60% 不变,60% 到 80% 按线性系数压缩,80% 以上加一个断电除湿保护的逻辑。

def humidity_compensate(pm25_raw, humidity): if humidity <= 60: return pm25_raw if humidity <= 80: # 线性区:湿度越高,压缩系数越小 k = 1.0 - (humidity - 60) * 0.01 return pm25_raw * k # 高湿区间直接按经验值打折,并在日志里标记 humidity_high return pm25_raw * 0.6

这个公式不是一个标准答案,每个传感器在高湿下的表现都不一样,它只是让你知道“补偿”这件事的存在。真正要做的是拿一个计量院校准过的参考仪器和你的设备放在一起测几天,拟合一条属于你自己系统的修正曲线,然后把这个过程写进论文。

6.2 低功耗设计:作业时段连续采样,夜间定时唤醒

工厂车间晚上没人作业,不需要实时连续采集。我习惯用 ESP32 的 deep sleep 模式,白天每分钟采一次,夜间每 30 分钟唤醒一次采一组数据就继续睡。这个改动让整个节点从必须插电变成可以靠 18650 电池撑两天,为无源物联网方向留了一个自然的延伸口子:如果下一版要接环境取能,比如太阳能或微振动发电,采样频率就得进一步降低。

补传逻辑也要配合低功耗改:唤醒后先检查 SD 卡里有没有断网积压的记录,有就优先传完,再传当前帧。我第一版没有补传功能,现场断了一次电,数据库出现整整两小时空窗,导致预警曲线不连续,演示时被问得无话可说。后来把数据记录和补传做进网关固件,哪怕网络断一天,恢复后数据也能完整补齐。

粉尘监测这类系统,做完不是结束,而是要经得起现场拷问。希望这套从选型到补传的思路能帮你少走几条弯路,也希望你真正跑通一次从传感器到小程序的数据闭环后,对物联网的“端—边—云”有手里的实感。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询