资讯动态

机器视觉循迹小车实战:OpenMV+STC8图像处理与PID控制全解析

发布时间:2026/9/9 6:17:01 来源:尧图企业网站定制
1. 别急着选主控这个项目的核心难点其实不在“车”上很多人一听到“循迹小车”第一反应就是51单片机、STC89C52、红外对管、灰度传感器那套经典玩法。确实用红外反射式传感器做循迹网上教程一抓一大把原理也简单传感器阵列检测黑线反射率差异单片机读电平然后按照预设逻辑控制左右电机转速差让小车沿着线走。但这次项目名字里加了一个关键词——“机器视觉”整个技术栈和设计思路就完全不一样了。机器视觉循迹小车的本质是让小车通过摄像头获取赛道图像然后通过图像处理算法提取出赛道信息再根据赛道信息控制电机转向和速度。相比传统红外循迹视觉循迹有几个绕不开的优势也有几个容易翻车的坑适应场景更广红外循迹对赛道颜色、光照、材质极其敏感白色地板黑色胶带是标配一旦换成浅色地板浅色线或者灯光直射传感器阈值就很难调。而视觉循迹可以通过算法做光照补偿、颜色空间转换适应能力大幅提升。能获取全局赛道信息摄像头看到的是一整幅图像不只是几个离散的点位。这意味着你可以预判弯道、识别十字路口、检测停车标志甚至区分T字路口和直角弯这是红外传感器很难做到的。处理链路更复杂从“传感器读电平”变成“图像采集→预处理→特征提取→决策控制”每一步都有学问也每一步都会出问题。再说说适合谁来做这个项目。如果你是电子设计竞赛参赛者、嵌入式方向的学生或者单纯是对机器视觉感兴趣的DIY爱好者这个项目都非常值得做。它不要求你有深厚的算法功底但能逼你把“图像处理”“PID控制”“串口通信”这些知识点全部串起来做一个真正能跑起来的完整系统。而且完成之后你手里等于有了一套可以扩展到物体识别、人脸追踪、智能车竞赛的平台后续学习空间很大。这篇文章我会把整车的设计思路、关键算法、工程实践中的坑、以及我个人调试了两个月的经验全部整理出来。不搞虚的全部是可以直接落地的细节。2. 方案选型我用过的三种路线以及最终为什么选了这一套2.1 常见视觉方案对比先说结论机器视觉循迹小车没有“最好”的方案只有“最适合你当前阶段”的方案。市面上主流的路子大致有三条我分别说下各自的优缺点和适用人群。路线一OpenMV / 摄像头 独立单片机STM32/STC8OpenMV本质是一块自带摄像头和MicroPython解释器的嵌入式视觉开发板内部可以运行颜色识别、线段检测等算法然后把结果通过串口发给主控单片机单片机负责电机控制。这是目前入门门槛最低、社区资料最多、也最适合电子设计竞赛的路线。优点视觉处理不占用主控资源算法写在Python里调试方便OpenMV官方IDE可以直接看当前画面和识别结果排错直观。缺点OpenMV性能有限分辨率高一点帧率就掉得厉害复杂算法跑不动价格略高正版H7 Plus大约五六百Python的实时性不如C但循迹这种场景完全够用。路线二树莓派 USB摄像头 Python/OpenCV树莓派性能强可以跑完整的OpenCV库能做颜色识别、Canny边缘检测、透视变换甚至能塞一个小型神经网络做端到端控制。适合想深入学习计算机视觉的玩家。优点生态极其丰富几乎你能想到的视觉算法都能在树莓派上找到现成例子可以接显示器调试所见即所得。缺点启动慢、功耗高、体积大、实时性不如单片机直接控制MIPI摄像头接口的驱动有时候会让人抓狂USB摄像头延迟也稍微高一点。适用毕业设计、课程设计、对Linux有一定基础的同学。赛事里用树莓派的不多因为体积和功耗太吃亏。路线三K210 / 边缘AI芯片 单片机K210是嘉楠科技出的RISC-V架构AI芯片板载KPU理论算力0.8TOPS可以跑训练好的目标检测模型YOLO系列轻量版、人脸检测功耗低价格便宜。适合做物体识别、巡线、视觉跟随等需要“识别特定目标”的场景。优点算力比OpenMV强价格却更便宜板子五六十块跑AI模型效率高。缺点开发资料相对杂乱模型训练和部署对新手不太友好MicroPython固件的库不全遇到问题排查难度大。适用有Python基础、不怕折腾、想尝试边缘AI的进阶玩家。2.2 我的最终选型OpenMV H7 Plus STC8A8K64S4A12我最终定下来的方案是OpenMV H7 Plus负责视觉算法STC8A8K64S4A12单片机负责电机控制和逻辑决策两者通过串口通信。选这套的原因很实在开发效率最高OpenMV的IDE自带帧缓冲区查看功能调阈值、调ROI都是所见即所得比在树莓派上盲调OpenCV参数省太多时间。主控资源充足STC8A8K64S4A12是STC8系列里配置比较顶的型号64KB Flash8KB SRAM主频24MHz可超频到30MHz用来做电机PID控制、编码器读取、串口解析绰绰有余。团队分工方便如果是竞赛组队视觉部分和运动控制部分可以完全并行开发最后串口协议一对接就行。赛事认可度高全国大学生智能汽车竞赛、电子设计竞赛里OpenMV单片机是相当常见的组合参考资料多遇到玄学问题能搜到前人经验。如果预算有限OpenMV可以换成带摄像头的K210开发板主控可以换成STM32F103C8T6整体思路和架构完全不变只是串口协议和引脚定义要改。3. 图像处理与赛道识别核心不是“看”而是“看懂”3.1 从RGB到二值图为什么我放弃了LAB空间多数OpenMV循迹教程会让你把图像从RGB转到LAB空间然后用find_blobs()函数寻找黑色色块或者标记线色块。这个思路在固定光照、固定背景的实验室环境里很好用。但我在实际调试中发现一个问题只要赛场灯光一变或者赛道旁边出现深色物体固定的LAB阈值就失灵了。后来我换了一种思路直接利用灰度图的亮度差异做动态阈值分割。OpenMV的sensor.set_pixformat(GRAYSCALE)可以直接输出灰度图然后我用find_lines()或者get_statistics()动态计算当前画面的灰度均值和标准差用“均值 - k * 标准差”作为阈值做二值化。这样摄像头自动适应不同光照不需要每次开机手动调阈值。不过我得提醒一句如果你用的是红外循迹的传统赛道白底黑线这个方案异常好用。但如果是浅色线深色底逻辑反过来即可代码里取反就行。3.2 提取赛道中线比“找线”更稳的“求质心”方式循迹小车最核心的控制量是赛道中心线在图像中的横向偏移量。传统做法是找黑色色块的cx()中心x坐标然后跟画面中心比如160或170做差得到偏差值。这个思路在直道和普通弯道很稳定但在一种情况下会崩——十字路口。在十字路口黑色色块会突然变成一个极大的连通域质心会跳到画面中央小车会以为自己在直道上直接冲过去。如果是需要通过十字路口还好如果是要在十字路口转弯这个信息就不够用了。我的解决方案是用ROI分区近端中线估计。具体说在图像下半部分靠近车头区域取一条带状ROI宽度大约是图像宽度的80%高度大约30像素。在这个ROI里找黑色区域的质心作为“近端赛道中心”。在图像上半部分再取一个ROI找黑色区域的质心作为“远端赛道中心”。最终偏差 近端质心x坐标 - 画面中心x坐标。这样一来即使远端出现十字路口的大片黑色近端ROI依然能给出当前车头附近可靠的横向偏差。配合后面要说的PID控制过十字路口的平顺性比单质心方案好了太多。3.3 帧率不够怎么办ROI与分辨率是最大的杠杆OpenMV H7 Plus在640x480分辨率下跑灰度图帧率大约在30fps左右但如果加入find_blobs()的多次调用或者大ROI扫描帧率会掉到20fps以下。对循迹这种动态响应要求高的场景帧率比分辨率重要得多。我实测下来QVGA320x240分辨率、灰度图、ROI裁剪到250x150左右帧率可以稳定在50fps以上。这个帧率配合PID控制反应速度完全够用。而且分辨率降低之后find_blobs()的耗时能减少一半以上OPENMV的负载也更小发热更低。另外一个容易忽略的点是镜头畸变。OpenMV默认镜头在画面边缘会有明显畸变如果ROI取到太靠近边缘的区域质心会有一两个像素的偏移。解决方法是把x方向的ROI限制在[20, 300]针对320宽度或者直接换一个视野更窄的镜头。视野窄了虽然看到的赛道范围小了但畸变和干扰也会同比例减少对小赛道反而更友好。4. PID控制与车身动态视觉给的是“眼睛”PID才是“手脚”4.1 为什么不能用简单的“偏差-转向”开关控制很多入门教程的转向控制是这样写的if error 0: left_wheel speed K right_wheel speed - K else: left_wheel speed - K right_wheel speed K这在直道和缓弯道上勉强能用但在S弯和急弯处会出大问题转向量是突变的车身会来回摆严重时直接冲出赛道。原因很简单这种控制方式没有考虑偏差的变化趋势。偏差在快速增大说明弯道很急需要更大的转向量偏差在减小说明车身已经在回正转向量应该适当衰减否则会过冲。解决这个问题只有一个标准工具PID。比例项让转向量跟随偏差积分项消除稳态误差比如由于左右电机速度不一致导致的持续偏航微分项预测偏差变化趋势提供阻尼。实际调参下来只用PD就足够积分项在循迹场景中反而可能因为累积误差造成转向过冲。4.2 我的PID参数整定过程和经验值以OpenMV输出偏差值error单位像素范围大约在[-120, 120]单片机PWM为0~100为例我最终定下来的参数是参数数值说明Kp0.6基础转向增益偏差100像素时转向量60Kd0.8微分增益抑制车身摆动Ki0.02积分微弱补偿双电机微小转速差基础速度35% PWM弯道较多的赛道推荐最大转向量55防止车速过快时转向过猛翻车整定步骤分享给大家拿去做参考先只加P从Kp0.2开始观察小车在直道和缓弯的响应。如果过弯太慢或者压线就逐步增加每次加0.05直到小车能在缓弯保持不出线。加入D从Kd0.3开始看直道上车身是否还摆。如果摆动幅度变小但过弯迟钝稍微加Kp如果过弯发飘加Kd。这个循环反复调直到直道平稳、弯道不飘。最后加一点Ki只为了补偿双电机机械差异导致的慢性偏航。跑几圈如果发现小车总是缓慢地偏向右就加一点Ki0.01起步够了就停。在最终赛道条件下精调把赛道摆成直线直角弯蛇形弯的布局循环跑每次只改一个参数记录现象。4.3 转向限幅与差速比的坑还有一个很多新手忽略的点转向量必须限幅。如果不做限幅当偏差很大比如视野内完全看不到线时PID计算出来的转向量可能超过电机PWM上限PWM占空比超过100%会产生截断而截断之后实际效果和100%没区别等于转向在“饱和区”工作。这种情况下小车会表现出“转向固定最大角度”没有任何过渡非常危险。我处理的方法是// 限制转向量在 [-max_steer, max_steer] if (steer max_steer) steer max_steer; if (steer -max_steer) steer -max_steer; left_speed base_speed - steer; right_speed base_speed steer; // 再做一次限幅防止单侧速度非预期 if (left_speed 100) left_speed 100; if (right_speed 100) right_speed 100;另外差速比不要拉得太极端。当转向量让内侧轮速度降到接近0甚至为负值时车身会绕内侧轮原地转动虽然响应极快但容易导致车身甩尾对重心较高的小车来说非常容易翻车。建议内侧轮最小速度限制在base_speed的30%以上宁可过弯半径大一点也要保证车身的稳定性。5. 串口通信与主控逻辑OpenMV和单片机之间怎么“说话”5.1 通信协议设计越简单越不容易出错OpenMV和单片机之间我用的是UART串口波特率115200数据格式非常简单的三字节帧帧头(0xAA) 偏差低字节 偏差高字节之所以不用文本协议比如发送error123\n是因为文本解析在单片机上要处理字符串转换容易出各种边界问题。二进制协议解析只需要一个状态机几行代码就能搞定。而偏差值范围在[-1024, 1024]之间用有符号short传输拆成两个字节单片机端拼回去即可。OpenMV端发送代码import ustruct from pyb import UART uart UART(3, 115200, timeout_char1000) error 20 # 示例值实际来自图像处理结果 data ustruct.pack(bh, 0xAA, error) uart.write(data)单片机端接收的解析状态机STC8C语言uint8_t uart_rx_buf[3]; uint8_t rx_cnt 0; void UART_ISR() interrupt 4 { if (RI) { RI 0; uint8_t ch SBUF; if (rx_cnt 0) { if (ch 0xAA) // 帧头 uart_rx_buf[rx_cnt] ch; } else { uart_rx_buf[rx_cnt] ch; if (rx_cnt 3) { rx_cnt 0; int16_t error (uart_rx_buf[1] | (uart_rx_buf[2] 8)); // 注意此处要根据实际负数编码方式处理 pid_process(error); } } } }5.2 丢帧和粘包处理串口通信的灰尘在于OpenMV的发送周期和单片机的中断接收之间没有流控当图像处理偶尔卡顿比如周围环境复杂导致find_blobs()耗时突然增加发送周期会抖动单片机可能收到不完整的数据帧。我的处理策略是状态机超时清空如果连续超过100ms没有收到新的帧头不管缓冲区里有什么数据全部清空回到等待帧头状态。帧头判决增强在等待帧头阶段如果收到的字节不是0xAA直接丢弃不进入帧数据接收状态。这样即使数据流中有垃圾字节也不会干扰后续帧的解析。主控端限幅兜底即使解析出来的error值异常大比如因为图像全黑导致质心计算错误PID计算前也需要做一次数值合理性检查超过设定范围直接按最大偏差处理。贴一段我实际用的帧头判决增强逻辑if (rx_cnt 0) { if (ch ! 0xAA) return; // 非帧头直接忽略 rx_buf[rx_cnt] ch; } else { rx_buf[rx_cnt] ch; if (rx_cnt 3) { rx_cnt 0; int16_t raw (rx_buf[1] | (rx_buf[2] 8)); // 符号扩展处理 if (raw 1024) raw - 4096; pid_process(raw); } }注意OpenMV端ustruct.pack(bh, 0xAA, error)中的h是有符号short如果error为负打包出来的高字节会是0xFF之类的值单片机端需要用int16_t接并且做符号扩展。这也是我上面代码里if (raw 1024) raw - 4096的原因——防止把负数当成超大正数。6. 整车组装与供电稳定性的隐形瓶颈6.1 供电架构摄像头和电机绝不能共用一组电源这是整个项目里最隐蔽、也最折腾人的坑。我之前做过一台小车用的是4节18650电池直接给电机驱动模块和OpenMV供电结果只要电机一转OpenMV画面就开始出现条纹干扰严重时花屏死机。查了很久才发现是电机启动瞬间电流大电池电压跌落导致OpenMV的DC-DC输出不稳定。后来我把供电架构改成了双电源隔离动力电源2节18650锂电池7.4V通过LM2596降压模块输出6V给两个减速电机供电。逻辑电源同一个7.4V电源经过另一路LM2596输出5V给OpenMV和单片机系统供电。两个降压模块的输入共用一个电池组但输出完全独立地线在单点连接这样电机的瞬态电流不会直接污染逻辑电源。实测下来画面干净了很多极少出现花屏。如果是用USB供电调试也要注意用充电宝供电时充电宝的自动休眠功能有时候会在电流波动时切断输出导致小车“突然失控”。这种情况可以把充电宝换成航模锂电池UBEC无人机降压模块稳定性会好很多。6.2 编码器是PID的“眼睛”吗这里需要澄清一个概念我在PID控制里用的是OpenMV图像算出的偏差而不是编码器反馈。严格来说这属于单闭环的位置式PID控制视觉偏差是唯一的反馈量。很多做智能车的同学会再加一个电机速度闭环用编码器测速做PID调速这就变成了双闭环。我的忠告是如果只是循迹单闭环就够了。加编码器会显著增加调参复杂度和机械装配难度而且依赖编码器的测速值如果不够准确反而会引入新的噪声。只有当你要做速度赛、需要精确控制过弯速度时才值得上双闭环。当然如果机械结构上左右电机转速差异很明显又没有编码器可以像我一样用微弱的Ki来补偿恒定的偏航漂移。实测下来只要左右电机是同型号、减速比一致Ki设个0.02就足够解决绝大多数慢性跑偏问题。6.3 底盘与重心不只是“拼起来”这么简单底盘的选择直接影响视觉稳定性。我第一辆车的底盘是某宝常见的亚克力三层板装完之后发现一个严重问题摄像头装在车头横杆上只要车身一震动画面就抖得厉害质心计算跟着跳小车在直道上都会走出蛇形路线。后来我换了铝合金底盘并且在摄像头支架和底盘之间加了一层EVA泡棉减震垫。效果立竿见影画面稳定了很多。这里分享一个建议摄像头的安装位置尽量靠近底盘中心线正上方镜头略向下倾斜10-20度这样既能看到近处的赛道线又能兼顾远方的弯道趋势。支架要短而粗悬臂越长振动放大越严重。7. 从“能跑”到“跑得稳”我在调试中总结的几个关键经验7.1 你首先要有个“复现场景”的调试台如果你是在地面贴赛道调试每次弯腰放车、捡车、调参数效率极低。我的建议是用一块倾斜平台模拟赛道一块约60cm x 80cm的木板表面贴黑色电工胶带做成弯道然后架高到腰部高度。这样人站着就能调试OpenMV的画面也能接电脑实时观察。调试效率提升不是一倍两倍。斜坡平台还有一个额外的好处重力会给小车一个沿斜坡下滑的分力相当于变相提高了基础速度让PID参数在“更严苛”的条件下暴露问题。平台上能稳定跑平地上只会更稳。7.2 永远不要只信一次跑通的结果我踩过最大的坑是某天晚上调好Kp和Kd小车连续跑了五圈都完美我以为大功告成了。结果第二天白天到赛场光照变强阈值不再适用小车在第一个弯就飞出去了。原因是OpenMV的灰度图在不同光照下白色背景的灰度值差异极大动态阈值虽然自适应了一部分但如果赛道上有一块反光极强的区域比如因为膜太光滑那个区域的灰度值会突然升高被当成“线”质心瞬间偏移。我的解决办法是加一个灰度值合理性检测每帧图像处理前先计算ROI区域的灰度均值和标准差如果标准差突然变得异常大说明画面里有强反光或阴影干扰就降低Kp或直接沿用上一帧的偏差值。虽然不能完全消除干扰但至少不会让车“疯狂抽筋”。7.3 让系统有“失控兜底”视觉循迹最大的风险是摄像头短暂失明比如被卷起来的胶带挡住、被电线绊住、处理卡顿超过200ms。这时OpenMV可能输出一个错误的偏差值或者串口暂停发送。如果不做兜底小车会按照最后一次接收的偏差继续转向轻则跑偏重则冲下桌子。我在主控里加了一个看门狗定时器如果超过150ms没有收到新的有效帧头就认为视觉链路异常立即让电机输出为0或者按预设策略减速直行。同时蜂鸣器响一声提醒调试者。这个功能在比赛现场救了我一次——摄像头排线松了小车减速停住没有冲出赛道边界。7.4 参数管理的习惯直接影响调试效率最后说一个容易被忽视的经验每次调参一定要记录下来。不要只在代码里改数值而是准备一个类似下面的调参记录表日期KpKdKi基础速度赛道描述现象与结论03-120.60.80.0235%直道90度弯蛇形直道稳蛇形过弯略慢03-130.70.90.0235%同上蛇形明显跟手但90度弯有轻微过冲03-140.71.00.0230%同上过冲改善直道略微发飘没有这套记录你今天调好了明天忘了后天再回到今天的参数全靠感觉回忆非常痛苦。有记录你就能清晰地看到每个参数变化带来的性能趋势调参的效率和成功率都会大幅提升。以上就是我从方案选型到最终稳定跑完整个赛道的完整过程。机器视觉循迹小车的本质是视觉、控制、机械三个领域的一次交叉实践。它不像纯软件项目那样可以无限重试也不像纯机械项目那样可以纯靠堆料——每一次失控背后往往都藏着某个环节没有考虑周全的细节。希望这篇文章能帮你少走一些我走过的弯路也期待你做出比我的车跑得更稳的作品。

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

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

免费获取报价