Vision Studio 完全教程
本教程采用实例驱动的方式,每一步都配有参数说明、代码示例、运行结果示意图和常见错误分析。适合从零开始学习机器视觉的工程师,也适合有经验者快速查阅。
第一章:视觉系统入门
1.1 什么是机器视觉?
机器视觉 (Machine Vision) 是让工业相机"看懂"生产场景的技术。与人类视觉相比:
| 对比项 | 人类视觉 | 机器视觉 |
|---|---|---|
| 速度 | 约 200ms 反应时间 | 微秒到毫秒级 |
| 精度 | 约 0.1mm (人眼分辨率) | 亚微米级 (< 0.001mm) |
| 疲劳 | 会疲劳,注意力下降 | 7×24 小时稳定 |
| 量化 | 定性判断(好/坏) | 定量测量(精确到 0.01mm) |
| 重复性 | 不同人不同结果 | 同一程序结果一致 |
| 适应环境 | 适应光照变化 | 需稳定光照 |
1.2 工业视觉典型应用场景
场景一:外观缺陷检测
┌─────────────────────────────────────────────┐
│ 传送带 → 产品到位传感器 → 相机拍照 │
│ → 深度学习模型检测缺陷 │
│ → 有缺陷 → 剔除器动作 │
│ → 无缺陷 → 流入下一工序 │
└─────────────────────────────────────────────┘
场景二:定位引导机器人抓取
┌─────────────────────────────────────────────┐
│ 料盘 → 相机拍照 │
│ → 模板匹配定位工件 │
│ → 计算 X/Y/角度偏移 │
│ → 发送坐标给机器人 │
│ → 机器人准确抓取 │
└─────────────────────────────────────────────┘
场景三:条码/二维码读取
┌─────────────────────────────────────────────┐
│ 包裹 → 触发相机 │
│ → 图像增强 → 解码条码/二维码 │
│ → 查询数据库 → 决定分拣通道 │
│ → 分拣机构动作 │
└─────────────────────────────────────────────┘
场景四:尺寸测量分选
┌─────────────────────────────────────────────┐
│ 零件 → 背光照明 → 相机拍照 │
│ → 边缘检测 → 拟合圆和直线 │
│ → 计算孔径/间距/角度 │
│ → 判断是否在公差范围内 │
│ → OK/NG 分选 │
└─────────────────────────────────────────────┘
1.3 Vision Studio 架构概览
Vision Studio 是 Darra RT IDE 内置的机器视觉开发环境,采用四层架构:
┌──────────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ 外观缺陷检测 │ │ 定位引导抓取 │ │ 条码识别 / OCR │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
├─────────┴────────────────┴────────────────────┴─────────────┤
│ 推理层 (Inference) │
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
│ │ ONNX Runtime (GPU/CUDA) │ │ OpenCV DNN (CPU 回退) │ │
│ │ • YOLOv8/v9/v10/v11 │ │ • 轻量模型 │ │
│ │ • ResNet/EfficientNet │ │ • 开发调试阶段 │ │
│ │ • 自定义 ONNX 模型 │ │ • 低算力边缘设备 │ │
│ └──────────────────────────┘ └──────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ 采集层 (Acquisition) │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────────┐ │
│ │ USB3 Vision│ │ GigE Vision│ │ GenTL (Camera Link/ │ │
│ │ (UVC) │ │ (1000BaseT)│ │ CoaXPress) │ │
│ └────────────┘ └────────────┘ └────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ 硬件层 (Hardware) │
│ USB 3.0 / GigE / Camera Link / CoaXPress │
└──────────────────────────────────────────────────────────────┘
各层职责:
| 层 | 职责 | 典型组件 |
|---|---|---|
| 硬件层 | 物理连接与数据传输 | 相机、镜头、光源、线缆、采集卡 |
| 采集层 | 相机驱动与图像获取 | USB 驱动、GigE Vision 协议、GenTL Producer |
| 推理层 | 模型推理与图像处理 | ONNX Runtime、OpenCV DNN |
| 应用层 | 业务逻辑与 PLC 集成 | 卡片图流程、Vision SCL 程序、PLC 变量映射 |
1.4 硬件要求
相机选型指南
| 应用场景 | 推荐相机类型 | 分辨率 | 帧率 | 传感器 |
|---|---|---|---|---|
| 外观缺陷检测 | 面阵相机 | 500万~2000万像素 | 30~75 fps | CMOS 全局快门 |
| 定位引导 | 面阵相机 | 130万~500万像素 | 30~60 fps | CMOS 全局快门 |
| 条码读取 | 线阵或面阵 | 200万~500万像素 | 60~200 fps | CMOS 滚动/全局 |
| 尺寸测量 | 面阵相机 + 远心镜头 | 500万~1200万像素 | 15~30 fps | CCD/CMOS 全局 |
| 高速检测 | 高速相机 | 130万~400万像素 | 200~1000 fps | CMOS 全局 |
| OCR 字符识别 | 高分辨率面阵 | 1200万像素以上 | 15~30 fps | CMOS 全局 |
镜头选型指南
| 镜头类型 | 特点 | 适用场景 |
|---|---|---|
| 定焦镜头 | 焦距固定,成像质量好 | 通用场景 |
| 变焦镜头 | 焦距可调,灵活性高 | 多工位切换 |
| 远心镜头 | 无透视误差,测量精度高 | 尺寸测量 |
| 微距镜头 | 高放大倍率 | 小零件检测 |
| 线扫镜头 | 适配线阵相机 | 卷材检测 |
镜头焦距计算公式:
焦距 f = (工作距离 × 传感器宽度) / 视野宽度
示例:工作距离 300mm,传感器宽 6.4mm(1/2"),视野宽 100mm
f = (300 × 6.4) / 100 = 19.2mm → 选用 20mm 定焦镜头
光源选型指南
| 光源类型 | 特点 | 适用场景 |
|---|---|---|
| 环形光源 | 均匀环形照明,消除阴影 | 字符识别、通用检测 |
| 背光源 | 从背面照明,轮廓清晰 | 尺寸测量、边缘检测 |
| 同轴光源 | 垂直照明,消除反光 | 镜面表面检测 |
| 条形光源 | 角度可调,突出缺陷 | 划痕检测、表面纹理 |
| 穹顶光源 | 漫射均匀照明 | 球形/反光表面 |
| 暗场光源 | 低角度照明,突出凹凸 | 划痕、压印检测 |
工控机推荐配置
| 配置等级 | CPU | 内存 | GPU | 存储 | 适用场景 |
|---|---|---|---|---|---|
| 入门级 | i5-12500 | 16GB | 无(CPU 推理) | 256GB SSD | 单相机简单检测 |
| 标准级 | i7-12700 | 32GB | RTX 3060 12GB | 512GB SSD | 双相机标准检测 |
| 高性能 | i9-13900 | 64GB | RTX 4090 24GB | 1TB NVMe | 四相机深度学习 |
| 边缘级 | NUC 13 Pro | 16GB | Intel Iris Xe | 256GB SSD | 边缘设备部署 |
1.5 软件流程概述
一个完整的视觉项目通常经历以下阶段:
阶段一:环境搭建
├─ 安装 Darra RT IDE
├─ 连接相机硬件
├─ 安装相机驱动(GenICam / USB)
└─ 配置 RuntimeResourcePolicy
阶段二:视觉编程
├─ 添加相机并配置参数
├─ 创建视觉程序(卡片图或 Vision SCL)
├─ 配置图像预处理流程
├─ 导入并配置模型
├─ 配置结果输出与 PLC 变量映射
└─ 配置触发条件与事件处理
阶段三:调试与验证
├─ 单步运行验证每个环节
├─ 调整参数优化效果
├─ 批量测试验证稳定性
└─ 模型验证与版本管理
阶段四:部署运行
├─ 部署模型到运行时
├─ 配置 RuntimeResourcePolicy
├─ 启动视觉服务
└─ 监控运行状态
1.6 常见误区
| 误区 | 正确理解 |
|---|---|
| 分辨率越高越好 | 分辨率越高,数据量越大,推理越慢。够用就好 |
| 深度学习万能 | 简单场景用传统方法(Blob/模板匹配)更快更稳 |
| 相机随便装 | 安装位置、角度、高度直接影响检测效果 |
| 光源随便选 | 光源是视觉系统最重要的部分,占项目成败的 70% |
| 参数一次调好 | 参数需要根据实际产线环境反复调试 |
第二章:相机配置
2.1 相机类型
Vision Studio 支持三种相机接口:
USB3 Vision (UVC)
即插即用,适合开发调试和小型系统。
特点:
- 带宽:USB 3.0 约 350 MB/s,USB 2.0 约 40 MB/s
- 线缆长度:最长 5 米(USB 3.0),可加延长器到 20 米
- 成本:低
- 适用:单相机、近距离、开发调试
支持设备:
- UVC 兼容相机(大多数 USB 工业相机)
- 海康机器人 USB 系列
- 大恒图像 USB 系列
- 普通 USB 摄像头(开发调试用)
GigE Vision
工业标准,适合远距离、多相机系统。
特点:
- 带宽:1000BASE-T 约 125 MB/s
- 线缆长度:最长 100 米(CAT6)
- 成本:中等
- 适用:多相机、远距离、产线部署
支持设备:
- Basler GigE 系列
- 海康机器人 GigE 系列
- 大恒图像 GigE 系列
- 符合 GenICam 标准的 GigE 相机
GenTL 采集卡
高性能,适合高速/高分辨率相机。
特点:
- 带宽:Camera Link 约 850 MB/s,CoaXPress 约 1.25 GB/s
- 线缆长度:Camera Link 最长 10 米,CoaXPress 最长 40 米
- 成本:高
- 适用:高速检测、高分辨率、工业相机
支持设备:
- Matrox 采集卡
- Euresys 采集卡
- Silicon Software 采集卡
- 厂商 GenTL Producer 兼容
2.2 添加相机
操作步骤:
- 在 IDE 左侧面板中选择 视觉系统 标签页
- 点击 添加相机 按钮
- 系统自动扫描可用相机
┌───────────────────────────────────────────────┐
│ 添加相机 │
│ │
│ ● 扫描到 3 个设备 │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ ☑ USB 相机 #1 (USB 3.0) │ │
│ │ Hikvision MV-CA013-UC │ │
│ │ 1280×1024 30fps │ │
│ ├─────────────────────────────────────────┤ │
│ │ ☐ GigE 相机 #1 (192.168.1.100) │ │
│ │ Basler acA2440-75um │ │
│ │ 2448×2048 75fps │ │
│ ├─────────────────────────────────────────┤ │
│ │ ☐ GenTL 采集卡 #1 │ │
│ │ Matrox Radient eV-CL │ │
│ │ 4096×3072 60fps │ │
│ └─────────────────────────────────────────┘ │
│ │
│ [ 添加选中设备 ] [ 刷新扫描 ] │
└───────────────────────────────────────────────┘
- 勾选要添加的相机,点击 添加选中设备
- 相机出现在视觉系统面板中,可开始配置参数
2.3 相机参数详解
曝光 (Exposure)
曝光时间决定了相机传感器收集光线的时间长度。
曝光时间 = 传感器收集光线的总时间
┌──────────────┐
│ │ 曝光时间短 → 图像暗,但运动模糊少
│ 电荷积累 │ 曝光时间长 → 图像亮,但运动模糊多
│ │
└──────────────┘
←── 曝光时间 ──→
| 参数 | 说明 | 取值范围 | 典型值 |
|---|---|---|---|
ExposureAuto | 自动曝光 | TRUE/FALSE | TRUE(开发时) |
ExposureTime | 手动曝光时间 (µs) | 10~1000000 | 3000~5000 µs |
ExposureMode | 曝光模式 | Timed/TriggerWidth | Timed |
选择曝光时间的经验法则:
- 静态物体:曝光时间 = 2000~5000 µs
- 运动物体:曝光时间 < 运动速度的倒数(如 2m/s → < 500 µs)
- 暗光环境:曝光时间适当增加,但注意运动模糊
曝光与帧率的关系:
最大帧率 ≈ 1 / (曝光时间 + 读出时间)
示例:曝光 5000 µs,读出时间 10000 µs
最大帧率 ≈ 1 / (0.005 + 0.010) = 66.7 fps
增益 (Gain)
增益放大图像信号,但也会放大噪声。
原始信号 ──→ [增益放大器] ──→ 放大后的信号(含噪声)
| 参数 | 说明 | 取值范围 | 典型值 |
|---|---|---|---|
GainAuto | 自动增益 | TRUE/FALSE | FALSE(手动控制) |
Gain | 手动增益 (dB) | 0~24 | 0~6 dB |
最佳实践:
- 优先调曝光时间,增益尽量小(< 6 dB)
- 增益 > 12 dB 时噪声明显增加
- 自动增益只适用于光照变化大的场景
触发模式 (Trigger Mode)
| 模式 | 说明 | 适用场景 |
|---|---|---|
Software | 软件触发 | 开发调试、手动触发 |
Hardware | 硬件 Line 触发 | 产线同步采集 |
FreeRun | 自由运行 | 连续采集监控 |
硬件触发接线:
相机 Line0 ──── PLC DO 输出 ──── 产品到位传感器
│
├── 上升沿触发采集
└── 下降沿触发结束曝光
像素格式 (Pixel Format)
| 格式 | 每像素位数 | 说明 | 适用场景 |
|---|---|---|---|
Mono8 | 8 bit | 灰度,数据量小 | 条码、尺寸测量 |
Mono10 | 10 bit | 灰度,高动态范围 | 缺陷检测 |
Mono12 | 12 bit | 灰度,更高动态范围 | 高精度检测 |
BayerRG8 | 8 bit | RAW 数据,需去马赛克 | 彩色相机原始数据 |
RGB8 | 24 bit | 彩色 | 颜色识别、通用 |
YUV422 | 16 bit | 彩色,压缩格式 | 视频流 |
2.4 多相机管理
Vision Studio 支持同时管理多个相机,每个相机独立配置。
添加多个相机:
视觉系统面板
┌──────────────────────────────────────────┐
│ 视觉系统 │
│ ┌──────────────────────────────────────┐│
│ │ 相机列表 ││
│ │ ├─ ● USB_CAM_1 [运行中] ││
│ │ │ 1280×1024 30fps 曝光 5000µs ││
│ │ ├─ ● GIGE_CAM_1 [运行中] ││
│ │ │ 2448×2048 75fps 曝光 3000µs ││
│ │ └─ ○ GENTL_CAM_1 [停止] ││
│ │ 4096×3072 60fps 曝光 2000µs ││
│ └──────────────────────────────────────┘│
│ │
│ [添加相机] [启动全部] [停止全部] │
└──────────────────────────────────────────┘
常见错误:
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 相机扫描不到 | 驱动未安装 / 线缆松动 | 检查设备管理器,安装 GenICam 驱动 |
| 相机报错 Timeout | 带宽不足 / 触发信号丢失 | 降低分辨率或帧率,检查触发线 |
| 多相机帧率下降 | USB 带宽共享 | 使用独立 USB 控制器,或换 GigE |
| 图像花屏 | 线缆质量问题 / 干扰 | 更换屏蔽线缆,检查接地 |
2.5 相机校准
相机校准的目的是确定相机的内参(焦距、畸变系数)和外参(位置、姿态)。
内参标定
为什么要标定:
- 消除镜头畸变(桶形/枕形畸变)
- 确定像素当量(mm/pixel)
- 校正透视误差
标定步骤:
- 准备标定板(棋盘格或圆点阵列)
┌────────────────────────────────┐
│ ██░░██░░██░░██░░██░░██░░██ │
│ ░░██░░██░░██░░██░░██░░██░░ │
│ ██░░██░░██░░██░░██░░██░░██ │
│ ░░██░░██░░██░░██░░██░░██░░ │
│ ██░░██░░██░░██░░██░░██░░██ │
│ ░░██░░██░░██░░██░░██░░██░░ │
│ ██░░██░░██░░██░░██░░██░░██ │
│ ░░██░░██░░██░░██░░██░░██░░ │
│ │
│ 棋盘格标定板 (7×7 内角点) │
│ 方格边长: 10mm │
└────────────────────────────────┘
- 从不同角度拍摄 10~20 张标定板图像
拍摄角度要求:
┌────────────────────────────────────────────┐
│ │
│ [正对] [左偏] [右偏] [上偏] [下偏] │
│ [倾斜] [旋转] [远距] [近距] [四角] │
│ │
│ 要求:覆盖整个视野、角度丰富 │
│ 标定板占视野 1/4~1/2 │
└────────────────────────────────────────────┘
- 在 Vision Studio 中打开标定工具,导入图像
- 系统自动检测角点并计算内参
标定结果示例:
相机标定报告
─────────────────────────────────────────────
相机: USB_CAM_1 (Hikvision MV-CA013-UC)
标定日期: 2026-07-20 14:30:00
标定板: 棋盘格 7×7, 方格 10mm
内参矩阵:
fx = 2456.78 fy = 2455.23
cx = 643.12 cy = 511.45
畸变系数:
k1 = -0.0452 k2 = 0.0123
p1 = 0.0005 p2 = -0.0003
k3 = -0.0012
标定误差: 0.08 像素 ✅
像素当量 (工作距离 300mm): 0.052 mm/pixel
─────────────────────────────────────────────
畸变矫正
标定完成后,可在预处理流程中插入畸变矫正:
[相机采集] ──→ [畸变矫正] ──→ [后续处理]
参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
CameraName | 使用哪个相机的标定参数 | 当前相机 |
Scale | 缩放因子 | 1.0 |
Interpolation | 插值方式 | Linear |
畸变矫正前后对比:
矫正前: 矫正后:
┌──────────────┐ ┌──────────────┐
│ ┌──┐ │ │ ┌──┐ │
│ │ │ 弯曲 │ │ │ │ 直线 │
│ └──┘ │ │ └──┘ │
│ ┌──┐ │ │ ┌──┐ │
│ │ │ │ │ │ │ │
│ └──┘ │ │ └──┘ │
└──────────────┘ └──────────────┘
桶形畸变 矫正后(直线)
2.6 常见错误
| 错误现象 | 可能原因 | 解决方法 |
|---|---|---|
| "设备未找到" | 相机驱动未安装 | 安装 GenICam 驱动或 UVC 驱动 |
| "采集超时" | 触发信号未到 | 检查触发模式,软触发直接点采集 |
| "图像全黑" | 曝光时间过短/光圈关闭 | 增加曝光时间,打开镜头光圈 |
| "图像全白" | 曝光时间过长/光照过强 | 减少曝光时间,降低增益 |
| "图像闪烁" | 光源频闪(50Hz 日光灯) | 曝光时间设为 20ms 整数倍 |
| "帧率低" | 带宽不足 | 降低分辨率,使用更小编码 |
第三章:图像采集
3.1 采集模式
Vision Studio 支持三种采集模式:
单帧采集 (Snap)
采集一帧图像后停止,适合触发式应用。
触发信号 ──→ 相机采集一帧 ──→ 输出图像 ──→ 等待下次触发
参数配置:
| 参数 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
Timeout | 采集超时 (ms) | 5000 | 3000~5000 |
ExposureMode | 曝光模式 | Timed | Timed |
TriggerMode | 触发模式 | Software | 根据场景 |
卡片图配置:
[触发信号] ──→ [相机采集 (Snap)] ──→ [后续处理]
参数: Timeout=5000
连续采集 (Grab)
连续采集图像流,适合视频监控类应用。
相机 ──→ 连续采集 ──→ 帧 1 → 帧 2 → 帧 3 → ... → 帧 N
参数配置:
| 参数 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
FrameRate | 目标帧率 (fps) | 30 | 15~30 |
BufferCount | 缓存帧数 | 4 | 4~8 |
DropMode | 丢帧策略 | Oldest | Oldest |
卡片图配置:
[相机采集 (Grab)] ──→ [帧处理循环]
参数: FrameRate=30
软触发 (Software Trigger)
通过软件指令触发采集,适合开发调试。
PLC 逻辑 ──→ VIS_JOB_START = TRUE ──→ 软件触发 ──→ 采集
触发流程:
1. PLC 设置 VIS_JOB_START = TRUE
2. Vision Studio 检测到上升沿
3. 调用 CAM_Capture() 采集一帧
4. 执行后续处理
5. 完成后设置 VIS_JOB_DONE = TRUE
硬触发 (Hardware Trigger)
通过相机硬件 Line 信号触发,适合产线同步。
传感器 ──→ 信号 ──→ 相机 Line0 ──→ 触发采集 ──→ 图像输出
接线方式:
PLC 输出 DO ────┐
├──── 相机 Line0 (触发输入)
传感器输出 ────┘
触发时序:
传感器信号 ┌────┐ ┌────┐
│ │ │ │
└────┴──────────────┴────┴────────
│ │
触发信号 ┌────┐ ┌────┐
│ │ │ │
└────┴────────┴────┴────────
│ │
曝光时间 ←─── 曝光 ──→ ←─── 曝光 ──→
│ │
图像读出 │←── 读出 ──→│
编码器触发 (Encoder Trigger)
通过编码器信号触发,适合传送带同步。
编码器 ──→ A/B 相脉冲 ──→ 相机 ──→ 等距触发采集
参数配置:
| 参数 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
EncoderMode | 编码器模式 | ABPhase | ABPhase |
PulsesPerTrigger | 每触发脉冲数 | 1000 | 根据分辨率 |
Direction | 方向 | Forward | Forward |
Divider | 分频系数 | 1 | 1 |
3.2 图像格式与缓存
像素格式选择
| 格式 | 每像素 | 一帧 1280×1024 | 适用场景 |
|---|---|---|---|
Mono8 | 1 byte | 1.25 MB | 条码、尺寸测量 |
Mono12 | 1.5 byte | 1.88 MB | 高精度检测 |
RGB8 | 3 byte | 3.75 MB | 颜色识别 |
BayerRG8 | 1 byte | 1.25 MB | 需去马赛克 |
选择建议:
- 不需要颜色 → Mono8(数据量最小,处理最快)
- 需要颜色 → RGB8(直接可用,无需转换)
- 高精度灰度 → Mono12(动态范围更高)
缓存管理
图像缓存管理策略:
| 参数 | 说明 | 默认值 | 推荐值 |
|---|---|---|---|
MaxImageMemory | 图像缓存上限 (MB) | 512 | 256~1024 |
BufferCount | 采集缓存帧数 | 4 | 4~8 |
SwapToDisk | 超出缓存时写磁盘 | false | false |
CleanupInterval | 清理间隔 (s) | 60 | 60~300 |
缓存溢出处理:
缓存使用率 > 80% ──→ 清理最早图像
缓存使用率 > 95% ──→ 停止新采集,抛出警告
缓存使用率 = 100% ──→ 返回错误,等待清理
3.3 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 采集超时 | 触发信号未到 | 检查触发源,或临时切到软触发 |
| 图像撕裂 | 缓存不足 | 增加 BufferCount |
| 丢帧 | 处理速度跟不上采集 | 降低帧率,优化处理流程 |
| 内存溢出 | 图像缓存未及时释放 | 检查循环中是否有 IMG_Release |
第四章:图像预处理
预处理是视觉系统的"第一道工序",预处理质量直接影响后续检测和测量的效果。
4.1 滤波 (Filtering)
滤波的目的是去除图像噪声,同时尽量保留边缘和细节。
高斯滤波
高斯滤波使用高斯核对图像进行卷积,平滑去噪。
原理:以每个像素为中心,用高斯核计算加权平均
┌─────────────┐
│ 1 4 7 4 1│
│ 4 16 26 16 4│
│ 7 26 41 26 7│ ← 5×5 高斯核 (σ=1.0)
│ 4 16 26 16 4│
│ 1 4 7 4 1│
└─────────────┘
÷ 273 (归一化)
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
KernelSize | DWORD | 否 | 5 | 核大小(奇数:3/5/7/9/11) |
SigmaX | REAL | 否 | 1.5 | X 方向标准差 |
SigmaY | REAL | 否 | Auto | Y 方向标准差(=SigmaX) |
KernelSize 选择:
核大小 3 → 轻度去噪,保留细节
核大小 5 → 中等去噪,平衡效果
核大小 7 → 强力去噪,可能丢失小边缘
核大小 9 → 强去噪,仅适合大目标
示例:
输入: 输出(高斯滤波 5×5):
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ ●●●●● │ │ ───── │
│ ● ● │ → │ │ │ │
│ ● ● │ │ │ │ │
│ ●●●●● │ │ ───── │
│ │ │ │
└──────────────┘ └──────────────┘
带噪声的矩形 去噪后边缘模糊
中值滤波
中值滤波用邻域像素的中值代替当前像素,对椒盐噪声特别有效。
原理:对邻域像素排序,取中值
原始像素值: 排序后: 中值:
[ 10, 255, 12 ] [ 10, 12, 15 ] 12
[ 8, 5, 14 ] [ 8, 5, 14 ]
[ 15, 255, 11 ] [ 11, 15, 255] ← 椒盐噪声 (255) 被排除了
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
KernelSize | DWORD | 否 | 5 | 核大小(奇数:3/5/7) |
应用场景:
- 去除椒盐噪声(传感器坏点)
- 保留边缘(比高斯滤波好)
- 不适合高斯噪声
双边滤波
双边滤波同时考虑空间距离和像素值差异,实现"保边去噪"。
原理:权重 = 空间权重 × 像素值权重
空间权重(高斯分布): 像素值权重(高斯分布):
┌───┐ ╱───╲
│ ● │ │ ← │ ← 边缘处权重小
└───┘ └───┘
中心权重最大 值差异大 → 权重小
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Diameter | DWORD | 否 | 9 | 邻域直径 |
SigmaColor | REAL | 否 | 75 | 颜色空间标准差 |
SigmaSpace | REAL | 否 | 75 | 坐标空间标准差 |
示例:
输入: 输出(双边滤波):
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ ┌──────┐ │ │ ┌──────┐ │
│ │ 噪声 │ │ → │ │ 平滑 │ │
│ │ 区域 │ │ │ │ (边缘保留)│
│ └──────┘ │ │ └──────┘ │
│ 边缘 │ │ 边缘清晰 │
└──────────────┘ └──────────────┘
滤波方法对比
| 方法 | 去噪效果 | 边缘保留 | 速度 | 适用场景 |
|---|---|---|---|---|
| 高斯滤波 | 中 | 差 | 快 | 通用去噪 |
| 中值滤波 | 好(椒盐噪声) | 中 | 中 | 传感器坏点修复 |
| 双边滤波 | 好 | 好 | 慢 | 保边去噪 |
| 均值滤波 | 差 | 差 | 最快 | 快速预览 |
4.2 形态学 (Morphology)
形态学操作基于图像形状进行处理,通常用在二值图像上。
腐蚀 (Erode)
腐蚀操作"收缩"前景区域,去除小的白色噪点。
原理:核范围内全为前景 → 保留,否则变背景
输入: 输出(腐蚀 3×3):
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ ●●●●● │ │ ●●● │
│ ● ● │ → │ │ │ ← 边缘被腐蚀
│ ● ● │ │ │ │
│ ●●●●● │ │ ●●● │
│ │ │ │
└──────────────┘ └──────────────┘
膨胀 (Dilate)
膨胀操作"扩张"前景区域,填充小的空洞。
原理:核范围内有前景 → 变前景
输入: 输出(膨胀 3×3):
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ ●●● │ │ ●●●●● │
│ ● │ → │ ●●●●● │ ← 边缘膨胀
│ ●●● │ │ ●●●●● │
│ │ │ │
└──────────────┘ └──────────────┘
开运算 (Open)
先腐蚀后膨胀,去除小噪点,分离相连物体。
应用:去除噪点、断开粘连
输入: 腐蚀后: 开运算结果:
┌──────┐ ┌──────┐ ┌──────┐
│ ●●○ │ │ ●● │ │ ●● │
│ ○●● │ → 腐蚀 │ ●● │ → 膨胀 │ ●● │
│ ●○● │ │ │ │ │ ← 小点被清除
└──────┘ └──────┘ └──────┘
● 前景 ○ 噪点
闭运算 (Close)
先膨胀后腐蚀,填充小空洞,连接断开的物体。
应用:填充空洞、连接断线
输入: 膨胀后: 闭运算结果:
┌──────┐ ┌──────┐ ┌──────┐
│ ● ○ │ │ ●●● │ │ ●●● │
│ │ → 膨胀 │ ●●● │ → 腐蚀 │ ●●● │ ← 空洞被填充
│ ● ○ │ │ ●●● │ │ ●●● │
└──────┘ └──────┘ └──────┘
● 前景 ○ 空洞
形态学参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Operation | ENUM | 是 | — | Erode/Dilate/Open/Close/Gradient/TopHat/BlackHat |
KernelShape | ENUM | 否 | Rect | Rect/Cross/Ellipse |
KernelSize | DWORD | 否 | 3 | 核大小 (3/5/7/...) |
Iterations | DWORD | 否 | 1 | 迭代次数 |
KernelShape 选择:
Rect (矩形) Cross (十字) Ellipse (椭圆)
███ █ ███
███ ███ █████
███ █ ███
通用形状 保留矩形角 圆形物体
4.3 阈值分割 (Thresholding)
阈值分割将灰度图像转换为二值图像,分离前景和背景。
全局阈值
像素值 > 阈值 → 白色 (255)
像素值 ≤ 阈值 → 黑色 (0)
输入灰度图: 阈值 128 后:
┌────────────────┐ ┌────────────────┐
│ 200 150 100 50 │ │ 255 255 0 0│
│ 180 120 80 40 │ │ 255 0 0 0│
│ 160 100 60 30 │ │ 255 0 0 0│
│ 140 90 50 20 │ │ 255 0 0 0│
└────────────────┘ └────────────────┘
自适应阈值
根据局部区域自动计算阈值,适合光照不均匀的图像。
原理:每个像素的阈值 = 该像素邻域均值 - C
┌──────────────┐
│ 局部区域计算 │
│ 阈值 = 均值 - C│
└──────────────┘
│
▼
输入(光照不均)→ 自适应阈值 → 均匀的二值图
Otsu 自动阈值
Otsu 算法自动寻找最佳阈值,使前景和背景的类间方差最大。
Otsu 算法原理:
1. 计算直方图
2. 遍历所有可能的阈值 (0~255)
3. 计算每个阈值的类间方差
4. 选择类间方差最大的阈值
直方图:
▲
│ ████
│ ████████
│ ████████████
│ ████████████████
│████████████████████
└─────────────────────▶
0 128 255
↑
最佳阈值 (Otsu)
阈值参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Threshold | REAL | 否 | 128 | 手动阈值 |
MaxValue | REAL | 否 | 255 | 最大值 |
Type | ENUM | 否 | Binary | 二值化类型 |
UseOtsu | BOOL | 否 | FALSE | 使用 Otsu |
AdaptiveMethod | ENUM | 否 | None | 自适应方法 |
BlockSize | DWORD | 否 | 11 | 自适应块大小 |
二值化类型详解:
Binary: BinaryInv: Trunc:
255 ──── 255 ──── 255 ────
│ │ │
│ │ │───
0 ──────── 0 ──── 0 ────────
thresh 255 thresh 255 thresh 255
ToZero: ToZeroInv:
255 ──── 255 ────
│ │───
│ │
0 ──── 0 ────────
thresh 255 thresh 255
4.4 色彩空间转换
BGR ↔ Gray
BGR → Gray: Gray = 0.299 × R + 0.587 × G + 0.114 × B
彩色图: 灰度图:
┌────────────────┐ ┌────────────────┐
│ ████ ████ │ │ ▓▓▓▓ ▓▓▓▓ │
│ ██ ████ ██ │ → │ ▓▓ ▓▓▓▓ ▓▓ │
│ ████████████ │ │ ▓▓▓▓▓▓▓▓▓▓▓ │
│ ██ ████ ██ │ │ ▓▓ ▓▓▓▓ ▓▓ │
└────────────────┘ └────────────────┘
3 通道 (BGR) 1 通道 (Gray)
BGR ↔ HSV
HSV 色相更接近人类视觉感知,适合颜色识别。
H (Hue): 色相 0°~180° (OpenCV 范围)
S (Saturation): 饱和度 0~255
V (Value): 明度 0~255
HSV 色环:
0° (红)
330° 30°
\ /
300° ○ 60°
/ \
270° 90°
240° 120°
210° 150°
180° (青)
颜色提取示例:
要提取红色物体:
1. 转 HSV: IMG_CvtColor(img, BGR2HSV)
2. 红色范围: H=0~10 或 160~180, S>50, V>50
3. 阈值: IMG_Threshold(hsv, ...)
结果:
原始图 → HSV 空间 → 红色阈值 → 红色区域二值图
4.5 几何变换
旋转
参数: Angle = 45°, Center = (640, 512), Scale = 1.0
旋转前: 旋转 45° 后:
┌────────────────┐ ┌────────────────┐
│ │ │ ┌─────┐ │
│ ┌──────────┐ │ │ / /│ │
│ │ │ │ → │ ┌─────┐ │ │
│ │ │ │ │ │ │/ │
│ └──────────┘ │ │ └─────┘ │
│ │ │ │
└────────────────┘ └────────────────┘
缩放
参数: Width=640, Height=480, Interpolation=Linear
原图 1280×1024: 缩放至 640×480:
┌────────────────┐ ┌────────────────┐
│ │ │ │
│ │ → │ │
│ │ │ │
│ │ │ │
└────────────────┘ └────────────────┘
插值方式选择:
| 插值方式 | 质量 | 速度 | 适用场景 |
|---|---|---|---|
| Nearest | 差 | 最快 | 快速预览 |
| Linear | 中 | 快 | 通用缩放 |
| Cubic | 好 | 中 | 高质量缩放 |
| Lanczos4 | 最好 | 慢 | 精确测量 |
仿射变换
仿射变换保持平行线,适合纠正倾斜。
变换矩阵:[[a, b, tx],
[c, d, ty]]
输入: 输出(纠正倾斜):
┌────────────────┐ ┌────────────────┐
│ ┌──────────┐ │ │ ┌──────────┐ │
│ \ \ │ → │ │ │ │
│ \────────\ │ │ │ │ │
│ │ │ └──────────┘ │
└────────────────┘ └────────────────┘
倾斜的物体 纠正后的物体
透视变换
透视变换将任意四边形映射为矩形,适合纠正透视畸变。
输入四个点: 输出矩形:
(x1,y1) ─── (x2,y2) (0,0) ─── (w,0)
│ │ → │ │
│ │ │ │
(x3,y3) ─── (x4,y4) (0,h) ─── (w,h)
4.6 图像增强
直方图均衡
增强图像对比度,使直方图均匀分布。
暗图: 直方图均衡后:
┌────────────────┐ ┌────────────────┐
│ │ │ ▓▓▓▓▓▓▓▓▓▓▓▓ │
│ ▓▓▓▓▓▓ │ → │ ▓▓▓▓▓▓▓▓▓▓▓▓ │
│ │ │ ▓▓▓▓▓▓▓▓▓▓▓▓ │
│ │ │ │
└────────────────┘ └────────────────┘
细节不明显 细节清晰可见
直方图对比:
均衡前: 均衡后:
▲ ▲
│ ████ │ ████████████████
│ ████████████ │ ██████████████████
│ ██████████████ │ ████████████████████
└───────────▶ └───────────▶
0 255 0 255
集中在暗部 均匀分布
CLAHE (限制对比度自适应直方图均衡)
CLAHE 是直方图均衡的改进版,避免过度放大噪声。
原理:将图像分块 → 每块做直方图均衡 → 限制对比度 → 拼接
参数:
ClipLimit: 2.0 (对比度限制,越大增强越强)
TileGridSize: 8 (分块大小,越小局部增强越强)
ClipLimit=0 (标准均衡): ClipLimit=2.0 (CLAHE):
┌────────────────┐ ┌────────────────┐
│ ▓▓▓▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓▓▓▓ │
│ ▓▓▓▓▓▓▓▓▓▓ │ → │ ▓▓▓▓▓▓▓▓▓▓ │
│ ▓▓▓▓▓▓▓▓▓▓ │ │ ▓▓▓▓▓▓▓▓▓▓ │
│ 噪声被放大 │ │ 噪声抑制 │
└────────────────┘ └────────────────┘
4.7 ROI 提取
ROI (Region of Interest) 提取感兴趣区域,减少处理数据量。
完整图像 1280×1024: ROI 区域 400×400:
┌────────────────────┐ ┌────────────────┐
│ │ │ │
│ ┌──────────┐ │ │ │
│ │ ROI 区域 │ │ → │ │
│ │ 400×400 │ │ │ (只处理这个 │
│ └──────────┘ │ │ 区域) │
│ │ │ │
└────────────────────┘ └────────────────┘
处理量: 1.3 MB 处理量: 0.16 MB (节省 88%)
ROI 参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
X | DWORD | 是 | — | 左上角 X |
Y | DWORD | 是 | — | 左上角 Y |
Width | DWORD | 是 | — | 区域宽度 |
Height | DWORD | 是 | — | 区域高度 |
Relative | BOOL | 否 | FALSE | 相对坐标 (0.0~1.0) |
Relative 模式:
Relative=FALSE: X=100, Y=100, W=400, H=400
绝对像素坐标,从 (100,100) 到 (500,500)
Relative=TRUE: X=0.1, Y=0.1, W=0.5, H=0.5
相对坐标,从图像 10% 位置开始,取 50% 宽高
4.8 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 图像模糊 | 滤波核太大 | 减小 KernelSize |
| 边缘丢失 | 滤波过度 | 换双边滤波,或减小 Sigma |
| 阈值效果差 | 光照不均匀 | 用自适应阈值或 CLAHE |
| 形态学过度 | 迭代次数太多 | 减少 Iterations |
| ROI 越界 | 区域超出图像 | 检查 ROI 范围,或加边界检查 |
| 色彩转换错误 | 输入格式不匹配 | 检查输入图像通道数 |
第五章:目标检测 (Object Detection)
5.1 深度学习检测器
Vision Studio 使用 ONNX Runtime 进行深度学习推理,支持 YOLOv8/v9/v10/v11 等主流模型。
ONNX 模型推理
参数表:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
ModelPath | STRING | 是 | — | ONNX 模型文件路径 |
InputWidth | DWORD | 否 | 640 | 模型输入宽度 |
InputHeight | DWORD | 否 | 640 | 模型输入高度 |
Confidence | REAL | 否 | 0.5 | 置信度阈值 |
NMSThreshold | REAL | 否 | 0.45 | NMS 阈值 |
MaxDetections | DWORD | 否 | 256 | 最大检测目标数 |
LabelsPath | STRING | 否 | — | 标签文件路径 |
ExecutionProvider | ENUM | 否 | Auto | 推理后端 |
ExecutionProvider 选项:
| 值 | 说明 | 要求 |
|---|---|---|
Auto | 自动选择(优先 GPU) | — |
CPU | CPU 推理 | — |
CUDA | NVIDIA GPU 推理 | CUDA 11.8+ |
OpenCL | Intel GPU 推理 | OpenCL 2.0+ |
TensorRT | NVIDIA TensorRT | TensorRT 8.6+ |
推理流程:
输入图像 1280×1024 → 预处理 → 模型推理 → 后处理 → 检测结果
│ │ │ │
▼ ▼ ▼ ▼
原始图像 缩放至 ONNX NMS 过滤
640×640 推理 + 缩放回原图坐标
YOLOv8 输出解析:
模型输出: [1, 84, 8400]
84 = 4 (边界框: cx,cy,w,h) + 80 (COCO 类别概率)
8400 = 3 个尺度的检测结果总和
- 80×80 网格 (小物体)
- 40×40 网格 (中物体)
- 20×20 网格 (大物体)
后处理步骤:
1. 解析 8400 个候选框
2. 过滤置信度 < 阈值的候选框
3. 对每个类别做 NMS
4. 缩放回原始图像尺寸
卡片图配置:
[相机采集] ──→ [缩放 640×640] ──→ [目标检测] ──→ [结果输出]
│ │
│ ModelPath: model.onnx
│ Confidence: 0.5
│ NMS: 0.45
│
│ (预处理)
│ Interpolation: Linear
│ KeepAspectRatio: TRUE
│ PadColor: Black
运行结果示例:
┌──────────────────────────────────────────────┐
│ 检测结果:3 个目标 │
│ │
│ ┌──────────┬────────┬────────┬────────────┐ │
│ │ 类别 │ 置信度 │ 位置 (x,y) │ 尺寸 (w,h)│ │
│ ├──────────┼────────┼────────┼────────────┤ │
│ │ bottle │ 0.92 │ (320,240) │ (80, 120) │ │
│ │ cap_top │ 0.88 │ (640,200) │ (60, 40) │ │
│ │ lid │ 0.76 │ (800,500) │ (100, 60) │ │
│ └──────────┴────────┴────────┴────────────┘ │
│ │
│ 推理耗时: 8 ms (GPU) │
└──────────────────────────────────────────────┘
模型导出到 ONNX
YOLOv8 导出示例 (Python):
from ultralytics import YOLO
# 加载训练好的模型
model = YOLO("runs/detect/train/weights/best.pt")
# 导出为 ONNX
model.export(format="onnx", imgsz=640, opset=19)
# 输出: best.onnx
导出参数说明:
| 参数 | 说明 | 推荐值 |
|---|---|---|
format | 导出格式 | "onnx" |
imgsz | 输入尺寸 | 640(与训练时一致) |
opset | ONNX opset 版本 | 19(与 Vision Studio 相容) |
simplify | 简化模型 | True |
dynamic | 动态 batch | False(固定尺寸推理更快) |
5.2 传统检测器
Blob 分析
Blob 分析对二值图像中的连通域进行检测和分析。
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
MinArea | REAL | 否 | 100 | 最小面积 (像素) |
MaxArea | REAL | 否 | 100000 | 最大面积 (像素) |
MinCircularity | REAL | 否 | 0.0 | 最小圆形度 (0~1) |
MinConvexity | REAL | 否 | 0.0 | 最小凸度 (0~1) |
MinInertiaRatio | REAL | 否 | 0.0 | 最小惯性比 |
FilterByColor | BOOL | 否 | FALSE | 按颜色过滤 |
BlobColor | DWORD | 否 | 255 | 目标颜色 (0=黑, 255=白) |
Blob 分析流程:
二值化图像 → 连通域标记 → 过滤 → 分析 → 输出
┌──────────────┐
│ ██████ │
│ ██░░██ ██ │
│ ██████ │
│ ██████│
└──────────────┘
连通域标记后:
┌──────────────┐
│ 111111 │
│ 11○○11 22 │
│ 111111 │
│ 333333│
└──────────────┘
过滤后(去掉面积 < 100 的 Blob #2):
Blob #1: 面积=200, 圆形度=0.85, 质心=(50,50)
Blob #3: 面积=300, 圆形度=0.40, 质心=(180,80)
Blob 输出属性:
| 属性 | 说明 | 示例 |
|---|---|---|
area | 面积 (像素) | 200 |
centroid_x | 质心 X | 50.0 |
centroid_y | 质心 Y | 50.0 |
bounding_box | 外接矩形 | (40,40,80,60) |
circularity | 圆形度 (4πA/P²) | 0.85 |
convexity | 凸度 (面积/凸包面积) | 0.95 |
elongation | 伸长率 | 1.2 |
orientation | 方向角度 | 45.0° |
连通域分析
连通域分析 (Connected Component Analysis) 标记图像中所有连通区域。
4-连通: 8-连通:
↑ ↖↑↗
← ○ → ←○→
↓ ↙↓↘
4-连通更严格,8-连通更容易连接
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Connectivity | DWORD | 否 | 8 | 连通性 (4 或 8) |
MinArea | REAL | 否 | 0 | 最小面积过滤 |
MaxArea | REAL | 否 | 0 | 最大面积 (0=不限) |
5.3 模板匹配 (Template Matching)
模板匹配在图像中搜索与模板图像最相似的位置。
归一化互相关匹配
原理:在图像上滑动模板,计算每个位置的相似度
相似度范围:-1~1(1 表示完全匹配)
输入图像: 模板: 匹配结果:
┌──────────────┐ ┌────────┐ ┌──────────────┐
│ │ │ ████ │ │ │
│ ○○ ████ │ │ ████ │ → │ ●● ████ │
│ ○○ │ │ ████ │ │ ●● │
│ ██ │ └────────┘ │ ██ │
└──────────────┘ └──────────────┘
模板是 4×4 方块 匹配评分: 0.95
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
TemplatePath | STRING | 是 | — | 模板图像路径 |
Method | ENUM | 否 | Normed | 匹配方法 |
MinScore | REAL | 否 | 0.7 | 最低匹配评分 |
MaxMatches | DWORD | 否 | 1 | 最大匹配数 |
SearchRoi | RECT | 否 | 全图 | 搜索区域 |
匹配方法:
| 方法 | 说明 | 对光照敏感度 | 速度 |
|---|---|---|---|
Normed | 归一化互相关 | 不敏感 | 中 |
NormedSqDiff | 归一化平方差(越小越匹配) | 敏感 | 中 |
CCoeffNormed | 归一化相关系数 | 不敏感 | 慢 |
形状匹配
基于轮廓的形状匹配,不受光照变化影响。
参数:
ContourTemplate: 形状模板文件
MinScore: 0.6
ScaleMin: 0.8
ScaleMax: 1.2
AngleRange: 360°
形状匹配结果:
┌──────────────────────────────────────┐
│ │
│ 匹配位置 1: (320, 240) 评分: 0.92 │
│ 缩放: 0.95 角度: 15.3° │
│ │
│ 匹配位置 2: (800, 600) 评分: 0.85 │
│ 缩放: 1.02 角度: -5.7° │
│ │
└──────────────────────────────────────┘
5.4 边缘检测 (Edge Detection)
Canny 边缘检测
Canny 边缘检测是最常用的边缘检测算法,具有低错误率、精确定位和单边缘响应等优点。
Canny 算法流程:
原始图像 → 高斯滤波 → 梯度计算 → 非极大值抑制 → 双阈值检测 → 边缘图像
双阈值:
高阈值: 150 → 强边缘(必定保留)
低阈值: 50 → 弱边缘(仅连接强边缘时保留)
梯度方向:
← 水平边缘 (垂直梯度最大)
↑ 垂直边缘 (水平梯度最大)
↗ 对角边缘 (两个方向梯度都大)
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
LowThreshold | REAL | 否 | 50 | 低阈值 |
HighThreshold | REAL | 否 | 150 | 高阈值 |
ApertureSize | DWORD | 否 | 3 | Sobel 算子大小 (3/5/7) |
阈值选择指南:
高阈值/低阈值 ≈ 2:1 ~ 3:1
场景 低阈值 高阈值
清晰边缘 50 150
弱边缘(如划痕) 30 90
噪声较大 80 240
Sobel 边缘检测
Sobel 算子计算图像梯度,对噪声敏感。
Sobel X 核: Sobel Y 核:
[-1 0 1] [-1 -2 -1]
[-2 0 2] [ 0 0 0]
[-1 0 1] [ 1 2 1]
Sobel X 结果: Sobel Y 结果:
检测垂直边缘 检测水平边缘
┌───┐ ┌───┐
│ │ │ │───│
│ │ │ │───│
│ │ │ └───┘
└───┘
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Dx | DWORD | 是 | 1 | X 方向阶数 |
Dy | DWORD | 是 | 0 | Y 方向阶数 |
ApertureSize | DWORD | 否 | 3 | 核大小 |
Laplacian 边缘检测
Laplacian 算子检测二阶导数,对噪声非常敏感。
Laplacian 核:
[0 1 0]
[1 -4 1]
[0 1 0]
特点:对边缘位置敏感,但会放大噪声
5.5 特征匹配 (SIFT/SURF/ORB)
特征匹配在不同图像间寻找对应的特征点,适合拼接、定位等场景。
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Method | ENUM | 是 | — | 特征检测方法 |
MaxFeatures | DWORD | 否 | 1000 | 最大特征点数 |
MatchRatio | REAL | 否 | 0.75 | Lowe 匹配比率 |
MinMatches | DWORD | 否 | 10 | 最少匹配点数 |
特征匹配方法对比:
| 方法 | 旋转不变 | 缩放不变 | 光照不变 | 速度 | 适用场景 |
|---|---|---|---|---|---|
| SIFT | 是 | 是 | 是 | 慢 | 精确匹配 |
| SURF | 是 | 是 | 是 | 中 | 通用匹配 |
| ORB | 是 | 否 | 部分 | 快 | 实时应用 |
5.6 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 检测不到目标 | 置信度阈值太高 | 降低 Confidence 到 0.3~0.4 |
| 检测框太多 | NMS 阈值太低 | 提高 NMSThreshold 到 0.5~0.6 |
| 推理速度慢 | 使用 CPU 推理 | 切换到 GPU,或换小模型 |
| 模型加载失败 | ONNX 版本不兼容 | 检查 opset 版本,重新导出 |
| 标签显示错误 | 标签文件顺序不对 | 检查 labels.txt 是否与训练时一致 |
| 模板匹配失败 | 光照变化 | 用形状匹配代替 |
| Canny 边缘不连续 | 阈值设置不当 | 降低 LowThreshold 或增加 HighThreshold |
第六章:图像测量 (Measurement)
6.1 边缘定位
边缘定位是测量的基础,Vision Studio 提供亚像素级别的边缘定位。
直线拟合
从图像中提取直线边缘,输出直线参数。
参数:
Roi: (100, 100, 500, 50) ← 矩形 ROI
EdgeThreshold: 50
MinLength: 100
MaxGap: 10
ROI 区域: 边缘检测结果: 直线拟合:
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ │ │ │ │ │
│ ───────────── │ │ ·········· │ │ ────────────── │
│ │ │ │ │ │
│ │ │ │ │ │
└────────────────────┘ └────────────────────┘ └────────────────────┘
搜索区域 亚像素边缘点 拟合的直线
输出:Line
| 字段 | 类型 | 说明 |
|---|---|---|
x1 | REAL | 起点 X |
y1 | REAL | 起点 Y |
x2 | REAL | 终点 X |
y2 | REAL | 终点 Y |
angle | REAL | 角度 (度) |
score | REAL | 拟合评分 |
圆拟合
从图像中提取圆形边缘,输出圆心和半径。
参数:
Roi: (100, 100, 300, 300)
EdgeThreshold: 50
MinRadius: 50
MaxRadius: 500
ROI 区域: 边缘点: 拟合圆:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ │ │ ······ │ │ ╭──────╮ │
│ ○○○○○○ │ │ ·· ·· │ │ ╱ ╲ │
│ ○○ ○○ │ → │ · · │ → │ │ ● │ │
│ ○○ ○○ │ │ · · │ │ ╲ ╱ │
│ ○○○○○○ │ │ ·· ·· │ │ ╰──────╯ │
│ │ │ ······ │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
搜索区域 亚像素边缘点 拟合圆 (圆心+半径)
输出:Circle
| 字段 | 类型 | 说明 |
|---|---|---|
center_x | REAL | 圆心 X |
center_y | REAL | 圆心 Y |
radius | REAL | 半径 (像素) |
score | REAL | 拟合评分 |
6.2 点测量
两点距离
参数:
Point1: (320, 240)
Point2: (800, 600)
计算:距离 = sqrt((800-320)² + (600-240)²)
= sqrt(580² + 360²) = sqrt(336400 + 129600) = sqrt(466000)
= 682.6 像素
标定后:682.6 × 0.05 mm/pixel = 34.13 mm
点到线距离
参数:
Point: (500, 300)
Line: (100, 200, 900, 200)
计算:点到水平线的距离 = |300 - 200| = 100 像素
标定后:100 × 0.05 = 5 mm
6.3 角度测量
两线夹角
线 1: (100, 200, 500, 200) ← 水平线
线 2: (300, 100, 300, 500) ← 垂直线
夹角 = 90°
输出:
┌──────────────────────────────────────┐
│ │
│ 线 2 │
│ │ 线 1 │
│ │──────────────────── │
│ │ │
│ │ 角度 = 90° │
│ │ │
└──────────────────────────────────────┘
6.4 圆拟合
最小二乘法
最小二乘法拟合圆:
给定 N 个边缘点,最小化 ∑(距离_圆心 - 半径)²
优点:计算快
缺点:对离群点敏感
RANSAC 拟合
RANSAC 拟合圆:
1. 随机选 3 个点拟合圆
2. 计算所有点到圆的距离
3. 统计内点数量
4. 重复 1-3 步 N 次
5. 选择内点最多的圆
优点:对离群点鲁棒
缺点:计算慢
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
Method | ENUM | 否 | LeastSquares | LeastSquares / RANSAC |
RansacThreshold | REAL | 否 | 2.0 | RANSAC 内点阈值 (像素) |
RansacMaxIter | DWORD | 否 | 1000 | RANSAC 最大迭代次数 |
6.5 卡尺工具 (Caliper)
卡尺工具是工业视觉中最常用的测量工具,用于测量边缘间距。
卡尺工具的工作原理:
┌──────────────────────────────────────────────┐
│ │
│ ┌─ 卡尺矩形 ROI ───────────────┐ │
│ │ ┌──┐ ┌──┐ ┌──┐ ┌──┐ │ │
│ │ │H1│ │H2│ │H3│ │H4│ │ │
│ │ └──┘ └──┘ └──┘ └──┘ │ │
│ │ 测量范围: 从 H1 到 H4 │ │
│ │ 宽度 = H4 - H1 = 200 像素 │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────────────┘
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
RoiX | DWORD | 是 | — | 卡尺 ROI X |
RoiY | DWORD | 是 | — | 卡尺 ROI Y |
RoiWidth | DWORD | 是 | — | 卡尺宽度 |
RoiHeight | DWORD | 是 | — | 卡尺高度 |
EdgeSelector | ENUM | 否 | Best | 边缘选择策略 |
EdgePolarity | ENUM | 否 | Any | 边缘极性 |
MinEdgeStrength | REAL | 否 | 30 | 最小边缘强度 |
NumEdges | DWORD | 否 | 2 | 边缘数量 |
EdgeSelector:
| 值 | 说明 | 示意图 |
|---|---|---|
Best | 最强边缘 | 选择梯度最大的边 |
First | 第一个边缘 | 从扫描方向第一个 |
Last | 最后一个边缘 | 从扫描方向最后一个 |
All | 全部边缘 | 返回所有检测到的边 |
EdgePolarity:
Any: ← 黑→白 或 白→黑 都检测
Positive: 只检测 ────────→ 白→黑 边缘
Negative: 只检测 ────────→ 黑→白 边缘
6.6 几何测量综合示例
测量零件孔径和位置度:
卡片图流程:
[相机采集] ──→ [畸变矫正] ──→ [边缘检测] ──→ [圆拟合] ──→ [测量] ──→ [结果输出]
关键参数:
畸变矫正: 使用标定参数
Canny: Low=50, High=150
圆拟合: Method=RANSAC, MinRadius=50
测量结果:
┌──────────────────────────────────────────────┐
│ 零件: 支架-A292 │
│ │
│ ┌──────────┬──────┬──────┬──────┬────────┐ │
│ │ 测量项 │ 标称值 │ 实测值 │ 公差 │ 结果 │ │
│ ├──────────┼──────┼──────┼──────┼────────┤ │
│ │ 孔径 #1 │ 20.0 │ 20.02│ ±0.1 │ OK │ │
│ │ 孔径 #2 │ 12.0 │ 12.03│ ±0.1 │ OK │ │
│ │ 孔径 #3 │ 8.0 │ 8.01 │ ±0.05│ OK │ │
│ │ 间距 #1 │ 50.0 │ 49.98│ ±0.2 │ OK │ │
│ │ 间距 #2 │ 30.0 │ 30.05│ ±0.2 │ OK │ │
│ │ 角度 │ 90.0 │ 89.8 │ ±1.0 │ OK │ │
│ └──────────┴──────┴──────┴──────┴────────┘ │
│ │
│ 总体判定: OK │
└──────────────────────────────────────────────┘
6.7 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 边缘检测不到 | 边缘强度不够 | 降低 EdgeThreshold,改善照明 |
| 拟合圆不准确 | 边缘点太少 | 扩大 ROI,增加边缘检测范围 |
| 测量结果跳变 | 边缘不稳定 | 使用 RANSAC,增加 AvgNum 做平均 |
| 单位错误 | 标定系数不对 | 确认像素当量 mm/pixel |
| 角度跳变 180° | 角度取模问题 | 使用有向角度 |
第七章:识别 (Recognition)
7.1 条码读取 (1D Barcode)
Vision Studio 支持常见的一维条码类型。
支持的条码类型
| 条码类型 | 说明 | 字符集 | 长度 |
|---|---|---|---|
Code128 | 高密度字母数字 | ASCII 128 | 可变 |
Code39 | 字母数字 + 符号 | 43 字符 | 可变 |
EAN13 | 13 位商品码 | 数字 | 13 位 |
EAN8 | 8 位商品码 | 数字 | 8 位 |
UPC-A | 12 位商品码 | 数字 | 12 位 |
UPC-E | 6 位压缩码 | 数字 | 6 位 |
ITF | 交错 2/5 码 | 数字 | 可变 |
Codabar | 条形码 | 数字+6 字符 | 可变 |
Code93 | 高密度安全码 | ASCII 93 | 可变 |
PDF417 | 堆叠式条码 | ASCII 全部 | 可变 |
DataBar | 复合条码 | 数字 | 可变 |
参数配置
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
RoiX/Y/Width/Height | DWORD | 否 | 全图 | 搜索区域 |
Symbology | ENUM | 否 | Auto | 码类型(Auto=自动检测) |
Timeout | DWORD | 否 | 2000 | 识别超时 (ms) |
TryHarder | BOOL | 否 | FALSE | 增强识别(降低速度) |
TryHarder 说明:
TryHarder=FALSE: 快速识别,适合清晰条码(1-5ms)
TryHarder=TRUE: 增强识别,适合模糊/损坏条码(50-200ms)
卡片图配置
[相机采集] ──→ [图像增强] ──→ [条码识别] ──→ [结果输出]
│ │
│ Symbology: Auto
│ Timeout: 2000
│ TryHarder: FALSE
│
CLAHE: ClipLimit=2.0
结果处理示例
// 条码识别结果处理
IF decode_result.content <> "" THEN
// 根据条码内容分拣
CASE decode_result.format OF
FORMAT_CODE128:
// Code128 条码
IF LEFT(decode_result.content, 3) = "ORD" THEN
channel := 1; // 订单
ELSIF LEFT(decode_result.content, 3) = "RET" THEN
channel := 2; // 退货
ELSE
channel := 3; // 其他
END_IF;
FORMAT_EAN13:
// EAN13 商品码
channel := 1;
END_CASE;
// 输出
VIS_WriteVariable("SORT_CHANNEL", INT_TO_REAL(channel));
END_IF;
7.2 二维码读取 (2D Code)
支持的二维码类型
| 码类型 | 说明 | 容量 | 特点 |
|---|---|---|---|
QR Code | 矩阵二维码 | 最多 7K 数字 | 广泛使用 |
Micro QR | 小型 QR 码 | 最多 35 字符 | 小空间 |
DataMatrix | 工业矩阵码 | 最多 2K 字符 | 容错高 |
Aztec | 金字塔码 | 最多 3K 字符 | 无需空白区 |
PDF417 | 堆叠式条码 | 最多 1K 字符 | 大容量 |
参数配置
QR 码参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
RoiX/Y/Width/Height | DWORD | 否 | 全图 | 搜索区域 |
Timeout | DWORD | 否 | 2000 | 识别超时 (ms) |
MicroQR | BOOL | 否 | FALSE | 支持 Micro QR |
DataMatrix 参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
RoiX/Y/Width/Height | DWORD | 否 | 全图 | 搜索区域 |
Timeout | DWORD | 否 | 2000 | 识别超时 |
RectOnly | BOOL | 否 | FALSE | 仅矩形 DM 码 |
7.3 字符识别 (OCR)
Vision Studio OCR 基于 Tesseract 引擎,支持数字、字母和完整文本识别。
参数配置
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
RoiX/Y/Width/Height | DWORD | 否 | 全图 | 搜索区域 |
Language | STRING | 否 | "eng" | 识别语言 |
CharWhitelist | STRING | 否 | "" | 白名单字符 |
PsmMode | ENUM | 否 | Auto | 页面分割模式 |
Timeout | DWORD | 否 | 3000 | 识别超时 (ms) |
Language 选项:
| 语言字符串 | 说明 |
|---|---|
"eng" | 英文 |
"eng+chi_sim" | 英文+简体中文 |
"eng+chi_tra" | 英文+繁体中文 |
"jpn" | 日文 |
"kor" | 韩文 |
"deu" | 德文 |
CharWhitelist 示例:
| 白名单 | 说明 | 识别内容 |
|---|---|---|
"0123456789" | 仅数字 | 1234567890 |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-" | 大写字母+数字+连字符 | SN-20260720-01 |
".0123456789" | 数字+小数点 | 12.34 |
PsmMode (页面分割模式):
| 模式 | 说明 | 适用场景 |
|---|---|---|
Auto | 自动检测 | 通用 |
SingleBlock | 单块文本 | 段落文字 |
SingleLine | 单行文本 | 序列号、条码下方文字 |
SingleWord | 单单词 | 短标签 |
SingleChar | 单字符 | 独立字符 |
RawLine | 原始行 | 精确行定位 |
OCR 预处理最佳实践
OCR 推荐预处理流程:
原始图像 → 灰度 → 二值化 → 形态学 → 倾斜校正 → OCR
每一步的作用:
1. 灰度:减少颜色干扰
2. 二值化:突出字符与背景的对比
3. 形态学闭运算:连接字符断裂部分
4. 倾斜校正:纠正拍摄角度偏差
预处理效果对比:
不处理: 处理后:
┌──────────────────────┐ ┌──────────────────────┐
│ SN-2026-07-20 │ │ SN-2026-07-20 │
│ ────────── │ → │ ────────── │
│ 字符模糊 │ │ 字符清晰 │
│ 背景不均匀 │ │ 背景均匀 │
│ 倾斜 2° │ │ 已校正 │
└──────────────────────┘ └──────────────────────┘
7.4 颜色识别
颜色识别在 HSV 空间中进行,适合区分不同颜色的物体。
颜色范围参考:
| 颜色 | H 范围 | S 范围 | V 范围 |
|---|---|---|---|
| 红色 | 0 | 50~255 | 50~255 |
| 橙色 | 11~25 | 50~255 | 50~255 |
| 黄色 | 26~35 | 50~255 | 50~255 |
| 绿色 | 36~85 | 50~255 | 50~255 |
| 蓝色 | 86~125 | 50~255 | 50~255 |
| 紫色 | 126~155 | 50~255 | 50~255 |
| 黑色 | 0~180 | 0~255 | 0~50 |
| 白色 | 0~180 | 0~30 | 200~255 |
颜色识别流程:
1. 转 HSV: IMG_CvtColor(img, BGR2HSV)
2. 按颜色范围阈值
3. 计算区域面积
4. 判断颜色
示例:识别红色零件
blue_mask := IMG_Threshold(hsv, LOWER:=(100,50,50), UPPER:=(130,255,255));
red_mask := IMG_Threshold(hsv, LOWER:=(0,50,50), UPPER:=(10,255,255));
red_mask := red_mask OR IMG_Threshold(hsv, LOWER:=(160,50,50), UPPER:=(180,255,255));
7.5 缺陷检测 (Anomaly Detection)
异常检测基于深度学习模型,检测图像中的异常区域。
参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
ModelPath | STRING | 是 | — | 异常检测 ONNX 模型 |
Threshold | REAL | 否 | 0.5 | 异常阈值 |
OutputHeatmap | BOOL | 否 | TRUE | 输出热力图 |
ExecutionProvider | ENUM | 否 | Auto | 推理后端 |
异常检测输出:
AnomalyResult:
anomaly_score: 0.87 (0~1, 越大越异常)
is_anomaly: TRUE (score > threshold)
heatmap: Image (异常区域热力图)
正常产品: 异常产品:
┌────────────────────┐ ┌────────────────────┐
│ │ │ │
│ │ │ ▓▓▓▓ │
│ (均匀) │ │ ▓▓▓▓▓▓▓▓ │
│ │ │ ▓▓▓▓▓▓▓▓ │
│ │ │ ▓▓▓▓ │
└────────────────────┘ └────────────────────┘
anomaly_score: 0.12 anomaly_score: 0.87
is_anomaly: FALSE is_anomaly: TRUE
7.6 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 条码读不到 | 条码模糊或损坏 | 启用 TryHarder,改善照明 |
| 条码读错 | 对比度太低 | 增加 CLAHE 预处理 |
| QR 码读不到 | 码太小或畸变 | 靠近拍摄,或降低分辨率 |
| OCR 识别率低 | 字符切割不好 | 调整二值化阈值,用白名单 |
| 颜色识别不准 | 光照变化 | 在 HSV 空间调大范围 |
| 异常检测误报 | 阈值太低 | 提高 Threshold |
第八章:视觉流程编程
8.1 卡片图编辑器
卡片图编辑器是视觉流程的可视化编排工具,通过拖拽卡片构建处理管道。
编辑器界面
┌──────────────────────────────────────────────────────────────┐
│ 工具箱 (Toolbox) │ 编辑区 (Canvas) │
│ ┌────────────────────┐ │ ┌──────────────────────────┐ │
│ │ 采集 │ │ │ │ │
│ │ ├ 相机采集 │ │ │ [相机采集]──[预处理] │ │
│ │ ├ 图像加载 │ │ │ │ │ │ │
│ │ │ │ │ ▼ ▼ │ │
│ │ 预处理 │ │ │ [目标检测]──[结果输出] │ │
│ │ ├ 滤波 │ │ │ │ │
│ │ ├ 尺寸调整 │ │ └──────────────────────────┘ │
│ │ ├ 色彩转换 │ │ │
│ │ │ │ ┌──────────────────────────┐ │
│ │ 检测 │ │ │ 属性面板 (Properties) │ │
│ │ ├ 目标检测 │ │ │ ┌──────────────────────┐│ │
│ │ ├ 模板匹配 │ │ │ │ 卡片: 目标检测 ││ │
│ │ │ │ │ │ 模型: model.onnx ││ │
│ │ 测量 │ │ │ │ 阈值: 0.5 ││ │
│ │ ├ 圆/弧测量 │ │ │ │ NMS: 0.45 ││ │
│ │ ├ 直线测量 │ │ │ └──────────────────────┘│ │
│ │ ├ 点间距 │ │ └──────────────────────────┘ │
│ │ │ │ │
│ │ 识别 │ │ [状态栏: 就绪 | 卡片数: 4] │
│ │ ├ 条码识别 │ │ │
│ │ └─── │ │ │
│ └────────────────────┘ │ │
└──────────────────────────────────────────────────────────────┘
连接规则
1. 单向连接:数据从上游流向下游
[相机] ──→ [预处理] ✓
[预处理] ←── [相机] ✗
2. 一进多出:一个输出可连多个输入
[目标检测] ──→ [结果输出]
│
└──→ [图像保存] ✓
3. 类型匹配:输出类型必须兼容输入类型
[相机](Image) ──→ [目标检测](Image) ✓
[相机](Image) ──→ [条码识别](Image) ✓
[目标检测](DetectionResult) ──→ [结果输出](DetectionResult) ✓
[目标检测](DetectionResult) ──→ [滤波](Image) ✗
4. 不支持多输入合并(用 Vision SCL 实现)
卡片图示例
示例 1:产品外观检测
[相机采集] ──→ [灰度转换] ──→ [高斯滤波] ──→ [目标检测] ──→ [结果输出]
GigE GRAY8 5×5 model.onnx PLC 变量
2448×2048 阈值 0.5
示例 2:定位引导
[相机采集] ──→ [畸变矫正] ──→ [模板匹配] ──→ [坐标转换] ──→ [结果输出]
USB 3.0 标定参数 参考图像 像素→mm 机器人坐标
1280×1024 评分 > 0.7
示例 3:条码识别
[编码器触发] ──→ [相机采集] ──→ [CLAHE] ──→ [条码识别] ──→ [分拣输出]
传送带同步 线阵扫描 增强对比度 Code128 通道号
8.2 Vision SCL 编程
Vision SCL 是视觉专用编程语言,扩展了视觉数据类型和处理函数。
程序结构
PROGRAM ProgramName
VAR_INPUT
// 输入参数(从 PLC 传入)
trigger : BOOL;
part_id : INT;
END_VAR
VAR_OUTPUT
// 输出参数(结果回写 PLC)
result_count : INT;
is_ok : BOOL;
END_VAR
VAR
// 局部变量
cam : Camera;
img : Image;
result : DetectionResult;
i : INT;
END_VAR
// 主程序逻辑
// ...
END_PROGRAM
事件处理
Vision SCL 支持事件驱动模型:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
OnTrigger | 收到触发信号时 | 重置计数器、记录起始时间 |
OnFrame | 采集到一帧时 | 检查图像质量、动态调曝光 |
OnInferenceCompleted | 推理完成时 | 条件分支、二次确认 |
OnFault | 发生故障时 | 切换备用相机、报警 |
OnTimeout | 超时时 | 输出默认值 |
事件绑定示例:
PROGRAM VisionWithEvents
VAR
cam : Camera;
img : Image;
result : DetectionResult;
END_VAR
// 注册事件处理器
EVENT_BIND(EVENT_ON_TRIGGER, "OnTriggerHandler");
EVENT_BIND(EVENT_ON_FRAME, "OnFrameHandler");
EVENT_BIND(EVENT_ON_INFERENCE_COMPLETED, "OnInferenceHandler");
EVENT_BIND(EVENT_ON_FAULT, "OnFaultHandler");
EVENT_BIND(EVENT_ON_TIMEOUT, "OnTimeoutHandler");
// 主流程
cam := CAM_Open("USB_CAM_1");
img := CAM_Capture(cam, TIMEOUT := 5000);
result := OBJ_Detect(img, MODEL := "model.onnx");
EVENT_WAIT_ALL(TIMEOUT := 1000);
END_PROGRAM
// 推理完成事件处理
ACTION OnInferenceHandler
VAR
i : INT;
END_VAR
VIS_Log(LOG_INFO, "检测到 " + INT_TO_STRING(result.object_count) + " 个目标");
FOR i := 0 TO result.object_count - 1 DO
VIS_WriteVariable("VIS_OBJ_" + INT_TO_STRING(i) + "_CLASS",
STRING_TO_REAL(result.objects[i].class_name));
END_FOR;
END_ACTION
// 故障处理
ACTION OnFaultHandler
VIS_Log(LOG_ERROR, "视觉故障: " + error_message);
IF error_code = ERR_CAMERA_DISCONNECTED THEN
// 切换备用相机
alt_cam := CAM_Open("USB_CAM_2");
IF alt_cam <> INVALID_CAMERA THEN
VIS_WriteVariable("VIS_CAM_SWITCHED", 1.0);
END_IF;
END_IF;
END_ACTION
// 超时处理
ACTION OnTimeoutHandler
VIS_Log(LOG_WARN, "超时: " + stage + " 已耗时 " +
INT_TO_STRING(elapsed_ms) + "ms");
VIS_WriteVariable("VIS_RESULT_COUNT", 0.0);
END_ACTION
异步执行模型
视觉程序以 Job 形式异步执行,不阻塞 PLC 扫描周期。
PLC 扫描周期: 视觉 Job 执行:
┌───┐ ┌───┐ ┌───┐ ┌──────────────────┐
│ │ │ │ │ │ │ Job #1287 │
│ ① │ ② │ ③ │ │ ├──────────────────┤
│ │ │ │ │ │ │ 采集 → 推理 → 输出│
└───┘ └───┘ └───┘ └──────────────────┘
│ │ │ │
│ │ └── VIS_JOB_DONE 读到结果
│ │
│ └── VIS_JOB_START 触发 Job
│
└── PLC 正常逻辑不受影响
Job 状态机:
┌──────────┐
│ IDLE │ ←── PLC 就绪
└────┬─────┘
│ 触发 (VIS_JOB_START ↑)
▼
┌──────────┐
│ QUEUED │ ←── 排队等待资源
└────┬─────┘
│ 资源就绪
▼
┌──────────┐
│ RUNNING │ ←── 执行中
└────┬─────┘
│
┌────────┼────────┐
▼ ▼ ▼
┌────────┐┌────────┐┌──────────┐
│DONE ││FAULT ││TIMEOUT │
│(成功) ││(故障) ││(超时) │
└────────┘└────────┘└──────────┘
8.3 结果回写 PLC 变量
视觉 Job 执行完成后,结果自动写入 PLC 变量区。
系统变量(所有视觉 Job 共享):
| 变量名 | 类型 | 说明 |
|---|---|---|
VIS_JOB_BUSY | BOOL | 有 Job 正在执行 |
VIS_JOB_DONE | BOOL | 最近一次 Job 完成 |
VIS_JOB_ERROR | BOOL | 最近一次 Job 异常 |
VIS_JOB_ERROR_ID | DWORD | 错误码 |
VIS_JOB_ID | DWORD | 当前 Job 序号 |
VIS_JOB_ELAPSED | REAL | 执行耗时 (ms) |
结果变量(按 Job 配置):
| 变量名 | 类型 | 说明 |
|---|---|---|
VIS_RESULT_COUNT | INT | 检测目标数 |
VIS_RESULT_01_CLASS | STRING[32] | 目标 1 类别 |
VIS_RESULT_01_CONF | REAL | 目标 1 置信度 |
VIS_RESULT_01_X | REAL | 目标 1 X 坐标 |
VIS_RESULT_01_Y | REAL | 目标 1 Y 坐标 |
VIS_RESULT_01_W | REAL | 目标 1 宽度 |
VIS_RESULT_01_H | REAL | 目标 1 高度 |
VIS_RESULT_01_ANGLE | REAL | 目标 1 角度 |
VIS_RESULT_02_* | ... | 目标 2(最多 256 个) |
PLC 侧触发和读取示例:
PROGRAM VisionIntegration
VAR
trigger_pulse : BOOL;
trigger_prev : BOOL;
result_count : INT;
i : INT;
END_VAR
// 上升沿触发
trigger_pulse := trigger_signal AND NOT trigger_prev;
trigger_prev := trigger_signal;
IF trigger_pulse THEN
VIS_JOB_START := TRUE;
ELSE
VIS_JOB_START := FALSE;
END_IF;
// 读取结果
IF VIS_JOB_DONE AND NOT VIS_JOB_BUSY THEN
IF VIS_JOB_ERROR THEN
// 错误处理
system_state := STATE_VISION_FAULT;
ELSE
result_count := VIS_RESULT_COUNT;
// 处理结果
END_IF;
// 清除完成标志
VIS_JOB_DONE := FALSE;
END_IF;
// 超时保护
IF VIS_JOB_BUSY AND (TIME_SINCE_TRIGGER > T#30S) THEN
VIS_JOB_ABORT := TRUE;
END_IF;
END_PROGRAM
8.4 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 卡片连接报错 | 类型不匹配 | 检查连接线的数据类型 |
| 视觉程序编译失败 | 语法错误 | 检查输出面板中的错误信息 |
| Job 排队超时 | 并发数过多 | 增加 max_concurrent_jobs |
| 结果不更新 | 未清除 VIS_JOB_DONE | PLC 中加清零逻辑 |
| 事件处理器不执行 | 事件未绑定 | 检查 EVENT_BIND 是否注册 |
第九章:模型管理
9.1 导入 ONNX 模型
模型管理器在 IDE 左侧 视觉系统 → 模型管理 标签页中。
界面:
┌──────────────────────────────────────────────────────────────┐
│ 模型管理 [+ 导入] │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ 本地模型 │ │ 项目模型 │ │ 部署模型 │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ ┌───────────────┬──────┬──────┬──────┬────────────────────┐│
│ │ 模型名称 │ 版本 │ 大小 │ 状态 │ 上次验证 ││
│ ├───────────────┼──────┼──────┼──────┼────────────────────┤│
│ │ surface_defect│ v3 │ 24MB │ ● 已部署 │ 2026-07-20 ││
│ │ position_det │ v2 │ 12MB │ ● 已验证 │ 2026-07-19 ││
│ │ barcode_reader│ v1 │ 8MB │ ○ 未验证 │ 2026-07-15 ││
│ └───────────────┴──────┴──────┴──────┴────────────────────┘│
│ │
│ [详情] [验证] [部署] [删除] [导出] │
└──────────────────────────────────────────────────────────────┘
导入步骤:
- 点击 导入 按钮
- 选择 ONNX 模型文件
- 系统自动解析模型元数据
导入模型
────────────────────────────────────────────────
文件: surface_defect_v3.onnx
大小: 24.3 MB
算子集: opset 19
┌─ 输入 ──────────────────────────────────────┐
│ images: float32[1,3,640,640] │
└──────────────────────────────────────────────┘
┌─ 输出 ──────────────────────────────────────┐
│ output0: float32[1,84,8400] │
└──────────────────────────────────────────────┘
模型名称: surface_defect
版本号: v3
[确定] [取消]
- 确认名称和版本后,模型复制到项目目录
Vision/Models/
9.2 模型版本管理
版本命名规则:v + 正整数,如 v1、v2、v3
版本操作:
| 操作 | 说明 |
|---|---|
| 新建版本 | 导入新 ONNX 文件,版本号自动递增 |
| 回滚版本 | 切换到历史版本,模型文件保留 |
| 对比版本 | 对比两个版本的输入/输出/大小/性能 |
| 删除版本 | 删除不需要的版本(部署中的不可删除) |
| 设置默认 | 指定默认使用的版本 |
版本对比:
surface_defect v2 ↔ v3 对比
────────────────────────────────────────────────
属性 │ v2 │ v3
──────────────┼───────────────┼─────────────────
文件大小 │ 18.2 MB │ 24.3 MB
输入尺寸 │ 640×640 │ 640×640
算子集 │ opset 17 │ opset 19
[email protected] │ 0.892 │ 0.935 ▲
推理延迟(CPU) │ 45 ms │ 38 ms ▲
推理延迟(GPU) │ 12 ms │ 8 ms ▲
模型架构 │ YOLOv8s │ YOLOv8m
训练数据集 │ 5000 张 │ 12000 张
训练日期 │ 2026-06-01 │ 2026-07-10
9.3 模型验证与测试
验证流程:
- 选择模型 → 点击 验证
- 选择验证数据集
- 系统逐张推理并生成报告
验证报告:
模型验证报告
────────────────────────────────────────────────
模型: surface_defect v3
验证集: 100 张图像
日期: 2026-07-20 14:30:00
┌─ 性能指标 ───────────────────────────────────┐
│ 平均推理延迟: 38 ms (CPU) / 8 ms (GPU) │
│ 平均帧率: 26 FPS (CPU) / 125 FPS (GPU) │
│ 最大内存占用: 156 MB │
└───────────────────────────────────────────────┘
┌─ 检测指标 ───────────────────────────────────┐
│ 准确率: 97.2% │ 误检率: 1.8% │
│ 召回率: 96.5% │ 漏检率: 3.5% │
│ [email protected]: 0.935 │ [email protected]:0.95: 0.712 │
└───────────────────────────────────────────────┘
┌─ 失败案例 ───────────────────────────────────┐
│ #1 缺陷: 划痕 │
│ 图像: test_023.jpg │
│ 实际: 有划痕 预测: 无缺陷 │
│ 置信度: 0.32 (低于阈值 0.5) │
│ #2 缺陷: 凹陷 │
│ 图像: test_047.jpg │
│ 实际: 有凹陷 预测: 无缺陷 │
│ 置信度: 0.41 (低于阈值 0.5) │
└───────────────────────────────────────────────┘
验证结论: 通过
建议: 失败案例均为低置信度漏检,可考虑降低
置信度阈值至 0.3 或补充训练数据
9.4 部署模型
部署流程:
- 选择已验证的模型版本
- 点击 部署 按钮
- 选择部署目标
部署模型
────────────────────────────────────────────────
模型: surface_defect v3
部署目标:
● 当前项目 (本地)
○ 远程运行时 (192.168.3.197)
部署配置:
推理后端: GPU (CUDA)
超时时间: 5000 ms
最大并发: 2
[部署] [取消]
部署步骤:
| 步骤 | 说明 | 状态 |
|---|---|---|
| 1. 模型压缩 | ONNX 优化 (图优化、常量折叠) | 完成 |
| 2. 格式转换 | 转换为运行时格式 | 完成 |
| 3. 资源分配 | 分配 GPU 显存 / CPU 内存 | 完成 |
| 4. 部署验证 | 加载模型并运行 3 次验证推理 | 完成 |
| 5. 注册完成 | 创建模型 Session,准备就绪 | 完成 |
9.5 蓝绿部署与回滚
蓝绿部署支持零停机切换模型版本。
蓝绿部署策略:
当前版本 (v3): 新版本 (v4):
┌──────────────┐ ┌──────────────┐
│ 蓝色 (活跃) │ │ 绿色 (预备) │
│ surface_def │ │ surface_def │
│ ect v3 │ │ ect v4 │
│ GPU 显存已分配│ │ GPU 显存已分配│
│ Session 就绪 │ │ Session 就绪 │
└──────────────┘ └──────────────┘
│ │
└──────────┬───────────┘
│
▼
切换命令: 蓝色→绿色
新推理请求走绿色
蓝色保持 5 分钟回滚窗口
回滚:
5 分钟内检测到问题
→ 切回蓝色
→ 绿色卸载
模型文件结构:
<项目根目录>/
└── Vision/
└── Models/
├── surface_defect/
│ ├── v1/
│ │ ├── model.onnx
│ │ ├── labels.txt
│ │ ├── metadata.json
│ │ └── validation.json
│ ├── v2/
│ └── v3/ ← 当前部署版本
├── position_detect/
└── models.index.json
9.6 常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 模型导入失败 | ONNX 版本不兼容 | 检查 opset 是否在 11~21 范围 |
| 模型验证失败 | 验证数据集格式不对 | 确保图像格式与模型输入一致 |
| 部署失败 | GPU 显存不足 | 切换 CPU 推理,或减少 batch size |
| 推理结果异常 | 输入尺寸与模型不匹配 | 检查 Resize 参数是否与模型一致 |
| 版本无法删除 | 该版本正在使用 | 先切换到其他版本 |
第十章:与 PLC 集成
10.1 触发信号
PLC 触发相机采集
触发方式对比:
| 方式 | 延迟 | 精度 | 接线 | 适用场景 |
|---|---|---|---|---|
| 软件触发 (PLC 变量) | 10~50 ms | 中等 | 无 | 非实时触发 |
| 硬件触发 (PLC DO) | < 1 µs | 高 | 需要 | 高速产线同步 |
| 编码器触发 | 编码器脉冲 | 精确位置 | 需要 | 传送带等距采集 |
PLC 变量触发配置:
触发条件配置
────────────────────────────────────────────────
触发方式: ● PLC 变量触发
触发变量: VIS_TRIGGER_01
触发沿: ● 上升沿 ○ 下降沿
触发后去抖: 50 ms
超时时间: 5000 ms
超时动作: 输出默认值
[确定] [取消]
PLC 侧代码:
// 产品到位时触发视觉
IF part_present AND NOT part_prev THEN
VIS_JOB_START := TRUE;
trigger_time := NOW();
END_IF;
part_prev := part_present;
10.2 结果回写
视觉 Job 完成后,结果自动写入 PLC 变量区。
回写流程:
1. PLC 设置 VIS_JOB_START = TRUE
2. 运行时检测上升沿,创建 Job
3. Job 开始,VIS_JOB_BUSY = TRUE
4. 采集 → 预处理 → 推理 → 后处理
5. 结果写入 VIS_RESULT_* 变量
6. VIS_JOB_DONE = TRUE, VIS_JOB_BUSY = FALSE
7. PLC 下一个扫描周期读到完成
8. PLC 读取结果变量
9. PLC 将 VIS_JOB_DONE 清零
PLC 读取结果完整示例:
PROGRAM VisionReader
VAR
trigger_pulse : BOOL;
trigger_prev : BOOL;
has_result : BOOL;
result_count : INT;
i : INT;
all_ok : BOOL;
END_VAR
// 触发
trigger_pulse := trigger_signal AND NOT trigger_prev;
trigger_prev := trigger_signal;
IF trigger_pulse THEN
VIS_JOB_START := TRUE;
ELSE
VIS_JOB_START := FALSE;
END_IF;
// 读取结果
IF VIS_JOB_DONE AND NOT VIS_JOB_BUSY THEN
IF VIS_JOB_ERROR THEN
// 错误处理
system_state := STATE_VISION_FAULT;
ELSE
result_count := VIS_RESULT_COUNT;
// 判断是否合格
IF result_count = 0 THEN
// 无缺陷,合格
all_ok := TRUE;
ELSE
// 有缺陷,不合格
all_ok := FALSE;
END_IF;
// 输出
product_ok := all_ok;
product_count := product_count + 1;
IF NOT all_ok THEN
defect_count := defect_count + 1;
END_IF;
END_IF;
// 清除完成标志
VIS_JOB_DONE := FALSE;
END_IF;
// 超时保护
IF VIS_JOB_BUSY AND (TIME() - trigger_time > T#30S) THEN
VIS_JOB_ABORT := TRUE;
system_state := STATE_VISION_TIMEOUT;
END_IF;
END_PROGRAM
10.3 视觉状态监控
运行时指标:
| 指标 | 说明 | 单位 | 读取方式 |
|---|---|---|---|
| GPU 利用率 | GPU 计算核心占用率 | % | 系统变量 |
| GPU 显存使用 | 显存已用量 | MB | 系统变量 |
| CPU 使用率 | 视觉进程 CPU 占用 | % | 系统变量 |
| 推理延迟 | 平均推理时间 | ms | VIS_JOB_ELAPSED |
| Job 排队长度 | 当前等待队列长度 | 个数 | 系统变量 |
| Job 吞吐量 | 每分钟完成数 | 个/min | 系统变量 |
| 成功率 | 最近 100 次成功率 | % | 计算 |
状态监控示例:
HMI 状态面板:
┌──────────────────────────────────────────────┐
│ 视觉系统状态 │
│ │
│ ● 运行中 帧率: 30 fps │
│ 推理延迟: 8 ms 排队: 0 │
│ GPU 显存: 512/1024 MB 成功率: 99.5% │
│ │
│ ┌─ 最近 100 次 Job ────────────────────────┐│
│ │ ████████████████████████████████████████ ││
│ │ ████ ││
│ │ 成功: 96 失败: 3 超时: 1 ││
│ └───────────────────────────────────────────┘│
└──────────────────────────────────────────────┘
10.4 故障处理与超时
故障分类:
| 故障类型 | 错误码 | 可恢复 | 处理方式 |
|---|---|---|---|
| 相机断开 | 0x1001 | 是 | 自动重连,最多 3 次 |
| 模型加载失败 | 0x2001 | 是 | 回退到上一个版本 |
| 推理异常 | 0x3001 | 否 | 记录日志,报错到 PLC |
| 显存不足 | 0x4001 | 是 | 自动回退到 CPU |
| 内存不足 | 0x4002 | 是 | 清理缓存后重试 |
| 超时 | 0x5001 | 是 | 输出默认值,下次重试 |
| 许可限制 | 0x6001 | 否 | 提示用户升级许可 |
自动恢复流程:
检测到故障 ──→ 判断可恢复性
│ │
│ 可恢复 │ 不可恢复
▼ ▼
重试计数 记录日志
│ 通知 PLC
▼ 通知操作员
重试操作 │
│ ▼
├── 成功 → 恢复正常 禁用视觉功能
│
└── 失败 → 判断是否达到最大重试次数
│
┌────────┼────────┐
▼ ▼ ▼
相机故障 模型故障 其他故障
重连相机 回退版本 禁用功能
10.5 端到端示例:视觉引导定位抓取
场景:机器人从传送带上抓取无序来料的工件。
硬件配置:
- 相机:俯视安装,固定于支架 (Eye-to-Hand)
- 机器人:6 轴工业机器人
- PLC:Darra RT PLC 控制传送带和机器人
视觉流程:
[编码器触发] ──→ [相机采集] ──→ [畸变矫正] ──→ [模板匹配]
│
▼
[坐标转换]
│
▼
[结果输出]
(机器人坐标)
PLC 逻辑:
PROGRAM PickAndPlace
VAR
// 输入
part_detected : BOOL; // 来料检测传感器
robot_ready : BOOL; // 机器人就绪
// 视觉相关
trigger_pulse : BOOL;
trigger_prev : BOOL;
vision_ok : BOOL;
// 输出
grab_x, grab_y, grab_angle : REAL;
grab_request : BOOL;
cycle_count : INT;
END_VAR
// 触发视觉
trigger_pulse := part_detected AND NOT trigger_prev;
trigger_prev := part_detected;
IF trigger_pulse AND robot_ready THEN
VIS_JOB_START := TRUE;
END_IF;
// 读取视觉结果
IF VIS_JOB_DONE THEN
VIS_JOB_DONE := FALSE;
IF NOT VIS_JOB_ERROR AND VIS_RESULT_COUNT > 0 THEN
// 读取第一个工件的坐标
grab_x := VIS_RESULT_01_X;
grab_y := VIS_RESULT_01_Y;
grab_angle := VIS_RESULT_01_ANGLE;
grab_request := TRUE;
vision_ok := TRUE;
ELSE
// 视觉失败
vision_ok := FALSE;
grab_request := FALSE;
LOG_ERROR("视觉定位失败");
END_IF;
END_IF;
// 机器人抓取完成
IF grab_request AND robot_grab_done THEN
grab_request := FALSE;
cycle_count := cycle_count + 1;
END_IF;
END_PROGRAM
性能指标:
| 指标 | 值 |
|---|---|
| 定位精度 | ±0.3 mm |
| 角度精度 | ±0.5° |
| 单次定位时间 | 150 ms |
| 抓取成功率 | 99.5% |
| 节拍 | 3 秒/件 |
第十一章:实际应用示例
11.1 产品外观缺陷检测
场景:注塑件表面缺陷检测(划痕、凹陷、缺料、飞边)。
视觉流程:
[相机采集] ──→ [预处理] ──→ [缺陷检测] ──→ [结果输出]
GigE 灰度+滤波 ONNX 模型 分拣信号
2448×2048 5×5 高斯 阈值 0.5 + 图像保存
关键配置:
| 参数 | 值 | 说明 |
|---|---|---|
| 相机 | Basler acA2440-75um | GigE, 75fps |
| 触发 | 硬件触发 (PLC DO) | 产品到位时 |
| 曝光 | 3000 µs | 适应白色注塑件 |
| 预处理 | 灰度 + 高斯滤波 5×5 | 去噪 |
| 模型 | surface_defect.onnx | YOLOv8m |
| 置信度阈值 | 0.5 | 过滤低质量 |
| 检测类别 | 划痕, 凹陷, 缺料, 飞边 | 4 类 |
| 结果输出 | 分拣 DO + 数据通信 | 分拣 + MES 上报 |
PLC 逻辑:
FUNCTION_BLOCK FB_SurfaceInspection
VAR_INPUT
part_present : BOOL;
ejector_ready : BOOL;
END_VAR
VAR_OUTPUT
ejector : BOOL;
pass_count : INT;
fail_count : INT;
END_VAR
VAR
trigger_prev : BOOL;
END_VAR
trigger_pulse := part_present AND NOT trigger_prev;
trigger_prev := part_present;
IF trigger_pulse AND ejector_ready THEN
VIS_JOB_START := TRUE;
END_IF;
IF VIS_JOB_DONE THEN
VIS_JOB_DONE := FALSE;
IF NOT VIS_JOB_ERROR THEN
IF VIS_RESULT_COUNT > 0 THEN
// 有缺陷,不合格
ejector := TRUE;
fail_count := fail_count + 1;
ELSE
// 合格
ejector := FALSE;
pass_count := pass_count + 1;
END_IF;
ELSE
ejector := TRUE;
END_IF;
END_IF;
IF ejector AND NOT part_present THEN
ejector := FALSE;
END_IF;
END_FUNCTION_BLOCK
性能指标:
| 指标 | 值 |
|---|---|
| 检测节拍 | 800 ms/件 |
| 检测精度 | 99.2% |
| 误检率 | 0.5% |
| 漏检率 | 0.3% |
| 最大吞吐量 | 75 件/分钟 |
11.2 定位引导机器人抓取
场景:无序来料,视觉系统定位工件,引导机器人抓取。
视觉流程:
[相机采集] ──→ [模板匹配] ──→ [坐标转换] ──→ [结果输出]
俯视安装 形状匹配 像素→mm 机器人坐标
1280×1024 评分 > 0.7 手眼标定 X/Y/角度
手眼标定:
标定板 → 机器人移动到 9 个位置 → 视觉记录像素坐标 → 计算变换矩阵
标定矩阵示例:
[ 0.052, 0.001, -10.5 ]
[ -0.001, 0.052, 8.2 ]
[ 0, 0, 1 ]
每个像素 = 0.052 mm
性能指标:
| 指标 | 值 |
|---|---|
| 定位精度 | ±0.3 mm |
| 角度精度 | ±0.5° |
| 单次定位 | 150 ms |
| 抓取成功率 | 99.5% |
11.3 条码/二维码读取
场景:物流分拣线,读取包裹条码,按目的地分拣。
视觉流程:
[编码器触发] ──→ [线阵扫描] ──→ [图像增强] ──→ [条码识别]
│
├──→ [分拣输出]
│ └→ 通道号 DO
│
└──→ [数据库写入]
└→ 追溯记录
关键配置:
| 参数 | 值 | 说明 |
|---|---|---|
| 相机 | 线阵相机 4096 像素 | 高速传送带 |
| 触发 | 编码器触发 | 与传送带同步 |
| 图像增强 | CLAHE | 改善条码对比度 |
| 条码类型 | Code128, QR, DataMatrix | 自适应 |
| 识别超时 | 1000 ms | 快速识别 |
性能指标:
| 指标 | 值 |
|---|---|
| 识别率 | 99.7% |
| 识别速度 | < 100 ms/次 |
| 传送带速度 | 2 m/s |
11.4 尺寸测量与分选
场景:机加工零件尺寸测量,判断是否在公差范围内。
视觉流程:
[相机采集] ──→ [边缘检测] ──→ [几何测量] ──→ [结果判断]
背光照明 Canny 亚像素 圆/直线拟合 公差范围
500 万像素 50/150 最小二乘 OK/NG
测量项目:孔径 3 处、间距 2 处、角度 1 处
性能指标:
| 指标 | 值 |
|---|---|
| 测量精度 | ±0.01 mm |
| 重复精度 | ±0.005 mm |
| 单次测量 | 500 ms |
| 误判率 | < 0.1% |
11.5 OCR 字符识别
场景:读取产品表面激光刻印的序列号,用于追溯。
关键配置:
| 参数 | 值 | 说明 |
|---|---|---|
| 相机 | 12MP 高分辨率 | 1200 万像素 |
| 镜头 | 50mm 微距 | 高放大倍率 |
| 照明 | 环形暗场 | 突出刻印字符 |
| 预处理 | 二值化 + 形态学闭运算 | 字符增强 |
| OCR 引擎 | Tesseract | 工业级 OCR |
| 白名单 | A-Z, 0-9, - | 限制字符集 |
| 最低置信度 | 0.8 | 视为识别失败 |
性能指标:
| 指标 | 值 |
|---|---|
| 识别率 | 99.3% |
| 误识率 | 0.02% |
| 识别速度 | < 300 ms/次 |
| 最小字符高度 | 15 像素 |
11.6 综合示例:完整视觉工作站
场景:一个完整的视觉工作站,包含产品检测、定位、条码识别和尺寸测量。
工作站布局:
┌──────────────────────────────────────────────────┐
│ │
│ 工位 1 工位 2 工位 3 │
│ 外观检测 定位抓取 条码读取 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 相机 1 │ │ 相机 2 │ │ 相机 3 │ │
│ │ GigE │ │ USB 3.0 │ │ 线阵 │ │
│ │ 缺陷检测 │ │ 模板匹配 │ │ 条码识别 │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌────┴────┐ │
│ │ PLC │ │
│ │ 汇总判断 │ │
│ │ 分拣输出 │ │
│ └─────────┘ │
│ │
└──────────────────────────────────────────────────┘
PLC 汇总逻辑:
// 三个工位视觉结果汇总
IF vis1_done AND vis2_done AND vis3_done THEN
// 全部合格
IF vis1_ok AND vis2_ok AND vis3_ok THEN
product_result := RESULT_OK;
ok_count := ok_count + 1;
ELSE
// 有不合格项
product_result := RESULT_NG;
ng_count := ng_count + 1;
// 记录不合格原因
IF NOT vis1_ok THEN
fault_reason := "外观缺陷";
ELSIF NOT vis2_ok THEN
fault_reason := "定位异常";
ELSE
fault_reason := "条码读取失败";
END_IF;
END_IF;
// 重置
vis1_done := FALSE;
vis2_done := FALSE;
vis3_done := FALSE;
END_IF;
第十二章:性能优化与排错
12.1 帧率优化
瓶颈分析:
视觉流程各阶段耗时分布(典型值):
总耗时 = 采集 + 预处理 + 推理 + 后处理
采集: 4 ms (8%)
预处理: 3 ms (6%)
推理: 38 ms (76%) ← 通常是最慢的环节
后处理: 5 ms (10%)
──────────────────
总计: 50 ms (20 fps)
优化策略:
| 策略 | 效果 | 说明 |
|---|---|---|
| 降低分辨率 | 显著 | 1280×1024 → 640×480,推理速度提升 2~4 倍 |
| 切换推理后端 | 显著 | CPU → GPU,速度提升 5~10 倍 |
| 使用小模型 | 显著 | YOLOv8m → YOLOv8n,速度提升 2 倍 |
| 模型量化 | 显著 | FP32 → INT8,速度提升 2 倍,精度损失 < 1% |
| 减少预处理 | 中等 | 只做必要的预处理 |
| 限制 ROI | 中等 | 只处理感兴趣区域 |
| 批量推理 | 中等 | 同时处理多帧 |
| 调整帧率 | 轻微 | 降低目标帧率减少处理压力 |
GPU 加速配置:
RuntimeResourcePolicy 配置
────────────────────────────────────────────────
GPU:
enabled: true
device_id: 0
memory_limit_mb: 1024
fallback_to_cpu: true
CPU:
max_cores: 2
affinity: [4, 5]
priority: below_normal
Concurrency:
max_concurrent_jobs: 2
queue_size: 10
job_timeout_ms: 30000
12.2 模型推理加速
模型选择指南:
| 需求 | 推荐模型 | 推理延迟 (GPU) | mAP |
|---|---|---|---|
| 最快速度 | YOLOv8n | 2 ms | 0.872 |
| 平衡 | YOLOv8s | 4 ms | 0.895 |
| 高精度 | YOLOv8m | 8 ms | 0.912 |
| 最高精度 | YOLOv8l | 12 ms | 0.928 |
模型量化:
FP32 模型 (24 MB) → INT8 量化 (12 MB)
量化方法:
后训练量化 (PTQ): 无需训练数据,简单
量化感知训练 (QAT): 需要训练数据,精度更好
量化效果:
FP32 → INT8: 速度提升 2 倍
精度损失: < 1%
内存减少: 50%
ONNX 优化:
ONNX Runtime 自动优化:
1. 图优化:合并节点、消除冗余
2. 常量折叠:预计算常量表达式
3. 算子融合:减少 kernel launch 开销
4. 内存优化:复用 intermediate tensor
优化效果:推理速度提升 10~30%
12.3 常见故障与解决方法
相机相关
| 故障 | 原因 | 解决方法 |
|---|---|---|
| 相机扫描不到 | 驱动未安装 | 安装 GenICam 或 UVC 驱动 |
| 采集超时 | 触发信号未到 | 检查触发模式,临时切软触发 |
| 图像全黑 | 曝光太短/光圈关闭 | 增加曝光时间,开光圈 |
| 图像全白 | 曝光太长/光照太强 | 减少曝光,降增益 |
| 图像闪烁 | 光源频闪 (50Hz) | 曝光时间设为 20ms 整数倍 |
| 帧率达不到 | 带宽不足 | 降低分辨率,减帧率 |
模型相关
| 故障 | 原因 | 解决方法 |
|---|---|---|
| 模型加载失败 | ONNX 版本不兼容 | 检查 opset 版本 |
| 推理结果为空 | 置信度阈值太高 | 降到 0.3 试试 |
| 推理速度慢 | CPU 推理 | 切 GPU 或换小模型 |
| 显存不足 | 模型太大 | 切 CPU 推理 |
| 检测框歪 | 输入尺寸不匹配 | 检查 Resize 参数 |
测量相关
| 故障 | 原因 | 解决方法 |
|---|---|---|
| 边缘检测不到 | 边缘强度弱 | 降低 EdgeThreshold |
| 测量结果跳变 | 边缘不稳定 | 增加测量次数取平均 |
| 单位错误 | 标定系数不对 | 确认像素当量 |
| 圆拟合不准 | 边缘点太少 | 扩大 ROI |
集成相关
| 故障 | 原因 | 解决方法 |
|---|---|---|
| PLC 收不到结果 | VIS_JOB_DONE 未清零 | PLC 中加清零逻辑 |
| Job 排队超时 | 并发数设置太小 | 增加 max_concurrent_jobs |
| 视觉程序不触发 | 触发变量未配置 | 检查触发方式 |
| 结果变量不更新 | 变量名不匹配 | 检查命名 |
12.4 日志分析
日志位置:%AppData%/DarraPLC/logs/vision_*.log
正常日志示例:
[2026-07-20 14:30:00.123] [INFO] [VisionJob] Job #1287 开始执行 (程序: surface_inspect)
[2026-07-20 14:30:00.124] [INFO] [Acquisition] 相机 USB_CAM_1 采集开始, 曝光 5000µs
[2026-07-20 14:30:00.128] [INFO] [Acquisition] 采集完成, 1280×1024, 耗时 4ms
[2026-07-20 14:30:00.129] [INFO] [Preprocessing] 预处理开始
[2026-07-20 14:30:00.132] [INFO] [Preprocessing] 预处理完成, 耗时 3ms
[2026-07-20 14:30:00.133] [INFO] [Inference] 推理开始 (模型: surface_defect v3, GPU)
[2026-07-20 14:30:00.141] [INFO] [Inference] 推理完成, 耗时 8ms, 检测到 2 个目标
[2026-07-20 14:30:00.142] [INFO] [Postprocessing] 后处理完成, 耗时 1ms
[2026-07-20 14:30:00.143] [INFO] [ResultWrite] 结果写入 PLC 变量, 2 个目标
[2026-07-20 14:30:00.143] [INFO] [VisionJob] Job #1287 完成, 总耗时 20ms
故障日志示例:
[2026-07-20 14:31:00.000] [ERROR] [VisionJob] Job #1288 失败
[2026-07-20 14:31:00.000] [ERROR] [Acquisition] 采集超时: 5000ms, 相机 USB_CAM_1
[2026-07-20 14:31:00.001] [WARN] [FaultHandler] 自动重试 #1, 等待 1000ms
[2026-07-20 14:31:01.002] [ERROR] [Acquisition] 采集超时: 5000ms, 相机 USB_CAM_1
[2026-07-20 14:31:01.002] [WARN] [FaultHandler] 自动重试 #2, 等待 2000ms
[2026-07-20 14:31:03.003] [ERROR] [Acquisition] 采集超时: 5000ms, 相机 USB_CAM_1
[2026-07-20 14:31:03.003] [WARN] [FaultHandler] 自动重试 #3, 等待 4000ms
[2026-07-20 14:31:07.004] [ERROR] [Acquisition] 采集超时: 5000ms, 相机 USB_CAM_1
[2026-07-20 14:31:07.004] [ERROR] [FaultHandler] 达到最大重试次数, 上报故障
[2026-07-20 14:31:07.005] [ERROR] [ResultWrite] 写入 PLC 故障码: 0x1001
常用 grep 分析命令:
# 查看所有错误
grep "ERROR" vision_*.log
# 查看某个 Job 的完整执行链
grep "Job #1287" vision_*.log
# 统计各类错误次数
grep -o "error_code: 0x[0-9A-F]*" vision_*.log | sort | uniq -c
# 查看推理延迟
grep "推理完成" vision_*.log
# 查看最近 100 条记录
tail -n 100 vision_*.log
12.5 诊断工具
IDE 诊断面板:
诊断面板
────────────────────────────────────────────────
┌─ 系统信息 ───────────────────────────────────┐
│ Vision Studio 版本: 1.0.0 │
│ ONNX Runtime 版本: 1.17.0 │
│ OpenCV 版本: 4.9.0 │
│ CUDA 版本: 11.8 │
│ 可用 GPU: NVIDIA RTX 3060 (12GB) │
└───────────────────────────────────────────────┘
┌─ 相机诊断 ───────────────────────────────────┐
│ USB_CAM_1: ● 连接正常 1280×1024 30fps │
│ GIGE_CAM_1: ● 连接正常 2448×2048 75fps │
│ 带宽使用: 245 MB/s / 350 MB/s (70%) │
└───────────────────────────────────────────────┘
┌─ 模型诊断 ───────────────────────────────────┐
│ surface_defect v3: ● 已部署 GPU 8ms │
│ position_detect v2: ● 已部署 CPU 25ms │
│ 显存使用: 512 MB / 12288 MB (4%) │
└───────────────────────────────────────────────┘
┌─ 性能基准 ───────────────────────────────────┐
│ 当前推理延迟: 8 ms (历史平均: 7.5 ms) │
│ 当前帧率: 30 fps (目标: 30 fps) │
│ Job 成功率: 99.5% (最近 1000 次) │
└───────────────────────────────────────────────┘
性能基准测试:
# 运行性能基准测试
工具 → 性能基准 → 视觉推理基准
基准测试结果:
────────────────────────────────────────────────
模型: surface_defect v3
输入尺寸: 640×640
迭代次数: 1000
GPU (CUDA):
平均: 7.8 ms (128 fps)
最小: 7.2 ms (139 fps)
最大: 12.4 ms (80 fps)
P99: 9.5 ms
CPU (OpenCV DNN):
平均: 38.2 ms (26 fps)
最小: 35.1 ms (28 fps)
最大: 45.8 ms (21 fps)
P99: 42.3 ms
12.6 常见错误总结
快速排查表:
| 现象 | 优先级 | 检查项 |
|---|---|---|
| 相机无图像 | 最高 | 驱动、线缆、触发模式、曝光 |
| 模型不推理 | 高 | ONNX 版本、输入尺寸、显存 |
| 检测不到目标 | 高 | 置信度阈值、模型质量 |
| 测量不准 | 中 | 标定系数、边缘参数、ROI 位置 |
| 性能慢 | 中 | 推理后端、分辨率、模型大小 |
| PLC 不通信 | 中 | 变量名、触发信号、超时设置 |
| 结果跳变 | 低 | 光源稳定性、边缘强度、滤波 |
| 偶发超时 | 低 | 并发数、资源分配、系统负载 |
本教程所有示例均可在 Darra RT IDE 中直接运行。
更多信息请参考: