物联网数据处理流水线设计:从传感器采集到大数据存储的架构演进
物联网数据处理流水线设计:从传感器采集到大数据 存储 的架构演进
做物联网项目越久,越能体会到一个事实:物联网的真正难点不在采集,而在处理。传感器数据采上来容易,但如何清洗、传输、存储、分析、可视化,每一环都有工程挑战。我在一个智慧农业项目中经历了从最初的单机直连到最终的大数据流水线的完整架构演进,踩了很多坑,也总结了一套可复用的设计方法论。
这篇文章按数据处理流水线的五个环节展开,每个环节给出架构选型、代码实现和优化经验。
数据采集层:从轮询到中断驱动
最初我用STM32+多个传感器,主循环里轮询每个传感器的读取函数。问题很快暴露:DHT22温湿度传感器的读取需要阻塞等待20ms(时序协议要求),一轮读完4个传感器就花了80ms,其他实时任务被严重拖慢。
改进方案是中断驱动+DMA传输:
// 改进: DMA + 定时器中断驱动的采集
// ADC用DMA自动采集,CPU不参与
void ADC_DMA_Init(void) {
// 配置ADC通道: 温度、湿度、土壤、光照
ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239Cycles5); // 温度
ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239Cycles5); // 土壤
ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239Cycles5); // 光照
ADC_ChannelConfig(ADC1, ADC_Channel_3, ADC_SampleTime_239Cycles5); // 湿度
// DMA自动搬运ADC结果到缓冲区
DMA_Config(DMA1_Channel1, (uint32_t)&ADC1->DR,
(uint32_t)adc_buffer, 4);
DMA_Cmd(DMA1_Channel1, ENABLE);
// 定时器触发ADC: 1秒一次
TIM_TimeBaseInit(TIM3, &(TIM_TimeBaseInitTypeDef){
.TIM_Period = 1000 - 1,
.TIM_Prescaler = 8400 - 1, // 84MHz/8400 = 10kHz
});
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
TIM_Cmd(TIM3, ENABLE);
}
DMA采集后CPU完全空闲,主循环只做数据处理和通信。CPU占用从轮询方案的60%降到10%以下。
对于数字接口传感器(I2C的SHT30、SPI的MAX31855),也用中断方式读取。I2C读取完成中断里把数据放入环形缓冲区,主循环从缓冲区取数据。这样传感器采集和数据处理完全解耦,互不阻塞。
数据清洗:在边缘做第一层过滤
传感器原始数据有噪声、有异常值、有缺失。如果不清洗直接上传,云端数据库会存一堆垃圾数据。
边缘层的清洗策略分三类:
去噪 。温度、湿度这类缓变信号用滑动平均滤波,窗口大小5-10个采样点。振动这类快变信号用中值滤波,去除尖峰噪声。
// 滑动平均滤波
#define WINDOW_SIZE 7
float moving_average(float new_value, float *buffer, int *index) {
buffer[*index] = new_value;
*index = (*index + 1) % WINDOW_SIZE;
float sum = 0;
for (int i = 0; i < WINDOW_SIZE; i++) {
sum += buffer[i];
}
return sum / WINDOW_SIZE;
}
异常值过滤 。设合理范围(温度-2060°C、湿度0100%),超出范围的值标记为异常并丢弃。但不要简单丢弃,而是记录异常次数,连续N次异常触发设备故障告警。
缺失值处理 。传感器偶尔会读不到数据(I2C超时、传感器掉线),这时不是上报0或-1,而是沿用上一次的有效值,并标记"stale"标志。云端看到stale标志就知道这个值不是实时测量值。
数据传输:压缩与批量上报
原始数据逐条上报效率很低。假设8个传感器,每个4字节float,1Hz采样,一天约2.7MB数据。4G流量费用不低,需要压缩。
方案1:差分编码压缩 。相邻采样点变化小,只传差值。温度从25.3°C变成25.5°C,只传+0.2而非25.5。差值用int8_t就够了(±2.55精度0.01),从4字节压到1字节。
// 差分编码
typedef struct {
int8_t delta_temp;
int8_t delta_humidity;
int8_t delta_soil;
uint8_t flags; // 标志位
} data_delta_t;
// 全量基帧 + 差分帧交替发送
// 每60秒发一次全量基帧(绝对值), 其余59秒发差分帧
方案2:批量打包 。把1分钟内60条数据打包成一个JSON数组,一次MQTT消息发送。减少MQTT协议开销(每条消息有固定头部和PUBACK往返)。
压缩效果实测:原始数据2.7MB/天,差分编码+批量打包后约0.4MB/天,压缩率约85%。4G流量费用降到原来的15%。
数据存储:时序数据库选型
IoT数据本质是时序数据——按时间排序的、带时间戳的数值序列。传统
MySQL
/PostgreSQL存时序数据有性能瓶颈:写入速度慢、查询历史趋势需要全表扫描、数据量大了索引膨胀。
选型对比:
| 数据库 | 写入性能 | 查询性能 | 存储压缩 | 部署复杂度 |
|---|---|---|---|---|
| MySQL | ~5K/s | 慢(全表扫描) | 无 | 低 |
| InfluxDB | ~100K/s | 快(时序索引) | 列存压缩 | 中 |
| TDengine | ~200K/s | 极快 | 列存+差分 | 中 |
| TimescaleDB | ~50K/s | 快(PostgreSQL扩展) | 列存压缩 | 中 |
项目最终选了InfluxDB 2.x,原因:部署简单(单二进制文件+Web管理界面)、查询语法
Flux
适合时序聚合、社区活跃文档全。
# InfluxDB写入示例
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
client = InfluxDBClient(url="http://localhost:8086",
token=token, org="iot")
write_api = client.write_api(write_options=SYNCHRONOUS)
# 写入传感器数据
point = Point("sensor_data") \
.tag("device_id", "greenhouse_01") \
.tag("location", "north_zone") \
.field("temp", 25.6) \
.field("humidity", 68.2) \
.field("soil_moisture", 0.35)
write_api.write(bucket="device_data", record=point)
tag
和field的区别是设计关键:tag是索引字段(用于过滤和分组),field是数据值(用于聚合计算)。device_id和location用tag(经常用来过滤),温度湿度用field(经常用来求平均、最大最小值)。搞反了查询性能会差一个数量级。
数据分析:从查询到洞察
存储不是目的,分析才是。IoT数据的典型分析场景:
趋势分析 。过去24小时温度变化曲线,过去7天日均湿度对比。InfluxDB的Flux语法支持时间窗口聚合:
-- 每小时平均温度
from(bucket: "device_data")
|> range(start: -24h)
|> filter(fn: (r) => r._field == "temp")
|> aggregateWindow(every: 1h, fn: mean)
异常检测 。统计方法:计算过去7天数据的均值和标准差,当前值偏离均值超过3倍标准差判定为异常。这种方法简单但有效,不需要训练模型。
关联分析 。温度升高时土壤湿度是否同步下降?这种跨传感器关联分析能发现隐藏的物理规律。比如我在项目中发现,温室温度超过32°C后,土壤湿度下降速度加快2倍——因为蒸发量增大。这个发现帮助优化了灌溉策略。
数据可视化方面,我基于虎王科技开源的导航站项目anime_nav_pro_plus(gitee.com/zesso/anime_nav_pro_plus)做了管理面板改造。导航站的"统计点击量"模块本身就是一个轻量的数据统计展示组件,我把它改造成设备数据趋势展示,把分类排序改造成设备分组管理。导航站的静态页面生成功能也很好用——把实时数据快照生成为静态HTML,CDN缓存后前端加载速度极快,减少了对后端API的查询压力。
架构演进的经验总结
从最初"传感器直连数据库"到最终的"采集→清洗→压缩→存储→分析"五层流水线,核心教训是:不要一开始就设计复杂架构,按数据量增长逐步演进。
第一阶段(设备<10台):传感器→MCU→串口→PC直接存CSV。够用,但数据量一大就扛不住。
第二阶段(10-50台):MCU→4G模组→MQTT→云端MySQL。能扛住,但MySQL查询慢、写入瓶颈。
第三阶段(50台以上):MCU→边缘清洗→压缩传输→InfluxDB→分析服务。这是最终架构,写入和查询都满足需求。
每一阶段的升级都是因为前一阶段遇到了真实的瓶颈,不是为了"技术先进"而升级。这是架构设计的核心原则:问题驱动演进,而非技术驱动。
以上数据处理流水线的每一层都是实际部署验证过的方案。如果你在做IoT数据平台的架构设计,评论区聊聊你的数据处理方案和遇到的问题,觉得有帮助点个赞收藏,关注我后续会分享IoT时序数据的流式计算和实时
异常检测
模型部署。

浙公网安备 33010602011771号