基于STM32的智能输液监护与PID闭环调控系统设计与实现
2026/9/20 11:50:27 网站建设 项目流程

从毕业设计开始的“标配”到后来医院场景里真正被需要的设备,智能输液监护这个方向其实已经被做了很多年。但大多数开源项目停留在“能检测、能报警”的层面,离“能用”还有很远距离。这次分享的升级版方案,核心思路是监护与调控闭环,也就是在传统滴速检测基础上,加入执行机构实现自动调速和异常处理,配套完整代码、原理图与仿真工程,整体可复现程度比较高。

这篇文章我会按实际开发顺序来讲:从系统框架怎么搭、关键器件为什么这么选,到滴速检测和PID控制的代码怎么写、仿真怎么做,最后再把我调试中踩过的坑和排查思路整理成速查表。内容尽量贴近实战,有些细节是我自己反复调整过的经验,不一定写在芯片手册里,但对做实物很有用。

1. 系统整体设计与思路拆解

1.1 从“监护”到“调控”的升级逻辑

先看基础版和升级版的本质区别。基础版智能输液系统一般只做三件事:红外对管检测滴速、OLED显示数据、超限蜂鸣器报警。它本质是一个测量+提示系统,最终调节滴速还是要靠护士手动去滚轮。

升级版在硬件上增加了执行机构(步进电机+滚轮夹持器或挤压泵),在控制逻辑上加入了闭环算法,系统从“告诉我现在的情况”变成“我帮你把情况维持在合理范围”。这个改动带来的不只是硬件成本上升,整个软件架构都要重做:你不再是简单读取传感器然后显示,而是在每一个控制周期里,感知、决策、执行紧密耦合。

设计时我给自己定了几条硬性要求:

  • 单芯片方案,不用树莓派或者加额外MCU做控制,降低成本和功耗
  • 步进电机加减速过程要平稳,不能对输液管产生突拉或突松,否则滴速会剧烈波动
  • 系统必须支持手动模式和自动模式的自由切换,毕竟临床场景里护士的习惯不同
  • 异常检测要去抖,不能因为病人换姿势导致短暂流速波动就疯狂报警

1.2 核心器件选型背后的计算逻辑

主控芯片选择STM32F103C8T6,这是很多人的老朋友了。Cortex-M3内核,72MHz主频,20KB RAM,64KB Flash。有人可能觉得资源有点紧,但实际算下来完全够用:液滴检测用外部中断,OLED显示用IIC总线,步进电机用定时器PWM输出,加上按键、蜂鸣器、WiFi模块(如果做远程),这些外设总共占用的引脚不超过25个,LQFP48封装正好覆盖。RAM方面,跑FreeRTOS轻量级内核加上几个任务栈,实测剩余大概还有8KB,足够用于协议解析和缓冲。

液滴检测这里有一个关键参数需要算清楚:采样孔的物理尺寸。红外对管是遮挡式检测,液滴从滴壶里落下时短暂阻断红外光线,产生一个下降沿脉冲。要想让检测稳定,滴壶上的检测窗口高度要和液滴直径匹配。常规的输液器滴壶液滴直径大约在4-5mm(20滴/mL规格),我设计的检测窗口高度做成了8mm,给液滴下落留下足够余量,避免液滴擦边导致检测不到。

电机执行机构的选择上,我对比过两种方案:5V步进电机(28BYJ-48)和微型直流减速电机。步进电机的优势是转角可控、保持力矩大,配合U型夹持器可以精确调节滚轮压紧程度;缺点是转速慢,但输液调节本来就不需要快,重点是精度。直流减速电机响应快,但很难精确控制“松紧程度”,容易过冲。最终选28BYJ-48,它的减速比是1:64,步距角5.625/64,约0.0879度,配合常见的ULN2003驱动板,控制分辨率相当细腻。

1.3 功能模块划分和系统框图

