资讯动态

STM32智能医疗输液点滴系统:嵌入式硬件与PID控制实践

发布时间:2026/9/8 15:05:14 来源:尧图企业网站定制
我手里的这套STM32智能医疗输液点滴系统算是我折腾过的嵌入式项目里比较有代表性的一个。它不光是单片机外设的堆砌而是把传感器采集、电机控制、人机交互、通信协议这些嵌入式基本功串在了一起正好能覆盖一个完整产品的核心链路。整个项目开源包含完整的代码工程、原理图源文件以及仿真工程适合正在学STM32想找个综合项目练手的同学也适合做毕业设计或者医疗电子方向产品预研的工程师参考。这个项目的核心功能很简单实时检测输液过程中的点滴速度自动调节输液速度到设定值当液面过低、输液管堵住或者滴速异常时立刻声光报警并采取措施通过按键可以设定目标滴速OLED屏幕实时显示当前滴速、设定值和工作状态预留串口可以把数据传给上位机或者监护系统。整套系统以STM32F103C8T6为主控红外对管检测液滴步进电机驱动蠕动泵调速软硬件方案都经过实测验证开源资料里有完整的设计文档拿来就能直接复现。1. 系统整体设计从需求到方案选型1.1 功能需求拆解智能输液系统的核心痛点说得直白一点就是医院输液场景里护士人手不够、病人又不能一直盯着输液瓶。传统输液靠人工巡检滴速靠护士目测数数调节液面低了也只能靠呼叫铃。这套系统要解决的就是把“人眼观察、手动调节”变成“传感器检测、自动控制”同时把异常情况主动暴露出来。我首先把功能需求拆成四个模块检测模块实时检测点滴速度和输液管内的液面状态。滴速检测精度要求达到1滴/分钟级别响应时间在1秒以内。控制模块根据设定滴速和当前滴速的偏差自动调节电机转速进而控制输液流速。调速过程不能有剧烈波动否则容易引起病人不适。人机交互模块支持滴速设定、当前状态显示、报警提示。显示刷新率至少每秒1次按键操作要简单直接最好两三个键就能完成所有设置。报警模块滴速偏差超限、液面过低、管路堵塞、电机故障都要能报警。报警方式包括蜂鸣器响声和LED闪烁同时通过串口上报给上位机。这四个模块对应到硬件上就是传感器电路、电机驱动电路、OLED和按键、蜂鸣器和LED全部由STM32统一调度。1.2 核心器件选型逻辑主控选择STM32F103C8T6也就是大家常说的“C8T6蓝色 pill”。这颗芯片48脚Flash 64KBRAM 20KB主频72MHz成本不到十块钱但外设资源相当齐全3个USART、2个SPI、2个I2C、4个16位定时器、10个12位ADC通道做输液监控这种中小型系统绰绰有余。选择它的另一个原因是对开发者友好网上资料最多出了故障也容易排查这个在开发调试阶段非常值钱。液滴检测采用红外对管方案一对发射管和接收管卡在滴壶两侧。液滴滴落时穿过红外光束接收管收到的光强发生变化输出信号经过比较器整形后变成下降沿脉冲送进STM32的外部中断引脚。这个方案成本极低响应快而且不接触液体符合医疗场景的卫生要求。备选方案有电容式液滴传感器和高速摄像头方案前者精度尚可但电路复杂后者成本过高不适合做开源项目。电机选择28BYJ-48步进电机配合ULN2003驱动板。输液调速不需要太高的转速步进电机可以精准控制转速和转角非常适合这种慢速、需要精确调速的场景。28BYJ-48扭矩足够带动蠕动泵挤压输液管驱动电路就是达林顿管阵列简单可靠成本几块钱。后面调试时要注意这个电机的转速上限在15转/分钟以内实际使用中要匹配蠕动泵的流量曲线不能一味拉高转速。显示用0.96寸I2C接口OLED4个引脚就能搞定不占用太多IO。OLED在阳光下也看得清功耗低做便携医疗设备很合适。如果后面要产品化可以换成TFT彩屏或者段码液晶代码结构调整不大。1.3 整体架构和数据流我把整个系统的信息流梳理清楚再动手画原理图写代码后面开发就顺畅很多。输入侧有三路信号红外对管的液滴脉冲、三个按键的按下事件、串口下发的远程指令。输出侧有四路OLED显示实时数据、步进电机控制滴速、蜂鸣器和LED报警、串口上报状态。STM32在中间起“大脑”作用用外部中断记录液滴脉冲计数用定时器做1秒的滴速计算窗口用PID算法算出电机转速调整量再用PWM或者步进脉冲控制电机。整个系统是个闭环控制系统目标是让实际滴速稳定在设定值附近。这套架构最大的好处是模块化每个功能都能单独调试出问题不会牵扯一片。2. 硬件原理图设计要点2.1 STM32最小系统与电源设计原理图先画最小系统。STM32F103C8T6的启动配置需要BOOT0和BOOT1引脚正常情况下都接低电平从Flash启动。复位电路用10K电阻上拉加100nF电容到地经典的RC复位。晶振电路用8MHz主晶振两个20pF负载电容走线时要让晶振靠近MCU的OSC_IN和OSC_OUT引脚不要打过孔绕一大圈否则容易出现起振不稳的问题。电源部分我做了两级设计。系统总输入是5V USB或者5V适配器先经过一个SS14肖特基二极管防反接再进AMS1117-3.3稳压给MCU供电。电机驱动直接吃5V不和MCU共用3.3V。特别要注意的是红外对管的发射端和接收端分别供电中间加隔离电阻避免电机启动瞬间的大电流拉低传感器电源导致误触发。电源走线是新手最容易翻车的地方。模拟地、数字地、功率地要单点接地别铺完铜就完事。我在给这套系统画PCB时把5V电机电源走线加宽到40mil以上3.3V信号线走15mil所有去耦电容都紧贴芯片电源引脚实测下来电机启停的时候MCU供电纹波控制在50mV以内稳定得很。2.2 液滴检测电路设计红外液滴检测的原理说起来简单做起来有几个细节要注意。我用的方案是发射管串一个150欧电阻限流接收管接成光敏模式输出端接一个10K上拉电阻再进LM393比较器。比较器的参考电压用一个10K电位器调节找到一个刚好能区分“有液滴遮挡”和“无液滴”的阈值。比较器输出接STM32的PA0引脚配置成外部中断下降沿触发。为什么要接比较器而不是把接收管输出直接接单片机因为接收管的模拟电压变化不是陡峭的数字电平直接进单片机引脚会导致多次触发。LM393输出的是干净的数字边沿配合外部中断每一滴只触发一次软件统计就准了。传感器安装位置也重要。滴壶上开孔的角度要保证红外光能穿过液滴的掉落轨迹同时避免环境光干扰。我在滴壶外面加了遮光套实测在强光环境下依然能稳定检测。发射管和接收管之间的距离大概15毫米太远了信号弱太近了容易误触发。2.3 电机驱动与报警电路步进电机驱动用了ULN2003这是最省事的方案。每个输入引脚串1K电阻到STM32的PB0到PB3输出直接接28BYJ-48的五线四相接口。ULN2003内置续流二极管电机线圈的反向电动势不会损坏MCU。控制逻辑上用定时器产生步进脉冲按四相八拍的顺序给电机的四路输入每给一个脉冲电机转一步步进角是5.625/64度一圈需要4096个脉冲。报警电路分两路。蜂鸣器用有源蜂鸣器5V供电MCU的PB5引脚通过一个S8050三极管驱动。LED用高电平点亮串联330欧限流电阻接PC13。软件里报警有三种模式滴速偏差过大时蜂鸣器响0.5秒停0.5秒循环液面低时连续响按键按下时短响一声做操作反馈。声音节奏区分开现场使用不用看屏幕就知道发生了什么。2.4 人机交互与通信接口按键电路用三个独立按键接PB12、PB13、PB14每个按键对地接10K下拉电阻按下时对应引脚读到高电平。为了省IO没有做矩阵键盘三个按键够用了一个菜单键切换设定项一个加键一个减键。OLED用I2C接口接PB6和PB7地址是0x78。串口用USART1的PA9和PA10通过CH340芯片转USB接电脑调试。我在固件里做了一套简单的串口协议每秒发送一帧状态数据格式是帧头当前滴速设定滴速报警标志上位机直接解析就行。这个串口接口后续接ESP8266或者NB-IoT模块做物联网代码改动很小。3. 软件代码实现与核心逻辑3.1 系统状态机设计软件架构我没有用裸机大循环里堆if-else的方式而是定义了一个简单的状态机把系统运行过程分成四种状态初始化状态、待机状态、运行状态、报警状态。初始化状态完成外设配置滴速清零电机回零OLED显示系统自检信息。待机状态等待用户设定滴速此时电机不运转检测功能开着但不输出控制。运行状态是主工作状态执行滴速检测、PID调速、数据显示。报警状态在异常时进入优先执行报警动作同时暂停调速等故障解除后回到运行状态。状态机的实现就是一个枚举变量加一个switch每个状态下执行对应的函数。好处是逻辑清晰不会出现“这个标志位怎么被改了”的烦恼。系统涉及电机启停、报警优先级这些复杂的联动场景状态机比裸奔的if-else可靠得多。实际调试时我在每个状态入口处加了一行串口打印看状态跳转一目了然排查问题效率高很多。3.2 液滴检测与滴速计算液滴检测的核心是外部中断和定时器的配合。PA0配置成下降沿触发的外部中断每次进中断服务函数滴液计数值加一。同时用一个1秒的定时中断每秒做一次滴速计算。滴速计算的逻辑如下把当前秒的液滴计数减去上一秒的计数得到的差值就是这一秒的瞬时滴速。但瞬时值抖动太大我用了一个滑动平均滤波缓存最近5秒的滴速值取平均作为实际滴速参与PID计算。uint8_t drop_count 0; // 外部中断里累加 uint16_t speed_buffer[5] {0}; // 5秒滑动缓存 uint8_t buffer_index 0; void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update)) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); speed_buffer[buffer_index] drop_count; drop_count 0; buffer_index (buffer_index 1) % 5; } }还有一个关键点输液管路有个参数叫“点滴系数”意思是1毫升液体相当于多少滴常见的有15、20滴/毫升。这个参数在工程里做成了宏定义用于把滴速换算成毫升/小时显示屏上同时给出滴/分钟和毫升/小时两种读数一个设置即可切换。3.3 PID控制与电机调速滴速控制我用的是增量式PID算法。为什么用增量式而不是位置式增量式PID输出的是控制量的增量对执行机构的冲击小而且在步进电机这种位置型执行器上天然适配——每次算出一个增量值叠加到当前电机速度上就行。PID的输入是“设定滴速减去实际滴速”的偏差输出是电机转速的调整量。三个参数我通过试凑法整定先只加比例项从小到大调直到系统出现等幅振荡然后加积分项消除静差最后加微分项抑制超调。最终这组参数在我的实验环境里整定结果如下参数数值作用Kp2.8快速响应误差变化Ki0.15消除稳态误差Kd0.08抑制超调与震荡这里要提醒一个关键点电机转速和滴速之间不是线性关系蠕动泵挤压输液管会产生机械滞后所以PID的输出限幅要谨慎设置。我在代码里限定了单次调整量不超过电机最大转速的10%避免出现大幅波动。实际跑下来从启动到稳定在目标滴速大约需要8到15秒这个响应速度对医疗场景完全够用。3.4 报警与数据显示报警逻辑单独写了一个模块。每次滴速计算完成后检查三个条件当前滴速与设定值偏差是否超过15%连续10秒滴速为零是否判定堵管以及电池电压是否偏低。任一条件满足就进入报警状态。OLED显示分为三个界面用菜单键切换主界面显示当前滴速和设定滴速参数界面显示目标毫升/小时和点滴系数状态界面显示系统运行时间和电机转速百分比。每个界面刷新间隔200毫秒用的I2C接口刷太快反而容易闪烁实测200毫秒效果最好。主界面代码如下OLED的驱动我用的经典的SSD1306库做了汉子和数字混合显示布局按一个坐标表排布这样改显示位置就是改坐标比一句一句写方便维护void display_main_screen(void) { char line1[16], line2[16]; sprintf(line1, Now:%d d/m, current_speed); sprintf(line2, Set:%d d/m, target_speed); OLED_ShowString(0, 0, line1); OLED_ShowString(0, 3, line2); if (alarm_status ! 0) { OLED_ShowString(0, 6, !!! ALARM !!!); } }护士看到报警信息能快速锁定问题。OLED屏幕128乘64分辨率分四行显示第一行当前滴速第二行设定滴速第三行报警区域其余留白没有多余的装饰医疗设备界面就得简洁直接。3.5 主循环代码框架整个主循环的框架就是个大循环里按顺序执行各个模块的刷新函数每一秒的定时器到点后执行采集和控制的组合。int main(void) { SystemInit(); delay_init(); // GPIO、定时器、串口、OLED、外部中断初始化 peripherals_init(); status SYS_INIT; while (1) { switch (status) { case SYS_INIT: do_sys_init(); break; case SYS_STANDBY: process_keyboard(); display_main_screen(); break; case SYS_RUNNING: speed_control_loop(); // 滴速检测 PID 电机输出 process_keyboard(); display_main_screen(); uart_send_status(); break; case SYS_ALARM: do_alarm(); // 蜂鸣器 LED 状态上报 process_keyboard(); break; } } }这种架构写在main函数里不到40行每个功能拆成独立函数单个模块几百行全工程源码加起来也就两千行左右可读性和可维护性对比“大乱炖”式的裸机代码优势明显。4. 仿真平台搭建与验证4.1 Proteus电路搭建仿真我用的是Proteus 8.9这也是开源资料里附带仿真工程所用的版本。在Proteus里搭建系统电路先放STM32F103C8T6再放OLED、按键、蜂鸣器、LED、ULN2003、步进电机、红外对管等外设。Proteus的元件库里STM32的型号是齐全的但有些外设模型需要自己拼。比如红外对管我用一个光耦模型代替——发射端接一个开关接收端输出仿真信号通过手动开关模拟液滴遮挡。这样搭建虽然和实物有一些物理差异但对于验证软件逻辑完全够用。电源接法要注意Proteus里STM32的VDD要接3.3VVDDA也要接3.3VVSSA接地如果漏接任何一个仿真时MCU就起不来这算是Proteus仿真的一个经典踩坑点。4.2 仿真中模拟液滴传感器仿真里液滴检测的模拟方法相对简单。我用一个按钮接在红外接收端到地之间按下一次代表一滴液滴通过松开再按下代表下一滴。这个按钮接在PA0上配合Proteus的虚拟中断模式产生的下降沿就能触发STM32的外部中断。为了仿真更真实可以做一个定时脉冲发生器用Proteus里的时钟源或者PULSE模式每隔固定时间自动产生一个脉冲这样就能模拟稳定的滴速输入测试PID控制在不同滴速下的响应。我在仿真里做了几组测试分别设定滴速为20、30、40滴/分钟观察系统是否能稳定跟踪。仿真结果显示稳态误差在正负1滴/分钟以内这个精度与实物的测试数据非常接近。仿真验证的价值在开发早期就能发现逻辑漏洞省下了烧录–接线–调试这个循环的时间成本。4.3 代码联调与仿真验证仿真工程的调试流程是先在Keil里编译工程生成hex文件然后在Proteus的MCU属性里加载这个hex文件点击运行就能看到仿真结果。Keil工程需要做两个配置Debug选项选择“Use Simulator”或者通过Proteus VSM接口进行联调另外要在Target选项卡里勾选“Use MicroLIB”否则Keil默认的C库较大Flash只有64K的C8T6容易装不下。我自己习惯先开着Keil和Proteus联调在代码里打断点仿真运行时能看到外设寄存器的实时值这对排查IO配置错误尤其好用。比如按键不响应直接看GPIO输入寄存器的值是不是预期变化立刻就能定位是按键问题还是配置问题。4.4 仿真与实物的差异提醒仿真能验证逻辑但不能代替实物验证。有一个典型的坑在Proteus里跑的时序和真实芯片有差异特别是定时器、外部中断的响应仿真里很理想实物上电磁干扰、电源抖动会影响信号质量。液滴检测就是典型例子。仿真里按一下按钮一个下降沿干干净净。实物的红外信号进入比较器后在阈值附近可能有抖动如果不在软件里加消抖或者做定时器窗口滤波一滴液滴可能触发好几次中断滴速数值直接爆表。另外Proteus里的步进电机模型和实际28BYJ-48的电气特性有差异仿真能看出电机转但看不出实际的扭矩和转速所以电机转速和滴速的实际对应曲线还是得在实物上标定。建议是仿真只用来验证逻辑正确性最后功能调试一定以实物为准。5. 常见问题排查与避坑实录5.1 下载器连接失败不少朋友烧录程序时遇到“No STM32 Target Found”的报错这个我一开始也遇到过。报错提示说如果产品嵌入了调试认证需要确认调试口是否使能但大多数开发板并没有开读保护问题基本出在硬件连接上。优先检查SWDIO和SWCLK两根线是否接反下载器与目标板之间线材长度是否超过20厘米。如果用的STM32F103C8T6最小系统板BOOT0引脚必须接低电平否则芯片上电后进入ISP模式内核不运行调试器连不上。还有一个容易忽略的坑是目标板供电不足下载器通过SWD口给板子供电只有几十毫安如果板上还接了传感器和OLED电流不够直接把MCU拉死USB转串口和ST-Link都认不到。解决方法极其简单给目标板单独供电调试器只接SWD两根信号线加GND别用调试器的3.3V供电。5.2 滴速测量数据跳变滴速数值跳变是调试中遇到最多的问题表现为明明输液滴速很稳定屏幕上显示的数值上蹿下跳从25跳到40再掉回20。排查步骤先看中断计数是否准确。我在外部中断服务函数里加了一个计数器翻转测试把PA0的输入信号同时接入示波器对比示波器脉冲数和LCD上的计数结果发现计数几乎是输入脉冲的两倍说明一个液滴被识别成了两个脉冲。原因是我用的红外对管输出信号在阈值附近有毛刺一个下降沿后面跟了一个小幅度的抖动又触发了一次。解决方法是加软件消抖外部中断触发后关闭中断开启一个5毫秒的定时器定时器到点后再重新打开中断。这样每一滴只进一次中断计数就准了。同时把滑动平均窗口从3秒改成5秒数据平滑了不少报警也更稳定了。5.3 电机不转或者抖动剧烈电机问题大多集中在驱动层。如果发现电机完全不动先测ULN2003的输入引脚波形。很多情况下不是电机坏了而是GPIO没有正确复用为推挽输出导致驱动电流不够。电机抖动的问题更有意思通常是控制脉冲频率太高。我的初始实现里定时器的预分频系数设置不对导致输出到电机的步进脉冲频率达到每秒3000个远超28BYJ-48的最高响应频率电机自然就堵转抖动。后来按电机性能表把最大步进频率限制在800Hz以内问题就解决了。在软件里定义了一个最大转速宏算出的电机目标转速超过上限直接截断从根上避免这个坑。另外ULN2003的COM引脚千万不要忘记接5V电源这个引脚是内部续流二极管的公共端不接的话电机线圈断开瞬间产生的反电动势直接怼到芯片输出端轻则驱动芯片过热重则损坏IO口。5.4 仿真运行卡顿和显示异常Proteus仿真卡顿几乎是通病特别是加了步进电机模型之后每个脉冲都要更新机械位置仿真速度肉眼可见地降低。我的做法是仿真时把步进电机的动画模型换成简单的旋钮模型或者直接在仿真里用数码管显示电机转速不去渲染机械运动仿真速度能快一倍。OLED在Proteus里显示异常也有保底解法Proteus自带的OLED模型时序模拟不完全偶尔出现花屏可以把I2C通信速率从400K降到100K问题基本能解决。5.5 资料包结构说明我在最终的工程目录里整理了一套资料包结构如下Hardware原理图源文件用立创EDA专业版可以打开同时附了PDF版本便于查阅PCB部分也开放了方便直接打样。Firmware完整的Keil MDK工程基于标准外设库开发包含所有源码和头文件编译环境是Keil 5。SimulationProteus仿真工程加载hex文件后可以一键运行。Docs技术文档包含系统设计说明、PID整定记录、测试数据。整理时我把源码里的注释全改成中文了关键函数的调用关系在文件开头的说明块里标注清楚这样即使没有我协助拿到资料的朋友也能快速上手。6. 一点个人体会整套系统从设计到调试完成前前后后花了两周多的时间。最深的体会是做这种软硬结合的项目千万不要一上来就埋头写代码。先把需求梳理清楚把器件选型定了把原理框架画出来把数据流理通后面开发完全是按图索骥效率高很多。关于医疗电子项目有一点必须特别强调这套系统目前定位于教学演示和科研验证如果要做成真正进入临床的医疗设备还需要考虑电气安全隔离、电磁兼容设计、生物相容性、冗余容错设计、软件可靠性等一系列行业规范要求。医疗设备关系到患者生命安全容不得半点侥幸。如果后续想做扩展我建议往两个方向走。一个是加无线通信把STM32的空闲串口接一个ESP8266模块把输液数据上传到云端或者护士站实现远程监控。另一个是换成FreeRTOS操作系统把滴速检测、PID控制、OLED显示、报警处理拆成独立任务系统的实时性和扩展性会再上一个台阶。这套架构的代码稍加改动就可以移植过去底层驱动基本不用动。最后分享一个小技巧调试这种闭环控制系统时在代码里加一个简单的串口波形输出用串口助手把实际滴速和设定滴速画成折线图观察PID控制曲线的趋势。调参数时不用猜看到哪一段振荡就知道该加大微分项看到哪一段有静差就知道该加积分项。这套调试方法比对着屏幕看数值高效得多是我在项目中收获最大的经验。

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

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

免费获取报价