基于树莓派的瓷砖缺陷检测原型:从图像采集到算法实现

📅 发布时间:2026/9/12 11:23:21
基于树莓派的瓷砖缺陷检测原型:从图像采集到算法实现
简介基于树莓派与CSI摄像头OV5647的轻量级瓷砖缺陷检测原型系统源码包面向嵌入式视觉入门者、树莓派爱好者及质检类课程设计人群。项目以main.py为入口按下按钮触发拍照并实时分析有缺陷时红灯与蜂鸣器报警无缺陷则亮白灯。检测算法采用Canny边缘检测提取边缘点借助霍夫变换识别线段再通过邻域端点匹配与梯度欧氏距离归一化阈值判断缺陷逻辑清晰且便于二次改造。资源共15个文件包含3个Python脚本、8张效果与接线图、3个HTML报告页面及1份项目说明文档压缩包仅263KB轻量易部署。硬件接线图可帮读者快速复现CSI摄像头连接HTML报告能直观展示检测结果说明文档则覆盖环境配置与运行方式。已有180人学习下载适合快速搭建演示原型或作为毕业设计、工程实训参考。1. 为什么在树莓派上做瓷砖缺陷检测实际场景瓷砖产线几百片/分钟通过人工目检漏检率高下线的瓷砖还要二次分拣成本一直在往上走。把相机架在皮带线上树莓派收到触发信号就抓拍一帧在几百毫秒内给出“有缺陷/无缺陷”结论再用GPIO口发信号给分拣机构这就是这个轻量级原型系统能覆盖的最小闭环。树莓派这样的SBC算力有限但瓷砖表面纹理相对规则传统图像处理和轻量CNN组合够用。这篇文章围绕Python写树莓派上瓷砖缺陷检测原型展开覆盖硬件接线、图像采集、检测算法调参和性能验证不涉及产线级PLC联动也不依赖云端服务适合有基础Python经验、想把手头树莓派变成视觉检测设备的工程师。2. 系统架构与硬件接线摄像头、灯光和GPIO怎么连2.1 树莓派型号与相机接口的选择依据这个原型最常见的底座是树莓派4B的4GB版本。4B有独立的ISP和双频Wi-Fi跑OpenCV的时候还能同时开SSH会话做调试树莓派3B能跑但分辨率要压到640×480TFLite推理时间将近翻倍对产线节拍不友好。树莓派5算力更强但CSI接口的驱动兼容性噪音还在原型阶段如果不想被底层问题绊住4B是更稳的选择。相机接口有CSI和USB两条路。CSI口的官方摄像头模组OV5647或IMX219走板载ISPCPU占用低延迟一般能控制在100ms以内USB摄像头绕开CSI驱动问题插上就能用但CPU占用高支架稳定性也不如CSI排线。瓷砖检测的相机要固定在瓷砖正上方焦距固定CS接口的优势就体现出来了。分辨率不建议直接上500万像素满帧实际取1280×720或1920×1080检测区域约300mm×300mm单块瓷砖占视野的70%以上再小的缺陷会低于2个像素后面算法再强也救不回来。2.1.1 分辨率和视场的换算关系举例来说IMX219在1920×1080下横向像素密度约1.8 pixel/mm一条宽度0.3mm的裂纹在图像里只占0.54像素基本检测不出来。原型系统通常把视野压到200mm×200mm让横向密度提升到2.7 pixel/mm。换算关系是分辨率宽度 / 视野宽度 横向像素密度这个参数在摄像头安装高度固定后用一把标尺拍一张图来校准比按镜头标称视角推算更准——近距离下镜头标称视角的偏差普遍在5%以上。2.2 照明设计与LED接线参数瓷砖缺陷检测九成的问题出在反光。釉面砖的高光区域会把裂纹掩盖掉低角度光还会在凹陷处形成伪影。常见的做法是采用环形无影灯或两条对称的漫反射LED条光源距瓷砖表面200-300mm倾斜角30-45度。单侧光更容易打出裂纹和崩瓷的阴影但会在釉面产生强反射双侧漫射光能压反射但深划痕的对比度会弱一档。原型阶段留出两个GPIO接口分别控制不同朝向的灯组调参时切换对照比把软件参数反复改来改去更省时间。LED接线的关键参数是限流电阻。树莓派GPIO输出3.3V不能直接驱动5V或12V灯带常见做法是用MOSFETAO3400或IRLZ44N做低边开关GPIO接栅极电阻10kΩ下拉源极接地漏极接LED灯带负极灯带正极接外部5V电源。如果只是驱动两颗5mm白光LED做实验不用MOSFET直接GPIO17→220Ω限流电阻→LED正极→负极接GND。PWM频率设到200Hz以上避免频闪被摄像头拍成横向暗带。2.2.1 GPIO引脚分配示例引脚信号用途GPIO17PWM输出LED亮度控制200Hz占空比从50%起调GPIO23数字输入光电开关触发信号GPIO24数字输出缺陷结果信号接蜂鸣器或分拣气缸GND6地线与外部电源、传感器共地接线图里最容易忽略的是共地。传感器和LED的外部电源如果不与树莓派GND相连GPIO读到的高电平是浮空的程序里可能一直触发或完全不触发。原型排线时先把GND接在同一个汇流条上再谈信号线。2.3 触发信号与传送带联动的坑产线场景经常用光电开关或接近开关触发拍照。传感器输出是NPN集电极开路还是PNP推挽决定了GPIO要不要接上拉电阻。NPN常开型的接法传感器正极接5V负极接GND信号端经10kΩ上拉电阻接GPIO23瓷砖到位时输出低电平在RPi.GPIO里配置为上拉并检测下降沿。机械式接近开关会有抖动回调里要加消抖否则一片瓷砖可能触发3次拍照后面的队列会越积越多。import time import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(23, GPIO.IN, pull_up_downGPIO.PUD_UP) GPIO.setup(24, GPIO.OUT, initialGPIO.LOW) last_trigger [0.0] def on_sensor(channel): now time.time() if now - last_trigger[0] 0.05: return last_trigger[0] now capture_and_detect() # 采集、检测、置位GPIO24 GPIO.add_event_detect(23, GPIO.FALLING, callbackon_sensor, bouncetime50) try: while True: time.sleep(1) except KeyboardInterrupt: GPIO.cleanup()逻辑说明bouncetime50让内核层在50ms内只认第一次边沿事件Python回调里再用时间戳做二次过滤双重保险capture_and_detect()是业务主函数里面做采集、检测和GPIO24置位。bouncetime的单位是毫秒传送带速度越快这个值越要调小0.8m/s的皮带、瓷砖长度300mm相邻瓷砖间隔至少375ms50ms不会漏拍速度翻倍后要降到30ms左右或用20ms加时间戳判断。最后一行的while True保证回调不会因主线程退出而失效。3. Python图像采集与预处理从CSI摄像头到ROI提取3.1 用picamera2或OpenCV读取视频流树莓派官方CSI摄像头在最新系统走libcamera栈老版本picamera库基本废弃OpenCV的cv2.VideoCapture(0)对CSI口的兼容性也不稳定常报“Failed to open camera”或直接黑屏。优先用picamera2拉流再转成OpenCV能直接处理的NumPy数组。连续检测场景用create_video_configuration如果只是拍摄单帧触发用create_still_configuration会更节省内存。sudo apt update sudo apt install -y python3-picamera2 python3-opencv安装命令里的python3-picamera2是libcamera栈在树莓派OS上的Python绑定python3-opencv提供OpenCV的Python接口。安装后先运行libcamera-hello确认摄像头能被系统识别等几秒钟能在屏幕上看到取景画面再跑下面的代码就不会对着黑屏找半天原因。import cv2 import numpy as np from picamera2 import Picamera2 picam2 Picamera2() config picam2.create_video_configuration( main{size: (1920, 1080), format: RGB888} ) picam2.configure(config) picam2.start() frame picam2.capture_array() # shape: (1080, 1920, 3) gray cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)create_video_configuration对应连续取流比create_still_configuration少一层静态帧缓冲的申请开销capture_array()返回NumPy数组后续OpenCV操作完全一致。格式配置为RGB888时cvtColor用COLOR_RGB2GRAY如果配置里选了BGR888就要改成COLOR_BGR2GRAY写反了灰度图会整体反差异常。USB摄像头替代方案是cv2.VideoCapture(0)如果遇到设备被占用用sudo lsof /dev/video0看进程把之前的picamera2进程杀掉即可。3.2 预处理流水线降噪、光照均衡和透视矫正瓷砖缺陷检测的ROI提取要先做三步预处理否则算法参数会被背景干扰。第一步降噪cv2.GaussianBlur用3×3核就够比bilateralFilter在树莓派上节省大量计算。第二步光照均衡用大核高斯模糊模拟背景光照原图减去背景再加128得到去光照的均匀图。第三步透视矫正摄像头安装歪了以后图像里瓷砖的矩形会变成梯形先用轮廓找到瓷砖外边界再取四个角点做透视映射。def preprocess(gray: np.ndarray) - np.ndarray: blur cv2.GaussianBlur(gray, (3, 3), 0) bg cv2.GaussianBlur(blur, (31, 31), 0) normalized cv2.subtract(blur, bg) 128 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) return clahe.apply(normalized)31×31的大核模糊近似背景照度釉面砖纹理较规整这样处理不会带出过多细节-到128的偏移在np数组上要注意数据类型直接blur - bg 128在uint8下可能会溢出cv2.subtract能正确处理截断。clipLimit2.0是保守值釉面砖纹理多时调到1.5哑光砖可以加到3.0tileGridSize用8×8改成16×16会带出瓷砖纹理噪点4×4让图像像砂纸。两个高斯模糊在树莓派4B上约20ms占单帧总耗时的大约15%对原型可以接受。3.3 ROI边界提取与透视变换预处理完成后用二值化把瓷砖本体从背景中抠出来。工业上常用adaptiveThreshold而不是固定阈值因为浅色砖和深色砖的灰度范围差异明显固定阈值没法统一。blockSize41、C10组合能稳定提取瓷砖边缘如果传送带颜色与瓷砖接近就得靠外部光源把瓷砖打得比背景更亮单改算法参数效果有限。thresh cv2.adaptiveThreshold( normalized, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blockSize41, C10 ) contours, _ cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) tile_contour max(contours, keycv2.contourArea) rect cv2.minAreaRect(tile_contour) box cv2.boxPoints(rect) box np.array(sorted(box, keylambda p: (p[1], p[0])))blockSize必须是奇数决定邻域半径C是偏移常量值越大自适应阈值越高暗纹理越容易被当成背景。findContours用RETR_EXTERNAL只找外层轮廓拿到轮廓后用minAreaRect获得带旋转角度的最小外接矩形比boundingRect更适应瓷砖斜放。sorted按y坐标再按x坐标排序四个点顺序统一后用cv2.getPerspectiveTransform计算变换矩阵再cv2.warpPerspective映射到固定尺寸560×560后续缺陷检测全部在ROI上执行背景干扰被彻底排除。4. 缺陷检测算法实现与参数调优4.1 裂纹与划痕形态学开运算与灰度差阈值瓷砖裂纹的特征是细长的暗线灰度值比周围低且具备连续性。直接Canny会把釉面纹理边界也当边缘误检率很高。轻量级做法是计算形态学开运算的结果先开运算去掉细线原图减去开运算结果留下裂纹和划痕的响应。结构元素的尺寸决定了能检出的最小裂纹宽度3×3内核适配约0.5mm宽的裂纹5×5内核只保留更粗的缺陷。kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) opened cv2.morphologyEx(gray_roi, cv2.MORPH_OPEN, kernel) diff cv2.absdiff(gray_roi, opened) _, mask cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY) cnts, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) defects [c for c in cnts if cv2.contourArea(c) 50]threshold的25是裂纹灰度差阈值。它要按现场光照重新标定用人工划线样板采10张图统计裂纹区域灰度差的最小值取约75分位作为参数不能随手拍脑袋定死。面积过滤contourArea 50在560×560的ROI上对应约0.15mm²的物理面积比它小的都算噪声。开运算对方向不敏感横裂纹和竖裂纹都能检出但曲面砖的纹理弧度较大时开运算容易把纹理凹陷误判成缺陷改用morphologyEx(gray_roi, cv2.MORPH_BLACKHAT, kernel)黑帽变换提取比邻域暗的小结构再配方向性内核横向用1×9纵向用9×1分别检测两个方向更稳。4.2 色差、针孔与崩瓷中值滤波、面积过滤和边缘偏差色差和针孔用颜色空间分割更直接。RGB空间受光照影响大先转LAB或HSV在L通道和a、b通道上做阈值。色差表现为局部区域的L或a、b偏离标准取整块ROI的L通道均值作为基准偏离超过15的像素标记为异常。针孔是尺寸很小的低亮度区域中值滤波后消失但裂纹不会被3×3中值滤波消除这一差异可以用来区分两类缺陷。def detect_pinholes(gray_roi: np.ndarray) - tuple[list, np.ndarray]: median cv2.medianBlur(gray_roi, 3) diff cv2.absdiff(gray_roi, median) _, mask cv2.threshold(diff, 12, 255, cv2.THRESH_BINARY) kernel_close cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) closed cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_close, iterations2) cnts, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) pinholes [c for c in cnts if 5 cv2.contourArea(c) 200] return pinholes, closedmedianBlur的核大小取3针孔在滤波前后灰度差通常较大阈值diff给12光照不均匀时调到8-10但噪声也会变多。MORPH_CLOSE迭代两次把零散针孔区域连成完整连通域面积过滤才有意义。针孔面积范围5到200像素小于5的基本是噪声大于200的可能与色差区域重叠。崩瓷检测在瓷砖边缘先二值化提取轮廓用cv2.approxPolyDP做多边形逼近比较逼近多边形与原始轮廓在边缘处的偏差偏差超过5个像素就标记为崩瓷。崩瓷的5像素阈值与成像分辨率强相关如果视野调整过这个值也要按比例重标。4.3 轻量CNN还是传统图像处理TFLite推理模板传统图像处理在树莓派上最大的优势是延迟低、参数透明、可解释性强。但缺陷类别一多比如裂纹、针孔、色差、崩瓷、气泡同时出现特征重叠度一高规则代码会膨胀到几百行。此时用轻量CNN更划算MobileNetV2或EfficientNet-Lite0量化成INT8后模型大小约2-3MB在4B上的推理耗时大致50-150ms和传统方法在一个量级。import tflite_runtime.interpreter as tflite interpreter tflite.Interpreter(model_pathtile_defect_qint8.tflite) interpreter.allocate_tensors() interpreter.set_num_threads(4) input_detail interpreter.get_input_details()[0] output_detail interpreter.get_output_details()[0] input_data cv2.resize(roi, (224, 224)) input_data input_data.astype(np.uint8)[np.newaxis, ...] interpreter.set_tensor(input_detail[index], input_data) interpreter.invoke() scores interpreter.get_tensor(output_detail[index])[0]set_num_threads(4)在4B的四核上通常能压掉20%-30%推理时间如果模型输入是float32astype(np.uint8)就不对要按照input_detail[dtype].__name__动态转换。直接拉伸到224×224会丢掉小裂纹的细节训练时把560×560的ROI按2×2切成280×280各自缩到224×224分别推理后合并结果比整张图直接缩放稳定得多。原型阶段不追求召回99%保证缺陷不漏检、误检率可控在5%以内后面产线分拣还有人工复核先把指标做到可量化更重要。4.4 检测参数调优的标定流程安装和调试阶段要一次性标定的参数集中在Canny滞后阈值、形态学核大小、针孔minArea和CLAHE的clipLimit。标定流程先用无缺陷样板砖采集20张图统计正常图像的响应分布将每个参数设定在正常响应分位的99%以上再拿10张人工缺陷样板记录缺陷区域是否都高于该阈值如果检出率低于95%说明阈值过高下调并重新验证。参数初始值调节方向典型范围灰度差阈值25降低提高检出率噪声增加15-40开运算核大小3×3增大只保留粗裂纹3×3到7×7针孔minArea5降低检出更小针孔误检增加3-20CLAHE clipLimit2.0提高增强对比度噪声变多1.5-3.0TFLite推理分辨率224提高损失细节耗时增加128-320调试时要记录每张测试图对应的参数响应值不要随机调参。把参数写进配置文件用脚本批量跑测试集输出响应分布直方图观察缺陷样本和正常样本之间的分离度分离度够大参数才能在这种光照和相机角度下稳定工作。5. 光线变化和性能瓶颈的验证技巧5.1 用直方图和基准板校验光照稳定性在实验室能跑通到了厂房常因环境光漂移导致结果不稳定。做两项验证第一项用标准白板或同一块无缺陷砖连续采集100帧统计灰度直方图均值偏移如果不同时段均值偏移超过20说明环境光影响大需要加遮光罩或改用封闭光源。第二项把基准板放在传送带上每10分钟采集一帧算均值和标准差的长时漂移判断LED老化或电源电压下降带来的光衰。灰度直方图用cv2.calcHist实现然后统计均值和标准差均值偏高说明过曝调低LED占空比或缩小光圈标准差偏大说明光照不均匀调整灯组的安装角度。5.2 帧率与延迟的实测方法原型可用的核心指标是“从触发信号到输出结果的时延”。在GPIO触发回调里记录时间戳检测完成后再次读取时间戳连续跑200次取p99避免偶发大波动影响判断。try: t0 time.perf_counter() result detect_one(frame) latency_ms (time.perf_counter() - t0) * 1000 print(flatency: {latency_ms:.1f} ms, result: {result}) time.sleep(1) except KeyboardInterrupt: passdetect_one内部包含图像采集、预处理、检测和结果输出取多次运行的最大值而非平均值更能暴露性能瓶颈。树莓派上常见的性能坑cv2.resize在循环里反复创建输出数组预先分配并指定interpolationcv2.INTER_NEAREST可以省一点cvtColor每帧只做一次不要在每个子函数里重复转换TFLite的Interpreter实例创建一次循环里只调用invoke()初始化开销会被彻底摊掉。5.3 误检率的量化评估与回归测试原型阶段的最后一步是把检测算法固化为一个可重复的版本并建立回归测试集。测试集至少包含30张无缺陷砖和每类缺陷各20张输出混淆矩阵和各类缺陷的F1-score每次改动参数后跑一遍确认没有在修复一类缺陷时引入另一类误检。这三大块写入项目说明文档时接线图、参数表、回归数据集三样东西的顺序不要换接线图保证后来的人能复现硬件参数表保证软件调整有据可依回归数据集让每一次改动都有可对比的验证结果。漏检集中在一类缺陷时说明该特征没有被算法覆盖应当补一条对应的检测分支而不是简单调低全局阈值。误检集中在特定纹理砖型时优先为该砖型增加正常模板数据而不是把所有阈值调严。分类模型则可以单独保留最后两层在增量数据上做微调避免整个网络重新训练带来的回归风险。本文还有配套的精品资源点击获取