从零打造 C# 工业视觉检测系统(四):海康威视 MVS SDK 入门与相机枚举实战
系列标签 :
C#海康威视MVS SDK工业相机机器视觉本文目标:掌握工业相机选型要点,学会使用海康 MVS SDK 枚举和发现设备
一、前言
工业相机是视觉检测系统的"眼睛"。与手机摄像头不同,工业相机需要:
- 高稳定性:7x24 小时连续工作
- 低延迟:从触发到出图 < 100ms
- 可编程控制:曝光、增益、白平衡等参数由软件控制
- 标准接口:GigE(千兆网)、USB3.0、CameraLink
本文将带你从相机选型开始,一直到用 C# 代码枚举出局域网中的所有相机。
二、工业相机选型指南
2.1 关键参数解读
| 参数 | 说明 | 选型建议 |
|---|---|---|
| 分辨率 | 像素数量,如 1920x1200 | 根据检测精度要求计算:精度 = 视野 / 分辨率 |
| 帧率 (FPS) | 每秒采集帧数 | 产线速度越快,帧率要求越高 |
| 传感器类型 | CCD / CMOS | CMOS 已成为主流,性价比高 |
| 接口类型 | GigE / USB3.0 / CamLink | GigE 传输距离远(100m),USB3.0 即插即用 |
| 触发模式 | 软触发 / 硬触发 | 产线联动通常使用硬触发(外接传感器) |
2.2 海康威视 MVS 生态
海康机器人(Hikrobot)是国内工业视觉领域的龙头企业,其 MVS(Machine Vision Software) 提供:
- MVS 客户端(类似相机厂商的"取景器")
- SDK 开发包(C/C++/C#/Python)
- 丰富的示例代码和文档
三、MVS SDK 环境配置
3.1 安装步骤
- 从海康机器人官网下载 MVS 标准开发包
- 安装时勾选"安装驱动"和".NET 开发包"
- 安装完成后,在
C:\Program Files (x86)\MVS\Development\DotNet\win64\目录下找到MvCamCtrl.Net.dll
3.2 项目引用
在 FormUI.csproj 中引用:
xml
<Reference Include="MvCamCtrl.Net">
<HintPath>..\..\..\MVS\Development\DotNet\win64\MvCamCtrl.Net.dll</HintPath>
</Reference>
注意 :路径需根据实际安装位置调整。如果团队开发,建议将 DLL 复制到项目 libs 目录下统一引用。
3.3 命名空间
csharp
using MvCamCtrl.NET;
using MvCamCtrl.NET.CameraParams;
四、核心数据结构
4.1 MV_CC_DEVICE_INFO_LIST
设备列表结构体,用于存储枚举到的所有相机:
csharp
// SDK 中的定义(简化示意)
public struct MV_CC_DEVICE_INFO_LIST
{
public uint nDeviceNum; // 设备数量
public IntPtr[] pDeviceInfo; // 设备信息指针数组
}
4.2 MV_CC_DEVICE_INFO
单个设备的详细信息,包含设备型号、序列号、IP 地址等:
csharp
public struct MV_CC_DEVICE_INFO
{
public MV_GIGE_DEVICE_INFO SpecialInfo; // GigE 设备特有信息
// ... 其他字段
}
五、相机枚举实战
5.1 P/Invoke 声明
由于 SDK 底层是 C++ 实现,C# 需要通过 DllImport 调用:
csharp
public static class DllMethod
{
private const string CameraDll = @".\Algorithm.dll";
[DllImport(CameraDll, CallingConvention = CallingConvention.Cdecl)]
public static extern int EnumCameraDevices(IntPtr p);
[DllImport(CameraDll, CallingConvention = CallingConvention.Cdecl)]
public static extern int InitializeCanmera();
[DllImport(CameraDll, CallingConvention = CallingConvention.Cdecl)]
public static extern int ConnectCamera(int selectIndex, int selectCameraIndex,
ref int CameraId, ref int ImgWidth, ref int ImgHeight);
[DllImport(CameraDll, CallingConvention = CallingConvention.Cdecl)]
public static extern int DisconnectCamera(int CameraId);
}
注意 :Algorithm.dll 是本项目的算法封装 DLL,内部调用了海康 SDK。实际开发中,你也可以直接调用 MvCamCtrl.Net 提供的托管方法。
5.2 枚举设备的核心代码
csharp
public static int EnumCamera()
{
// 分配非托管内存,用于接收设备列表
IntPtr pt = Marshal.AllocHGlobal(
Marshal.SizeOf(typeof(MV_CC_DEVICE_INFO_LIST)));
// 调用 SDK 枚举设备
int ret = EnumCameraDevices(pt);
if (ret != 0)
{
Marshal.FreeHGlobal(pt);
return ret; // 返回错误码
}
// 将非托管内存转换为托管结构体
CameraData._DeviceList = (MV_CC_DEVICE_INFO_LIST)Marshal.PtrToStructure(
pt, typeof(MV_CC_DEVICE_INFO_LIST));
// 遍历每个设备,提取详细信息
for (int i = 0; i < CameraData._DeviceList.nDeviceNum; i++)
{
CameraData._DeviceInfoList[i] = (MV_CC_DEVICE_INFO)Marshal.PtrToStructure(
CameraData._DeviceList.pDeviceInfo[i], typeof(MV_CC_DEVICE_INFO));
}
// 释放非托管内存!重要!
Marshal.FreeHGlobal(pt);
return ret;
}
关键点:
Marshal.AllocHGlobal/Marshal.FreeHGlobal必须成对出现,否则内存泄漏PtrToStructure将 C++ 结构体指针转换为 C# 结构体- 错误码
ret需要查 SDK 文档,0 通常表示成功
5.3 SelectCamera 弹窗实现
枚举到设备后,通过弹窗让用户选择要连接的相机:
csharp
public partial class SelectCamera : Sunny.UI.UIForm
{
public int selectCameraIndex = -1;
private void SelectCamera_Load(object sender, EventArgs e)
{
selectCameraIndex = -1;
// 枚举设备
DllMethod.EnumCamera();
for (int i = 0; i < CameraData._DeviceList.nDeviceNum; i++)
{
// 提取型号名称(GB2312 编码)
string modelTop = Encoding.GetEncoding("GB2312")
.GetString(CameraData._DeviceInfoList[i].SpecialInfo.stGigEInfo, 52, 20)
.TrimEnd('\0');
string modelBottom = Encoding.GetEncoding("GB2312")
.GetString(CameraData._DeviceInfoList[i].SpecialInfo.stGigEInfo, 164, 20)
.TrimEnd('\0');
string cameraItem = modelTop + "(" + modelBottom + ")";
this.DeviceListBox.Items.Add(
string.IsNullOrEmpty(cameraItem) ? "Camera" + i : cameraItem);
}
// 已连接的相机置灰禁用
for (int i = 0; i < CameraData.MAX_CAMERA_NUM; i++)
{
if (CameraData.CameraIndex[i] != -1)
{
AddNonSelectableIndex(CameraData.CameraIndex[i]);
}
}
}
}
细节说明:
- 海康 SDK 返回的字符串是 GB2312 编码 ,必须用
Encoding.GetEncoding("GB2312")解码,否则中文乱码 TrimEnd('\0')去除 C 风格字符串的结束符- 通过
AddNonSelectableIndex将已连接的相机设为不可选,避免重复连接
5.4 已连接设备禁用的实现
csharp
private HashSet<int> _nonSelectableIndices = new HashSet<int>();
private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index < 0) return;
bool isNonSelectable = _nonSelectableIndices.Contains(e.Index);
string itemText = DeviceListBox.Items[e.Index].ToString();
// 已连接的设备显示灰色
Color textColor = isNonSelectable ? Color.Gray : e.ForeColor;
using (Brush brush = new SolidBrush(textColor))
{
e.Graphics.DrawString(itemText, e.Font, brush, e.Bounds);
}
e.DrawFocusRectangle();
}
六、常见问题与排查
Q1:EnumCameraDevices 返回非 0 错误码?
- 检查 MVS 客户端能否识别相机
- 检查防火墙是否阻挡了 GigE 发现端口(默认 3956)
- 确认相机 IP 与电脑在同一网段
Q2:中文相机名称乱码?
必须使用 Encoding.GetEncoding("GB2312"),UTF-8 无法正确解码。
Q3:已断开连接的相机仍显示灰色?
需要在断开连接时调用 RemoveNonSelectableIndex,并刷新 ListBox。
七、总结
本文从工业相机选型开始,讲解了:
- 关键参数:分辨率、帧率、接口类型的选型方法
- 环境配置:MVS SDK 安装与 DLL 引用
- 枚举设备 :
Marshal非托管内存操作、PtrToStructure转换 - UI 交互:弹窗选择、已连接设备禁用
下篇预告:《从零打造 C# 工业视觉检测系统(五):相机连接、连续取流与双路实时预览》
我们将实现相机的连接/断开、连续取流,以及将 IntPtr 图像数据显示到 PictureBox 中。
欢迎点赞、收藏、关注,你的支持是我持续输出的动力!