智能送药小车设计:STM32与K210的嵌入式系统综合实践

📅 2026/8/7 4:22:42
智能送药小车设计:STM32与K210的嵌入式系统综合实践
1. 项目背景与核心挑战复盘2021年全国大学生电子设计竞赛的F题“智能送药小车”可以说是一个集机械、控制、视觉、通信于一体的综合性控制类题目。拿到国一意味着我们的方案在稳定性、精度和鲁棒性上经受住了考验。现在回过头看这个题目的核心难点非常明确如何在有限的时间内让一辆小车在模拟的医院病房环境中准确、快速、可靠地完成“取药-送药-返回”的闭环任务。这听起来简单但拆解开来每一个环节都布满了“坑”。题目要求小车从药房出发识别并抓取指定编号的药瓶然后将其运送至对应编号的病房门口最后返回药房。环境地图是已知的但存在十字路口、直角弯、坡道等元素对路径规划和循迹精度提出了高要求。药瓶和病房的编号均需通过视觉识别这要求图像处理算法必须快速且抗干扰。整个任务流程环环相扣任何一个环节的失误都可能导致任务失败或严重超时。当时我们团队的核心思路是“机械求稳控制求精视觉求快系统求简”。我们不追求某个单项技术的极致炫技而是强调整个系统的协同与稳定。主控采用经典的STM32F407负责底层电机控制、传感器融合和任务调度视觉识别则交给了专为边缘AI设计的K210同时备用一个OpenMV作为冗余和调试辅助。这个组合在当时是性价比和可靠性兼顾的选择。下面我就把这套经过实战检验的方案从硬件选型到软件逻辑再到调试中那些“血与泪”的教训毫无保留地拆解一遍。2. 硬件系统架构稳定压倒一切一套可靠的硬件平台是算法得以施展的基础。在电赛这种高强度、短周期的比赛中硬件系统的设计首要考虑的不是性能天花板而是稳定性、易调试性和容错能力。2.1 主控与驱动STM32F407与TB6612的经典组合我们选择了意法半导体的STM32F407VET6作为主控大脑。选择它基于几个很实际的考虑首先其Cortex-M4内核带FPU应付PID运算、路径解算等浮点运算游刃有余避免了定点数处理的繁琐和精度损失。其次它拥有丰富的定时器资源可以轻松产生多路互补PWM来控制电机并且直接支持编码器接口模式读取电机编码器信号几乎不占用CPU资源。最后其充足的IO口和多个UART、SPI、I2C接口为连接各类传感器和视觉模块提供了极大便利。电机驱动方面我们放弃了体积较大、发热严重的L298N选择了东芝的TB6612FNG。这款驱动芯片集成度高内置MOSFET H桥效率高、发热小一片芯片就能驱动两个直流电机并且支持高达1.2A的连续电流对于我们的小车电机来说完全够用。它的控制逻辑也非常简单两个PWM引脚控制速度两个逻辑引脚控制方向STM32的通用定时器输出PWM波直接驱动软件层面非常清爽。注意TB6612的VM电机电源和VCC逻辑电源一定要分开供电。我们曾因为偷懒共用一个电源在大电流堵转时导致逻辑电压被拉低整个主控复位小车瞬间“瘫痪”。后来改用独立的两路电源如7.4V锂电池经降压模块给VM同时再降压到5V或3.3V给VCC和主控问题彻底解决。2.2 视觉模块K210为主OpenMV为辅的双保险策略视觉识别是本题的核心得分点。我们采用了K210作为主识别模块OpenMV4作为辅助和调试备份的方案。K210Kendryte K210是一颗带硬件AI加速器的RISC-V双核处理器其KPU神经网络处理器可以离线运行训练好的模型进行目标检测和分类速度远超传统单片机运行OpenCV算法。我们用它来执行最关键的药瓶编号识别和病房编号识别。它的优势在于一旦模型训练好并部署识别速度极快可达60fps以上且不受光照变化影响只要训练数据覆盖了各种光照条件。我们使用MaixPy IDE进行开发将YOLO-Fastest这类轻量级模型转换后部署识别准确率在赛场上达到了接近100%。OpenMV4则作为辅助。我们主要用它来做一些简单的颜色跟踪、巡线辅助判断以及作为“第三只眼”来监控赛场环境。例如在进入十字路口前用OpenMV的广角镜头提前扫描路口情况辅助决策。更重要的是它是一个极佳的调试工具。当K210的识别出现问题时我们可以迅速通过串口切换让OpenMV顶上进行颜色模板匹配的识别虽然速度慢、受光照影响大但作为保底方案能确保小车不至于因为视觉问题而完全“瞎掉”。2.3 传感器系统多信息融合实现精准定位单纯依靠巡线无法应对复杂路径和精准停靠。我们采用了**“编码器陀螺仪巡线传感器”**的多传感器融合方案。光电编码器500线安装在电机输出轴用于测量车轮的实际转速和行走距离里程计。这是实现闭环速度控制和位置估算的基础。STM32的定时器编码器接口模式可以直接读取四倍频后的脉冲数精度很高。MPU6050六轴陀螺仪用于测量小车的姿态角特别是Z轴的角速度Yaw。在长直道行驶和过弯时融合陀螺仪的角速度数据可以纠正纯粹依靠编码器累积误差带来的航向角漂移实现更准确的直线行走和角度旋转。灰度传感器阵列我们自制了一个8路一体的灰度传感器模块安装在车头底部。用于实时检测地面黑线实现基础的巡线功能。其反馈是PID控制器中最直接的“误差”输入。2.4 机械与执行机构可靠性与精度的平衡抓取机构是另一个容易失分的点。我们设计了一个简单的**“升降式电磁铁”** 方案。结构一个微型舵机控制一个四连杆升降机构电磁铁安装在末端。小车到达药瓶上方后舵机动作使电磁铁下降通电吸起药瓶药瓶顶部贴有薄铁片再升起。优点结构简单、动作快、可靠性高。相比机械爪它不需要复杂的对位只要电磁铁能覆盖到铁片即可吸附容错能力强。调试心得电磁铁的供电线路一定要粗且最好靠近电池直接取电避免通过主控板稳压芯片防止吸合瞬间电流过大导致系统电压跌落。我们为电磁铁单独设置了一个MOS管开关电路由STM32控制通断。3. 软件控制逻辑从循迹到任务调度的全链路设计软件是硬件的灵魂也是比赛中最耗费精力的部分。我们的程序架构采用前后台裸机状态机模式没有上RTOS以降低复杂度确保实时性。3.1 底层双闭环PID控制让小车“走得直、转得准”小车的运动控制是所有高级功能的基础。我们实现了两个层次的PID控制环速度闭环内环以编码器反馈的实际速度作为测量值与目标速度进行比较通过PID运算后直接输出PWM占空比给电机驱动。这个环保证了即使负载变化如上下坡小车也能维持设定的速度这是平稳运行的前提。目标速度-PID速度控制器-PWM输出-电机-编码器反馈位置/循迹闭环外环对于直线循迹误差来源于灰度传感器阵列。我们将最中间两个传感器的差值作为横向位置误差通过一个PD控制器计算出期望的角速度补偿量叠加到速度闭环的目标上让小车自动对准黑线。灰度传感器-计算横向误差-PD位置控制器-输出角速度补偿量-叠加到左右轮目标速度差对于定点旋转如90度转弯我们则以MPU6050反馈的Yaw角作为测量值与目标角度做差通过一个角度PID控制器直接输出一个差速给左右轮直到角度误差为零。参数整定技巧先调速度环再调位置/角度环。速度环的P值从小往大加直到电机启动响应迅速且无明显抖动。位置/角度环的P值决定了纠正力度D值用于抑制过冲和振荡。在赛场上我们准备了几组针对不同赛道材质纸质、PVC的PID参数上场前根据实际情况快速切换。3.2 视觉识别与通信协议K210与STM32的高效对话K210和STM32之间通过UART串口通信定义一套简洁高效的协议至关重要。通信协议设计我们自定义了基于帧头、指令、数据、校验和的简单协议。例如0xAA 0x55 0x01 [X坐标] [Y坐标] [药瓶编号] [校验和]代表识别到药瓶并返回其位置和编号。0xAA 0x55 0x02 [病房编号] [校验和]代表识别到病房编号。STM32也可以发送指令给K210如0xAA 0x55 0xA0命令其开始识别药瓶。K210端程序要点图像预处理与ROI设置固定摄像头位置和角度后药瓶和病房门牌在图像中的区域ROI大致是固定的。预先设定好ROI可以大幅减少需要处理的像素数量提高识别速度。模型推理与后处理调用KPU进行推理获取识别框和类别。通过非极大值抑制NMS去除重复框然后取置信度最高的结果。数据发送将识别结果按照协议打包通过UART发送给STM32。这里有一个关键点必须设置合理的发送频率。我们采用“变化才发送”的策略即只有当识别到的编号发生变化时才发送一次数据避免串口数据拥堵。STM32端处理设置串口空闲中断DMA这是高效接收不定长数据的关键。配置DMA循环接收串口数据并开启串口空闲中断。当一帧数据接收完毕线路空闲时触发中断在中断服务函数中处理这一帧完整的数据。数据解析与校验在中断服务函数中解析帧头、指令和数据并计算校验和进行验证确保数据正确。状态更新将解析出的药瓶编号、病房编号等信息更新到全局变量中供主循环状态机使用。3.3 顶层任务状态机流程的指挥官整个送药流程由一个清晰的状态机State Machine来控制。状态机的每个状态对应任务的一个步骤状态转移由传感器条件和视觉识别结果触发。typedef enum { STATE_IDLE, // 空闲 STATE_GO_TO_PHARMACY, // 前往药房 STATE_SEARCH_BOTTLE, // 寻找药瓶 STATE_GRAB_BOTTLE, // 抓取药瓶 STATE_GO_TO_WARD, // 前往病房 STATE_DELIVER, // 投放药瓶 STATE_RETURN_HOME // 返回药房 } TaskState_t; // 主循环中的状态机处理 void TaskStateMachine_Update(void) { switch (g_currentState) { case STATE_GO_TO_PHARMACY: // 执行循迹到药房区域的代码 if (到达药房标志) { g_currentState STATE_SEARCH_BOTTLE; 通过串口发送指令给K210开始识别药瓶 } break; case STATE_SEARCH_BOTTLE: // 控制小车在药房区域缓慢移动/旋转 if (收到K210的有效药瓶数据) { 记录药瓶坐标 g_targetBottleNum 识别到的编号 g_currentState STATE_GRAB_BOTTLE; } break; case STATE_GRAB_BOTTLE: // 根据药瓶坐标微调小车位置使电磁铁对准药瓶 if (位置对准) { 控制舵机下降 延时 打开电磁铁 延时 控制舵机上升 g_currentState STATE_GO_TO_WARD; 通过串口发送指令给K210开始识别病房门牌 } break; // ... 其他状态类似 case STATE_RETURN_HOME: if (回到起点) { g_currentState STATE_IDLE; 停车任务完成 } break; } }这种状态机的设计使得程序逻辑非常清晰调试时可以通过一个全局变量轻松查看当前处于哪个阶段哪里出了问题一目了然。4. 核心算法与策略深度剖析除了基本的控制一些策略性的算法直接决定了任务的完成质量和速度。4.1 基于编码器与陀螺仪的航迹推演Odometry要实现点到点的移动仅靠巡线是不够的尤其是在没有引导线的区域如药房内的自由移动。我们实现了简单的航迹推演。原理根据左右轮编码器在单位时间内的脉冲差值推算出小车在本体坐标系下的位移和旋转角度再结合上一时刻的全局位姿积分得到当前在全局坐标系下的估计位置X, Y, θ。公式简化位移 左轮位移 右轮位移 / 2旋转角度 右轮位移 - 左轮位移 / 轮距融合陀螺仪编码器在打滑时会产生巨大误差。我们采用互补滤波将编码器推算出的角度变化与陀螺仪积分出的角度进行融合得到更可靠的航向角θ。这让我们在药房内旋转搜索药瓶时角度控制更加精准。4.2 视觉识别策略与抗干扰处理赛场光照复杂视觉识别必须考虑抗干扰。药瓶识别K210运行目标检测模型。我们训练集包含了赛场上可能出现的各种光照、角度下的药瓶图片并做了数据增强旋转、裁剪、调整亮度对比度。在程序中我们只关心置信度大于0.7的检测结果并且连续3帧检测到同一编号才确认为有效识别避免单帧误判。病房编号识别病房门牌是静态的但我们仍然采用了目标检测而非简单的模板匹配。因为门牌可能受侧面光照影响产生阴影。模型同样需要包含不同光照条件的样本。OpenMV的辅助角色在调试阶段我们用OpenMV的色块识别功能来辅助标定。例如在药瓶放置区贴不同颜色的色标OpenMV可以快速找到色块中心帮助我们标定K210识别框的物理坐标映射关系即图像中的像素坐标如何对应小车需要移动的实际距离。4.3 路径规划与决策逻辑题目地图固定路径可以事先规划好。我们将地图抽象成节点关键点如起点、路口、药房入口、病房门口和边。路径预存在代码中硬编码了几条关键路径的指令序列例如“从起点到十字路口直行200cm - 右转90度 - 直行150cm - 左转90度 - 直行进入药房区域”。决策逻辑小车根据当前任务状态和视觉识别到的编号选择对应的预存路径执行。例如识别到3号药瓶就查询“药房到3号病房”的路径指令序列然后依次执行。路口处理这是最容易出错的地方。我们的策略是在接近路口时降低速度通过灰度传感器识别路口的“T”型或“十”型特征结合编码器里程精确停在路口中心。然后根据路径指令进行转弯。转弯后立即用陀螺仪确认转角是否到位并进行微调。5. 调试历程与经典故障排查电赛四天三夜大半时间都在调试和排错。下面分享几个让我们记忆深刻的“坑”。5.1 电源系统引发的“灵异”复位现象小车在直线行驶时一切正常但每当电磁铁吸合或者电机急加速时单片机偶尔会复位屏幕黑一下又重启。排查过程首先怀疑程序跑飞检查了堆栈大小、数组越界、中断嵌套等问题未果。用示波器监测STM32的3.3V电源引脚。发现在电磁铁吸合的瞬间3.3V电压有一个明显的跌落降到2.8V左右持续时间约几十毫秒足以触发芯片的欠压复位BOR。追根溯源发现我们最初为了省事电机的驱动电源VM和整个控制板的电源VCC都来自同一个7.4V锂电池然后通过一个大的降压模块降到5V再通过LDO降到3.3V。当电机或电磁铁启动时电池内阻和导线电阻导致瞬间大电流拉低了整个供电回路的电压。解决方案电源分离使用两路独立的降压模块。电池电压直接给电机驱动芯片VM供电。同时电池电压通过另一路降压模块如LM2596降到5V专门给单片机、传感器、舵机等控制部分供电。两者共地即可。增加电容在控制板的5V和3.3V电源入口处并联多个大容量如100uF电解电容和多个小容量0.1uF陶瓷电容用于缓冲瞬间的电流需求。优化布线电机动力线粗与控制信号线细分开走线避免平行长距离走线减少干扰。5.2 串口通信数据错乱与丢失现象K210偶尔能返回正确数据但经常出现帧头错误、数据截断或完全收不到的情况。排查过程检查波特率STM32和K210两端均为115200匹配。检查电平K210的UART是3.3V电平STM32也是直接连接无误。在STM32端将接收到的每一个字节都打印出来观察。发现数据中经常夹杂着0x00或者非预期字节。解决方案共地共地共地这是最容易被忽视的一点。K210开发板和STM32核心板必须用导线可靠地连接两者的GND引脚。如果只连接了TX和RX没有共地参考电平不一致会导致信号误判。启用硬件流控如果支持如果双方芯片都支持RTS/CTS可以启用避免因处理速度不匹配导致数据丢失。但我们的模块不支持所以采用软件流控。软件流控与协议强化增加帧尾和超时判断除了帧头在协议中增加固定的帧尾如0x0D 0x0A。在STM32接收中断中不仅判断空闲还判断是否收到帧尾以及从帧头开始是否超时超时则丢弃本帧。双缓冲接收开辟两个缓冲区当DMA正在向缓冲区A写数据时应用程序可以处理已经接收完的缓冲区B的数据实现“乒乓操作”避免处理不及时导致的数据覆盖。K210端发送间隔降低K210的数据发送频率确保每帧数据之间有足够的时间间隔避免串口缓冲区溢出。5.3 巡线传感器受环境光干扰现象在赛场强烈的顶光或侧光下原本黑色的引导线在灰度传感器看来反光严重数值接近白色导致误判。排查过程尝试调整传感器阈值但光照不均匀一个阈值无法适应全场。尝试在传感器上方加遮光罩有一定改善但无法根除。解决方案动态阈值法在程序初始化时小车先静止在赛道上依次读取每个灰度传感器在“黑线”和“白底”上的值计算出一个初始阈值。在运行过程中定期如每10秒采样当前“白底”区域的传感器值对阈值进行微调。这需要小车有识别“白底”状态的能力。采用数字式红外对管传感器换用自带调制解调功能的红外对管传感器如TCRT5000其发出的红外光受环境可见光影响较小。虽然也需要调阈值但稳定性比普通光敏电阻或光敏二极管好很多。软件滤波对传感器读值进行滑动平均滤波或者采用“投票法”例如8路传感器中只有连续3路或以上检测到黑线才认为有效避免单点噪声干扰。5.4 抓取机构成功率不稳定现象电磁铁有时能吸起药瓶有时吸不起来或者吸起来后中途掉落。排查过程检查电磁铁供电电压吸合时电压跌落严重。发现电磁铁下降的高度有时不一致导致与药瓶距离过大磁力不够。解决方案电源保障同5.1为电磁铁提供独立的、粗导线的供电线路并在其两端并联一个大容量电解电容如470uF/16V在吸合瞬间提供峰值电流。位置闭环在升降舵机上加装一个简单的电位器或编码器构成位置反馈。这样每次下降的高度可以精确控制确保电磁铁端面与药瓶顶面的距离恒定在最佳范围如2-3mm。增加机械导向在电磁铁外围增加一个轻质的塑料圆筒作为导向罩。下降时导向罩先接触药瓶周围桌面确保电磁铁能垂直对准药瓶中心避免歪斜。6. 赛前准备与赛场应变硬件和软件调好了只算成功了一半。赛场的临场发挥同样重要。1. 参数快速标定工具我们编写了一个简单的上位机用Python的Tkinter或Qt通过蓝牙或Wi-Fi模块如HC-05或ESP8266连接小车。上位机可以实时显示传感器数据、摄像头画面并能动态修改PID参数、速度设定值等保存多组参数配置文件。在赛场适应阶段能快速调整小车以适应实际的赛道摩擦力和光照条件。2. 多套程序与参数备份SD卡里存放了多个版本的固件main_v1.0_aggressive.bin激进速度版、main_v1.0_conservative.bin保守稳定版、main_v1.0_backup.bin前一天稳定版。根据赛场情况和自身状态选择最合适的版本下载。PID参数也以头文件或宏定义的方式保存多套。3. 建立检查清单Checklist比赛开始和每次封箱前严格执行检查清单[ ] 所有螺丝、线缆是否紧固[ ] 电池电量是否充足准备三组以上电池[ ] 传感器镜头是否洁净[ ] 灰度传感器高度是否合适[ ] 程序版本和参数文件是否正确[ ] 备用元器件电机、舵机、传感器、芯片是否齐全[ ] 万用表、示波器、螺丝刀、焊台等工具是否就位4. 赛场应变策略第一次运行务必选择最保守、最稳定的参数和速度。目标是“完赛”拿到基础分。时间分配留出最后至少2小时进行封箱前的最终测试和稳定化运行。中间任何修改必须预留回滚时间。心理建设遇到突发问题保持冷静按照“电源-信号-逻辑”的顺序进行排查。队友间明确分工一个人操作一个人记录一个人思考。回看整个项目智能送药小车是一个典型的嵌入式系统综合应用案例。它没有用到特别高深的理论但极其考验对基础知识的综合运用能力、工程实践能力和团队协作能力。把每一个简单的环节供电、通信、传感、控制都做稳定了整个系统才能可靠。那些熬夜调试的夜晚那些解决一个bug后的狂喜那些赛场上紧张的时刻共同构成了这段难忘的经历。希望这份详细的复盘能给后来者一些实实在在的参考少走我们走过的弯路。