3. 图像采集与接口

图像采集是整个视觉系统的起点。接口选不对,后面再好的算法也白搭。我见过不少项目,前期没仔细考虑接口,结果现场布线、带宽、稳定性全出问题,最后只能换相机重来。

这一节,咱们把工业相机最主流的两个接口协议掰开揉碎讲清楚。再聊聊SDK怎么调、触发怎么配、帧率怎么控。嗯,都是实战里绕不开的硬骨头。

3.1 GigE Vision vs USB3 Vision:到底选哪个?

先看一张对比图,心里有个底。

GigE Vision vs USB3 Vision 协议对比 GigE Vision • 传输距离:100m(Cat6) • 带宽:1 Gbps(理论125 MB/s) • 多相机:交换机级联,轻松扩展 • CPU负载:较高(需处理网络协议) • 线缆:成本低,通用网线 • 供电:PoE可选 • 抖动:较大(需精确时钟同步) 适合:远距离、多相机、大场景 如:整车检测、流水线长距离 USB3 Vision • 传输距离:3-5m(需中继器) • 带宽:5 Gbps(理论500 MB/s) • 多相机:需USB控制器,扩展有限 • CPU负载:较低(UVC/U3V驱动) • 线缆:专用USB3.0线,较贵 • 供电:总线供电(5V/900mA) • 抖动:低,适合高精度触发 适合:近距离、高带宽、低延迟 如:PCB检测、晶圆缺陷检测

说白了,选型就一句话:距离远、相机多、环境复杂 → GigE Vision;距离近、带宽高、响应快 → USB3 Vision。

我个人习惯,凡是超过10米的产线,直接上GigE。PoE供电一根线搞定,省心。但如果是高分辨率、高帧率的场景,比如5000万像素的晶圆检测,USB3 Vision的带宽优势就体现出来了。

⚠️ 我曾经踩过的坑: 有个项目用了USB3 Vision相机,现场布线距离8米。我图省事买了普通USB3延长线,结果图像频繁丢包。后来换成带信号中继器的有源延长线,问题才解决。记住:USB3超过5米必须用主动式延长方案。

3.2 相机SDK二次开发:Halcon vs OpenCV

接口协议定好了,接下来就是怎么把图像从相机里拽出来。这里有两个主流路线:Halcon 和 OpenCV。

3.2.1 Halcon 采集流程

Halcon的采集接口封装得比较完善。它通过HALCON Image Acquisition接口统一管理,底层自动适配GigE Vision、USB3 Vision、Camera Link等协议。你不需要关心具体的传输细节。

看个典型的采集代码:

// Halcon C++ 采集示例
HTuple hv_AcqHandle, hv_Image;
// 打开相机(自动枚举)
OpenFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "default",
                 -1, "default", -1, "false", "default", "cam1", 0, -1, &hv_AcqHandle);
// 循环采集
while (true) {
    GrabImageAsync(hv_AcqHandle, &hv_Image, -1);
    // 处理图像...
}
// 关闭
CloseFramegrabber(hv_AcqHandle);

嗯,代码很简洁。但要注意,GrabImageAsync 的最后一个参数是超时时间(毫秒)。我建议设成 -1 表示无限等待,否则在高帧率场景下容易超时报错。

💡 个人经验: Halcon的采集线程和图像处理线程最好分开。我曾经在一个线程里又采又处理,结果帧率直接掉了一半。用两个线程 + 队列缓冲,帧率稳稳的。

3.2.2 OpenCV 采集流程

OpenCV本身不直接支持工业相机协议。你需要通过相机厂商的SDK(如Basler的pylon、海康的MVS)先拿到图像数据,再转成OpenCV的Mat格式。

以Basler相机为例:

// OpenCV + Basler pylon 采集示例
PylonInitialize();
CInstantCamera camera(CTlFactory::GetInstance().CreateFirstDevice());
camera.Open();
// 设置触发模式
camera.TriggerSelector.SetValue(TriggerSelector_FrameStart);
camera.TriggerMode.SetValue(TriggerMode_On);
camera.TriggerSource.SetValue(TriggerSource_Line1);
camera.AcquisitionFrameRateEnable.SetValue(true);
camera.AcquisitionFrameRate.SetValue(30.0);

