基于Python与OpenCV的工业视觉检测系统:从架构设计到现场部署实战
1. 项目概述从概念到落地的工业视觉检测最近几年和不少做自动化设备、产线集成的朋友聊天发现一个共同的痛点产品外观检测。人工目检效率低、标准不一、容易疲劳而市面上的标准视觉检测软件要么贵得离谱要么灵活性不够很难适配千变万化的现场工况。于是自己动手用 Python 和 OpenCV 搭建一套工业级的机器视觉检测系统就成了一个极具性价比和实用价值的方案。这不仅仅是调用几个 API 那么简单它涉及到从图像采集、预处理、算法设计到系统集成、稳定运行的完整链条。这套系统的核心目标是在保证高精度和高稳定性的前提下实现缺陷的自动识别、分类和定位最终将结果反馈给 PLC 或机械臂完成剔除、分拣等动作。它适合谁呢如果你是自动化工程师、设备开发人员或者是对机器视觉感兴趣、希望将算法能力应用到实际产线的开发者那么这个从零到一的构建过程会给你带来很多启发。我们不仅要讲“怎么做”更要深挖“为什么这么做”以及在实际工厂环境中那些容易踩坑的细节。2. 系统整体架构与核心设计思路一套可靠的工业视觉系统绝不能是几个脚本的简单堆砌。它需要像一台精密的仪器每个环节都经过深思熟虑的设计。我的整体架构可以概括为“三层两线”。2.1 硬件层眼睛、大脑与手脚的协同硬件是系统的物理基础选型直接决定了系统的上限。工业相机与镜头这是系统的“眼睛”。千万不能直接用 USB 网络摄像头。工业相机需要稳定、抗干扰。我通常选择千兆网GigE接口的工业相机因为它传输距离远、抗干扰能力强、协议标准。分辨率根据检测精度来定比如检测 0.1mm 的瑕疵视野FOV是 100mm那么相机分辨率至少需要 (100/0.1) 1000 像素选择 1280x1024 的相机是稳妥的。镜头选型涉及焦距、视野和工作距离的计算。一个简单的公式焦距 f (工作距离 * 传感器尺寸) / 视野。例如工作距离 500mm视野 100mm使用 1/1.8” 的传感器靶面尺寸约 7.2mm x 5.4mm那么所需焦距 f ≈ (500 * 7.2) / 100 ≈ 36mm。我会选择一款 35mm 的定焦工业镜头。光源与打光方案这是视觉检测成败的“七分光”。打光不好再好的算法也白搭。核心原则是“凸显特征抑制干扰”。对于表面划伤、凹坑等缺陷常用低角度环形光或条形光形成明暗对比对于字符、印刷品检测常用同轴光避免反光。我习惯准备一个可调亮度的光源控制器在现场反复调试找到最能突出缺陷、同时背景均一的光照条件。这是最耗时间但也最值得投入的环节。工控机与触发机制工控机是“大脑”需要无风扇设计、宽温耐受、多网口和串口。我选择搭载 Intel i5 或 i7 处理器的型号内存 16GB 起步并配备固态硬盘。触发是协同的“神经”。通常由光电传感器或 PLC 给出一个触发信号通常是 24V 脉冲给相机的 I/O 口相机收到信号后立即拍照同时通过网线将触发事件通知工控机上的软件。这种硬触发方式能确保拍照时机与产品位置严格同步是高速流水线检测的基石。2.2 软件层稳定、高效与可维护的代码架构软件层采用模块化设计核心是解耦和容错。图像采集模块不使用相机厂商自带的臃肿 SDK而是采用Harvesters或pypylon库通过 GenICam 标准协议直接控制 GigE 相机。这样代码通用性强切换相机品牌影响最小。这个模块的核心任务是建立相机连接、配置参数曝光、增益、触发模式、接收触发信号、抓取图像并将图像放入一个线程安全的队列中。import threading from queue import Queue from harvesters.core import Harvester class CameraAcquisition: def __init__(self, camera_cti_file): self.harvester Harvester() self.harvester.add_cti_file(camera_cti_file) # 加载相机驱动文件 self.device None self.image_queue Queue(maxsize10) # 设置缓冲队列防止堆积 self.acquisition_thread None self._stop_event threading.Event() def start_acquisition(self): self.device self.harvester.create_image_acquirer(0) self.device.remote_device.node_map.AcquisitionMode.value Continuous self.device.remote_device.node_map.TriggerMode.value On self.device.remote_device.node_map.TriggerSource.value Line1 # 硬件触发源 self.device.start_image_acquisition() self._stop_event.clear() self.acquisition_thread threading.Thread(targetself._grab_loop) self.acquisition_thread.start() def _grab_loop(self): while not self._stop_event.is_set(): try: with self.device.fetch_buffer() as buffer: if buffer: # 将图像数据转换为numpy数组 image_data buffer.payload.components[0].data image image_data.reshape(buffer.payload.components[0].height, buffer.payload.components[0].width) self.image_queue.put(image.copy()) # 深拷贝避免内存覆盖 except Exception as e: # 记录日志不退出循环 log_error(f抓图失败: {e}) time.sleep(0.01)图像处理与算法模块这是系统的“心脏”。我将其设计为一个可插拔的管道Pipeline。每个检测项如尺寸测量、缺陷检测、OCR都是一个独立的处理单元Processor。主程序从队列中取出图像依次流经需要的处理器。这样做的好处是新增或修改检测项时只需增删或替换对应的处理器不影响整体框架。通信与控制系统这是系统的“手脚”。检测结果需要传递给外部世界。与 PLC 通信最可靠的方式是通过工业以太网协议如Modbus TCP。Python 可以使用pymodbus库。我们会在工控机上模拟一个 Modbus 从站Server将检测结果OK/NG、缺陷类型代码、坐标写入到特定的保持寄存器Holding Register中。PLC 作为主站Client定期读取这些寄存器从而获知结果并控制剔除气缸动作。from pymodbus.server import StartTcpServer from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.datastore import ModbusSequentialDataBlock def run_modbus_server(): # 初始化数据存储块地址0开始用于存储10个寄存器的值 store ModbusSlaveContext( hrModbusSequentialDataBlock(0, [0]*10) # 保持寄存器初始为0 ) context ModbusServerContext(slavesstore, singleTrue) # 启动Modbus TCP服务器端口502是标准端口 StartTcpServer(contextcontext, address(0.0.0.0, 502))人机交互HMI与数据管理模块这是系统的“面孔”和“记忆”。我用PyQt5或DearPyGui开发一个简单的桌面界面用于实时显示图像、检测结果、统计报表并提供参数调整入口。所有检测结果图像、结果、时间戳都会存入SQLite或MySQL数据库便于后期追溯和质量分析。2.3 核心设计思路鲁棒性高于一切工业现场环境恶劣振动、灰尘、温度变化、电磁干扰。因此系统的设计必须围绕“鲁棒性”展开。异常处理无处不在每个可能失败的环节网络断开、相机掉线、算法异常都必须有try...except包裹并记录到日志文件系统应能降级运行或安全重启。心跳与看门狗主程序会定期向一个“看门狗”线程或外部硬件发送“心跳”信号。如果超过设定时间未收到心跳则认为主程序已死锁看门狗将自动重启整个应用。参数持久化与版本管理所有相机参数、算法阈值、通信配置都保存在 JSON 或 YAML 配置文件中。修改参数无需改动代码。对配置文件进行版本管理可以快速回滚到稳定状态。资源管理严格管理内存和线程。图像处理完成后立即释放大内存对象使用线程池控制并发数量避免线程爆炸。3. 核心视觉算法拆解与实战要点有了稳固的架构接下来就是填充核心的视觉算法。工业检测算法千变万化但归根结底可以归纳为几类经典问题。3.1 图像预处理化腐朽为神奇的第一步原始图像通常包含噪声、光照不均、背景复杂等问题。预处理的目标是“净化”图像让目标特征更容易被后续算法捕捉。灰度化与滤波彩色图像通常先转为灰度图以减少计算量。对于椒盐噪声使用中值滤波cv2.medianBlur效果最好对于高斯噪声使用高斯滤波cv2.GaussianBlur。滤波核大小是关键参数太大会模糊边缘太小去噪不彻底。我的经验是从 3x3 或 5x5 开始尝试根据效果微调。光照校正这是应对现场光照波动的利器。常用的方法是“顶帽变换”Top-hat和“底帽变换”Bottom-hat结合。顶帽变换原图 - 开运算能提取比背景亮的细节如凸起、划痕底帽变换闭运算 - 原图能提取比背景暗的细节如凹坑、脏污。将两者结合可以有效抵消不均匀背景。import cv2 import numpy as np def uneven_light_compensate(gray_img, kernel_size31): 使用顶帽和底帽变换进行光照补偿 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size)) # 顶帽变换提取亮区域特征 tophat cv2.morphologyEx(gray_img, cv2.MORPH_TOPHAT, kernel) # 底帽变换提取暗区域特征 blackhat cv2.morphologyEx(gray_img, cv2.MORPH_BLACKHAT, kernel) # 原图加上顶帽减去底帽可以增强对比并平滑背景 compensated cv2.add(gray_img, tophat) compensated cv2.subtract(compensated, blackhat) # 最后进行直方图均衡化进一步拉伸对比度 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8,8)) result clahe.apply(compensated) return result二值化将灰度图转为黑白图是分割目标与背景的关键步骤。全局阈值cv2.threshold在背景对比度高时有效。但更通用的是自适应阈值cv2.adaptiveThreshold它为图像中每个小区域计算独立的阈值能很好地处理光照渐变。我常用高斯加权平均的自适应方法ADAPTIVE_THRESH_GAUSSIAN_C。注意预处理的所有参数滤波核大小、形态学核大小、阈值参数都必须做成可配置项并在现场用实际样品进行精细调试。一个常见的坑是在办公室灯光下调好的参数到了车间完全失效。因此必须采集现场不同时段、不同批次产品的图像来验证参数的鲁棒性。3.2 特征提取与模板匹配定位与比对的基础在检测前我们通常需要知道产品在哪里或者与标准品相比对。轮廓查找与筛选通过cv2.findContours可以找到二值图像中所有的轮廓。但我们需要的是目标产品的轮廓。这里就需要根据先验知识进行筛选通过面积cv2.contourArea、外接矩形长宽比、轮廓近似多边形边数等特征过滤掉噪声和无关区域。特征点匹配ORB/SIFT当产品可能发生旋转、缩放时轮廓匹配可能失效。这时需要特征点算法。ORB 是免费且速度快的选择SIFT/SUFT 精度更高但受专利保护SIFT 专利已过期。其流程是1) 在标准模板图上提取特征点和描述符2) 在实时图中提取特征点3) 使用 FLANN 或 BFMatcher 进行匹配4) 使用 RANSAC 算法计算单应性矩阵Homography从而得到实时图与模板图之间的透视变换关系。def find_homography_by_orb(template_img, query_img): 使用ORB特征寻找单应性矩阵 orb cv2.ORB_create(nfeatures1000) kp1, des1 orb.detectAndCompute(template_img, None) kp2, des2 orb.detectAndCompute(query_img, None) if des1 is None or des2 is None: return None # 使用BFMatcher进行匹配 bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue) matches bf.match(des1, des2) matches sorted(matches, keylambda x: x.distance) # 提取匹配点对 src_pts np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1,1,2) dst_pts np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1,1,2) # 使用RANSAC计算单应性矩阵 H, mask cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) return H模板匹配在定位后对于固定的特征区域如 Logo、焊点可以使用归一化相关系数匹配法cv2.TM_CCOEFF_NORMED进行比对。它会返回一个相关度分数通过设定阈值来判断该特征是否存在或位置是否正确。实操心得特征点匹配非常消耗计算资源。在实际部署时不要对整张高分辨率图片进行匹配。可以先通过粗略定位如机械夹具保证产品大致位置或 ROI感兴趣区域缩小搜索范围大幅提升速度。同时将标准模板的特征描述符des1预先计算并序列化到文件每次启动时加载避免重复计算。3.3 缺陷检测算法选型实战这是最核心的部分。针对不同的缺陷类型需要“对症下药”。1. 划伤、裂纹等线状缺陷这类缺陷通常表现为细长的、与背景有对比度的区域。方法使用方向梯度检测。可以先使用 Sobel 或 Scharr 算子计算 X 和 Y 方向的梯度然后计算梯度幅值。线状缺陷区域的梯度幅值会显著高于平滑区域。再通过阈值化和形态学闭运算连接断点将其提取出来。关键参数梯度算子的核大小、幅值阈值、形态学核的长度和宽度应匹配划伤的预期长宽比。2. 凹坑、凸起、脏污等区域缺陷这类缺陷表现为局部区域的灰度或纹理与周围正常区域不同。方法一背景差分。适用于背景相对稳定、产品表面纹理均一的场景。先获取一张“完美”的标准图像作为背景实时图与背景图做绝对值差分再阈值化。难点在于如何获取和更新“标准背景”因为光源会衰减相机可能微动。我常用的是动态背景更新算法将一段时间内所有 OK 品的图像进行移动平均缓慢更新背景模型。方法二纹理分析更鲁棒。使用局部二值模式LBP或灰度共生矩阵GLCM计算局部纹理特征。正常区域的纹理特征是均匀的而有缺陷的区域纹理特征会发生突变。通过计算整张图纹理特征的统计量如均值、方差或滑动窗口计算局部纹理与全局纹理的差异来定位缺陷。方法三深度学习分割。当传统方法难以定义复杂缺陷时这是终极武器。使用 U-Net 等网络用标注好的缺陷图像进行训练让模型直接学习缺陷的像素级特征。优点是精度高、适应性强缺点是需要大量标注数据、训练成本高、推理需要 GPU 加速。3. 尺寸测量需要高精度的长度、角度、圆直径等测量。方法亚像素边缘检测是关键。cv2.Canny检测出的边缘是像素级的。使用cv2.cornerSubPix对于角点或通过灰度矩心法可以将边缘定位精度提高到亚像素级别如 0.1 像素。之后在亚像素边缘点上进行直线拟合cv2.fitLine或圆拟合cv2.minEnclosingCircle结合最小二乘法得到精确的几何参数。标定像素距离到实际物理距离的转换必须通过标定来完成。使用高精度的标定板如棋盘格拍摄一张图像通过cv2.findChessboardCorners和cv2.calibrateCamera可以得到相机的内参和畸变系数。更简单的是如果镜头是垂直拍摄的无透视畸变只需拍摄一个已知实际尺寸的物体如量块计算“像素/毫米”的比例系数即可。但要注意这个系数在视野中心与边缘可能因镜头畸变而不同所以对于大视野高精度测量必须进行完整的相机标定和畸变校正。4. 系统集成、调试与性能优化全流程算法模块准备好后需要将它们集成到主框架中并在真实的工控环境下进行调试和优化。4.1 主程序流程与控制逻辑实现主程序是一个多线程的调度中心。我通常设计一个VisionSystem主类来管理所有模块的生命周期和数据流。import threading import time from queue import Queue, Empty import logging class VisionSystem: def __init__(self, config_path): self.config self._load_config(config_path) self.camera CameraAcquisition(self.config[camera][cti_file]) self.processor_pipeline self._init_pipeline(self.config[pipeline]) self.result_queue Queue() # 用于传递最终结果 self.modbus_client ModbusClient(self.config[plc][ip]) self.ui_updater None # UI更新回调函数 self.running False self.processing_thread threading.Thread(targetself._processing_loop) def _processing_loop(self): 核心处理循环 while self.running: try: # 1. 从相机队列获取图像超时设置防止阻塞 raw_image self.camera.image_queue.get(timeout1.0) start_time time.time() # 2. 流水线处理 result {image: raw_image, ok: True, defects: []} current_image raw_image.copy() for processor in self.processor_pipeline: current_image, result processor.process(current_image, result) if not result[ok]: break # 任一环节失败可提前终止 # 3. 记录耗时 result[process_time_ms] (time.time() - start_time) * 1000 # 4. 结果分发 self.result_queue.put(result) if self.ui_updater: self.ui_updater(result) # 更新UI self._send_to_plc(result) # 发送给PLC except Empty: continue # 队列为空继续循环 except Exception as e: logging.error(f处理循环异常: {e}) # 记录错误图像便于分析 self._save_error_image(raw_image) def _send_to_plc(self, result): 将结果转换为Modbus寄存器值并发送 if result[ok]: reg_values [1] # 寄存器0写入1表示OK else: reg_values [2] # 寄存器0写入2表示NG # 可以将缺陷类型编码后写入后续寄存器 for i, defect in enumerate(result[defects][:5]): # 最多传5个缺陷 reg_values.append(self._encode_defect(defect)) try: self.modbus_client.write_registers(0, reg_values) except Exception as e: logging.error(fPLC通信失败: {e}) # 触发报警或重试机制 def start(self): self.running True self.camera.start_acquisition() self.processing_thread.start() logging.info(视觉系统启动) def stop(self): self.running False self.processing_thread.join() self.camera.stop_acquisition() logging.info(视觉系统停止)4.2 现场调试从实验室到产线的鸿沟实验室跑通的系统到了现场大概率会“趴窝”。现场调试是真正的挑战。1. 环境隔离与稳定性测试振动将工控机、相机、光源牢固安装在机械结构上使用减震垫。检查所有线缆接头是否锁紧。电磁干扰动力线380V与信号线网线、IO线必须分开走线槽最好垂直交叉。使用带屏蔽层的网线和 IO 线并将屏蔽层单点接地。温度长时间运行后相机传感器和光源 LED 温度升高可能导致图像亮度、焦点漂移。需要做至少 8 小时的持续运行测试观察关键参数如平均灰度值、清晰度是否稳定。2. 光源与镜头微调光源在现场光照环境下重新微调光源亮度和角度。有时需要加装遮光罩防止环境光干扰。镜头使用软件触发相机拍摄然后手动微调镜头焦距直到图像最清晰。对于远心镜头还需要调节光圈在景深和进光量之间取得平衡。3. 参数鲁棒性验证采集至少 100-200 个连续生产的 OK 品和已知的 NG 品图像。用这些图像组成测试集运行你的检测算法。统计误检率将 OK 品判为 NG和漏检率将 NG 品判为 OK。工业上通常要求误检率低于 0.1%漏检率根据缺陷严重程度而定关键缺陷要求为 0%。调整算法阈值在误检率和漏检率之间寻找最佳平衡点。通常采用ROC 曲线来辅助决策。4.3 性能优化让系统飞起来工业节拍要求高优化至关重要。1. 算法层面ROI 限定永远不要处理整张图。在图像中精确划定需要检测的区域ROI只对 ROI 进行处理能立即减少 50%-90% 的计算量。图像降采样对于定位、粗略匹配等不需要最高精度的步骤可以先将图像缩小到原来的 1/2 或 1/4 进行处理定位到目标后再在原图对应区域进行精细分析。选择高效算法在满足精度要求下选择速度更快的算法。例如中值滤波比高斯滤波慢可以考虑用均值滤波加阈值调整来替代。轮廓查找时使用cv2.RETR_EXTERNAL只找最外层轮廓比找所有轮廓快。利用硬件加速OpenCV 很多函数默认使用了 Intel IPP 或 OpenCL 加速。确保你的 OpenCV 是编译了这些优化的版本。对于循环密集型操作考虑使用 Numba 进行 JIT 编译加速。2. 系统层面多线程与流水线如主程序所示图像采集、处理、通信放在不同线程利用多核 CPU。甚至可以进一步将处理流水线的不同阶段如预处理、特征提取、分类分配到不同线程形成生产者-消费者模型。内存复用避免在循环中不断创建和销毁大数组如图像。预先分配好内存块循环中使用np.copyto(dst, src)或直接修改原数组。减少磁盘 I/O调试阶段的图像保存、日志写入非常耗时的。正式运行时除非是 NG 品或系统报警否则不要保存图像。日志级别设置为 WARNING 或 ERROR减少信息量。5. 常见工业现场问题与排查实录即使系统设计得再完美现场也会出现各种意想不到的问题。下面是我踩过的一些坑和解决方法。5.1 图像质量问题问题现象可能原因排查步骤与解决方案图像模糊1. 镜头焦距未调准。2. 镜头或保护镜有油污、灰尘。3. 曝光时间过长产品移动产生拖影。1. 锁定相机软件触发手动精细调节镜头焦距环。2. 用无尘布和酒精清洁镜头表面。3. 缩短曝光时间增加光源亮度或相机增益补偿。对于运动物体曝光时间需满足曝光时间(秒) ≤ 允许模糊像素 / (运动速度(像素/秒))。图像亮度不稳定闪烁1. 环境光干扰如日光灯频闪。2. 光源驱动器不稳定或与相机曝光不同步。3. 使用自动曝光或自动增益模式。1. 加装遮光罩或改用直流供电的 LED 光源。2. 检查光源电源确保功率充足稳定。对于高频拍摄使用相机触发信号同时触发光源频闪模式使光源只在拍照瞬间亮起。3.工业检测中务必关闭所有自动模式自动曝光、自动白平衡、自动增益全部采用固定参数以保证一致性。图像出现条纹或噪点增多1. 电磁干扰。2. 相机传感器温度过高。3. 网线质量差或过长数据传输丢包。1. 重新布线做好屏蔽和接地。2. 改善相机散热或选择散热更好的型号。3. 使用标准的 CAT6 及以上网线长度不超过 80 米。在相机配置软件中开启GVSP Packet Resend功能。5.2 通信与触发问题问题PLC 收不到检测结果或结果错乱。排查Ping 测试在工控机上pingPLC 的 IP 地址检查网络物理连通性。端口监听在工控机上使用netstat -an | findstr :502命令查看 502 端口是否已被你的 Modbus 服务器程序监听。防火墙关闭工控机 Windows 防火墙或添加入站规则允许 502 端口。地址映射确认 Modbus 寄存器地址映射关系。PLC 程序里读取的寄存器地址如 40001对应的是服务器上下文中的哪个偏移量通常是 0。一个常见的混淆是 Modbus 地址的“基地址”问题有的库从 0 开始有的从 1 开始需要对齐。数据类型确认写入寄存器的数值是否在 0-6553516位无符号整数范围内。如果需要传递浮点数或字符串需要拆分成多个寄存器并按约定好的格式解析。问题相机拍照时机不对有时漏拍有时连拍。排查触发信号测量用示波器测量连接到相机 Trigger 输入口的信号。确认是干净的 24V 脉冲上升沿陡峭脉宽合适参考相机手册通常 1μs频率不超过相机最大帧率。触发去抖在相机配置中开启Trigger Debouncer功能并设置合适的去抖时间如 10ms可以滤除信号抖动造成的误触发。触发延迟产品经过传感器到进入相机视野中心有时间差。在相机配置中设置Trigger Delay参数让相机在收到信号后等待一段时间再曝光。软件触发替代测试暂时将相机触发模式改为软件触发在程序中定时触发拍照。如果正常则问题出在硬件触发信号上如果不正常则问题出在相机或软件配置上。5.3 算法稳定性问题问题白天和晚上检测效果差异大。原因环境光窗户自然光变化影响了整体光照条件。解决物理隔绝为检测工位搭建暗箱彻底隔绝环境光。这是最根本、最有效的方法。算法补偿如果无法做暗箱则采用更鲁棒的算法。放弃对绝对灰度值敏感的阈值分割改用对光照变化不敏感的梯度特征或纹理特征。或者在每次检测前先对一个已知的、固定的“参考白块”区域进行采样计算当前光照下的灰度值动态调整后续算法的阈值即“白平衡”或“增益校准”。问题产品颜色批次性差异导致误检。原因原材料或喷涂工艺波动导致产品颜色轻微变化。解决颜色空间转换将图像从 RGB 转换到 HSV 或 Lab 颜色空间。颜色信息主要存在于 H色调通道而亮度变化主要体现在 V 或 L 通道。在 H 通道上进行处理可以有效抵抗亮度变化的影响。统计过程控制收集多个批次 OK 品图像的特征值如平均灰度、颜色直方图计算其均值和标准差。在检测时如果当前产品特征值落在历史统计的 ±3σ 范围内则认为是正常波动否则报警提示可能来料异常。这需要将视觉系统与 MES 系统联动。搭建这样一套系统最大的体会是“平衡”。你要在精度与速度、成本与性能、通用性与专用性之间反复权衡。没有一劳永逸的参数只有最适合当前场景的方案。每次上线新项目最花时间的往往不是写代码而是和机械工程师、电气工程师一起优化机械夹具、调整光源角度、梳理信号逻辑。视觉是系统工程代码只是其中一环。最后一个小技巧一定要在工控机上配置远程桌面并做好系统镜像备份。这样当现场出现棘手问题时你可以远程登录查看如果系统崩溃也能快速恢复。