整个系统的功能模块划分为:

  • 检测模块:红外对管(发射管+接收管)、LM393比较器整形电路,负责把模拟光强变化转成干净的数字方波
  • 主控模块:STM32F103C8T6最小系统,负责信号采集、算法处理、控制输出
  • 显示交互模块:0.96寸OLED(IIC接口)、三个独立按键(模式切换、设定值加减、手动调速)
  • 执行模块:28BYJ-48步进电机+ULN2003驱动,用于调节滚轮或挤压管路的松紧
  • 报警模块:有源蜂鸣器,用于滴速异常、输液完成、气泡检测等告警
  • 电源模块:DC-5V输入,经AMS1117-3.3给主控和传感器供电,电机直接用5V
  • 预留扩展:串口UART1(调试/上位机)、UART2(可接ESP8266做WiFi远程监护,也可接蓝牙模块)

这个框图基本决定了后面软件任务怎么划分。检测是中断驱动的实时任务,控制是周期性的定时任务,显示和按键是低优先级轮询任务,这样的优先级安排在做RTOS移植时非常自然。

2. 核心硬件设计细节:原理图里的关键处理

2.1 红外检测电路:别忽略比较器的回差

很多初版设计直接用ADC读红外接收管的电压值,然后软件设阈值判断有没有液滴。这在实验室里没问题,但实际场景会出幺蛾子:环境光变化、滴壶位置偏移、红外管老化都会让信号幅值漂移,固定阈值很快就失效。

我在升级版中采用了LM393比较器+可调电位器+正反馈回差的硬件整形方案。LM393是开漏输出,上拉电阻选10K;正反馈电阻选100K,通过调节正反馈比例可以设置几到几十mV的回差电压。这个回差非常重要:没有它,红外信号在临界点附近会产生大量毛刺抖动,单片机外部中断会被反复触发,需要软件大量去抖来弥补,增加了CPU开销和响应延迟。

电路连接方式:

  • 红外发射管:限流电阻220Ω接5V,实测电流约12mA,保证接收管有足够的光电流
  • 红外接收管:NPN型光敏三极管,集电极接3.3V,发射极接GND并一个10K电阻,信号从发射极取出
  • 信号送入LM393同相输入端IN+,反相输入端接10K电位器的调节电压,用于设定触发阈值
  • 输出端加上拉电阻后直接接到STM32的PB0引脚,配置为外部中断下降沿触发

2.2 电机驱动:ULN2003必须处理的续流问题

ULN2003内部已经集成了达林顿管,每路输出都有内部续流二极管,所以很多人直接用它驱动28BYJ-48就完事了。实际使用中有两个容易被忽略的点:

第一个是驱动电压和逻辑电压的关系。ULN2003的COM脚(9脚)要接到电机电源正极(+5V),否则续流二极管起不到作用。我见过COM脚悬空的板子,一断电就烧元件,串口调试时也容易被干扰。

第二个是电机驱动不要和逻辑电路共用电源走线。步进电机起步瞬间电流可以达到300mA以上,如果在同一个电源轨上,LCD和传感器很可能复位。我的做法是在PCB上进行电源分区:5V输入后先经一个磁珠分成模拟电源和电机电源,模拟电源再经AMS1117降到3.3V。这样即使在电机换相瞬间,MCU侧电压波动也能控制在100mV以内。

2.3 原理图里值得注意的几个防护器件

  • 电源入口:5V输入并联TVS管(SMBJ5.0A)和一个100uF电解电容,再串一个自恢复保险丝500mA。医院环境里电源插拔频繁,防反接和浪涌很重要
  • OLED的IIC线:SDA和SCL各串一个100Ω电阻,可以抑制线间串扰,也能在插拔时保护MCU引脚
  • 蜂鸣器:驱动三极管基极串1K电阻,蜂鸣器两端反向并联二极管,这同样是防反电动势的
  • 按键输入:用内部上拉+外部0.1uF电容滤波,消抖可以靠外部电路去承担大部分

原理图的绘制我用的是立创EDA专业版,因为它的元件库和嘉立创PCB打样无缝衔接,导出BOM直接下单就行。当然使用Altium Designer操作习惯的读者,这套电路改用AD画也完全没问题。

3. 软件核心实现:滴速检测与控制算法

3.1 液滴脉冲检测与滴速计算的两种方法

