简介:这是一份基于 Vue 框架的视觉系统前端项目源码,主要面向前端开发初学者,也适合需要快速搭建可视化业务界面的开发者参考。项目采用当前流行的 Vue CLI 工程结构,内部按组件、视图、路由、状态管理、公共资源等模块进行划分,层次清晰,读者可以从中系统理解现代前端项目的组织方式。资源包共包含 15 个文件,以页面模板文件、逻辑脚本文件、项目配置描述文件等为主要类型,压缩包整体仅有 131KB,十分轻量,下载解压后即可在本地完成开发环境部署与运行调试。当前已有两千余人学习下载,对于希望快速上手 Vue 的同学来说,是一份性价比很高的参考资料。通过拆解这份项目代码,可以掌握 Vue CLI 脚手架的基础使用流程,学习组件的拆分与传参方式,理解路由与状态管理在实际项目中的搭配应用;同时结合视觉系统的业务特点,了解后台界面布局与数据展示的实现思路,对后续开发同类型管理系统或数据可视化项目具有较强的迁移参考价值。
1. 视觉系统:从工位到产线的第一公里
一条手机中框检测线,六个工位配了六个老师傅盯外观;我做项目时把其中三个工位换成一套视觉系统,节拍从 12 秒压到 4 秒,漏检率反而降了一个量级。这就是视觉系统最常见的落地形态:相机加镜头加光源,配一台工控机跑算法,在产线上替人做定位、测量、缺陷检测和识别。它解决的问题很直白——把人眼从重复劳动里解放出来,把“看起来差不多”变成“数值上可以判定”。适合正在评估视觉方案、准备自己攒一套验证机的自动化工程师和设备集成商。这套东西入门不难,散件买齐一天能搭起来,但真正决定能不能稳定量产的是参数配合和坑的经验。
2. 成像链路与检测原理:先弄清黑匣子里的四类任务
2.1 图像处理链路的四步:从灰度矩阵到合格/不合格
一副 1280×960 的 8 位灰度图,在内存里就是一个 1280×960 的矩阵,每个元素取值 0 到 255,0 是纯黑,255 是纯白。所谓视觉检测,本质上就是对这个矩阵做运算,最后输出一个判断结果。完整的链路可以拆成四步:采集、预处理、特征提取、判定。
采集是把光信号变成数字信号。光源照亮工件,镜头把像投到传感器上,传感器按像素点输出电平值,再由相机内部的 ADC 转成灰度数据。这里最关键的是曝光和增益:曝光决定进光时间,增益决定对微弱信号的放大倍数。预处理是降噪和增强,常见操作是高斯滤波、中值滤波、直方图均衡,目的是让后面的特征不被噪声干扰。特征提取是算东西——面积、周长、重心、边缘直线、圆半径、局部灰度差、模板相似度。判定则是把特征和预设阈值比较,输出 OK 或 NG。
很多新手拿到一幅图就急着调算法,实际上现场八成的图像问题在采集阶段就决定了。光源打不好,预处理再强也救不回来;反过来,光源稳定的时候,一个全局阈值常常就够用了。这也是工业视觉和日常手机拍照的最大区别:日常拍照追求好看,工业成像追求稳定,所有参数都要锁定。
2.2 定位、测量、缺陷检测、识别:四类任务各自的算法选型
视觉系统的任务类型可以归成四类,选型路径完全不同。
定位类任务的输出是坐标和角度,典型场景是机械手抓取、贴合对位。常规做法是模板匹配,把一张标准图切成小模板,在拍摄图像里滑窗搜索最高相似度;更快更稳的是几何匹配,只匹配边缘的直线和圆弧,不依赖灰度。这类任务传统方法已经很成熟,除非工件形变很大才考虑深度学习。
测量类任务输出是具体数值,典型场景是轴径、缝隙、平面度、同心度。现场常用亚像素边缘拟合直线或圆:先在边缘附近找灰度梯度最大的位置,再用直线拟合或最小二乘圆拟合出几何参数。测量最忌讳的是光源不稳定,因为哪怕曝光差一个等级,边缘位置都会偏移零点几个像素,换算成实际尺寸可能是 0.01 毫米以上的漂移。
缺陷检测类任务最杂,划痕、脏污、缺角、毛刺都算。传统方法用 Blob 分析加差分:拿一张无缺陷的基准图,和当前图做差分,亮斑就是疑点。这个方法对固定工件有效,但对反光和纹理变化很敏感。现在批量新项目更常走深度学习分割,用小样本训练一个分割模型,直接把缺陷区域标出来,泛化能力强不少。
识别类任务最直观:读二维码、OCR 字符、判定标签有无贴反。工业里常用的是直接读码器或 OCR 算子,也有不少人用轻量分类网络做品牌标识校验。这里要提醒一句:别一上来就上深度学习,传统算子的 q 稳定性和可解释性好,数据不够时调试成本也更低。
2.3 视觉系统的数据流:相机、触发、IPC 与 PLC 之间的配合
整个系统的数据流并不复杂。光电传感器检测到工件到位,发出一个电信号给相机触发拍照;相机把图像送到工控机的图像处理进程;算法算完结果,通过以太网、串口或 IO 卡把 OK/NG 信号给 PLC。理解这个流向后,再做集成设计就有了主线。
这里要特别关注节拍预算。工业项目通常不关心“每秒多少帧”,而是关心单件耗时。一般处理窗口是 100 毫秒到 1 秒不等,具体拆法:采集 10 毫秒,预处理 5 毫秒,模板匹配 20 毫秒,判定 5 毫秒,总共 40 毫秒。也就是说,传统视觉在绝大多数 200 毫秒节拍的产线上是绰绰有余的,卡脖子的地方往往在触发抖动和通信等待,不是算法本身。
数据流设计的另一个关键是触发方式。软件触发会让拍照时机不一致,尤其当传送带速度有波动时,工件位置会漂移。常见的做法是硬触发:外部传感器直接连接到相机的触发输入端子,保证每一帧都拍在相同的位置。后面专门有一章说触发和通信,这里先记住结论:能用硬触发就不要用软触发。
3. 硬件选型:相机、镜头、光源的参数边界与配合逻辑
3.1 相机选型:分辨率、帧率、接口之间的取舍
相机选型先定传感器类型。CMOS 传感器在主流工业项目里几乎全面替代了 CCD,原因是帧率高、成本低、噪声也控制得不错。其次是黑白和彩色:做尺寸测量和缺陷检测,优先黑白传感器,因为彩色相机的拜耳阵列会把灰度插值,等效分辨率打折而且算法更慢;只有需要判读颜色本身时才选彩色。
分辨率的确定靠换算而不是感觉。假设检测视野是 80 毫米宽,要求最小特征的可靠分辨率是 0.1 毫米,那么至少要有 80 / 0.1 = 800 像素。但要留裕量,一般按 3 倍以上取,也就是选用 1280×960 的相机比较稳妥。像素不是越高越好,分辨率高了帧率会降、曝光负担会大、CPU 处理时间会涨,最终影响节拍。
接口选择目前主流是 USB3 Vision 和 GigE Vision。USB3 带宽高,接单相机时性价比最好;GigE 线缆能到 100 米,适合分布式布局和多相机组网,但实际带宽比 USB3 低。Camera Link 是老一代高端接口,现在新项目很少碰。无论是哪种接口,都要注意相机 SDK 里的“帧率”是理论值,实际能跑到多少还要看带宽和 CPU 解码能力。可以拉一个简单试算:1280×960 灰度图一帧约 1.2MB,标称 60fps 意味着每秒 72MB 的数据;USB3 理论带宽约 400MB/s,实际可用七成,单相机够用,但多相机就不一定了。
3.2 镜头选型:焦距、靶面尺寸、畸变对视野的影响
镜头选型的核心是匹配感光靶面。相机传感器靶面多大,镜头成像圆就要覆盖多大,否则图像四角会发黑。常见的靶面规格有 1/3 英寸、1/2.5 英寸、2/3 英寸和 1 英寸,选镜头时一定要看它的最大支持靶面,宁可镜头靶面比相机大,不能反过来。
焦距计算是固定公式:焦距等于工作距离乘以传感器宽度除以视野宽度,单位保持一致。比如工作距离 300 毫米,视野 100 毫米宽,传感器宽度 8.8 毫米,那么焦距约等于 300×8.8 / 100 = 26.4 毫米,可以就近选 25 毫米或 30 毫米定焦镜头的规格。工业现场推荐用定焦而不是变焦,变焦结构有松动风险,定焦装好后用螺丝加螺纹胶固定,位置就不会漂。
畸变是测量项目的大坑。广角镜头边缘畸变大,做尺寸测量时边上的误差可能到几个像素。解决方法是选远心镜头:它在一定景深范围内放大倍率几乎不变,工件前后移动也不改变尺寸。远心镜头贵,但做精度测量时这笔钱别省。做简单位置检测则普通定焦加上标定补偿就够了,不必过度设计。
3.3 光源与三个必调参数:曝光、增益、光源亮度
打光在外行看来是玄学,在我看是有章法的选择题。环形光适合平面圆形工件,打出均匀的轮廓;条形光适合划痕检测,低角度照射让瑕疵产生明显阴影;同轴光适合反光平面,光线垂直落上去,高反光表面看起来均匀;穹顶光(圆顶光)适合球面或曲面,消除方向性阴影。先按工件形状定光源类型,再按表面材质定颜色和角度,这个顺序不要乱。
光源颜色不是随便选的。红色光打在深色或吸光表面,对比度更好。白光通用的光通量高,适合大多数场景。用单色光源配黑白相机,还能在镜头前加同色滤光片滤掉环境光,这个组合在产线上很实用。
装完光之后,真正要调的是三个参数,按顺序来:先固定光源亮度到接近饱和,再把曝光时间调到图像亮度合适,最后才动增益。曝光时间延长的副作用是运动模糊,工件在运动时曝光超过 2 毫秒就可能拖影;增益加大的副作用是噪点变多,边缘定位变抖。所以现场的第一原则:优先照顾曝光,增益能不开就不开。这个顺序能解决大半“图像忽亮忽暗”的投诉。
4. 用 OpenCV 跑通最小视觉检测:采集、预处理、判定三步走
4.1 图像采集:从打开相机到拿稳定帧
在写判定逻辑之前,先要保证能从相机拿到稳定可复现的图像。我这里用 OpenCV 的 VideoCapture 做示例,逻辑和厂商 SDK 一致,只不过工业相机通常走自家 SDK 拉流,拿到内存后再转成 ndarray。下面的代码是最小可运行版本。
import cv2 # 打开默认相机,0 表示第一个设备;工业相机用厂商 SDK 时替换为回调里的帧 cap = cv2.VideoCapture(0) # 设置分辨率,降低到需要的值即可,不要贪大 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) # 曝光值在多数驱动中是相对数值,范围 0-10000 左右 cap.set(cv2.CAP_PROP_EXPOSURE, 500) def grab_frame(): ok, frame = cap.read() if not ok: raise RuntimeError("拿不到帧,先检查连接和相机枚举") return frame # 验证性地抓一帧并转灰度 frame = grab_frame() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)这段代码背后有个容易被忽略的点:不同相机驱动对曝光属性的支持不一样。有些相机的曝光设置为 -1 表示自动曝光,手动模式下才接受具体数值。如果 set 之后再用 get 读出来的值不是刚刚设的,驱动往往只是做了裁剪或没生效,这时候得到的是自动曝光,图像亮度会随环境变化跳动。稳定帧的第一步就是把自动曝光、自动白平衡自动增益全部关掉,让参数完全锁定。
4.2 预处理与判定:找轮廓、算面积、给结果
拿到一张 8 位灰度图后,常用流程是高斯模糊降噪、阈值二值化、找轮廓、按几何特征判定。下面以检测圆形工件是否居中为例,完整走一遍。
import cv2 import numpy as np frame = grab_frame() # 沿用上面的采集函数 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 高斯模糊去噪:核大小必须为正奇数,数值越大图像越糊 gray = cv2.GaussianBlur(gray, (5, 5), 0) # 二值化:阈值 100 是把灰度 100 以下变白,以上的变黑 _, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV) # 找外轮廓,RETR_EXTERNAL 只取最外层,避免内部纹理干扰 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: print("NG: 没有找到工件") raise SystemExit(1) # 取面积最大的轮廓当成目标工件 part = max(contours, key=cv2.contourArea) # 关键特征:面积和最小外接圆的圆心与半径 area = cv2.contourArea(part) (px, py), radius = cv2.minEnclosingCircle(part) M = cv2.moments(part) cx = M["m10"] / M["m00"] cy = M["m01"] / M["m00"] # 判定规则:面积下限 + 重心偏差阈值 h, w = gray.shape offset = np.sqrt((cx - w / 2) ** 2 + (cy - h / 2) ** 2) is_ok = area > 50000 and offset < 20 print("OK" if is_ok else "NG", "area=%.0f offset=%.1f" % (area, offset))这里选轮廓几何而不是模板匹配,是因为圆形的重心和面积在几何上很稳定,不受平移旋转影响,代码也简单。阈值 100 不是拍脑袋,是基于灰度直方图里工件和背景的分界谷底取的;现场如果换光源,第一优先调的是这个值而不是滤波核大小。minEnclosingCircle 返回的是浮点精度。面积阈值 50000 是按当前分辨率下工件约占图像一半面积得出的,换视野要重新标定,不能直接照搬。
4.3 参数适配顺序:ROI、阈值、匹配分数怎么调才不翻车
新手拿到代码最容易做的三件事,一是全图找轮廓,二是阈值随意给,三是匹配分数定得过高。这三件事恰恰是翻车高发区。
全图找轮廓的风险在于把杂点、夹具边缘、传送带纹理全当成候选,干扰判别。正确做法是先画 ROI,只截取工件可能出现的区域,再用掩膜输出。这样做同时减少处理时间,对节拍有正面帮助。ROI 应在装好相机和光源后手动标定一次,存成配置文件,不要每次启动手写。
阈值调整有个经验规则:先把阈值拉得很低或很高,看图像里黑白区域是否产生明显跃变,那个跃变点附近就是最佳阈值。如果阈值的微小变化导致面积剧烈变化,说明前景和背景灰度太接近,此时要处理的不是阈值,而是光源。调完阈值再放松匹配分数,比如模板匹配分数从 0.95 降到 0.85,观察误判率是否下降;如果降到 0.7 还经常匹配失败,说明模板和现场成像差异太大,需要重新采模板而不是继续降分。
参数保存也值得重视,建议把所有相机参数和算法阈值写进一个 YAML 配置文件,每次启动加载,不要散落在代码里。否则换一台工控机或者换一个批次光源,整套参数就得重新玄学调试。
5. 视觉系统落地避坑:五个常见翻车现场与处理办法
5.1 频闪:图像里出现明暗横条纹
现象是图像上半幅偏亮、下半幅偏暗,或者出现规律的明暗横条纹,工件静止时条纹还会跳动。很多人以为相机坏了,换机无效。
原因大部分时候不在相机,而在光源供电。工厂车间照明和部分 LED 光源直接接交流市电,50Hz 交流电导致光强以 100Hz 的倍频波动。当相机曝光时间比光源波动周期短时,每次曝光落在波峰或波谷不同位置,亮度就跟着抖。另一个常见原因是光源控制器工作在频闪模式,而相机的触发和光源闪脉冲没有对齐,拍到一半亮一半暗的帧。
解决办法是把光源改成直流恒压驱动,或者把光源控制器切到常亮模式;如果必须用频闪,就得加触发延迟让光源先点亮、相机再曝光,这个延迟可以用示波器或直接试错找到稳定窗口。一句话总结:稳定的光源比高功率的光源重要得多,省电模式往往是频闪的元凶。
5.2 丢帧:标称 60fps 实际只有 15fps
现象是相机标称帧率 60fps,跑起来实际只有十几帧,画面明显卡顿,产线动起来后检测结果漏掉一部分。问题往往不在相机,而在传输和处理链路。
一个典型瓶颈是 USB3 带宽。一台 500 万像素相机在 8 位灰度下每帧约 5MB,标称 30fps 就需要 150MB/s 的持续带宽;而 USB3 实际稳定带宽只有理论值的七成左右,算下来已经到顶。再加上多台相机共用一条 USB 控制器,带宽被平分,每一路分到的更少。CPU 解码和显示预览也会抢时间,预览窗口越大越卡。
解决路径按性价比排序:第一优先把机器视觉处理的图像分辨率压到目标尺寸,或者设置 ROI 只输出工件区域;第二换硬触发,让相机只在需要的时候拍照,而不是一直连续输出;第三关掉实时预览窗口,处理后只显示结果;最后才考虑换更高带宽的采集方案或者分布式处理。特别注意,在调试阶段开着预览没关系,但交付时这段代码一定要去掉或做成开关,否则现场会因为预览占用资源而误判“视觉跑不动”。
5.3 过曝与高光:反光件成像不稳定
现象是金属件边缘出现一整片亮斑,工件的轮廓被融合进高光里,找不到边界;换成磨砂件又发暗。同一套参数换一个批次产品,判定结果跟着变。
原因在于反光表面的镜面反射:直射光线被表面按反射角打走,一部分方向进入镜头形成高光,另一部分方向完全黑掉。高光区域的灰度值顶到 255,边缘特征丢失。解决手段是偏振光,在光源前加偏振片、镜头前加检偏片,旋转检偏片把反射光滤掉,只保留漫反射信息,高光就能压下去。另一个手段是换同轴光,让光线垂直入射、垂直反射,对平面反光工件最友好。再配合降低曝光时间,一般就能稳定成像。
这里要强调:反光处理必须以实测为准,任何纸面经验都顶不过现场转一档角度看效果。同一块铝板,拉丝方向和镜面抛光的光路完全不一样,安装时留出调节余量,用螺纹和角度刻度固定好位置。
5.4 标定漂移:测量结果随时间悄悄偏掉
现象是一切刚装好时精度正常,一两周后测量值整体偏了 0.02 毫米,再过段时间偏更多,而且只朝一个方向偏。排查算法和参数都一样,找不到原因。
最常见的原因是机械结构松动。相机固定支架在产线震动下发生微小位移,或者镜头在重力作用下轻微下坠,都会让图像里的工件整体平移或缩放。另一个原因是温度:长时间生產后设备发热,镜头座和相机壳体热膨胀,焦距发生微变。工件离相机越远,这种漂移在图像中放大得越明显。
解决关键是做好物理防护和定期校验。相机支架优先用刚性结构的三角或龙门架,避免悬臂;镜头加锁紧环并用螺纹胶固定;现场放一块标准标定板,每周跑一次标定程序,记录误差趋势。如果发现误差超过容差的一半,就要安排复标定,不要等产线报警。经验是把标定参数和上次标定日期一起写进日志,方便判断是突发还是缓慢恶化。
5.5 环境光干扰:白天晚上两套结果
现象是同样的工件和程序,白班检测正常,夜班误判率上升;或者靠窗产线出太阳时 NG 变多。很多人查相机参数,查半天发现相机没变,变的是环境。
环境光每秒都在变,而视觉系统要的是稳定成像,所以解决思路是隔绝和滤除。物理上用遮光罩或防护罩把相机前后左右挡起来,只留光源的光路。装置实在无法加罩,就上滤光片:给光源配窄带滤光片,镜头前配同波段的带通滤光片,让系统只接收光源波长的光,环境光即便进来也被滤掉大半。红外光源配红外滤光片是常用组合,但要注意黑白相机对红外的响应,不是所有 CCD/CMOS 都有效。
如果环境光变化导致曝光变化,还可以启动相机的“固定曝光+增益下限锁定”,宁可用更小的曝光让图像偏暗,也不要让自动增益去补环境光。自动增益会把环境光的波动带进图像,对算法来说比偏暗更致命。
6. 从单站到产线:触发、通信与结果回传的落地技巧
6.1 外部触发:让相机只在工件到位时拍照
产线上让相机连续拍照是浪费带宽,也让每帧位置不固定。外部触发的做法是光电传感器或接近开关接相机的硬件触发输入,工件到达时给一个脉冲,相机立即抓拍。接线时注意触发电压要匹配,NPN/PNP 输出类型不要接反,否则信号一直高电平不触发。实际操作里我会先把传感器对着手指试几次,确认每次都能出图再上产线。
6.2 结果回传:把 OK/NG 交给 PLC 的三个姿势
最简单的回传方式是通过以太网 TCP 发一个字符串,视觉系统作为服务端监听 PLC 的请求,有结果就回OK或NG。稳定性和实时性要求高时,用数字 IO 输出,相机或工控机的 IO 卡直接输出高低电平给 PLC;需要在产线界面上看原因时,则走 Modbus TCP,把缺陷类型注册成寄存器地址,PLC 轮询读取。三种方式选型依据是节拍和数据量,环节简单就 IO,要追溯就网络。
6.3 上线前的一小时现场检验
新系统交接前,我习惯在产线做三件事:用标定板验证精度,连续跑一百件统计误判率,人为制造几个缺陷件确认真能拦下来。这一小时会暴露绝大部分问题。我现在做视觉系统的习惯是,交机前故意把曝光时间再调小一档,模拟现场最差的光线条件,看判定结果是否还稳;这个习惯替我省了后面不少运维电话。希望帮到你。
本文还有配套的精品资源,点击获取