2025年电赛C题实战:基于单目视觉的目标物测量装置设计与实现
前言
2025 年电赛 C 题的难点不在于识别图形,而在于如何在单目条件下把识别结果稳定地换算成真实尺寸。纯目标检测很容易得到类别和像素框,但题目要的是距离和边长。这套方案的思路是先用 A4 参考物建立尺度,再用几何分析和 YOLO 做目标筛选与测量。
下面直接讲方案里真正有用的部分。
整体方案
系统围绕一张带黑边的 A4 参考板工作,完整链路如下:
- 摄像头采集固定分辨率图像。
- 在预设 ROI 内寻找 A4 黑边内轮廓。
- 对 A4 做透视校正,得到统一尺度的俯视图。
- 基础题直接在俯视图中做几何测量。
- 发挥题和发挥题二先在俯视图中运行 YOLO,再结合几何分析计算真实边长。
YOLO 只负责定位目标,尺度换算还是走 A4 参考物,这样可以绕开直接用检测框回推尺寸时精度不稳的问题。
基础题:先把 A4 参考系建立起来
识别黑边内轮廓,不用外轮廓
题目场景里有一条 5 mm 黑色基准线。参考板贴近地面时,外轮廓容易和背景基准线混在一起,所以代码直接用黑边内轮廓的实际尺寸:
1 | A4_WIDTH_MM = 170 |
注意这里不是用标准 A4 的 210 mm × 297 mm,而是黑边内框对应的可识别区域,后续距离计算都依赖这个数值。
固定 ROI + 轮廓筛选
项目没有做全画面搜索,而是在主画面里裁出一个固定 ROI,再在其中做灰度化、高斯滤波和二值化轮廓提取。随后保留面积足够大的候选轮廓,并取排序后的第二大轮廓作为黑边内框目标。这个策略建立在比赛场景相对固定的前提上,优点是快,缺点是对摆位比较敏感。
核心逻辑可以概括成下面这段:
1 | roi_x, roi_y, roi_w, roi_h = (526, 222, 227, 276) |
后续再通过面积阈值、四边形逼近和长宽比约束,把不合理轮廓筛掉。
透视变换:按高推宽
找到 A4 之后做透视变换。代码没有直接用轮廓的上下边长度定目标宽度,而是先算左右边的平均高度,再按 A4 已知比例反推矫正后的宽度。A4 水平旋转时图像里”宽”的变化比”高”更明显,所以用高来反推更稳定:
1 | max_height = int((height_left + height_right) / 2) |
这样得到的俯视图尺度稳定一些,后面的距离和边长估计误差也会小一点。
距离和尺寸如何换算
距离估计采用标准针孔模型:
1 | distance = (real_height_mm * focal_length_px) / image_height_px |
程序分别用 A4 的宽和高估出两个距离,再取平均值,降低单方向误差。基础题模式下,DataCollectionThread 会在限定次数和超时内采集最多 8 张有效数据,再输出平均距离和平均边长。
发挥题:YOLO 负责找目标,几何分析负责算边长
发挥题没有直接拿 YOLO 检测框宽高当结果。先在 A4 俯视图里运行 YOLO 定位候选目标,再对每个候选框做轮廓分析,找出最长垂直线段作为真实边长的估计依据。检测框受外接矩形和旋转角度影响,直接换算误差偏大,所以这里绕开了框尺寸。
最长垂直线段作为边长候选
find_short.py 里的核心函数会先对目标轮廓做 Douglas-Peucker 简化,再遍历相邻边,找出同时与前后边近似垂直的线段。满足约束的线段中,最长的一条被视为最可信的边长候选:
1 | epsilon = 0.01 * cv2.arcLength(main_contour, True) |
1 | is_perpendicular = ( |
在发挥题里,系统会比较每个目标的最长垂直线段长度,选出“最小正方形”;然后再结合 A4 提供的距离与焦距参数,把像素长度换算成真实边长。当前实现还会对结果做一个很小的经验补偿,以抵消实际拍摄误差。
为什么要预加载和多帧平均
如果 YOLO 在进入发挥题时才首次加载,界面会明显卡顿。所以程序在 CameraThread 初始化时就预加载 best.pt,窗口启动后用一张空白图做一次预热推理,把首次延迟转移到后台。
发挥题不是单帧出结果,而是在超时和最大尝试次数限制内采集 5 张有效数据再取平均。单帧偶发误差基本被抹掉了,等待时间也还在能接受的范围里。
发挥题二:把“识别全部”改成“只测一个”
发挥题二的逻辑和发挥题类似,但增加了一个用户交互步骤:先在界面选择数字 0-9,再只保留对应类别的检测结果。程序在 YOLO 输出后,会把 cls_name 转成数字,只分析用户指定的类别。
这套模式适合比赛里“指定目标测量”的场景。它并没有改变测量方法,改变的只是目标筛选策略,因此整体复用度很高。




