1. 视觉采集模块:给机器装上“眼睛”

机器视觉检测,听起来高大上,但其实核心第一步,和我们人眼看东西的逻辑很像——你得先“看见”。这个“看见”的过程,就是视觉采集模块干的活儿。我干了这么多年,发现很多新手朋友一上来就琢磨复杂的算法,结果在第一步“看”上就栽了跟头,拍回来的图像要么模糊、要么变形、要么光线不对,后面算法再牛也白搭。所以,咱们得把基础打牢,这个模块就是整个系统的“眼睛”,眼睛不好使,大脑再聪明也分析不出个所以然。

选择相机和镜头,绝对不是随便买个贵的就行,它是个“量体裁衣”的技术活。我刚开始做项目时,就犯过这个错误,客户要检测手机外壳的微小划痕,我图省事用了手头一个百万像素的普通相机,结果拍出来的图像放大后,划痕边缘全是马赛克,根本没法精确测量。后来才明白,这里面的门道得根据你的“检测目标”来定。我总结了一个简单的决策链条,你可以跟着这个思路走,基本不会跑偏。

1.1 第一步:明确你要“看”什么

这听起来像废话,但恰恰是最容易被忽略的。你得坐下来,和工艺工程师或者质检员好好聊聊,把检测需求量化。我一般会问自己几个问题:

  • 目标物有多大? 是检测整个汽车车身,还是一颗米粒上的字符?这决定了你的视野范围(Field of View, FOV)。比如检测PCB板上的焊点,你的相机视野至少要能覆盖一整块板子。
  • 你需要多“清楚”? 也就是精度要求。比如,客户要求检测零件尺寸,公差是±0.1mm。那么你的相机分辨率必须保证图像上的一个像素代表的实际物理尺寸(像素精度)要小于0.1mm,通常我会取0.05mm甚至更小,留出余量。计算公式很简单:像素精度 = 视野范围 / 相机分辨率。假设视野是100mm宽,你要达到0.05mm的精度,那么相机在水平方向至少需要100 / 0.05 = 2000个像素。
  • 目标物是静止还是运动的? 如果是流水线上的产品,你就必须考虑运动模糊。这就需要选择全局快门(Global Shutter)相机,而不是常见的卷帘快门(Rolling Shutter)。全局快门能瞬间捕捉整个画面,避免物体移动时产生的果冻效应。我吃过亏,用卷帘快门相机拍快速移动的包装盒,上面的条码都拉成斜线了,根本扫不出来。
  • 环境光线怎么样? 是室内稳定的灯光,还是户外变化剧烈的自然光?这直接关系到你是否需要额外的光源,以及选择什么样的光源(环形光、条形光、同轴光等)。光源的目的是为了增强对比度,让需要检测的特征(如划痕、污点)从背景中“跳”出来。

把这些问题的答案记下来,它们就是你选择硬件的“需求清单”。

1.2 第二步:根据清单匹配硬件

有了需求清单,就可以开始挑选了。这里主要涉及三样东西:相机、镜头和光源。它们仨是个“铁三角”,必须匹配好。

相机选择: 除了上面提到的分辨率、快门类型,还要关注传感器尺寸和接口。传感器尺寸影响成像质量和景深。接口方面,现在工业上常用的是GigE(千兆网)和USB3.0。GigE线缆长,抗干扰好,适合设备分布较广的场景;USB3.0即插即用方便,但线长一般不超过5米。我个人的经验是,对于固定工位的检测,USB3.0更方便;对于产线上多个相机需要联动的,用GigE组网更靠谱。

镜头选择: 镜头的主要参数是焦距和光圈。焦距决定视野和工作距离(相机到物体的距离)。简单说,焦距越长,视野越小,工作距离通常越远,像个望远镜;焦距越短,视野越大,工作距离越近,像个广角镜。光圈(F值)控制进光量和景深。F值越小,光圈越大,进光越多,但景深越浅(清晰的范围小)。在需要整个物体上下表面都清晰的情况下,比如检测一个有一定厚度的零件,就需要用小光圈(大F值)来获得大景深。

这里有个实用技巧:很多镜头厂商官网都提供在线的“镜头计算器”,你输入传感器尺寸、工作距离、视野大小,它就能推荐合适的焦距。我每次选型都会用这个工具先筛一遍。

