ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于LORA与STM32的低功耗城市河道水位监控系统毕业设计

基于LORA与STM32的低功耗城市河道水位监控系统毕业设计

1. 项目背景与核心需求拆解

1.1 这个选题到底在做什么

城市河道监控这个词听起来挺大,但落到单片机毕业设计这个层面,本质上就是一套低功耗、远距离、多点位的水文数据采集与回传系统。核心逻辑不复杂:在河道的关键断面放几个采集节点,每个节点用传感器测水位、流速、水温这类基础参数,数据通过无线方式汇总到网关,网关再上传到本地服务器或者云平台,管理人员在后台就能看到实时曲线和报警信息。

LORA在这套系统里扮演的角色是节点到网关之间的无线传输通道。选它而不是WiFi或者蓝牙,原因很直接:河道监控点位通常分布在几百米到几公里的范围内,WiFi覆盖不够,蓝牙更不用谈,4G模块虽然能直接用但每个节点都要插SIM卡,成本和功耗都上去了。LORA的典型通信距离在城区环境能到1到3公里,空旷河道甚至能到5公里以上,而且模块本身功耗极低,配合低功耗单片机可以做到电池供电运行数月,这是它在这个场景下最大的优势。

这个选题适合谁来做?如果你是电子信息、物联网、自动化相关专业的本科生,正在找毕业设计题目,并且希望做出来的东西既有硬件实物又有软件界面,还能写出一份像样的论文,那这个方向是合适的。它涉及的知识面覆盖了传感器驱动、无线通信协议、单片机编程、上位机开发、数据库存储这几个模块,工作量饱满但不会过于偏向某一个方向导致你完全做不动。

1.2 核心需求与技术指标

在动手之前,先把需求理清楚。河道监控系统不是做个demo亮个灯就完事,答辩老师会问你几个关键问题:测什么、测多准、传多远、跑多久、怎么用。这四个问题对应了系统的核心技术指标。

需求维度具体指标说明
采集参数水位、水温、流速(可选)水位是核心,其他根据传感器预算增减
水位精度±1cm以内超声波或压力式传感器均可达到
通信距离单节点到网关1-3km城区环境,有遮挡情况下的保守值
节点功耗电池供电运行≥30天需要低功耗策略配合
数据上传周期可配置,默认5分钟一次平衡实时性与功耗
报警响应超阈值后30秒内推送本地蜂鸣器+后台弹窗
后台功能实时显示、历史查询、报警记录基础三件套

这些指标不是拍脑袋定的,是我在实际做类似项目时反复调整后觉得本科毕设层面既能实现又有说服力的配置。你如果传感器选得好,精度可以更高;如果预算紧张,流速可以先不做,只保留水位和水温,论文里把流速作为后续扩展方向写进去也完全说得过去。

1.3 为什么选LORA而不是其他方案

这个问题答辩一定会被问到,提前想清楚。我把几种常见无线方案在这个场景下的表现列出来对比:

方案通信距离功耗组网难度成本适用性
WiFi50-100m高低低不适合,覆盖太短
蓝牙10-30m中低低完全不适合
Zigbee100-300m中中中需要密集组网,河道场景不现实
4G依赖基站高低高(每节点SIM卡)可用但成本和功耗高
LORA1-5km极低中中最匹配

LORA的核心优势在于它的扩频调制技术,简单说就是用更宽的频带换取更低的信噪比需求,所以它能在信号很弱的情况下依然解调出数据。你可以把它理解成在一个嘈杂的菜市场里,别人都在大声喊话(WiFi),而LORA是用一种特殊的慢速节奏说话,虽然语速慢但对方能在噪音中听清。代价就是传输速率低,LORA典型速率在0.3kbps到37.5kbps之间,但这对于每5分钟发一次几十字节的传感器数据来说完全够用。

2. 系统整体架构与硬件选型

2.1 三层架构设计思路

整套系统我建议按感知层、传输层、应用层三层来组织,这是物联网项目最经典的架构,论文里写起来也清晰。

