从相似三角形到YOLO:单目测距的简易实现与误差分析 1. 单目测距的基本原理单目测距听起来很高大上但其实原理特别接地气。想象一下你站在路边看到远处有个电线杆。虽然不知道具体多远但如果你知道电线杆的实际高度再对比它在视野中的大小就能大概估算出距离。这就是单目测距的核心思想——用已知的物体尺寸通过它在图像中的表现来推算距离。这里的关键是相似三角形原理。摄像头成像时物体、镜头和成像面之间会形成一组相似三角形。具体来说当物体距离摄像头为D实际宽度为W时在图像上呈现的像素宽度P与焦距F满足关系D/W F/P。稍微变形一下就能得到距离计算公式D W * F / P。焦距F是摄像头的固有参数就像人的视力一样固定不变。如果不知道焦距怎么办别急我们可以用标定的方法来测量。具体操作是先拍一张已知距离D和实际宽度W的物体照片测量图像中的像素宽度P然后套用公式F P * D / W就能算出焦距。这个方法我在调试摄像头时经常用实测误差可以控制在3%以内。2. YOLOv5与单目测距的完美结合2.1 为什么选择YOLOv5传统单目测距有个痛点需要手动标注物体位置。而YOLOv5这个目标检测算法正好能自动解决这个问题。我实测过多个版本v5s在精度和速度上取得了很好的平衡特别适合实时测距场景。它的检测框输出格式(xyxy)直接给出了物体在图像中的像素坐标省去了大量预处理工作。2.2 具体实现步骤先说说我的开发环境配置Python 3.8PyTorch 1.10OpenCV 4.5YOLOv5最新代码库核心代码其实就三个部分焦距标定用已知距离的物体照片计算焦距def calculate_focalDistance(img_path): image cv2.imread(img_path) # 获取YOLO检测结果 perWidth bbox_right - bbox_left # 像素宽度 focalLength (perWidth * KNOWN_DISTANCE) / KNOWN_WIDTH return focalLength距离计算用标定好的焦距预测新照片def distance_to_camera(knownWidth, focalLength, perWidth): return (knownWidth * focalLength) / perWidth可视化输出把结果画在图片上plot_one_box(xyxy, image, labelf{distance}cm, colorcolors[int(cls)])3. 实战杯子测距实验我找了个标准尺寸的红色马克杯宽15cm高9cm做测试。先用20cm距离的照片标定焦距然后分别测试30cm、40cm、50cm的情况。实测数据如下实际距离预测距离绝对误差相对误差20cm20.0cm0cm0%30cm31.2cm1.2cm4%40cm42.8cm2.8cm7%50cm55.1cm5.1cm10.2%从数据可以看出随着距离增加误差会明显放大。这主要是因为远距离时物体在图像中占比小几个像素的偏差就会导致较大误差摄像头存在镜头畸变边缘区域的成像会有变形实际拍摄时很难保证完全正对物体中心4. 误差分析与优化方案4.1 主要误差来源根据我的项目经验误差主要来自三个方面检测框抖动YOLO的检测框在不同帧间会有轻微波动拍摄角度摄像头与物体不水平时实际成像宽度会变小镜头畸变普通摄像头的桶形畸变会影响边缘区域的测量精度4.2 实测有效的改进方法经过多次调试我发现这几个方法能显著提升精度多帧平均连续采集10帧数据取平均值可以减少检测抖动distances [] for _ in range(10): distance calculate_distance(...) distances.append(distance) final_distance np.median(distances)角度补偿当知道倾斜角度θ时可以乘以cosθ进行补偿adjusted_width detected_width / math.cos(theta)镜头标定用棋盘格预先标定摄像头校正畸变# 使用OpenCV的标定功能 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera(...) undistorted cv2.undistort(image, mtx, dist)多物体校验同时检测多个已知尺寸的物体交叉验证5. 进阶技巧与注意事项在实际项目中我还总结出一些实用经验物体选择最好选择长宽比固定的规则物体比如A4纸21×29.7cm、硬币等。我试过用手机做目标物结果因为不同型号尺寸差异太大导致误差明显。光照条件强烈反光会严重影响检测精度。建议在均匀光照环境下测试或者给目标物体贴无光胶带。摄像头选择固定焦距的工业摄像头比手机摄像头更稳定。我用过一款200万像素的basler相机在3米范围内误差能控制在2%以内。标定技巧标定时要把物体放在画面中央因为镜头中心区域的畸变最小。我一般会拍5组不同距离的照片取平均焦距值。代码优化对于实时应用可以把YOLO检测和距离计算分开线程处理。这样即使检测耗时较长也能保证距离更新的频率。这套方案虽然简单但在很多场景下已经足够实用。比如我帮朋友做的仓库货物距离监控系统就是用这个方法实现的成本不到500元比激光雷达方案便宜了90%。当然对于更高精度的需求还是需要考虑双目视觉或者其他深度传感器。