1. 项目背景与核心需求
去年帮朋友检测新房空气质量时,发现市售甲醛检测仪要么价格虚高,要么精度堪忧。作为电子工程专业出身的技术从业者,我决定自制一台成本可控、精度可靠的检测装置。这个项目涉及传感器选型、信号处理、数据可视化等多个技术环节,最终成品实现了0.01mg/m³分辨率,测量范围0-5mg/m³,完全符合GB/T18883-2002标准要求。
甲醛检测的核心难点在于低浓度气体分子的精准捕获。市面千元以下设备多采用半导体传感器,受温湿度影响大。而专业级电化学传感器虽然精度高,但单价就要数百元。经过反复测试验证,我找到了一套平衡成本与性能的解决方案。
2. 硬件系统设计
2.1 传感器选型方案
对比测试了三种主流传感器:
- DART-2(电化学式):响应时间<30s,但单价480元
- ZE08-CH2O(半导体式):单价65元,需温度补偿
- SGP30(金属氧化物):集成温湿度补偿,单价120元
最终选择SGP30作为核心传感器,其特点在于:
- 内置温湿度补偿算法
- I²C数字输出,无需额外ADC
- 典型精度±15%以内
- 预热时间仅15秒
实测发现:半导体传感器在湿度>70%时读数会漂移30%以上,必须配合BME280环境传感器使用才能保证精度。
2.2 信号处理电路设计
传感器输出信号需经过两级处理:
-
前置放大电路:采用TI的INA3221运放,增益设置为100倍
- 供电电压:3.3V
- 偏置电压:1.65V(中点偏置)
- 带宽限制:10Hz低通滤波
-
AD转换模块:ADS1115 16位ADC
- 采样率:860SPS
- 输入范围:±2.048V
- I²C地址:0x48
电路布局需特别注意:
- 传感器与运放距离<3cm
- 电源走线宽度≥0.5mm
- 模拟地与数字地单点连接
3. 软件系统实现
3.1 传感器驱动开发
基于Arduino框架编写传感器驱动:
cpp复制#include <Wire.h>
#include "SGP30.h"
SGP30 sgp;
void setup() {
Serial.begin(9600);
while(!sgp.begin()){
delay(500);
}
sgp.initAirQuality();
}
void loop() {
sgp.measureAirQuality();
Serial.print("HCHO:");
Serial.print(sgp.HCHO);
Serial.println("mg/m3");
delay(1000);
}
关键参数校准方法:
- 在零气环境(活性炭过滤空气)下执行baseline校准
- 使用标准浓度气体(0.5mg/m³)进行跨度校准
- 存储校准系数到EEPROM
3.2 数据可视化方案
采用0.96寸OLED显示实时数据,界面包含:
- 当前浓度值(大号字体)
- 历史曲线(每5秒一个点)
- 安全阈值提示(红色警示条)
通过蓝牙模块(HC-05)可同步数据到手机APP,通信协议如下:
| 字节序 | 内容 | 说明 |
|---|---|---|
| 0 | 0xAA | 帧头 |
| 1 | 0x01 | 数据类型标识 |
| 2-3 | 浓度值 | 小端格式,单位0.01mg |
| 4 | 校验和 | 前4字节异或 |
4. 标定与测试
4.1 标准气体标定流程
-
准备3种标准气体:
- 0.1mg/m³(接近国标限值)
- 1.0mg/m³(中等浓度)
- 3.0mg/m³(高浓度)
-
每个浓度点测试步骤:
- 通气5分钟使浓度稳定
- 记录10组传感器读数
- 计算平均值与标准偏差
-
建立校准曲线:
python复制# 最小二乘法拟合 import numpy as np x = [0.1, 1.0, 3.0] # 标准值 y = [0.12, 1.05, 2.91] # 测量值 coeff = np.polyfit(x, y, 1) print(f"校准公式: y = {coeff[0]:.4f}x + {coeff[1]:.4f}")
4.2 环境对比测试
与万元级专业设备(日本理研FP-31)进行72小时连续监测对比:
| 时间点 | 自制设备读数 | 专业设备读数 | 偏差 |
|---|---|---|---|
| 第1天9:00 | 0.08 | 0.07 | +14% |
| 第1天15:00 | 0.21 | 0.19 | +10.5% |
| 第2天10:00 | 0.05 | 0.05 | 0% |
测试环境:温度25±2℃,湿度50±5%。结果显示在0.05-0.3mg/m³常见区间,误差控制在15%以内。
5. 常见问题解决方案
5.1 传感器响应异常
现象:开机后读数持续为零
- 检查步骤:
- 确认I²C地址是否正确(SGP30默认0x58)
- 测量传感器供电电压(应为3.3±0.1V)
- 用示波器检查SCL/SDA信号波形
根本原因:90%情况是I²C上拉电阻未接(需4.7kΩ上拉)
5.2 读数漂移问题
解决方案:
- 每24小时执行一次baseline校准
- 在程序中添加滑动平均滤波:
cpp复制#define FILTER_LEN 5 float filterBuf[FILTER_LEN]; float movingAverage(float newVal) { static int index = 0; filterBuf[index] = newVal; index = (index + 1) % FILTER_LEN; float sum = 0; for(int i=0; i<FILTER_LEN; i++){ sum += filterBuf[i]; } return sum / FILTER_LEN; }
5.3 功耗优化技巧
通过以下措施使待机电流从25mA降至3.8mA:
- 关闭未使用的单片机外设(ADC、TIMER2等)
- 设置OLED为1Hz刷新率
- 启用传感器休眠模式:
cpp复制void sleepSensor() { Wire.beginTransmission(0x58); Wire.write(0x01); // 休眠命令 Wire.endTransmission(); }
6. 成品组装建议
外壳加工方案:
- 3D打印壳体(PLA材料)
- 壁厚≥2mm
- 进气孔直径3mm,间距10mm
- 预留传感器更换窗口
电路装配要点:
- 传感器安装时避免手指直接接触敏感元件
- 先焊接电源线,再连接信号线
- 使用含银焊锡(如SN96.5/AG3.0)降低接触电阻
实测整机成本控制在180元左右(含外壳),相比同精度商业设备节省80%以上成本。这个项目最让我意外的是SGP30在高温环境下的稳定性——在35℃测试时,其内置的温度补偿算法仍能保持±20%以内的读数精度,这已经远超普通半导体传感器的性能表现。