CGrabResultPtr ptrGrabResult;
camera.StartGrabbing();
while (camera.IsGrabbing()) {
    camera.RetrieveResult(5000, ptrGrabResult, TimeoutHandling_ThrowException);
    if (ptrGrabResult->GrabSucceeded()) {
        Mat img(ptrGrabResult->GetHeight(), ptrGrabResult->GetWidth(),
                CV_8UC3, (uint8_t*)ptrGrabResult->GetBuffer());
        // 处理图像...
    }
}
camera.StopGrabbing();
camera.Close();
PylonTerminate();

你看,OpenCV路线需要多一层转换。但好处是灵活,你可以直接调用OpenCV的算法库,不用额外买Halcon的license。

对比项 Halcon OpenCV + 厂商SDK
采集接口封装 统一接口,自动适配 需手动调用厂商SDK
图像格式转换 原生HObject,无需转换 需转Mat,有性能开销
算法库 商业级,闭源 开源,社区丰富
成本 高(按节点收费) 低(开源免费)
适合场景 高端、复杂检测 快速原型、中小项目
🔑 我的建议: 如果项目预算充足、对检测精度要求极高,直接上Halcon。如果只是做原型验证或者小批量产线,OpenCV + 厂商SDK完全够用。我有个项目用OpenCV做了PCB焊点检测,效果不比Halcon差,省了十几万license费。

3.3 触发模式与帧率控制

图像采回来了,但什么时候采?采多快?这两个问题直接决定检测系统的成败。

3.3.1 触发模式

工业相机一般支持三种触发模式:

  • 自由运行模式(Free Run):相机按内部时钟连续采集。适合传送带匀速、无间断的场景。
  • 软件触发(Software Trigger):通过API发送指令触发采集。适合需要精确控制采集时刻的场景。
  • 硬件触发(Hardware Trigger):通过外部信号(如光电传感器、编码器)触发。适合高速、高精度同步场景。

我个人最常用的是硬件触发。为什么呢?因为产线上产品位置是随机的,靠软件触发总有延迟。硬件触发直接接光电开关,产品一到,信号一来,相机立刻拍,延迟在微秒级。

⚠️ 注意: 硬件触发信号的电平标准和抖动范围一定要看相机手册。我遇到过用5V信号触发3.3V的相机,结果烧了IO口。还有一次触发信号抖动太大,导致图像位置偏移。后来加了施密特触发器整形,问题解决。

3.3.2 帧率控制

帧率不是越高越好。它受几个因素限制:

  1. 传感器读出时间:分辨率越高,读出越慢。
  2. 曝光时间:曝光越长,帧率越低。
  3. 传输带宽:GigE Vision理论125 MB/s,USB3 Vision理论500 MB/s。实际要打7折。
  4. 处理时间:算法处理完一帧才能处理下一帧。

帧率计算公式:

最大帧率 = 1 / (曝光时间 + 读出时间 + 传输时间)

实际帧率 = min(最大帧率, 处理帧率, 触发频率)

举个例子:一个500万像素的相机,曝光时间2ms,读出时间10ms,传输时间5ms。那么最大帧率 = 1 / (0.002 + 0.01 + 0.005) ≈ 58.8 fps。但如果你算法处理一帧要30ms,那实际帧率只有33 fps。

💡 避坑指南: 我曾经在一个项目中,相机标称90fps,但现场死活跑不到。查了半天,发现是GigE网卡的巨型帧(Jumbo Frame)没开。开启后帧率直接翻倍。另外,网卡中断调节也很关键,默认设置下CPU频繁中断,带宽利用率低。调成自适应或高吞吐模式,效果立竿见影。

3.4 实战选型建议

最后,给几个实战中的选型建议:

  • 远距离、多相机:GigE Vision + PoE交换机。布线简单,扩展方便。
  • 高分辨率、高帧率:USB3 Vision。带宽优势明显。
  • 高精度同步:硬件触发 + 编码器。确保每帧图像位置一致。
  • 快速原型:OpenCV + 厂商SDK。成本低,迭代快。
  • 量产项目:Halcon。稳定、可靠、技术支持好。

嗯,图像采集这块就聊到这儿。接口选对、SDK调好、触发配准、帧率控稳,你的视觉系统就成功了一半。剩下的,就是算法的事了。