news 2026/9/9 2:12:43

2024电赛捡球小车全解析:从视觉识别到运动控制的完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2024电赛捡球小车全解析:从视觉识别到运动控制的完整方案

简介:面向2024年电赛参赛者的捡球小车全套资料,覆盖无轨智能车从底层驱动到上层算法控制的完整工程。包内以嵌入式C代码为主,含142个.h头文件、97个.c源文件,另有少量汇编启动文件、链接脚本、Keil/IAR/CCS工程文件,以及Python辅助脚本和清理临时文件的批处理,便于跨平台阅读与二次开发。资源共344个文件,压缩包仅5.44MB,体量精简但结构完整,适合正备战电赛、需要整机方案参考的电子类学生。目前已获312人学习下载,可直接获得一套可编译调试的赛题骨架,在电机驱动、传感器采集、捡球逻辑等方面提供具体代码样例,帮助少走弯路。文件按功能组织清晰,能较快理解无轨小车任务中的模块分工与主流程。从芯片初始化到运动控制的代码层级完整,参考价值较高,尤其适合赛前冲刺期的快速上手与功能裁剪。 2024电赛的捡球小车,算是当年控制类题目里比较有代表性的一个。做这个项目前后折腾了一个多月,从最初的结构选型到最后的现场调试,踩了不少坑,也总结了一些经验。这几天把全套资料整理了一遍,想着把完整的设计思路、硬件选型、核心算法和调试过程都写出来,给准备参加电赛或者做机器人入门的朋友一个参考。

如果你正准备电赛,或者想自己做一台能识别、抓取、搬运乒乓球的小车,这篇文章应该能帮你少走不少弯路。我会从系统方案讲起,然后拆分硬件搭建、视觉识别、控制逻辑和现场调试这几个关键环节,每一步都说清楚“为什么这么做”和“实际做的时候要注意什么”。

1. 项目概述与整体设计思路

1.1 核心需求拆解

捡球小车这个题目,表面上看是让小车把散落的乒乓球收集到指定位置,但拆开来看,实际包含了三个核心问题:怎么找到球、怎么把球抓起来、怎么把球送到目的地。这三个问题对应到技术上,就是视觉识别、机械结构和运动控制。

先说找球。电赛场地一般是平整的浅色地面,乒乓球是橙白色,对比度还算明显,用摄像头做颜色识别是主流方案。但比赛现场的光线不稳定,反光、阴影、不同颜色的地板都会干扰识别,所以视觉方案不能只做简单的颜色阈值,得加一些滤波和形态学处理。我采用的是OpenMV摄像头,理由是它上手快、有现成的颜色识别库,而且在比赛这种需要快速开发的场景下,稳定性比从零训练一个深度学习模型可靠得多。

再说抓球。乒乓球直径40mm,重量很轻,但表面光滑,用机械爪夹取容易打滑,用吸球结构又受限于风扇功率和小车供电。比较下来,我选了“铲斗式”结构加舵机控制——小车前方装一个弧形铲斗,对准球的位置铲进去,球就顺着坡度滚进球仓。这个方案结构简单、容错率高,只要定位误差在±5cm以内就能把球收进来,比精确夹取对视觉和控制的要求低很多。

最后是运动控制。场地一般是3m×3m左右的区域,散落十来个球,如果小车没有路径规划,靠随机转向找球,效率极低。我用的是“分区扫描+逐区清理”的策略:把场地划分成九宫格,从起点开始按蛇形路径巡检九宫格,摄像头发现球后停车对准抓取,然后继续巡检。这样既保证了覆盖完整,又不会出现小车在原地反复转圈的情况。

1.2 为什么采用这个技术栈

选定方案的时候,我对比过几个备选路线,这里把核心取舍说明白。主控上面,很多人会选STM32,但就捡球小车这个应用场景来说,ESP32其实更适合——它有WiFi和蓝牙,调试的时候可以直接无线传图,不用每次下载程序都插线;而且双核160MHz的算力配合MicroPython,跑图像处理的前级逻辑加运动控制,性能完全够用。更重要的是,ESP32的PWM舵机库和电机驱动库非常成熟,生态完善,遇到问题一搜就有解决方案。