感知层就是分布在河道各处的采集节点,每个节点包含单片机最小系统、传感器模块、LORA发送模块、电源管理模块。传输层是LORA网关,它负责接收多个节点的数据并转发到上位机,网关可以用单片机+LORA+串口转USB的方案,也可以直接用ESP32做网关再通过WiFi上传。应用层就是跑在电脑或服务器上的后台软件,负责数据存储、可视化、报警判断。

这样分层的好处是每一层可以独立调试。你先调通一个节点能正常读传感器,再调通节点到网关的无线通信,最后调通网关到上位机的数据链路,出问题的时候能快速定位是哪一层的事。我见过很多同学把所有代码写在一起,最后数据传不上来完全不知道是传感器坏了、LORA配置错了还是串口没打开,排查起来非常痛苦。

2.2 单片机选型:STM32还是51

这是毕设最经典的纠结。我的建议很明确:如果时间充裕就选STM32,如果时间紧张或者基础薄弱就选STC51系列。

STM32的优势在于外设丰富、库函数成熟、低功耗模式完善。河道监控节点需要定时唤醒采集、采集完进入休眠,STM32的Stop模式和Standby模式能把功耗压到微安级别,配合LORA模块的休眠,整机平均电流可以做到几百微安,电池续航轻松过一个月。而且STM32的ADC精度和DMA功能在处理传感器数据时更顺手。

51单片机的优势是简单、便宜、资料多。如果你之前只学过51,强行上STM32可能会在环境搭建和库函数理解上卡很久。51也能做低功耗,只是休眠模式比较粗糙,续航可能只有几天到一周,但答辩时如果老师不深究功耗细节,也能过。

具体型号推荐:STM32F103C8T6是最稳妥的选择,资料多、价格低、性能足够。LORA模块推荐正点原子或者安信可的SX1278方案,SPI接口,STM32驱动起来很方便。

2.3 传感器选型与参数计算

水位测量是核心,常见方案有三种:

超声波测距方案:用HC-SR04或者更工业级的超声波模块,安装在河道上方往水面打,通过距离换算水位。优点是非接触、不易被泥沙堵塞,缺点是受温度影响大、水面波动时读数跳动。适合水位变化不大、水面相对平静的场景。

压力式水位传感器:投入水中,通过水压换算水深。优点是精度高、不受水面波动影响,缺点是接触式,长期泡在水里需要考虑防腐蚀和泥沙淤积。适合水质较好、泥沙少的河道。

浮子式水位计:机械结构,浮子随水位升降带动编码器。优点是直观可靠,缺点是机械部件长期使用会磨损,而且安装结构比较复杂。

对于毕设,我推荐超声波方案,因为安装简单、不需要接触水体、调试直观。具体计算:假设超声波模块安装高度距河底H=3m,测得距水面距离d=1.2m,则水位h=H-d=1.8m。超声波模块的测距精度一般在±3mm到±1cm之间,换算成水位精度完全满足±1cm的要求。

水温用DS18B20就够了,单总线接口,精度±0.5℃,防水封装直接扔水里。流速如果要做,可以用霍尔式流速传感器,但价格较高,预算不够可以省略。

2.4 电源方案设计

节点供电有三种选择:市电适配器、太阳能+锂电池、纯锂电池。毕设演示阶段用锂电池最方便,推荐18650锂电池两节并联,容量约5000mAh。

功耗估算:STM32在Stop模式下约10μA,LORA模块休眠约1μA,传感器不工作时断电。工作时STM32运行约20mA,LORA发射瞬间约120mA,传感器约15mA。假设每5分钟工作10秒,则平均电流约为(20+120+15)×10/300 ≈ 5.2mA,加上休眠电流,取6mA。5000mAh电池理论续航约833小时,约34天。实际因为电池自放电和电压转换损耗,打七折约24天,满足≥30天的指标需要优化或者加大电池容量。

注意:LORA发射瞬间电流很大,电池供电时要在模块电源脚并联一个大电容(100μF以上),否则发射时电压跌落可能导致单片机复位。

3. 核心硬件电路与驱动实现

3.1 最小系统与LORA接口电路

STM32F103C8T6最小系统包括晶振电路(8MHz外部晶振+32.768kHz RTC晶振)、复位电路、BOOT选择电路、SWD调试接口。这些是标准电路,照着官方参考设计画就行。