光源与打光方案: 这是让新手最头疼,但也最能体现经验的地方。打光的目的不是把物体照亮,而是塑造特征。举个例子,检测玻璃瓶表面的划痕。如果你从正面直接打光,划痕可能根本看不见。但如果你用暗场照明(光线从侧面以低角度照射),划痕因为散射光线,会在暗背景下呈现为明亮的线,一下子就凸显出来了。

我常用的几种打光思路:

  • 突出表面轮廓或字符: 用低角度的条形光,形成明暗对比。
  • 检测透明物体内部或表面划痕: 用背光(将物体放在光源和相机之间)或同轴光(光线通过分光镜与相机光路同轴)。
  • 消除反光: 用漫射圆顶光,创造均匀无影的照明环境。

实在拿不准的时候,有个土办法:买一个可调亮度的环形LED灯,在物体周围不同角度和距离试一下,用手机先拍着看效果,找到最能凸显缺陷的那个角度,然后再去匹配专业的工业光源。

2. 后台分析模块:让机器“看懂”图像

图像拍回来了,一堆像素数据摆在面前,机器怎么知道哪里是缺陷、哪里是合格品呢?这就是后台分析模块要干的活,相当于给机器装上一个“大脑”。这个过程,我习惯把它拆解成三个有逻辑顺序的步骤:标定、处理、提取。很多教程一上来就讲复杂的算法,容易把人绕晕。咱们按实际项目流程走,会清晰很多。

2.1 相机标定:建立真实世界的“尺子”

你可以把未标定的相机想象成一个近视眼还没配眼镜,它看到的世界是扭曲的、比例失调的。标定,就是给它配上一副精确的“眼镜”,并告诉它:“你看,现实世界里1毫米,在你的照片里应该是多少像素。” 这是所有定量测量(比如量长度、算面积)的基础,没有标定,后面的测量全是空中楼阁。

标定的核心是求出一组参数:内参(焦距、主点坐标、畸变系数等)和外参(相机在三维空间中的位置和朝向)。听着复杂,但用OpenCV这样的开源库来做,流程已经非常标准化了。我常用的方法是张正友标定法。

实操步骤简述:

  1. 制作标定板: 打印一张黑白棋盘格图案(比如9x6的内角点),贴在一个平整的硬板上。棋盘格方块的尺寸要精确测量。
  2. 多角度拍摄: 用你的相机,从不同角度、不同距离拍摄十几到二十几张标定板的照片。要确保标定板在画面中清晰,且角度变化丰富(有倾斜、有旋转)。
  3. 运行标定程序: 用OpenCV的 findChessboardCorners 函数自动检测每张图片的角点,然后用 calibrateCamera 函数计算相机参数。
  4. 评估与矫正: 程序会输出重投影误差,这个值越小越好(一般小于0.5像素就算不错了)。同时,你会得到相机的内参矩阵和畸变系数。之后,用 undistort 函数处理你拍的所有图像,就能得到矫正后的、无畸变的图像了。

我踩过的一个坑是标定板不够平整。有一次用普通A4纸打印的棋盘格,拍摄时中间有点拱起,导致标定误差很大。后来换成了亚克力板覆膜,问题就解决了。所以,标定板的平整度和精度,直接决定了你“尺子”的准头。

2.2 图像处理与特征提取:从像素中找“线索”

拿到矫正后的清晰图像,我们就要开始找我们关心的“东西”了。这一步的目标是把原始的、充满干扰的图片,变成只包含关键信息的“特征图”。我把它比作刑侦人员从杂乱无章的案发现场,提取出清晰的指纹、脚印这些关键证据。

