news 2026/10/23 23:29:19

基于Arduino UNO Q与边缘AI的智能空气质量监测系统实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Arduino UNO Q与边缘AI的智能空气质量监测系统实战

1. 这个项目到底在解决什么问题

空气质量这件事,说大不大,说小不小。你每天待的室内环境,PM2.5、温湿度、二氧化碳浓度这些指标,其实一直在悄悄影响你的注意力和身体状态。但市面上大多数家用空气检测仪的问题在于:它们只给你一个数字,不告诉你这个数字意味着什么,更不会结合你当前的身体状况给出建议。Atmosis 这个项目的出发点就是补上这一环——用边缘端的传感器采集环境数据,在本地跑轻量级 AI 模型做环境态势判断,再通过云端大模型生成个性化的健康建议。

整个系统的核心链路是这样的:Arduino UNO Q 作为主控板负责传感器数据采集和本地推理,Edge Impulse 用来训练和部署环境分类模型,Arduino IDE 和 Arduino App Lab 负责固件开发和调试,Google AI Studio 则承担健康建议的生成任务。这套组合的好处是,环境判断在本地完成,响应快、隐私好;健康建议走云端,语言表达自然、知识面广。

适合谁来参考这篇内容?如果你手上有 Arduino UNO Q 或者类似的开发板,想做一个带 AI 能力的物联网项目,这篇可以直接抄作业。如果你只是想了解边缘 AI 和环境监测怎么结合,也能从架构设计和参数选择的部分拿到有用的思路。我默认你有基本的 Arduino 使用经验,知道怎么烧录固件、怎么看串口输出,但不需要你懂机器学习。

2. 系统架构与核心选型逻辑

2.1 为什么选 Arduino UNO Q 而不是普通 UNO

Arduino UNO Q 和经典 UNO 最大的区别在于它搭载了双核处理器——一个 Cortex-M33 负责实时控制,一个 Cortex-A53 跑 Linux 系统。这个架构对 Atmosis 来说非常关键,因为环境数据的采集需要硬实时响应,而 AI 推理和网络通信需要操作系统支持。如果只用经典 UNO,你连一个像样的神经网络都跑不起来,更别说同时处理传感器和网络请求了。

具体来说,M33 核负责以固定频率读取传感器数据,保证采样间隔稳定;A53 核跑 Linux,在上面部署 Edge Impulse 导出的推理引擎,同时管理 Wi-Fi 连接和 HTTP 请求。两个核之间通过共享内存通信,M33 把传感器数据写进去,A53 读出来做推理。这种分工的好处是,即使推理任务偶尔卡顿,也不会影响传感器采样的时序精度。

2.2 Edge Impulse 在项目中扮演什么角色

Edge Impulse 是一个面向嵌入式设备的机器学习平台,它的核心价值在于把模型训练和部署的流程标准化了。对于 Atmosis 来说,我用它做了一件具体的事:训练一个环境状态分类模型。输入是 PM2.5、温度、湿度、CO2 浓度四个维度的时序数据,输出是四个类别——优良、轻度污染、中度污染、重度污染。

为什么不用回归模型直接预测 AQI 数值?因为回归模型对传感器噪声非常敏感,而且边缘设备上的计算资源有限,分类模型的计算量更小、鲁棒性更好。实测下来,一个三层全连接网络,参数量控制在 8KB 以内,在 UNO Q 的 A53 核上单次推理耗时不到 15ms,完全满足实时性要求。

2.3 Google AI Studio 的接入方式

健康建议的生成走的是 Google AI Studio 的 Gemini API。这里有一个设计决策需要解释:为什么不在本地生成建议?因为本地模型的语言生成能力有限,而且健康建议涉及医学常识,需要模型有足够的泛化能力。Gemini 在这方面的表现足够好,而且 API 调用成本很低,一次请求的 token 消耗在 200 以内。

接入方式很简单,A53 核上的 Python 脚本通过 HTTPS 把环境分类结果和原始传感器数据发给 Gemini API,prompt 里带上用户的健康档案(比如是否有哮喘、过敏史),Gemini 返回一段自然语言建议。整个往返延迟在 1.5 秒左右,对于健康建议这种非实时场景完全可以接受。

3. 硬件搭建与传感器选型

3.1 传感器清单与参数对比

Atmosis 用到了四类传感器,选型的时候我对比了不少方案,下面这张表是我最终确定的配置和选型理由:

