🔍 OCR 碑文识别 LLM 系统 — 架构说明
版本:LLM-V1.18 | 技术栈:Python 3 + PyQt5 + SQLite + Requests(多线程) 定位:SMT PCBA 产线「大板码/ReelID 扫码 → 入库 → 显微镜拍照 → LLM 识别 → 校验提交 → 结果上报」的一体化桌面工具。
1. 项目概述
本系统为 PCBA reel 卷盘扫描识别 工具,核心链路为:
- 通过串口/条码枪输入大板码(
|分隔的多字段编码); - 从大板码中解析出 ReelID(第 7 个字段),末尾
MSL自动替换为MSD; - 可选调用入库 API 将大板码入 WMS 仓库;
- 调用 Web API 查询该 ReelID 对应的行数;
- 通过显微镜截图 API 获取图片(或从本地文件夹匹配);
- 调用 LLM(Qwen3.6-27B) 识别图片中的文字;
- 识别结果提交到 Web API 校验;
- 校验通过/失败后,向 QMS 碑文结果接口 上报
OK/NG; - 全过程记录日志与提交统计(支持导出 Excel)。
2. 目录结构
| 文件 | 职责 |
|---|---|
主入口.py |
PyQt5 GUI 主程序:窗口、事件、业务流程编排、统计对话框 |
LLM.py |
LLM 图片识别接口(base64 编码 → OpenAI 兼容接口 → JSON 解析) |
LLM1.py |
LLM 识别增强变体(含文字方向 orientation 检测,实验性) |
ThreadWorkers.py |
后台工作线程:LLM / API / 提交 / 碑文上报 / 入库 |
WebApiClient.py |
HTTP API 客户端封装(GET/POST/PUT/DELETE + Session 复用) |
getPicByUrl.py |
显微镜图片抓取(HTTP → Base64 解码 → OpenCV → 保存本地) |
SerialCom.py |
串口通信(连接、持续读取、回调) |
ConfigManager.py |
配置读写(app_config.json,带默认值合并) |
LogManager.py |
日志管理(按天轮转、UTF-8、自动清理过期日志) |
StatisticsDB.py |
SQLite 提交统计(stats.db,按 ReelID 聚合成功/失败计数) |
app_config.json |
运行时配置文件 |
主入口.spec |
PyInstaller 打包脚本(含 conda DLL 自动收集) |
3. 总体架构
系统采用 分层架构:表现层(PyQt5)→ 业务编排层(主窗口)→ 服务层(API/LLM/串口/日志/统计)→ 数据层(JSON 配置 + SQLite + 本地图片)。
4. 核心模块详解
4.1 主入口 主入口.py(约 2600 行)
关键类:
| 类 | 职责 |
|---|---|
MainWindow(QMainWindow) |
主窗口,承载全部 UI 与业务编排 |
ReelIDLineEdit(QLineEdit) |
大板码输入框(200ms 防抖缓冲,屏蔽手动按键,支持条码枪扫描与 Ctrl+V) |
SerialReaderThread(QThread) |
串口读取线程(与 SerialCom 协作) |
StatsDialog(QDialog) |
提交统计对话框(表格 + 柱状图 + 导出 Excel) |
OCRToastMessage / ToastMessage |
黄色 Toast 提示 |
XianweijingCaptureTask(QRunnable) |
显微镜截图异步任务 |
MainWindow 运行时状态:
- 后台线程持有者:
serial_thread、api_worker、llm_worker、submit_worker、beiweng_worker、warehouse_worker - 业务状态:
current_reelid、current_row_nums、current_ocr_texts、input_fields(动态输入框)、picture_base64_map(ReelID→Base64 缓存) - 超时定时器:
ocr_timeout_timer(单次触发,默认 60s)
关键方法(业务流):
| 方法 | 职责 |
|---|---|
on_reelid_detected / on_reelid_manual_input |
大板码输入 → 解析 ReelID → 可选入库 |
_start_ocr_flow |
记录 ReelID → 清空结果 → 调用 API 查询 |
query_api / on_api_result |
查询行数 → 创建动态输入框 → 触发抓图/识别 |
start_ocr_processing / on_ocr_finished |
启动 LLM 识别 → 填充结果 → 自动提交 |
submit_ocr_results / on_submit_result |
提交校验 → 更新统计 → 触发碑文上报 |
report_beiweng_result |
向 QMS 碑文接口上报 OK/NG |
show_stats_dialog |
打开统计对话框 |
clean_expired_images |
按 pic_keep_days 清理过期图片 |
4.2 LLM 识别 LLM.py
- 使用 OpenAI 兼容接口(
http://10.32.36.25:11434/v1/chat/completions),模型Qwen3.6-27B(运行时可配置)。 query_llm_for_image(image_path, instruction, config):图片 base64 编码 → 构造 multimodal 请求 → 解析响应。extract_json_from_llm_response:三级降级解析(```json代码块 → 整体 JSON → 正则提取{...}→ 正则分词兜底)。analyze_batch_images:批量图片识别。
4.3 后台线程 ThreadWorkers.py
| 线程类 | 信号 | 职责 |
|---|---|---|
LLMWorkerThread |
llm_started/finished/error |
LLM 识别(延迟导入 LLM 模块),校验行数是否达标 |
ApiWorker |
api_result/api_error |
查询 ReelID 行数 |
SubmitDataWorker |
submit_result/submit_error |
提交 OCR 结果(携带 is_manual 标记) |
BeiwengResultSubmitWorker |
submit_result/submit_error |
上报碑文 OK/NG |
WarehouseWorker |
warehouse_result/warehouse_error |
大板码入库 |
所有网络请求均在后台线程执行,通过
pyqtSignal回传主线程,避免阻塞 GUI。
4.4 其他模块
WebApiClient.py:requests.Session复用,统一异常处理,支持上下文管理器。getPicByUrl.py:fetch_and_save_image(url, save_path)兼容text/plain与 JSON 两种响应,解码 Base64 为 OpenCV 图像并保存,返回(BGR 图像, 原始 base64)。SerialCom.py:SerialCommunication(底层连接)+ContinuousSerialReader(持续读取 + 回调)。ConfigManager.py:JSON 配置读写,加载时用默认值补齐缺失键。LogManager.py:TimedRotatingFileHandler(midnight 轮转)+ UTF-8 + 自动清理log_keep_days天前的日志。StatisticsDB.py:SQLite 表submission_stats,字段reelid(主键)/manual_success/manual_fail/auto_success/auto_fail,update_stats使用ON CONFLICT DO UPDATE累加计数。
5. 端到端业务流程
6. 线程与异步模型
- 串口读取:
SerialReaderThread(后台QThread)持续读取,回调on_serial_data。 - 网络请求:全部通过独立
QThread(*Worker)执行,信号槽回传结果,不阻塞 GUI 线程。 - 显微镜截图:
QThreadPool.globalInstance()执行XianweijingCaptureTask(QRunnable),通过QMetaObject.invokeMethod(QueuedConnection)线程安全回传。 - 超时保护:LLM 识别使用
ocr_timeout_timer(单次触发,ocr_timeout默认 60s)。 - 延迟导入:
getPicByUrl(cv2/numpy ~1.5s)、LLM、PyQt5.QtChart均按需导入,缩短启动时间。
7. 配置项(app_config.json)
| 键 | 默认值 | 说明 |
|---|---|---|
webapi_url |
http://10.58.2.106:8000 |
查询/提交 Web API 基础地址 |
api_path_getrownums |
.../getrownums |
查询行数接口 |
api_path_add |
.../add |
提交校验接口 |
shot_url |
http://127.0.0.1/shoot |
显微镜截图接口 |
folder_path |
— | 本地图片文件夹 |
port / baudrate |
COM1 / 115200 |
串口参数 |
auto_ocr / auto_submit |
true / true |
自动识别 / 自动提交 |
enable_xianweijing_capture |
true |
启用显微镜截图 |
enable_warehouse |
true |
启用入库 |
warehouse_api_url / warehouse_location |
— / E9-2F |
入库接口 / 货架位置 |
beiwen_result_url |
http://qms.luxshare-ict.com:8081/... |
碑文结果上报接口 |
enable_beiweng_report |
true |
启用碑文上报 |
llm_url / llm_token / llm_model |
Ollama 兼容接口 | LLM 服务配置 |
ocr_timeout |
60 |
LLM 识别超时(秒) |
log_keep_days |
7 |
日志保留天数 |
pic_keep_days |
3 |
图片清理(0 = 不清理) |
8. 外部依赖与接口
| 外部系统 | 接口 | 用途 |
|---|---|---|
| WMS 仓库 | POST /api/materialcenter/anonymous/v1/reel/addrid |
大板码入库(code/rid/location) |
| 物料中心 | POST .../ReelStatusInscriptionAnonymous/getrownums |
查询 ReelID 行数 |
| 物料中心 | POST .../ReelStatusInscriptionAnonymous/add |
提交碑文内容校验 |
| 显微镜/高拍仪 | GET http://127.0.0.1/shoot |
获取图片(返回 Base64) |
| QMS | POST /API/IQCData/BeiWenResultPort |
上报 OK/NG 结果 |
| LLM 服务 | POST .../v1/chat/completions(Ollama) |
图片文字识别 |
9. 关键技术点
- 大板码解析:按
|分割取第 7 字段作为 ReelID;末尾字段含MSL时替换为MSD。 - 防抖输入:
ReelIDLineEdit200ms 防抖缓冲,兼容条码枪连扫与 Ctrl+V 粘贴。 - 图片文件夹回退:主目录不可写时自动在应用目录创建
output/并回写配置。 - Base64 缓存:
picture_base64_map缓存图片 Base64,碑文上报时优先使用,缺失时回退读取本地文件,上报后清除。 - 统计导出:
StatsDialog支持将统计信息导出为 Excel(openpyxl写.xlsx,不可用时回退 CSV)。 - 启动优化:Splash 进度 + 延迟导入 + 串口扫描延迟到首帧渲染后。
- 配置防误写:
load_config中使用blockSignals包裹setChecked,防止stateChanged触发意外保存。
10. 已知说明
LLM1.py为增强变体(含文字方向检测orientation),当前主流程使用LLM.py。LLM.py内置模型为Qwen3.8-27B,实际运行时以配置llm_model(Qwen3.6-27B)为准。备份/、build/、dist/为历史打包产物与备份,非运行时源码。