液滴下落触发外部中断后,需要计算实时滴速。这里有两种思路,我分别说下适用场景和坑。

方法一:单位时间内计脉冲数

简单,设定一个200ms或500ms的定时窗口,统计窗口内下降沿次数,换算成滴/分钟。问题在于响应延迟:如果滴速是40滴/分钟,即1.5秒一滴,200ms窗口可能连续几个窗口都是0,显示数字跳变非常厉害。加长窗口到2秒,响应又太迟钝,调节动作滞后明显。

方法二:滴间隔法(前台使用)

记录相邻两个下降沿的时间间隔,然后换算滴速:滴速 = 60秒 / 滴间隔。例如间隔1秒,滴速就是60滴/分钟。这个方法的优点是响应快,每一个滴落事件都能立即更新当前速度,闭环控制可以做到“滴一滴,换算一次,调整一次”。缺点是单次间隔容易受到震动干扰导致跳动,需要加一阶低通滤波。

我采用的滤波公式是:

filtered_rate = 0.7 * last_filtered_rate + 0.3 * new_rate;

这个系数组合在实验里表现比较好。0.7的衰减系数让瞬时抖动的影响减弱,但又不至于让响应时间拖得太慢,相当于一个轻量级的低通滤波。如果想更平滑可以把系数改成0.8/0.2,但控制带宽会相应降低,KM值大时容易震荡。

滴速计算的核心代码逻辑:

