C# + 机器视觉 + AI:从工业相机取图到 YOLO 目标检测的完整工控解决方案
🤖 C# + 机器视觉 + AI:从工业相机取图到 YOLO 目标检测的完整工控解决方案
📝 前言
随着工业 4.0 和智能制造的浪潮,机器视觉与 AI 的结合已成为工控领域的热门技术。传统的视觉方案往往依赖规则算法,难以应对复杂场景;而引入深度学习模型(如 YOLO)后,我们可以实现高精度的目标检测、缺陷分类等智能应用。
本文将带你从零开始,在 C#(WinForms) 中实现一个完整的工控视觉系统:
- 通过 海康工业相机 SDK 实时取图
- 调用 Python + YOLOv8 进行目标检测
- 在上位机界面实时显示检测结果
- 可选:通过 Modbus TCP 与 PLC 通信,根据检测结果控制设备
这是一个典型的“工控 + AI”落地案例,代码可直接用于项目原型开发,希望能为你的技术栈注入新的灵感。
🛠 环境准备
硬件
- 海康威视工业相机(网口或 USB3.0)
- 工控机 / PC(推荐带独立显卡,加速推理)
- 可选:PLC(如西门子 S7-1200)用于联动控制
软件与库
| 组件 | 说明 |
|---|---|
| Visual Studio 2022 | C# 开发环境 |
| .NET 6.0 / .NET Framework 4.8 | 目标框架 |
| MVS(机器视觉软件) | 海康相机驱动及 SDK |
| Python 3.9+ | 深度学习推理环境 |
| ultralytics | YOLOv8 推理库 |
| OpenCV-Python | 图像处理 |
| NModbus4 | C# Modbus 通信库(可选) |
安装命令:
pip install ultralytics opencv-python numpy
🏗 系统架构
┌─────────────────────────────────────────────────────────┐
│ C# 上位机 (WinForms) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 相机控制模块 │ │ 图像显示模块 │ │ 结果解析模块 │ │
│ │ (海康SDK) │ │ (PictureBox)│ │ (JSON) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Python 推理引擎 (进程调用) │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ YOLOv8 模型 (ultralytics) │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PLC (Modbus TCP / 串口) │ │
│ │ 接收结果,执行动作(报警/分拣) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
🔌 第一步:海康相机实时取图
参考上一篇文章《C# 调用相机终极指南》,我们封装一个 HikCamera 类,实现相机初始化、开始采集和图像回调。
关键代码片段
using MvCameraControl;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
public class HikCamera
{
private MyCamera _camera;
private bool _isGrabbing;
public event Action<Bitmap> ImageCaptured;
public bool Open()
{
// 枚举设备、创建、打开(具体代码见上篇文章)
// ...
_camera.MV_CC_RegisterImageCallBack_NET(ImageCallback, IntPtr.Zero);
_camera.MV_CC_StartGrabbing_NET();
_isGrabbing = true;
return true;
}
private void ImageCallback(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
if (!_isGrabbing) return;
// 将原始数据转为 Bitmap
Bitmap bitmap = ConvertToBitmap(pData, pFrameInfo);
ImageCaptured?.Invoke(bitmap);
}
private Bitmap ConvertToBitmap(IntPtr pData, MyCamera.MV_FRAME_OUT_INFO_EX info)
{
// 假设相机输出 BGR8 格式
int width = (int)info.nWidth;
int height = (int)info.nHeight;
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
bmp.PixelFormat);
int stride = bmpData.Stride;
int bytesPerPixel = 3;
for (int y = 0; y < height; y++)
{
IntPtr srcLine = IntPtr.Add(pData, y * info.nFrameLen / height);
IntPtr dstLine = IntPtr.Add(bmpData.Scan0, y * stride);
CopyMemory(dstLine, srcLine, width * bytesPerPixel);
}
bmp.UnlockBits(bmpData);
return bmp;
}
[DllImport("kernel32.dll")]
private static extern void CopyMemory(IntPtr dest, IntPtr src, int length);
}
在 WinForms 中,订阅 ImageCaptured 事件,将图片显示到 PictureBox 上。
🐍 第二步:Python YOLO 推理脚本
我们编写一个脚本 yolo_detect.py,接收 base64 编码的图像,返回检测结果的 JSON。
import sys
import json
import base64
import cv2
import numpy as np
from ultralytics import YOLO
# 加载模型(首次运行会自动下载)
model = YOLO('yolov8n.pt') # 可换成自己训练的模型
def detect_objects(image_base64):
# 解码图像
img_bytes = base64.b64decode(image_base64)
nparr = np.frombuffer(img_bytes, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# 推理
results = model(img, conf=0.5) # 置信度阈值
detections = []
for r in results:
boxes = r.boxes
if boxes is None:
continue
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
conf = box.conf[0].item()
cls = int(box.cls[0].item())
detections.append({
"class": cls,
"class_name": model.names[cls],
"confidence": conf,
"bbox": [x1, y1, x2, y2]
})
return json.dumps(detections)
if __name__ == "__main__":
input_data = sys.stdin.read().strip()
if input_data:
output = detect_objects(input_data)
sys.stdout.write(output)
else:
sys.stderr.write("No image data")
提示:如果推理速度慢,可以启用 GPU(需安装 CUDA 和 torch with CUDA)。
🔄 第三步:C# 调用 Python 脚本
我们复用上篇文章的 PythonHelper 类,将相机捕获的 Bitmap 转为 base64,传给 Python,解析返回的 JSON。
在 WinForms 中集成
public partial class FormMain : Form
{
private HikCamera _camera;
private PythonHelper _pythonHelper;
private System.Windows.Forms.Timer _inferenceTimer; // 控制推理频率
private Bitmap _currentFrame;
public FormMain()
{
InitializeComponent();
_pythonHelper = new PythonHelper("python", @"D:\Projects\yolo_detect.py");
_camera = new HikCamera();
_camera.ImageCaptured += OnImageCaptured;
// 定时器:每隔 100ms 取最新帧进行推理
_inferenceTimer = new System.Windows.Forms.Timer();
_inferenceTimer.Interval = 100;
_inferenceTimer.Tick += async (s, e) => await DetectOnFrame();
_inferenceTimer.Start();
}
private void OnImageCaptured(Bitmap bitmap)
{
// 显示实时画面
pictureBox1.Image = bitmap;
// 保存最新帧,供推理使用
_currentFrame = new Bitmap(bitmap);
}
private async Task DetectOnFrame()
{
if (_currentFrame == null) return;
// 转为 base64
string base64 = ImageToBase64(_currentFrame);
try
{
string jsonResult = await _pythonHelper.RunScriptAsync(base64);
var detections = JsonConvert.DeserializeObject<List<Detection>>(jsonResult);
DrawDetections(detections);
}
catch (Exception ex)
{
// 记录错误,不弹窗以免影响实时性
Console.WriteLine($"推理错误: {ex.Message}");
}
}
private string ImageToBase64(Image img)
{
using (var ms = new MemoryStream())
{
img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return Convert.ToBase64String(ms.ToArray());
}
}
private void DrawDetections(List<Detection> detections)
{
if (pictureBox1.Image == null) return;
// 在原图上绘制检测框
Bitmap bmp = new Bitmap(pictureBox1.Image);
using (Graphics g = Graphics.FromImage(bmp))
{
foreach (var det in detections)
{
float x1 = (float)det.bbox[0];
float y1 = (float)det.bbox[1];
float x2 = (float)det.bbox[2];
float y2 = (float)det.bbox[3];
using (Pen pen = new Pen(Color.Red, 2))
{
g.DrawRectangle(pen, x1, y1, x2 - x1, y2 - y1);
}
using (Font font = new Font("Arial", 12))
using (SolidBrush brush = new SolidBrush(Color.Red))
{
string text = $"{det.class_name} {det.confidence:P0}";
g.DrawString(text, font, brush, x1, y1 - 15);
}
}
}
pictureBox1.Image = bmp;
}
private class Detection
{
public int @class { get; set; }
public string class_name { get; set; }
public double confidence { get; set; }
public double[] bbox { get; set; }
}
}
🏭 第四步:与 PLC 通信(可选)
根据检测结果,我们可以通过 Modbus TCP 控制 PLC 执行相应动作(如报警、分拣)。这里使用 NModbus4 库。
安装 NuGet:Install-Package NModbus4
示例:检测到“person”类且置信度>0.8时,写线圈
using Modbus.Device;
using System.Net.Sockets;
public class ModbusController
{
private TcpClient _tcpClient;
private ModbusIpMaster _master;
public void Connect(string ip, int port)
{
_tcpClient = new TcpClient(ip, port);
_master = ModbusIpMaster.CreateIp(_tcpClient);
_master.Transport.ReadTimeout = 1000;
}
public void WriteCoil(int address, bool value)
{
_master.WriteSingleCoil(address, value);
}
}
// 在检测结果回调中调用
if (detections.Any(d => d.class_name == "person" && d.confidence > 0.8))
{
_modbus.WriteCoil(0, true); // 触发报警
}
else
{
_modbus.WriteCoil(0, false);
}
⚙️ 性能优化技巧
- 多线程:相机回调线程只负责保存最新帧,推理在单独的 Task 中执行,避免阻塞。
- 帧率控制:通过 Timer 控制推理频率,避免 GPU 过载。
- 图像缩放:将相机图像缩放至 640x640 再送入 YOLO,可大幅提升推理速度。
- 模型量化:使用 YOLOv8 的 int8 量化模型,进一步加速。
- 进程复用:如果频繁调用,可考虑使用 Python.NET 或 HTTP 服务,避免每次启动进程的开销。
📚 参考文档与资源
- 海康机器视觉 SDK:MVS 开发指南
- YOLOv8 官方文档:Ultralytics YOLOv8
- C# Modbus 通信:NModbus4 GitHub
- OpenCvSharp 图像处理:OpenCvSharp 文档
🧩 总结与展望
本文提供了一个完整的“工控+AI”解决方案,涵盖了工业相机取图、YOLO 目标检测、上位机界面显示以及与 PLC 联动的全流程。你可以在此基础上:
- 替换为自己训练的 YOLO 模型(缺陷检测、字符识别等)
- 增加多相机支持
- 集成数据库记录检测结果
- 使用 Web 界面替代 WinForms,实现远程监控
机器视觉与 AI 的融合正在重塑工业自动化,希望这篇博客能为你打开一扇新的大门。
如果你觉得有用,欢迎点赞、收藏、评论!你的支持是我持续创作的动力!
Happy Coding! 🚀
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)