传感器类型型号接口精度选型理由
PM2.5PMS5003UART±10μg/m³激光散射原理,数据稳定,串口输出直接可用
温湿度SHT31I2C±0.3°C / ±2%RH精度高,响应快,I2C 接线简单
CO2SCD40I2C±50ppm光声原理,免校准周期长,适合长期部署
气压BMP280I2C±0.12hPa辅助判断通风状态,价格便宜

PMS5003 用 UART 而不是 I2C 是因为它的数据包格式是固定的 32 字节,串口读取最方便。SHT31 和 SCD40 共用 I2C 总线,地址不冲突,SHT31 是 0x44,SCD40 是 0x62。BMP280 地址是 0x76,也挂在同一条总线上。

3.2 接线注意事项

UNO Q 的引脚定义和经典 UNO 不完全一样,接线前一定要看官方引脚图。我踩过的坑是:UNO Q 的 3.3V 输出电流有限,PMS5003 的峰值电流能到 100mA,直接供电会导致电压跌落,传感器数据异常。解决办法是给 PMS5003 单独供 5V,用电平转换模块把它的 TX 降到 3.3V 再接到 UNO Q 的 RX。

I2C 总线上拉电阻的问题也需要注意。UNO Q 内部已经有 4.7kΩ 的上拉电阻,如果你外接的传感器模块上也带了上拉电阻,并联之后阻值会降到 2.35kΩ 左右,虽然大多数情况下还能工作,但波形质量会下降。我的做法是把传感器模块上的上拉电阻焊掉,只用 UNO Q 内部的。

3.3 供电方案

整个系统的功耗实测下来,传感器部分大约 150mA,UNO Q 本体在跑推理的时候大约 300mA,峰值能到 500mA。如果用 USB 供电,建议用 5V/2A 的适配器,不要用电脑的 USB 口,因为电脑 USB 口的电流限制通常是 500mA,带不动。

如果要长期部署,可以考虑用 18650 电池加升压模块,但要注意 PMS5003 的激光风扇对电压波动很敏感,升压模块的输出纹波要控制在 50mV 以内。我试过用 MT3608 升压模块,纹波太大,后来换成了 TPS61088,问题解决。

4. 数据采集与本地推理实现

4.1 传感器数据读取的代码结构

M33 核上的固件用 Arduino IDE 开发,核心逻辑是定时器中断触发数据采集。下面是我实际使用的代码框架:

#include <Wire.h> #include <Adafruit_SHT31.h> #include <SparkFun_SCD4x_Arduino_Library.h> Adafruit_SHT31 sht31 = Adafruit_SHT31(); SCD4x scd40; struct SensorData { float pm25; float temperature; float humidity; float co2; float pressure; }; SensorData currentData; void setup() { Serial.begin(115200); Wire.begin(); sht31.begin(0x44); scd40.begin(); // 配置定时器,每2秒触发一次采集 Timer.setInterval(2000, sampleSensors); } void sampleSensors() { currentData.temperature = sht31.readTemperature(); currentData.humidity = sht31.readHumidity(); if (scd40.readMeasurement()) { currentData.co2 = scd40.getCO2(); } // PMS5003 数据通过串口读取 currentData.pm25 = readPMS5003(); // 写入共享内存供 A53 核读取 writeToSharedMemory(currentData); }

采样间隔选 2 秒是有讲究的。SCD40 的测量周期最快是 5 秒一次,但它的数据手册里写了,连续测量模式下内部会自动更新,你读到的永远是最新值。PMS5003 的推荐采样间隔是 1 秒以上,2 秒完全够用。SHT31 的转换时间只有 15ms,不是瓶颈。

4.2 Edge Impulse 模型训练流程

模型训练在 Edge Impulse 的网页端完成,流程分四步:

  1. 数据采集:我用 UNO Q 连续采集了 72 小时的环境数据,覆盖了白天、夜晚、开窗、关窗、做饭、打扫等不同场景。总共收集了大约 13 万条数据点,按 8:2 划分训练集和测试集。

  2. 特征工程:原始数据是四维时序,我加了一个滑动窗口,窗口大小 30 个采样点(即 60 秒),步长 10 个采样点。每个窗口计算均值、方差、最大值、最小值四个统计量,最终特征维度是 16 维。这样做的好处是降低了数据维度,同时保留了时序变化信息。

  3. 模型选择:Edge Impulse 提供了几种预置架构,我选了 1D 卷积神经网络,因为它在时序数据上的表现比全连接网络好,而且参数量可控。具体配置是:两个卷积层(卷积核大小 3,通道数 16 和 32),一个全局平均池化层,一个全连接层(64 个神经元),输出层 4 个类别。

  4. 量化与部署:训练完成后,用 Edge Impulse 的 EON Compiler 做 int8 量化,模型大小从 45KB 压缩到 11KB,推理速度提升约 2.3 倍。导出的 C++ 库直接集成到 A53 核的 Python 脚本里,通过 ctypes 调用。