我在开发过程中体会到,电赛这种限时赛事,核心原则是“稳定压倒一切”。别去追求高精尖的技术,而是要把最成熟的技术组合用稳。比如不用视觉SLAM,而用固定路径扫描;不用深度学习识别,而用颜色阈值加滤波处理——每一个决策都是在“时间有限”和“现场不确定性大”这两个前提下做出的最优解。

2. 硬件选型与模块搭建

2.1 主控与视觉模块选型

主控选的是ESP32-WROOM-32开发板,这款板子IO口充足,ADC精度够用,最关键是支持Micropython,写起来效率极高。视觉模块用了OpenMV Cam H7 Plus。这里要特别说明一下,OpenMV本身也是一块小主控,它和ESP32之间通过UART串口通信,OpenMV负责图像识别,把结果(球的位置坐标、颜色信息)发给ESP32,ESP32再下指令给电机驱动和舵机。这样分层的架构,好处是图像处理的高算力需求和高实时性需求,跟运动控制的实时响应解耦了,各干各的,不会出现卡顿。

选OpenMV而不是直接用ESP32-CAM,也是权衡过的。ESP32-CAM便宜,但图像处理库不够成熟,要做色彩识别、寻找色块、计算中心坐标这些操作,需要自己写OpenCV代码,还得考虑内存优化,开发周期至少翻一倍。OpenMV自带的find_blobs函数直接返回色块的中心坐标、像素面积、外框尺寸,开发效率高太多了。

2.2 底盘与驱动结构设计

底盘我用了四驱小车底盘,四个直流减速电机,轮子带橡胶轮胎,抓地力好,适合在光滑场地跑。这里有个关键细节——电机驱动模块用的是TB6612FNG,而不是更常见的L298N。原因有三个:体积小、发热低、效率高。TB6612FNG的压降只有0.5V左右,电池7.4V供电的话,电机实际能到6.9V,能够更好地保证动力输出。L298N的压降要2V以上,跑一会儿就烫手,动能损耗太明显。

电机选的是1:48减速比的N20微型电机,6V供电下转速约300rpm。这里要算一笔账:小车轮胎直径65mm,周长大约204mm,每分钟转300圈,理论速度约61m/min,也就是1m/s左右。这个速度在捡球任务里偏快,容易冲过目标,但好在可以通过PWM调速。我实际测试下来,把PWM调到60%左右,速度大概0.6m/s,抓球稳定性最好。

机械臂部分,一开始我试过用两个舵机组成的二自由度机械臂加夹爪,但发现控制复杂,夹取误差大。后来推翻重来,用了方案最简单的“前铲式”结构——一个180°舵机控制铲斗抬起和放下,加两个限位挡板。铲斗放下来的时候,前缘贴地,球被推进来以后就直接滚进球仓,铲斗抬起来的时候,球就稳稳卡在仓里,不会掉出去。

2.3 电源与供电配置

电源是很多新手容易忽略的地方,但往往是现场翻车的高发区。我用了两块锂电池:一块7.4V 2200mAh的2S锂电池给电机供电,一块3.7V 18650电池给OpenMV单独供电。为什么要分开?因为电机启动瞬间的电流冲击很大,四驱全速启动时瞬时电流能到3A以上,如果摄像头和主控跟电机共用电源,电压会被瞬间拉低,导致ESP32重启或者OpenMV丢帧。分电之后,系统稳定性提升非常明显。

主控ESP32的供电则是从电机电池那边加了一路DC-DC降压模块,降到5V/3A给ESP32和舵机控制器供电。不直接从7.4V接舵机,是因为很多舵机额定电压是4.8V~6V,电压过高容易烧舵机。降压模块留足了余量,舵机启动瞬间的电流冲击也扛得住,实测满载时电压纹波控制在±0.2V以内,非常稳。

3. 核心算法与逻辑实现

3.1 视觉识别与坐标转换

