十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

海康相机SDK二次开发实战:从取流到PLC通讯的全链路解析

海康相机SDK二次开发实战:从取流到PLC通讯的全链路解析 简介海康相机SDK二次开发资源包面向.NET开发者及物流自动化分拣场景围绕MvCodeReaderSDKNet模块实现一维码识别与XY坐标获取可支撑输送线上的条码定位与自动分拣控制。资源共41个文件以C#源码.cs、项目工程.sln/.csproj、配置文件.config及编译产物.exe/.dll为主另含调试缓存与界面资源文件压缩包约486KB结构紧凑便于直接开展二次开发。目前已有5569人学习参考。内含完整的Visual Studio解决方案及WinForm界面程序覆盖实时扫描、多码识别、条码坐标返回等核心接口调用并提供测试分拣系统的工程骨架与调试记录可帮助读者快速理解海康SDK的集成流程结合实际业务构建高效、精准的条码识别应用。 做机器视觉集成的人大概率都经历过这样一个阶段方案验证时用海康的VisionMaster拖着相机跑鼠标点几下就把流程调通了可一旦到了客户现场要把拍照功能嵌进自己的上位机或者产线软件里就发现所有事情都得靠代码重新写一遍。这时候海康相机SDK二次开发就成了绕不开的活儿。我最早接触海康的MVSMachine Vision Software是给一个3C检测项目做上位机集成当时的任务并不复杂相机拍一张图把图存下来给算法模块分析再把OK/NG信号发给PLC。就这么一个看起来轻轻松松的需求实际踩了版本匹配、回调机制、通讯协议选型好几个坑。这篇东西我就把这几年做海康相机SDK二次开发的思路、链路和折腾过程整理出来给准备入坑或者已经在坑里的朋友一个参考。1. 先搞清楚SDK二次开发到底是在解决什么问题很多刚接触工业相机的人会把SDK二次开发和用软件控制相机拍照划等号其实不完全准确。VisionMaster、MVS客户端这些工具已经帮你把拍照、参数配置、图像显示、甚至一些简单的图像处理都做好了你通过鼠标就能完成大部分操作。但工具做得再顺手它也替代不了以下这些场景拍照动作要嵌入到你的业务流程中比如扫码触发、工位到位才拍、与检测算法联动图像数据要直接进你的算法模块不走磁盘缓存或者在内存里做像素级处理结果要实时回传给PLC、数据库、MES系统控制产线动作需要定制界面、定制交互、定制数据格式这些需求恰恰是通用视觉软件做不到或者不好做的SDK二次开发的价值就在这里它把相机控制这个动作拆成一个个API让你在自己的代码里按需组合。海康的SDK体系里最核心的是MVS自带的SDK包里面包含了C、C、C#的接口。虽然官方也提供了像Python这类语言的封装但我实际做项目用的最多的还是C和C#C适合做底层算法集成的实时系统C#适合快速开发上位机交互界面。另外还有一套专门给深度学习或图像算法平台用的接口一般不太常碰。这里顺便回答一个很多人问过我的问题做SDK二次开发是否必须买海康的工业相机答案是不一定但同一套SDK生态内开发最省事。海康SDK主要面向自家相机但MVS也支持GigE Vision和USB3 Vision标准协议理论上可以枚举到符合这些标准的第三方相机。只是实际兼容性不好说我建议如果没有特殊原因就别拿SDK去兼容杂牌相机排查问题时会让你怀疑人生。2. 环境搭建里最容易栽跟头的三件事2.1 版本对应别让SDK和相机固件打架有朋友问过海康威视工业相机和视觉软件的版本号要对应吗这个问题很关键答案是不仅要对而且必须把SDK、MVS客户端、相机固件三者的关系理清楚。海康的MVS安装包实际上是客户端SDK驱动运行库合一的你在官网下载最新的MVS之后里面自带的SDK就是当前最新版本。这个SDK在绝大多数情况下向前兼容旧设备也就是说你用新SDK去连一台几年前的老相机通常问题不大。反过来如果你用一个很老的SDK去连新出的相机可能出现枚举不到设备、取流报错、甚至崩溃的情况原因就是新相机固件使用的协议字段和特征码老SDK不认识。我处理过最典型的例子一台用旧固件的面阵相机在最新MVS客户端里能正常出图但只要切到老版本的SDK开发就出现MV_CC_OpenDevice返回MV_E_NOT_FIND_DEVICE的错误码。最后排查下来是相机固件版本太老SDK里的设备属性握手和固件不匹配。解决方式就是在MVS的设备管理里升级相机固件升级完再跑代码就正常了。所以我的建议是开发之前先把MVS更新到当前稳定版本同时把相机固件也刷到匹配版本不要拖到调试的时候才暴露这类问题。2.2 32位和64位不匹配最常见的低级报错做C#上位机的朋友最容易踩的一个坑就是明明把MVS装好了代码看起来也没问题一运行就报BadImageFormatException或者直接提示找不到Dll。这个问题十有八九是平台位数没对上。MVS安装目录下的Development文件夹里会同时提供Win32和x64两套库。C#项目在Visual Studio里默认是AnyCPU如果你的系统是64位的AnyCPU在运行时会被JIT成64位进程这时候如果加载的是32位的Dll就会报错。反过来如果你强制指定x86又去加载x64的库同样报错。所以第一步一定是先把项目的平台目标显式设置成x64或x86和你要加载的SDK库保持一致。我一般统一用x64因为现在工业电脑基本都是64位系统而且x64下做图像内存缓冲区分配更从容。C项目则要注意把运行库的路径、包含目录、库目录都加到工程配置里并且是Debug/Release分开设置避免到时候切换编译配置又要重新填一遍。另外还要注意VC运行库。MVS的SDK运行时依赖Visual C Redistributable有些精简版系统没装Dll是找到了但加载时还是会失败。装一遍最新的VC运行库合集就能解决。2.3 MVS安装时的组件选择MVS安装包在安装过程中会让你选择组件那里有一个USB相机驱动和GigE相机驱动的选项默认是都勾上的。如果你只用网络相机装完USB的驱动也没啥影响但如果只勾了一个而你的相机接口恰好是另外一种就会出现设备管理器里能看到硬件MVS里就是搜不到的情况。安装完之后建议重启一次电脑让驱动和系统服务彻底生效。别小看这一步我遇到过不止一次装完MVS不重启GigE相机就是枚举不到重启后一切正常原因是相机Discovery服务没有在装完后立刻完成注册。3. 从枚举设备到拿到一帧图SDK核心调用链路拆解把环境搞定之后开发SDK的基本流程其实非常固定整个调用链路可以总结为初始化SDK环境枚举设备列表选择要操作的相机创建设备句柄打开设备设置相机参数曝光、增益、触发模式等注册取流回调或主动取流开始取流拿到图像数据后做处理停止取流、关闭句柄、反初始化下面我用C把这套流程的核心骨架写出来这部分对C#也是一样的逻辑只是P/Invoke调用的方式不同。3.1 设备枚举为什么你搜不到相机#include MvCameraControl.h #include iostream int main() { // 1. 初始化SDK MV_CC_Initialize(); // 2. 枚举设备 MV_CC_DEVICE_INFO_LIST deviceList; memset(deviceList, 0, sizeof(deviceList)); MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, deviceList); if (deviceList.nDeviceNum 0) { std::cout no device found std::endl; MV_CC_Finalize(); return -1; } // 3. 选择第一台设备并创建句柄 MV_CC_DEVICE_INFO* pDeviceInfo deviceList.pDeviceInfo[0]; void* handle nullptr; MV_CC_CreateHandle(handle, pDeviceInfo); // 4. 打开设备 MV_CC_OpenDevice(handle); // ... 设置参数、注册回调、取流 ... // 最后关闭句柄、反初始化SDK MV_CC_CloseDevice(handle); MV_CC_DestroyHandle(handle); MV_CC_Finalize(); return 0; }这段代码看着简单但枚举不到相机是我被问过最多的问题。GigE网络相机枚举不到90%的原因可以归结为这几种相机IP和电脑IP不在同一网段电脑防火墙拦截了SDK的广播发现报文多块网卡时SDK广播发到了错误的网卡IP的问题最好排查把相机和电脑都设成固定IP哪怕拿一根网线直连把相机IP改成192.168.1.x、电脑IP也改成192.168.1.x子网掩码255.255.255.0基本都能解决。防火墙需要去Windows防火墙设置里允许MVS相关的程序通过或者干脆在调试阶段把专用网络的防火墙关掉。多网卡的话可以在MVS客户端里指定一个网卡进行发现代码里也可以通过MV_CC_SetSDKLogPath或者传输层配置来指定最简单的办法是拔掉不用的网线。USB3相机枚举不到的情况就少一些但要确认USB口是3.0接口、线缆是带屏蔽的高质量线USB3延长线最好不要超过3米这个在高速传输下对信号质量影响明显。3.2 主动取流与回调取流怎么选打开设备之后有两个关键步骤设置参数和开始取流。参数设置走了海康的节点映射机制本质上就是往相机寄存器里写值曝光、增益、白平衡都在这一层配置。比如MV_CC_SetFloatValue(handle, ExposureTime, 5000.0f); // 设置曝光时间为5000us MV_CC_SetEnumValue(handle, TriggerMode, 0); // 0表示内触发自由运行海康的SDK还支持通过MV_CC_SetIntValue、MV_CC_SetEnumValue、MV_CC_SetBoolValue等接口设置不同类型的节点参数所有参数名都沿用GenICam标准命名所以如果你用过其它品牌的相机SDK上手海康会非常快。取流这块海康SDK提供了两种方式主动取流和回调取流。主动取流就是你在一个线程里反复调用MV_CC_GetImageBuffer去取最新的图像回调取流则是注册一个回调函数SDK内部有图像到达时自动调用你的回调函数。示例如下// 回调取流 void __stdcall ImageCallBack(unsigned char* pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser) { // 在这里处理图像数据 // pData指向图像缓冲区pFrameInfo里有宽高、像素格式等信息 } MV_CC_RegisterImageCallBack(handle, ImageCallBack, nullptr); MV_CC_StartGrabbing(handle);我的经验是如果图像到达后要立刻丢给算法模块做实时检测用回调取流最合适因为省去了自己轮询的时序问题如果只是做单张采集或者周期很慢的采集主动拉流更直观代码也好写。很多人不知道的是回调函数里面不要做耗时操作比如存图、深度学习推理这类应该把图像数据拷贝出来丢给工作线程慢慢处理。否则回调不及时返回SDK内部的取流缓冲会很快被占满然后就是丢帧、画面卡顿。3.3 像素格式转换Raw转RGB是绕不开的一步工业相机输出的原始数据通常是Bayer格式的Raw图也就是每个像素只有一种颜色分量需要通过插值算法去还原成RGB。海康SDK里已经封装好了转换函数MV_CC_ConvertPixelType使用方式也很简单MV_CC_PIXEL_CONVERT_PARAM convertParam; memset(convertParam, 0, sizeof(convertParam)); convertParam.nWidth frameInfo.nWidth; convertParam.nHeight frameInfo.nHeight; convertParam.pSrcData pData; convertParam.nSrcDataLen frameInfo.nFrameLen; convertParam.enSrcPixelType frameInfo.enPixelType; convertParam.enDstPixelType PixelType_Gvsp_RGB8_Packed; convertParam.pDstBuffer rgbBuffer; convertParam.nDstBufferSize rgbBufferSize; MV_CC_ConvertPixelType(handle, convertParam);这一步还有一个容易被忽略的点图像数据的行对齐问题。相机输出的每行字节数不一定是你想象中宽度x像素大小那么简单可能会为了内存对齐而填充额外的字节所以不能用裸指针直接当作BMP数据用。用SDK自带的转换和存图接口最保险如果你要自己处理记得使用MV_FRAME_OUT_INFO_EX里的nFrameLen和行有效字节数来做步长计算。保存图片也一样直接用MV_CC_SaveImageToFile接口传路径就能存成BMP或JPEG在回调里存图时要确认好保存文件名的唯一性避免多线程写同一个文件导致图片损坏。4. 取到图之后上位机通讯和PLC协同怎么写才不丢帧4.1 通讯协议选的不是最好的而是最匹配现场的取到图像是SDK开发的第一步但一个完整的视觉项目相机前面有传感器触发后面有PLC要接收结果。很多人做到这一步就开始纠结上位机和PLC之间用什么协议尤其像VisionMaster和C#上位机通讯网上搜出来各种答案都有反而更懵。先理清一个概念SDK二次开发解决的是相机的控制和图像数据获取这是设备内部的事而你的上位机和PLC、MES之间的通讯是另一个层面的事两者不能混为一谈。在做选择时要从现场部署和维护的角度去考虑而不是纯从技术角度找最强方案。我做过的项目里通常遵循这样一个原则数据交互类型推荐方式理由拍照触发对时序敏感硬件触发光耦IO或编码器脉冲硬触发延时是微秒级的软件触发受系统调度影响检测结果OK/NG和状态位输入输出点或Modbus TCP读写寄存器PLC侧梯形图直接访问寄存器简单可靠批量参数下发、配方切换Modbus TCP / 配置文件数据量小用标准协议方便维护图像数据上传、算法结果大块数据TCP Socket自定义协议Modbus不适合传大块数据帧格式灵活度不够这里我特别想强调硬触发很多刚入行的朋友喜欢用上位机发指令让相机拍照觉得这样逻辑统一。但在高速产线上全靠上位机发软触发的话相机曝光时刻和物体到位时刻的偏差很难控制导致拍到的位置总是不对。正确做法是用传感器信号直接接相机的Line0输入用IO硬触发相机拍照上位机只负责接收图像和处理结果。SDK里只需要把TriggerSource设成Line0TriggerMode设为On相机就会在外部信号到来时自动抓拍。4.2 一台信捷PLC做Modbus TCP服务器相机上位机作为客户端怎么配合我之前做过的一个项目就是信捷PLC作为Modbus TCP服务器相机上位机作为客户端整个链路是这样的传感器信号触发相机拍照 - 算法分析 - 上位机把结果比如0表示OK1表示NG写入PLC的保持寄存器 - PLC根据寄存器值控制气缸分拣。这个架构的好处在于你不需要在相机代码里去搞复杂的工业协议解析Modbus TCP的标准实现又简单又成熟PLC工程师对这种模式也门儿清以后客户要改动作逻辑不用找你改代码直接在PLC里改梯形图就行。用C#写Modbus客户端最简单的方式是直接用现有的开源库比如NModbus。核心逻辑大概长这样using Modbus.Device; // 连接PLC默认端口502 using (var client new TcpClient(192.168.0.10, 502)) { var master ModbusIpMaster.CreateIp(client); // 向PLC的保持寄存器地址100写入结果0OK1NG ushort result isOk ? (ushort)0 : (ushort)1; master.WriteSingleRegister(100, result); // 也可以批量写多个寄存器用于传测量值、坐标等数据 ushort[] values { result, xCoord, yCoord }; master.WriteMultipleRegisters(200, values); }这里有个容易踩的坑Modbus的寄存器地址映射关系需要和PLC程序里的地址表对齐。很多PLC的Modbus寄存器地址从0开始映射到内部的D寄存器中间差个偏移量比如PLC里的D100对应Modbus地址9999。你跟PLC工程师对接时一定要先确认寄存器映射表不要想当然地从100开始写。另外一个经验是上位机写寄存器的频率不要太高。有些初学者图方便在图像回调里直接去写PLC寄存器结果相机帧率一上来网络包把PLC的通讯口堵死反而导致PLC扫描周期变慢。我的做法是把结果先放到一个共享变量独立的通讯线程每20ms或者50ms去刷新一次PLC寄存器反正产线动作的响应时间远没有快到毫秒级这种刷新频率足够稳定。5. 从面阵到线扫从本地到RTSP二次开发还能往哪走把基础链路跑通之后就会发现海康相机SDK的能力远不止拍照存图这么简单。很多项目做着做着就会碰到一些进阶需求比如线扫相机、网络取流、ROS系统对接这里把我的看法分享一下。5.1 线扫相机调试与开发的几个注意点线扫相机在印刷检测、锂电材料表面检测这类场景中用得非常广它和面阵相机最大的区别是每次只拍一条线靠物体运动带动扫描成像所以行频必须和运动速度严格匹配否则图像会被拉长或压缩。线扫相机的SDK二次开发逻辑和面阵基本一致只是参数配置上有几个特殊点行频要和编码器脉冲频率匹配外部触发通常接编码器信号而且相机的工作模式要设置成LineStart触发。海康SDK里这些参数一样在节点映射里配TriggerMode设置为OnTriggerSource设置为LineX接编码器的那个口LineTriggerSource根据编码器信号类型设置为Encoder或Line0AcquisitionLineRate行频上限如果不用外部触发线扫相机的数据量远比面阵大像素格式通常是8位灰度或24位RGB一秒钟的数据量可能达到几百MB甚至更高这时候电脑的网卡、CPU处理能力、SDK缓冲区的配置都会成为瓶颈。实测下来千兆网口勉强够基础用如果要长期稳定跑高速线扫建议上万兆网卡或CameraLink采集卡否则丢帧丢到怀疑人生。5.2 海康相机的RTSP地址和ROS录制是怎么玩的海康的工业相机其实也支持RTSP协议也就是说你可以用VLC这类播放器直接打开相机的RTSP流做实时预览。相机的RTSP地址一般是rtsp://用户名:密码IP地址:554/Streaming/Channels/101这种格式具体看相机型号和固件版本在MVS客户端里或用户手册里能查到。RTSP这个功能做产品调试时确实方便不需要装任何额外软件就能看到画面。但要提醒一下如果你在正式的上位机软件里用RTSP去拿图像做测量那就有问题了——RTSP流的延迟、丢包重传机制、编码损耗都会影响图像质量。二次开发里拿图像的首选永远是通过SDK接口直接取原始图像数据RTSP顶多作为预览或者第三方平台集成的辅助通道。至于ROS录制就是另一个话题了。海康官方提供了ROS驱动包把相机作为ROS的图像话题发布出去然后在ROS环境里用rosbag record就能把图像数据录下来。这在机器人抓取、AGV视觉导航这类场景里非常实用因为你需要的是带时间戳的图像序列而不是单张图片。做这类开发时要特别注意时间戳同步相机的时间戳要和系统时钟对齐否则录出来的数据集没法用于后续算法训练和评估。5.3 MVS虚拟相机没有硬件也能先开发调试说一个可能很多人不知道的功能MVS自带的虚拟相机。这个虚拟相机可以模拟一台输出测试图像比如彩条、渐变图的GigE相机你用SDK枚举设备时它能被识别出来。我强烈建议在项目初期先用虚拟相机把代码流程跑通。这样可以摆脱硬件调试的排队问题尤其是当你在现场没有相机可用的前提下先把枚举、打开、取流、转格式、存图、通讯模块都写好等真机到了就只需要调参数。虚拟相机和真实相机在SDK接口层面完全一致唯一区别在于没有真实的物理触发所以外触发相关的逻辑还是要等真机到位后验证。6. 踩坑实录编译不过、枚举不到相机、回调中断这部分我专门整理了几个高频问题每个都是实际遇到过并且花了时间排查的。如果你在开发中也碰到类似的情况照着这个思路排查会省很多时间。6.1 程序一运行就进disassembly从崩溃回溯到调用约定有朋友问过程序进入为什么会进入disassembly里面怎么退出sdk这个问题看着玄乎本质很简单程序在回调或者某个Dll调用时发生了访问违规调试器停在了反汇编窗口而不是你的源代码行里。说到底就是内存访问越界或者调用了无效函数指针。在我给一个客户的C#项目接入海康SDK时就出现过这种现象。表现是程序一启动取流很快就卡住然后崩溃调试器跳进disassembly调用栈里能看到MvCameraControl.dll的函数。排查过程大概是这样的先看是不是Dll版本问题把MVS的库替换成和客户端一致的版本问题依旧再看是不是回调线程安全怀疑是C#的委托在回调返回后被GC回收了导致SDK下次回调时调用了一个空指针最终定位就是第二个原因C#在注册回调时如果直接用局部变量传递委托局部变量在方法结束后就可能被垃圾回收器回收等SDK的线程去调用这个委托时内存已经被释放了。解决方式是把委托对象保存成类的成员变量保证它的生命周期和取流过程一致并且在停止取流前不要释放。如果你用的是C同理要注意回调函数必须是全局函数或静态函数不能是某个对象的非静态成员函数否则this指针和调用约定都会出问题。6.2 一个进程里同时使用SDK和VisionMaster相机被占用还有一类问题是到底能不能同时用VisionMaster和SDK操作同一台相机。答案是你可以在一个系统里同时安装和打开这两个软件但同一时间只能有一个进程持有相机控制权。VisionMaster打开相机以后会独占设备你再拿SDK去枚举大概率会得到一个MV_E_RESOURCE或者MV_E_BUSY之类的错误码。如果确实需要在VisionMaster处理的同时让SDK去取流唯一的办法是开启相机的多路取流或者并发访问功能但这个功能不是所有型号都支持而且开启后两个进程拿到的帧率会共享带宽。实际项目中如果你需要集成通常的做法是VisionMaster负责完成视觉检测和结果显示SDK负责图像采集和原始数据旁路保存两者交替使用相机或者用一台相机专门采集、一台专门检测。遇到设备被占用时不要反复重连先把占用相机的进程退出等一两秒再重新枚举否则驱动层的状态可能卡住只能重启电脑。6.3 多网卡环境下GigE相机枚举不到工业电脑通常不只一块网卡有的接了现场交换机有的接了办公网这时候GigE相机经常枚举不到。原因是SDK默认的广播发现报文会走系统默认网卡如果相机接在非默认网卡上广播包就发不到相机所在的网段。排查方法很直接把相机的IP地址、子网掩码、网关配置好然后在MVS客户端的设备管理里把网卡选择改为相机所在的物理网卡。如果你是在代码里动态发现设备可以用MV_CC_EnumDevicesEx接口传入网卡IP或者子网掩码来做指定网段的枚举不要依赖默认网卡。如果你要在多网卡环境下长期稳定运行我还建议把相机的IP固定下来箭头不要设成DHCP否则IP一变所有配置和连接都会出问题。最后从一个老经验的角度说一句海康相机SDK的整个调用链路其实不复杂真正容易出问题的永远在细节——版本匹配、线程生命周期、通讯参数、现场网络环境。不管多急一定要先把最基础的枚举到设备、取到一帧图、存成一个文件这条链路跑通再往上叠加业务逻辑否则一上来就拼模块出了问题你根本分不清是相机配置的问题、取流的问题还是通讯的问题。先把地基夯实后面一路就顺了。本文还有配套的精品资源点击获取
返回列表