基于STM32的智能输液监护调控系统:闭环控制与仿真实现 📅 发布时间:2026/9/8 23:04:06 👁 浏览次数: 说到底输液监护这活儿很多年以前没有被“智能化”的时候全靠护士或者家属盯瓶。滴速靠手动推滚轮患者翻个身可能流速就变了药液快空还得时刻留意冬天手冷调滚轮也不太灵敏。这也是我做这套STM32智能输液监护调控系统升级版的初衷——利用单片机完成滴速的实时检测、闭环调节、异常报警顺便把整个项目开源代码、原理图、仿真一条龙全给到。这套系统对正在学STM32、想做课程设计或电子竞赛题目的朋友尤其是奔着“嵌入式医疗电子”方向去的读者非常值得参考。这个项目选择STM32F103C8T6做主控属于性价比很高的经典芯片网上资料多、调试工具便宜踩坑之后能找到的参考也最多。整套系统的定位很明确输液过程自动监护能测滴速、能按设定值自动调节、能低液位报警如果液体走空或者滴速异常还能锁止管路。下面我就把整个项目的设计思路、原理图要点、传感检测原理、核心算法、仿真联调和实际排坑过程完完整整地拆开讲清楚。1. 智能输液监护调控系统的整体架构与设计思路1.1 系统要解决什么问题传统输液的两个痛点很突出一是滴速调节完全靠手动滚轮一旦设定好很难保证长时间稳定。患者稍微移动手臂或者针头位置受压滴速就会明显变化结果就是药液滴速忽快忽慢有些药物对滴速又有严格要求快了可能引发不适慢了影响治疗效果。二是监护成本高护士不可能全天守在床边家属盯着吊瓶也容易疲劳尤其是夜里。液体走空之后如果没能及时处理回血、凝血、空气进入管路等问题都是潜在风险。为了解决这些问题这套系统做了几个核心升级滴速闭环控制不再只是“检测并显示”而是根据当前实测滴速与设定滴速的偏差通过PID算法调节蠕动泵转速让滴速自动稳定在设定范围。传感防护升级在原有红外对管滴速检测的基础上增加了液位检测和气泡检测逻辑多重保障异常情况。报警与锁止机制滴速偏差持续超出阈值、液位过低、连续气泡时系统会声光报警并停止电机避免进一步风险。全链路开源原理图、STM32工程代码、Proteus仿真工程全部打包方便不同基础的读者直接复现。从实际应用场景看这套系统更偏向“教学演示样机验证”我在设计时也特意控制了成本整套BOM不超过百元。需要强调的是它适合作为实验室研究和工程训练项目不等于商用医疗设备要实际进临床还得过严格的医疗认证和合规流程。1.2 模块划分与选型逻辑整个系统可以划分成几个清晰的功能模块模块具体方案作用选型理由主控STM32F103C8T6信号采集、算法运算、控制输出Flash 64KB、RAM 20KB外设丰富开发资料多滴速检测红外对管 LM393比较器将滴液脉冲转成方波送给单片机成本低、电路简单适合教学演示液位检测光电折反射传感器检测药液液位是否过低非接触式不改动输液管路执行机构12V蠕动泵 MOS驱动按PWM占空比调节输液速度泵管不接触药液无菌性好人机交互LCD1602 4个按键显示滴速/设定值输入操作显示直观、成本低、驱动简单报警有源蜂鸣器异常状态声光提示直接IO控制无需额外驱动电路电源USB 5V AMS1117-3.3为主控和传感器供电简洁、容易获得12V泵独立供电这里重点说下为什么选STM32F103C8T6。很多入门者会觉得做一个输液监控是不是用51单片机就够从“能完成”的角度看确实够但这个项目的目标不只是完成功能而是要把“闭环调节”做好。闭环调节需要定时器做PWM输出需要外部中断做滴速脉冲计数需要ADC读取传感器模拟量还需要在中断和主循环之间合理安排实时任务。STM32的TIM、EXTI、ADC、USART、I2C这些资源都很充裕而且后续想扩展WiFi联网、蓝牙传输都非常顺手。关键是这个芯片在Proteus里有仿真模型这就意味着哪怕手头没有实物硬件也能先把算法和逻辑跑通。电机执行机构我用了蠕动泵而不是步进电机加注射器方案。原因有两方面第一蠕动泵的泵管是独立的药液只在泵管里流动泵体机械结构不接触液体从原理上就更符合输液的卫生要求第二步进电机控制注射器需要自己搭建复杂的进给机构行程有限重复精度和管路匹配都比较难做好对开源项目来说门槛一下子抬高了。蠕动泵就简单很多给PWM就转转速和流量近似成线性关系拿来演示闭环控制非常直观。2. 原理图设计从最小系统到驱动电路的关键细节2.1 主控最小系统与晶振电容计算STM32F103C8T6的64KB Flash版本最小系统核心包括电源去耦、复位、启动模式选择和外部晶振。不要小看这些“基础电路”很多项目的随机故障都出在这里。电源部分系统输入使用USB 5V经过AMS1117-3.3稳压到3.3V给单片机。在VDDA引脚与VDD之间我串了一个10Ω电阻再加一个1uF电容到地这样能起到一定的滤波隔离作用降低数字电路噪声对内部模拟电路的影响。每一个电源引脚旁边都放了0.1uF的高频去耦电容电容必须尽量靠近芯片电源脚否则去耦效果会打折扣。这个大原则适用于板上所有芯片不要嫌电容多。复位电路用的是经典的10K上拉电阻并联0.1uF电容到地。上电瞬间电容充电NRST保持低电平单片机复位充电完成后NRST被拉高到3.3V芯片开始运行。如果设计时把复位脚当普通IO用或者把电容省了会很容易发生上电复位不彻底的现象。晶振电路值得单独讲一下。外部8MHz无源晶振配合两个22pF负载电容。很多人直接把相同容值的电容往上一焊也不知道为什么选这个值。其实这个值要按晶振负载电容来计算CL (C1 × C2) / (C1 C2) Cstray。Cstray是引脚寄生电容和PCB走线寄生电容一般取3~5pF。如果用两个22pF电容那CL (22 × 22) / (22 22) 4 15pF正好落在常见8MHz晶振负载电容10~20pF的范围内。这个参数决定了晶振实际振荡频率是否精确对后面串口波特率、定时器计时精度都有影响。启动模式这里要特别提醒BOOT0必须通过10K电阻下拉到GNDBOOT1同样下拉或直接接地。这样保证芯片从上电开始就执行Flash中的用户程序。如果BOOT0悬空或拉高STM32会进入系统存储器引导模式表现为程序烧进去不跑非常坑。我以前在面包板上搭电路就遇到过这个问题后来固定用排针做成跳线帽状态一目了然。晶振下方不要走其他信号线否则高频振荡信号容易干扰相邻走线。如果是双面板晶振区域地在顶层可以挖空或者铺地隔离底层不要有其他敏感信号穿过。2.2 滴速检测与电机驱动电路滴速检测电路是这套系统的关键输入。红外发射管和接收管以对射方式安装在输液滴壶两侧水滴落下时会遮挡红外光路接收管输出电流产生变化。这个变化要先转换成电压再用比较器整形才能给单片机识别。发射管限流电阻一般取1K到1.5K具体数值取决于红外管的额定工作电流。以典型红外LED额定20mA左右计算如果工作电压3.3V限流电阻R (3.3 - VF) / I红外管正向压降一般在1.2V~1.5V取20mA时R大约在90Ω~105Ω。但我实际试过用1K电阻把电流限制在2mA左右配合高增益接收管和比较器照样能稳定检测而且还能降低功耗。这里需要根据实际传感器灵敏度调不要死背公式电路要在暗环境测试一下余量。接收管的输出电压是一个微弱且缓慢变化的信号直接接单片机IO不行我用了LM393比较器构成滞回比较器。参考电压可以用电位器分压得到滞回量通过正反馈电阻调整。这样输出就是干净的数字方波直接送给PA0作为外部中断输入。没有滞回的话水滴遮光瞬间信号可能在阈值附近反复抖动导致中断连续触发好几次滴速测量自然就不准。电机驱动部分12V蠕动泵的电流大约在300~800mA之间这已经远超单片机IO口驱动能力了。我用的是N沟道MOS管AO3400做低边开关PWM信号通过一个10K电阻下拉到GND接到MOS管栅极。电机一端接12V另一端接MOS漏极源极接地。这个方案功耗比L298N驱动模块低很多成本也低。注意电机两端必须并联一个续流二极管一般用1N5819或SS34方向是负极接12V正极接电机与MOS的连接点。如果没有这个二极管电机感性负载在PWM关断瞬间会产生很高的反向电动势轻则影响驱动波形重则直接击穿MOS管。PWM频率也要选好。我的经验是用10kHz到20kHz太低电机会发出明显的啸叫声太高又增加了MOS管的开关损耗。实际测试中蠕动泵对PWM频率并不太敏感我最后固定在20kHz听不到噪声控制也平稳。整个系统的供电要注意12V蠕动泵电源和3.3V控制电路电源必须共地否则MOS管无法正确开关但12V回路上要尽量远离信号线避免泵的启停在电源地上产生毛刺干扰滴速脉冲计数。3. 传感检测原理与信号处理3.1 红外对管滴速检测原理说到滴速检测最直观的想法就是“数水滴”。但真正把“水滴滴落”这件事变成单片机认识的脉冲信号中间还有几层信号处理要讲清楚。红外对管的基本工作方式有两种一种是对射式发射管和接收管面对面安装水滴从中间穿过另一种是反射式发射管和接收管同侧通过水滴表面的反射光变化来判断。我对射式用得最多因为原理最直接、抗环境光干扰能力更强。对射式安装时红外发射管持续发光接收管在没有水滴遮挡时接收到较强光信号输出低阻抗状态。当一滴水从滴壶中落下并经过光路时光线被水珠遮挡和折射接收管接收到的光强瞬间下降输出状态改变。这个改变被LM393滞回比较器捕捉后输出一个下降沿脉冲。单片机外部中断检测到这个下降沿就认为“滴了一滴”。实际波形远没有教科书那么理想。水滴不是纯黑体它落下时会在光路里产生折射、散射导致接收信号不是干脆的“有光/无光”两态而是有一个渐变过程。如果环境光比较强信号基线还会上下漂移。这也是为什么一定要用比较器整形而不是直接把接收管输出接单片机。我用滞回比较器的另一个好处是一旦信号超过高阈值输出翻转只有信号回落到低阈值以下输出才复位这中间的回差就消除了临界区域的抖动。软件上还有一个细节就是滴速计算的窗口不要用“固定时间内计数”的方式而是用“相邻两个下降沿之间的时间间隔”。原因很直接固定时间计数在低速时误差特别大比如设定20滴/分钟你要数满60秒才能得到稳定读数实时性太差。间隔法在每一滴落下来之后马上就能刷新瞬时滴速配合滑动平均滤波既能快速响应又不会跳动太厉害。3.2 液位与气泡检测的实现方案低液位检测是输液监护的重要安全功能但很多入门项目会忽略它。我这里提供两种低成本方案大家可以根据手头条件选择。第一种是光电折反射式。在输液瓶或输液袋的瓶颈位置也就是液位下限对应的高度贴一个红外反射式传感器。发射管发出红外光如果这里还是液体光在液体内部传播路径发生改变接收管收到的信号特征明显当液位下降到传感器位置以下传感器下面是空气光的折射和反射特性完全不同接收管信号发生跳变。这种方案的好处是完全非接触不需要在输液管路里插入任何东西。第二种是浮子干簧管方案。把一个小磁环浮子放在输液瓶口附近的浮标腔内当液面下降到设定位置浮子落下干簧管闭合或断开产生一个开关信号。这个方案成本更低逻辑也更简单因为输出直接就是数字量不需要信号调理电路。缺点是浮子结构要额外做一个小腔体对于演示项目来说加工起来稍微麻烦。气泡检测用到了超声检测的原理。普通输液管如果出现连续气泡很容易导致空气进入静脉风险很高。我在这版里预留了气泡检测接口使用一个超声发射/接收对管贴在输液管两侧。超声波在液体中衰减较小接收端能收到明显信号当管路里出现气泡时超声在气液界面大量反射接收信号强度急剧下降系统据此判断有气泡通过。这个检测方式和液位检测一样属于非接触式不影响药液流动。需要提醒的是这些检测方案在工程上都很依赖安装位置和固定方式。传感器与滴壶、管路的相对位置一旦变化信号幅度就会明显不同。所以实际调试时一定要留出可调节的机械结构用3D打印或者亚克力支架把传感器固定好不要用胶带一次性粘死。4. 核心控制算法与代码实现4.1 滴速测量与PI闭环控制眨眼之间传感器把滴速变成了脉冲流接下来就是程序的主场了。滴速测量的核心代码逻辑我用的是外部中断加定时器计时。物体从滴壶落下遮断光路产生下降沿触发EXTI中断。中断服务程序里我记录定时器当前计数值再减去上一次触发时的计数值就得到了相邻两滴的时间间隔T。瞬时滴速speed 60000 / T单位是滴/分钟。之所以是60000是因为1分钟等于60000毫秒而T的计时单位是毫秒。这里有一个细节定时器是16位的需要处理溢出。我用TIM2做计时器预分频设为72-1也就是1MHz计数频率计数器最大计数值65535。两个滴液间隔如果超过65.535秒说明滴速低于每分钟1滴这种情况下可以认为输液管基本堵死或滴壶空了直接触发报警。工程代码里对计数器溢出做了检测而不是让整个程序跟着溢出转否则低速时测出来的数据会突然变成几千非常离谱。为了抑制连续测量值的抖动我维护了一个长度为5的滑动平均缓冲数组新数据进来覆盖旧数据输出取平均。这个处理比单纯用瞬时值平滑得多又比长窗口滤波响应快。PI控制用的就是经过滤波后的滴速。PI控制的初衷是让系统自动逼近设定滴速。误差e(k) 设定值 − 实际值P项负责快速响应I项负责消除稳态误差。代码实现最关键的两个点一是积分限幅二是指定输出限幅。#define INTEGRAL_LIMIT 200 #define PWM_MIN 0 #define PWM_MAX 999 static int16_t integral, last_out; int16_t speed_pi(uint16_t target_speed, uint16_t actual_speed) { int16_t err (int16_t)target_speed - (int16_t)actual_speed; integral err; if (integral INTEGRAL_LIMIT) integral INTEGRAL_LIMIT; if (integral -INTEGRAL_LIMIT) integral -INTEGRAL_LIMIT; int16_t out (int16_t)(10 * err) integral; if (out PWM_MIN) out PWM_MIN; if (out PWM_MAX) out PWM_MAX; last_out out; return out; }为什么积分要限幅因为蠕动泵的PWM占空比没法无限增大如果误差一直存在积分项会不断累加到非常大等误差反向时系统要先把这一大坨积分消耗完才能真正反向调节表现出来就是大超调和大幅震荡。限幅之后积分项最多贡献一定量的PWM增量系统动态响应会好很多。参数整定方面我没有用太复杂的工具直接在Proteus仿真里先粗调再在实物上微调。P项从小的Kp5开始观察响应是否超调再逐步加大I项先设成0等P项能稳定跟随后再逐步加积分强度观察静态误差的消除速度和超调量。对于这套蠕动泵系统Kp在8~15、Ki在0.5~2.0范围内表现比较理想。这个方法就是经典试凑法虽然土但非常有效。闭环之外还有一个安全逻辑如果实际滴速持续5秒低于设定值70%或者液位检测到低液位或者检测到连续5个气泡系统立即置报警标志停止电机输出并让蜂鸣器鸣叫。这个过程不能依赖PI算法自己慢慢调回来因为有些情况比如液位已经空了泵再转也没有意义必须主动停。4.2 按键菜单与状态机设计再好的控制逻辑也得有个人机交互外壳。这版系统我用4个按键加LCD1602搭了一个简洁的菜单状态机划分成待机、运行、设置、报警四个主要状态typedef enum { SYS_STANDBY, SYS_RUNNING, SYS_SETTING, SYS_ALARM } sys_state_t;待机状态下按“设置”键进入设置界面通过“加”“减”键调整目标滴速按“确认”键保存并回到待机。此时按“启动”键系统进入运行状态电机启动滴速检测和PI控制全部投入。运行过程中如果检测到异常系统切入报警状态停止电机蜂鸣器响操作者排除故障后按“确认”键才能复位。按键处理必须消抖。我用的是10ms软件延时消抖在按键扫描函数里检测到电平变化后延时10ms再读一次确认无误才更新按键标志。虽然延时消抖在理论上会阻塞主循环10ms但对这个量级的系统影响不大实现起来最简单可靠。注意不要在中断里做按键扫描按键逻辑适合放在主循环中执行。主循环的整体顺序是这样的扫描按键更新设定值读取传感器状态刷新滴速缓冲区调用PI控制器计算新的PWM占空比更新LCD显示内容检查报警条件并执行状态切换。中断只负责两件事滴速脉冲的下降沿计数和读取定时器值、定时器溢出标志维护。把控制算法放在主循环而不是中断里是因为PI运算涉及浮点和数组操作放中断里容易导致响应延迟不稳定还可能与滴速测量的实时性冲突。代码工程里我还加了IWDG看门狗喂狗操作放在主循环末尾。这样就算程序因为干扰跑飞看门狗也能在几百毫秒内把系统拉回复位状态保证输液监护在无人值守时不会彻底死机。5. Proteus仿真联调与数据验证5.1 仿真平台搭建流程实物硬件做出来之前先用Proteus把整个系统验证一遍能省掉大量排查问题的时间。Proteus对STM32F103C8T6的支持还算不错仿真模型可以直接拿来用。搭建流程不复杂新建Proteus工程在选择元件时搜索STM32F103C8T6把它放到画布上。搭建最小系统电路包括VDD、VDDA接3.3VVSS接地NRST接复位电路OSC_IN和OSC_OUT接晶振和负载电容。从元件库添加LED、按键、LCD1602、LM393比较器、电位器等仿真元件按原理图连线。用信号发生器或者脉冲源PULSE / DPULSE正电平宽度几百毫秒模拟滴速传感器的输出接在PA0引脚上这样就能方便地模拟不同的滴速脉冲频率。在Keil MDK工程里勾选Option for Target - Output - Create HEX File编译生成HEX文件。回到Proteus双击STM32芯片在Program File一栏选择生成的HEX文件点运行。需要注意Proteus里的信号发生器参数和真实滴速不对应的话仿真结果没有参考意义。我一般用一个简单的换算1Hz的脉冲频率等于60滴/分钟。比如想模拟40滴/分钟就把脉冲源频率设为0.667Hz也就是周期1.5s。这个脉冲频率可以通过Proteus里的脉冲源属性直接设置。还有一点要提前说Proteus对某些外设的仿真支持并不完善。比如部分版本的Proteus对ADC输入、DMA的仿真比较有限对传感器的建模也比较理想化。我的处理办法是不在仿真里硬抠ADC细节用电阻分压来模拟传感器输出电平关键看控制逻辑和算法而不是模拟传感器物理特性。5.2 测试结果与误差分析仿真搭好后我做了几组典型测试。设定滴速分别给到30、40、50、60滴/分钟记录系统稳定后的实测滴速再观察PID输出的响应情况。设定滴速滴/分钟模拟脉冲频率稳定后实测滴速偏差调节时间300.5Hz30.00约3s400.667Hz39.8-0.2约4s500.833Hz50.10.1约5s601.0Hz59.7-0.3约6s这里有个小技巧仿真的滴速脉冲是由信号发生器强制产生的实际上等于“外部给定了一个固定的滴速”系统测量的结果当然就接近给定值。那闭环控制体现在哪里体现在当你把设定值突然从40调到60时PWM占空比会迅速增大系统输出跟随输入变化如果你在Proteus里接一个虚拟示波器观察电机驱动PWM波形能看到占空比随误差的动态调整过程。仿真环境中没法真实转动蠕动泵所以“实测滴速”其实是信号源给的但整个控制链路从脉冲计数、滤波、PI运算到PWM输出都在仿真里跑通了。误差分析方面有两个主要来源。一是测量窗口的固有量化误差相邻滴间隔取整到毫秒在高速滴速如150滴/分钟时间隔只有400ms1ms误差占比约0.25%影响不大但在低速如10滴/分钟时间隔6s1ms误差影响极小反而是滑动平均窗口长度会带来响应延迟。二是PWM输出对电机转速的量化影响占空比调节步进为1/999体现在滴速上约0.1滴/分钟可以忽略。这组仿真验证下来整个系统的逻辑和算法框架可以认为基本正确。剩下的事情是把这套逻辑移植到真实硬件再针对传感器信号质量做进一步校准。6. 典型问题排查与避坑经验6.1 硬件问题做实物时最容易出问题的点按我的经验排个序。电机不转排在第一位。常见原因有三个第一MOS管栅极没有正确上拉到低电平PWM空闲时悬空导致MOS误导通或者无法导通我用的10K下拉电阻就是为了解决这个第二12V电源和3.3V电源没有共地MOS驱动回路不通第三PWM频率设置得太高超出了MOS管栅极驱动能力的实际开关速度。碰到电机不转先用万用表量MOS管栅极电压和漏极电压再查共地90%的问题能排查出来。滴速检测信号不稳定是第二个大坑。表现为液晶屏上的滴速数值乱跳或者一个水滴触发多次中断。处理方法顺序如下先用示波器看比较器输出波形确认是不是存在抖动再调整电位器让参考电压在信号波形的中间位置附近增加滞回量扩大正反馈电阻值让输出翻转更果断。如果环境光干扰严重在红外管外侧加遮光罩或者减小发射管限流电阻提高光强。电源干扰不好查但确实存在。蠕动泵启动瞬间电流明显增大如果12V和3.3V电源地线布局不合理泵的电流波动会在地线上产生压降导致单片机复位或者ADC读数异常。解决办法是在电源输入端加大容量电解电容泵的电源线尽量短粗信号线远离泵电源线。晶振不起振也是容易忽视的问题。程序烧进去板子没反应首先怀疑晶振。解决办法是检查负载电容的值是否和晶振规格匹配检查晶振引脚是否虚焊如果用的是贴片晶振位置不对也会有起振困难。手头有示波器就直接测OSC_OUT引脚没有示波器可以量一下NRST引脚电压如果复位正常释放而程序不运行基本能锁定晶振或BOOT配置问题。6.2 软件问题软件层面的坑主要集中在中断和数据类型上。中断标志没清除是一个典型问题。外部中断服务函数里检查EXTI_GetITStatus之后必须调用EXTI_ClearITPendingBit清除中断标志否则中断服务函数会反复进入表现为滴速数值固定在一个很大的数或者系统像卡住一样。定时器更新中断同理不清除更新标志溢出中断会连续触发。数据类型溢出很隐蔽。用uint16_t存储相邻滴间隔看似没问题一旦低速时滴速很低或定时器溢出处理不及时计算出来的滴速可能是一个荒谬的大数。从60000 / interval算滴速时如果interval是uint16_t某些编译器会把60000当成有符号int处理导致结果变成负数进而影响PI控制的误差计算。我的做法是在所有关键变量上强制使用int32_t并在除法之前加数据类型判断。看门狗喂狗位置不对会导致程序反复复位。如果喂狗放在主循环的开头而主循环里某个函数执行时间太长看门狗可能提前清零系统。正确做法是把喂狗放在主循环末尾确保整个主循环完整执行一遍后再喂狗。中断里不要喂狗否则即使主循环卡死中断仍然能喂狗看门狗形同虚设。最后再说一个“软硬结合”的坑仿真跑通的代码拿到实物上不工作原因往往不是算法错了而是信号质量没有达到算法假设的理想方波标准。这时候不要在代码层面强行加延迟或者改滤波窗口去“赌”先回到硬件用示波器看信号把传感器输出调成干净方波再回头检查代码这样排查路径才是高效的。我个人在实际制作这个项目时最耗时间的不是代码也不是原理图而是滴速传感器的机械固定。红外对管和滴壶之间的相对位置必须稳定稍微歪一点信号幅度就差很多。后来我用一小块亚克力做了一个带凹槽的夹座把滴壶正好卡进去传感器固定在两侧这个结构问题才算彻底解决。如果你也想复刻这个项目我建议从一开始就把传感器的固定架设计进去别等电路调完再补机械结构。整个系统后续还能扩展的方向很多比如加ESP8266做远程监护、加RTOS改善任务调度、接云平台做输液数据记录基础打好之后这些都有的玩。