OpenMV端最主要的任务是识别地面上的乒乓球,计算它在图像中的位置。代码核心就是find_blobs函数,设置LAB色彩空间的阈值来提取橙球。但实际用的时候,单靠颜色阈值会出很多问题——地面反光、阴影、白线都会被误判成目标。我的处理方案是加两个过滤条件:一是色块面积必须大于某阈值(比如200像素),二是色块的最小外接矩形的宽高比要接近1:1(因为球从顶部看是圆形)。

识别到色块之后,就会得到目标中心在图像坐标系中的位置(cx, cy),这个坐标是相对于摄像头画面的,要转成小车坐标系才能用来控制运动。我用的是简单的小孔成像模型:摄像头的安装高度固定为h=15cm,俯仰角为0度(即水平朝前下方45度安装),那么小球在摄像头前方的距离可以通过几何关系换算得到。

实际落地的时候,我没搞太复杂的标定,而是用了“查表法”——把场地分成前后几排,在每排的典型距离上放一个球,记录对应的像素纵坐标,做成一个映射表。这样OpenMV只需要返回cy的值,ESP32就可以查表得到球的“前后距离”,cx经过简单的线性映射得到“左右偏转角度”。这个办法在电赛时间紧张的情况下非常实用,避免了繁琐的相机内参标定,鲁棒性也足够。

这里给出我的OpenMV端核心代码简化版,实测效果很稳定:

import sensor, image, time, math from pyb import UART # 初始化摄像头 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QQVGA) # 160x120,够用且速度快 sensor.skip_frames(30) # LAB色彩空间阈值,从IDE的阈值编辑器中获取 red_threshold = (30, 100, 15, 65, 10, 60) uart = UART(3, 115200) last_cx = -1 last_cy = -1 while(True): img = sensor.snapshot() blobs = img.find_blobs([red_threshold], pixels_threshold=200, area_threshold=200) if blobs: # 取面积最大的色块 largest_blob = max(blobs, key=lambda b: b.area()) cx = largest_blob.cx() cy = largest_blob.cy() # 过滤:宽高比接近1:1,排除拉长的噪声 w = largest_blob.w() h = largest_blob.h() if abs(w - h) / max(w, h) < 0.5: last_cx = cx last_cy = cy # 通过串口发送坐标,格式 "x,y\n" uart.write("{} ,{}\n".format(cx, cy))

代码逻辑并不复杂,但要注意几个细节。pixels_threshold设太低会把噪点误判成目标,设太高又容易漏掉远距离的小球,这个值需要根据场地实际光线调节,一般200左右起步。宽高比过滤非常关键——场地上的长条阴影经常会被识别成色块,但阴影形状不是圆形,宽高比通常大于1.5,直接丢掉就行。

3.2 抓球决策与运动控制模型

ESP32端收到坐标后,要把它转成运动指令。我的控制策略是分级决策,有点像自动驾驶里的行为决策层和运动控制层的分工。先根据cx判断球在小车的偏左还是偏右:

  • 如果|x_c - 80| < 15(画面中心宽度为160,80是中心),说明球基本在小车正前方,直接前进抓取。
  • 如果cx < 65,说明球偏左,左轮减速、右轮匀速,让车头往左转。
  • 如果cx > 95,说明球偏右,右轮减速、左轮匀速,往右转。

这里控制转向的比例是关键。我采用的是P控制,转向速度跟偏移量成正比,偏移越大,转向越猛。为了避免转向过度引起震荡,限定了最大值:

# 接收OpenMV的坐标 def on_receive(cx, cy): # 转成转向偏差,图像宽度是160,中心是80 error = cx - 80 max_turn = 50 # 最大转向占空比加成 turn = max(-max_turn, min(max_turn, int(error * 0.8))) base_speed = 40 # 基础PWM占空比,0-100 left_speed = base_speed - turn right_speed = base_speed + turn # 边界裁剪,确保PWM在0-100之间 left_speed = max(0, min(100, left_speed)) right_speed = max(0, min(100, right_speed)) set_motor(left_speed, right_speed)

