ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Halcon工业相机开发全流程:从SDK配置到实时图像采集(C++版)

Halcon工业相机开发全流程:从SDK配置到实时图像采集(C++版) 刚把一个完整的工业视觉项目从零跑通时我最大的感受是Halcon本身并不难难的是把相机接进来、让图像稳定实时地流动起来。这个项目名为“Halcon工业相机开发全流程从SDK配置到实时图像采集C版”它的目标非常明确用C写一个带实时图像采集功能的桌面程序底层采集交给工业相机图像处理交给Halcon整个流程覆盖从SDK配置、相机参数初始化、实时取流到常见问题的排查。这篇文章就是我对这个项目的完整记录。做工业视觉这几年我见过太多人卡在第一步安装完Halcon代码里也引入了头文件但相机就是出不了图或者图像出来了跑一会儿就丢帧、掉线、内存暴涨。这些问题其实都不是算法问题而是采集链路上某个细节没做对。本文就把整个流程拆开讲适合正在入门Halcon与工业相机开发、或者已经写了些图像算法但没做过底层采集的工程师参考也适合准备做在线检测项目的朋友提前避坑。1. 项目背景与整体设计思路1.1 这个项目要解决什么问题工业场景里最常见的需求是“把产线上的产品图像实时拿回来交给视觉算法去判断好坏”。无论是做缺陷检测、尺寸测量、读码还是定位抓取第一步永远是先把图像稳定拿到。这个项目本质上就是搭建一个最通用的视觉采集基础框架。我接手这个项目时客户要求有三点第一相机必须支持硬触发因为产线上工件是运动的不能用软件随便一拍第二采集不能丢帧节拍要求是每秒能处理20张图第三程序要能长时间连续运行不能跑两小时就崩。这三点看起来简单实际做起来牵扯到SDK选型、触发配置、网络参数、内存管理等多个环节。1.2 为什么用C和Halcon做主力这个项目选择C主要原因是客户现场的检测程序要与老旧的MES系统对接同时在性能上要求稳定可控。C在内存管理和实时性方面有天然优势尤其是在高速采集场景下要用到回调函数、异步抓取、缓冲区复用等机制用C写更直接。Halcon的定位是机器视觉算法库不是相机SDK。它本身能通过GigE Vision、USB3 Vision等标准协议直接连接很多相机但实际项目中我更倾向于让Halcon负责图像处理把相机采集交给Halcon的Hframegrabber或者厂商SDK。这样做的理由很实际Halcon的处理算子和数据结构太强了做图像放大缩小、滤波、查找、测量都比OpenCV省心而相机厂商SDK在设备兼容性和异常处理上更细致。1.3 整体流程架构说明整个项目流程可以分成四层环境层Halcon运行库、Visual Studio开发环境、相机驱动与SDK。设备层工业相机、网卡/IP配置、触发信号接线。采集层打开设备、配置参数、实时取流、图像格式转换。应用层图像显示、保存、算法处理缺陷检测、测量、UI交互。后面每一节都会围绕这些层次展开重点放在采集层和应用层交界的地方因为那里坑最多。2. 开发环境准备从安装到工程配置2.1 Halcon安装版本与License激活Halcon的安装其实很简单官网下载安装包后一路Next就行。需要注意两点一是版本选择当前开发建议用20.11或22.11这类长期支持版本新版对GigE Vision和USB3相机的兼容性明显更好二是安装时记得勾选“CLI Libraries”和“C Libraries”组件否则后面在VS里会找不到头文件和导入库。License这块有几个常见情况需要明确。试用License只能跑30分钟而且处理图像会叠加水印不适合做开发调试商业License则有单机加密狗和网络浮动授权两种。开发阶段我习惯用本机License文件这样调试时不依赖网络授权服务器能少踩很多事。安装完成后先到安装目录下确认关键文件是否存在以我的安装路径为例C:\Program Files\MVTec\HALCON-22.11\ ├── include\halconcpp\HalconCpp.h ├── lib\x64-win64\halconcpp.lib └── bin\x64-win64\halcon.dll如果这些文件都在基本就说明核心组件装好了。给新手一个判断标准装完后先打开HDevelop运行一个简单的read_image读取图片只要能显示结果就表示License和核心库正常接下来再折腾C工程。2.2 Visual Studio工程配置的完整步骤我用的环境是Visual Studio 2019 Halcon 22.11这个组合在工业项目里非常常见。新建一个空的控制台项目后需要配置三个地方包含目录C/C - 常规 - 附加包含目录库目录链接器 - 常规 - 附加库目录附加依赖项链接器 - 输入 - 附加依赖项具体值可以参考下面这份配置配置项值包含目录C:\Program Files\MVTec\HALCON-22.11\include\halconcpp以及C:\Program Files\MVTec\HALCON-22.11\include库目录C:\Program Files\MVTec\HALCON-22.11\lib\x64-win64附加依赖项halconcpp.lib运行环境x64重点Halcon 22.11不再支持32位应用这里最关键的是平台一定要选x64。我有次在Debug模式下忘了切换解决方案平台默认用x86编译结果链接阶段报了一堆无法解析的外部符号排查了半天才发现是平台不匹配。2.3 DLL运行时路径的问题工程编译通过不代表运行正常。运行时会从Path环境变量里找halcon.dll找不到就报“无法启动此程序因为计算机中丢失halcon.dll”。解决办法有两种我推荐把Halcon的bin目录加到系统环境变量或者在Visual Studio里把bin\x64-win64复制到输出目录。在这份配置中还需注意Halcon运行时依赖VC运行库。如果目标机器是精简版系统建议提前装好Microsoft Visual C Redistributable特别是2015到2022合集版否则现场部署时程序可能莫名其妙起不来。环境这块还容易忽视另一个细节Halcon安装目录不要放在中文路径下不然偶发性的读取文件失败、License加载异常会让人抓狂。工业上位机程序的“玄学”问题有很大一部分就来自中文路径和权限差异。3. 相机接入SDK选型与初始化细节3.1 两条接入路线怎么选相机接入是项目中最核心、也最容易出问题的部分。做Halcon开发通常有两条路线路线优点缺点适用场景Halcon直接通过GigE Vision/USB3 Vision打开相机代码统一、上手快一套代码适配多品牌相机底层信号和丢帧控制的精细度不如厂商SDK快速验证、多品牌相机混合使用、算法预研相机厂商SDK如Basler pylon、海康MVS、大华SDK取流再传给Halcon设备侧的带宽、缓冲区、丢包统计、触发控制更精细需要维护两套API代码量更大长时间运行、高帧率检测、现场调试要求高的项目我的经验是如果产品还没定或者现场可能换相机品牌用Halcon直接打开相机做预研验证算法效果一旦进入正式交付阶段就转到厂商SDK因为能拿到更全的诊断信息。这个项目的最终版本用了Basler相机配合pylon SDK做采集Halcon只负责处理两者通过共享内存方式交互。3.2 GigE相机网络配置的几项硬指标工业相机里GigE千兆网相机占比最高因为它传输距离远、部署方便。但GigE相机的网络配置极其讲究很多“连不上相机”的故障都是这里出的。首先要确保相机和电脑在同一个网段。比如相机默认IP是192.168.1.10子网掩码255.255.255.0那电脑网卡IP就要设成192.168.1.x比如192.168.1.100。其次要关闭网卡的防火墙Windows防火墙会把相机的UDP广播包拦截掉表现出来就是相机厂商工具能看到相机但程序里OpenFramegrabber一直失败。另外建议在网卡高级设置里关闭“大量发送卸载”和“虚拟机队列”某些网卡开了这些功能会导致GigE丢包。还有一点经常被忽略GigE相机的最大传输带宽是1000Mbps如果网卡被其他通信占用或者网线质量差都会直接表现为图像花屏和丢帧。这里可以用厂商的SDK工具看一下实际带宽比如Basler的pylon Viewer里有网卡诊断信息能直观看到丢包重传率。3.3 Halcon方式打开相机的参数详解如果走Halcon直接打开相机HFramegrabber的OpenFramegrabber函数参数非常多我这里贴一段典型的GigE相机打开代码#include HalconCpp.h using namespace HalconCpp; HFramegrabber acq; acq.OpenFramegrabber( GigEVision2, // 接口类型新版Halcon用GigEVision2而不是GigEVision 0, 0, 0, 0, 0, 0, default, // 外部触发模式默认是同步内部触发 0, // 默认相机接口 Gray, // 像素类型Gray或RGB -1, // 默认端口 default, // 线缆类型 default, // 由相机决定 Camera1, // 相机名称可以是网卡设备ID或用户自定义名称 0, -1 // 宽度、高度用默认值 );这段代码里最容易踩坑的是最后的相机名称。直接用“Camera1”这种名字往往打不开正确做法是在HDevelop里先用info_framegrabber算子查询设备列表把查询到的完整设备名直接复制过来用。也可以填相机IP比如192.168.1.10在单相机场景下更稳。3.4 相机参数初始化曝光、增益、ROI相机打开后第一件事不是抓图而是先设置曝光、增益、目标区域这些参数。Halcon通过SetFramegrabberParam来设置参数名和相机厂商的SDK保持一致例如// 设置曝光时间单位为微秒 acq.SetFramegrabberParam(ExposureTime, 500.0); // 设置增益 acq.SetFramegrabberParam(Gain, 5.0); // 设置采集区域(ROI) acq.SetFramegrabberParam(Width, 1280); acq.SetFramegrabberParam(Height, 1024); // 设置像素格式不同相机叫法略有差异 acq.SetFramegrabberParam(PixelFormat, Mono8);关于曝光时间我需要补充一个换算逻辑如果产线运动速度是每秒100mm相机视野是50mm成员公差要求拖影不超过0.1mm那曝光时间就必须小于曝光允许值。计算公式是曝光时间等于允许拖影像素除以物体运动速度典型情况下200μm的曝光时间都偏长得开闪光灯补光。关于ROI我特别想说一点不要随意缩小ROI来“提高帧率”因为很多相机的ROI变化会导致重新配置传感器瞬间引起画面闪烁或丢帧。如果你只需要处理局部区域正确的做法是全幅采集后在Halcon里用crop_part裁剪而不是直接改相机参数。4. 实时图像采集的实现与优化4.1 单帧采集与连续采集的区别单帧采集用GrabImage它是一次性阻塞调用适合拍照后静态处理的场景。连续采集中间最常用的是GrabImageAsync它属于异步抓取——启动抓图后程序不阻塞图像准备好以后再来取。这两个函数的最关键区别是超时和缓冲区占用。GrabImage在相机未触发时可能一直阻塞界面直接卡死GrabImageAsync则可以设置超时时间配合循环实现实时刷新。我之前写过一个最简单的实时显示循环大概长这样HImage image; while (running) { // 第二个参数-1表示无限等待可以改成具体毫秒数 acq.GrabImageAsync(image, -1); HWindow window(0, 0, width, height); window.DispImage(image); }但这段代码在长时间运行时会发现内存不断上涨原因就是每一帧都产生新的HImage对象。正确的做法是复用同一个HImage变量让Halcon内部的图像数据被覆盖而不是重新分配。4.2 图像格式转换与Halcon数据的深层细节Halcon的HImage内部图像内存格式不是简单的灰度数组或RGB数组。从相机拿回来的可能是Mono8、Mono12、BayerRG8等格式需要统一转换到算法需要的格式。常用函数是ConvertImageType和ChangeFormat。尤其要注意12位像素格式。很多相机默认输出Mono12但Halcon里如果不做类型转换后续做阈值分割时灰阶范围和白平衡处理都会出问题。我在项目中会统一转换到byte类型避免算法层判断错误。通过GetImagePointer1可以拿到图像指针这在做Halcon和OpenCV数据交换时是核心接口。传参时要特别注意图像的Width和Height步长Halcon的图像行数据是按像素宽度对齐的直接当连续内存访问容易读到错的像素。4.3 用厂商SDK取流再转给Halcon的方式当换成Basler pylon SDK后采集部分变成了这样#include pylon/PylonIncludes.h using namespace Pylon; PylonInitialize(); CInstantCamera camera(CTlFactory::GetInstance().CreateFirstDevice()); camera.StartGrabbing(); CGrabResultPtr ptrGrabResult; while (camera.IsGrabbing()) { camera.RetrieveResult(500, ptrGrabResult, TimeoutHandling_Return); if (ptrGrabResult-GrabSucceeded()) { // 把pylon的buffer包装成Halcon图像 HImage image; image.GenImage1(byte, ptrGrabResult-GetWidth(), ptrGrabResult-GetHeight(), (Hlong)ptrGrabResult-GetBuffer()); // 后续交给Halcon处理 } } camera.StopGrabbing();这里唯一要注意的就是GetBuffer返回的指针生命周期。当ptrGrabResult被销毁时缓冲区会被pylon自动回收所以你必须在当前循环内完成Halcon图像生成和后续操作。如果真的需要跨帧保存图像用HImage的复制构造函数做深拷贝别只存指针。4.4 缓冲区数量和丢帧的关系以丢帧排查为例项目中客户点名提到“大华相机丢帧”这里展开说说。丢帧现象在实时采集里非常典型表象是程序跑一会儿画面像幻灯片一样卡顿或者显示的图像编号不连续。排查顺序一般是硬件到软件逐步缩小范围。先确认触发源。如果相机是硬触发模式丢帧往往不是相机问题而是触发信号本身有毛刺。用示波器看触发电平确认信号稳定后再查软件。如果相机是内部自由运行模式那丢帧大概率是采集线程处理不过来。以某大华相机为例其GigE接口底层用UDP传输数据包是有可能丢在网卡前面的。解决办法就是把相机端的包大小和网卡端MTU调成一致比如包大小默认1500字节网卡虚拟队列开启后常常发生丢包。我在现场调了一个类似问题时把网卡高级设置里的Receive Buffers从默认512调到2048同时把相机GevSCPSPacketSize调到8192配合jumbo frame丢帧率直接从百分之几降到零。软件侧的关键是缓冲区数量。Halcon或者厂商SDK默认分配的抓图缓冲区不够时采集端塞满后就会开始丢新帧。用pylon时这个参数是MaxNumBuffer我通常会根据帧率和处理时间来估算缓冲区数量 处理一帧耗时 / 相机帧间隔 2比如相机25帧每秒帧间隔40ms算法处理一帧要80ms那保险的缓冲区数量大概是80/4024个。低于这个数就明显丢帧。4.5 显示与处理的线程设计实时采集程序里采集、处理、显示一定不要在同一个线程里做。早期我犯过这个错把图像处理和显示放在一起结果界面卡死到鼠标都拖不动。推荐的线程模型是相机采集线程只负责抓图抓到图后丢到队列里处理线程从队列取出图像做算法显示线程单独更新窗口。队列加锁可以用C的std::mutex配合std::condition_variable为了防止队列积压处理不过来时可以做丢帧策略即只保留最新帧。这里有一个细节。GrabImageAsync的超时设置很关键不要设成-1无限等待。现场如果触发信号有一段时间不来无限等待会让软件看起来像死机。我一般设为1000ms超时就抛一次异常并计数超过几次就报警。5. 触发与信号实时采集中最容易翻车的地方5.1 软触发与硬触发在Halcon中的配置工业视觉有两种触发方式。软触发由软件发命令拍照速度受程序控制适用于静态工件硬触发依靠外部传感器的电信号拍照时机精确可控适用于运动产线。在Halcon中软触发可以通过设置TriggerMode为On、TriggerSource为Software然后调用SetFramegrabberParam(TriggerSoftware, 1)实现。硬触发则是把相机接入PLC或光电传感器的信号线Halcon侧依然是设置TriggerMode为On但TriggerSource设为线缆的输入引脚比如Line1。硬触发的一个重要坑是信号类型不匹配。有些相机需要的是下降沿触发而传感器输出的是上升沿这时虽然信号到了但相机就是不触发。Halcon或相机SDK里通常有TriggerActivation这个参数可以设RisingEdge或FallingEdge来匹配。5.2 海康相机未收到触发信号的排查路线热词里有个高频问题是“海康工业相机未收到触发信号”这里给一套完整的排查路线其他品牌相机也适用。第一步先确认相机当前到底接没接到信号。用厂商SDK工具海康MVS、Basler pylon Viewer打开相机把触发源设为Line1然后手动给线缆一个短接信号。如果工具里能看到图像说明硬件链路没问题问题出在软件配置。第二步检查触发线和地线是不是接反。不少现场用PLC输出的是漏型还是源型信号接线方式完全不同。海康相机的IO端口是光耦隔离的需要外部提供电压直接接PLC的NPN输出和PNP输出是不一样的接反就会出现“未收到信号”。第三步检查触发电平是否满足相机要求。很多工业相机要求触发高电平不低于12V或24V如果用树莓派或单片机3.3V去触发经常是时好时坏。第四步检查信号有没有干扰。如果触发线比较长可加上拉电阻或使用带屏蔽层的双绞线。我遇到过一次现场电磁干扰超严重触发信号抖动频繁结果间接导致了软件里出现“意外图片”。5.3 触发模式和取流超时的配合在硬触发模式下设备没触发时不会有图像数据。如果用GrabImageAsync去等图像建议把所有可能等待的情况都做超时控制。Halcon的OpenFramegrabber窗口里可以传超时也可以通过GrabImageAsync传入毫秒数。经验心得不要相信“默认参数够用”这句话。硬触发模式下我强烈建议设置超时保护否则产线暂停时程序里会堆起一堆异常重试逻辑排障时非常痛苦。另外硬触发时如果相机设置了AcquisitionFrameRateEnable有些相机会限制触发频率导致拍了很久但没拍全。排查时留意一下这个参数触发模式下通常建议关闭帧率限制或把帧率限制设得比实际触发频率高。6. 采集之后的延伸图像处理、显示与UI集成6.1 直接上Halcon做缺陷检测这套采集环境稳定之后下一步就是接Halcon本身的算法能力。热词里“Halcon缺陷检测”频率很高我简单说一下从采集到缺陷判断的最小闭环。假设要检测一个金属表面的划痕采集到图像后先用MeanImage做均值滤波降噪再用DynThreshold做动态阈值分割提取出疑似缺陷区域最后用Connection和Area等特征算子做筛选。示例代码大致如下HImage image; // 假设已经采集到 HImage smoothed image.MeanImage(9, 9); HRegion region smoothed.DynThreshold(image, 5, dark); HRegion connected region.Connection(); HTuple areas; connected.AreaCenter(areas, nullptr);这里面要理解一个关键点缺陷检测的鲁棒性不在哪个算子的参数调得多好而是采集端图像质量稳定。光照晃动、曝光漂移、对焦点偏移都会让检测误杀率飙升。所以做缺陷检测前我会反复确认相机曝光时间和光源亮度在24小时内都稳定这是图像处理能落地的前提。6.2 测量与工具封装热词里还有“Halcon测量”。Halcon的一维测量MeasurePos是非常好用的工具。比如测一个零件的宽度先创建测量对象然后循环取边缘点即可。HMeasure m; m.MeasureCreatePair(row, col, angle, len1, len2, width, height, 1.0, all, all); HTuple widthValue m.MeasurePos(image, 1.0, positive, first, all);这里的设计思路是把测量参数和图像处理代码解耦。同一张图像可以创建多个测量对象对应不同的尺寸维度算法层只需要配置一个结构体数组即可。6.3 与Qt集成时的高频问题热词里有“Qt怎么调用Halcon”这是C开发者的常见需求。Qt界面中间要嵌一个Halcon窗口通常用Halcon自带的QHalconWidget控件来做。但要注意Halcon提供的控件并非对Qt所有版本都适配良好新版Halcon对Qt5的支持比较成熟对Qt6需要查阅对应版本说明。如果不想依赖QHalconWidget另一种常见做法是把Halcon显示的图像转成QImage再画在QLabel上。转换核心逻辑是// 将Halcon的Image指针复制到QImage缓冲区 unsigned char* data (unsigned char*)image.GetImagePointer1(); QImage qimg(data, width, height, QImage::Format_Grayscale8);这种情况下要注意图像的像素格式必须是byte类型且QImage和Halcon图像的生命周期要保持一致。每次刷新的时候直接update()即可性能比我预想的要好很多。6.4 深度学习方向的扩展热词里的“Halcon深度学习工具”说明现在很多人已经想把深度学习用起来。Halcon的深度学习本质上是一套训练好的网络模型通过推理得到结果。采集到的图像可以直接喂给ApplyDlModel做推理实现语义分割、目标检测或分类。在使用深度学习做检测时我的一点建议是不要在采集端做太多图像增强因为训练模型时的图像分布已经固定了。如果在采集端加滤镜反而会导致推理结果变差。采集端唯一要保证的就是清晰度和亮度一致。这套架构跑通后还有一个收益就是后续换算法需求时不需要动采集底层只需要替换模型和输出解析代码对整个项目的可维护性提升非常大。这是我在设计之初就坚持把采集和处理解耦的原因。7. 项目落地中的几个重要复盘写到这里最后一节我想专门用来复盘一下因为这个项目里碰到的问题值得每个做工业视觉的人记住。第一稳定压倒一切。工业视觉项目不是算法大赛客户不会因为你用了更酷的模型就买单但会因为产线停机而投诉。所以设计时我优先保证采集不出问题再谈处理精度。第二日志是救命稻草。这个项目能快速定位丢帧和触发问题靠的就是在采集关键节点加了日志。每个重要动作都打上系统时间戳一旦出问题看日志就知道是卡在取流、卡在处理还是卡在显示。第三设备参数要有统一配置入口。相机曝光、触发、ROI这些参数如果散落在代码里换一台电脑或者换一个现场调参就要改代码。我做了个简单的XML配置文件启动时读进来现场调试只改配置文件效果立竿见影。第四多留一成性能余量。实际处理耗时和理论计算总会有偏差缓冲区数量、采集线程优先级、算法复杂度都建议留20%的余量。比如客户要求10帧每秒我会按12帧的性能设计这样即使现场光照变化导致算法耗时增加也不会立刻掉帧。最后再分享一个小技巧现场调试时带一个带网口的小型交换机。很多相机连不上电脑的问题用交换机把笔记本和相机单独连起来排除现场网络环境干扰后定位问题会快很多。这个习惯帮我省过不少时间。
返回列表