当前位置: 首页 > news >正文

C#调用工业相机入门:USB/网口相机图像采集的基础实现

工业相机是机器视觉系统的核心组件,广泛应用于缺陷检测、尺寸测量、条码识别、产品定位等工业场景。对于C#工业上位机开发者来说,掌握工业相机的调用方法是必备技能。

很多新手在刚接触工业相机时,会被各种厂商SDK、不同接口类型搞得晕头转向。本文将从最基础的原理讲起,详细讲解USB和GigE网口这两种最主流工业相机的C#调用方法,所有代码均经过实际验证,可直接用于项目开发。

工业相机基础与选型

在开始写代码之前,我们先了解一下工业相机的基本分类和核心参数,这对后续的开发和选型非常重要。

主流接口类型对比

目前工业现场最常用的是USB3.0和GigE网口两种接口的相机,它们的优缺点和适用场景如下:

接口类型传输带宽最大传输距离供电方式易用性适用场景
USB3.05Gbps5米USB总线供电近距离、小体积设备、移动检测
GigE1Gbps100米(网线)单独供电/PoE远距离、多相机系统、工业现场

核心参数解读

  • 分辨率:决定图像的清晰度,常见的有130万、200万、500万像素
  • 帧率:每秒采集的图像数量,决定系统的检测速度
  • 快门类型:全局快门适合拍摄高速运动物体,卷帘快门成本更低
  • 像素深度:通常为8位(灰度)或24位(彩色)

整体开发流程

无论使用哪种接口的相机,其调用流程基本一致,都遵循"连接-配置-采集-显示-释放"的标准流程。

枚举相机设备

选择并连接相机

配置相机参数

设置采集模式

启动采集

接收图像数据

图像格式转换

显示/保存/处理

停止采集

释放资源

USB相机通用实现(DirectShow)

对于USB相机,最通用的方式是使用DirectShow接口。几乎所有的USB工业相机和普通摄像头都支持DirectShow,不需要安装任何厂商SDK,非常适合入门和快速开发。

环境搭建

我们使用AForge.NET库,它对DirectShow进行了很好的封装,使用起来非常简单。

Install-Package AForge.Video Install-Package AForge.Video.DirectShow

核心代码实现

