Halcon 连海康相机,最正统的方案是用 Halcon 自带的 GigE Vision / USB3 Vision 接口直接取流。但现实是:
- 海康的 SDK(MVS)默认装完后,Halcon 的
open_framegrabber 不一定能直接认到设备 - 取到了图像又可能遇到掉帧、断开、缓存堆积
- 好不容易调通了,转到 C# 环境又来一波配置问题
这套流程我前后折腾了三个晚上,把踩过的坑和最终的稳定方案整理出来。
环境:
- Halcon 20.11(HDevelop + .NET)
- Visual Studio 2022 Community
- 海康 MVS 4.2.1
- 相机:MV-CA013-21GC(GigE,130万像素)
- 网卡:Intel I219-LM(板载)
坑 1:Halcon 认不到海康相机#
插上相机,MVS 能正常出图,但 Halcon 的 HDevelop 里 open_framegrabber ('GigEVision', ...) 返回空设备列表。
Halcon 的 GigE Vision 采集驱动 不依赖 MVS,它有自己的传输层。如果先装了 MVS,MVS 的 Filter Driver 占用了 GigE Vision 设备,Halcon 的驱动就接不上。
方案 A(推荐):卸载 MVS 的 Filter Driver,用 Halcon 的传输层
打开 MVS 安装目录下的 FilterDriverTool:
C:\Program Files (x86)\MVS\Development\FilterDriverTool\FilterDriverTool.exe
把里面的 GigE 相机 Filter Driver 开关关掉,或者直接把 Filter Driver 卸载。
重启后 Halcon 就能认到了。
方案 B(如果你非要用 MVS 的驱动):用 Halcon 的 GenericImageAcquisition 接口
1
2
3
4
| // C# 通过 MVS SDK 取到 Bitmap,再转给 Halcon
HObject ho_Image;
Bitmap bmp = MVS_GetImage(); // MVS SDK 回调取图
HOperatorSet.GenImageInterleaved(out ho_Image, bmp.GetHbitmap(), "bgrx", width, height, 0);
|
这个方案多一次内存拷贝,帧率会掉 10-15%,但对稳定性要求高的场合够用。
验证方法#
在 HDevelop 里跑这句,能列出设备就对了:
* 列出所有 GigE Vision 设备
list_system ('framegrabbers', Information, InfoValues)
* 如果列表里有设备,直接打开
open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'CAMERA_001', 0, -1, AcqHandle)
⚠️ CAMERA_001 是你的设备名,可以在 MVS 里看到,或者在 Halcon 的 信息 窗口里查。
坑 2:GigE 网卡配置不对,疯狂掉帧#
图像能出来,但跑个几分钟就开始掉帧。帧率从 30fps 掉到 5fps,然后断开。
Halcon 的 GigE 驱动对网卡的 Jumbo Frame(巨型帧) 和 接收缓冲区 有要求。默认 Windows 网卡设置太小。
网卡属性里改两处:
1. 巨型帧 → 9014 Bytes(或 9000)
2. 接收缓冲区 → 2048(最大)
具体路径:
控制面板 → 网络和共享中心 → 更改适配器设置
→ 右键你的相机网卡 → 属性 → 配置 → 高级
改完重启,帧率稳定在 30fps,连续跑了一下午没掉过。
如果还掉帧#
把 MVS 的 GvspPacketDelay 参数从默认的 3000 改到 5000(微秒):
* Halcon 里设置 Packet Delay
set_framegrabber_param (AcqHandle, 'GvspPacketDelay', 5000)
这个参数控制相机等待网络包的宽容度。千兆网在长距离或交换机环境容易丢包,加这个值能显著减少重传。
坑 3:连续采集,缓存越堆越多#
第一次取图正常,第二次慢了,第三次直接卡死。
Halcon 的 grab_image 每次调用都会分配新内存。循环取图时如果没有及时释放 HObject,内存涨得比血压还快。
每次循环记得释放临时变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // C# 循环取图的正确写法
HOperatorSet.OpenFramegrabber("GigEVision", 0, 0, 0, 0, 0, 0, "default",
-1, "default", -1, "false", "default", cameraName, 0, -1, out acqHandle);
while (running)
{
HObject ho_Image = null;
HTuple hv_AcqHandle = null;
try
{
HOperatorSet.GrabImage(out ho_Image, acqHandle);
// 在这里做图像处理
// ...
// 关键:用完立即释放
ho_Image.Dispose();
}
catch (HalconException ex)
{
// 超时或断开时捕获异常,不要崩
Console.WriteLine($"采集异常: {ex.Message}");
break;
}
}
HOperatorSet.CloseFramegrabber(acqHandle);
|
另一条原则: 不要在取图线程里做耗时处理。取图和图像处理分离:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // ❌ 错误:取图线程里做模板匹配
while (running)
{
GrabImage(out img, handle);
FindScaledShapeModel(img, out ID); // 耗时 30ms,下一帧就丢了
}
// ✅ 正确:取图扔队列,处理线程消费
ConcurrentQueue<HObject> queue = new ConcurrentQueue<HObject>();
// 取图线程
while (running)
{
GrabImage(out img, handle);
queue.Enqueue(img.Clone());
img.Dispose();
}
// 处理线程
while (running)
{
if (queue.TryDequeue(out HObject frame))
{
ProcessImage(frame);
frame.Dispose();
}
}
|
坑 4:C# 项目里 Halcon .NET 引用找不到#
Visual Studio 添加引用时找不到 halcon.dll 或 halcondotnet.dll。
Halcon 的 .NET 程序集没有自动注册到 GAC,必须手动引用。
1. 项目 → 添加引用 → 浏览
2. 找到 Halcon 安装目录,一般在:
C:\Program Files\MVTec\Halcon20.11\bin\dotnet20\
3. 选这两个:
→ halcondotnet.dll
→ hdevenginedotnet.dll
4. 如果是 x64 项目,确保目标平台是 x64:
项目属性 → 生成 → 目标平台 → x64
别忘了把 halcon.dll 复制到输出目录,或者把 Halcon 的 bin 目录加到系统 PATH。
一个偷懒的写法是直接写 Post-Build 事件:
1
2
3
| <PostBuildEvent>
copy "$(HALCONROOT)\bin\$(HALCONARCH)\halcon.dll" "$(TargetDir)"
</PostBuildEvent>
|
坑 5:Halcon 采集超时而没有错误处理#
相机如果被拔掉、网线松动、或者相机死机,Halcon 的 grab_image 会卡住不动,不会返回错误。
默认的 Halcon 采集没有超时机制,它会一直等下一帧。
设置采集超时参数:
* 设置超时:3000ms
set_framegrabber_param (AcqHandle, 'timeout', 3000)
C# 里捕获超时异常:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| HOperatorSet.SetFramegrabberParam(acqHandle, "timeout", 3000);
try
{
HOperatorSet.GrabImage(out ho_Image, acqHandle);
}
catch (HalconException ex)
{
// 超时会抛出 HALCON Error #5312
if (ex.ErrorCode == 5312)
{
Console.WriteLine("采集超时,检查相机连接");
// 重新连接
ReconnectCamera();
}
}
|
心跳检查: 用一个定时器每隔 5 秒检查一下采集线程是否还在跑:
1
2
3
4
5
6
7
8
9
10
11
12
13
| // 5秒没取到帧,判定相机断开
private DateTime _lastFrameTime = DateTime.Now;
private readonly TimeSpan _heartbeatTimeout = TimeSpan.FromSeconds(5);
// 取图线程里更新
_lastFrameTime = DateTime.Now;
// 心跳线程
if (DateTime.Now - _lastFrameTime > _heartbeatTimeout)
{
Console.WriteLine("相机心跳超时,尝试重连...");
ReconnectCamera();
}
|
完整代码:C# + Halcon 海康相机采集 Demo#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
| using System;
using System.Threading;
using System.Threading.Tasks;
using HalconDotNet;
namespace HikvisionHalconDemo
{
public class CameraCapture : IDisposable
{
private HTuple _acqHandle;
private CancellationTokenSource _cts;
private DateTime _lastFrameTime;
private bool _isRunning;
public event Action<HObject> FrameCaptured;
public event Action<string> ErrorOccurred;
public bool Connect(string cameraName)
{
try
{
HOperatorSet.OpenFramegrabber("GigEVision", 0, 0, 0, 0, 0, 0,
"default", -1, "default", -1, "false", "default",
cameraName, 0, -1, out _acqHandle);
// 配置参数
HOperatorSet.SetFramegrabberParam(_acqHandle, "timeout", 3000);
HOperatorSet.SetFramegrabberParam(_acqHandle, "GvspPacketDelay", 5000);
Console.WriteLine($"相机 {cameraName} 连接成功");
return true;
}
catch (HalconException ex)
{
Console.WriteLine($"连接失败: {ex.Message}");
return false;
}
}
public void StartCapture()
{
if (_acqHandle == null || _acqHandle.Length == 0)
throw new InvalidOperationException("请先连接相机");
_isRunning = true;
_cts = new CancellationTokenSource();
Task.Run(() => CaptureLoop(_cts.Token));
}
private void CaptureLoop(CancellationToken token)
{
HObject ho_Image = null;
while (!token.IsCancellationRequested && _isRunning)
{
try
{
HOperatorSet.GrabImage(out ho_Image, _acqHandle);
_lastFrameTime = DateTime.Now;
FrameCaptured?.Invoke(ho_Image);
}
catch (HalconException ex)
{
if (ex.ErrorCode == 5312) // 超时
{
ErrorOccurred?.Invoke("采集超时,检查相机连接");
}
else
{
ErrorOccurred?.Invoke($"采集异常: {ex.Message}");
}
// 尝试重连
ReconnectCamera();
break;
}
finally
{
ho_Image?.Dispose();
}
}
}
private void ReconnectCamera()
{
Console.WriteLine("正在尝试重连...");
try
{
HOperatorSet.CloseFramegrabber(_acqHandle);
Thread.Sleep(1000);
string deviceName = GetCameraName();
if (!string.IsNullOrEmpty(deviceName) && Connect(deviceName))
{
Console.WriteLine("重连成功");
StartCapture();
}
}
catch (Exception ex)
{
ErrorOccurred?.Invoke($"重连失败: {ex.Message}");
}
}
private string GetCameraName()
{
try
{
HTuple information, infoValues;
HOperatorSet.ListSystem("framegrabbers", out information, out infoValues);
for (int i = 0; i < infoValues.Length; i++)
{
string val = infoValues[i].S;
if (val.Contains("CAMERA"))
return val;
}
}
catch { }
return null;
}
public void Stop()
{
_isRunning = false;
_cts?.Cancel();
}
public void Dispose()
{
Stop();
if (_acqHandle != null && _acqHandle.Length > 0)
{
HOperatorSet.CloseFramegrabber(_acqHandle);
_acqHandle.Dispose();
}
_cts?.Dispose();
}
}
}
|
使用示例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| class Program
{
static void Main(string[] args)
{
using (var camera = new CameraCapture())
{
camera.FrameCaptured += (img) =>
{
// 在这里做图像处理
// 比如:显示到窗口、保存、模板匹配等
};
camera.ErrorOccurred += (msg) =>
{
Console.WriteLine($"[Error] {msg}");
};
if (camera.Connect("CAMERA_001"))
{
camera.StartCapture();
Console.WriteLine("按回车停止...");
Console.ReadLine();
}
}
}
}
|
| 坑 | 症状 | 根治 |
|---|
| 认不到相机 | Halcon 设备列表为空 | 关掉 MVS Filter Driver |
| 频繁掉帧 | 跑了就卡 | 网卡开巨型帧 + 大缓冲区 |
| 缓存堆积 | 越跑越慢 | 及时 Dispose + 分离取图/处理线程 |
| 引用找不到 | VS 编译报错 | 手动引用 dll + x64 平台 |
| 无错误处理 | 拔线就卡死 | 设 timeout + 心跳监测 |
用 GigE Vision 相机最忌讳不配网卡直接上。花 5 分钟调好巨型帧和接收缓冲区,后面省十几个小时的排查时间。
文中代码可以直接复制到 Visual Studio 2022 跑起来。
发布日期:2026-07-02
标签:#机器视觉 #Halcon #海康威视 #C# #GigEVision