LORA模块(以SX1278为例)与STM32的连接采用SPI接口:

STM32引脚LORA模块引脚功能
PA5SCKSPI时钟
PA6MISO主入从出
PA7MOSI主出从入
PA4NSS片选
PB0RESET复位
PB1DIO0中断输出

SPI配置为模式0,时钟极性低、相位第一个边沿采样,速率不要超过10MHz,SX1278手册推荐在8MHz以下。NSS片选可以用软件控制,初始化时拉高,通信时拉低。

DIO0引脚很重要,它用于指示发送完成或接收完成中断。如果用轮询方式,可以不用中断,但效率低。建议配置DIO0为外部中断,发送完成后触发中断再切换模式,这样单片机不用一直等着。

3.2 传感器驱动要点

超声波模块HC-SR04的驱动逻辑:Trig引脚给至少10μs高电平触发,然后Echo引脚会输出高电平,高电平持续时间就是声波往返时间。距离 = 高电平时间 × 声速 / 2。声速取340m/s,但实际受温度影响,可以用DS18B20测得的温度修正:v = 331.4 + 0.607 × T。

用STM32的定时器输入捕获功能测Echo高电平时间最准。配置TIM2通道1为输入捕获模式,上升沿捕获记录CNT值,然后切换为下降沿捕获再记录CNT值,差值乘以定时器周期就是时间。注意定时器溢出问题,如果距离较远高电平时间可能超过定时器周期,需要处理溢出中断累加计数。

DS18B20的驱动关键是严格的时序。单总线协议对时间要求很苛刻,复位脉冲拉低至少480μs,然后释放,等待15-60μs后检测存在脉冲。写时隙和读时隙都有微秒级要求。建议用延时函数精确控制,并且关中断防止被打断。如果嫌麻烦可以用DHT22代替,但DHT22不防水,需要自己做防水封装。

3.3 低功耗策略实现

低功耗是河道监控节点的生命线,具体做法:

硬件层面:传感器电源用MOS管控制,不采集时完全断电。LORA模块用AT指令进入Sleep模式,电流降到1μA级别。STM32不用的外设时钟全部关闭。

软件层面:主循环结构改为定时器唤醒+任务执行+休眠。用RTC闹钟每5分钟唤醒一次,唤醒后依次给传感器上电、延时稳定、读取数据、通过LORA发送、等待发送完成、关闭所有外设、进入Stop模式。Stop模式下STM32电流约10-20μA,RTC继续运行。

// 简化的低功耗主循环伪代码 int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI_Init(); MX_RTC_Init(); MX_USART_Init(); LORA_Init(); LORA_Sleep(); while (1) { // RTC闹钟唤醒后执行 Sensor_Power_On(); HAL_Delay(100); // 等待传感器稳定 water_level = Ultrasonic_Read(); water_temp = DS18B20_Read(); Sensor_Power_Off(); LORA_Wakeup(); LORA_SendData(water_level, water_temp); HAL_Delay(50); // 等待发送完成 LORA_Sleep(); // 进入Stop模式,等待下次RTC闹钟 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config(); // 唤醒后重新配置时钟 } }

实操心得:STM32从Stop模式唤醒后,系统时钟会切回HSI,需要重新调用SystemClock_Config()把时钟切回HSE,否则串口波特率会不对,LORA配置也会乱。这个坑我踩过,调试了半天才发现是时钟问题。

4. LORA通信协议与组网实现

4.1 LORA参数配置与计算

SX1278的配置参数直接决定了通信距离和抗干扰能力,几个关键参数:

频率:国内常用470MHz频段,具体在470-510MHz之间选。频率越低绕射能力越强,但天线尺寸越大。470MHz在河道场景下比较平衡。

扩频因子SF:取值6-12,SF越大通信距离越远但速率越低。SF=7时速率约5.5kbps,SF=12时约0.3kbps。河道监控数据量小,建议SF=9或10,兼顾距离和速率。

带宽BW:常用125kHz。带宽越大速率越高但灵敏度越低。125kHz是标准选择。

编码率CR:常用4/5,即每4个有效位加1个纠错位。信道条件差可以用4/8增强纠错。