这个过程通常是流水线式的,我结合一个检测金属表面划痕的例子来说:

  1. 预处理(去噪、增强对比度): 工业图像常有噪声。我会先用一个高斯滤波或者中值滤波来平滑图像,消除细小的噪声点。如果图像整体偏暗或对比度低,可以用直方图均衡化或者伽马变换来拉伸对比度,让划痕和背景的差异更明显。

    import cv2
    # 读取图像
    img = cv2.imread('surface.jpg', cv2.IMREAD_GRAYSCALE)
    # 高斯模糊去噪
    img_blur = cv2.GaussianBlur(img, (5, 5), 0)
    # 直方图均衡化增强对比
    img_eq = cv2.equalizeHist(img_blur)
    
  2. 图像分割(把目标“抠”出来): 我们需要把可能是划痕的区域和背景分开。对于划痕这种与背景有明显灰度差的,阈值分割是最快的方法。可以用大津法(OTSU)自动计算一个最佳阈值。

    # 使用OTSU自动阈值进行二值化
    _, img_bin = cv2.threshold(img_eq, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    # 此时 img_bin 是黑白二值图,白色(255)是前景(可能是划痕),黑色是背景
    
  3. 特征提取(量化“证据”): 现在二值图里可能有很多白色的连通区域,不全是划痕,可能是污渍或反光。我们需要提取每个区域的“特征”来筛选。划痕通常呈细长条形,而污渍可能更圆润。我们可以计算每个连通区域的特征:

    • 面积: 太小可能是噪声,直接过滤。
    • 长宽比: 划痕的长宽比通常很大(比如大于5:1)。
    • 最小外接矩形: 计算矩形的角度和长度。
    • 轮廓特征: 比如轮廓的周长、圆形度等。

    通过设定这些特征的阈值,我们就能把真正的划痕区域筛选出来。OpenCV的 findContourscontourAreaminAreaRect 等函数是完成这些工作的利器。

2.3 检测结果计算:从特征到数据

特征提取出来之后,我们得到的是图像坐标系下的数据(比如某个缺陷轮廓的像素坐标)。但对于质量控制,我们需要的是真实世界的数据(比如划痕实际长0.5毫米)。这时候,前面标定的“尺子”就派上用场了。

关键一步:坐标转换与测量。 假设我们在标定时得到,图像中1个像素对应实际尺寸是0.02毫米。现在我们测量出某个划痕在图像上的长度是50个像素,那么它的实际长度就是 50 * 0.02 = 1.0 毫米。

对于更复杂的测量,比如两个圆心之间的距离,我们需要先把两个圆心的像素坐标,通过相机内参和已知的物体平面高度(如果是在一个平面上测量),转换到世界坐标系下,然后再计算欧氏距离。如果只是同一平面上的测量,且相机镜头光轴与平面垂直,那么直接用像素距离乘以像素精度就够了。

结果判断(决策逻辑): 最后,我们把计算出的实际数据,和预设的工艺标准进行比较。比如:

  • 如果划痕长度 > 0.8毫米,则判定为不合格(NG)。
  • 如果零件直径在 10.0mm ± 0.1mm 范围内,则判定为合格(OK)。

这个判断逻辑可以非常简单,也可以非常复杂,比如引入统计过程控制(SPC),分析尺寸的趋势。但核心都是基于我们从图像中提取并计算出的那些量化数据。

3. 动作决策模块:让系统“动手执行”

检测结果出来了,是合格还是不合格,缺陷在哪里,数据都有了。但如果系统只是显示在屏幕上,那还得靠人工去搬走不良品,这就没完全发挥自动化的价值。动作决策模块,就是打通“感知”到“执行”的最后一公里,让机器视觉系统真正“活”起来,能对外部世界产生直接影响。

这个模块通常需要和PLC(可编程逻辑控制器)、机械臂、气缸、指示灯、报警器等执行机构联动。我经手的一个典型项目是锂电池极片缺陷检测分拣线,就很能说明问题。

3.1 通信与信号交互:和外部世界“对话”

视觉系统(通常是一台工控机或嵌入式主机)和分析软件(如我们自己写的OpenCV程序、Halcon、VisionPro等)不能自己直接去推气缸,它们需要通过某种方式告诉PLC:“第3号工位的产品是NG,请把它踢出去。” 常用的通信方式有:

  • 数字I/O(输入/输出)卡: 这是最直接、最快速的方式。工控机上插一块I/O卡,通过电缆连接到PLC的输入点。当视觉系统判定产品NG时,程序就控制I/O卡上某个特定的输出引脚从0V变成24V(或者反之),这个电压变化信号被PLC检测到,PLC就会执行预设的剔除程序(比如触发一个气缸)。这种方式响应快,延迟低,适合高速产线。我早期项目用得最多。
  • 工业以太网协议: 如Modbus TCP/IP、EtherNet/IP、PROFINET等。这种方式更“智能”,可以传输更丰富的数据,不仅仅是OK/NG信号,还可以把缺陷类型、位置坐标、尺寸数据都发送给PLC或机器人。比如,你不仅可以告诉机器人“这个零件坏了”,还可以告诉它“坏的地方在(X=100, Y=50)的位置,请用工具去修补一下”。这种方式柔性更高,是现在更主流的趋势。
  • 串口通信(RS232/485): 在一些老旧设备改造或简单应用中还能见到,通过发送特定的字符串指令来控制。

在实际编程中,你需要根据硬件连接方式,调用相应的库。比如用Python,你可能需要 pymodbus 库来处理Modbus通信,或者使用厂商提供的SDK来控制专用的I/O卡。

3.2 决策逻辑与流程控制:设计系统的“大脑”

有了通信渠道,就要设计什么时候、发送什么信号。这不仅仅是“NG就踢”那么简单,需要考虑生产节拍、设备状态和容错机制。

还是用那个锂电池极片检测的例子,我们的决策逻辑是这样的:

  1. 同步触发: 流水线上的传感器(光电传感器或编码器)检测到极片到达相机正下方,给视觉系统一个触发信号。
  2. 采集与分析: 视觉系统立刻拍照,并在极片移动到剔除工位之前完成所有图像处理和判断(这要求算法必须在几十到几百毫秒内完成)。
  3. 结果发送: 如果检测OK,视觉系统通过I/O卡发送一个“OK”脉冲信号给PLC。如果检测NG,则发送“NG”信号,同时通过以太网将缺陷图像和坐标数据发送给上位机数据库保存,用于后续追溯和分析。
  4. 执行动作: PLC收到“OK”信号,什么都不做,极片继续流向下一道工序。PLC收到“NG”信号,则会在极片到达剔除气缸正下方时,精确地控制气缸动作,将其吹入废料箱。
  5. 异常处理: 这是体现经验的地方。我们设计了几个异常处理分支:
    • 视觉系统超时未响应: 如果PLC发出触发信号后,超过预定时间没收到视觉系统的任何反馈,PLC会判定视觉系统故障,触发生产线报警并暂停,防止误动作。
    • 连续NG: 如果连续出现多个NG产品,系统会判断可能是来料批次问题或设备出现严重异常,自动提升报警等级,通知工程师介入。
    • 通信中断: 系统会定期进行“心跳”检测,如果发现与PLC的通信中断,会自动尝试重连并记录日志。

这个决策逻辑需要用代码清晰地实现,通常是一个状态机,确保每个环节都严丝合缝。我习惯用流程图先把整个逻辑画出来,和机械、电气工程师反复确认后,再开始编码,能避免很多后期的改动。

3.3 人机交互与数据追溯:让过程“看得见,管得住”

一个成熟的系统不能只是一个黑盒子。动作决策模块还需要提供一个界面,让操作人员能实时了解系统状态,并能追溯历史。

  • 实时监控界面(HMI): 我用PyQt或简单的Web前端做过不少这样的界面。界面上会实时显示相机拍摄的画面,用醒目的框(绿色OK,红色NG)标出检测结果。同时显示当前的生产计数、OK/NG数量、良率、设备状态(运行/停止/报警)等。操作员可以一目了然地掌握产线情况。
  • 数据记录与追溯: 每一个被检测的产品,无论OK还是NG,其图像(或至少NG图像)、检测结果、时间戳都会被保存到数据库或本地硬盘。当出现质量争议或需要分析缺陷趋势时,我们可以轻松地调出历史记录,查看当时的具体情况。我曾经就靠这个功能,帮客户定位到某批原材料的问题,避免了更大的损失。
  • 参数调整与维护: 在界面上,高级工程师可以有权限制性地修改一些检测参数阈值(比如划痕长度的合格标准),而无需修改代码。系统也应有日志功能,记录所有的报警、操作和关键事件,方便故障排查。

动作决策模块做得好,整个视觉检测系统就从“实验室demo”变成了可以7x24小时稳定运行的“工业士兵”。它不仅仅是发一个信号,更是集成了流程控制、异常处理、人机交互和数据管理的综合能力体。当你看到机械臂精准地抓取NG品放入指定筐内,或者听到剔除气缸那一声清脆的“啪嗒”声时,你会觉得前面所有关于像素和算法的折腾,都值了。

4. 实战案例:从零搭建一个零件尺寸检测站

光讲理论可能还有点虚,我结合一个最近做的简单但很典型的项目——金属垫片内径外径检测,把上面三个模块串起来,带你走一遍完整的流程。这个项目需求很简单:流水线上过来的金属垫片,需要快速检测其内径和外径是否在公差范围内(比如外径10±0.1mm,内径5±0.05mm),不合格的自动剔除。

4.1 项目规划与硬件选型

首先,我们明确需求:检测速度要求每分钟300个(即5个/秒),精度要求0.02mm,工作环境是车间,有轻微振动。根据这个需求清单:

  1. 相机: 精度0.02mm,假设垫片最大外径视野需要15mm(留边距),那么相机水平分辨率至少需要 15 / 0.02 = 750 像素。考虑到成本和对角线视野,我们选择一个130万像素(1280x1024)的全局快门工业相机,接口用GigE,方便布线。帧率在1280x1024分辨率下能达到30帧以上,远高于5帧的需求。
  2. 镜头: 计算焦距。传感器尺寸假设是1/2英寸(约6.4mm x 4.8mm),工作距离初步设定为200mm(方便安装和打光),视野需要15mm。通过在线镜头计算器,得出需要约25mm焦距的镜头。我们选择一个C口、焦距25mm、手动光圈和调焦的工业镜头。
  3. 光源: 金属垫片表面可能反光。为了清晰勾勒出内外圆的边缘,我们选择背光照明方式。将垫片放在一个漫射背光源上,相机从上方拍摄。这样垫片会呈现为一个黑色的剪影,边缘对比度极高,非常利于后续的图像处理。我们选择一个LED背光板,亮度可调。
  4. 其他: 一个简单的支架,一个光电传感器(用于触发拍照),一个剔除气缸(由PLC控制),一台工控机。

4.2 软件实现步骤详解

硬件搭好后,开始写代码。我用Python和OpenCV来演示核心步骤。

第一步:相机标定 我们用棋盘格标定法,得到相机的内参和畸变系数,并计算出在当前安装高度下,图像的像素精度。假设我们计算得到1像素 = 0.018mm。把这个值保存下来,作为后续测量的基准。

第二步:图像采集与预处理 光电传感器触发后,相机拍照。因为用了背光,我们直接得到一张高对比度的图像。

import cv2
import numpy as np

# 模拟从相机采集图像(实际中会用SDK抓取)
# 这里我们加载一张模拟的背光图像,垫片是黑色,背景是白色
img_original = cv2.imread('washer_backlight.jpg', cv2.IMREAD_GRAYSCALE)

# 1. 二值化:由于背光,直接阈值化就能得到很好的效果
_, img_binary = cv2.threshold(img_original, 127, 255, cv2.THRESH_BINARY_INV)
# 注意:垫片现在是白色(255),背景是黑色(0)

第三步:特征提取与测量 我们的目标是找到垫片的外圆和内圆。

# 2. 寻找轮廓
contours, _ = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 假设最大的轮廓是垫片外轮廓
if contours:
    # 找到面积最大的轮廓(垫片主体)
    main_contour = max(contours, key=cv2.contourArea)

    # 3. 拟合外圆
    (x_outer, y_outer), radius_outer = cv2.minEnclosingCircle(main_contour)
    diameter_outer_pixel = radius_outer * 2
    diameter_outer_mm = diameter_outer_pixel * pixel_per_mm  # pixel_per_mm 是标定得到的值,如 0.018

    # 4. 寻找内孔轮廓(需要找内部轮廓)
    # 我们使用 RETR_CCOMP 来获取内外层轮廓
    contours_ccomp, hierarchy = cv2.findContours(img_binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
    inner_contours = []
    for i, contour in enumerate(contours_ccomp):
        # 如果轮廓有父轮廓(即它是某个轮廓的内部孔洞),且其父轮廓是最大的那个轮廓(垫片主体)
        if hierarchy[0][i][3] == 0 and cv2.contourArea(contour) > 50: # 假设面积大于50像素的才是真正的孔
            inner_contours.append(contour)

    if inner_contours:
        # 通常只有一个内孔,取面积最大的内孔轮廓
        inner_contour = max(inner_contours, key=cv2.contourArea)
        (x_inner, y_inner), radius_inner = cv2.minEnclosingCircle(inner_contour)
        diameter_inner_pixel = radius_inner * 2
        diameter_inner_mm = diameter_inner_pixel * pixel_per_mm

    # 5. 判断与输出
    tolerance_outer = 0.1  # 外径公差±0.1mm
    tolerance_inner = 0.05 # 内径公差±0.05mm
    nominal_outer = 10.0
    nominal_inner = 5.0

    is_ok = True
    if abs(diameter_outer_mm - nominal_outer) > tolerance_outer:
        print(f"外径不合格: {diameter_outer_mm:.3f}mm")
        is_ok = False
    if abs(diameter_inner_mm - nominal_inner) > tolerance_inner:
        print(f"内径不合格: {diameter_inner_mm:.3f}mm")
        is_ok = False

    if is_ok:
        print("产品合格")
        # 通过I/O卡或网络发送OK信号给PLC
        send_signal_to_plc('OK')
    else:
        print("产品不合格")
        # 发送NG信号,并可附带具体是内径还是外径超差
        send_signal_to_plc('NG')
        # 同时可以保存NG图片和数据进行追溯
        save_ng_data(img_original, diameter_outer_mm, diameter_inner_mm)

    # 6. (可选)在图像上绘制结果,用于显示或调试
    img_display = cv2.cvtColor(img_original, cv2.COLOR_GRAY2BGR)
    cv2.circle(img_display, (int(x_outer), int(y_outer)), int(radius_outer), (0, 255, 0), 2) # 绿色外圆
    cv2.circle(img_display, (int(x_inner), int(y_inner)), int(radius_inner), (0, 0, 255), 2)   # 红色内圆
    cv2.putText(img_display, f'Outer: {diameter_outer_mm:.3f}mm', (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
    cv2.putText(img_display, f'Inner: {diameter_inner_mm:.3f}mm', (10, 60),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    cv2.imshow('Result', img_display)
    cv2.waitKey(0)

第四步:与执行机构联动 send_signal_to_plc 函数的具体实现,取决于你选择的通信方式。如果是I/O卡,可能就是设置一个引脚的高低电平;如果是Modbus TCP,就是向PLC的某个寄存器写入一个值。PLC收到这个信号后,会控制流水线在合适的位置将NG品剔除。

4.3 调试与优化中的经验之谈

项目上线从来不会一帆风顺。在这个垫片检测项目中,我们遇到了几个典型问题:

  1. 边缘毛刺导致圆拟合不准: 垫片冲压可能有毛刺,导致二值化后的轮廓不光滑,minEnclosingCircle拟合的圆偏大。解决方法是在二值化后,先对图像进行一次形态学闭运算(先膨胀再腐蚀),可以平滑边缘,填充小孔洞,让轮廓更接近真实的几何形状。
    kernel = np.ones((3,3), np.uint8)
    img_binary_smoothed = cv2.morphologyEx(img_binary, cv2.MORPH_CLOSE, kernel)
    # 然后用 img_binary_smoothed 去找轮廓
    
  2. 多个零件粘连或部分进入视野: 流水线上偶尔可能两个垫片叠在一起,或者一个垫片只有一半进入视野。我们的程序会找到多个轮廓。这时需要加入筛选逻辑:比如只处理面积在一定范围内的轮廓;或者通过轮廓的圆形度4*pi*面积/周长^2,越接近1越圆)来过滤掉非圆的物体。
  3. 光照波动: 虽然背光相对稳定,但电网电压波动或LED老化可能导致亮度轻微变化,影响二值化阈值。我们可以将固定阈值改为自适应阈值cv2.adaptiveThreshold),或者定期(如每检测1000个产品)自动计算一次阈值(如使用OTSU算法)。
  4. 性能优化: 为了达到5个/秒的速度,我们需要优化代码。例如,将ROI(感兴趣区域)固定在我们知道垫片会出现的大致位置,减少处理面积;使用C++重写核心循环(如果Python确实达不到要求);确保相机触发和图像传输的延迟最小。

通过这个完整的案例,你应该能感受到,一个机器视觉检测项目是如何从需求出发,一步步完成硬件选型、算法开发、系统集成,并最终解决实际生产问题的。每个环节都有坑,但也都有对应的解决方法。关键是多动手,多测试,在真实环境中去迭代和优化你的系统。

更多推荐