ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenCV双Mark点定位原理与像素当量标定实战

OpenCV双Mark点定位原理与像素当量标定实战 搞机器视觉或者自动化设备的朋友对“mark点”应该都不陌生。无论是一台SMT贴片机、点胶机还是机械手抓取工位只要产品换型或者载具重复放置存在偏差几乎都会用两个mark点来做位置基准。说白了就是通过两个事先人为设置的参照点在每次拍摄的图像里重新建立一个坐标系再把这个坐标系和目标点绑在一起从而算出目标点当前到底在哪。这个方法看起来简单但真正磨过现场的人都知道中间涉及的坐标变换、像素当量标定、图像稳定性处理一环漏了都会让结果“飘”。这篇文章我就把这套方法从头到尾捋一遍包括数学原理、OpenCV检测、精度排查和批量应用希望能给正在调视觉定位的朋友一点参考。1. 两个mark点定位的核心逻辑1.1 为什么是两个mark点而不是一个或三个先回答一个最常被问到的问题确定一个点为什么偏偏需要两个mark点一个mark点不行吗一个mark点只能告诉我们原点在哪里但完全不知道工作区域有没有旋转。如果载具只是平移没有旋转那确实一个mark点在很多场合也够用但生产现场没有这么理想载具多次放置、皮带线停位波动、机械手夹取的角度偏差都会让产品在图像里既平移又旋转。这时候你只找到一个参考点目标点和参考点之间的像素坐标虽然固定但目标点的实际物理坐标会随着旋转不断变化所以必须第二个点来提供角度信息。那为什么不是三个或者更多两个点已经能确定一个刚体平面上的正交坐标系三个点属于冗余配置。多一个点可以用来做透视校正、畸变校正或者交叉验证但那是另一个层面的需求。普通二维平面定位两点是最少配置也是现场调试成本最低的组合。你少放一个mark点算法就没法确定旋转多放一个点算法要做最小二乘拟合代码复杂度和标定工作量都会上升。所以没有任何特殊原因的话两个mark点就是最优解。1.2 数学原理两个mark点如何建立坐标系把思路转换成数学语言非常清晰。假设相机图像里检测到两个mark点的中心坐标分别为 P1 (x1, y1) 和 P2 (x2, y2)目标点中心坐标为 T (xt, yt)。第一步确定基准方向向量。从 P1 指向 P2 的向量为V P2 - P1 (x2 - x1, y2 - y1)这个向量长度的物理含义不一定是实际毫米长度它是一个像素距离。我们先把向量归一化得到单位方向向量ex V / |V|其中 |V| 就是两个mark点在图像中的像素距离。第二步构造第二个单位方向向量。需要与 ex 垂直最简单的方式是把 ex 旋转90度得到一个垂直向量 ey。常用取法ey (-V_y / |V|, V_x / |V|)这样 ex 和 ey 就构成了一个以 P1 为原点、P1指向P2为X轴的正交坐标系。第三步计算目标点在这个坐标系下的坐标。先求目标点相对P1的向量W T - P1 (xt - x1, yt - y1)然后分别投影到两个单位方向向量上u W · exv W · ey这里的点积就是投影计算。最终得到的 (u, v) 就是目标点在这个mark点坐标系下的坐标。这个坐标有个特点只要目标点相对两个mark点的相对位置不变无论产品怎么旋转、平移u和v都不变。换句话说u和v才是真正稳定的描述量而不是图像里的原始像素坐标(xt, yt)。这里要注意一个细节ey 的方向取决于V的旋转方向。如果使用 (-V_y, V_x)坐标系是左手还是右手要看图像坐标系的定义。实际代码里判断标准只需要让 v 的符号符合你对目标点位置方向的预期。一旦方向确定后续所有计算都必须保持一致否则会得到镜像结果。1.3 像素坐标到物理坐标的换算前面得到的 (u, v) 单位仍然是像素因为 W 和 ex 都是像素单位点积出来的结果单位还是像素。实际应用中我们往往需要毫米甚至微米单位的物理坐标所以需要引入一个比例系数。比例系数通常叫像素当量单位是 mm/pixel。计算方法非常简单在载具上找一个已知物理距离的基准比如两个圆孔中心距是50mm在图像中检测出它们之间的像素距离是2500 pixel那么像素当量就是k 实际距离 / 像素距离 50 / 2500 0.02 mm/pixel得到k之后目标点在mark点坐标系下的物理坐标就是X_actual u * kY_actual v * k很多初学者会纠结相机安装角度和目标点平面不平行怎么办两个mark点建立的坐标系已经“吃掉”了图像坐标系和载具坐标系之间的旋转不需要额外标定相机和载具的角度关系。前提是相机光轴尽量垂直于工作平面并且镜头畸变不能太严重。如果畸变大就需要先做相机标定或者用第三个mark点做透视校正。后面在精度部分我会细说。这里还有一个常见的误区直接测量 P1 到 P2 的物理距离并用于像素当量。这是没问题的但要注意 P1 和 P2 的实际物理中心距必须测量准确。有些现场师傅用卡尺量mark点中心距时量的是外沿距离如果mark点本身是圆形焊盘外沿和中心的差就是半径会产生固定偏差。2. mark点检测与图像预处理2.1 什么样的mark点最容易被稳定识别mark点设计得好不好直接决定视觉定位能省多少事。实际项目中常见的mark点有三种圆形、十字形、方形/焊盘形。圆形用得多因为圆心定位算法成熟圆度特征容易筛选而且对光照方向变化不敏感。十字形适合半导体晶圆或高精度加工场景可以用亚像素边缘拟合交线来获得中心。方形焊盘也可以但边缘的直角容易受到毛刺或反光影响定位稳定性略差。在选择mark点时最重要的是对比度。mark点与周围背景的灰度差越大越好。比如深色防焊层上的裸铜圆点、白色陶瓷基板上的黑色圆点都是很好的组合。其次是mark点周围不能有太多干扰特征否则在图像处理时很容易误检。还要注意对称性mark点本身最好中心对称这样就算光照有渐变中心位置也不会被明显拉偏。有人会问是不是选两个离得越远的mark点越好从算法稳定性角度距离越远单位像素对应的旋转角度误差越小。比如两个mark点相距只有100像素检测误差是0.5像素那角度误差是0.286度如果相距1000像素同样的检测误差角度误差只有0.0286度。所以现场布置mark点时尽量把两个点放在载具对角线方向上拉开距离。但同时也要让它们保持在相机视野内有些大载具一次拍不到两个mark点就只能采用两段式定位或者相机拼接那就是另一个话题了。2.2 图像预处理滤波、二值化、形态学Mark点检测的经典流程我把每一步的作用都写清楚。第一步彩色图转灰度图。如果使用环形红光可以在HSV空间做颜色筛选但大多数场景直接用灰度图就够。如果背景颜色干扰严重比如红色背景上有黑色mark点也可以把红色通道提取出来因为黑色Mark点在红通道上同样会表现得很暗。第二步高斯滤波降噪。目的不是把图像变模糊而是抑制传感器噪声和表面纹理。核大小一般取3×3或5×5太大会让边缘变钝影响圆心定位精度。第三步二值化。常见做法是固定阈值或者大津法Otsu。如果光照稳定固定阈值最简单找一个让mark点是白色、背景是黑色的值就行。如果环境光有变化大津法更稳妥。也可以用自适应阈值但对小mark点容易产生噪声不推荐。第四步形态学处理。开运算先腐蚀后膨胀可以去掉比mark点小的噪声点闭运算先膨胀后腐蚀可以填充mark点内部的细小空洞。结构元尺寸一般取3到7个像素。这一步非常关键很多误检都是因为没有做形态学过滤。第五步轮廓提取和筛选。使用 findContours 提取所有白色区域的轮廓然后根据面积、圆度、外接矩形宽高比筛选出真正的mark点。圆度的计算方式有很多我习惯用轮廓面积和周长circularity 4 * π * area / (perimeter^2)标准圆的圆度接近1如果筛选范围设成0.85到1.0基本上能把大部分干扰排除掉。2.3 mark点中心定位方法解析得到mark点轮廓后有几种计算中心的方式选择不同方法对精度影响很大。第一种是轮廓矩法也叫重心法。直接计算轮廓的零阶矩和一阶矩得到轮廓重心。这个方法简单但当mark点轮廓不完整比如被遮挡或二值化断裂时重心会偏移。适合面积大、边缘清晰的mark点。第二种是最小二乘拟合圆。取轮廓上的点用最小二乘法拟合一个圆圆心就是mark点中心。这个方法对边缘局部噪声有一定抵抗能力因为所有点都参与拟合局部异常会被平均掉。精度比重心法高尤其在轮廓有毛刺但不影响圆形几何的时候。第三种是亚像素边缘拟合。在Canny边缘图或者灰度梯度方向上对边缘位置做亚像素插值再用这些亚像素点拟合圆或椭圆。这是目前工业视觉里最常用的高精度做法。但需要图像质量好、边缘锐利、光照均匀。如果镜头有畸变且未标定椭圆拟合反而可能引入系统误差。我自己的习惯是现场快速验证用轮廓矩正式项目上最少用最小二乘拟合圆。亚像素拟合留给精度要求特别高的场景。计算复杂度上轮廓矩最快拟合圆稍慢亚像素最慢但可控。3. 实操OpenCV实现两个mark点定位目标点3.1 最小的定位流程设计如果要用OpenCV搭一个能跑的定位程序我建议按照这个顺序设计采集一帧图像。预处理并检测两个mark点中心坐标 P1、P2。检测或者手动指定目标点坐标 T。计算两个mark点之间的距离、单位向量。将目标点坐标投影到mark点坐标系得到 u、v。用标定好的像素当量换算成物理坐标输出。这里有个细节容易被忽略目标点的定义方式。如果目标点是另一个圆形特征可以直接套用同一个检测函数。如果目标点是任意点比如人工点选就只需要一个坐标输入。但在实际项目中目标点通常也是视觉识别的特征比如焊盘中心、连接器脚中心。3.2 核心代码圆检测与坐标变换下面这段代码是完整可运行的核心逻辑前提是mark点是圆形图像背景简单。我用了轮廓提取加最小外接圆而不是霍夫圆检测因为霍夫圆对参数敏感实际项目里轮廓法更可控。import cv2 import numpy as np def detect_circles(image, min_area100, max_area5000): 检测二值化图像中的圆形区域返回每个圆的圆心坐标和半径 # 灰度化 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 高斯滤波 blurred cv2.GaussianBlur(gray, (5, 5), 0) # 大津法二值化mark点设为白色 _, binary cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # 形态学开运算去噪 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) cleaned cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 提取轮廓 contours, _ cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) circles [] for cnt in contours: area cv2.contourArea(cnt) if area min_area or area max_area: continue # 圆度筛选 perimeter cv2.arcLength(cnt, True) if perimeter 0: continue circularity 4 * np.pi * area / (perimeter * perimeter) if circularity 0.85: continue # 最小外接圆 (x, y), radius cv2.minEnclosingCircle(cnt) circles.append((x, y, radius)) return circles def compute_coordinate_in_mark_frame(p1, p2, t): 通过两个mark点计算目标点在mark坐标系下的坐标 p1, p2, t 都是 (x, y) 元组 vx p2[0] - p1[0] vy p2[1] - p1[1] length np.hypot(vx, vy) if length 0: raise ValueError(两个mark点重合无法建立坐标系) # 单位向量 ex ex np.array([vx / length, vy / length]) # 垂直向量 ey这里选择旋转90度 ey np.array([-vy / length, vx / length]) # 目标点相对 P1 的向量 w np.array([t[0] - p1[0], t[1] - p1[1]]) u float(np.dot(w, ex)) v float(np.dot(w, ey)) return u, v if __name__ __main__: # 读取图像 img cv2.imread(workpiece.png) circles detect_circles(img) # 实际项目中需要按照mark点的排序规则区分P1、P2 # 假定检测到的前两个圆分别是mark1和mark2 if len(circles) 2: raise RuntimeError(未检测到足够的mark点) # 这里你可以根据自己的逻辑排序比如按x坐标排序 p1 (circles[0][0], circles[0][1]) p2 (circles[1][0], circles[1][1]) # 目标点你可以手工点选也可以用同样函数检测 # 这里假设目标点是图像中第三个圆 if len(circles) 3: t (circles[2][0], circles[2][1]) else: # 手动点选或从其他算法获取 t (0, 0) # 替换为实际目标点 u, v compute_coordinate_in_mark_frame(p1, p2, t) # 像素当量单位 mm/pixel通过标定得到 scale 0.02 x_mm u * scale y_mm v * scale print(目标点在mark坐标系下的像素坐标: ({:.3f}, {:.3f}).format(u, v)) print(目标点物理坐标: ({:.3f} mm, {:.3f} mm).format(x_mm, y_mm))这个代码没有做目标点的手动点选界面原因是实际项目中目标点常常不是固定单一的你需要将它换成为自己产品特征服务的检测函数。但核心的基于两个mark点计算坐标的 function 是通用的直接复制就能用。3.3 现场调试时怎么确认计算结果靠谱代码跑通不等于现场能用。拿到相机画面后我建议你在图像上把检测到的两个mark点圆心用十字标记画出来再看一眼十字中心是不是在mark点正中央。这一步能直接暴露二值化阈值、光照等问题。然后可以做一个静态验证固定载具不动连续拍摄10次打印目标点物理坐标观察波动范围。如果每次结果波动超过你要求精度的两三倍那不是计算逻辑问题而是mark点检测不稳定。常见原因是反光、图像过曝或者mark点面积太小导致圆心定位抖动。动态验证就更接近真实使用手动平移和旋转载具重新拍照再计算目标点物理坐标。理论上只要目标点相对两个mark点没有移动计算结果应该保持不变。如果平移后结果稳定但旋转后偏差变大那问题多半出在像素当量或镜头畸变上。这一点我在下一节展开讲。4. 精度问题与排查技巧4.1 误差从哪里来硬件、图像、算法三层拆解先看硬件层。相机安装如果和运动平台不平行拍出来的图像就会有透视变形这种变形会导致不同位置的像素当量不一致。打个比方你标定时用图像中心的mark点对定了像素当量但目标点到了图像边缘由于透视同样的物理距离在图像里占比变了计算出来的mm数就偏了。要解决这个问题一是尽量把相机光轴调垂直二是用线性相机标定去畸变三是用多点透视变换校正。再看图像层。光照变化是最隐蔽的误差源。同一颗mark点早上和晚上的反光不同二值化出来的轮廓会有一两像素的偏移。用轮廓重心法时这种偏移会被放大。对于高要求项目请务必使用稳定的光源比如环形光源或同轴光源并把曝光时间固定不要用自动曝光。自动曝光一开图像亮度随时在变mark点检测的亚像素位置就会跟着漂。算法层误差主要来自圆拟合的不稳健。比如轮廓上有毛刺minEnclosingCircle会把这颗毛刺当成圆的边界中心就会偏。这种情况下我建议改用最小二乘圆拟合只使用边缘点参与计算毛刺影响会小很多。更进一步的亚像素拟合法也能用但对算法和图像要求更高。4.2 像素当量标定的两个常见错误第一个错误是用单个距离标定像素当量没有验证不同位置的当量是否一致。正确做法是找一个有已知间距的标定板在图像的左上、中心、右下分别测三组距离看看算出的像素当量差别有多大。如果差别超过1%说明镜头畸变或透视已经不可忽略。这时候要么做相机标定要么只用图像中心区域。第二个错误是量两个mark点实际距离时量得不准。很多mark点是圆形焊盘现场用卡尺量的是焊盘外径边缘到边缘的距离而不是圆心到圆心的距离。如果两个mark点半径相同边缘距离和中心距离其实相同所以这个方法可行但如果mark点不是圆形或者形状不对称就容易出错。标定像素当量更稳妥的方式是放一块标准块上面有精密加工、间距已知的圆孔或圆点用视觉检测到的圆心距离除以标称距离。4.3 常见问题速查表下面这个表是我根据实际项目经验整理的遇到问题可以对照着排查。现象可能原因处理办法目标点坐标整体偏了一个固定值标定像素当量时基准距离量错重新测量基准距离核实mm单位平移载具后坐标波动大光照不稳或Mark点反光固定光源、固定曝光改用圆拟合旋转载具后坐标偏差大镜头畸变或透视未校正做相机标定去畸变或用三点透视变换检测到的Mark点圆心不在中心二值化阈值不对边缘断裂调整阈值使用形态学闭运算偶发把干扰物识别成Mark点圆度筛选过宽或面积范围太宽缩小面积范围检查圆度阈值加ROI相机移动后坐标错乱相机与工作台相对位置变化重新标定像素当量甚至重做零点校准如果现场问题不在表里我的建议是逐步排除。先把图像检测结果可视化确认两个mark点中心每次都被正确找到然后单独验证纯平移、纯旋转两种情况下的坐标输出。哪一步不正常问题就锁定在哪一部分。5. 进阶旋转补偿与批量应用5.1 用两个mark点实现产品的自动角度跟随很多设备不仅需要知道目标点的位置还要知道目标点的角度或者要在产品旋转一个角度后仍然准确定位。两个mark点天然可以解决角度问题。设第一次拍到的两个mark点向量为 V0 P2 - P1当前拍到的为 V1 P2 - P1。两个向量之间的夹角就是产品的旋转角θ atan2(V1_y, V1_x) - atan2(V0_y, V0_x)得到θ之后可以把它发给运动控制卡进行角度补偿。需要注意atan2的范围是 -π 到 π实际角度可能超过这个范围要根据产品旋转累计量做处理否则会出现跳变。如果你的机构不能直接补偿角度还有一种思路通过坐标变换把目标点从mark坐标系映射到机械平台坐标系。比如已经知道初始位置下目标点在mark坐标系中的坐标是 (u0, v0)那么当前产品旋转θ、平移Δ后目标点的平台坐标就是X X_mark u0 * cos(θ) - v0 * sin(θ)Y Y_mark u0 * sin(θ) v0 * cos(θ)这里的 X_mark、Y_mark 是当前mark坐标系原点在平台坐标系中的位置。这个公式本质上就是二维刚体变换。你如果需要在项目中同时做位置和角度补偿强烈建议把这一套封装成一个函数。5.2 多目标点批量定位一次标定无限复用在实际产线上一个载具上往往有十几个待加工点。传统做法是每个点都去图像里检测一遍然后逐一求坐标这样既慢又容易受到单点检测噪声影响。更好的做法是先离线标定一次在标准姿态下检测两个mark点 P1、P2同时得到所有目标点 T1、T2、...、Tn 的图像坐标。然后对每个目标点都计算它在mark坐标系中的坐标 (ui, vi)。这个坐标是不变的。每次生产时只需要实时检测两个mark点建立当前mark坐标系然后直接用 (ui, vi) 反算所有目标点的当前物理坐标。反算公式也很简单。当前mark点 P1、P2 得到 ex、ey 和长度比例目标点当前坐标就是Ti P1 ui * ex vi * ey注意这里的 ui、vi 是像素单位如果要输出mm可以在算出ex、ey后再乘以像素当量。这个做法的好处是你只需要在第一次换型时把所有目标点标定一遍后续换产完全不需要重新逐个设点生产效率提升非常明显。5.3 什么时候需要第三个mark点两个mark点虽然通用但有两个致命前提一是不考虑透视畸变二是两个mark点和目标点在同一平面。如果载具发生了翘曲、相机光轴倾斜严重、或者需要在大视野边缘定位目标点两点法的精度就会打折。这时候可以考虑第三个mark点。三个mark点的作用是在两个mark点坐标系的基础上增加对角度的冗余校验或者直接求解一个平面透视变换矩阵Homography。透视变换可以纠正由相机倾斜带来的图像变形比单纯旋转缩放更准确。但代价是需要至少4个点才能解单应矩阵三个点能解的是仿射变换。如果只有三个mark点仿射变换刚好能解可以对平行四边形变形做校正但不能校正任意透视变形。我的建议是如果现场条件允许在载具对角线外侧布置3个mark点平时只用其中两个当某颗mark点被遮挡或脏污时算法可以自动切换到另一对组合。这样既不增加日常计算量又提高了系统的容错率属于花小钱办大事的典型方案。最后说一点个人实际操作中的体会。两个mark点定位这套方法技术门槛真的不高但越是基础的方法越考验细节的严谨程度。我在现场见过太多项目最后精度不过关问题不是算法不行而是mark点本身没设计好或者像素当量标定得过且过。搞定位这件事先把基准做好后面的所有坐标才站得住脚。如果能把mark点选型、光源稳定、像素当量验证这三件事做到位这套两点定位方案就能稳稳扛住绝大多数自动化生产场景。
返回列表