3. 二维码生成与识读工程实践
二维码追溯系统的第一步,是把业务数据变成可被机器稳定读取的图形符号。本节围绕 QR 码与 DataMatrix 两种主流码制,完成从“生成—识读—条件测试—识读率评估”的完整闭环。核心目标不是会调用一个库,而是理解参数如何影响识读率,并能用数据判断当前方案是否可用。
一、码制选择与生成参数
QR 码与 DataMatrix 都属于二维矩阵码,但适用场景不同。QR 码有定位图案和四个方向的校正图形,支持较大容量、较高纠错等级,适合物流标签、追溯码、支付码等需要手机或手持终端快速识读的场合。DataMatrix 结构更紧凑,同样面积下可容纳更多数据,且对低对比度、金属表面、小尺寸标记更友好,常用于电子元器件、医疗器械、汽车零部件等直接部件标记(DPM)。
生成时最关键的两个参数是纠错等级和模块尺寸。QR 码纠错等级分为 L、M、Q、H,分别对应约 7%、15%、25%、30% 的码字恢复能力。追溯码通常选 M 或 Q;若标签可能被油污、磨损或部分遮挡,应选 H。DataMatrix 的 ECC 200 纠错能力由码字数量决定,一般可恢复约 25% 的受损码字。
模块尺寸指每个最小黑白方格的物理边长。它决定了码的物理大小和识读距离。经验上,识读距离与模块尺寸、摄像头分辨率满足:可识读距离 ≈ 模块尺寸 × 像素数 / 视场角系数。工程中更常用简化判断:模块在图像中至少占 3~5 个像素,才能被稳定解码。因此,若摄像头在 300 mm 距离下水平视场覆盖 200 mm,图像宽度 1280 像素,则每毫米约 6.4 像素;模块尺寸 0.5 mm 时约 3.2 像素,已接近下限,应增大模块或缩短距离。
下面用 Python 的 qrcode 与 pylibdmtx 生成示例。示例参数为教学假设,实际项目需按标签尺寸和打印精度调整。
import qrcode
from qrcode.constants import ERROR_CORRECT_H
# 生成 QR 码:内容为追溯码,纠错 H,模块 10 像素
qr = qrcode.QRCode(
version=None, # 自动选择版本
error_correction=ERROR_CORRECT_H,
box_size=10, # 每个模块 10 像素
border=4, # 静区 4 模块
)
qr.add_data("TRC-2024-0001|LOT-A|SN-000123")
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save("trace_qr.png")
print("QR version:", qr.version, "modules:", qr.modules_count)
DataMatrix 生成可使用 pylibdmtx 或 treepoem。若用于部件直接标记,还需考虑打标方式(激光、喷墨、点刻)对模块边缘的影响,通常需要把模块尺寸放大到 0.25 mm 以上,并保证静区不小于 1 个模块。
二、识读环境搭建
识读端分两类:固定式摄像头和手持终端。固定式适合产线、分拣口、仓储门禁,要求视场稳定、光照可控;手持终端适合移动作业、盘点、巡检,要求适应不同角度和距离。两者共同的关键是成像质量,而不是单纯堆高分辨率。
搭建固定式识读环境时,先确定工作距离和视场。例如产线皮带宽度 400 mm,摄像头安装高度 500 mm,选用 1/2.5 英寸传感器、焦距 6 mm 镜头,则水平视场约 300 mm,可覆盖皮带中部。若标签可能出现在边缘,应改用 4 mm 镜头或提高安装高度。光源建议使用条形白光或环形光,避免频闪;曝光时间尽量短于 1/500 s,减少运动模糊。
手持终端识读则要关注解码库的二值化策略和多帧融合能力。很多开源库默认使用全局阈值,在反光、阴影或低对比度下容易失败。工程上可先做局部自适应二值化,再送入解码器;若单帧失败,可连续采集 5~10 帧,取成功解码次数最多的一帧结果。
下面给出一个基于 OpenCV 与 pyzbar 的识读示例,包含灰度化、自适应二值化和多帧尝试:
import cv2
from pyzbar.pyzbar import decode
cap = cv2.VideoCapture(0)
success_count = 0
for i in range(10):
ok, frame = cap.read()
if not ok:
continue
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 局部自适应二值化,适应不均匀光照
binary = cv2.adaptiveThreshold(
gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 31, 5)
results = decode(binary)
if results:
success_count += 1
print("解码内容:", results[0].data.decode("utf-8"))
break
print("成功帧数:", success_count)
cap.release()
三、光照、距离与破损条件测试
识读率测试不能只测“理想条件”,必须覆盖实际可能出现的边界。建议至少设置三组变量:光照、距离、破损。每组取 30~50 次识读,记录成功次数,计算识读率 = 成功次数 / 总尝试次数 × 100%。
光照测试可设:正常室内光(约 300~500 lx)、强侧光(产生反光)、弱光(约 50 lx)、逆光。距离测试可设:标称距离、0.5 倍、1.5 倍、2 倍。破损测试可设:无破损、遮挡 10%、遮挡 25%、划伤 1 条、污渍覆盖。注意,遮挡和破损应作用在码的不同区域,因为 QR 码的定位图案和校正图形受损时,失败率会显著上升。
下表为教学假设的测试记录示例,用于说明如何组织数据,不代表真实实测结果:
| 条件 | 尝试次数 | 成功次数 | 识读率 | 主要失败原因 |
|---|---|---|---|---|
| 正常光、标称距离 | 50 | 50 | 100% | — |
| 强侧光、标称距离 | 50 | 43 | 86% | 局部过曝,二值化丢失模块 |
| 弱光、标称距离 | 50 | 38 | 76% | 对比度不足,边缘模糊 |
| 正常光、2 倍距离 | 50 | 31 | 62% | 模块像素不足 3 px |
| 正常光、遮挡 25% | 50 | 40 | 80% | 部分码字超出纠错能力 |
从示例可以看出,距离和光照是影响最大的两个因素。若 2 倍距离识读率低于 80%,应优先增大模块尺寸或提高摄像头分辨率;若弱光识读率低,应增加补光或改用更高感光度传感器,而不是单纯提高纠错等级。纠错等级只能对抗码字损坏,不能弥补成像模糊。
四、结果判读与优化方向
识读率不是唯一指标,还要看首次识读时间和误码率。首次识读时间指从触发到输出正确内容的时间,产线通常要求小于 300 ms。误码率指解码结果与原始数据不一致的比例,正常应为 0;若出现误码,说明校验或解码库存在问题,必须排查。
优化时按以下优先级处理:第一,保证模块在图像中至少 3~5 像素;第二,改善光照均匀性,避免反光和阴影;第三,选择合适纠错等级,追溯码建议 M 或 Q,易损场景用 H;第四,增加多帧尝试和自适应二值化;第五,若仍不达标,再考虑更换码制或识读设备。
把上述流程跑通后,你会得到一组可复现的识读率数据。它既是当前方案的验收依据,也是后续优化标签尺寸、光源布局和识读设备选型的输入。真正稳定的追溯系统,靠的不是“能扫”,而是“在边界条件下仍然能扫”。