发射功率:SX1278最大20dBm(100mW),实际使用建议17dBm,留有余量且功耗略低。

通信距离估算可以用Friis公式粗略计算,但实际环境复杂得多。我实测下来,城区有建筑遮挡时SF=9、BW=125kHz、17dBm配置下,可靠通信距离约1.5km;空旷河道视线良好时能到3-4km。

4.2 自定义通信协议设计

LORA模块本身只负责物理层传输,数据格式需要自己定义。建议设计一个简单的帧结构:

字节内容说明
00xAA帧头
1节点ID1-255,区分不同采集点
2-3水位高字节单位mm,大端
4-5水位低字节
6-7水温单位0.1℃,有符号
8电池电压单位0.1V
9校验和前面所有字节异或
100x55帧尾

帧长度固定11字节,解析简单。节点ID在烧录时通过Flash存储或者拨码开关设置,保证每个节点唯一。校验和用异或就够了,毕设层面不需要CRC。

网关收到数据后先检查帧头帧尾,再校验异或和,都通过才认为数据有效。无效数据直接丢弃,不做重传请求,因为河道监控对实时性要求不高,丢一帧下次再传就行。

4.3 网关多节点接收处理

网关需要同时接收多个节点的数据,LORA本身不支持多节点同时发射,所以需要时分复用。每个节点分配不同的发送时间片,比如节点1在0分0秒发,节点2在0分10秒发,节点3在0分20秒发,以此类推。网关一直处于接收模式,收到数据后根据节点ID区分来源。

时间同步可以用网关定期广播时间校准帧来实现,但毕设层面可以简化:节点用RTC独立计时,只要各节点RTC误差不大(每天几秒),短时间内不会碰撞。如果碰撞了,下一周期重新错开就行。

网关的STM32配置为LORA接收模式,DIO0中断触发后读取FIFO数据,解析后通过串口发送给上位机。串口波特率建议115200,数据格式为“节点ID,水位,水温,电压\n”,上位机按行解析。

// 网关接收处理伪代码 void LORA_RxCallback(void) { uint8_t buf[32]; uint8_t len = LORA_ReadFIFO(buf); if (buf[0] == 0xAA && buf[len-1] == 0x55) { uint8_t checksum = 0; for (int i = 0; i < len-2; i++) { checksum ^= buf[i]; } if (checksum == buf[len-2]) { uint8_t node_id = buf[1]; int16_t level = (buf[2]<<8) | buf[3]; int16_t temp = (buf[4]<<8) | buf[5]; uint8_t volt = buf[6]; printf("%d,%d,%d,%d\n", node_id, level, temp, volt); } } LORA_EnterRxMode(); // 重新进入接收模式 }

5. 上位机后台与数据可视化

5.1 上位机技术选型

上位机方案有三种常见选择:Qt C++桌面程序、Python PyQt、Web网页。从毕设角度,我推荐Python方案,因为开发快、库丰富、代码量少,而且Python的pyserial、pandas、matplotlib、flask这些库能覆盖串口通信、数据处理、图表绘制、Web服务的全部需求。

如果学校要求必须用C#或者Java,那就用对应的串口库和图表库,逻辑是一样的。核心功能就三块:串口收数据、存数据库、界面显示。

5.2 串口数据接收与解析

Python用pyserial打开串口,设置波特率115200,超时1秒。开一个独立线程循环读取,读到一行就解析。

import serial import threading import sqlite3 from datetime import datetime class SerialReader(threading.Thread): def __init__(self, port, baudrate=115200): super().__init__() self.ser = serial.Serial(port, baudrate, timeout=1) self.running = True self.conn = sqlite3.connect('river_monitor.db') self.cursor = self.conn.cursor() self.cursor.execute(''' CREATE TABLE IF NOT EXISTS sensor_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, node_id INTEGER, water_level INTEGER, water_temp INTEGER, voltage INTEGER, timestamp DATETIME ) ''') self.conn.commit() def run(self): while self.running: try: line = self.ser.readline().decode('utf-8').strip() if line: parts = line.split(',') if len(parts) == 4: node_id = int(parts[0]) level = int(parts[1]) temp = int(parts[2]) volt = int(parts[3]) self.cursor.execute( 'INSERT INTO sensor_data VALUES (NULL,?,?,?,?,?)', (node_id, level, temp, volt, datetime.now()) ) self.conn.commit() # 检查报警条件 if level > 2500: # 水位超过2.5m报警 self.trigger_alarm(node_id, level) except Exception as e: print(f"解析错误: {e}") def trigger_alarm(self, node_id, level): print(f"报警!节点{node_id}水位{level}mm超过阈值")

