当前位置: 首页 > 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.gsyq.cn/news/1480908.html

相关文章:

  • 成都GEO优化怎么做?2026本地GEO搜索优化与代运营落地指南 - 速递信息
  • 告别臃肿系统!Tiny11Builder助你打造轻量级Windows 11开发环境
  • 酷比魔方掌玩mini4Pro-vs-ibbot青春版:拼跑分的消费平板 vs 能印Token的生产节点
  • SharpKeys完整指南:3分钟掌握Windows键盘重映射的免费神器
  • 从零构建嵌入式Linux系统:S3C2440内核移植与YAFFS2根文件系统制作实战
  • CSDN AI看板权限体系升级背后:企业版新增的8类组织级统计维度,含部门效能看板、销售线索溯源、API调用量审计
  • 5分钟终极指南:用obs-backgroundremoval为OBS添加专业虚拟背景
  • 如何在Windows电脑上轻松安装安卓应用:终极免费APK安装器指南
  • 2026年丙烯酸聚氨酯面漆主流厂商综合实力排行:廊坊同升防腐设备有限公司 全场景适配的高端防腐服务商 - 奔跑123
  • Visual C++运行库一键修复指南:终极解决Windows软件无法启动问题
  • 打造极简美学博客:Argon主题完整安装与个性化配置终极指南
  • 2026 潮州漏水维修全攻略|苏易修缮:厨卫 / 阳台 / 外墙 / 屋顶 / 地下室|靠谱防水门店 - 苏易修缮
  • Anthropic推理中间件蒸发:零延迟架构与流式响应优化实战
  • 微软开源 VibeVoice:60 分钟音频一次搞定,语音 AI 的格局变了
  • Adobe Illustrator脚本神器:如何用智能工具集提升10倍设计效率
  • 从900MHz无绳电话拆解,掌握无线通信系统硬件与固件设计精髓
  • FPGA资源友好型Verilog指数计算模块(CORDIC定点实现)
  • GDA安卓逆向工具深度解析:从静态分析到动态调试的全链路安全解决方案
  • Cursor Pro破解工具:如何突破AI编程助手试用限制的终极指南
  • MATLAB调用ANSYS做机械臂轨迹跟踪闭环仿真,含MPC控制器与参数化结构建模
  • 2026 揭阳漏水维修全攻略|苏易修缮:厨卫 / 阳台 / 外墙 / 屋顶 / 地下室|靠谱防水门店 - 苏易修缮
  • AI提示词极限赛:从入门到精通的技术全景与实战指南
  • Citra 3DS模拟器:如何在PC上完美运行任天堂3DS游戏的终极指南
  • USBCopyer:3分钟配置,实现U盘文件智能同步的Windows神器
  • 【稀缺实操资料】CSDN AI企业账号多开备案模板(含加盖公章的《多账号运营声明书》范本+市场监管局咨询话术),仅限前200位技术负责人领取
  • Android多架构Speex回音消除so库编译工程(含ARM/ARM64/x86全平台支持)
  • 5分钟掌握视频字幕提取:本地化解决方案让你告别手动转录烦恼
  • 抖音下载器终极指南:三步实现批量下载与智能管理
  • 华为奋斗者协议:技术职场中的激励契约与工程师职业选择分析
  • Rust 错误处理从 if-else 到 thiserror:生产级错误链与错误转换