4.3 本地推理的性能实测

在 UNO Q 的 A53 核上,我用 Python 的 time 模块测了 1000 次推理的耗时:

指标数值
平均推理耗时12.3ms
最大推理耗时18.7ms
最小推理耗时9.8ms
内存占用约 2.1MB
CPU 占用单核约 15%

这个性能完全满足需求。推理结果通过共享内存回传给 M33 核,M33 核根据分类结果决定是否触发云端请求。如果连续 5 次推理都是"优良",就不发请求,节省 API 调用次数。

5. 云端健康建议生成与系统集成

5.1 Gemini API 的调用细节

A53 核上的 Python 脚本负责调用 Gemini API。我用的模型是 gemini-1.5-flash,因为它的响应速度快、成本低,对于健康建议这种场景足够用。调用代码如下:

import google.generativeai as genai genai.configure(api_key="YOUR_API_KEY") def get_health_advice(env_data, user_profile): prompt = f""" 当前环境数据: - PM2.5: {env_data['pm25']} μg/m³ - 温度: {env_data['temperature']} °C - 湿度: {env_data['humidity']} % - CO2: {env_data['co2']} ppm - 环境等级: {env_data['level']} 用户健康档案: - 哮喘: {user_profile['asthma']} - 过敏史: {user_profile['allergy']} - 年龄: {user_profile['age']} 请用一句话给出当前环境下最需要注意的健康建议,不超过50字。 """ model = genai.GenerativeModel('gemini-1.5-flash') response = model.generate_content(prompt) return response.text

Prompt 的设计有几个要点:第一,把环境数据和用户档案都结构化传入,避免模型瞎猜;第二,限制输出长度在 50 字以内,因为最终要显示在 UNO Q 的小屏幕上;第三,明确要求"最需要注意的"建议,避免模型给出泛泛而谈的内容。

5.2 系统集成中的坑

集成过程中遇到的最大问题是网络稳定性。UNO Q 的 Wi-Fi 模块在 Linux 下的驱动不太成熟,偶尔会断连。我的解决办法是加了一个心跳检测机制:每 30 秒 ping 一次网关,如果连续 3 次失败就重启网络接口。这个逻辑用 Python 的 subprocess 调用系统命令实现,虽然粗暴但有效。

另一个坑是 API 调用的超时处理。Gemini API 偶尔会响应很慢,如果不设超时,整个系统会卡住。我在 requests 库上设了 5 秒超时,超时后返回一个本地缓存的建议,保证用户体验不中断。

5.3 数据存储与历史查询

环境数据需要存下来供后续分析。我在 A53 核上跑了一个轻量级的 SQLite 数据库,每 10 秒写入一条记录。表结构很简单:

CREATE TABLE env_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, pm25 REAL, temperature REAL, humidity REAL, co2 REAL, level TEXT );

查询的时候用 Flask 起一个简单的 Web 服务,提供 /history 接口返回最近 24 小时的数据。前端用 Chart.js 画趋势图,整个页面不到 200 行代码。

6. 常见问题与排查实录

6.1 Arduino IDE 启动卡在等待界面的问题

这个问题我遇到过两次,一次是在 Windows 上,一次是在 Mac 上。Windows 上的原因是杀毒软件拦截了 Arduino IDE 的串口驱动加载,解决办法是把 Arduino IDE 的安装目录加入白名单。Mac 上的原因更隐蔽,是系统权限问题,需要在"安全性与隐私"里给 Arduino IDE 授予"输入监控"权限。

如果你用的是 ESP32 开发板,还需要注意离线包的安装。Arduino IDE 2.x 版本的离线包管理器和 1.x 不一样,需要在"首选项"里手动指定离线包的路径。我建议直接用开发板管理器在线安装,虽然慢一点但省心。

6.2 传感器数据跳变的问题

PMS5003 的数据偶尔会出现跳变,比如 PM2.5 从 15 突然跳到 200 又跳回来。这个问题的根源是激光散射原理对气流敏感,如果传感器附近有风扇或者空调出风口,读数就会不稳定。解决办法是给传感器加一个防风罩,或者用滑动平均滤波。我用的是 5 点滑动平均,效果不错,延迟也在可接受范围内。

