chore: 初始化四个子项目骨架目录结构

- 新增 mingxi-capture(PyQt5桌面程序)目录骨架
- 新增 mingxi-vision(FastAPI推理服务)目录骨架
- 新增 mingxi-backend(Django业务后端)目录骨架
- 新增 mingxi-frontend(Vue3管理后台)目录骨架
- 迁入 docs/(技术设计文档和PCB调研资料)
- 补充 .gitignore(模型文件、Node.js依赖、运行时数据库)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ila
2026-05-24 17:21:17 +08:00
co-authored by Claude Sonnet 4.6
parent bdd275054b
commit 37a4c13799
66 changed files with 2377 additions and 178 deletions
+200 -176
View File
@@ -1,176 +1,200 @@
# ---> Python # ---> 明析平台专用
# Byte-compiled / optimized / DLL files
__pycache__/ # 模型文件(体积大,不进版本库)
*.py[cod] *.pt
*$py.class *.onnx
mingxi-vision/models/
# C extensions
*.so # 前置机本地数据
mingxi-capture/queue.db
# Distribution / packaging mingxi-capture/config.ini
.Python
build/ # 大文件
develop-eggs/ references/*.mkv
dist/ references/*.mp4
downloads/
eggs/ # IDE
.eggs/ .vscode/
lib/ .idea/
lib64/
parts/ # ---> Node.js (mingxi-frontend)
sdist/ node_modules/
var/ mingxi-frontend/dist/
wheels/ mingxi-frontend/.env.local
share/python-wheels/
*.egg-info/ # ---> Python
.installed.cfg # Byte-compiled / optimized / DLL files
*.egg __pycache__/
MANIFEST *.py[cod]
*$py.class
# PyInstaller
# Usually these files are written by a python script from a template # C extensions
# before PyInstaller builds the exe, so as to inject date/other infos into it. *.so
*.manifest
*.spec # Distribution / packaging
.Python
# Installer logs build/
pip-log.txt develop-eggs/
pip-delete-this-directory.txt dist/
downloads/
# Unit test / coverage reports eggs/
htmlcov/ .eggs/
.tox/ lib/
.nox/ lib64/
.coverage parts/
.coverage.* sdist/
.cache var/
nosetests.xml wheels/
coverage.xml share/python-wheels/
*.cover *.egg-info/
*.py,cover .installed.cfg
.hypothesis/ *.egg
.pytest_cache/ MANIFEST
cover/
# PyInstaller
# Translations # Usually these files are written by a python script from a template
*.mo # before PyInstaller builds the exe, so as to inject date/other infos into it.
*.pot *.manifest
*.spec
# Django stuff:
*.log # Installer logs
local_settings.py pip-log.txt
db.sqlite3 pip-delete-this-directory.txt
db.sqlite3-journal
# Unit test / coverage reports
# Flask stuff: htmlcov/
instance/ .tox/
.webassets-cache .nox/
.coverage
# Scrapy stuff: .coverage.*
.scrapy .cache
nosetests.xml
# Sphinx documentation coverage.xml
docs/_build/ *.cover
*.py,cover
# PyBuilder .hypothesis/
.pybuilder/ .pytest_cache/
target/ cover/
# Jupyter Notebook # Translations
.ipynb_checkpoints *.mo
*.pot
# IPython
profile_default/ # Django stuff:
ipython_config.py *.log
local_settings.py
# pyenv db.sqlite3
# For a library or package, you might want to ignore these files since the code is db.sqlite3-journal
# intended to run in multiple environments; otherwise, check them in:
# .python-version # Flask stuff:
instance/
# pipenv .webassets-cache
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies # Scrapy stuff:
# having no cross-platform support, pipenv may install dependencies that don't work, or not .scrapy
# install all needed dependencies.
#Pipfile.lock # Sphinx documentation
docs/_build/
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. # PyBuilder
# This is especially recommended for binary packages to ensure reproducibility, and is more .pybuilder/
# commonly ignored for libraries. target/
#uv.lock
# Jupyter Notebook
# poetry .ipynb_checkpoints
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more # IPython
# commonly ignored for libraries. profile_default/
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control ipython_config.py
#poetry.lock
# pyenv
# pdm # For a library or package, you might want to ignore these files since the code is
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. # intended to run in multiple environments; otherwise, check them in:
#pdm.lock # .python-version
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control. # pipenv
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
.pdm.toml # However, in case of collaboration, if having platform-specific dependencies or dependencies
.pdm-python # having no cross-platform support, pipenv may install dependencies that don't work, or not
.pdm-build/ # install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ # UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# Celery stuff # This is especially recommended for binary packages to ensure reproducibility, and is more
celerybeat-schedule # commonly ignored for libraries.
celerybeat.pid #uv.lock
# SageMath parsed files # poetry
*.sage.py # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# Environments # commonly ignored for libraries.
.env # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
.venv #poetry.lock
env/
venv/ # pdm
ENV/ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
env.bak/ #pdm.lock
venv.bak/ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# Spyder project settings # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.spyderproject .pdm.toml
.spyproject .pdm-python
.pdm-build/
# Rope project settings
.ropeproject # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# mkdocs documentation
/site # Celery stuff
celerybeat-schedule
# mypy celerybeat.pid
.mypy_cache/
.dmypy.json # SageMath parsed files
dmypy.json *.sage.py
# Pyre type checker # Environments
.pyre/ .env
.venv
# pytype static type analyzer env/
.pytype/ venv/
ENV/
# Cython debug symbols env.bak/
cython_debug/ venv.bak/
# PyCharm # Spyder project settings
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can .spyderproject
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore .spyproject
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # Rope project settings
#.idea/ .ropeproject
# Ruff stuff: # mkdocs documentation
.ruff_cache/ /site
# PyPI configuration file # mypy
.pypirc .mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
+2 -2
View File
@@ -1,3 +1,3 @@
# mingxi_platform # mingxi_platform
明晰平台 明晰平台
@@ -0,0 +1,143 @@
# PCB 与 HRIPCB 数据集差异分析
> 分析时间:2026-05-07
> 分析对象:`pcb/`(TCC PCB Dataset)与 `HRIPCB/`(HRIPCB Dataset)
---
## 1. 核心差异一览
| 维度 | PCB (TCC) | HRIPCB |
|------|-----------|--------|
| **来源** | Roboflow `tcc-r4j2r/pcb-la0tj` | Roboflow `ma007/hripcb` |
| **发布时间** | 2025-12-11 | 2023-04-16 |
| **规模** | 7,971 张 | 1,386 张 |
| **导出预处理** | **Resize to 240×240 (Fit within)** | **Resize to 640×640 (Stretch)** |
| **缺陷密度** | **~2.0 框/张图** | **~4.2 框/张图** |
| **同图变体数** | 每张原始图 **3~6 个变体** | 每张原始图 **2 个变体** |
| **License** | CC BY 4.0 | CC BY 4.0 |
---
## 2. 关键差异详解
### 2.1 图像分辨率与预处理策略不同
这是两个数据集最本质的区别。
- **PCB**:被 Roboflow 预处理为 **240×240(Fit within,保持比例 + 填充)**。文件名中虽带有 `_600` 字样(暗示原始采集分辨率为 600×600),但实际导出图已缩放至 240×240。
- **HRIPCB**:被预处理为 **640×640(Stretch,直接拉伸)**。原始图被强制拉成 640×640,可能存在轻微形变。
**训练影响**:YOLOv8 训练时会统一 `imgsz=640`,两者标签均为归一化坐标(0~1),可直接混用。但 PCB 从 240 放大到 640 时细节损失会比 HRIPCB 更明显,小目标特征可能更模糊。
### 2.2 缺陷密度不同
| 数据集 | Split | 图片数 | 缺陷框数 | 平均框/图 |
|--------|-------|--------|----------|-----------|
| PCB | train | 6,346 | 12,964 | **2.04** |
| PCB | valid | 798 | 1,588 | **1.99** |
| PCB | test | 827 | 1,660 | **2.01** |
| HRIPCB | train | 1,108 | 4,667 | **4.21** |
| HRIPCB | valid | 139 | 583 | **4.19** |
| HRIPCB | test | 139 | 576 | **4.14** |
- **PCB**:平均每张图约 **2.0 个缺陷框**,整体相对稀疏。
- **HRIPCB**:平均每张图约 **4.2 个缺陷框**,缺陷更密集,有些图可能包含同一类型的多个缺陷实例。
**结论**:HRIPCB 的缺陷密度约为 PCB 的 **2 倍**。这在训练时意味着 HRIPCB 样本对模型学习"密集小目标共存"场景的贡献更大。
### 2.3 数据构成方式不同
**PCB 文件名特征**:
```
l_light_01_missing_hole_01_1_600_jpg.rf.xxx.jpg
l_light_01_missing_hole_01_2_600_jpg.rf.xxx.jpg
l_light_01_missing_hole_01_3_600_jpg.rf.xxx.jpg
```
- 前缀 `l_light_` 表明采集时控制了光照条件(left light / 左侧光源)。
- `_1_`、`_2_`、`_3_` 后缀表明**同一张缺陷样本有多个变体**(可能是不同光照、角度或采集参数)。
- 数据冗余度更高,同类缺陷的重复样本多。
**HRIPCB 文件名特征**:
```
01_missing_hole_01_jpg.rf.xxx.jpg
01_missing_hole_01_jpg.rf.yyy.jpg
```
- 命名更简洁,每张原始图仅有 **2 个 Roboflow 自动变体**(后缀 hash 不同)。
- 样本独立性更强,重复度低。
### 2.4 总标注实例数
| 数据集 | 图片数 | 总缺陷实例数 |
|--------|--------|-------------|
| PCB | 7,971 | ~16,212 |
| HRIPCB | 1,386 | ~4,826 |
| **合并后** | **9,357** | **~21,038** |
---
## 3. 对模型训练的影响
| 影响项 | 说明 |
|--------|------|
| **分辨率差距** | PCB 从 240 上采样到 640 会丢失细节,小目标检测可能受一定影响;HRIPCB 原生 640,细节保留更完整 |
| **密度差异** | HRIPCB 更密集的标注有助于模型学习"多缺陷共存"场景,对实际产线更有价值 |
| **重复样本** | PCB 中同一缺陷的多个变体相当于轻度数据增强,有助于提升光照/角度鲁棒性 |
| **来源互补** | PCB 偏"受控光照实验"风格,HRIPCB 偏"标准工业样本"风格,合并后泛化能力更强 |
| **类别分布** | 两者类别定义完全一致(6 类缺陷),标签格式均为标准 YOLOv8 归一化坐标,可直接合并 |
---
## 4. 使用建议
### 4.1 基础用法
直接使用 `dataset.py` 合并后训练:
```bash
python dataset.py merge --src pcb HRIPCB --dst merged
```
合并后共 **9,357 张图、约 21,038 个缺陷实例**,数据量足够支撑 YOLOv8n/s 的训练。
### 4.2 进阶用法:重新划分
如果担心 PCB 占比过高(85%)导致模型过度偏向 PCB 的数据风格,可以打乱后重新按 8:1:1 划分:
```bash
python dataset.py merge --src pcb HRIPCB --dst merged --rebalance --train 0.8 --val 0.1 --test 0.1
```
### 4.3 训练参数建议
针对两个数据集的分辨率差异,建议:
```yaml
# train.yaml
model: yolov8n.pt
data: D:/github2026/pcb-defect-detection/merged/data.yaml
imgsz: 640 # 统一统一到 640,补偿 PCB 的 240 上采样损失
batch: 16 # RTX 4060 8GB 安全值
epochs: 100
amp: True # 混合精度必开
patience: 20 # 早停
```
### 4.4 如果精度不够
可考虑以下策略:
1. **分阶段训练**:先用 PCB 预训练,再用 HRIPCB fine-tune(HRIPCB 密度高、标注更"干净")。
2. **调整样本权重**:在 Ultralytics 中通过 `sample_weights` 或自定义 dataloader 提升 HRIPCB 样本的采样概率。
3. **数据清洗**:检查 PCB 中 240×240 上采样后是否出现模糊/不可辨认的样本,必要时剔除低质量数据。
---
## 5. 附:数据来源链接
- **PCB (TCC)**:`https://universe.roboflow.com/tcc-r4j2r/pcb-la0tj/dataset/1`
- **HRIPCB**:`https://universe.roboflow.com/ma007/hripcb/dataset/1`
+826
View File
@@ -0,0 +1,826 @@
# mingxi-vision 技术架构与设计分析
> 版本:v1.0 · 日期:2026-05-24
> 硬件约束:联想 Y7000P · RTX 4060 Laptop 8GB · Python 3.8 · Windows 11
> 上游依赖:`yolo_classification_system/yolo8/inference.py`(迁移基础)
---
## 1. 定位与职责边界
`mingxi-vision` 是整个明析平台的**推理微服务**,职责单一:
```
接收图像 → 前处理 → 模型推理 → 后处理 → 返回结构化缺陷JSON
```
它**不**做的事:
- 不持久化数据(无数据库依赖)
- 不做鉴权(内网服务,由 backend 代理)
- 不管理产线/批次业务(backend 的职责)
- 不直接控制相机(capture 的职责)
---
## 2. 框架选型:FastAPI
### 为什么不用 Django
现有系统用 Django,但推理服务有不同的特征:
| 维度 | Django(现有) | FastAPI(选用) |
|------|--------------|----------------|
| 定位 | 全功能Web框架 | 轻量API框架 |
| 启动时间 | ~3-5s | ~0.5s |
| ORM/DB | 必须配置 | 无需数据库 |
| 异步支持 | 有限(Django 4+ 部分支持) | 原生 async/await |
| 自动文档 | 需要额外配置 | 内置 /docs (Swagger) |
| 推理场景适配 | 过重 | 刚好合适 |
### Python 3.8 的约束
FastAPI 在 Python 3.8 下完全可用,需注意:
- 类型注解用 `Optional[X]` 而非 `X | None`(3.10+ 语法)
- `from __future__ import annotations` 可缓解部分问题
- Pydantic v1(随 FastAPI 早期版本)在 3.8 稳定
```
# requirements.txt 核心版本锁定
fastapi==0.104.1 # 3.8 兼容的最后稳定版系列
uvicorn[standard]==0.24.0
pydantic==1.10.13 # v1,Python 3.8 最稳定
ultralytics==8.0.235 # 训练阶段用
onnxruntime-gpu==1.16.3 # 推理部署阶段用
opencv-python-headless==4.8.1.78
numpy==1.24.4 # 3.8 + torch 2.0 兼容版本
python-multipart==0.0.6 # FastAPI 文件上传必须
```
---
## 3. 双运行时策略
这是 mingxi-vision 最核心的设计决策。
### 问题
- **训练阶段**:用 `ultralytics` 的 `.pt` 格式,便于迭代和验证
- **部署阶段**:用 `.onnx` 格式,跨平台、无需安装 PyTorch、性能更稳定
- 两套格式的推理 API 有差异,需要统一抽象
### 解决方案:运行时适配器(Adapter 模式)
```
┌─────────────────────────────┐
│ InferenceEngine │ ← 统一接口
│ detect(image) → [Defect] │
└──────────┬──────────────────┘
│ 根据配置选择
┌───────────────┴────────────────┐
▼ ▼
UltralyticsAdapter OnnxRuntimeAdapter
(.pt 文件,训练验证用) (.onnx 文件,生产部署用)
ultralytics.YOLO onnxruntime.InferenceSession
```
### 代码设计
```python
# engine/base.py
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List
import numpy as np
@dataclass
class DefectBox:
class_id: int
class_name: str
class_name_zh: str
confidence: float
severity: str # fatal / major / minor / rework / none
box_xyxy: List[float] # [x1, y1, x2, y2],像素坐标
@dataclass
class DetectResult:
defects: List[DefectBox]
duration_ms: float
image_width: int
image_height: int
model_version: str
@property
def defect_count(self) -> int:
return len(self.defects)
@property
def max_severity(self) -> str:
order = ["fatal", "major", "minor", "rework", "none"]
found = {d.severity for d in self.defects}
for s in order:
if s in found:
return s
return "none"
@property
def avg_confidence(self):
if not self.defects:
return None
return round(sum(d.confidence for d in self.defects) / len(self.defects), 4)
class BaseInferenceEngine(ABC):
@abstractmethod
def detect(self, image: np.ndarray, conf: float) -> DetectResult:
...
@abstractmethod
def warmup(self) -> None:
"""启动时预热,避免第一次推理延迟"""
...
```
```python
# engine/ultralytics_adapter.py
import time
import numpy as np
from .base import BaseInferenceEngine, DefectBox, DetectResult
from .defect_classes import DEFECT_CLASSES
class UltralyticsAdapter(BaseInferenceEngine):
"""开发/训练验证阶段使用"""
def __init__(self, weights_path: str):
from ultralytics import YOLO
self._model = YOLO(weights_path)
self._weights_path = weights_path
def warmup(self):
dummy = np.zeros((640, 640, 3), dtype=np.uint8)
self._model.predict(source=dummy, verbose=False, conf=0.01)
def detect(self, image: np.ndarray, conf: float = 0.45) -> DetectResult:
h, w = image.shape[:2]
t0 = time.perf_counter()
results = self._model.predict(source=image, verbose=False, conf=conf)
duration_ms = (time.perf_counter() - t0) * 1000
defects = []
result = results[0]
if result.boxes is not None and len(result.boxes):
xyxy = result.boxes.xyxy.cpu().numpy()
confs = result.boxes.conf.cpu().numpy()
clsids = result.boxes.cls.cpu().numpy()
for i in range(len(xyxy)):
cid = int(clsids[i])
meta = DEFECT_CLASSES.get(cid, {
"name": str(cid), "zh": str(cid), "severity": "minor"
})
defects.append(DefectBox(
class_id=cid,
class_name=meta["name"],
class_name_zh=meta["zh"],
confidence=round(float(confs[i]), 4),
severity=meta["severity"],
box_xyxy=[round(float(x), 1) for x in xyxy[i]],
))
return DetectResult(
defects=defects,
duration_ms=round(duration_ms, 1),
image_width=w,
image_height=h,
model_version=str(self._weights_path),
)
```
```python
# engine/onnx_adapter.py
import time
import cv2
import numpy as np
import onnxruntime as ort
from .base import BaseInferenceEngine, DefectBox, DetectResult
from .defect_classes import DEFECT_CLASSES
class OnnxRuntimeAdapter(BaseInferenceEngine):
"""生产部署阶段使用,无 PyTorch 依赖"""
def __init__(self, model_path: str, device: str = "cuda"):
providers = (
["CUDAExecutionProvider", "CPUExecutionProvider"]
if device == "cuda"
else ["CPUExecutionProvider"]
)
self._session = ort.InferenceSession(model_path, providers=providers)
self._input_name = self._session.get_inputs()[0].name
self._input_shape = self._session.get_inputs()[0].shape # [1,3,640,640]
self._imgsz = self._input_shape[2] # 通常 640
self._model_path = model_path
def warmup(self):
dummy = np.zeros(
(1, 3, self._imgsz, self._imgsz), dtype=np.float32
)
self._session.run(None, {self._input_name: dummy})
def _preprocess(self, image: np.ndarray):
"""BGR → RGB → letterbox → NCHW float32 [0,1]"""
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
img, ratio, (dw, dh) = self._letterbox(img, self._imgsz)
img = img.astype(np.float32) / 255.0
img = np.transpose(img, (2, 0, 1)) # HWC → CHW
img = np.expand_dims(img, 0) # CHW → NCHW
return img, ratio, dw, dh
@staticmethod
def _letterbox(img, new_size=640):
h, w = img.shape[:2]
ratio = min(new_size / h, new_size / w)
nh, nw = int(h * ratio), int(w * ratio)
img = cv2.resize(img, (nw, nh), interpolation=cv2.INTER_LINEAR)
dw = (new_size - nw) / 2
dh = (new_size - nh) / 2
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right,
cv2.BORDER_CONSTANT, value=(114, 114, 114))
return img, ratio, dw, dh
def _postprocess(self, outputs, orig_h, orig_w, ratio, dw, dh, conf_thres):
"""YOLOv8 ONNX 输出解码:[1, 84, 8400] → DefectBox列表"""
pred = outputs[0][0] # [84, 8400]
pred = pred.T # [8400, 84]
boxes = pred[:, :4] # cx,cy,w,h
scores = pred[:, 4:] # [8400, num_classes]
class_ids = np.argmax(scores, axis=1)
confidences = scores[np.arange(len(scores)), class_ids]
mask = confidences > conf_thres
boxes, class_ids, confidences = (
boxes[mask], class_ids[mask], confidences[mask]
)
if len(boxes) == 0:
return []
# cx,cy,w,h → x1,y1,x2,y2
x1 = boxes[:, 0] - boxes[:, 2] / 2
y1 = boxes[:, 1] - boxes[:, 3] / 2
x2 = boxes[:, 0] + boxes[:, 2] / 2
y2 = boxes[:, 1] + boxes[:, 3] / 2
# 去除 letterbox padding,还原到原始坐标
x1 = np.clip((x1 - dw) / ratio, 0, orig_w)
y1 = np.clip((y1 - dh) / ratio, 0, orig_h)
x2 = np.clip((x2 - dw) / ratio, 0, orig_w)
y2 = np.clip((y2 - dh) / ratio, 0, orig_h)
# NMS
nms_ids = cv2.dnn.NMSBoxes(
np.stack([x1, y1, x2 - x1, y2 - y1], axis=1).tolist(),
confidences.tolist(), conf_thres, iou_threshold=0.45
)
if len(nms_ids) == 0:
return []
defects = []
for idx in nms_ids.flatten():
cid = int(class_ids[idx])
meta = DEFECT_CLASSES.get(cid, {
"name": str(cid), "zh": str(cid), "severity": "minor"
})
defects.append(DefectBox(
class_id=cid,
class_name=meta["name"],
class_name_zh=meta["zh"],
confidence=round(float(confidences[idx]), 4),
severity=meta["severity"],
box_xyxy=[
round(float(x1[idx]), 1), round(float(y1[idx]), 1),
round(float(x2[idx]), 1), round(float(y2[idx]), 1),
],
))
return defects
def detect(self, image: np.ndarray, conf: float = 0.45) -> DetectResult:
orig_h, orig_w = image.shape[:2]
inp, ratio, dw, dh = self._preprocess(image)
t0 = time.perf_counter()
outputs = self._session.run(None, {self._input_name: inp})
duration_ms = (time.perf_counter() - t0) * 1000
defects = self._postprocess(outputs, orig_h, orig_w, ratio, dw, dh, conf)
return DetectResult(
defects=defects,
duration_ms=round(duration_ms, 1),
image_width=orig_w,
image_height=orig_h,
model_version=str(self._model_path),
)
```
---
## 4. 模型管理:单例 + 启动加载
### 问题根源
现有 `inference.py` 的 `_MODEL_CACHE` 是进程级字典,首次请求时才加载模型(懒加载)。对推理服务来说,这会导致第一个请求有数秒延迟,在演示现场是灾难性的。
### 解决方案:FastAPI 生命周期钩子
```python
# engine/loader.py
from typing import Optional
from .base import BaseInferenceEngine
from config import settings
_engine: Optional[BaseInferenceEngine] = None
def get_engine() -> BaseInferenceEngine:
if _engine is None:
raise RuntimeError("推理引擎未初始化,请检查启动日志")
return _engine
def init_engine() -> BaseInferenceEngine:
global _engine
if settings.runtime == "onnx":
from .onnx_adapter import OnnxRuntimeAdapter
_engine = OnnxRuntimeAdapter(
model_path=settings.model_path,
device=settings.device,
)
else:
from .ultralytics_adapter import UltralyticsAdapter
_engine = UltralyticsAdapter(weights_path=settings.model_path)
_engine.warmup() # 预热,消除第一次推理的延迟
return _engine
```
```python
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from engine.loader import init_engine
from api.detect import router as detect_router
from api.health import router as health_router
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时加载模型
engine = init_engine()
print(f"[mingxi-vision] 引擎就绪: {engine.__class__.__name__}")
yield
# 关闭时释放资源(ONNX session会自动GC)
app = FastAPI(
title="明析推理服务",
description="PCB缺陷检测推理接口",
version="1.0.0",
lifespan=lifespan,
)
app.include_router(detect_router, prefix="/api")
app.include_router(health_router, prefix="/api")
```
---
## 5. 图像接收与前处理
### 接收来源
mingxi-vision 接受两种图像来源,用同一个端点处理:
```python
# api/detect.py
import cv2
import numpy as np
from fastapi import APIRouter, File, Form, UploadFile, HTTPException
from fastapi.responses import JSONResponse
from engine.loader import get_engine
from engine.annotator import draw_boxes
from schema import DetectResponse
import base64, uuid
router = APIRouter()
@router.post("/detect", response_model=DetectResponse)
async def detect(
image: UploadFile = File(...),
conf: float = Form(default=0.45, ge=0.01, le=0.99),
line_id: str = Form(default=""),
batch_id: str = Form(default=""),
return_annotated: bool = Form(default=False),
):
# 1. 读取图像字节
raw = await image.read()
if len(raw) > 20 * 1024 * 1024: # 20MB 上限
raise HTTPException(status_code=413, detail="图像文件过大(上限20MB)")
# 2. 解码为 numpy BGR
arr = np.frombuffer(raw, dtype=np.uint8)
img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
if img is None:
raise HTTPException(status_code=422, detail="无法解码图像,请检查文件格式")
# 3. 推理
engine = get_engine()
result = engine.detect(img, conf=conf)
# 4. 可选:返回标注图
annotated_b64 = None
if return_annotated:
annotated = draw_boxes(img.copy(), result.defects)
_, buf = cv2.imencode(".jpg", annotated, [cv2.IMWRITE_JPEG_QUALITY, 85])
annotated_b64 = base64.b64encode(buf.tobytes()).decode()
return DetectResponse(
task_id=str(uuid.uuid4()),
line_id=line_id,
batch_id=batch_id,
duration_ms=result.duration_ms,
image_width=result.image_width,
image_height=result.image_height,
defect_count=result.defect_count,
max_severity=result.max_severity,
avg_confidence=result.avg_confidence,
defects=[d.__dict__ for d in result.defects],
annotated_image_b64=annotated_b64,
model_version=result.model_version,
)
```
### 中文路径问题(继承自现有代码)
现有 `inference.py` 已有 `_copy_to_ascii_temp_input()` 解决中文路径导致 OpenCV 无法读图的问题。`mingxi-vision` 改用 **内存读取**(`np.frombuffer` + `cv2.imdecode`),从源头消除路径问题,不再需要这个 workaround。
---
## 6. 后处理:标注图生成
```python
# engine/annotator.py
import cv2
import numpy as np
from typing import List
from .base import DefectBox
SEVERITY_COLORS = {
"fatal": (0, 0, 220), # 红(BGR)
"major": (0, 128, 255), # 橙
"minor": (0, 215, 255), # 黄
"rework": (255, 165, 0 ), # 蓝
"none": (180, 180, 180), # 灰
}
def draw_boxes(image: np.ndarray, defects: List[DefectBox]) -> np.ndarray:
for d in defects:
x1, y1, x2, y2 = [int(v) for v in d.box_xyxy]
color = SEVERITY_COLORS.get(d.severity, (180, 180, 180))
cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)
label = f"{d.class_name_zh} {d.confidence:.2f}"
(tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.55, 1)
cv2.rectangle(image, (x1, y1 - th - 6), (x1 + tw + 4, y1), color, -1)
cv2.putText(
image, label, (x1 + 2, y1 - 4),
cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 1, cv2.LINE_AA
)
return image
```
---
## 7. 缺陷类别定义
```python
# engine/defect_classes.py
# 与 HRIPCB 数据集的 6 类对齐,扩展至 10 类(含真实产线样本迁移学习后)
DEFECT_CLASSES = {
# ── HRIPCB 基础 6 类(公开数据集,开箱即用)──
0: {"name": "missing_hole", "zh": "缺孔", "severity": "fatal"},
1: {"name": "mouse_bite", "zh": "鼠咬", "severity": "major"},
2: {"name": "open_circuit", "zh": "断路", "severity": "fatal"},
3: {"name": "short_circuit", "zh": "短路", "severity": "fatal"},
4: {"name": "spur", "zh": "毛刺", "severity": "minor"},
5: {"name": "spurious_copper", "zh": "余铜", "severity": "major"},
# ── 扩展类(真实产线样本微调后启用)──
6: {"name": "oxidation", "zh": "氧化", "severity": "minor"},
7: {"name": "solder_ball", "zh": "锡珠", "severity": "rework"},
8: {"name": "scratch", "zh": "划痕", "severity": "minor"},
9: {"name": "label_error", "zh": "标签错贴","severity": "rework"},
}
SEVERITY_ORDER = ["fatal", "major", "minor", "rework", "none"]
```
---
## 8. 配置管理
```python
# config.py
import os
from typing import Literal
from pydantic import BaseSettings # pydantic v1
class Settings(BaseSettings):
# 运行时选择
runtime: Literal["ultralytics", "onnx"] = "ultralytics"
model_path: str = "./models/pcb_defect_v1.pt"
device: Literal["cuda", "cpu"] = "cuda"
# 推理默认参数
default_conf: float = 0.45
max_image_size_mb: int = 20
# 服务配置
host: str = "0.0.0.0"
port: int = 8001
workers: int = 1 # 推理服务单 worker,GPU 不支持多进程共享
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()
```
`.env` 文件示例:
```bash
# 开发阶段(ultralytics .pt)
RUNTIME=ultralytics
MODEL_PATH=./models/pcb_defect_v1.pt
DEVICE=cuda
# 生产阶段(ONNX Runtime)
# RUNTIME=onnx
# MODEL_PATH=./models/pcb_defect_v1.onnx
# DEVICE=cuda
```
---
## 9. 健康检查与诊断接口
```python
# api/health.py
import platform
from fastapi import APIRouter
from engine.loader import get_engine
router = APIRouter()
@router.get("/health")
def health():
try:
engine = get_engine()
return {
"status": "ok",
"runtime": engine.__class__.__name__,
"platform": platform.system(),
}
except RuntimeError as e:
return {"status": "error", "detail": str(e)}
@router.get("/health/gpu")
def gpu_info():
"""开发调试用,确认 GPU 是否被正确使用"""
info = {"cuda_available": False, "onnx_providers": []}
try:
import torch
info["cuda_available"] = torch.cuda.is_available()
if torch.cuda.is_available():
info["gpu_name"] = torch.cuda.get_device_name(0)
info["vram_total_gb"] = round(
torch.cuda.get_device_properties(0).total_memory / 1e9, 1
)
except ImportError:
pass
try:
import onnxruntime as ort
info["onnx_providers"] = ort.get_available_providers()
except ImportError:
pass
return info
```
---
## 10. 完整目录结构
```
mingxi-vision/
├── api/
│ ├── __init__.py
│ ├── detect.py # POST /api/detect(核心推理接口)
│ └── health.py # GET /api/health, /api/health/gpu
├── engine/
│ ├── __init__.py
│ ├── base.py # DefectBox, DetectResult, BaseInferenceEngine
│ ├── loader.py # 单例管理 + init_engine()
│ ├── ultralytics_adapter.py # .pt 推理(开发阶段)
│ ├── onnx_adapter.py # .onnx 推理(生产阶段)
│ ├── annotator.py # 在图像上绘制标注框
│ └── defect_classes.py # 缺陷类别 + 等级映射表
├── models/
│ ├── pcb_defect_v1.pt # ultralytics 训练产物(gitignore)
│ └── pcb_defect_v1.onnx # 导出的 ONNX 模型(gitignore)
├── scripts/
│ ├── export_onnx.py # pt → onnx 导出脚本
│ └── benchmark.py # 本地推理性能测试
├── tests/
│ ├── test_detect_api.py
│ └── fixtures/ # 测试用 PCB 图片
├── schema.py # Pydantic 响应模型
├── config.py # Settings(pydantic BaseSettings)
├── main.py # FastAPI app + lifespan
├── requirements.txt
├── .env.example
└── README.md
```
---
## 11. 响应 Schema
```python
# schema.py
from typing import List, Optional
from pydantic import BaseModel
class DefectItem(BaseModel):
class_id: int
class_name: str
class_name_zh: str
confidence: float
severity: str # fatal / major / minor / rework / none
box_xyxy: List[float] # [x1, y1, x2, y2]
class DetectResponse(BaseModel):
task_id: str
line_id: str
batch_id: str
duration_ms: float
image_width: int
image_height: int
defect_count: int
max_severity: str # 本次检测中最高等级缺陷
avg_confidence: Optional[float]
defects: List[DefectItem]
annotated_image_b64: Optional[str] # 仅 return_annotated=true 时有值
model_version: str
```
实际响应示例:
```json
{
"task_id": "a3f1c2d4-...",
"line_id": "LINE-01",
"batch_id": "BAT-20260524-001",
"duration_ms": 41.3,
"image_width": 3072,
"image_height": 2048,
"defect_count": 2,
"max_severity": "fatal",
"avg_confidence": 0.8762,
"defects": [
{
"class_id": 2,
"class_name": "open_circuit",
"class_name_zh": "断路",
"confidence": 0.9134,
"severity": "fatal",
"box_xyxy": [234.5, 891.2, 312.8, 943.7]
},
{
"class_id": 4,
"class_name": "spur",
"class_name_zh": "毛刺",
"confidence": 0.8390,
"severity": "minor",
"box_xyxy": [1204.1, 456.3, 1251.9, 489.0]
}
],
"annotated_image_b64": null,
"model_version": "./models/pcb_defect_v1.pt"
}
```
---
## 12. 性能分析(Y7000P RTX 4060)
### 推理延迟预估
| 运行时 | 模型 | 分辨率 | 单次推理 | 含预处理+后处理 |
|--------|------|--------|---------|--------------|
| ultralytics | YOLOv8n | 640×640 | ~12ms | ~20ms |
| ultralytics | YOLOv8s | 640×640 | ~20ms | ~30ms |
| ONNX Runtime GPU | YOLOv8n | 640×640 | ~8ms | ~15ms |
| ONNX Runtime CPU | YOLOv8n | 640×640 | ~80ms | ~100ms |
> 数据来源:`pcb缺陷检测初步方案.md` 引用的 Y7000P 实测基准
**PCB 后道复判场景需求**:触发模式,每块板约 1-3 秒间隔,15ms 的推理延迟完全满足。
### 显存占用
| 模型 | 显存占用 |
|------|---------|
| YOLOv8n(ultralytics) | ~1.2GB |
| YOLOv8s(ultralytics) | ~2.4GB |
| YOLOv8n(ONNX CUDA EP) | ~0.8GB |
RTX 4060 8GB 剩余 6GB+ 可用,不构成瓶颈。
### 并发限制
```python
# main.py 启动参数
# workers=1:GPU 不支持多进程共享同一 CUDA Context
# 推理天然串行,配合 FastAPI 的 async 处理并发等待队列
uvicorn main:app --host 0.0.0.0 --port 8001 --workers 1
```
PCB 产线触发模式下,单工位请求天然串行,`workers=1` 完全够用。若需多产线并发,在 `mingxi-backend` 侧做请求队列即可。
---
## 13. 模型导出(pt → onnx)
```python
# scripts/export_onnx.py
from ultralytics import YOLO
model = YOLO("./models/pcb_defect_v1.pt")
model.export(
format="onnx",
imgsz=640,
opset=12, # onnxruntime 1.16 兼容
simplify=True, # onnx-simplifier 优化计算图
dynamic=False, # 固定 batch=1,推理服务不需要动态 batch
)
# 产物:pcb_defect_v1.onnx
```
切换到 ONNX 的时机:
```
1. YOLOv8n 在验证集 mAP@0.5 > 0.90 → 导出 ONNX
2. 运行 scripts/benchmark.py 对比两个运行时延迟
3. 确认输出一致后,修改 .env 的 RUNTIME=onnx
4. 重启服务,观察 /api/health 确认切换成功
```
---
## 14. 与现有代码的对应关系
| 现有 `inference.py` 函数 | mingxi-vision 对应位置 | 变化 |
|------------------------|----------------------|------|
| `_MODEL_CACHE` | `engine/loader.py` 单例 | 从懒加载改为启动时加载 |
| `_get_model()` | `loader.init_engine()` | 新增 ONNX 分支 |
| `_copy_to_ascii_temp_input()` | **已删除** | 改用内存读图,无需文件路径 |
| `_parse_result()` | `ultralytics_adapter.detect()` | 增加 severity 映射 |
| `run_image_inference()` | `engine/ultralytics_adapter.py` + `engine/onnx_adapter.py` | 拆成两个运行时 |
| `run_camera_inference()` | **不迁移** | 由 `mingxi-capture` 负责 |
| `run_video_inference()` | **暂不迁移** | 此版本不需要 |
| Django `MEDIA_ROOT` 文件落盘 | **已删除** | 推理服务无状态,不落盘 |
---
## 15. 关键设计决策汇总
| 决策 | 选择 | 理由 |
|------|------|------|
| Web 框架 | FastAPI | 轻量、无需 DB、原生 async、自动文档 |
| 模型运行时 | 双运行时(适配器模式) | 开发用 .pt 方便迭代,生产用 .onnx 稳定 |
| 模型加载时机 | 启动时加载 + warmup | 消除首次推理延迟,路演不卡顿 |
| 图像读取 | 内存读取(np.frombuffer) | 彻底消除中文路径问题 |
| 标注图返回 | 按需(return_annotated=true) | 默认不返回,减少响应体积和编码开销 |
| 并发模型 | workers=1,单进程 | GPU CUDA Context 不支持多进程共享 |
| 鉴权 | 无 | 内网服务,由 backend 代理调用 |
| 数据持久化 | 无 | 无状态推理服务,落库交给 backend |
| NMS 后处理 | ONNX 适配器自实现,ultralytics 自带 | 两路输出语义一致 |
+218
View File
@@ -0,0 +1,218 @@
# PCB缺陷检测初步方案(单机开发版)
> 硬件约束修正:仅有一台联想 Y7000P 笔记本(Intel i7-14650HX 16核 @2.20GHz,16GB RAM,预计 RTX 4060 Laptop 8GB 独显)。
> 本方案在 v1.1 和"pcb缺陷识别开源项目.md"基础上,针对"无 Jetson、无独立服务器、仅一台笔记本"做完整重构。
> 对齐 CLAUDE.md:Python3.8 + YOLOv8 + Vue3.0 + Django4.0。
> 起始日期:2026-05-07
---
## 1. 硬件盘点与能力边界
| 组件 | 规格 | 训练能力 | 推理能力 |
|---|---|---|---|
| CPU | i7-14650HX 16C/24T @2.20GHz,睿频5.2GHz | 数据预处理 / 标注 / 辅助 | OpenVINO CPU推理可行 |
| GPU | RTX 4060 Laptop 8GB GDDR6(Y7000P 2024标配,需核实) | **YOLOv8n/s 可训练,batch≤16** | FP16 实时推理 60+ FPS |
| RAM | 16GB DDR5 | **瓶颈**,大数据集需分批加载 | 推理无压力 |
| 存储 | 1TB NVMe(标配) | 够用,注意清理缓存 | — |
| 散热 | 笔记本主动散热 | **持续满载训练时需外接散热器** | 推理负载低 |
**结论**:这台笔记本可以**完成训练 + Demo推理 + Web开发全流程**,但 Jetson / TensorRT / DeepStream 路线需要**降级替代**。
### 首要动作:确认 GPU 型号
开机执行 `nvidia-smi`(或设备管理器)确认:
- 若为 **RTX 4060 8GB**:方案按本文执行
- 若为 **RTX 4050 6GB**:把 `imgsz` 从 640 降到 416,`batch` 从 16 降到 8
- 若为**核显/无独显**:只能用 CPU 训练,YOLOv8n 一轮要数小时,建议立即借/租算力
---
## 2. 方案修正总览(对比原方案)
| 环节 | 原方案 | 修正后(单笔记本) | 修正理由 |
|---|---|---|---|
| 训练硬件 | Jetson Orin Nano | **Y7000P RTX 4060** | 无 Jetson |
| 训练模型 | YOLOv8n/s 随意 | **固定 YOLOv8n**(s 作备选) | 8GB 显存 + 16GB 内存限制 |
| 边缘部署演示 | Jetson + DeepStream + TensorRT | **笔记本 + USB工业相机 + ONNX Runtime GPU** | 节省硬件预算,聚焦算法 |
| Demo 形态 | 边缘盒子现场演示 | **笔记本直接演示 + 预录视频兜底** | 现场网络/电源风险可控 |
| 实时推理 | TensorRT FP16 | **ONNX Runtime DirectML 或 CUDA EP** | Windows 原生支持,无需交叉编译 |
| 数据标注 | 标注团队 | **本机 Label Studio / labelme** | 5人团队自标注即可 |
| 产品部署 | 云服务器 | **本机 Docker + ngrok 内网穿透** 临时演示 | 省服务器钱 |
| 生产边缘盒 | Jetson 自购 | **MVP 阶段客户采购 / 租用** | 获奖后再投硬件 |
---
## 3. GitHub 项目选型(按硬件修正)
### 主选:aaravomega/pcb-defect-detection
https://github.com/aaravomega/pcb-defect-detection
- YOLOv8 + Ultralytics 标准流程,与 Python3.8 技术栈一致
- Windows 原生支持,不需 Linux / Docker
- fork 后直接改 `data.yaml` 和 `train.py`
### 数据:HRIPCB(主)+ DeepPCB(备)
- **HRIPCB**(Roboflow 一键导出 YOLO 格式):https://universe.roboflow.com/ma007/hripcb/dataset/3
- **DeepPCB**(含模板图,后期差分增强用):https://github.com/tangsanli5201/DeepPCB
### 演示方案:放弃 Deepstream,改用 ONNX Runtime + OpenCV
- 原因:NVIDIA DeepStream 主要面向 Jetson 和 Tesla 服务器卡,笔记本 4060 用起来收益小、坑多
- 替代:训练后的 `best.pt` → 导出 ONNX → **ONNX Runtime GPU (CUDA Execution Provider)** → OpenCV 读 USB 相机 → Vue 看板展示
- 备选:**OpenVINO**(Intel CPU 推理,i7-14650HX 上 YOLOv8n 可跑 20-30 FPS,作为 GPU 挂掉时的兜底)
### 挠性板扩展:Flexible-PCB-Defect-Detection
https://github.com/Pranshuk29/Flexible-PCB-Defect-Detection
- 仅在 MVP 阶段、成功对接梅州智科后启用
- 不在 Demo 阶段占用训练资源
---
## 4. 训练配置(针对 8GB 显存 + 16GB 内存)
### 4.1 环境清单(Windows 11 + WSL2 可选)
```
Python 3.8.x
PyTorch 2.0.1 + CUDA 11.8(Y7000P 驱动原生支持)
Ultralytics 8.x
onnxruntime-gpu 1.16+
opencv-python 4.8+
label-studio 或 labelme
```
> 不建议用 WSL2 训练:Windows 下 PyTorch CUDA 性能已够,WSL 多一层开销且调试麻烦。
### 4.2 训练超参数(起步版)
```yaml
# train.yaml(Y7000P 实测可行)
model: yolov8n.pt
data: hripcb.yaml
imgsz: 640
batch: 16 # 8GB 显存极限,若 OOM 降到 8
epochs: 100
device: 0 # 单 GPU
workers: 4 # 16GB 内存下别超 4,否则卡死
amp: True # 混合精度必开,省显存
cache: ram # 数据集 <2GB 时开 RAM 缓存加速
patience: 20 # 早停,省电省时间
```
### 4.3 训练时间预估(HRIPCB 1386 张)
| 模型 | 单轮时间 | 100轮总时 | 显存占用 |
|---|---|---|---|
| YOLOv8n | ~45s | **约 1.5 小时** | ~4GB |
| YOLOv8s | ~80s | 约 2.5 小时 | ~6GB |
| YOLOv8m | OOM 风险 | 不推荐 | >8GB |
**建议**:先 n 后 s,n 版本达到 mAP@0.5 > 0.9 即可上路演,s 作为备选。
### 4.4 散热与稳定性
- 训练时**外接散热底座**,温度控制在 85℃ 以内
- 关闭 Windows 自动更新,避免训练中断
- 每晚跑训练、白天做产品开发,错峰使用笔记本资源
---
## 5. Demo 现场展示方案(替代 Jetson)
### 5.1 硬件清单(总预算 ≤ 2000 元)
| 设备 | 型号建议 | 价格 | 用途 |
|---|---|---|---|
| USB 工业相机 | 海康威视 MV-CU013-A0UC 或 海康 USB 摄像头 | ~800-1500 | 实拍 PCB 样品 |
| LED 环形光源 | 工业白光环形灯 | ~200 | 保证光照一致 |
| 简易光箱 / 黑色背景板 | 自制 | ~100 | 降低杂光干扰 |
| PCB 样品 | 向本地厂讨要废板 | 0 | 演示素材 |
| 外接散热底座 | 联想/酷冷至尊 | ~200 | 训练稳定性 |
### 5.2 演示流程
```
笔记本(Y7000P)
├─ USB 相机 → OpenCV 取流
├─ ONNX Runtime GPU 推理
├─ Django 后端写入事件
├─ Vue 看板实时展示(localhost)
└─ 投屏到决赛现场大屏
```
### 5.3 三重兜底
- **A 计划**:现场实时相机 + PCB 样品演示(最理想)
- **B 计划**:预录 90 秒场景视频 + Vue 看板重放(现场网络/相机故障时启用)
- **C 计划**:纯截图 PPT(极端情况,笔记本挂掉时)
**每个计划必须提前演练 3 次以上**。
---
## 6. 开发环境(单机全栈)
```
Y7000P 上同时跑:
├─ 终端1:Django runserver (8000)
├─ 终端2:Vue dev server (5173)
├─ 终端3:YOLOv8 推理服务(FastAPI 独立进程,8001)
├─ 终端4:Label Studio(8080,标注用)
└─ 后台:PostgreSQL 或 SQLite(MVP 阶段 SQLite 足够)
```
**资源分配**:
- 训练时段:关掉 Django/Vue/Label Studio,独占 GPU 和内存
- 开发时段:关掉训练,16GB 内存跑全栈无压力
- **不要同时训练和开发**,16GB 必 OOM
---
## 7. 三个月执行计划(对齐 Y7000P 节奏)
| 周 | 工作内容 | 是否占用 GPU |
|---|---|---|
| W1 | 确认 GPU 型号;装环境;fork aaravomega;下载 HRIPCB | 轻度(跑通推理) |
| W2 | YOLOv8n 在 HRIPCB 上训练完成;拿到 mAP 基准 | **整夜占用** |
| W3 | Django 后端框架 + Vue 看板骨架;接 SQLite | 否 |
| W4 | YOLOv8 导出 ONNX;FastAPI 推理服务;OpenCV 取相机流 | 轻度 |
| W5 | 买 USB 工业相机 + 环形光;搭建演示光箱;拍测试素材 | 否 |
| W6 | Demo 版 v1:相机 → 推理 → 看板 全链路打通 | 轻度 |
| W7 | 联系丰顺威达 / 梅州超捷 / 智科,签 NDA 收样本 | 否 |
| W8 | 真实样本标注(Label Studio)+ 迁移学习微调 | **整夜占用** |
| W9 | 规则引擎(批次缺陷率/追溯)+ 事件管理 + 告警 | 否 |
| W10 | 挠性板扩展(若智科签约)+ 录 90s 场景视频 | 轻度 |
| W11 | 软著申请;客户 MOU 签署;BP 定稿 | 否 |
| W12 | 决赛 Demo 联调;三重兜底演练;路演彩排 | 否 |
---
## 8. 风险清单(硬件层)
| 风险 | 概率 | 影响 | 对策 |
|---|---|---|---|
| 笔记本训练过热降频 | 高 | 训练慢 | 外接散热底座 + 限制房间温度 |
| 8GB 显存 OOM | 中 | 训练中断 | batch 降到 8;imgsz 降到 416 |
| 16GB 内存爆满 | 中 | 系统卡死 | workers 降到 2;关闭浏览器等 |
| 决赛现场笔记本故障 | 低 | **灾难** | 二号机备份(团队另一人的笔记本同步工程)+ B/C 计划 |
| GPU 驱动更新导致 CUDA 挂掉 | 低 | 训练中断 | 关闭 NVIDIA 自动更新;锁定驱动版本 |
| 真实 PCB 样本获取失败 | 中 | 只能用公开数据集 | 提前 2 周对接 2 家以上厂商分散风险 |
---
## 9. 后续升级路径(晋级后)
- **初赛通过后**:考虑租云 GPU(AutoDL / 腾讯云 GN7 一周 ~300 元)加速 YOLOv8s / m 训练
- **获奖后**:用奖金采购 1 台 **Jetson Orin Nano 8GB**(约 3500 元)做客户演示边缘盒子,切回原方案的 DeepStream 部署路线
- **MVP 阶段**:客户侧硬件由客户采购或租用,我方只交付**软件 + 配置服务**
---
## 10. 一句话结论
**用好 Y7000P 这一台机器,完全够支撑 Demo 版晋级**——关键是**把训练和开发错峰安排,显存内存资源专用**。Jetson / 云服务器留到 MVP 阶段和获奖后再投入,Demo 阶段聚焦"算法精度 + 产品闭环 + 本地客户意向"三板斧即可。
+41
View File
@@ -0,0 +1,41 @@
# PCB线路板缺陷检测项目简介
## 一、项目介绍
PCB(印刷电路板)被称为"电子产品之母",手机、电脑、汽车里都有它的身影。一块线路板上布满了细如发丝的铜线路和密密麻麻的焊点,任何一处瑕疵都可能导致整台设备故障。
传统的人工检测方式依赖质检员用放大镜逐片检查,效率低、眼睛疲劳后漏检率高,而且培养一名熟练质检员需要数月时间。我们的项目利用AI视觉技术——就像给机器装上一双"火眼金睛",通过工业相机自动拍摄线路板图像,再由AI算法实时识别短路、断路、缺焊、异物等各类缺陷,检测速度比人工快数十倍,且24小时不知疲倦,漏检率大幅降低。
本项目旨在帮助中小线路板工厂以较低成本实现智能化质检升级,提升产品良率,降低返工成本。
## 二、工厂调研问题清单
### 生产流程类
1. 工厂目前各生产环节(曝光、蚀刻、钻孔、电镀、丝印等)的工艺流程是怎样的?
2. 质检环节安排在哪些工序之后?目前有哪些检测站点?
3. 日产量/月产量大概是多少?产线运行几班制?
### 缺陷情况类
4. 目前最常见的缺陷类型有哪些?各占多大比例?
5. 各类缺陷的成因是什么?主要在哪些工序产生?
6. 是否有缺陷分类的标准或规范?是否有缺陷样本图片存档?
7. 最严重、最亟待解决的缺陷类型是哪一种?
### 现有检测方式类
8. 目前采用人工目检还是已有AOI(自动光学检测)设备?
9. 如有AOI设备,是什么品牌型号?检测效果和痛点是什么?
10. 人工检测的漏检率和误检率大约是多少?
11. 一名质检员一天能检测多少片板?
### 数据与合作类
12. 是否愿意提供历史缺陷图片用于AI模型训练?能否签署数据保密协议?
13. 对AI检测的准确率期望值是多少?(如漏检率<0.5%、误检率<2%)
14. 产线上是否已有工业相机或是否方便加装?
15. 如果部署AI检测系统,希望以什么形式合作?(一次性采购、按月租赁、按检测量计费?)
### 环境与部署类
16. 产线环境的光照条件如何?是否有稳定的照明?
17. 检测工位是否有网络环境?内网还是外网?
18. 线路板来料的尺寸范围是多少?最小线宽/线距是多少?
19. 对检测速度的要求是什么?(如每秒检测几片)
20. 工厂目前的IT基础设施如何?是否有专人维护自动化设备?
+131
View File
@@ -0,0 +1,131 @@
# PCB缺陷识别开源项目调研
> 服务于梅州市新一代电子信息暨人工智能产业创新创业大赛"PCB/电路板AI视觉缺陷检测平台"选题。
> 结合2025年决赛PCB获奖企业产品形态(刚性板、挠性板、铝基板、Mini LED板、PTFE高频板、OSP涂层等),筛选出覆盖"基础6缺陷 / 挠性板 / 边缘部署 / 高质量基准"四个互补切面的5个GitHub项目,均有公开数据集可直接复用。
> 调研日期:2026-05-07
---
## 选型总览
| # | 项目 | 框架 | 数据集 | 覆盖切面 | 对应梅州PCB厂 |
|---|---|---|---|---|---|
| 1 | tangsanli5201/DeepPCB | 基础baseline | DeepPCB(1500对) | 6类标准缺陷 + 模板对齐 | 超捷、华达、智科(AOI后复判) |
| 2 | Ixiaohuihuihui/Tiny-Defect-Detection-for-PCB | 小目标优化 | HRIPCB(北大,1386张) | 微小缺陷 + 高分辨率 | 丰顺威达(PTFE高频板)、兴宁精进(高效散热) |
| 3 | aaravomega/pcb-defect-detection | YOLOv8 | 7971张(HRIPCB扩增) | 即插即用算法底座 | 作为MVP版核心底座 |
| 4 | clintonoduor/PCB-Defect-Detection-using-Deepstream | YOLOv5 + Jetson + TensorRT | HRIPCB | 边缘实时部署 | 所有中小PCB厂(不接受云端) |
| 5 | Pranshuk29/Flexible-PCB-Defect-Detection | YOLOv5 + FlexSim | 自建FPC挠性板数据集 | 挠性板专项 | 梅州智科(挠性印制电路板,2025三等奖) |
---
## 1. tangsanli5201/DeepPCB — 基准数据集 + baseline
- **仓库**:https://github.com/tangsanli5201/DeepPCB
- **数据集特性**:
- **1,500组图像对**:每组包含"无缺陷模板图 + 带缺陷测试图",**像素级对齐**
- 6类缺陷:open(断路)、short(短路)、mousebite(鼠咬)、spur(毛刺)、pinhole(针孔)、spurious copper(余铜)
- 标注格式:PASCAL VOC(bounding box),开箱即用
- **适配梅州场景**:对应**梅州超捷、华达、智科**等走"AOI后复判"路线的PCB厂——有模板图可用**差分增强**,检测精度更高
- **建议用法**:作为**初赛资料**的算法精度证据(mAP数字有行业共识),先跑通baseline再迁移到真实样本
## 2. Ixiaohuihuihui/Tiny-Defect-Detection-for-PCB — 微小缺陷 + 高分辨率
- **仓库**:https://github.com/Ixiaohuihuihui/Tiny-Defect-Detection-for-PCB
- **数据集**:**HRIPCB**(北京大学人机交互开放实验室发布)
- 1,386张合成PCB图,6类缺陷(同DeepPCB)
- **分辨率更高,缺陷尺度更小**,更接近真实AOI拍摄环境
- **镜像下载**:
- Roboflow:https://universe.roboflow.com/ma007/hripcb/dataset/3 (一键导出YOLO/COCO格式)
- Kaggle:https://www.kaggle.com/datasets/akhatova/pcb-defects
- **适配梅州场景**:对应**丰顺威达(PTFE高频板孔工艺)、兴宁精进(高效散热板)**这类**线宽细、缺陷尺度小**的场景
- **建议用法**:作为训练主力,HRIPCB的小目标特性能弥补DeepPCB分辨率不足的问题
## 3. aaravomega/pcb-defect-detection — YOLOv8 即插即用
- **仓库**:https://github.com/aaravomega/pcb-defect-detection
- **数据集**:**7,971张**PCB标注图,6类缺陷(HRIPCB + 数据扩增)
- **框架**:YOLOv8 + Ultralytics transfer learning
- **技术栈匹配**:与 CLAUDE.md 约定的 Python3.8 + YOLOv8 完全一致
- **适配梅州场景**:作为**MVP版的算法底座**,YOLOv8n/s 在 Jetson Nano/Orin 上可跑实时推理
- **建议用法**:直接 fork,把 Django4.0 后端和 Vue3.0 看板接上就是完整产品
## 4. clintonoduor/PCB-Defect-Detection-using-Deepstream — 边缘部署样板
- **仓库**:https://github.com/clintonoduor/PCB-Defect-Detection-using-Deepstream
- **数据集**:HRIPCB + YOLOv5 训练权重
- **亮点**:**NVIDIA DeepStream + Jetson AGX** 端到端 pipeline
- 含 TensorRT 量化
- 视频流实时推理
- 实时可视化
- **适配梅州场景**:**路演现场演示神器**
- 评委偏好"边缘盒子 + 摄像头 + 实时告警"三件套
- 中小 PCB 厂不接受云端部署,边缘盒子是刚需
- **建议用法**:作为 **Demo 版的硬件展示方案**,现场摆一台 Jetson Orin Nano + 工业相机 + PCB 样品
## 5. Pranshuk29/Flexible-PCB-Defect-Detection — 挠性板专项差异化
- **仓库**:https://github.com/Pranshuk29/Flexible-PCB-Defect-Detection
- **数据集**:自建 **FPC 挠性板**数据集 + YOLOv5 + **FlexSim 数据增强**(模拟弯折形变)
- **适配梅州场景**:
- **直接对应 2025 三等奖企业"梅州智科电路板"的挠性印制电路板产品线**
- FPC 因弯折产生的形变缺陷(折痕、铜箔裂纹、层间分离)是**通用 PCB 数据集覆盖不到**的差异化能力
- **建议用法**:作为**本次参赛的差异化亮点**
- 在标准 6 类缺陷之外加一个"挠性板专用"能力模块
- 与梅州智科对接签 MOU(客户意向书)
---
## 备选项目(路演备选 / 答辩应对)
| 项目 | URL | 适用场景 |
|---|---|---|
| AyaAshraf76/PCB-defect-detection | https://github.com/AyaAshraf76/PCB-defect-detection | YOLOv8 + Roboflow 数据,教学级 |
| Hazrat-Ali9/Deep-Learning-AOI-for-PCBs | https://github.com/Hazrat-Ali9/Deep-Learning-AOI-for-PCBs-Real-Time-YOLO-Detection-of-Missing-Holes-Mouse-Bites-Shorts-and-Spurs | 实时 AOI 场景,补充参考 |
| EzekiasOkuPevi/PCB-Defect-Detection-Using-Deep-Learning | https://github.com/EzekiasOkuPevi/PCB-Defect-Detection-Using-Deep-Learning | YOLOv8 vs YOLOv10 对比,答辩用 |
| SiemonCha/pcb-defect-detection | https://github.com/SiemonCha/pcb-defect-detection | 生产级 pipeline,工程参考 |
| utkarsh-ankit/Defect-Detection-of-PCB | https://github.com/utkarsh-ankit/Defect-Detection-of-PCB | MobileNets 轻量化方案 |
---
## 整合推进建议(对齐 v1.1 的 Demo 版→MVP 版分档)
### Demo 版(1.5 个月,路演可用)
1. 拉取 **项目1 DeepPCB** + **项目2 HRIPCB** 数据集合并训练
2. 用 **项目3 aaravomega/pcb-defect-detection** 的 YOLOv8 底座做迁移学习
3. 用 **项目4 Deepstream** 方案在 Jetson 上做实时演示
4. 产出:现场可演示的边缘盒子 + 标准 mAP 基准数字
### MVP 版(3 个月,客户可交付)
1. 接入 **1 家梅州本地 PCB 厂**真实产线样本,微调项目3的模型
2. 用 **项目5 Flexible-PCB** 的 FPC 方案扩展挠性板能力,对接梅州智科
3. Django4.0 + Vue3.0 产品化:用户系统 / 规则引擎 / 告警回放 / 追溯看板
4. 产出:客户 MOU、软著证书、真实产线验证数据
### 路演包装话术
> "基于北大 HRIPCB 学术数据集 + 真实梅州产线样本双源训练,面向梅州本地 PCB 产业集群的 AI 视觉质检平台。"
**学术可信度**(北大数据集)**+ 本地化落地**(真实产线)**双背书**,契合 2025 公告"项目展示完成度"与"自主知识产权"考察点。
---
## 版权与合规提示
- **DeepPCB、HRIPCB** 均以学术用途发布
- 商业化部署前需在对应论文 / README 中确认授权条款
- 路演阶段使用无碍,但要提前准备自有数据集方案用于答辩应对
- 建议:**尽早启动真实样本采集**,降低对学术数据集的依赖
---
## 参考资料
- [pcb-defect-detection · GitHub Topics](https://github.com/topics/pcb-defect-detection)
- [HRIPCB on Roboflow Universe](https://universe.roboflow.com/ma007/hripcb/dataset/3)
- [PCB Defects dataset on Kaggle](https://www.kaggle.com/datasets/akhatova/pcb-defects)
- [PCB-YOLOV8X: a network for detecting micro-sized defects on PCB surfaces (Nature, 2026)](https://www.nature.com/articles/s41598-026-46089-6)
- [An Open Source Enhancement Algorithm for PCB Defect Detection Based on YOLOv8 (arXiv 2025)](https://arxiv.org/html/2507.02963v1)
- [Defect detection of printed circuit board assembly based on YOLOv5 (Nature, 2024)](https://www.nature.com/articles/s41598-024-70176-1)
- [PCB Electronic Component Soldering Defect Detection Using YOLO11 (PMC, 2025)](https://pmc.ncbi.nlm.nih.gov/articles/PMC12158347/)
+38
View File
@@ -0,0 +1,38 @@
# 简介
* 手机、电脑、汽车里都有PCB印刷电路板。
* 一块线路板上布满了细如发丝的铜线路和密密麻麻的焊点,任何一处瑕疵都可能导致整台设备故障。
* 传统的人工检测方式依赖质检员用放大镜逐片检查,效率低、眼睛疲劳后漏检率高,而且培养一名熟练质检员需要数月时间。
* 我们的项目利用AI视觉技术,通过工业相机自动拍摄线路板图像,再由AI算法实时识别短路、断路、缺焊、异物等各类缺陷,检测速度比人工快数十倍,且24小时不知疲倦,漏检率大幅降低。
* 搭配管理平台,随时查看记录和统计缺陷,生成数据报告.
* 本项目帮助中小线路板工厂以较低成本实现智能化质检升级,提升产品良率,降低返工成本。
#调研问题:
##生产流程类
1. 工厂质检环节安排在哪些工序之后?
2. 日产量/月产量大概是多少?产线运行几班制?
## 缺陷情况类
1. 目前最常见的缺陷类型有哪些?各占多大比例?
2. 各类缺陷的成因是什么?主要在哪些工序产生?
3. 是否有缺陷分类的标准或规范?是否有缺陷样本图片存档?
4. 最严重、最亟待解决的缺陷类型是哪一种?
## 现有检测方式类
1. 目前采用人工目检还是已有AOI(自动光学检测)设备?
2. 如有AOI设备,是什么品牌型号?检测效果和痛点是什么?
3. 人工检测的漏检率和误检率大约是多少?
4. 一名质检员一天能检测多少片板?
## 数据与合作类
1. 对AI检测的准确率期望值是多少?(如漏检率<0.5%、误检率<2%)
2. 如果部署AI检测系统,希望以什么形式合作?(一次性采购、按月租赁、按检测量计费?)
## 环境与部署类
1. 产线环境的光照条件如何?是否有稳定的照明?
2. 检测工位是否有网络环境?内网还是外网?
3. 线路板来料的尺寸范围是多少?最小线宽/线距是多少?
4. 对检测速度的要求是什么?(如每秒检测几片)
5. 工厂目前的IT基础设施如何?是否有专人维护自动化设备?
Binary file not shown.
@@ -0,0 +1,757 @@
# 明析平台 软件重构设计文档
> 版本:v2.0 · 日期:2026-05-24
> 项目:基于机器视觉的图像识别技术应用平台——明析
> 参赛:梅州市2026年新一代电子信息暨人工智能产业创新创业大赛 · 初创企业(创客)组
---
## 1. 重构背景
### 1.1 现有代码概况
当前仓库 `yolo_classification_system` 是一套**通用YOLO推理系统**,实现了:
| 模块 | 现状 |
|------|------|
| 后端 | Django 4 + DRF,JWT鉴权,支持图片/视频/摄像头三种推理模式 |
| 推理 | `inference.py` 封装 ultralytics YOLO,模型缓存,结果落库 |
| 数据模型 | `UserProfile`、`YoloResult`、`SceneNews`、`SystemLog`、`YoloStatsDaily` |
| 前端 | Vue 3 + Vite,开发/生产环境分离,API代理配置完整 |
| 演示模型 | 驾驶员疲劳检测(`driver-drowsiness-detection/best.pt`) |
### 1.2 重构目标
将通用系统改造为**PCB线路板缺陷检测专用平台(明析)**,在不改动客户现有硬件的前提下,通过算法叠加实现AI视觉质检能力升级。
---
## 2. 系统物理架构
### 2.1 两台机器部署
```
┌─────────────────────────────────┐ 局域网 ┌──────────────────────────────────────┐
│ 前置机(工业PC) │ ─────────────→ │ 推理服务器(Linux+GPU) │
│ │ │ │
│ 相机 ──USB3──→ mingxi-capture │ │ mingxi-vision :8001 (FastAPI) │
│ (PyQt5桌面程序) │ │ mingxi-backend :8000 (Django) │
│ Win7 SP1+ 工业机 │ │ mingxi-frontend :80 (Vue3) │
└─────────────────────────────────┘ └──────────────────────────────────────┘
```
| 角色 | 机器 | 操作系统 | 运行软件 |
|------|------|---------|---------|
| 前置机 | 工厂现有工业PC(USB3.0) | Windows 7 SP1 及以上 | mingxi-capture.exe |
| 推理服务器 | 团队配置(RTX 4060 或以上) | Linux | mingxi-vision + mingxi-backend + mingxi-frontend |
### 2.2 数据流
```
操作工(点击按钮)或 PLC触发信号
│
▼
mingxi-capture(前置机)
│
├─① 同步 POST 图像 ──────────────→ mingxi-vision(推理服务器:8001)
│ │ 推理完成
│ ←── 返回标注图+缺陷列表+结论 ──────┘
│
├─ 立即在桌面程序展示结果(操作工可见)
│ · 合格 / 不合格 结论
│ · 标注了缺陷位置的图像
│ · 每个缺陷的类型、等级、置信度
│
└─② 异步队列 POST 结果 ──────────→ mingxi-backend(:8000)
│ 存入 SQLite
若失败 → 本地SQLite重试队列 │
▼
管理员/组长/厂长
通过浏览器查看
(短轮询,1-2秒延迟)
```
### 2.3 端口分配
| 服务 | 端口 | 调用方 |
|------|------|-------|
| mingxi-vision | 8001 | mingxi-capture(同步推理) |
| mingxi-backend | 8000 | mingxi-capture(存档)、浏览器前端 |
| mingxi-frontend | 80 | 浏览器 |
---
## 3. 子项目拆分方案
### 3.1 项目总览
```
mingxi-capture/ Python 3.8 · PyQt5 · Windows桌面程序(前置机运行)
mingxi-vision/ Python 3.8 · FastAPI · PCB缺陷推理服务(推理服务器运行)
mingxi-backend/ Python 3.8 · Django 4 · 业务后端(推理服务器运行)
mingxi-frontend/ Vue 3.0 · 管理后台(推理服务器运行,浏览器访问)
```
### 3.2 代码迁移决策
| 现有文件/模块 | 处置方式 | 说明 |
|-------------|---------|------|
| `yolo8/models.py` → `UserProfile` | **直接复用** | JWT鉴权体系完整,扩展 role 字段 |
| `yolo8/models.py` → `SystemLog` | **直接复用** | 日志结构通用 |
| `yolo8/jwt_utils.py` | **直接复用** | token版本机制保留 |
| `yolo8/models.py` → `YoloResult` | **重构为** `DefectResult` | 增加PCB专用字段:缺陷等级、产线 |
| `yolo8/models.py` → `SceneNews` | **删除** | 与PCB业务无关 |
| `yolo8/models.py` → `YoloStatsDaily` | **重构为** `ProductionStat` | 改为产线维度统计 |
| `yolo8/inference.py` → `run_image_inference` | **迁移至** `mingxi-vision` | 推理服务核心,增加ONNX支持 |
| `yolo8/inference.py` → `run_camera_inference` | **删除** | 由 `mingxi-capture` 海康SDK替代 |
| `yolo8/inference.py` → `_copy_to_ascii_temp_input` | **删除** | 改为内存读图,彻底消除中文路径问题 |
| `yolo8/views.py` → 鉴权相关视图 | **直接复用** | `auth_*`、`user_*` 无需改动 |
| `yolo8/views.py` → `yolo_task_image` | **重构为** `detect_pcb` | 简化,接收capture推送 |
| `frontend/` | **重构** | 删除通用YOLO页面,改为管理后台 |
---
## 4. mingxi-capture(前置采集程序)
### 4.1 定位
运行在**前置机**(工厂工业PC)上的 Windows 原生桌面程序。操作工唯一的交互界面,负责连接相机、触发检测、展示结果,并将结果异步上报至后台。
### 4.2 技术栈
```
语言:Python 3.8
GUI:PyQt5(支持 Windows 7 SP1 及以上,系统托盘、对话框完整支持)
相机:MvImport(海康威视MVS Python SDK,随MVS安装包提供)
本地存储:SQLite(通过 Python 内置 sqlite3)
网络:requests(同步调用vision)+ threading(异步队列推送backend)
打包:PyInstaller → 单文件 .exe 或目录,客户解压即用
最低系统:Windows 7 SP1 x64
```
### 4.3 目录结构
```
mingxi-capture/
├── camera/
│ ├── hik_camera.py # 海康相机封装(MvImport,Phase 2接入)
│ └── mock_camera.py # 模拟模式(读本地图片文件夹,Demo/开发用)
├── ui/
│ ├── main_window.py # 主窗口:触发按钮 + 图像展示 + 缺陷列表
│ ├── config_dialog.py # 配置弹窗:服务器地址、端口、相机参数
│ └── tray_icon.py # 系统托盘图标:显示/隐藏主窗口、退出
├── core/
│ ├── capture_worker.py # 拍图+调推理(在QThread中执行,不阻塞UI)
│ ├── upload_queue.py # 异步上报队列(本地SQLite缓存+重试)
│ └── settings.py # 读写 config.ini 配置文件
├── assets/
│ └── tray_icon.png # 托盘图标
├── config.ini # 默认配置(首次运行生成)
├── queue.db # 本地重试队列(SQLite,运行时生成)
├── main.py # 程序入口
├── requirements.txt
└── mingxi-capture.spec # PyInstaller打包配置
```
### 4.4 主窗口功能
```
┌─────────────────────────────────────────┐
│ 明析采集程序 [最小化到托盘] [×] │
├─────────────┬───────────────────────────┤
│ │ ┌───────────────────────┐│
│ [手动检测] │ │ ││
│ │ │ 标注图显示区域 ││
│ 状态: 就绪 │ │ (检测后显示) ││
│ │ │ ││
│ 结论: │ └───────────────────────┘│
│ ██ 不合格 │ │
│ │ 缺陷列表: │
│ 共 3 处 │ 类型 等级 置信度 │
│ 最高: 致命 │ 断路 致命 0.94 │
│ │ 短路 致命 0.88 │
│ │ 毛刺 轻微 0.72 │
└─────────────┴───────────────────────────┘
```
### 4.5 配置弹窗(系统托盘右键→设置)
```
推理服务地址: [192.168.1.100] 端口: [8001]
后台服务地址: [192.168.1.100] 端口: [8000]
相机序列号: [自动检测 ▼]
模拟模式: [✓] 启用 图片目录: [C:\test_images\ 浏览]
置信度阈值: [0.45]
[保存] [取消]
```
配置持久化至 `config.ini`,程序启动时自动读取。
### 4.6 异步上报队列
```python
# core/upload_queue.py 逻辑
# 1. capture_worker 推理成功后,将结果插入本地 SQLite queue.db
# 2. 后台线程每 5 秒扫描一次 queue.db 中状态为 pending 的记录
# 3. 尝试 POST 到 mingxi-backend
# 成功 → 标记为 sent
# 失败 → retry_count + 1,超过 10 次标记为 failed(不再重试)
# 4. 队列表结构:
# id, image_base64, result_json, created_at, status, retry_count, last_error
```
### 4.7 mock_camera 模拟模式
Phase 1(路演)无真实相机时,从配置目录中按序读取 `.jpg`/`.png` 文件,接口与 `hik_camera.py` 完全一致,上层代码无感知切换。
### 4.8 打包部署
```bash
pyinstaller mingxi-capture.spec
# 输出:dist/mingxi-capture/(目录模式)或 dist/mingxi-capture.exe(单文件)
# 客户端:解压后双击 mingxi-capture.exe 即可运行
# 随安装包附带:海康 MVS Runtime DLL(如需相机功能)
```
### 4.9 触发模式(阶段支持)
| 触发方式 | Phase 1 | Phase 2 |
|---------|---------|---------|
| 手动点击按钮 | ✅ | ✅ |
| PLC/传感器信号(串口/数字IO) | — | ✅ |
| 定时自动(固定频率) | — | ✅ |
---
## 5. mingxi-vision(推理服务)
### 5.1 职责
- 接收图像(来自 `mingxi-capture`)
- 调用明析平台(YOLOv8/ONNX)执行PCB缺陷检测
- 返回缺陷列表(位置、类别、置信度、等级)+ 标注图(可选)
- 支持模型热重载(管理员通过后台切换模型时调用)
### 5.2 技术栈
```
语言:Python 3.8
框架:FastAPI + uvicorn(workers=1,单进程,GPU不跨进程共享)
推理:ultralytics YOLOv8(开发阶段)/ ONNX Runtime(生产部署)
并发:asyncio.Lock(推理串行化,防止GPU冲突)
GPU:CUDA 11.x + cuDNN
```
### 5.3 并发处理:推理锁
```python
# engine/loader.py
import asyncio
_inference_lock = asyncio.Lock()
_engine: Optional[BaseInferenceEngine] = None
def get_inference_lock() -> asyncio.Lock:
return _inference_lock
# api/detect.py
async def detect(image: UploadFile, conf: float = 0.45, ...):
raw = await image.read()
img = cv2.imdecode(np.frombuffer(raw, np.uint8), cv2.IMREAD_COLOR)
async with get_inference_lock(): # 请求自动排队,GPU串行执行
result = engine.detect(img, conf)
return build_response(result)
```
- 并发请求不会崩溃,自动排队等待
- RTX 4060,YOLOv8s,PCB图:约 20~40ms/张 → 25~50 fps
- Windows / Linux 行为完全一致(asyncio 事件循环原语,无OS依赖)
### 5.4 模型热重载接口
```
POST /api/model/reload
Body: { "model_path": "/opt/mingxi/models/pcb_v2.onnx" }
Response: { "status": "ok", "model_version": "pcb_v2", "warmup_ms": 312 }
```
热重载流程:
1. 管理员在浏览器上传新模型并点击"切换"
2. `mingxi-backend` 保存文件、更新数据库,调用此接口
3. `mingxi-vision` 获取推理锁(等待当前推理完成),卸载旧模型,加载新模型,执行 warmup
4. 释放锁,返回成功
### 5.5 目录结构
```
mingxi-vision/
├── api/
│ ├── detect.py # POST /api/detect
│ ├── model.py # POST /api/model/reload
│ └── health.py # GET /api/health, /api/health/gpu
├── engine/
│ ├── base.py # DefectBox, DetectResult, BaseInferenceEngine
│ ├── loader.py # 单例 + asyncio.Lock + init_engine()
│ ├── ultralytics_adapter.py
│ ├── onnx_adapter.py
│ ├── annotator.py # 标注图生成(按severity着色)
│ └── defect_classes.py # HRIPCB 6类 + 扩展4类
├── models/ # .pt 和 .onnx 文件(gitignored)
├── scripts/
│ ├── export_onnx.py # pt → onnx 导出脚本
│ └── benchmark.py
├── tests/
├── schema.py # Pydantic 响应模型
├── config.py # BaseSettings(读 .env)
├── main.py # FastAPI lifespan + app
└── requirements.txt
```
### 5.6 推理接口
```
POST /api/detect
Content-Type: multipart/form-data
Fields:
image: <图像文件>
conf: float (default: 0.45)
return_annotated: bool (default: false)
Response 200:
{
"task_id": "uuid",
"duration_ms": 38,
"verdict": "fail", // pass / fail
"defect_count": 2,
"max_severity": "fatal",
"avg_confidence": 0.89,
"defects": [
{
"class_id": 2,
"class_name": "open_circuit",
"class_name_zh": "断路",
"confidence": 0.94,
"severity": "fatal",
"box_xyxy": [120, 45, 198, 123]
}
],
"annotated_image_base64": "..." // 仅 return_annotated=true 时返回
}
```
### 5.7 缺陷类别与等级映射
```python
DEFECT_CLASSES = {
# HRIPCB 数据集原始6类
0: {"name": "missing_hole", "zh": "缺孔", "severity": "fatal"},
1: {"name": "mouse_bite", "zh": "鼠咬", "severity": "major"},
2: {"name": "open_circuit", "zh": "断路", "severity": "fatal"},
3: {"name": "short_circuit", "zh": "短路", "severity": "fatal"},
4: {"name": "spur", "zh": "毛刺", "severity": "minor"},
5: {"name": "spurious_copper", "zh": "余铜", "severity": "major"},
# 扩展类(Phase 2 标注后训练)
6: {"name": "oxidation", "zh": "氧化", "severity": "minor"},
7: {"name": "solder_ball", "zh": "锡珠", "severity": "rework"},
8: {"name": "scratch", "zh": "划痕", "severity": "minor"},
9: {"name": "label_error", "zh": "标签错贴","severity": "rework"},
}
```
---
## 6. mingxi-backend(业务后端)
### 6.1 职责
- 接收 `mingxi-capture` 推送的检测结果,持久化至 SQLite
- 提供 REST API 给浏览器管理后台
- 用户管理、三级权限控制
- 模型文件管理(上传、备份、切换、触发vision热重载)
- 系统参数配置
### 6.2 技术栈
```
语言:Python 3.8
框架:Django 4.0 + django-rest-framework
数据库:SQLite(内网单服务器部署,无高并发写入,SQLite足够)
认证:JWT(从现有系统直接复用)
```
> 初期不引入 Celery/Redis,保持部署简单。告警推送(Phase 2)再评估是否引入。
### 6.3 用户权限设计
| 角色 | 检测记录可见范围 | 其他权限 |
|------|---------------|---------|
| 操作工 | 仅自己触发的记录 | 无 |
| 组长 | 本组所有成员的记录 | 查看本组统计 |
| 管理员 | 全部记录 | 用户管理、系统配置、模型管理、查看所有统计 |
```python
# UserProfile 扩展
class UserProfile(models.Model):
user = models.OneToOneField(User)
role = models.CharField(choices=[
("operator", "操作工"),
("leader", "组长"),
("admin", "管理员"),
], default="operator")
group = models.ForeignKey("WorkGroup", null=True) # 操作工和组长属于某个工组
class WorkGroup(models.Model):
name = models.CharField(max_length=100)
leader = models.ForeignKey(UserProfile, null=True, related_name="led_group")
```
### 6.4 模型管理设计
```python
class ModelRegistry(models.Model):
"""模型版本注册表"""
name = models.CharField(max_length=100) # 文件名(含扩展名)
runtime = models.CharField(choices=["pt", "onnx"])
file_path = models.CharField(max_length=500) # 服务器本地路径
file_size = models.IntegerField() # 字节数
is_active = models.BooleanField(default=False) # 当前生效模型
uploaded_by = models.ForeignKey(UserProfile)
uploaded_at = models.DateTimeField(auto_now_add=True)
notes = models.TextField(blank=True)
```
**模型管理规则:**
- 同时只有一个 `is_active=True`
- 最多保留 10 个版本,超出时自动删除最早上传的非活跃版本及其文件
- 切换模型时:更新 `is_active` → 调用 `POST http://localhost:8001/api/model/reload`
### 6.5 管理员可配置参数
| 参数类别 | 具体参数 |
|---------|---------|
| 推理参数 | 置信度阈值、IOU阈值、图像输入尺寸 |
| 模型管理 | 上传模型、切换活跃模型、查看备份列表 |
| 产线配置 | 产线名称、前置机IP(仅记录,不主动通信) |
| 用户管理 | 新增/停用账号、分配角色、分配工组 |
| 告警规则 | Phase 2,预留接口 |
### 6.6 数据模型设计
```python
class DefectResult(models.Model):
"""单次检测记录"""
operator = models.ForeignKey(UserProfile, null=True)
line_id = models.CharField(max_length=50, blank=True)
source_image = models.ImageField(upload_to="originals/")
annotated_image = models.ImageField(upload_to="annotated/", null=True)
verdict = models.CharField(choices=["pass", "fail"])
defect_count = models.IntegerField(default=0)
max_severity = models.CharField(choices=["none","rework","minor","major","fatal"])
detections_json = models.TextField() # 完整缺陷列表JSON
avg_confidence = models.FloatField()
duration_ms = models.IntegerField()
model_version = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
class ProductionStat(models.Model):
"""每日产线统计(聚合)"""
stat_date = models.DateField()
line_id = models.CharField(max_length=50)
total_count = models.IntegerField(default=0)
pass_count = models.IntegerField(default=0)
fail_count = models.IntegerField(default=0)
defect_dist = models.TextField() # 各缺陷类型数量JSON
class SysConfig(models.Model):
"""系统参数键值存储"""
key = models.CharField(max_length=100, unique=True)
value = models.TextField()
updated_at = models.DateTimeField(auto_now=True)
```
### 6.7 API 接口设计
```
# 认证(直接复用现有代码)
POST /api/auth/login
POST /api/auth/refresh
POST /api/auth/logout
GET /api/user/me
# 检测结果(capture推送入口)
POST /api/detect/result/ # capture 上报推理结果
GET /api/detect/results/ # 列表(按角色自动过滤)
GET /api/detect/results/{id}/
# 统计
GET /api/stats/overview/ # 今日合格率、缺陷总数
GET /api/stats/trend/ # 近N天趋势
GET /api/stats/defect-dist/ # 缺陷类型分布
# 模型管理(管理员)
GET /api/admin/models/ # 模型列表(最多10条)
POST /api/admin/models/upload/ # 上传新模型
POST /api/admin/models/{id}/activate/ # 切换活跃模型(触发vision热重载)
DELETE /api/admin/models/{id}/ # 手动删除某个备份
# 系统配置(管理员)
GET /api/admin/config/
PATCH /api/admin/config/
# 用户管理(管理员)
GET /api/admin/users/
POST /api/admin/users/
PATCH /api/admin/users/{id}/
GET /api/admin/groups/
POST /api/admin/groups/
# 系统日志(直接复用)
GET /api/admin/logs/
```
---
## 7. mingxi-frontend(管理后台)
### 7.1 定位
**纯管理后台**,供组长、厂长、管理员通过浏览器使用。操作工不通过浏览器操作(操作工使用前置机桌面程序)。
### 7.2 技术栈
```
框架:Vue 3 + Vite + TypeScript(保持现有配置不变)
UI:Element Plus(复用现有)
图表:ECharts(统计报表)
实时:短轮询(每1秒请求一次最新记录,满足1-2秒延迟要求,无需WebSocket)
```
> 短轮询而非 WebSocket:部署简单,Django 无需 Channels,延迟满足要求。
### 7.3 页面规划
| 路由 | 页面 | 可见角色 | 现有/新增 |
|------|------|---------|---------|
| `/login` | 登录 | 全部 | 复用 |
| `/dashboard` | 实时监控看板 | 组长、管理员 | **新增** |
| `/history` | 检测历史记录 | 全部(按权限过滤) | **新增** |
| `/stats` | 统计报表 | 组长、管理员 | **新增** |
| `/admin/models` | 模型管理 | 管理员 | **新增** |
| `/admin/config` | 系统配置 | 管理员 | **新增** |
| `/admin/users` | 用户管理 | 管理员 | 复用改造 |
### 7.4 实时监控看板(/dashboard)
- 最新检测结果卡片(标注图缩略图 + 结论)
- 今日合格率仪表盘
- 缺陷类型分布饼图
- 近10条记录时间线
- 每1秒轮询 `/api/detect/results/?limit=1&ordering=-created_at`
### 7.5 需删除的现有页面
```
SceneNews 相关页面
通用YOLO视频/摄像头检测页面
驾驶员疲劳检测相关组件
```
---
## 8. 系统间通信
### 8.1 完整通信图
```
前置机(Windows) 推理服务器(Linux)
┌──────────────────────┐ ┌──────────────────────┐
│ mingxi-capture │ │ mingxi-vision │
│ │──① POST─────→│ /api/detect │
│ QThread推理线程 │←─ 结果返回──│ asyncio.Lock串行 │
│ │ └──────────────────────┘
│ 主线程展示结果 │
│ │ ┌──────────────────────┐
│ upload_queue线程 │──② 异步────→│ mingxi-backend │
│ (失败则重试) │ │ /api/detect/result │
└──────────────────────┘ │ │
│ SQLite存档 │
└──────────┬───────────┘
│ REST API
┌──────────▼───────────┐
│ mingxi-frontend │
│ 浏览器短轮询 │
└──────────────────────┘
管理员/组长/厂长 浏览器访问
```
### 8.2 关键通信约定
| 通信路径 | 协议 | 格式 | 备注 |
|---------|------|------|------|
| capture → vision | HTTP POST | multipart/form-data(图像二进制) | 同步,等待结果 |
| vision → capture | HTTP Response | JSON(含 base64 标注图) | 一次请求一次响应 |
| capture → backend | HTTP POST | JSON(推理结果) | 异步,本地队列保障 |
| browser → backend | HTTP REST | JSON | 短轮询 + 增删改查 |
| backend → vision(热重载) | HTTP POST | JSON | 仅管理员切换模型时触发 |
### 8.3 前置机配置两个地址
前置机桌面程序配置界面中填写:
- **推理服务地址**:`http://192.168.x.x:8001`(vision)
- **后台服务地址**:`http://192.168.x.x:8000`(backend)
两个服务通常在同一台服务器上,填写相同IP、不同端口。
---
## 9. 硬件集成(相机配置)
### 9.1 推荐型号
| 阶段 | 型号 | 分辨率 | 接口 | 用途 |
|------|------|--------|------|------|
| Demo/POC | MV-CS060-10GM | 600万像素 | USB3.0 | 算法验证,接工业PC |
| 产线部署 | MV-CA060-10GM | 600万像素 | GigE | 稳定产线,线长100m |
| 大板检测 | MV-CA120-10GM | 1200万像素 | GigE | 视野≥200mm的PCB |
### 9.2 分辨率选型计算
```
视野:100mm × 75mm(单次拍摄区域)
最小缺陷:0.1mm(针孔、断路)
像素尺寸 ≤ 0.1mm ÷ 3 = 0.033mm/pixel
所需分辨率:100 ÷ 0.033 ≈ 3030 pixels(水平)
结论:600万像素(3072×2048)满足需求 ✓
```
### 9.3 配套清单(单工位 Demo 版)
| 器材 | 型号/规格 | 估价 |
|------|----------|------|
| 工业相机 | MV-CS060-10GM | 1800-2500元 |
| 镜头 | 16mm C口定焦(1/1.8"靶面) | 500-800元 |
| LED环形光源 | 同轴/低角度,直径120mm | 400-600元 |
| USB3.0线缆 | ≤3m,锁扣式 | 80-150元 |
| 简易支架光箱 | 铝型材,可调高度 | 200-400元 |
| **合计** | | **约 3000-4450元** |
---
## 10. 开发阶段划分
### Phase 1:Demo版(目标:2026年6月中旬决赛前)
**路演方式:** 预先准备好的PCB图片,模拟模式演示完整流程,约5分钟。
**核心展示:** 操作工点击→检测→标注图展示→管理后台历史记录。
```
P0(必须完成,路演核心):
[ ] mingxi-vision:图片推理API + asyncio.Lock + 启动warmup
[ ] mingxi-capture:PyQt5桌面程序,Mock模式,结果展示界面
[ ] mingxi-backend:接收capture推送,DefectResult落库,基础REST API
[ ] mingxi-frontend:登录 + 检测历史列表 + 详情页
[ ] mingxi-backend:三级权限(操作工/组长/管理员)
P1(有时间做,加分项):
[ ] mingxi-frontend:实时监控看板(短轮询)
[ ] mingxi-backend:模型管理(上传/备份/切换)
[ ] mingxi-frontend:统计报表页
[ ] mingxi-capture:系统托盘图标
```
### Phase 2:POC版(目标:2026年Q3,接入真实相机)
```
[ ] mingxi-capture:接入海康MVS SDK,对接 MV-CS060-10GM
[ ] mingxi-capture:PLC触发接口(串口/数字IO)
[ ] mingxi-vision:ONNX Runtime部署(export_onnx.py导出)
[ ] mingxi-vision:模型热重载接口完善
[ ] mingxi-backend:告警规则引擎(连续不良数、缺陷率阈值)
[ ] 完成1家PCB厂POC试用
```
### Phase 3:产线版(目标:2026年Q4,首单签约)
```
[ ] 多相机支持(多条产线并发采集)
[ ] 模型迁移学习(基于客户真实样本微调)
[ ] 告警推送(企业微信/短信)
[ ] MES/ERP对接接口
[ ] 边缘部署探索(Jetson Orin,脱离大型服务器)
[ ] 软件著作权申请
```
---
## 11. 开发环境搭建
### 11.1 各项目启动命令
```bash
# mingxi-vision(推理服务器,Linux)
cd mingxi-vision
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8001 --workers 1
# mingxi-backend(推理服务器,Linux)
cd mingxi-backend
pip install -r requirements.txt
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
# mingxi-frontend(推理服务器,Linux)
cd mingxi-frontend
npm install
npm run build # 生产:dist/ 由 nginx 托管
npm run dev # 开发:localhost:5173
# mingxi-capture(前置机,Windows,开发阶段)
cd mingxi-capture
pip install -r requirements.txt
python main.py # 启动桌面程序,自动进入Mock模式(若配置了图片目录)
```
### 11.2 环境变量
```bash
# mingxi-vision/.env
MODEL_PATH=./models/pcb_defect_v1.pt
RUNTIME=ultralytics # 或 onnx
DEVICE=cuda # 或 cpu
CONF_THRESHOLD=0.45
# mingxi-backend/.env
SECRET_KEY=...
DATABASE_PATH=./db.sqlite3
VISION_SERVICE_URL=http://127.0.0.1:8001
CORS_ALLOWED_ORIGINS=http://localhost:5173
MODEL_STORAGE_DIR=/opt/mingxi/models
MODEL_MAX_BACKUPS=10
```
### 11.3 开发联调注意事项
- `mingxi-capture` 在 Windows 开发机上运行,配置 vision/backend 为开发机局域网IP
- `mingxi-vision` 和 `mingxi-backend` 建议在同一台机器(或WSL2)上启动
- SQLite 文件在 `mingxi-backend/` 目录下,无需额外数据库服务
---
## 12. 参考资料
| 资料 | 路径/来源 |
|------|---------|
| 创业技术书 | `梅州创新创业大赛/创业技术书v1.3.docx` |
| 明析vision架构详细设计 | `梅州创新创业大赛/mingxi-vision-架构设计.md` |
| 现有后端代码 | `yolo_classification_system/` |
| PCB缺陷检测调研 | `梅州创新创业大赛/pcb缺陷检测初步方案.md` |
| 海康MVS SDK | 随MVS安装包提供,路径:`C:\Program Files (x86)\MVS\Development\` |
| HRIPCB数据集 | 公开学术数据集,CC BY 4.0,6类缺陷,10,000+样本 |
| PyQt5文档 | https://doc.qt.io/qtforpython-5/ |
| PyInstaller文档 | https://pyinstaller.org/en/stable/ |
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+7
View File
@@ -0,0 +1,7 @@
Django==4.1.13
djangorestframework==3.14.0
django-cors-headers==4.3.1
PyJWT==2.8.0
python-dotenv==1.0.0
Pillow==10.2.0
requests==2.31.0
View File
View File
View File
View File
View File
View File
+5
View File
@@ -0,0 +1,5 @@
PyQt5==5.15.10
requests==2.31.0
opencv-python==4.8.1.78
numpy==1.24.4
pyinstaller==6.3.0
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+9
View File
@@ -0,0 +1,9 @@
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==1.10.13
ultralytics==8.0.235
onnxruntime-gpu==1.16.3
opencv-python-headless==4.8.1.78
numpy==1.24.4
python-multipart==0.0.6
python-dotenv==1.0.0
View File
View File
View File