最近在工业视觉检测项目中遇到了一个典型需求:需要同时控制三台海康威视工业相机进行连续采集,并将实时图像显示在UI界面中。这个看似基础的需求在实际实现时会遇到不少技术难点,特别是当需要保证图像采集的实时性和稳定性时。
核心需求可以拆解为三个技术要点:
这个方案在自动化检测线上很常见,比如在电子元件外观检测中,可能需要从不同角度同步采集产品图像;或者在物流分拣系统中,需要同时获取物品的顶视和侧视图。这类场景对采集同步性和系统稳定性要求很高。
在实际项目中,三台海康相机我选用的是MV-CE060-10GC型号(600万像素,GigE接口),选择这个型号主要考虑:
重要提示:使用多台GigE相机时,建议为每台相机分配独立的物理网卡,或者使用支持巨帧(Jumbo Frame)的交换机。我最初尝试用单网卡连接三台相机时,经常出现丢帧现象。
需要特别注意的依赖项:
xml复制<!-- 在.csproj文件中需要包含 -->
<Reference Include="HalconDotNet"/>
<Reference Include="MvCamCtrl.NET"/>
海康相机的控制核心是其提供的MvCamCtrl.NET.dll,封装了相机操作的所有API。多相机初始化的典型代码如下:
csharp复制// 枚举设备
List<CameraInfo> cameraList = new List<CameraInfo>();
int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE, ref deviceList);
if (nRet != 0 || deviceList.nDeviceNum < 3)
{
throw new Exception("未找到足够数量的相机设备");
}
// 创建并初始化相机实例
List<MyCamera> cameras = new List<MyCamera>();
for (int i = 0; i < 3; i++)
{
var cam = new MyCamera();
nRet = cam.MV_CC_CreateDevice_NET(ref deviceList.pDeviceInfo[i]);
nRet = cam.MV_CC_OpenDevice_NET();
// 设置采集模式为连续采集
nRet = cam.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
cameras.Add(cam);
}
在多相机系统中,以下几个参数需要特别注意调整:
配置示例:
csharp复制// 设置网络参数(每台相机都需要单独设置)
cam.MV_CC_SetIntValue_NET("GevSCPSPacketSize", 9000);
// 设置采集参数
cam.MV_CC_SetFloatValue_NET("AcquisitionFrameRate", 15.0f);
cam.MV_CC_SetFloatValue_NET("ExposureTime", 5000.0f);
相比主动取图(GetImage),回调函数取图有以下优势:
核心实现步骤:
csharp复制// 定义回调函数
private void ImageCallback(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
int cameraIndex = (int)pUser;
// 转换图像数据为Halcon对象
HObject image = ConvertToHalconImage(pData, pFrameInfo);
// 更新对应相机的显示
UpdateDisplay(cameraIndex, image);
}
// 为每台相机注册回调
for (int i = 0; i < 3; i++)
{
cameras[i].MV_CC_RegisterImageCallBackEx_NET(ImageCallback, (IntPtr)i);
cameras[i].MV_CC_StartGrabbing_NET();
}
海康SDK返回的是原始图像数据,需要转换为Halcon能处理的格式:
csharp复制private HObject ConvertToHalconImage(IntPtr pData, MyCamera.MV_FRAME_OUT_INFO_EX frameInfo)
{
HObject image = new HObject();
if (frameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8)
{
// 单通道8位图像
HalconAPI.GenImage1(out image, "byte",
frameInfo.nWidth, frameInfo.nHeight, pData);
}
else if (frameInfo.enPixelType == MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
{
// RGB三通道图像
HalconAPI.GenImageInterleaved(out image, pData, "rgb",
frameInfo.nWidth, frameInfo.nHeight, -1, "byte",
frameInfo.nWidth, frameInfo.nHeight, 0, 0, -1, 0);
}
return image;
}
在WPF中显示Halcon图像需要特殊处理,因为Halcon的HWindowControl是WinForms控件。我采用的方案是:
XAML部分:
xml复制<Grid>
<WindowsFormsHost Grid.Column="0">
<halcon:HWindowControl x:Name="hWindow1"/>
</WindowsFormsHost>
<WindowsFormsHost Grid.Column="1">
<halcon:HWindowControl x:Name="hWindow2"/>
</WindowsFormsHost>
<WindowsFormsHost Grid.Column="2">
<halcon:HWindowControl x:Name="hWindow3"/>
</WindowsFormsHost>
</Grid>
由于回调函数在非UI线程执行,必须通过Dispatcher更新界面:
csharp复制private void UpdateDisplay(int cameraIndex, HObject image)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
switch(cameraIndex)
{
case 0:
hWindow1.HalconWindow.DispObj(image);
break;
case 1:
hWindow2.HalconWindow.DispObj(image);
break;
case 2:
hWindow3.HalconWindow.DispObj(image);
break;
}
// 显示帧率等信息
UpdateStatus(cameraIndex);
}));
}
在多相机连续采集场景下,内存泄漏是常见问题。需要特别注意:
优化后的回调示例:
csharp复制private ConcurrentBag<HObject> _imagePool = new ConcurrentBag<HObject>();
private void ImageCallback(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
if (!_imagePool.TryTake(out HObject image))
{
image = new HObject();
}
// 重用Halcon对象
ConvertToHalconImage(image, pData, pFrameInfo);
// ...显示处理...
// 使用后放回池中
_imagePool.Add(image);
}
虽然三台相机是独立采集,但某些应用场景需要保证图像的时间同步性。可以采用:
硬件同步配置示例:
csharp复制// 配置为硬件触发模式
cam.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
cam.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_LINE0);
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 随机单帧丢失 | 网络带宽不足 | 降低帧率或分辨率,启用Jumbo Frame |
| 连续多帧丢失 | 回调处理耗时过长 | 优化图像处理算法,减少回调函数工作量 |
| 周期性丢帧 | 交换机缓冲不足 | 更换高性能工业交换机,调整流量控制参数 |
SDK初始化失败
图像显示卡顿
相机连接不稳定
在实际项目中,这个基础框架可以扩展为:
一个进阶技巧是使用Halcon的GPU加速功能处理图像:
csharp复制// 在初始化时启用GPU
HOperatorSet.SetSystem("use_gpu", "true");
// 指定使用的GPU设备
HOperatorSet.SetSystem("gpu_device_id", "0");
经过多次项目验证,这套方案可以稳定实现三台200万像素相机在30fps下的连续采集与显示,平均CPU占用率在15%以下(i7-9700K处理器)。关键是要确保网络配置优化和回调函数高效执行。