ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

树莓派+Python轻量级瓷砖缺陷检测原型:从算法到硬件实现

2026/9/12 11:17:24 拓冰建站 浏览量
树莓派+Python轻量级瓷砖缺陷检测原型:从算法到硬件实现 简介一套运行在树莓派硬件上的轻量级瓷砖缺陷检测原型系统源码面向嵌入式视觉、工业质检方向的开发者和高校学生。项目通过CSI总线接入OV5647摄像头按钮触发拍照后自动分析瓷砖表面是否存在缺陷判定结果以红灯加蜂鸣器或白灯直观反馈算法链路涵盖Canny边缘检测、霍夫变换线段提取、邻域线段关联、梯度欧氏距离计算及归一化阈值判别思路清晰且便于二次开发。压缩包共15个文件、约263KB其中3个Python脚本main.py、Utils.py、GenReport.py承担主流程、图像处理与报告生成8张jpg包含正常/缺陷样本及硬件接线图3个html模板用于检测结果可视化另有README.md说明项目整体结构。当前已有180人下载学习对于想快速搭建边缘端视觉检测演示、完成课设或入门树莓派图像识别的读者是一份轻量且完整的实用参照。1. 为什么选择用Python在树莓派上做轻量级瓷砖缺陷检测很多做视觉项目的朋友一听到瓷砖缺陷检测第一反应就是上深度学习准备几千张标注图、租一台带GPU的服务器。如果目标只是“先跑通一个原型系统”这个思路反而最费时间。瓷砖表面纹理稳定、缺陷与背景灰度差明显、相机和光源都可以人工控制这种条件下传统图像处理的开发速度和检测效果都更划算。这套原型系统用Python写检测脚本用树莓派4B做边缘端运算接一只ov5647摄像头模块成像再用GPIO控制补光灯就能把裂纹、针孔这类缺陷找出来并输出结果。适合自动化设备预研工程师也适合在毕业设计里完整展示采集、处理、联动链路的同学。下面把算法选型、硬件接线、源码和调试顺序依次讲清按这个路径走不用在模型训练和标注数据上绕路。2. 瓷砖缺陷检测的轻量级方案算法选型与图像预处理参数2.1 缺陷的成像特征决定检测算法先看瓷砖上常见缺陷在相机里的样子。裂纹在图像里是一条暗色细线长度远大于宽度针孔是没有釉料的小洞表现为圆形暗斑直径只有几个到十几个像素崩边在瓷砖边缘出现缺口轮廓形状不规则。三类缺陷在灰度上都比正常釉面暗这是传统阈值分割能成立的前提。缺陷类型成像特征推荐检测算法关键筛选参数裂纹暗色细线长宽比大顶帽变换 灰度阈值长宽比 3最小面积 50针孔圆形暗斑面积小灰度阈值 连通域分析面积 50~500圆形度 0.5崩边边缘缺口形状不规则轮廓检测 ROI裁切边缘邻域内面积突变轻量级方案里不选神经网络的原因很直接原型阶段最缺的是标注数据而传统方案只需要调阈值和形态学参数。样本用来验证系统而不是用来训练模型。“轻量级”不是算法简陋而是在分辨率和缺陷类型受限的前提下用最少的计算资源解决主要问题。树莓派4B跑一帧1280x960的完整传统视觉流程从采集到输出结果可以控制在100毫秒量级这对原型系统来说已经够用。补光角度对成像的影响在这一步就要想清楚。低角度光最容易照出裂纹因为光从侧面进入凹槽会形成明显阴影垂直打光时裂纹对比度显著下降后面阈值怎么调都吃力。2.2 顶帽变换增强裂纹与针孔的最小灰度处理直接用原始灰度图做阈值分割的常见问题是瓷砖表面本身有深浅不一的纹路和灰尘固定阈值要么把纹路切进来要么漏掉低对比度缺陷。顶帽变换top-hat先做背景抑制思路是用形态学开运算估计背景亮度再用原图减背景把比背景暗的缺陷单独留下。import cv2 import numpy as np def enhance_dark_defects(img_bgr): gray cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) kernel_size 15 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)) background cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) tophat cv2.subtract(gray, background) _, thresh cv2.threshold(tophat, 30, 255, cv2.THRESH_BINARY) return gray, tophat, thresh高斯模糊核用5x5作用是压掉传感器的高频噪声太小没效果太大把细裂纹的边缘也磨平了。kernel_size取15是经验起点它必须大于期望检出的最宽缺陷否则开运算会把宽裂纹错当成背景。阈值30表示只保留比背景暗30个灰度级以上的像素缺陷与釉面反差越大这个值就能调得越高。实际调参时先降到5和8把小缺陷找出来再逐步往上提直到背景噪声消失比直接套固定值可靠。提示同一批瓷砖的釉色深浅会有波动阈值不要写死在代码里从配置文件读入。第4章源码就是这么组织的。2.3 形态学开运算与连通域面积过滤的边界条件阈值分割之后的结果里通常有噪声灰尘形成的孤立暗点以及瓷砖纹理被切割后留下的碎屑。形态学开运算能去掉一部分孤立小点但核如果太大会把真正的针孔一起抹掉。3x3椭圆核是处理针孔场景的安全起点开运算一次约能滤掉半径1个像素的孤立噪声。open_kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) cleaned cv2.morphologyEx(thresh, cv2.MORPH_OPEN, open_kernel) contours, _ cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates [] for c in contours: area cv2.contourArea(c) if area 50: continue candidates.append(c)min_area50是按1280x960分辨率给的初值语义是“面积小于50像素的连通域一律视为噪声”。这个数字和像素、物理尺寸的换算直接关联摄像头离瓷砖1米时50像素可能对应一个直径2mm的针孔离瓷砖0.3米时同样50像素对应更小的孔洞。所以换安装位置后必须重新标定像素与毫米的比例这是原型项目里最容易漏的一步。2.4 长宽比与最小外接矩形区分裂纹和针孔裂纹和针孔在阈值图里都是暗色连通域区分它们最常用的特征是长宽比。用最小外接矩形包住连通域长边是短边3倍以上时判为裂纹否则按面积和圆形度判为针孔或颗粒。圆形度用轮廓面积和最小外接圆面积之比计算越接近1越像圆。def classify_contour(c): area cv2.contourArea(c) rect cv2.minAreaRect(c) w, h rect[1] if min(w, h) 1e-6: return unknown, 0.0 long_side, short_side max(w, h), min(w, h) ratio long_side / short_side (x, y), radius cv2.minEnclosingCircle(c) circularity area / (np.pi * radius * radius) if radius 0 else 0.0 if ratio 3.0 and short_side 5: return crack, ratio if circularity 0.5 and area 500: return pinhole, ratio return chip, ratio长宽比阈值取3.0是因为裂纹在成像时往往弯曲长宽比会因此略微下降取2.5到3之间都在合理范围取太高会把弯曲裂纹漏掉。short_side 5是为了排除宽大的暗色块。圆形度高的连通域长宽比通常接近1而细长裂纹的圆形度很低这两个特征互相印证。实际项目里优先看长宽比再看面积圆形度只做补充判断。3. 树莓派4B硬件接线ov5647摄像头与GPIO补光灯的接入方式3.1 ov5647摄像头模块与树莓派4B的CSI接口连接树莓派官方摄像头模块的核心传感器是ov5647通过排线接到板上的CSI接口。树莓派4B的CSI接口在HDMI接口旁边是一个按压卡扣式排线座。接排线时把金属触点朝网口方向推到底压紧卡扣全程不要带电操作。插好后确认排线两端都到位新手最容易犯的错是插反方向导致系统识别不到摄像头。直观检查方法是看排线上的印刷字方向通常有字的一面朝接口外侧。方向不对时运行libcamera-hello会直接报无法找到摄像头。CSI摄像头和USB摄像头有本质区别图像数据不经过CPU搬运走专用接口同样分辨率下CPU占用明显更低。原型系统用CSI摄像头还省下USB口键盘鼠标和后续外设都能接上调试时少很多麻烦。3.2 启用摄像头系统配置与拍照验证命令树莓派官方系统默认不启用摄像头接口刚刷完系统要先进入配置界面打开Camera选项。sudo raspi-config # Interface Options - Camera - Enable重启重启后先确认硬件链路libcamera-hello --list-cameras libcamera-still -o test.jpg --width 1280 --height 960 --rotation 0第一条命令应列出ov5647第二条命令拍一张测试图。test.jpg能正常生成说明摄像头硬件没问题。后面要确认OpenCV能否直接访问摄像头设备检查/dev/video0是否存在这个节点不存在时传统做法是加载bcm2835-v4l2驱动模块并加入开机加载列表重启后再查一遍。这一步做完第4章的源码才能直接在树莓派上跑。树莓派上安装OpenCV直接用系统源提前装好可以省去源码编译的时间sudo apt update sudo apt install -y python3-opencv3.3 GPIO引脚分配与补光灯接线对照表补光灯是成像质量的关键因素树莓派4B的CSI接口没有闪光灯同步能力光源控制只能走GPIO。原型里用一组白光LED照亮瓷砖控制端接BCM编号17的GPIO引脚外部用NPN三极管或MOS管做开关避免让GPIO直接驱动大电流LED。树莓派物理引脚BCM编号接线对象说明序号11GPIO17补光灯控制信号高电平点亮低电平熄灭序号14GNDLED驱动板负极必须与树莓派共地序号45V电源LED灯串正极经驱动大功率灯珠勿从GPIO取电对照表格画硬件接线图时按引脚序号和BCM编号一一对应即可。三极管驱动常规接法是控制信号接基极基极与GPIO之间串1kΩ电阻LED负极接集电极LED正极接5V发射极接地。共地是最容易出错的一环。灯串如果使用独立12V适配器供电适配器负极必须和树莓派GND连在一起否则GPIO输出的高电平对驱动电路来说没有参考地灯始终不亮。3.4 采集与光源同步GPIO点亮补光灯后再拍照原型系统的工作方式不是常亮灯而是拍照瞬间点亮。这样LED发热量小也能让相机自动曝光时环境光尽量稳定。点亮之后要等一小段时间约100到300毫秒让LED电流稳定后再拍否则同一块瓷砖拍两次的灰度值都会漂移。import time import cv2 import RPi.GPIO as GPIO LED_PIN 17 GPIO.setmode(GPIO.BCM) GPIO.setup(LED_PIN, GPIO.OUT) cap cv2.VideoCapture(0, cv2.CAP_V4L2) GPIO.output(LED_PIN, GPIO.HIGH) time.sleep(0.2) ret, frame cap.read() if not ret: print(采集失败检查 /dev/video0) GPIO.cleanup() exit(1) GPIO.output(LED_PIN, GPIO.LOW) cap.release() GPIO.cleanup()cv2.VideoCapture的第二个参数cv2.CAP_V4L2是显式指定V4L2后端在树莓派上比默认后端稳定。time.sleep(0.2)放在亮灯之后、拍照之前这个顺序不能颠倒。RPi.GPIO是树莓派专用库开发机上没有先在电脑上调算法时可以用模拟接口占位到树莓派上再接真实GPIO。4. 在树莓派上跑通瓷砖缺陷检测的Python原型源码4.1 原型系统的运行流程运行流程拆成四步初始化、采集、检测、输出。初始化阶段设置GPIO和摄像头采集阶段开灯、等待稳定、读一帧检测阶段做第2章的预处理和连通域分析输出阶段把缺陷列表写成JSON并打印。设计上要把“缺陷检测”和“硬件控制”分开。检测函数只接收一张BGR图像返回缺陷列表硬件部分只负责亮灯和拍照。这样在电脑上调试图像算法时不需要树莓派硬件在树莓派上跑完整系统时也不用改动算法代码。4.2 完整可运行的检测脚本import json import time import cv2 import numpy as np import RPi.GPIO as GPIO LED_PIN 17 MIN_AREA 50 RATIO_THRESH 3.0 def enhance_dark_defects(img_bgr): gray cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15)) background cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel) tophat cv2.subtract(gray, background) _, thresh cv2.threshold(tophat, 30, 255, cv2.THRESH_BINARY) return thresh def find_defects(thresh): open_kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) cleaned cv2.morphologyEx(thresh, cv2.MORPH_OPEN, open_kernel) contours, _ cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) defects [] for c in contours: area cv2.contourArea(c) if area MIN_AREA: continue rect cv2.minAreaRect(c) w, h rect[1] if min(w, h) 1e-6: continue long_side, short_side max(w, h), min(w, h) ratio long_side / short_side x, y int(rect[0][0]), int(rect[0][1]) if ratio RATIO_THRESH: dtype crack elif area 500: dtype pinhole else: dtype chip defects.append({ type: dtype, x: x, y: y, w: int(w), h: int(h), area: int(area), ratio: round(ratio, 2) }) return defects def capture_and_detect(save_pathNone): GPIO.setmode(GPIO.BCM) GPIO.setup(LED_PIN, GPIO.OUT) cap cv2.VideoCapture(0, cv2.CAP_V4L2) GPIO.output(LED_PIN, GPIO.HIGH) time.sleep(0.2) ret, frame cap.read() GPIO.output(LED_PIN, GPIO.LOW) cap.release() GPIO.cleanup() if not ret: raise RuntimeError(camera read failed) if save_path: cv2.imwrite(save_path, frame) thresh enhance_dark_defects(frame) defects find_defects(thresh) return { width: frame.shape[1], height: frame.shape[0], defect_count: len(defects), defects: defects } if __name__ __main__: result capture_and_detect(save_path/tmp/last_frame.jpg) print(json.dumps(result, indent2, ensure_asciiFalse))执行方式就是python3 tile_inspect.py。脚本先点亮补光灯等待LED稳定后采集一帧关闭灯光再进入检测流程。find_defects内部先做形态学开运算再进行轮廓提取和特征筛选。输出的JSON包含图像尺寸、缺陷数量和每个缺陷的类型、中心坐标、宽高、面积、长宽比字段结构方便后续接串口显示或数据库。4.3 关键函数与参数在工作中的设定依据增强函数里的高斯核5x5和顶帽结构核15是相对平衡的组合。15的结构核能滤掉宽度约8像素以下的背景纹理同时保留更宽的裂纹。如果瓷砖表面纹理较粗把15改成21纹理细密则改成11。改完要在阈值图上观察背景噪声变化不能只看最终输出。find_defects里min_area50对应连通域面积下限。设定依据来自中间结果而不是拍脑袋。调试时先在所有轮廓上画出边界框和面积值统计正常瓷砖上有哪些噪声轮廓取一个能覆盖它们的上限作为min_area。现场灰尘多就调大光线干净可以调小。JSON输出里的x和y来自minAreaRect的中心点经纬度式的定位方式在原型阶段足够用。后面的w和h是旋转矩形在局部坐标系下的尺寸如果要做缺陷面积统计直接用area字段更准确不要拿w乘h因为矩形有旋转角度。4.4 把参数外置成配置文件项目说明才能跟上代码原型交付时要复制到另一台机器或者换一块瓷砖批次参数经常要改。常见做法是把检测参数放到config.json代码启动时读取这样项目说明文档只描述配置文件的作用不用维护散落的常量。{ width: 1280, height: 960, led_pin: 17, gaussian_kernel: 5, tophat_kernel: 15, threshold: 30, open_kernel: 3, min_area: 50, ratio_threshold: 3.0 }每个字段都对应代码里一个可调输入。width和height受摄像头模组限制ov5647在树莓派驱动下最高可达2592x1944原型检测建议用1280x960处理耗时更短。led_pin对照第3章引脚表确认不要与系统默认占用的GPIO冲突。threshold是最敏感的参数换砖后优先调它。5. 瓷砖缺陷检测原型上线前的三组调试场景5.1 曝光与白平衡漂移让阈值失效先锁死相机参数树莓派摄像头默认开启自动曝光和自动白平衡同一种瓷砖在不同时间会拍出不同的灰度分布。固定阈值对这种漂移非常敏感上午能分出缺陷的阈值下午可能把整块瓷砖都报成缺陷。先把自动参数关掉锁定曝光时间和白平衡。cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0.25) # 切手动曝光模式 cap.set(cv2.CAP_PROP_EXPOSURE, 300) # 依环境亮度调整 cap.set(cv2.CAP_PROP_AUTO_WB, 0) # 关自动白平衡不同V4L2驱动的枚举值含义略有差异核心目的是固定曝光。验证方法是同一块瓷砖连续拍5张统计灰度均值波动超过±5就要继续调。5.2 从一张图开始调参保存中间结果而不是盯着终端调试最有效的手段是保存中间图像。把顶帽变换后的图、阈值图、形态学处理后的连通域标记图都写到/tmp目录用看图软件逐张对比能立刻看出是阈值太高造成漏检还是形态学核太大把小针孔抹掉了。建议用带参数的命令行入口跑系统python3 tile_inspect.py --debug --save-dir /tmp/tile_debug。每改一次参数重跑同一张测试图对比三组中间结果的变化。这比在终端一遍遍打印数值高效得多。5.3 用离线样本跑批评估误检率和漏检率原型进产线之前准备两组图片正常瓷砖20张带缺陷瓷砖20张。离线跑一遍检测脚本统计误检率和漏检率。误检率高通常问题在光照和背景漏检率高通常问题在阈值和形态学参数。指标计算公式常见瓶颈误检率误报缺陷数 / 正常样本数光照不均匀、背景纹路过粗漏检率未检出缺陷样本数 / 缺陷样本数阈值过高、结构核过大最后这轮跑批放在树莓派上执行和真实运行环境保持一致。不要只在开发机上验证因为树莓派的图像处理库版本和摄像头驱动与开发机存在差异结果可能有偏差。本文还有配套的精品资源点击获取