volatile uint32_t last_tick = 0; volatile uint32_t drip_interval_ms = 0; volatile float drip_rate_filtered = 0; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == DRIP_SENSOR_PIN) { uint32_t now = HAL_GetTick(); if (last_tick != 0) { drip_interval_ms = now - last_tick; if (drip_interval_ms > 50) { // 小于50ms视为抖动,丢弃 float new_rate = 60000.0f / drip_interval_ms; drip_rate_filtered = 0.7f * drip_rate_filtered + 0.3f * new_rate; } } last_tick = now; } }

这里有个细节:drip_interval_ms用的数据类型是uint32_t,如果系统连续运行超过49天,HAL_GetTick的32位毫秒计数会溢出,减法回绕后依然能正确计算低32位的差值,所以实际不用太担心溢出问题。但如果是16位的定时器来计时,就得小心溢出处理了。

3.2 PID闭环控制:增量式算法的实际落地

闭环控制的执行对象是步进电机。电机的正转、反转对应滚轮夹持的压紧和松开。控制目标是把实时滴速维持在设定值附近。这里用增量式PID实现,公式:

du(k) = Kp * [e(k) - e(k-1)] + Ki * e(k) + Kd * [e(k) - 2*e(k-1) + e(k-2)]

增量式的优势是输出的是控制量的增量,对执行机构来说就是相对上一次位置再走多少步,不会因为误差累积导致电机一路走到极限位置。

代码核心片段:

typedef struct { float Kp; float Ki; float Kd; float target; float err[3]; float out; } PID_TypeDef; PID_TypeDef pid; float PID_Calculate(PID_TypeDef *pid, float measure) { pid->err[2] = pid->err[1]; pid->err[1] = pid->err[0]; pid->err[0] = pid->target - measure; float du = pid->Kp * (pid->err[0] - pid->err[1]) + pid->Ki * pid->err[0] + pid->Kd * (pid->err[0] - 2.0f * pid->err[1] + pid->err[2]); pid->out += du; // 输出限幅,限制步进电机的每次调节步数 if (pid->out > 2000) pid->out = 2000; if (pid->out < -2000) pid->out = -2000; return pid->out; }

控制周期我设置为500ms,即每500ms根据当前滤波后的滴速进行一次PID计算,根据输出值的符号和大小决定步进电机正转/反转的步数。500ms这个值经过了实际输液场景的调试,太短(如100ms)会导致电机频繁微调、电机温度上升很快;太长(如2s)又会导致调节响应太慢,滴速波动范围大。

关于PID参数整定,分享一次完整的实操过程:

我用的方法是“先P后I再D”的分步整定法:

  • 先把Ki和Kd设为0,Kp从小往大调,观察滴速响应曲线。Kp=5时发现稳态误差明显,设定60滴,实际只能到54滴左右
  • 再加Ki,Ki=0.1时稳态误差基本消除,但发现超调超过10滴,随后产生约3个周期的衰减震荡
  • 最后加Kd,Kd=0.5后过冲明显改善,稳定在±2滴的精度

最终参数是Kp=8.0,Ki=0.15,Kd=0.5。这个参数试过用Ziegler-Nichols整定法推算,但实际效果反而没有手工整定的好,原因在于输液系统是一个非线性很强的受控对象,滴速和滚轮夹紧角度之间并不是线性关系。

3.3 手动模式和自动模式的软件切换

不是所有场景都适合自动控制,比如输液即将结束的时候,自动调节已无意义,护士可能需要手动把滚轮放松一点让最后一点药液顺利滴完。所以我在软件里做了一个状态机:

  • 状态AUTO:PID闭环调节,按键加减设定值
  • 状态MANUAL:PID计算停止,电机根据按键点动运行,每按一次走固定步数
  • 状态STOP:电机锁紧,触发完成报警,等护士确认

模式切换的代码不算复杂,但有个地方要小心,就是切换过程中要做状态重置。PID的误差历史数组必须清零,否则从手动切回自动时,残留的旧误差会让电机猛地动作一下,很危险。这个重置操作我最初漏掉了,结果仿真实测时发现一切换模式滴速就冲高到90多,几秒后才慢慢恢复。

void PID_Reset(PID_TypeDef *pid) { pid->err[0] = 0; pid->err[1] = 0; pid->err[2] = 0; pid->out = 0; }

3.4 用FreeRTOS组织多任务,配方的优先级这样定

升级版的项目里我引入了FreeRTOS,好处是任务职责清晰,代码逻辑更好维护。任务划分如下:

任务名称优先级周期/触发功能
DripDetectTask最高(中断回调)外部中断记录滴间隔,更新滤波滴速
ControlTask500msPID计算,输出电机控制指令
DisplayTask200ms刷新OLED显示数据
KeyTask20ms扫描按键消抖与检测
AlarmTask事件触发蜂鸣器报警控制

Priority设置时注意,ControlTask不能设置成和DripDetectTask同优先级,因为滴速检测需要极低延迟,而PID控制即使偶尔延迟100ms影响也不大。任务间共享的变量(drip_rate_filteredpid.target等)都加了临界区保护,最轻量的方式是进入临界区时taskENTER_CRITICAL()taskEXIT_CRITICAL(),操作完成后立刻退出。也可以用原子变量,但考虑到可读性,临界区对于这个项目足够。

任务之间使用FreeRTOS的Queue来传递事件消息,比如检测到连续6秒无滴落会向AlarmTask发送一个EVENT_STOP消息。用队列比直接操作标志位好在哪?如果标志位在多个任务里被读取和清零,很难保证时序;消息队列自带阻塞和优先级机制,系统结构会清晰很多。

4. 仿真验证与调试过程

4.1 Proteus仿真的搭建要点与局限

Proteus仿真这个项目的核心价值是:在没有实体硬件的阶段,快速验证逻辑正确性。我在仿真中搭建了STM32F103C8T6、LM393检测模拟模块、步进电机驱动和虚拟终端。液滴脉冲使用一个信号发生器(或手动开关)模拟,周期可调的方波输出接到PB0脚,这样能模拟不同滴速下的系统响应。

仿真中可行的验证:

  • PID控制器的响应曲线是否平滑,设定值从50改到80时,输出是否快速跟随且超调可控
  • 模式切换时是否存在异常动作
  • 报警逻辑是否在滴速过低时触发

仿真中最容易踩的坑是外设的中断触发极性。用方波模拟液滴信号时,信号源的默认初始电平可能是低电平,而红外检测电路的原设计是“无滴落时高电平,滴落瞬间低电平”,两者接反会导致上电就误触发大量中断,仿真里表现为滴速显示为一个巨大的数。排查方法很简单,先用虚拟示波器看PB0脚的波形,再对照代码逻辑。

但必须坦诚:Proteus仿真替代不了实物调试。仿真里28BYJ-48的机械负载特性、ULN2003的真实驱动电流波形、红外对管在真实滴壶场景中的响应延时都无法精确建模。尤其是液滴下落时红外信号并不是干净的数字方波,而是一个有拖尾的模拟信号,这只能靠真实硬件去调比较器阈值。所以仿真环节定位是逻辑验证,而非参数测试。

4.2 硬件实物调试流程记录

实物调试我采用的顺序是“先点灯、再测传感器、最后闭环”。

先只烧录最小工程,验证GPIO、时钟配置正常,OLED能点亮显示字符。然后用示波器探头接在LM393输出端,用手在滴壶侧面遮挡红外对管,观察是否有干净的下降沿。如果没有,调整电位器阈值,找到信号跳变的临界点后稍微往回收一点。

传感器正常后,把电机控制函数加上,先用开环方式手动让电机正转、反转,确认滚轮夹持器的行程范围:从完全松开到完全锁紧,电机大概需要多少步,这个参数后面会作为PID输出限幅的参考。

最后才把PID闭环算法跑起来。第一次闭环测试时我碰到一个诡异的现象:设定60滴/分钟,实际滴速在58-66之间波动,电机每隔几秒就来回转动一次。后来排查发现,问题不在PID参数,而是我的步进电机细分函数写错了——每次PID输出的步数直接作用到了八拍控制的相序表上,导致电机实际走的角度只有理论值的一半。修正细分逻辑后再测,波动范围收窄到±2滴。

4.3 Keil开发环境与ST-Link调试常见问题

实际调试中很多读者会遇到一类问题:“电脑无法识别ST-Link”。就Keil MDK下ST-Link的排查,我整理了一套自己的顺序:

  • 确认ST-Link的驱动是否安装成功。Windows设备管理器里应看到“STMicroelectronics STLink dongle”之类的设备。如果显示感叹号,重新安装驱动
  • 确认Debug选项里选择了ST-Link Debugger,且在Settings中能识别到芯片IDCODE。这里常见的是IDCODE显示0x00000000或连接时弹Error: Flash Download failed - "Cortex-M3",基本是接线问题,SWDIO、SWCLK、GND三根线不能少,且每根线要尽可能短
  • 如果出现标题热词里提到的error: no stm32 target found! if your product embeds debug authentication,大多是芯片读保护开启了,或者SWD引脚被代码复用。解决办法是使用ST-Link Utility的“Connect under reset”模式,甚至需要拉高BOOT0引脚强制从系统存储器启动,再整片擦除
  • 最后是供电问题。ST-Link的SWD接口不能提供足够电流给整个系统,如果目标板上有电机等大功率外设,必须外接5V电源,且GND要与ST-Link共地。共地这个细节很多初学者容易忽略,不共地时调试器根本无法通信

排障顺序上,我个人的经验是“先查供电、再查接线、最后才怀疑软件配置”,这个顺序能绕过绝大多数新手问题。

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

5.1 问题速查表

现象可能原因排查/解决
滴速显示数字巨大且乱跳比较器阈值设置不当,干扰脉冲被当作液滴用示波器观察LM393输出波形,调整电位器;软件里增加最小间隔过滤
电机不转或抖动ULN2003的COM脚未接电源正极检查COM脚到5V的连接;确认信号线接线顺序
电机发热严重PID控制周期过短或Kp过大延长控制周期,或调小Kp值;检查电机是否持续处于微调状态
OLED白屏或花屏IIC地址错误或上拉电阻缺失确认OLED地址(通常为0x78/0x7A),SDA/SCL各接4.7K上拉电阻
切换模式后滴速突变PID状态未重置模式切换时调用PID_Reset清零历史误差
SWD调试连接不上芯片读保护或BOOT引脚电平不对用ST-Link Utility的全擦除选项,必要时拉高BOOT0再连接
电机噪声大、蜂鸣器误响电源纹波太大电机独立供电或增加电容滤波;信号线远离电机线

5.2 关于抗干扰,软件和硬件层面各做了什么

这个项目里最大的可靠性威胁来自干扰。输液场景中的电磁干扰源主要是步进电机的瞬态电流变化和医用设备附近的射频干扰。硬件层面我做了:

  • 红外传感器的信号线使用双绞线走线(PCB上画成等长差分),且在靠近MCU端加了100pF滤波电容
  • 电机驱动板和主控板不共地铜皮,通过磁珠单点连接
  • 蜂鸣器驱动回路做了RC吸收,避免关断瞬间的高压尖峰干扰

软件层面,除了前面提到的50ms最小间隔过滤和滤波算法,还有一个细节:外部中断里不直接调用HAL_Delay,也不做任何耗时操作,只做时间戳记录和变量赋值。这句经验是从一次现场事故中学到的:最初版本把OLED刷新操作放进了中断里,结果滴速一到80滴以上,中断服务函数执行时间太长,主循环直接卡死,蜂鸣器都不响了。把耗时操作从中断里搬出去之后,问题彻底解决。

5.3 这套系统还能怎么扩展

升级版已经做了闭环调控,如果再往下走,可以围绕数据化和智能化做文章:

  • 增加ESP8266模块,用MQTT协议把滴速、剩余液量估算值、报警事件上传到云平台或局域网服务器,实现护士站集中监控。代码里UART2预留就是干这个用的
  • 增加压力传感器,检测输液管堵塞:电机堵转时电流会上升,通过监测ULN2003输入端的电流信号间接判断堵塞
  • 如果做更高端版本,可以换成伺服驱动方式控制蠕动泵,精度更高,并且可以使用更完整的闭环模糊PID算法

6. 开源资料和复现建议

6.1 资料包里有什么

完整的项目资料包含:

  • 原理图源文件(立创EDA格式)和PDF导出版
  • PCB工程文件,可以直接打样,板子是双层板,尺寸约80mm x 60mm
  • 完整的MDK5工程源代码,包含了所有外设驱动、PID控制逻辑和FreeRTOS任务代码
  • Proteus仿真工程,支持读取后直接运行逻辑仿真
  • 一份使用说明文档,包含接线图、电位器调节步骤和常见问题解答

拿到资料后建议按这个顺序上手:

  1. 先打开PDF原理图,对照实际模块,把电源走向和信号流向理清楚
  2. 看代码的main.c和功能头文件,理解初始化顺序和任务创建
  3. 打开仿真工程,运行一遍,确认逻辑行为符合预期
  4. 有条件的再打板做实物,按前面说的调试流程一步步来

6.2 我的实操体会

这个项目断断续续做了三轮才达到完全满足需求的稳定度。第一轮只是把硬件搭通,发现红外检测的阈值漂移问题严重;第二轮重新设计了比较器电路,加入了硬件回差,检测稳定多了,但步进电机每次动作的噪音太大;第三轮才真正把PID参数整定到位,并且在电源布线上花了心思,最终测试时滴速可以稳定在±2滴的精度范围。

有个小建议是:如果你在复现时条件有限,做不了实物PCB,先用杜邦线加面包板也可以验证核心算法。面包板的寄生电容和线间干扰虽然会让传感器信号差一些,但通过调电位器和软件滤波,依然可以把主流程跑通。唯一不建议面包板操作的是电机驱动,ULN2003的电流在面包板上会产生明显的压降,电机会显得无力,这个和程序无关。

另外,很多读者来问我为什么滴速老是测不准,最后发现他们用的是自带滴壶的输液器,滴壶壁上已经有刻度线或者加强筋,这些结构会造成红外对管的反射杂散信号。如果你也遇到这类问题,最简单的办法是滴壶外包一层黑色热缩管,只在检测窗口位置留一个狭缝,就能大幅减少误触发。

这个项目的代码和文档我整理了不少版本,核心思路就是“以数据流驱动控制流”,软件上优先级和状态机清晰,硬件上每个模块都有独立的电源和信号处理。如果你正在做类似的医疗电子项目、毕业设计或者参加电子设计竞赛,相信这套方案能给你一个比较完整的参考。

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

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

立即咨询