数据库用SQLite就够了,单文件、免安装、Python内置支持。表结构简单,一个sensor_data表存所有节点的数据,用node_id区分。

5.3 可视化界面实现

用PyQt5或者tkinter做界面,核心是实时曲线+历史查询+报警列表三个区域。

实时曲线用matplotlib的动画功能,每收到新数据就更新曲线。X轴是时间,Y轴是水位,不同节点用不同颜色。历史查询用日历控件选日期,从数据库查对应时间段的数据画出来。报警列表用表格控件,显示报警时间、节点ID、报警值。

import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg class RealTimePlot(FigureCanvasQTAgg): def __init__(self): self.fig, self.ax = plt.subplots(figsize=(8, 4)) super().__init__(self.fig) self.ax.set_xlabel('时间') self.ax.set_ylabel('水位(mm)') self.ax.set_title('实时水位监控') self.lines = {} self.data = {} def update_data(self, node_id, level, timestamp): if node_id not in self.data: self.data[node_id] = {'x': [], 'y': []} line, = self.ax.plot([], [], label=f'节点{node_id}') self.lines[node_id] = line self.data[node_id]['x'].append(timestamp) self.data[node_id]['y'].append(level) # 只保留最近100个点 if len(self.data[node_id]['x']) > 100: self.data[node_id]['x'].pop(0) self.data[node_id]['y'].pop(0) self.lines[node_id].set_data( self.data[node_id]['x'], self.data[node_id]['y'] ) self.ax.relim() self.ax.autoscale_view() self.ax.legend() self.draw()

注意:matplotlib在PyQt里绘图性能一般,数据点多了会卡。建议限制显示点数,或者用pyqtgraph库替代,pyqtgraph专门为实时绘图优化,性能好很多。

6. 常见问题与排查技巧实录

6.1 LORA通信失败排查流程

LORA调不通是最常见的问题,按以下顺序排查:

第一步:检查硬件连接。SPI四根线有没有接反,NSS有没有拉低,RESET有没有正常复位。用万用表量模块电源脚电压,应该在3.3V左右。如果电压偏低,检查电源供电能力,LORA发射瞬间电流大,供电不足会导致模块反复重启。

第二步:读寄存器验证SPI通信。SX1278的版本寄存器地址是0x42,读出来应该是0x12。如果读出来是0x00或者0xFF,说明SPI通信有问题。检查SPI模式(模式0)、时钟速率(先降到1MHz试试)、片选控制。

第三步:检查频率配置。发送和接收的频率必须完全一致,包括频率值、带宽、扩频因子、编码率。有一个不一致就收不到。建议把配置参数打印出来对比。

第四步:检查天线。天线没接或者接错会严重影响距离甚至完全收不到。SX1278的天线接口是IPEX或者弹簧天线,确认焊接良好。天线频率要匹配,470MHz模块用470MHz天线,用433MHz天线效率会下降。

第五步:用示波器看DIO0。发送时DIO0应该有脉冲输出,如果没有说明发送没成功。检查发送函数是否正确调用了TX模式设置和FIFO写入。

6.2 传感器数据异常处理

超声波读数跳动大:水面波动导致回波不稳定。解决方法:连续读5次取中值,或者用滑动平均滤波。另外超声波模块的盲区一般在2-3cm,安装高度要保证水面在盲区之外。

DS18B20读出来一直是85℃:这是DS18B20的默认上电值,说明转换还没完成就读了。DS18B20温度转换需要时间,12位精度下需要750ms。发送转换命令后要延时足够时间再读,或者用寄生电源模式下的等待逻辑。