还有一个关键点,就是“近距盲区”问题。OpenMV装在车头前方,当球滚到摄像头正下方时,镜头里反而看不到了。所以我在铲斗前面加了一个红外避障传感器,当检测到前方5cm内有障碍物时,不管视觉有没有看到球,直接落下铲斗、前进铲球。这个传感器就像是小车的“触觉”,弥补了视觉盲区的缺陷。

3.3 场地扫描与分区清理策略

抓球效率方面,一开始我的做法是小车直接朝最明显的球开过去,抓完一个再找下一个。这在小球数量少的时候还行,但当场上同时有十来个球时,小车就会乱跑,经常出现一个区域的球被反复扫过,另一个区域却完全遗漏的情况。

后来我把场地分成了九个区域,用蛇形扫描路径做全局巡检,才彻底解决了覆盖问题。核心逻辑如下:

  1. 按从左到右、从上到下的顺序巡检九个区域;
  2. 每个区域内,小车左右巡视,OpenMV实时寻找目标;
  3. 识别到球后,进入“抓球模式”;
  4. 抓球完成后,回到规划路径的断点位置,继续巡检;
  5. 巡检完九个区域后,自动返回起点。

这套策略最大的好处是让小车的行为可预测、可调试。你永远知道小车下一步要干什么,出问题能迅速定位是哪个环节出了错。这在比赛调试中节省了大量时间。

4. 实测调试与高频问题排查

4.1 现场调试流程与顺序

调试顺序极其重要,很多人上来就直接调视觉识别,结果小车动起来就乱跑,根本分不清是视觉的问题还是底盘的问题。我的建议是严格按照“底盘 → 串口通信 → 视觉识别 → 联调”的顺序来。

先别装视觉模块,直接给ESP32写一个固定路径程序,让小车按照预设的路径走一遍。这一步是验证底盘驱动、电机方向、PWM调速有没有问题。特别注意左右轮方向:我第一版代码左右轮转向写反了,小车原地打转,检查接线查了半天,最后发现是电机驱动逻辑写反了,对调一下就正常了。

第二步,把OpenMV和ESP32用串口线连起来,用USB转TTL工具监听串口输出。放一个球在摄像头前面,看OpenMV发出来的坐标数值有没有规律、稳不稳定。如果坐标跳动特别大,优先检查曝光和阈值。

第三步,用上位机或者OLED屏实时显示小车状态。我给ESP32加了一个0.96寸的OLED,显示当前的运行模式、OpenMV返回的坐标、电量电压,调试起来非常直观。比如小车原地转圈,一查显示发现cx一直显示80(中心),说明OpenMV把背景墙上的红色标签当成球了——这就是阈值没调准导致的误识别。

4.2 常见问题速查表

结合我自己的调试经历,以及群里其他参赛队伍反馈的高频问题,整理了一份速查表:

现象根本原因解决方案
小车开机后无反应电池电压不足或降压模块没输出先测电池电压,再查DC-DC输出,逐级排查
摄像头识别不到球阈值设置不当或曝光过度在OpenMV IDE里重新设置LAB阈值,锁定自动曝光
识别到很多假目标地面反光/杂色干扰加宽高比过滤;调整摄像头安装角度避免直射光;减小曝光时间
小车抓球时总是差一点视觉坐标偏差或反应滞后检查摄像头是否牢固;增加PID参数微调;降低接近速度
球进入铲斗又掉出来铲斗抬升速度太慢或角度不够调高舵机PWM频率;加大铲斗抬起角度限位;在铲斗前缘加挡边
四个电机转速不一致电池电压降低或电机老化逐个测电机空载电流;调整PWM占空比补偿差速
抓球过程中小车莫名重启电机电流冲击导致电压跌落主控和电机分电源供电;在电机电源端加一个大电容

这里面我特别想说一下“小车抓着抓着突然重启”这个问题。一开始我以为是代码bug,查了半天最后用万用表发现,电机全速启动的瞬间,系统电压从7.4V掉到了4.8V,ESP32的供电电压掉到3.3V以下直接复位了。解决办法就是前面说的分电源供电加粗导线,另外在电机电源输入端并联了一个470uF的电解电容和0.1uF的陶瓷电容,效果立竿见影。