publicclassUsbCamera:IDisposable{privateVideoCaptureDevice_videoDevice;privateBitmap_currentFrame;privatereadonlyobject_frameLock=newobject();// 枚举所有可用的USB相机publicstaticList<string>EnumerateCameras(){varcameras=newList<string>();varvideoDevices=newFilterInfoCollection(FilterCategory.VideoInputDevice);foreach(FilterInfodeviceinvideoDevices){cameras.Add(device.Name);}returncameras;}// 连接相机publicvoidConnect(intcameraIndex){varvideoDevices=newFilterInfoCollection(FilterCategory.VideoInputDevice);if(cameraIndex<0||cameraIndex>=videoDevices.Count)thrownewArgumentException("无效的相机索引");_videoDevice=newVideoCaptureDevice(videoDevices[cameraIndex].MonikerString);// 设置分辨率和帧率(根据相机支持的参数选择)_videoDevice.VideoResolution=_videoDevice.VideoCapabilities[0];// 注册新帧事件_videoDevice.NewFrame+=VideoDevice_NewFrame;}// 启动采集publicvoidStart(){if(_videoDevice==null||_videoDevice.IsRunning)return;_videoDevice.Start();}// 停止采集publicvoidStop(){if(_videoDevice==null||!_videoDevice.IsRunning)return;_videoDevice.SignalToStop();_videoDevice.WaitForStop();}// 获取当前帧publicBitmapGetCurrentFrame(){lock(_frameLock){return_currentFrame?.Clone()asBitmap;}}privatevoidVideoDevice_NewFrame(objectsender,NewFrameEventArgseventArgs){lock(_frameLock){_currentFrame?.Dispose();_currentFrame=(Bitmap)eventArgs.Frame.Clone();}}publicvoidDispose(){Stop();_videoDevice?.NewFrame-=VideoDevice_NewFrame;_currentFrame?.Dispose();}}

使用示例(WinForm)

privateUsbCamera_camera;privatevoidForm1_Load(objectsender,EventArgse){// 枚举相机并添加到下拉框varcameras=UsbCamera.EnumerateCameras();comboBoxCameras.Items.AddRange(cameras.ToArray());if(cameras.Count>0)comboBoxCameras.SelectedIndex=0;}privatevoidbtnStart_Click(objectsender,EventArgse){_camera=newUsbCamera();_camera.Connect(comboBoxCameras.SelectedIndex);_camera.Start();// 定时器刷新显示timerDisplay.Start();}privatevoidtimerDisplay_Tick(objectsender,EventArgse){varframe=_camera.GetCurrentFrame();if(frame!=null){pictureBox.Image?.Dispose();pictureBox.Image=frame;}}

常见问题

  1. 图像卡顿:不要在NewFrame事件中直接操作UI控件,应该使用定时器异步刷新
  2. 内存泄漏:一定要及时释放Bitmap对象,否则会导致内存快速增长
  3. 分辨率设置:必须选择相机支持的分辨率,否则会抛出异常

GigE网口相机实现(海康MVS SDK)

GigE网口相机是工业现场的主流选择,几乎所有的工业相机厂商都提供了自己的SDK。这里以国内最常用的海康威视MVS SDK为例,讲解网口相机的调用方法。

环境搭建

  1. 下载并安装海康威视MVS客户端软件
  2. 在项目中引用MVS SDK提供的MvCameraControl.Net.dll
  3. 将SDK目录下的所有dll文件复制到项目输出目录

核心代码实现

publicclassHikGigeCamera:IDisposable{privateIntPtr_handle=IntPtr.Zero;privateBitmap_currentFrame;privatereadonlyobject_frameLock=newobject();privatebool_isGrabbing=false;// 枚举所有可用的GigE相机publicstaticList<string>EnumerateCameras(){varcameras=newList<string>();vardeviceList=newMV_CC_DEVICE_INFO_LIST();varstatus=MvCameraControl.MV_CC_EnumDevices_NET(MvCameraControl.MV_GIGE_DEVICE,refdeviceList);if(status!=MvCameraControl.MV_OK)thrownewException($"枚举相机失败,错误码:{status}");for(inti=0;i<deviceList.nDeviceNum;i++){vardeviceInfo=(MV_CC_DEVICE_INFO)Marshal.PtrToStructure(deviceList.pDeviceInfo[i],typeof(MV_CC_DEVICE_INFO));varip=$"{deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp&0xFF}."+$"{(deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp>>8)&0xFF}."+$"{(deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp>>16)&0xFF}."+$"{(deviceInfo.SpecialInfo.stGigEInfo.nCurrentIp>>24)&0xFF}";cameras.Add($"{deviceInfo.SpecialInfo.stGigEInfo.chModelName}({ip})");}returncameras;}// 连接相机publicvoidConnect(intcameraIndex){vardeviceList=newMV_CC_DEVICE_INFO_LIST();MvCameraControl.MV_CC_EnumDevices_NET(MvCameraControl.MV_GIGE_DEVICE,refdeviceList);if(cameraIndex<0||cameraIndex>=deviceList.nDeviceNum)thrownewArgumentException("无效的相机索引");// 创建句柄varstatus=MvCameraControl.MV_CC_CreateDevice_NET(ref_handle,deviceList.pDeviceInfo[cameraIndex]);if(status!=MvCameraControl.MV_OK)thrownewException($"创建设备失败,错误码:{status}");// 打开设备status=MvCameraControl.MV_CC_OpenDevice_NET(_handle);if(status!=MvCameraControl.MV_OK)thrownewException($"打开设备失败,错误码:{status}");// 注册图像回调status=MvCameraControl.MV_CC_RegisterImageCallBack_NET(_handle,ImageCallBack,IntPtr.Zero);if(status!=MvCameraControl.MV_OK)thrownewException($"注册回调失败,错误码:{status}");}// 设置触发模式publicvoidSetTriggerMode(boolenable){varmode=enable?1:0;MvCameraControl.MV_CC_SetEnumValue_NET(_handle,"TriggerMode",(uint)mode);if(enable){// 设置触发源为软触发MvCameraControl.MV_CC_SetEnumValue_NET(_handle,"TriggerSource",7);}}// 软触发一次publicvoidTriggerOnce(){if(!_isGrabbing)return;MvCameraControl.MV_CC_TriggerSoftwareExecute_NET(_handle);}// 启动采集publicvoidStartGrabbing(){if(_handle==IntPtr.Zero||_isGrabbing)return;varstatus=MvCameraControl.MV_CC_StartGrabbing_NET(_handle);if(status==MvCameraControl.MV_OK)_isGrabbing=true;}// 停止采集publicvoidStopGrabbing(){if(_handle==IntPtr.Zero||!_isGrabbing)return;MvCameraControl.MV_CC_StopGrabbing_NET(_handle);_isGrabbing=false;}// 获取当前帧publicBitmapGetCurrentFrame(){lock(_frameLock){return_currentFrame?.Clone()asBitmap;}}privatevoidImageCallBack(IntPtrpData,refMV_FRAME_OUT_INFO_EXpFrameInfo,IntPtrpUser){lock(_frameLock){_currentFrame?.Dispose();// 将原始数据转换为Bitmapvardata=newbyte[pFrameInfo.nFrameLen];Marshal.Copy(pData,data,0,(int)pFrameInfo.nFrameLen);unsafe{fixed(byte*pDataPtr=data){_currentFrame=newBitmap((int)pFrameInfo.nWidth,(int)pFrameInfo.nHeight,(int)pFrameInfo.nWidth*3,System.Drawing.Imaging.PixelFormat.Format24bppRgb,(IntPtr)pDataPtr);}}}}publicvoidDispose(){StopGrabbing();if(_handle!=IntPtr.Zero){MvCameraControl.MV_CC_CloseDevice_NET(_handle);MvCameraControl.MV_CC_DestroyDevice_NET(_handle);_handle=IntPtr.Zero;}_currentFrame?.Dispose();}}

关键注意事项

  1. SDK版本匹配:必须使用与MVS客户端相同版本的SDK,否则会出现各种奇怪的问题
  2. 网络配置:GigE相机需要与工控机在同一网段,建议使用静态IP
  3. 巨帧设置:为了提高传输效率,建议在网卡属性中开启巨帧(Jumbo Frame)
  4. 多线程安全:图像回调是在SDK的线程中执行的,访问共享资源时必须加锁

图像显示与保存

WinForm显示

如前面的示例所示,使用PictureBox控件显示图像即可。需要注意的是,不要在UI线程中进行耗时的图像处理操作。

WPF显示

在WPF中显示Bitmap需要进行格式转换:

publicstaticBitmapSourceConvertToBitmapSource(Bitmapbitmap){varbitmapData=bitmap.LockBits(newRectangle(0,0,bitmap.Width,bitmap.Height),ImageLockMode.ReadOnly,bitmap.PixelFormat);varbitmapSource=BitmapSource.Create(bitmapData.Width,bitmapData.Height,bitmap.HorizontalResolution,bitmap.VerticalResolution,PixelFormats.Bgr24,null,bitmapData.Scan0,bitmapData.Stride*bitmapData.Height,bitmapData.Stride);bitmap.UnlockBits(bitmapData);returnbitmapSource;}

图像保存

publicvoidSaveImage(stringpath,Bitmapimage){image.Save(path,ImageFormat.Bmp);}

工业级稳定性优化

从demo到生产环境,还需要进行以下几个方面的优化:

  1. 多线程采集:将相机采集和图像处理放在单独的线程中,避免阻塞UI线程
  2. 异常处理:为所有相机操作添加try-catch块,记录详细的异常信息
  3. 自动重连:当相机连接断开时,系统能够自动尝试重连
  4. 内存管理:及时释放所有IDisposable对象,避免内存泄漏
  5. 心跳检测:定期检测相机的连接状态,及时发现设备故障

常见问题排查

  1. 相机连接不上

    • 检查相机电源和网线是否连接正常
    • 检查工控机和相机是否在同一网段
    • 关闭防火墙或添加例外
    • 重启相机和工控机
  2. 图像不显示

    • 检查是否调用了StartGrabbing方法
    • 检查图像回调是否正常触发
    • 检查图像格式是否正确
  3. 采集卡顿或丢帧

    • 开启网卡巨帧
    • 降低相机分辨率或帧率
    • 优化图像处理代码
    • 使用性能更好的工控机
  4. 内存泄漏

    • 确保所有Bitmap对象都被正确释放
    • 不要在循环中创建大量临时对象
    • 使用内存分析工具定位泄漏点

总结

本文详细讲解了USB和GigE网口两种主流工业相机的C#调用方法,从基础原理到核心代码,再到常见问题排查,覆盖了入门开发的所有环节。

需要注意的是,不同厂商的SDK虽然API不同,但调用流程基本一致。掌握了海康SDK的使用方法,再去学习其他厂商(如Basler、大华、大恒)的SDK就会非常容易。

在实际项目中,我们还需要结合具体的应用场景,进行相机参数优化、图像处理算法开发和系统稳定性测试,才能打造出真正可用的工业视觉系统。

http://www.jsqmd.com/news/969192/

相关文章:

  • 别再死记硬背了!用‘棋盘与米粒’和‘哈夫曼编码’的故事,5分钟搞懂二叉树为什么这么快(O(log n))
  • 2026年国内权威聚苯乙烯泡沫保温板厂家实力排行盘点 推荐欧诗德(天津)节能科技有限公司 - 奔跑123
  • 7种字重自由选择:为什么思源宋体是中文设计者的字体革命?
  • Android设备完整性验证:构建企业级安全防护体系
  • 2026无锡跑网约车赚钱秘诀!选滴滴直营正规租车,低门槛高收益 - 速递信息
  • 成都GEO优化怎么做?2026本地GEO搜索优化与代运营落地指南 - 速递信息
  • 告别臃肿系统!Tiny11Builder助你打造轻量级Windows 11开发环境
  • 深度解析CVE-2026-4372:Hugging Face Transformers供应链级RCE漏洞,AI模型安全的至暗时刻
  • 2026年|降AI率收藏!学长实测10款降AI率软件红黑榜:论文降AI避坑(含免费降低AI率办法)
  • 用几何和动画可视化理解Jain‘s Fairness Index:从二维正方形到N维超平面
  • 嵌入式C语言RMS实时计算模块,256点滑动平均可配,低内存高响应
  • 从零构建嵌入式Linux系统:S3C2440内核移植与YAFFS2根文件系统制作实战
  • 终极免费在线法线贴图生成器:5分钟让你的3D模型活起来!
  • SharpKeys完整指南:3分钟掌握Windows键盘重映射的免费神器
  • KeyboardChatterBlocker:终极免费解决方案,彻底告别机械键盘连击烦恼
  • 酷比魔方掌玩mini4Pro-vs-ibbot青春版:拼跑分的消费平板 vs 能印Token的生产节点
  • 甘肃想报考书法教育培训教师?手把手解答书法从业者最常见的七个问题及正规报考机构推荐 - 教育推荐官【官方】
  • 材料科学中的线性回归:物理驱动的变量转换与建模实践
  • 5分钟终极指南:用obs-backgroundremoval为OBS添加专业虚拟背景
  • 如何在Windows电脑上轻松安装安卓应用:终极免费APK安装器指南
  • CSDN AI看板权限体系升级背后:企业版新增的8类组织级统计维度,含部门效能看板、销售线索溯源、API调用量审计
  • 终极指南:3分钟掌握Godot游戏资源解包神器
  • 5分钟永久激活Windows和Office:KMS智能激活工具全攻略
  • 2026年丙烯酸聚氨酯面漆主流厂商综合实力排行:廊坊同升防腐设备有限公司 全场景适配的高端防腐服务商 - 奔跑123
  • 单片机USB 鼠标键盘实验
  • Visual C++运行库一键修复指南:终极解决Windows软件无法启动问题
  • 深入理解 RAG 检索增强架构:多路召回、重排序与 HyDE 策略的协同优化原理与实现
  • 基于STM32的智能自动抽水机:从传感器到电机驱动的嵌入式系统实践
  • 打造极简美学博客:Argon主题完整安装与个性化配置终极指南
  • 市面上有哪些是真正安全的降AIGC工具(告别论文AI标记风险)