【星汇极客】从省二到国三:我的光电设计竞赛STM32+OpenMV实战复盘
1. 竞赛背景与入门准备
光电设计竞赛是很多电子爱好者和嵌入式学习者都会关注的重要赛事,我最初接触这个比赛时也是一头雾水,完全不知道从哪里下手。记得大二那年,我第一次组队参加省赛,当时选的是迷宫循迹小车项目,用的主控是STM32F103,图像识别部分则交给了OpenMV。虽然最后只拿了个省二,但这段经历让我真正理解了嵌入式系统的软硬件结合之道。
如果你刚开始准备这个竞赛,我建议先从了解比赛规则开始。光电设计竞赛分为奇数年和偶数年两种赛制,奇数年通常是命题竞技类,比如迷宫循迹、光电追踪等具体项目;偶数年则是自由命题,需要自己设计项目并提交方案文档和演示视频。我参加的第23届省赛属于命题类,要求小车在迷宫中自主行走并识别颜色宝藏,这对硬件设计和算法实现都有不低的要求。
组队方面,最好找有不同专长的队友。我的团队里,有人负责硬件电路设计,有人专攻STM32编程,我则主要处理OpenMV的图像识别部分。分工明确可以大大提高效率,避免最后阶段手忙脚乱。工具选择上,STM32系列单片机是首选,因为资源丰富、社区支持好;OpenMV则是一款专为机器视觉设计的摄像头模块,用MicroPython编程,上手比OpenCV简单很多。
最初的材料准备阶段,重点是要把方案文档写清楚。评委会先通过你的文档和视频判断项目可行性,所以文档结构要清晰,包括项目背景、系统架构、硬件选型、算法流程等。视频演示则要突出关键功能,比如小车循迹的稳定性、颜色识别的准确率等。我们第一次提交时,就因为视频中小车跑偏了一次差点被刷下来,所以演示前一定要反复测试。
提示:初赛材料提交前最好让队友互相检查,避免出现低级错误。文档中的图片和图表要尽量清晰,视频剪辑要突出亮点功能。
2. 硬件设计:STM32主控与电路搭建
硬件设计是整个项目的基础,也是最容易踩坑的地方。我们最初用的是STM32F103C8T6核心板,这款单片机性价比高,但资源有限。后来为了处理更复杂的任务,我们升级到了STM32F407,它的主频更高、内存更大,还能跑一些轻量级的实时操作系统。
电机驱动部分,我们试过L298N和TB6612FNG两种模块。L298N价格便宜但发热严重,效率不高;TB6612FNG则更小巧,驱动效率也更高,最终我们选的是后者。电源管理方面,最初用的是一节18650电池,但发现电压波动会影响OpenMV的工作稳定性,后来换成了两节锂电池串联+稳压模块,确保5V和3.3V输出稳定。
传感器布局是另一个关键点。迷宫小车需要红外传感器来循迹,我们用了五路红外循迹模块,中间三个用于检测路径,左右两个用于判断岔路口。安装高度要调整到离地面1-2厘米左右,太高会检测不到黑线,太低又容易磕碰。OpenMV摄像头我们固定在小车前方,倾斜角度调了无数次才找到最佳视角,既要保证视野覆盖足够大的区域,又要避免畸变影响识别精度。
电路连接上,STM32的GPIO口要合理分配。比如电机驱动需要PWM输出,我们用了TIM3和TIM4定时器生成四路PWM信号;红外传感器接在普通GPIO口,设置为输入模式;OpenMV则通过串口与STM32通信,发送识别结果。所有接线最好用杜邦线先测试,稳定后再用焊枪固定,避免比赛中途松动。
注意:电源线要尽量粗一些,电机启动时的电流冲击很大,细线容易发热甚至烧毁。我们曾在测试中因为电源线过热导致系统重启,差点耽误提交时间。
3. OpenMV图像识别算法实战
OpenMV是一款非常适合初学者的视觉模块,用它做颜色识别和物体检测比传统OpenCV简单很多。我们最开始直接用了官方例程中的颜色块识别代码,但实际测试发现效果并不理想——迷宫环境的光线变化会严重影响识别准确率。
为了解决这个问题,我们做了两件事:一是调整摄像头白平衡,二是自定义颜色阈值。OpenMV IDE提供了颜色阈值工具,可以实时查看识别效果。我们先在迷宫的不同位置采样光照数据,然后针对每种颜色(红、绿、蓝)生成多组阈值,最后取中间值作为最终参数。代码里用了image.find_blobs()函数来查找色块,并通过面积过滤掉噪声点。
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 2000)
# 自定义颜色阈值 - 红色示例
red_threshold = (30, 100, 40, 80, 20, 60)
while(True):
img = sensor.snapshot()
blobs = img.find_blobs([red_threshold], area_threshold=100)
for blob in blobs:
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
进阶部分,我们尝试了机器学习模型部署。OpenMV支持TensorFlow Lite模型,我们收集了几百张宝藏图片,在Edge Impulse平台上训练了一个分类模型,然后转换成TFLite格式加载到摄像头中。这样不仅可以识别颜色,还能区分不同形状的宝藏。模型 inference 时间大约需要200ms,对于低速移动的小车来说足够用了。
实际比赛中,图像识别最难的不是算法本身,而是环境适应性。赛场灯光可能偏黄或偏蓝,迷宫底板的反光程度也不一样。我们最终方案里添加了动态阈值调整功能:小车启动先识别一段时间白色背景板,自动计算当前环境下的颜色偏移量。这个改进让我们的识别准确率提高了至少30%。
4. STM32与OpenMV的通信协议
软硬件之间的数据交互是很多新手容易忽略的部分。我们最初用串口直接发送字符串,比如"red,125,60"表示在坐标(125,60)发现红色宝藏。但测试中发现字符串解析耗时且容易出错,后来改成了二进制协议。
通信格式定义如下:每帧数据以0xAA 0xBB开头,接着是1字节数据类型(0x01表示色块信息,0x02表示控制命令),然后是数据内容,最后以0xCC结束。STM32端收到数据后先校验帧头和帧尾,再提取有效数据。这样不仅提高了传输效率,还降低了误码率。
// STM32端串口接收示例
#define HEADER1 0xAA
#define HEADER2 0xBB
#define FOOTER 0xCC
uint8_t rx_buffer[20];
uint8_t rx_index = 0;
void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
uint8_t data = USART_ReceiveData(USART1);
if(rx_index == 0 && data != HEADER1) return;
if(rx_index == 1 && data != HEADER2) {
rx_index = 0;
return;
}
rx_buffer[rx_index++] = data;
if(data == FOOTER) {
process_data(rx_buffer, rx_index);
rx_index = 0;
}
}
}
数据内容方面,我们传输的色块信息包括:颜色类型(1字节)、中心点x坐标(2字节)、y坐标(2字节)、置信度(1字节)。STM32根据这些数据计算小车需要转向的角度和距离。为了避免通信拥堵,我们设置了100ms的发送间隔,OpenMV只在检测到新色块时才发送数据。
提示:串口通信最好加入超时重传机制。我们遇到过因为线材干扰导致数据丢失的情况,后来在协议中添加了应答机制——STM32收到数据后回复ACK,OpenMV如果没收到ACK就重发上一次的数据。
5. 迷宫循迹算法与路径规划
迷宫循迹是小车项目的核心难点,既要保证不跑出赛道,又要高效地寻找路径。我们最初用了最简单的PID控制算法:红外传感器返回的五路数据被转换为位置偏差,然后计算PWM输出调整电机转速。
具体实现中,中间三个传感器用于计算当前位置偏离黑线的程度,左右两个传感器用于检测十字路口。当左右传感器同时检测到黑线时,说明遇到了十字路口,此时需要根据预设策略决定直行、左转还是右转。PID参数调试花了我们大量时间,最后确定的Kp=0.8,Ki=0.01,Kd=0.2,这个参数组合在大多数场地下都能稳定运行。
进阶版本中,我们加入了简单的路径规划算法。小车会记录走过的路径,遇到死胡同后自动回溯到上一个岔路口选择另一条路。我们在STM32中用一个数组存储移动历史,每个路口的选择方向都被记录下来。为了避免循环绕路,还会标记已经访问过的区域。
// 简易路径记录数据结构
#define MAX_PATH 100
typedef struct {
uint8_t x;
uint8_t y;
uint8_t direction; // 0:北, 1:东, 2:南, 3:西
} PathNode;
PathNode path_history[MAX_PATH];
uint8_t path_index = 0;
void record_path(uint8_t x, uint8_t y, uint8_t dir) {
if(path_index >= MAX_PATH) return;
path_history[path_index].x = x;
path_history[path_index].y = y;
path_history[path_index].direction = dir;
path_index++;
}
实际比赛时,迷宫布局是赛前才公布的,我们没办法提前训练。所以算法必须具有良好的通用性。我们最终方案采用了右手法则优先策略——始终优先选择右侧路径,这样能保证至少可以走通整个迷宫。虽然不一定是最优路径,但在时间有限的比赛中很实用。
6. 调试技巧与常见问题解决
调试是项目开发中最耗时的部分,我们踩过的坑可能比成功经验还多。硬件方面,最常见的问题是电源噪声干扰。电机启动时会产生很大的电流冲击,导致单片机复位或OpenMV工作异常。我们最终解决方案是:电机电源与控制系统电源完全分离,使用两个独立的锂电池供电,中间通过光耦隔离信号。
软件调试中,我们大量使用了STM32的串口打印功能。将关键变量值通过串口发送到电脑,实时监控程序运行状态。OpenMV端也可以用print()语句输出调试信息,通过IDE的串口终端查看。建议调试时分段进行:先测试红外循迹单独工作,再测试OpenMV识别单独工作,最后整合两者通信。
比赛现场最容易出现的问题是环境光变化影响识别。我们准备了一组备用参数:强光环境下使用较高的颜色阈值,弱光环境下使用较低的阈值。现场调试时,先用测试卡校准摄像头,整个过程只需要几分钟。机械结构方面,轮子打滑是最头疼的问题。我们试过橡胶轮、硅胶轮等多种材质,最后发现普通橡胶轮加上适当的配重效果最好。
注意:比赛前一定要进行老化测试。我们曾经遇到过连续运行一小时后电机驱动芯片过热的情况,后来加了散热片才解决。所有可能出问题的地方都要有备用方案,比如多带一套传感器模块、备用电池等。
7. 竞赛答辩与项目展示技巧
到了答辩环节,作品本身的技术水平只占一部分分数,展示方式同样重要。我们的PPT遵循"多图少字"原则,首页放项目亮点和团队信息,接着用系统架构图展示硬件组成和软件流程,然后用实物照片和演示视频说明功能实现,最后简单提一下创新点和应用前景。
视频演示要精心剪辑,总长度控制在2分钟内。开头10秒展示整体效果,中间分段演示各个功能模块,最后用实际比赛场景收尾。我们特意在视频中添加了字幕说明,因为评委可能对技术细节不了解,文字提示可以帮助他们理解你在做什么。
答辩环节最重要的是自信和诚实。评委问到的技术问题,知道就详细回答,不知道就坦诚说明"这个方面我们还没有深入研究"。切忌胡编乱造,评委都是行业专家,很容易听出真假。我们被问到创新点时,最初回答得太泛泛,后来聚焦到"基于STM32F7的实时控制系统"和"自适应光学校准算法"两个具体点上,反而获得了认可。
展示实物时,要注意外观整洁。我们的第一版小车线材裸露,评委直接说"做得太粗糙"。后来用了3D打印的外壳包裹所有电路,瞬间提升了专业感。如果项目涉及机械结构,最好能拆解展示内部设计,这会让评委觉得你们确实深入到了每个细节。
最终评分时,创新性和完成度各占一定比重。即使功能没有全部实现,也要把已经完成的部分演示透彻。我们的省赛版本其实只能识别两种颜色,但我们在答辩中重点强调了识别算法的扩展性,并展示了如何添加新颜色的完整流程,这给评委留下了很好的印象。
更多推荐
所有评论(0)