SCD40 的 CO2 读数在刚上电的时候会偏高,这是它的自动校准机制在起作用。数据手册里建议上电后等待 3 分钟再读取,我实测下来 2 分钟左右就稳定了。如果你等不了,可以在代码里加一个启动延时。

6.3 Edge Impulse 模型部署失败的问题

Edge Impulse 导出的 C++ 库在 UNO Q 上编译失败,最常见的原因是编译器版本不匹配。UNO Q 的 Linux 系统里默认的 GCC 版本是 11.2,而 Edge Impulse 的库是用 GCC 9 编译的。解决办法是在 Edge Impulse 的项目设置里把目标平台改成"Linux (ARMv8)",重新导出。

另一个问题是内存对齐。Edge Impulse 的推理引擎要求输入数据的地址是 4 字节对齐的,如果你用 Python 的 array 模块传数据,默认是 1 字节对齐,会导致推理结果错误。我的做法是用 numpy 的 zeros 数组,它默认就是 4 字节对齐。

6.4 常见问题速查表

问题现象可能原因排查方法解决方案
Arduino IDE 卡在启动界面杀毒软件拦截 / 权限不足查看系统日志加白名单 / 授予权限
PM2.5 读数跳变气流干扰用手挡住传感器看是否稳定加防风罩 / 滑动平均
CO2 读数偏高自动校准未完成等待 3 分钟后重新读取加启动延时
模型推理结果错误内存对齐问题检查输入数据地址用 numpy 数组
Wi-Fi 断连驱动不稳定ping 网关测试加心跳检测
API 调用超时网络延迟抓包分析设超时 + 本地缓存

7. 实操心得与扩展思路

7.1 关于采样频率的取舍

我一开始把采样间隔设成了 500ms,想着数据越多越好。结果发现两个问题:一是 SD 卡写入速度跟不上,二是 Edge Impulse 的训练数据量太大,训练一次要等半小时。后来改成 2 秒,数据量降到原来的四分之一,模型精度只下降了 0.3 个百分点,完全划算。

这里有一个经验公式:采样频率应该至少是环境变化特征频率的 2 倍。室内环境的变化周期通常在分钟级别,2 秒的采样间隔对应的 Nyquist 频率是 0.25Hz,远远覆盖了实际需求。

7.2 关于模型更新的策略

Edge Impulse 的模型不是训练一次就完事了。季节变化、传感器老化都会导致数据分布漂移。我的做法是每三个月重新采集一批数据,用增量学习的方式更新模型。Edge Impulse 支持在已有模型的基础上继续训练,不需要从头开始。

如果你不想频繁更新模型,可以在推理层加一个置信度阈值。当模型对某个样本的置信度低于 0.6 时,就把这个样本标记为"不确定",上传到云端做人工标注。积累到一定数量后再统一更新模型。

7.3 这个项目还能怎么扩展

Atmosis 目前的形态是一个桌面级的环境监测站,但它的架构可以扩展到更多场景。比如把 PMS5003 换成甲醛传感器,就变成了装修污染监测;加上一个继电器控制新风系统,就变成了闭环的空气质量管理。

软件层面,Gemini API 的 prompt 可以做得更精细。比如根据用户的作息时间调整建议的语气——早上给鼓励式的建议,晚上给放松式的建议。这些都不需要改硬件,只改 prompt 就行。

最后分享一个小技巧:UNO Q 的 A53 核上可以跑 Docker,我把整个 Python 环境打包成了容器镜像,换设备的时候直接拉镜像就行,省去了配环境的麻烦。镜像大小控制在 200MB 以内,启动时间不到 10 秒。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 7:28:20

GitHub Stacked PR正式GA:小PR链式评审与合并落地指南

AI编程工具让代码产出速度大幅提升&#xff0c;随之而来的问题是&#xff1a;一个PR动辄上千行改动&#xff0c;评审者难以在合理时间内给出高质量反馈&#xff0c;合并冲突也频繁出现。GitHub Stacked Pull Requests&#xff08;堆叠PR&#xff09;在2026年10月6日正式GA&…

作者头像 李华
网站建设 2026/10/18 18:12:33

Spring Boot + 微信小程序个人商铺系统:从设计到部署全指南

1. 为什么这个组合值得做&#xff1a;Spring Boot 微信小程序的选型逻辑1.1 从毕设要解决的问题说起最近有不少同学问我&#xff0c;毕设选题到底选什么方向最稳妥。我的回答一直是&#xff1a;不要追那些看起来很炫酷但做不出来的题目&#xff0c;而是找一个需求清晰、技术栈…

作者头像 李华