1. 项目背景与核心价值
条形码识别技术已经渗透到我们生活的方方面面,从超市收银台到仓库管理,再到图书馆借阅系统。但市面上成熟的商业扫描设备动辄上千元,对于小型商户、创客爱好者或教学实验场景来说成本过高。这正是我决定用单片机开发低成本条形码识别系统的初衷。
这个项目的核心价值在于:用不到100元的硬件成本(单片机+摄像头模块),实现基础的一维条形码识别功能。虽然性能无法与专业设备媲美,但足以满足库存盘点、简易收银等场景需求。我在实际测试中,对标准EAN-13条形码的识别率能达到92%以上,识别速度约1.5秒/条。
2. 硬件选型与搭建
2.1 核心硬件清单
- 主控芯片:STM32F103C8T6(性价比之王,72MHz主频足够处理图像)
- 图像采集:OV7670摄像头模块(30万像素,带FIFO缓存)
- 显示模块:0.96寸OLED(I2C接口,用于实时显示识别结果)
- 辅助硬件:蜂鸣器(识别成功提示)、按键(触发扫描)
硬件采购建议:OV7670一定要选择带FIFO的版本!原始型号不带缓存,会导致图像撕裂。我最初贪便宜买了无缓存版,结果浪费三天时间调试无果。
2.2 电路连接要点
cpp复制// 典型接线示意图
OV7670_VSYNC -> PA0 // 垂直同步信号
OV7670_HREF -> PA1 // 行同步信号
OV7670_PCLK -> PA2 // 像素时钟
OV7670_D[7:0] -> PB[7:0] // 数据总线
特别注意:OV7670的工作电压是3.3V,而部分STM32开发板的IO口默认是5V输出,直接连接可能烧毁摄像头。我的解决方案是在数据线串联100Ω电阻分压,实测稳定可靠。
3. 条形码识别算法实现
3.1 图像预处理流程
- 灰度化:将RGB图像转为灰度(采用Y=0.299R+0.587G+0.114B公式)
- 二值化:动态阈值法(非固定阈值!环境光线变化时更鲁棒)
- 降噪处理:3x3中值滤波去除孤立噪点
c复制// 动态二值化示例代码
uint8_t threshold = calculate_local_threshold(image, x, y);
if(pixel > threshold)
binary_image[y][x] = 1;
else
binary_image[y][x] = 0;
3.2 条形码定位技巧
条形码的典型特征是有连续的黑色条和白色间隔。我采用的方法是:
- 水平投影分析:统计每行黑色像素数量,找到峰值区域
- 垂直边缘检测:通过Sobel算子增强条码边缘
- 旋转校正:当条形码倾斜时,用Hough变换检测倾斜角度
实测发现:超市常见的EAN-13条码,其起始/终止符都是"101"模式(黑-白-黑),这个特征可以帮助快速定位条码区域。
3.3 解码核心逻辑
以EAN-13为例,其编码规则如下:
- 左边6位数字:奇偶组合编码(A组/B组)
- 右边6位数字:统一用C组编码
- 校验位计算:加权求和模10
我设计的状态机解码流程:
- 识别起始符"101"
- 按模块宽度解析数字(每个数字由4个条/空组成)
- 遇到中间分隔符"01010"时切换解码模式
- 校验最后一位是否匹配计算结果
4. 性能优化实战
4.1 内存管理技巧
STM32F103只有20KB RAM,而OV7670输出QVGA图像需要96KB缓冲区。我的解决方案:
- 只采集ROI区域(仅扫描图像中心200x50像素区域)
- 使用DMA传输图像数据,避免CPU阻塞
- 二值化后立即释放原始图像内存
4.2 实时性提升
通过以下手段将识别时间从3.2秒优化到1.5秒:
- 降低分辨率:从320x240降至160x120
- 跳行采样:每隔2行处理1行
- 提前终止:当解码出完整条码时立即结束处理
5. 常见问题与解决方案
5.1 图像模糊导致识别失败
现象:条码边缘不清晰,解码错误率高
解决方法:
- 调整摄像头焦距(OV7670背面有调节螺丝)
- 增加补光LED(我用的是5mm白光LED,串联限流电阻)
- 软件端采用锐化滤波增强边缘
5.2 不同光照条件下的适应
测试数据:
| 光照条件 | 原始识别率 | 优化后识别率 |
|---|---|---|
| 强光直射 | 65% | 89% |
| 昏暗环境 | 70% | 85% |
| 正常室内 | 92% | 96% |
优化措施:
- 动态调整摄像头增益(通过SCCB总线配置OV7670寄存器)
- 二值化阈值与环境光强联动(通过光敏电阻采集环境亮度)
5.3 条码变形校正
当条码曲面粘贴在圆柱体上时,会出现透视变形。我的处理方案:
- 检测条码四角位置
- 计算透视变换矩阵
- 用双线性插值进行图像矫正
c复制// 透视变换核心代码
void perspective_transform(uint8_t *src, uint8_t *dst, Point2f src_pts[4]) {
Mat transform = getPerspectiveTransform(src_pts, dst_pts);
warpPerspective(src, dst, transform, dst.size());
}
6. 项目扩展方向
目前系统只能处理EAN-13/UPC-A等一维条码。后续可扩展:
- 二维码识别:需要升级摄像头分辨率(至少200万像素)
- 无线传输:添加ESP8266模块实现Wi-Fi数据传输
- 数据库对接:通过串口连接PC端查询商品信息
我在实际部署中发现,给系统增加一个简单的语音播报功能(使用SYN6288模块)能显著提升用户体验,特别是在仓储盘点场景下,操作员可以专注在货物上而不必频繁查看屏幕。