资讯动态

STM32智能输液监护调控系统:闭环滴速控制与安全监测实战

发布时间:2026/9/9 7:09:41 来源:尧图企业网站定制
STM32的开源项目里医疗电子方向一直关注度很高智能输液监护调控系统算是这类项目里的常青树。最近我把手上这套升级版彻底整理完了代码、原理图、仿真文件全部开放主控用的是STM32F103C8T6功能上比基础版多了闭环滴速控制、气泡检测、液位检测、分级报警和OLED人机交互可复现性很强。不管你是正在找毕设方向还是想给嵌入式简历加一个完整的医疗电子案例这套工程都值得花几个晚上跑一遍。这篇博文就用来记录我从需求拆解、硬件设计、软件调试到仿真跑通的完整过程顺带把踩过的坑也交代清楚。1. 项目整体设计与思路拆解1.1 升级版到底比基础版强在哪很多新手做的第一版输液监护系统通常只有红外对管检测滴速加一个LCD显示然后人工手动调滚轮来控制快慢。这种方案做课程设计没问题但离“监护调控”这四个字其实差得很远滚轮稍微碰一下滴速就飘了液体快输完了没人盯着就容易回血管路里进了气泡也不能第一时间发现。这次升级版主要补了三块短板。第一是闭环控制用步进电机驱动蠕动泵代替人手调滚轮STM32实时读取滴速跑PID算法自动修正电机速度目标设置40滴/分就能稳定维持在40滴/分左右。第二是安全监测增加了气泡检测、低液位检测、堵管检测任何一个异常都会触发分级报警。第三是人机交互OLED屏实时显示目标滴速、当前滴速、累计量和报警状态按键可以修改参数整机逻辑用状态机管理行为可预测、可追溯。从学习角度来说这套项目覆盖了GPIO、外部中断、定时器、PWM、ADC、I2C、UART这些STM32的常用外设还涉及传感器信号调理、执行机构驱动、PID控制算法和嵌入式状态机设计。如果读者只是单纯想把外设API调通那网上有一大堆demo但这个项目赚就赚在逼你把这些外设串成一个能闭环工作的系统。1.2 系统架构拆解感知、决策、执行三层整个系统的信号链路可以分成三层和护士人工监护输液过程做个类比就很好理解。感知层相当于护士的眼睛。滴速传感器用红外对管卡在莫非氏滴管两侧液滴下落瞬间遮挡红外光接收管输出一个脉冲STM32数脉冲就知道滴速。气泡检测是另一组红外对管贴在滴壶下方输液管两侧利用空气和液体对红外光折射率的差异来判断管路里有没有连续气泡。再配合一个称重模块HX711实时采集输液瓶重量变化既能估算剩余液量也能辅助判断是否输液完成。决策层相当于护士的大脑也就是STM32F103C8T6。它读取感知层的信号经过滤波、换算、比较得出当前状态。状态正常就维持当前控制策略状态异常就切换报警状态同时通过蜂鸣器和OLED提示护士介入。执行层相当于护士的手。STM32输出PWM或者脉冲序列通过ULN2003驱动28BYJ-48步进电机电机带动蠕动泵滚轮挤压输液管从而控制液体流速。注意这里的控制对象是步进电机的转速转速越快单位时间内挤压管子的次数越多输液速度越快。1.3 技术选型背后的原因主控选STM32F103C8T6没有太多悬念。这颗芯片已经是开源嵌入式项目的“万金油”72MHz主频、64KB Flash、20KB RAM外设资源对这个项目来说完全够用而且网上资料和学习路线极其成熟。128KB Flash的CBT6也不贵但C8T6在Proteus里仿真支持更好一些。滴速检测没用摄像头识别或者超声波方案因为红外对管方案最稳定也最便宜。难点在于信号调理后面第2章会详细讲。执行机构选用步进电机而不是直流电机是因为步进电机可以精确控制转速和转角开环情况下就能获得不错的重复精度PID调起来也更容易收敛。蠕动泵是现成的泵头结构把输液管卡进去就能用不需要自己设计复杂的流体机构。为什么不用FreeRTOS因为单任务循环在这个场景下完全够用而且裸机逻辑更容易让初学者读懂。把实时性要求高的滴速计数放在中断里处理其他计算放在主循环里跑这个分工模式本身就是嵌入式开发的经典套路。2. 硬件电路与原理图实现2.1 主控最小系统与供电设计STM32F103C8T6的最小系统不算复杂但每一部分都有讲究。供电采用三级结构外部12V适配器先进LM2596降压到5V给步进电机和比较器供电再经AMS1117-3.3线性稳压到3.3V给主控和传感器供电。电机和逻辑电路分开供电是为了避免步进电机启动瞬间拉低电压导致单片机复位这个坑我调试时踩过好几次。晶振电路很多人是照抄参考设计但电容取值还是有讲究的。8MHz晶振的负载电容CL通常在12pF到20pF之间匹配电容计算公式是CL C1 * C2 / (C1 C2) Cstray其中Cstray是PCB走线和引脚寄生电容经验值3pF到5pF。如果晶振规格书中CL12pF取Cstray为4pF那么C1和C2各取16pF比较合适实际使用贴片电容没有16pF这个标称值那就用15pF或者20pF偏差不大都能正常工作。我的板子上焊的是两个20pF电容实测串口波特率误差在允许范围内。复位电路用10k上拉电阻加100nF电容到地BOOT0引脚通过10k电阻下拉到地让芯片从Flash启动。下载调试接口引出SWDIO、SWCLK、GND、3V3四根线一个四针排针就能搞定。这里不建议用JTAG20针接口浪费引脚空间SWD两根线足够用。2.2 滴速检测传感器与信号整形电路滴速检测的红外对管发射管和接收管分别安装在滴壶两侧位置要正对而且尽量贴近管壁避免环境光干扰。这个机械定位很关键我在洞洞板上手动弯脚固定结果前后两版数据差别很大后来用3D打印做了一个小卡座才算稳定。传感器输出的原始信号是接收管两端电压的变化变化幅度不大、边沿也不陡直接接单片机引脚会导致外部中断乱触发。我的做法是在传感器后面加一级LM393比较器整形。接收管串联一个电阻把光信号转成电压接到LM393的同相输入端反相输入端接一个10k电位器调节阈值。没有液滴遮挡时接收管受光强、电压高输出低电平液滴落下来挡住光路电压降低输出翻转为高电平。阈值调在正常光照和完全遮挡的中间位置抗干扰效果最好。比较器输出是开漏结构需要加一个10k上拉电阻到3.3V。如果环境光干扰严重可以在接收管前面加一片滤光片或者用脉冲调制方式驱动发射管让单片机只识别同一个频率的信号这是工业上常用的抗环境光手段项目进阶时可以试试。2.3 步进电机驱动电路设计执行机构我选择了28BYJ-48步进电机这是一个四相五线减速电机减速比1/64非常适合低速蠕动泵场景便宜、扭矩够用、控制简单。驱动芯片选ULN2003本质上就是达林顿晶体管阵列内部自带续流二极管可以省掉外部二极管。接线顺序不能乱。ULN2003的IN1到IN4分别接STM32的PA0到PA3OUT1到OUT4接电机五线排线的四个相线电机公共端接5V。驱动方式用四相八拍也就是半步模式每个脉冲电机转过半个步距角配合1/64减速比最终轴的步进精度足够细腻蠕动泵出液也更均匀。PWM并不是电机调速的唯一方式。对于步进电机更常见的做法是定时器输出固定频率的脉冲串频率越高电机转得越快。我在代码里就用定时器PWM输出通过修改PWM频率来控制速度。有人会问为什么不用PWM占空比因为步进电机需要的是脉冲序列不是模拟电压占空比只影响平均电压不直接改变转速这个区别新手容易混淆。电源方面电机启动瞬间电流可以达到300mA以上5V的LM2596输出端要并一个470uF电解电容稳压。如果在仿真里跑这个电路要注意给电机驱动留足够电源余量不然虚拟环境下也可能出现复位的现象。2.4 气泡与液位检测电路气泡检测我用的也是红外对管但安装位置和信号处理方式和滴速检测不同。滴速检测要的是脉冲边沿气泡检测要的是长时间的光路变化。当输液管里是液体时红外光线透过率相对低接收管输出一个稳定电平管路里连续通过气泡时空气对红外光的折射和散射会让接收管电平产生明显跳变用ADC采样或者比较器输出都能捕捉到。我在设计时留了两路接口一路是红外对管接收信号直接进STM32的ADC引脚软件里做滑动平均值滤波超过阈值就判定气泡另一路是进比较器输出数字电平适合后续扩展。实测下来ADC成分辨率更高可以区分小气泡和大气泡但干扰也更多数字电平成分稳定但无法分级。这个项目最终用了ADC方案整数倍采样后判断是否连续N次超过阈值再触发报警防止单个杂散信号误报。低液位检测最直接的方式是安装一个液位开关浮球式或者光电式都行。但考虑到输液瓶是耗材换瓶子就意味着传感器也要跟着动我加了一路HX711称重方案作为辅助。HX711是24位高精度ADC直接读取桥式应变片压力传感器的电压差软件里换算成克数。当重量低于设定的空瓶阈值时判定输液完成。称重方案的优点是通用性强不需要改动输液管和瓶体结构适合做成品原型。2.5 原理图绘制和布局心得原理图工具我用的是嘉立创EDA因为开源项目分享给其他人看更方便AD或者KiCad也没问题。实际的工程文件里包含了完整原理图这里讲几个必须注意的细节。去耦电容是所有硬件工程师反复强调的东西但新手依然容易忘。STM32每个电源引脚旁边都要放一个100nF陶瓷电容而且要尽量靠近引脚否则高速翻转时电源噪声会很大极端情况下会引起外设误动作。晶振附近不要走高频信号线尤其是不要和PWM输出线平行走线否则时钟容易受干扰串口乱码、定时器计数不准都可能和这个有关。排针和其他接口的标注要清晰。SWD、滴速传感器、电机接口、电源输入的丝印必须写清楚不然隔几天自己看了都懵。调试时我喜欢在每个电源域加一个LED电源指示灯3V3、5V、12V各一颗上电一眼就能看出哪路没输出。测试点也尽量多留几个示波器探头可以直接夹。3. 软件逻辑与关键代码实现3.1 工程结构与开发环境开发环境我用的是STM32CubeIDE配合HAL库。选HAL库而不是标准外设库主要是新项目上手快而且CubeMX生成的初始化代码不容易出错。工程按功能模块划分文件这个习惯对后期维护和二次开发非常重要。项目根目录下主要模块有main.c负责主循环和状态机调度bsp_dripper.c负责滴速传感器和气泡传感器读取pid.c实现增量式PID计算motor.c封装步进电机驱动ui.c管理OLED显示和按键扫描alarm.c管蜂鸣器和错误状态。有人可能会觉得裸机工程没必要分这么细但我自己写了两版之后明确告诉你模块化能救命。第一版把所有代码堆在一个main.c里调试气泡检测的时候改了一个变量滴速显示就开始乱跳排查了半天发现是全局变量重名了。拆成独立模块之后每个文件只暴露必要接口问题边界清楚很多。3.2 滴速采集外部中断与滤波算法滴速采集是整个系统的数据源头。如果滴速值不准后面的PID再调也是白搭。我用外部中断加定时器实现滴速传感器整形后的脉冲信号接入STM32的PA4引脚配置为上升沿触发外部中断TIM2配置成1秒中断每个周期统计一次外部中断次数。核心代码逻辑如下volatile uint32_t dripper_tick_count 0; volatile uint32_t dripper_drips_per_min 0; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin DRIPPER_PIN) { dripper_tick_count; } } void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE)) { // 每一秒读取一次计数换算成“滴/分钟” dripper_drips_per_min dripper_tick_count * 60; dripper_tick_count 0; __HAL_TIM_CLEAR_FLAG(htim2, TIM_FLAG_UPDATE); } }这个原始数据直接拿来控制电机是不够稳的。实测中液滴下落偶尔会出现连续两滴靠得很近或者一滴被管道边缘弹开导致脉冲间隔不均匀瞬时滴速能跳10滴/分以上。我做了两级处理先对原始滴速做中值滤波连续取5次数据排序后取中间值滤掉毛刺再把中值结果做滑动平均窗口长度取4让显示和控制曲线都平滑下来。堵管检测也是在这一层完成。如果连续超过10秒没有检测到滴速脉冲同时电机还在运行就认为管路堵塞系统进入堵管报警状态。这个检测逻辑放在主循环里就行不需要额外定时器。3.3 滴速闭环控制从手调到PID开环控制把电机设定在一个固定转速然后祈祷滴速稳定这在实验室里能凑合稍微有点外部扰动就会拉胯。比如液面高度变化、输液管被压了一下、温度变化导致液体黏度改变都会影响实际流速。反馈控制才是这个项目的正路。PID控制的目标非常明确让当前滴速稳定在目标滴速附近。控制量是步进电机的PWM频率。我把增量式PID写成了独立模块方便参数整定和移植typedef struct { float kp; float ki; float kd; float target; float last_error; float integral; float output; } PID_t; void PID_Calculate(PID_t *pid, float current) { float error pid-target - current; float proportional pid-kp * error; pid-integral error; float integral pid-ki * pid-integral; float derivative pid-kd * (error - pid-last_error); float output proportional integral derivative; // 输出限幅防止电机频率过高或过低 if (output 2000) output 2000; if (output 100) output 100; // 积分限幅防止长时间饱和 if (pid-integral 5000) pid-integral 5000; if (pid-integral -5000) pid-integral -5000; pid-last_error error; pid-output output; }参数整定建议按先比例、后积分、再微分的顺序来。先把Ki和Kd设为0只调Kp观察滴速围绕目标值振荡的情况Kp合适后加入Ki消除稳态误差此时要注意系统可能开始周期振荡需要适当减小KpKd在这个项目里一般不加大因为滴速信号本身已经做了滤波微分项放大了噪声反而容易引起电机抖动我的实际参数是Kp0.8Ki0.05Kd0。控制上还有两个细节值得一提。一是目标滴速变化时不要直接跳到新值可以做一个斜坡函数让目标值每秒靠近设定值一定量这样系统不容易超调二是电机启动初期滴速还没建立起来PID会大比例输出容易导致流速冲过头所以启动后前两秒我做了开环缓启动等第一个滴速脉冲稳定出现后再切入闭环。3.4 人机交互与报警状态机系统有五个按键启动、暂停、目标滴速、目标滴速-、报警复位。OLED左侧显示目标滴速和当前滴速右侧显示累计量、剩余量和系统状态。一屏装不下所以做了一级菜单循环按键长按进入参数调节界面短按切换显示项。整机逻辑用状态机管理这是我认为这版项目最值得学习的部分。状态一共六个IDLE待机、RUNNING正常运行、PAUSED暂停、ALARM_BUBBLE气泡报警、ALARM_EMPTY输液完成报警、ALARM_OCLUSION堵管报警。主循环里对滴速、气泡检测、称重数据每个周期做一次判断比如当前处于RUNNING状态时检测到气泡信号就立即停止电机、置位蜂鸣器、切换状态到ALARM_BUBBLE同时OLED显示报警信息。等护士处理完按键触发复位系统先回到PAUSED状态再按启动才继续运行。使用状态机的好处是行为可预测不会出现“这个报警为什么自己消失了”这种诡异问题。代码里状态迁移都集中在state_machine.c中逻辑一目了然。4. 仿真搭建与系统联调4.1 Proteus仿真环境搭建很多人以为这个项目一定要有实物才能做其实Proteus仿真把大部分功能都验证得很充分。仿真工程里用了STM32F103C8T6的Proteus模型配合LM393比较器、ULN2003、OLED模型和步进电机模型。滴速传感器的仿真可以简化处理。因为Proteus里没有现成的红外对管滴速传感器模型我用一个脉冲发生器VPULSE接到比较器输入端来模拟液滴信号。VPULSE的频率对应滴速比如要模拟60滴/分就设置频率为1Hz的方波占空比30%。这样CPU看到的输入信号和真实红外传感器整形后的输出几乎没有区别可以用来验证滴速计算和PID控制逻辑。OLED在Proteus里有仿真模型用I2C接口连接就能显示内容。如果Proteus版本比较旧没有这个模型也可以用虚拟终端串口输出代替显示功能效果一样能确认逻辑正确。我习惯两种方式同时跑OLED看界面串口看调试数据。4.2 仿真信号调试与虚拟仪表仿真的价值在于可以精确控制输入这在实物测试里反而不容易做到。我可以把VPULSE频率设成0.8Hz、1Hz、1.5Hz分别对应48滴/分、60滴/分、90滴/分观察系统在PID调节下的收敛过程。这里要提醒一点仿真里的AD采样、比较器、电机模型都是理想化的没有真实环境中的噪声和延迟所以仿真中表现良好的PID参数实物上未必能直接用。比较稳妥的做法是先用仿真把代码逻辑、状态迁移、显示内容全部验证好再在实物上进行第二轮整定。我遇到过不少人和我抱怨“仿真跑得好好的实物完全不对”多半就是因为把仿真结果当成了实物预期。虚拟示波器是个好帮手。把TIM2输出的PWM引脚用Probe拉进虚拟示波器可以直观看到电机速度变化也就是PID控制量的变化比看数字调试信息直观很多。步进电机模型的转速也可以用电压表估算但精度有限建议以串口打印的滴速数据为准。4.3 从仿真到实物联调顺序与测试数据从仿真转到实物时我强烈建议按“先小后大、先开环后闭环”的顺序来。第一步只下载一个LED闪烁程序确认最小系统、SWD和供电正常第二步点亮OLED并扫描按键确认外设I2C正常第三步接上滴速传感器用示波器看整形后波形确认中断能触发第四步用开环PWM驱动电机确认转向和转速范围全部通过后才把PID闭合起来。下面是实测的一组数据环境温度25度左右输液管是普通一次性输液器液体是纯净水目标滴速分别设了40、60、80滴/分等待系统稳定2分钟后记录目标滴速(滴/分)当前滴速均值(滴/分)最大偏差(滴/分)稳定时间(秒)4039.81.2126060.31.8158079.62.518可以看到目标滴速越高误差波动越明显这和蠕动泵在高速转动时出液脉冲叠加效应有关系属于正常现象。如果偏差超过3滴/分我会优先检查滴速传感器的安装是否移位其次再考虑PID参数。5. 常见问题与排查技巧实录5.1 滴速值跳变、显示不稳定新用户反馈最多的是OLED上滴速数字一直跳哪怕电机没开也在乱跳。这个问题的根源通常不是算法而是传感器信号质量。先拿示波器探头夹在比较器输出引脚上看波形是不是干净的标准方波。如果上升沿有一长串抖动说明阈值电压设置得太靠近信号中间区域的缓慢变化部分调整电位器让阈值尽量靠近“完全遮挡”时的电压值。如果信号本身没问题再检查传感器安装是否松动液滴是否每次都落在红外光路的正中间。软件侧的滤波只是补救手段不是根治手段。5.2 步进电机抖动、失步、噪声大电机抖动通常有三个原因。一是PWM频率太低28BYJ-48在频率低于100Hz时会明显一顿一顿至少设到500Hz以上二是输出限幅设置得太窄PID输出顶到上限后电机转速跟不上三是电源带载能力不够电机一启动就把5V拉垮驱动芯片逻辑混乱这种情况用示波器看5V电源波形就能确认。失步问题则和加减速有关目标转速突变时电机丢步所以我在代码里做了斜坡启动从80Hz起每100ms增加20Hz实测对减少失步很有帮助。5.3 仿真与实物表现不一致仿真和实物不一致是嵌入式开发的常态不要慌。最常见的差异来源有三个传感器模型缺失、电源模型理想化、时间基准不精确。仿真中我用VPULSE模拟滴速现实中红外传感器有边沿抖动仿真中LED等效代替步进电机现实中电机有惯性、启动电流和振动。面对差异时不要急着改PID参数先逐一确认采集进来的数据是否和实际物理量一致数据链路没问题了再谈控制效果。5.4 ST-LINK下载失败、提示no stm32 target found这个报错在STM32开发里太经典了。先用万用表量一下目标板3.3V是否正常然后确认SWDIO、SWCLK、GND三根线没有接反。如果线序没问题检查BOOT0引脚是不是被意外拉高了BOOT0为高电平时芯片进入系统存储器模式SWD是连不上的。还有一个容易忽略的原因是目标板供电不足ST-LINK的3.3V输出能力有限如果板子外围器件太多就要外接独立电源。最后如果以上都不行按住复位键点击下载在下载瞬间松开复位键有时候能让芯片复位时序对上。5.5 常见问题速查表现象可能原因排查思路滴速数字乱跳传感器阈值不合适、安装偏移、环境光干扰示波器观察比较器输出波形调整阈值电位器OLED不显示I2C地址错误、接线松脱、初始化时序不对用I2C扫描代码检查地址确认SCL/SDA上拉电阻电机不转驱动芯片供电异常、控制引脚接错、PWM未输出检查ULN2003输入输出电平用示波器看PWM电机抖动失步PWM频率太低、电源掉电、限幅过窄提高PWM频率增大电源电容调整PID输出范围气泡传感器误报ADC采样毛刺、阈值太灵敏、气泡排完但标志未清除增加连续计数判断调整阈值检查复位逻辑系统反复复位电源跌落、看门狗超时、晶振起振不良示波器看电源波形检查NVIC配置更换晶振电容报警一直响无法复位状态机卡死、按键消抖失败、条件未解除串口打印状态值逐个状态排查迁移条件6. 开源资料使用与二次开发建议拿到这套开源工程后建议不要直接烧录就跑那样学到的内容会少很多。第一步先把原理图从头到尾对照看一遍不懂的元件去查数据手册把每个芯片为什么在那里、为什么接这些电阻电容搞清楚。第二步自己画一遍最小系统板哪怕不制板只是在软件里排一下版也能帮你加深理解。第三步才是下载验证核心功能并用调试器观察滴速采集和PID控制的中间变量。如果你手里没有实物仿真工程也能把这个项目跑通大半但要注意在Proteus里调好的PID参数到了实物大概率要重新整定。如果你打算在此基础上做更完整的产品原型建议优先考虑三个方向一是换用无刷电机或者直流电机加编码器方案提高长时运行可靠性蠕动泵在24小时连续工作时对电机寿命要求比较高二是用低功耗单片机加OLED休眠机制把整机功耗降下来方便电池供电三是增加无线通信模块把报警信息推送到护士站但这会引入安全风险和协议设计问题需要更多思考。最后再分享一点个人体会。这个项目我前后改了三个版本最深的感受是不要一上来就追求“高级”。第一版我也想过直接用摄像头的机器视觉识别液滴用WiFi模块上云管理结果被硬件稳定性和外设调试耗掉了大量时间。后来回归到红外对管加步进电机、裸机状态机这套最朴素的方案功能和稳定性反而快速达标。嵌入式项目的难点从来不是哪个外设不会用而是当所有外设组合在一起时你还能不能清晰地分析和解决问题。这套系统即使不做任何扩展把每一个模块的“为什么”都搞清楚收获也比泛泛刷十个demo大得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价