在工业视觉检测领域,多相机同步采集是常见的硬性需求。最近我在一个自动化检测项目中遇到了这样的场景:需要同时控制三台海康威视工业相机进行连续图像采集,并将实时画面显示在UI界面中。这种方案在PCB板检测、包装流水线质检等场景中非常普遍。
传统单线程轮询取图方式会导致界面卡顿,而多线程直接操作又容易引发资源竞争。经过实践验证,采用回调函数机制是最可靠的解决方案。下面我将完整分享这个项目的技术实现细节,包括海康SDK的调用技巧、Halcon的图像处理集成,以及WPF界面显示优化方案。
关键提示:使用GigE相机时,建议为每台相机单独分配静态IP(如192.168.1.101~103),避免DHCP导致的连接不稳定。
xml复制<PackageReference Include="HalconDotNet" Version="20.11.0" />
<PackageReference Include="MvCamCtrl.NET" Version="2.1.8" />
mermaid复制graph TD
A[相机1触发采集] --> B[回调函数获取图像]
C[相机2触发采集] --> B
D[相机3触发采集] --> B
B --> E[图像缓冲队列]
E --> F[UI显示线程]
csharp复制public class CameraController
{
private List<ICamera> _cameras = new List<ICamera>();
private ConcurrentQueue<HImage> _imageQueue = new ConcurrentQueue<HImage>();
public void AddCamera(ICamera camera) {...}
public void StartAcquisition() {...}
private void ImageCallback(IntPtr pData, ref MV_FRAME_OUT_INFO_EX pFrameInfo) {...}
}
public interface ICamera
{
void Open();
void StartGrab();
event Action<HImage> ImageReceived;
}
csharp复制// 海康相机初始化
var camera = new HKCamera();
camera.Open("192.168.1.101");
camera.SetEnumValue("AcquisitionMode", 0); // 连续采集模式
camera.SetEnumValue("TriggerMode", 0); // 触发模式关闭
camera.SetFloatValue("ExposureTime", 5000); // 曝光时间5ms
// 注册回调函数
MV_CC_RegisterImageCallBack_NET(camera.Handle, ImageCallback, IntPtr.Zero);
csharp复制private static void ImageCallback(IntPtr pData, ref MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
{
try
{
var image = new HImage();
MV_CC_PIXEL_CONVERT_PARAM_NET convertParam = new MV_CC_PIXEL_CONVERT_PARAM_NET
{
nWidth = pFrameInfo.nWidth,
nHeight = pFrameInfo.nHeight,
pSrcData = pData,
pDstBuffer = Marshal.AllocHGlobal(pFrameInfo.nWidth * pFrameInfo.nHeight * 3)
};
// 转换图像格式为BGR24
camera.MV_CC_ConvertPixelType_NET(ref convertParam);
// 生成Halcon图像
image.GenImageInterleaved(convertParam.pDstBuffer, "bgr",
pFrameInfo.nWidth, pFrameInfo.nHeight, -1, "byte",
pFrameInfo.nWidth, pFrameInfo.nHeight, 0, 0, 8, 0);
_imageQueue.Enqueue(image);
}
catch (Exception ex)
{
Logger.Error($"回调异常: {ex.Message}");
}
}
csharp复制public class ImageBufferPool
{
private ConcurrentBag<HImage> _pool = new ConcurrentBag<HImage>();
public HImage Get() {...}
public void Return(HImage img) {...}
}
csharp复制// WPF图像显示控件
<Image x:Name="dispImage1" Stretch="Uniform">
<Image.Source>
<WriteableBitmap x:Name="wb1"
PixelWidth="2448" PixelHeight="2048"
Format="Bgr24"/>
</Image.Source>
</Image>
// 更新图像方法
void UpdateImage(HImage image)
{
Dispatcher.Invoke(() =>
{
IntPtr ptr = image.GetImagePointer1(out string type, out int width, out int height);
wb1.WritePixels(new Int32Rect(0, 0, width, height),
ptr, width * height * 3, width * 3);
});
}
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 图像卡顿 | 网络带宽不足 | 1. 检查交换机端口速率 2. 降低图像分辨率 3. 启用JPEG压缩 |
| 回调丢失 | 缓冲区溢出 | 1. 增加SDK缓冲区数量 2. 优化处理速度 3. 设置MV_CC_SetImageNodeNum_NET |
| 内存泄漏 | 未释放资源 | 1. 使用using块包裹HImage 2. 定期调用GC.Collect() 3. 检查Marshal.FreeHGlobal调用 |
在以下配置下的测试结果:
| 指标 | 单相机 | 三相机同步 |
|---|---|---|
| CPU占用率 | 12% | 35% |
| 内存占用 | 450MB | 1.2GB |
| 延迟时间 | 80ms | 120ms |
| 丢帧率 | 0.1% | 0.5% |
触发同步方案:通过IO触发信号实现三相机硬件同步
csharp复制camera.SetEnumValue("TriggerMode", 1); // 触发模式开启
camera.SetEnumValue("TriggerSource", 7); // 硬件触发
HDR成像:通过曝光时间设置实现高动态范围
csharp复制var exposures = new[] { 2000, 5000, 10000 };
foreach(var exp in exposures)
{
camera.SetFloatValue("ExposureTime", exp);
camera.MV_CC_StartGrabbing_NET();
Thread.Sleep(100);
}
三维重建:结合多视角图像进行三维点云重建
halcon复制gen_binocular_rectification_map (..., Map1, Map2)
disparity_image_to_xyz (..., X, Y, Z)
这个方案目前已在多个工业现场稳定运行超过2000小时。实际部署时建议增加看门狗机制,当相机断线时能自动重连。对于更高帧率需求(>30fps),可以考虑使用CameraLink接口的相机型号。