4.3 性能调优与稳定性提升

系统稳定之后,我开始做性能微调,主要围绕三个方面。第一是视觉帧率,OpenMV默认跑QQVGA分辨率只有30fps左右,我通过降低分辨率到160x120、跳过帧率设置、只检测ROI区域(图像中心区域)等方法,把识别帧率稳定在了50fps左右,小车反应速度明显提升。

第二是运动平滑度,改用梯形加减速之后,小车起步和停车的冲击感明显降低,机械结构和传感器的抖动减少,抓球成功率高了不少。实现思路就是启动时不要直接给满速PWM,而是让PWM在100ms内从20逐步加到目标值,停车时反向操作。

第三是策略容错。在目标检测连续超过1秒钟没看到球的时候,小车会进入“原地旋转扫描模式”,转一圈重新定位周围的球,比傻傻往前开有效得多。这个功能在球少、场地大的时候特别管用,能避免小车在场地里空跑。

5. 比赛现场的经验与心得

做完这个项目,最大的体会是:电赛考察的不只是知识储备,更是工程取舍能力和临场应变能力。赛前一周,我们花了好几个通宵把视觉识别和小车底盘调稳,结果比赛第一天场地光线跟实验室完全不一样,阈值全部失效。当时没有慌,而是直接在比赛现场重新标定了一遍阈值,又调整了摄像头角度避开顶光灯直射,花了大约四十分钟就把问题解决了。这种“现场解决问题的能力”,才是比赛真正的收获。

另外分享一个小技巧:把所有传感器的数据都通过串口实时打印出来,留一个Debug模式开关。这样在现场排查问题时,不用改一行代码,就能看到小车“脑子里在想什么”。很多莫名其妙的问题,只要能看到数据流,基本十分钟内就能定位。捡球小车这个项目,做完之后我最大的感受就是,把简单可靠的东西做到极致,就足以应对复杂的任务。如果你也准备做类似的项目,希望能从这篇文章里获取一些思路,少踩一些我踩过的坑。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 2:11:21

身份证识别SDK全套源码详解:从图像处理到OCR识别核心链路

简介&#xff1a;在安防、政务终端与酒店登记等实名认证场景中&#xff0c;身份证识别技术的应用日益广泛。通常情况下&#xff0c;开发者要么调用按次计费的云端OCR接口&#xff0c;要么受制于加密的商用SDK&#xff0c;始终无法深入核心算法。而一套完整的身份证识别SDK源码&…

作者头像 李华
网站建设 2026/9/9 2:06:26

论文降AI率工具横向实测:稳定性比最低值更重要

前段时间一个学弟找我说&#xff0c;论文用AI辅助写了初稿&#xff0c;提交前查了一下AIGC检测率&#xff0c;直接标红一片&#xff0c;各种系统都提示“疑似深度合成内容”。他问我哪款论文降AI率工具能救急。说实话&#xff0c;这问题我挺能感同身受的——现在不少高校对学位…

作者头像 李华
网站建设 2026/9/9 2:03:34

5款AI课堂系统实测对比:备课减半、学生提升18分

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/9 2:03:16

DeepSeek Harness四种Preset深度解析:参数原理与实战选型

如果你刚装上 DeepSeek Harness&#xff0c;大概率会在界面上和这四个 Preset 打个照面&#xff1a;极简、标准、PTC、创造。我最初以为这跟很多工具里的"风格模板"差不多&#xff0c;无非是换个提示词或者换个语气&#xff0c;直到我拿同一段需求分别跑了四个模式&a…

作者头像 李华
网站建设 2026/9/9 2:03:03

长江经济带GIS矢量图制作全流程:从数据获取到拓扑检查与实战应用

简介&#xff1a;长江经济带GIS矢量图是一份面向地理信息从业者、城乡规划研究者及经济地理分析人员的专题地图数据集&#xff0c;聚焦2015年长江经济带沿线地级行政区的空间底图需求。压缩包共7个文件&#xff0c;以shapefile格式组织&#xff0c;包括shp几何要素、dbf属性表、…

作者头像 李华