简介:面向全国大学生电子设计竞赛智能送药小车方向的K210数字识别模型资源包,适合电赛参赛学生、嵌入式开发者以及对机器学习视觉应用有兴趣的初学者。资源围绕K210芯片内置的神经网络处理器设计,提供可直接使用的数字识别模型与配套代码,帮助解决送药小车准确识别药瓶编号、匹配床位与药品信息等关键问题。压缩包共8个文件,大小约1.59MB,覆盖模型文件、脚本程序、标签定义、说明文档和示例图片等类型;其中模型文件负责在K210上完成数字识别推理,脚本程序实现启动加载与流程控制,标签文件标定识别类别,说明文档交代部署步骤和注意事项,示例图片则方便验证识别效果。目前已有1173人学习浏览,是一份兼具完整性和实操性的赛题资源。通过这份资料,读者可以系统了解K210平台从模型部署到实时数字识别的完整流程,并能基于现有代码快速修改和扩展,为后续实现路径规划、障碍避让等功能打下基础。
1. 赛题拆解与整体方案选型
1.1 智能送药小车到底在跑什么任务
全国大学生电子设计竞赛里,“智能送药小车”这道题每年都能吸引大量队伍,核心任务大致可以归纳为:小车从药房出发,沿着场地上的引导线行驶,途经若干病房门口,每个病房门口贴着数字标签,小车需要准确识别目标病房的数字,在对应病房前停车,完成“送药”动作后再返回。听起来不复杂,但真正做起来会发现,控制、视觉、通信、机械四个方向全都得沾一遍,任何一个环节掉链子都直接影响成绩。
数字识别这部分尤其关键。引导线循迹可以用灰度传感器或者OpenMV解决,但“停在哪一间病房”必须靠视觉读数字才能确定。现场不会给你提前录好的数据,光线、角度、数字字体都是变量,所以识别模型不能是“摆设”,得真正能在K210上跑得稳、判得准。我在备赛时把大部分时间都砸在了这套视觉识别链路上,走通之后小车整体的可靠性提升非常明显。
1.2 为什么选择K210做数字识别
送药小车的视觉方案,常见的有OpenMV、树莓派加摄像头、以及K210这三条路。树莓派算力最强,但启动慢、功耗高、体积大,对电赛这种四天三夜的节奏来说性价比不高;OpenMV上手快,但算力有限,跑轻量CNN模型比较吃力,识别速度容易被拖垮。
K210是一颗带KPU(神经网络处理器)的RISC-V芯片,最大优势在于:硬件级卷积加速、低功耗,而且官方提供了Micropython固件,SDK资料也比较全。它的KPU可以跑经过量化的CNN模型,实测识别一张数字图只需要几十毫秒,完全满足小车在运动过程中实时判读的需求。更关键的是,K210价格便宜,坏了大不了换一片,不像树莓派那样“金贵”。
具体到我做的这套方案,K210负责图像采集、数字识别、通过串口把结果发给STM32;STM32负责电机控制、循迹逻辑、决策调度。两块芯片各管一摊,分工清楚,联调起来反而省心。
1.3 系统整体架构与数据流
整个识别链路的数据流是这样的:
- K210通过DVP接口接入摄像头,采集RGB565图像
- 图像缩放+灰度化,送入KPU进行模型推理
- 推理结果经过置信度筛选和滑动窗口滤波,得到稳定的数字结论
- K210通过UART将识别结果发送给STM32
- STM32综合循迹传感器数据和识别结果,控制电机完成停车、转向、返回等动作
我在实际搭建时,把K210挂在小车前方偏上位置,摄像头俯视前方地面区域,保证数字标签进入画面时有足够的像素面积。这里有个容易踩的坑:如果摄像头安装角度太陡,数字会变形严重,模型识别率直接下降。建议安装角度控制在30到45度之间,让标签尽量正对镜头。
2. K210数字识别模型:从训练到部署
2.1 数据集准备:MNIST还是自采集
很多队伍第一反应是直接拿MNIST手写数字数据集来训练,毕竟省事。但MNIST是手写体,与赛场印刷体数字差异不小,直接迁移效果并不理想。我建议优先采集“仿赛场”数据集,也就是用组委会公布的模拟场景、打印字体数字标签,在不同距离、不同角度、不同光线下拍摄几百张图,标注后作为训练数据。
如果时间实在来不及,MNIST可以用,但需要在训练时做数据增强:随机旋转、缩放、平移、亮度抖动,强迫模型学到更泛化的特征。我当时的做法是:以MNIST为基础,混入自采的印刷字体数据,大概每个类别凑了2000张左右,效果比单纯用MNIST高出一截。
2.2 模型选择与训练要点
K210的KPU对模型结构有要求,并不是随便一个网络都能跑。它支持TFLite格式的模型经过NNcase工具链转换后部署,官方推荐使用较小的CNN结构。直接跑ResNet这种大网络想都不用想,K210的6MB SRAM根本吃不消。
我使用的是类似LeNet-5的轻量CNN结构,只保留两个卷积层和两个全连接层,参数量在几十万级别。输入分辨率用112x112,既能保住数字边缘细节,又不会让KPU推理时间太长。
训练框架我用的是TensorFlow 2.x,训练完导出TFLite模型再做权重量化,转为int8精度。这一步不能省,KPU本质上是定点加速器,不量化根本无法部署。
2.3 kmodel转换与部署的关键细节
模型训练是常规操作,真正折磨人的是转换那一步。NNcase工具链对TFLite算子支持有限,一个不留神就会转换报错。我当时卡了好几个小时,最后才发现模型里一个BatchNormalization层融合导致算子不兼容,去掉后用tf.quantization.quantize手动完成伪量化,问题才解决。
转换命令大概长这样:
# 假设已经有tflite模型文件 import nncase # 设置推理目标平台 target = nncase.Target() target.arch = "k210" # 加载tflite并编译为kmodel model = nncase.kmodel() model.compile(target, "./model.tflite", "./model.kmodel", preprocess=False)注意NNcase版本要和K210固件匹配,我用的是0.1.0系列的旧版本,新版本语法差异比较大。转换出的kmodel文件会直接烧录到K210的Flash中,推理时从Flash加载。
这个过程中还有一个常见坑:模型参数稍微大一点,Flash放不下或者运行时内存不足。解决办法是压缩输入分辨率、减少全连接层节点数,K210毕竟不是GPU,适当“瘦身”很合理。
3. 识别主逻辑与代码实现
3.1 K210端数字识别代码
K210端我用的是MaixPy固件,好处是MicroPython写起来快,坏处是性能有损耗,一帧图像推理算上后处理大概几十毫秒,对小车场景来说完全够用。
核心代码逻辑如下:
import sensor, image, lcd, time from maix import nn # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.set_vflip(True) sensor.skip_frames(30) # 从Flash加载kmodel model = nn.load("/sd/model.kmodel") while True: img = sensor.snapshot() # 裁出ROI区域,减少干扰 roi = img.crop((80, 100, 160, 160)) # 转灰度,统一尺寸 resized = roi.resize(112, 112) # 推理 results = model.forward(resized) # 解析输出 cls, score = parse_results(results) if score > 0.8: print("数字:", cls, "置信度:", score) send_to_stm32(cls, score) time.sleep_ms(50)代码本身不难,但crop的ROI坐标要靠实际摆放位置去标定,不是随便写的。我在调试时写了一个小的标定脚本,把实时画面推送到MaixPy IDE里,手动框出数字出现的区域,然后把坐标固定到代码里。
3.2 数字标签与病房号的映射逻辑
赛题里病房号不一定是1、2、3这种简单编号,可能需要识别两位数字,或者指定数字对应不同目标。K210直接识别两位数会增加模型输出类别数,需要额外训练11到99的类别,训练数据量会暴涨。
比较稳妥的做法是:只让模型输出0到9的数字类别,再通过多帧组合逻辑来关联病房号。比如目标病房是“12”,小车先沿路经过“1”号标签,再经过“2”号标签,识别逻辑里记录连续出现的两个标签,组合后与目标匹配。这种方案不需要模型增加类别,训练压力小,而且实际比赛时场景也是单向经过的,逻辑上成立。
3.3 滑动窗口滤波:避免单帧误判
识别数字时最怕的就是偶尔一帧看错,小车哗一下冲过去停在错误病房,整场直接报废。我引入了一个简单的滑动窗口滤波:连续保存最近5帧的识别结果,取出现次数最多的数字作为最终结果,并且要求票数不低于3。
这个思路跟数字信号处理里的中值滤波很像,只不过作用在类别标签上。实测效果非常好,原来偶发性的误判被基本压制住,代价只是识别结果延迟了大概两三帧,小车运动速度不快,完全可以接受。
4. K210与STM32的通信协议设计与调试
4.1 串口通信协议是怎么定的
K210识别出数字后,需要通过UART告诉STM32。一开始我图省事直接发数字的ASCII码,结果联调时发现偶尔丢字节、错位解析,后来改成固定帧格式才解决问题。
我最终使用的协议格式:
帧头:0xAA 0x55 数据1:数字类别(0-9) 数据2:置信度(0-100) 校验:帧头+数据累加和的低字节 帧尾:0x0D 0x0A开头加两个字节帧头,是为了让接收方能在乱流中找到起始位置;末尾加校验和,是为了丢弃被干扰的无效帧。波特率统一用115200,数据位8、无校验、一个停止位。固定帧长8个字节,解析逻辑简单很多。
4.2 STM32端的解析代码
STM32端我是用HAL库写的串口接收中断,通过状态机逐字节解析:
uint8_t rx_buf; uint8_t packet[8]; uint8_t pack_index = 0; uint8_t state = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 状态机解析 switch (state) { case 0: if (rx_buf == 0xAA) state = 1; break; case 1: if (rx_buf == 0x55) state = 2; else state = 0; break; case 2: packet[0] = rx_buf; // 数字 state = 3; break; case 3: packet[1] = rx_buf; // 置信度 state = 4; break; case 4: // 校验和判断 if ((uint8_t)(packet[0] + packet[1] + 0xAA + 0x55) == rx_buf) { process_number(packet[0], packet[1]); } state = 0; break; } HAL_UART_Receive_IT(&huart1, &rx_buf, 1); } }这里有个细节,串口中断接收一字节就进一次回调,状态机切换速度必须快,避免高波特率下丢字节。我在调试时用逻辑分析仪抓过波形,115200波特率下逐字节处理完全没问题。
4.3 通信层面的教训:电平匹配很关键
K210的UART是3.3V电平,STM32F103的串口也是3.3V电平,按理说可以直接连。但我的板子上电机驱动和电源纹波比较大,K210和STM32共地不好时,串口数据经常出现乱码。
解决方式有两个:一是把两个板子的GND用粗导线直接连在一起,保证共地;二是在通信线上串一个100欧电阻,并加一个对地电容滤掉高频毛刺。经过这两步处理后,通信稳定多了,再也没有随机乱码的情况。
5. 赛场环境下的准确率与稳定性优化
5.1 图像预处理:把“变量”变成“常量”
K210端的图像预处理重点不是搞什么高级算法,而是减少外部环境的影响。我固定了摄像头曝光时间,关闭自动白平衡和自动增益,在室内灯光条件下把画面亮度调到稳定。否则小车一开动,画面亮度变化会让模型输出的置信度剧烈波动。
代码里加了一句:
sensor.set_auto_exposure(False) sensor.set_auto_whitebal(False)这样在固定光照下,每帧图像特征一致,模型的输出也稳定。如果比赛场地光线变化大,可以在ROI区域做一次直方图均衡化,但注意这会增加几毫秒处理时间,要平衡好帧率。
5.2 反光和阴影问题怎么解决
数字标签一般覆膜或塑封,在灯光直射下容易反光,拍出来白茫茫一片。我的对策是:在摄像头镜头前加一个偏振片,并调整角度消除反光,效果立竿见影。如果手头没有偏振片,可以通过拉大对比度来缓解,但本质还是改变拍摄角度最有效。
阴影问题则主要在ROI裁剪环节处理。如果数字标签刚好落在阴影边界上,裁出来的图像可能一半亮一半暗,识别率差很多。我做了个最笨的办法:把ROI区域再缩小一圈,只保留数字本体周边的有效区域,阴影边界的影响自然减小。
5.3 电机干扰导致K210重启的排查
这个坑必须单独拿出来说。联调时发现,只要电机一启动,K210偶尔会黑屏重启,严重时直接把识别结果丢掉。排查了半天,发现罪魁祸首是电源问题:电机堵转瞬间电流骤增,导致电压跌落,K210的电源芯片承受不住直接复位。
解决思路是给K210的供电单独加一个DC-DC降压模块,与电机驱动电源完全隔离,同时在电源输入端加470uF电解电容和100nF陶瓷电容做储能滤波。改完之后,无论电机怎么转,K210都能稳定运行。其实电赛里很多莫名的复位、卡死现象,十有八九都是电源没做好,别急着怀疑代码。
5.4 常见问题排查速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 识别结果不断跳变 | 曝光不稳、ROI偏移 | 关闭自动曝光,重新标定ROI |
| 置信度总是很低 | 训练数据与现场差异大 | 自采数据重新训练,增加增强 |
| kmodel加载失败 | 固件版本与NNcase不匹配 | 查询官方版本对应表重新编译 |
| 串口收不到数据 | K210与STM32地电位不共 | 粗导线共地,加滤波电容 |
| 电机一启动K210就重启 | 电源跌落 | 独立供电、加大电容、共地处理 |
| 数字贴纸反光严重 | 光线角度不好 | 加偏振片,调整镜头俯仰角 |
6. 备赛节奏与进阶扩展建议
6.1 备赛时间安排的实战心得
电赛只有四天三夜,看着时间充足,实际一上手就会发现处处都是坑。我的建议是:视觉识别模型必须在比赛之前就做到“能跑”,比赛期间只做参数微调,不要现场从零训练。我队伍里大概花了两个周末做模型训练和K210部署,比赛前一周把通信协议和整车联调跑通,最后比赛四天反而比较从容。
如果队伍里有人之前没接触过K210,至少留出两天时间专门熟悉MaixPy环境和模型转换流程。没有这个缓冲,比赛时光是固件烧录、依赖安装就够你喝一壶。
6.2 这套方案还能扩展成什么
送药小车只是K210数字识别的一个典型场景。把数字识别换成二维码识别,或者把模型换成简单目标检测网络,就能做仓库巡检车、图书分类机器人等题目。K210的优势就在于这样一个低成本边缘AI模组,可以快速验证很多视觉方案。
我自己在做完送药小车后,又试过在K210上跑多个小模型,按需加载,也算是对KPU资源管理有了更深理解。对于想进一步挑战的队伍,可以尝试把两个K210组成前后双视觉系统,一个看近处数字、一个看全局路线,整体系统复杂度会上去不少,但上限也更高。
最后再分享一个小经验:写代码时多留打印信息,K210端的识别结果、置信度、串口发送日志全部打出来,联调时才能快速定位是视觉问题还是通信问题。很多队伍一上来就追求“干净代码”,结果出了问题只能猜,反而浪费大量时间。
本文还有配套的精品资源,点击获取