水位数据偶尔出现极大值:超声波受到干扰或者回波丢失。在代码里加范围判断,水位值超过合理范围(比如0-5m)就丢弃,用上次有效值代替。

6.3 低功耗不达标排查

休眠电流偏大:用万用表μA档测休眠电流,如果超过100μA,检查以下几点:STM32是否真的进入了Stop模式(用调试器看PWR_CR寄存器)、GPIO是否配置为模拟输入(浮空输入会漏电)、LORA是否真的休眠(读寄存器确认)、传感器电源是否完全切断。

唤醒后死机:Stop模式唤醒后时钟切换有问题。确保唤醒后重新配置系统时钟,并且清除唤醒标志位。另外注意看门狗,如果开了独立看门狗,Stop模式下看门狗还在跑,超时会导致复位。可以在休眠前喂狗,或者休眠时关闭看门狗。

电池续航短:实际测量工作电流和休眠电流,重新计算平均功耗。如果工作电流过大,检查LORA发射功率是否设太高、传感器是否一直供电。如果休眠电流正常但续航还是短,可能是电池容量虚标或者自放电大,换好一点的电池。

6.4 上位机常见问题

串口打不开:检查串口号是否正确、是否被其他程序占用、驱动是否安装。Windows下可以在设备管理器看端口列表,Linux下用ls /dev/ttyUSB*查看。

数据解析乱码:波特率不匹配或者数据格式不对。确认单片机串口波特率和上位机一致,数据以换行符结尾。如果用的是中文编码问题,统一用UTF-8。

数据库写入慢:SQLite默认每次插入都提交,数据量大时慢。可以改成批量提交,攒100条一起写。或者用WAL模式提高并发性能。

界面卡顿:串口读取和界面更新在同一个线程会导致卡顿。把串口读取放在独立线程,通过信号槽机制通知界面更新。matplotlib绘图用blit模式减少重绘。

7. 论文写作与答辩准备建议

7.1 论文结构安排

毕设论文一般要求1.5万到2万字,章节安排建议:

第一章绪论,写河道监控的背景意义、国内外现状、本文主要工作。背景部分不要写太多空话,聚焦在“当前河道监控手段的不足”和“LORA技术的适用性”上。

第二章系统总体设计,画系统框图,说明三层架构,给出技术指标。

第三章硬件设计,分模块写单片机最小系统、传感器电路、LORA电路、电源电路。每个模块配原理图和说明。

第四章软件设计,分节点软件和网关软件,画流程图,贴关键代码。

第五章上位机设计,写串口通信、数据库、界面实现。

第六章测试与结果分析,写通信距离测试、功耗测试、数据精度测试,用表格和图表展示结果。

第七章总结与展望,总结完成的工作,提出改进方向。

7.2 答辩常见问题准备

老师常问的问题:为什么选LORA不选其他、通信距离实测多少、功耗怎么算的、数据精度如何保证、系统有什么创新点。提前准备好答案,最好有实测数据支撑。

创新点可以从这几个角度写:低功耗策略的具体实现、自定义通信协议的设计、多节点时分复用的组网方式、上位机的实时可视化。不需要多高大上,把工程细节讲清楚就是本科毕设的水平。

7.3 实物演示注意事项

答辩演示前一定要提前到场调试,检查电池电量、天线连接、串口线、上位机软件。最好准备两套节点,一套演示一套备用。演示时先展示节点采集数据,再展示网关接收,最后展示上位机界面。如果现场无线环境干扰大导致通信不稳定,可以提前录好演示视频备用。

实操心得:答辩现场往往有很多WiFi和蓝牙设备,对LORA有干扰。如果演示时通信距离明显缩短,可以临时把扩频因子调大、速率调低,牺牲速率换距离。另外演示时把节点和网关距离拉近一点,保证稳定演示效果,距离测试数据用之前实测的就行。

整套系统做下来,硬件成本大概在200-400元之间,取决于传感器选型和模块品牌。时间投入方面,如果每天能投入3-4小时,从零开始到完成实物和论文大约需要6-8周。前两周搭硬件和调传感器,中间两周调LORA通信和低功耗,后两周做上位机和写论文,最后留一周测试和准备答辩。这个节奏比较合理,不会前松后紧。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进