OpenMV4测距与物体尺寸测量:从像素标定到工程实战

发布时间:2026/9/16 20:03:16
OpenMV4测距与物体尺寸测量:从像素标定到工程实战 简介面向OpenMV4开发者与嵌入式视觉初学者的一套测距及物体大小测量代码基于MicroPython实现。资源围绕三角测距原理结合摄像头内参和畸变校正系数K1、K2、K3讲解从画面特征检测到实际距离与尺寸换算的完整流程可用于巡检机器人、目标定位、实验教学等场景。包体非常精简共2个文件包含1个Python脚本和1个Word说明文档压缩包仅13KB便于直接阅读与二次修改。目前已有2657人学习下载。脚本覆盖摄像头初始化、图像采集、颜色/边缘特征提取、距离估算和尺寸换算等核心步骤配套文档则具体解释标定思路与误差控制方式同时提醒更换被测物体后需要重新测量K1、K2、K3以保证精度整体代码结构清晰、注释明确能帮助使用者快速复现并理解单目视觉测量原理减少开发调试成本。1. openmv4 测距为什么先要从「像素宽度」说起如果你给机械臂或小车加“距离感知”又不想一上来就上双目摄像头或深度相机openmv4测距和测量物体大小代码这件事基本是低成本原型里最实用的一条路。它的核心不是机器学习而是把“像素宽度”当作输入同一个目标在图像里占的宽窄既取决于实际物理宽度也取决于它距离摄像头多远。单目视觉必须先固定其中一个变量才能推另一个所以一切的起点都在于如何把blob.w()这个像素值用好。这篇文章面向两类人刚拿到 OpenMV 想快速跑通定位的初学者以及在做抓取、巡检、仓储项目时需要评估误差边界的工程师。后面所有内容都走标定几何这条常见路线不依赖外部算力代码在 OpenMV IDE 里可以直接跑通。下面的推导和代码也刻意绕开了复杂的神经网络方案因为在这个量级的硬件上几何标定的可预测性远高于端到端模型。2. openmv4测距先选模型尺寸法、坐标法还是 TOF 辅助2.1 尺寸法相似三角形是 openmv4测距代码的默认模型在 OpenMV 的常见例程里测距最容易被复现的做法是预先记录一个已知宽度的物体在某个距离下的像素宽度然后用相似三角形推算当前距离。假设参考距离是REF_DIST_CM参考距离下测到的像素宽度是REF_PIX_WIDTH当前目标的像素宽度是px_w那么当前距离约等于distance_cm REF_DIST_CM * REF_PIX_WIDTH / px_w这个式子的成立条件有两个目标平面与成像平面平行且目标宽度方向没有明显倾斜。选择这个模型的原因很实际——它只需要一个颜色阈值和两个标定值不依赖地面平面假设对摄像头装在云台或机械臂末端的情形都成立。它也在同一个标定体系内把测距和测量物体大小连起来了因为距离一旦解出来用同样的像素与实际宽度关系就能反推目标尺寸。误差来源需要提前心里有数目标倾斜、镜头畸变、阈值抖动造成的像素宽度 ±1 个像素波动都会线性映射到距离值上。2.2 坐标映射法的边界不是所有场景都适合算距离另一条常见路线是“像素坐标→地面坐标”的单目测距。摄像头固定高度和俯仰角后把图像 y 轴逐行映射到实际地面距离再用 x 轴像素映射横向位置。这种方法在平地巡检、AGV 避障里很常见远距离误差主要由俯仰角漂移和镜头畸变主导。但一旦摄像头装在云台上改变俯仰角整个映射表就失效需要重新标定目标如果悬浮在桌面以上也不再适用。对“测量物体大小”这个需求来说我更倾向尺寸法。原因是物体在单目视觉里的真实高度本来就缺约束坐标映射法不仅要把目标识别出来还要额外假设目标底面贴合地面这在实际抓取场景里很难成立。尺寸法至少把不确定性集中在“像素宽度”这一个量上后续的滤波和校正都有明确抓手。2.3 TOF 和超声波的辅助思路什么时候该外接tof测距、超声波测距也是 openmv4 项目里常见的配套手段。VL53L0X 这类 TOF 模块通过 I2C 接入能直接给毫米级距离超声波模块则用声波往返时间换算厘米OpenMV 通过引脚捕获回波脉宽即可读取。它们和视觉测距的区别在于视觉能同时锁定目标是哪个而 TOF/超声波只能给一个方向上的距离无法回答“这个距离属于画面里的哪个物体”。所以常见做法是视觉负责目标识别与框选测距芯片负责距离赋值两边再合并进控制逻辑。这个组合在精度和成本之间折中得很好适合对距离精度要求高于视觉像素标定的场合。下面把几种方式放在一起对比测距方式原理标定成本主要误差来源适合场景尺寸法视觉相似三角形低一次标定倾斜、畸变、阈值抖动已知宽度目标、机械臂抓取像素坐标映射平面单应中四点标定俯仰角、地面不平平地 AGV、固定摄像头TOF 辅助飞行时间低被测物表面材质精确距离赋值超声波辅助声波回波低多径、软材质吸波大范围粗测2.4 先把“比例因子”定义清楚无论是测距还是测大小标定最终都收敛到同一个量单位像素对应的实际尺寸。推荐标两个参考点而不是只标一个。第一点把目标放在D120cm记录已知宽W_target的像素宽度P1第二点放在D250cm记录P2。这样能得到两组比例K1P1/W_target、K2P2/W_target后续代码里可以线性插值也可以先反推距离再修正。第 3 章的代码按“单点标定 距离修正”实现逻辑更直白适合先跑通再扩展成两点插值。3. openmv4 测量物体大小的核心代码find_blobs 加比例标定3.1 用 find_blobs 锁定目标并拿到像素宽度OpenMV 自带的find_blobs是测量物体大小最常用的入口。它的输入是颜色阈值输出包含每个色块的x, y, w, h、面积、像素数和density。这里的w表示色块外接矩形宽度单位是像素加上blob.cx()可以定位目标中心。需要区分的是pixels_threshold和area_threshold前者过滤像素总数太少的小噪点后者按面积过滤。做距离计算时建议选最大的那个 blob而不是blobs[0]因为最大块通常才是目标主体。实际使用中mergeTrue建议保持开启并把margin适当调大防止同一目标因颜色轻微不连续被拆成几个子块。反光严重时density会明显偏低可以在进入距离计算前加一层判断。另外需要注意blob.w()是外接矩形的像素宽不是色块最宽处的实测宽度目标略微旋转时它只会变小不会变大。3.2 完整代码同时输出距离与物体实际宽度import sensor, image, time from statistics import median # ---- 标定值在固定距离测一次即可 ---- REF_DIST_CM 45.0 # 标定时物体距摄像头 REF_PIX_WIDTH 96.0 # 标定时 find_blobs 返回的 blob.w() TARGET_REAL_WIDTH_CM 6.0 # 目标物的实际宽度 # ---- 颜色阈值从 IDE 阈值编辑器复制LAB 空间 ---- THRESHOLD (25, 80, 30, 80, 10, 70) sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # 320x240 sensor.skip_frames(30) dist_history [] width_history [] while True: img sensor.snapshot() blobs img.find_blobs([THRESHOLD], pixels_threshold200, area_threshold200, mergeTrue) if not blobs: continue # 取最大面积 blob 作为目标 max_blob max(blobs, keylambda b: b.area()) px_w max_blob.w() if px_w 4: continue # 1) 尺寸法测距距离与像素宽度成反比 dist_cm REF_DIST_CM * REF_PIX_WIDTH / px_w # 2) 反推实际宽度比例随距离线性变化乘上距离倍率修正 real_w_cm (px_w / REF_PIX_WIDTH) * TARGET_REAL_WIDTH_CM * (dist_cm / REF_DIST_CM) # 3) 中位数滤波抑制单帧抖动 dist_history.append(dist_cm) width_history.append(real_w_cm) if len(dist_history) 7: dist_history.pop(0) width_history.pop(0) d_filt median(dist_history) w_filt median(width_history) img.draw_rectangle(max_blob.rect(), color(255, 0, 0)) img.draw_string(max_blob.x(), max_blob.y() - 12, %.1fcm %.1fcm % (d_filt, w_filt), color(255, 255, 0)) print(dist%.1fcm real_w%.1fcm px%d % (d_filt, w_filt, px_w)) time.sleep_ms(50)代码第 1 个公式直接实现相似三角形测距。第 2 个公式把参考位置对应的px/cm比例换算到当前距离因为同一物体离得越远单位厘米对应的像素越少而dist_cm / REF_DIST_CM表示当前距离相对参考距离的倍数实际宽度就按这个倍数修正。真正标定只有一个动作把目标放在距离摄像头 45cm 处让find_blobs输出一个已知 6cm 宽目标的像素宽度填到顶部即可跑通整套流程。参数方面pixels_threshold200过滤零散噪点mergeTrue合并相邻同色块避免目标被拆开导致w偏小。历史队列用中位数而不是平均值因为中位数对偶尔一帧的误检不敏感。这里没有用blob.x()做视野边缘修正原因是宽度方向在画面边缘受畸变影响比高度方向小除非做过镜头校正否则额外补偿只会引入新误差。3.3 测量物体大小时要先分辨“框宽”与“实际宽”blob.w()返回的是外接矩形的像素宽而你要的是目标真实物理宽度。只要目标没有明显倾斜w和实际宽度线性相关一旦目标旋转或斜切w会变小距离计算就会严重偏大。常见做法是标定时让目标正面正对镜头运行阶段通过density max_blob.density()做辅助筛选。density描述的是色块像素数占外接矩形的比例若低于 0.5多半是反光、光斑或部分遮挡造成的假目标这帧可以直接丢弃而不是强行输出一个错误距离。4. openmv4测距代码的调参顺序阈值、分辨率、曝光与边界情况4.1 第一步用直方图把颜色阈值精确复制进代码打开 OpenMV IDE运行帧缓冲在工具菜单里能找到阈值编辑器。先对准目标物框选目标区域三个通道的直方图会分别显示min/max区间把结果直接粘贴到代码里的THRESHOLD元组即可。需要注意这个阈值是 LAB 色彩空间下对光照敏感的区间曝光高了L 通道整体偏大室内灯光偏暖A/B 通道会整体漂移。所以调阈值前先固定自动曝光和白平衡。操作顺序建议是先把目标放到实际工作距离然后手动固定曝光再打开阈值编辑器框选目标最后把结果粘贴进代码。如果在自动曝光下复制阈值换一个距离后亮度变化整个阈值区间就会失效。4.2 第二步分辨率与测距精度的实际取舍OpenMV4 的感光芯片能跑多档分辨率但实时find_blobs不建议开最大分辨率。下面给出不同档位的选择和理由分辨率尺寸帧率表现测距精度适用距离QQVGA160x120高更流畅像素少近距离够用5~20cmQVGA320x240中等更平衡中等代码里默认15~80cmVGA640x480低注意 CPU 占用高远距离更稳40cm 以上距离越远目标像素宽度越小在 VGA 下多 1 个像素可能只代表几毫米误差而在 QQVGA 下同样 1 个像素的抖动就能带来百分之几的距离波动。如果做固定距离的抓取实验优先用 QQVGA 提升帧率如果做远距离测距可以考虑把分辨率提到 VGA 并把pixels_threshold适当调大避免背景噪点抢占最大面积 blob。4.3 第三步曝光与白平衡会影响整个比例关系自动曝光在目标忽近忽远时会让图像亮度来回变化同样的物体在不同亮度下像素宽度会抖动。进一步看多数 openmv4测距项目的误差其实不是公式不对而是模糊帧里边缘判定不稳定。常见做法是初始化时关闭自动曝光和白平衡手动给固定值保证同一环境下的阈值区间一致。代码片段如下sensor.set_auto_whitebal(False, rgb_gain_db0.0) sensor.set_auto_exposure(False, exposure_us20000) sensor.set_auto_gain(False, gain_db8.0)exposure_us数值越大画面越亮但运动目标会拖影反而让w变大。环境光不足时优先把曝光调到 25000~30000us而不是把 gain 拉太高否则噪点会被当成目标。固定曝光后重新跑一遍阈值编辑器把新阈值覆盖到代码里。4.4 边界情况对照表下面这张表整理了 openmv4测量物体大小代码调试时最常遇到的五种问题每一种都对应明确的调参方向现象可能原因处理建议blob 断成几段阈值区间太窄或反光调宽阈值、开启mergeTrue距离数值来回跳目标边缘模糊、抖动多帧中位数滤波、固定曝光近距离偏大/远距离偏小目标旋转或镜头畸变保持目标正对、缩短使用距离范围框住了背景大块同色区域目标与背景颜色接近拉高area_threshold或换颜色空间多个目标同时出现误选了非目标按area()排序或加 ROI4.5 ROI 剪裁能大幅减少误检如果目标只在画面的固定区域出现把 ROI 传给find_blobs是性价比最高的优化一是减少计算量二是排除画面边缘的反光或杂色。ROI 参数格式是(x, y, w, h)单位为当前分辨率下的像素。例如目标在画面左侧可以写成img.find_blobs([THRESHOLD], roi(10, 30, 140, 180), ...)先用img.draw_rectangle在画面上画出该区域验证位置。需要提醒的是ROI 缩窄后如果目标超出边界返回的w会被截断距离和尺寸都会出错所以 ROI 边缘要留至少 10 个像素的余量。5. 让 openmv4 一直量得准用 AprilTag 在线校正比例因子5.1 为什么用 AprilTag 做校正find_blobs的比例标定是一次性的温度变化、机械结构松动或镜头被碰过之后REF_PIX_WIDTH可能整体漂移但全部重新标定又很麻烦。实际项目里我一般会在视野角落贴一个固定尺寸的 AprilTag利用它的像素宽度实时反推当前环境的“像素/厘米”比例再用这个比例去校正普通目标的尺寸换算。标签本身由 OpenMV 的find_apriltags直接识别不需要训练模型也不需要额外库。5.2 校正代码把标签比例接到主循环里TAG_REAL_W_CM 4.5 # 贴在场景里的 AprilTag 实际边长 while True: img sensor.snapshot() tags img.find_apriltags() if tags: tag tags[0] tag_px_w tag.w() # 标签的像素宽度 px_per_cm tag_px_w / TAG_REAL_W_CM # 当前深度下的比例因子 # 用校正后的比例去算普通目标的实际宽度 blobs img.find_blobs([THRESHOLD], pixels_threshold200, mergeTrue) if blobs: b max(blobs, keylambda x: x.area()) real_w_cm b.w() / px_per_cm print(corrected_w%.2f cm % real_w_cm) time.sleep_ms(20)tag.w()返回的是 AprilTag 的像素宽度px_per_cm就是当前深度下单位厘米对应的像素数。把这个比例接到普通色块的换算上能自动吸收镜头轻微位移带来的比例漂移。这个思路的前提是标签和目标处于同一深度平面附近如果两者深度差明显需要先分别计算出各自的距离再各自换算不能直接共用一个px_per_cm。5.3 记住两步验证方法代码跑通后建议做三组实测目标分别放在 30cm、50cm、70cm记录校正后的输出与实际卷尺读数。若误差超过 8%优先怀疑目标没有正对镜头或镜头形变较大可以尝试打开 OpenMV 的sensor.set_lens_correction(True, 1.2)后再重新校正一遍。把三组误差记录下来画一条线性修正曲线加到print之前的换算里这是在不换硬件的前提下收敛精度最直接的手段。本文还有配套的精品资源点击获取