ARTICLE DETAIL

资讯详情

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

海康MV-CU工业相机C# Winform开发实战:SDK集成与图像采集全攻略

海康MV-CU工业相机C# Winform开发实战:SDK集成与图像采集全攻略 很多做上位机的朋友第一次拿到海康MV-CU系列相机时第一反应都是去找海康威视网络摄像头SDK结果发现根本对不上号。我最初也犯过这个错——MV-CU是海康机器人Hikrobot的USB3.0工业相机配套的是MVSMachine Vision Software和它的SDK跟安防监控那套完全是两个体系。这篇文章我就把在Winform里用C#驱动MV-CU系列相机的完整链路讲一遍包括环境准备、采集框架、参数控制、线程模型的坑以及我从现场调试里沉淀下来的几条经验。适合正在做视觉项目上位机开发、计划把工业相机集成到自己软件里的工程师参考也适合刚接触海康工业相机、被Demo代码绕晕的新手。1. 先弄清楚MV-CU到底是什么和安防摄像头的本质区别很多刚入手的人会混淆两个概念海康威视的安防摄像头就是装墙上那种和海康机器人的工业相机虽然都姓海康但技术体系完全不一样。MV-CU系列属于海康机器人旗下的USB3.0 Vision工业相机它的核心定位是给机器视觉、缺陷检测、尺寸测量这些场景做图像采集不是用来录像监控的。1.1 型号命名里的门道MV-CU系列的命名是有规律的搞懂了选型和使用都方便。比如MV-CU013-A0UMMV是机器视觉产品的统一前缀CU代表USB3.0接口013代表130万像素A0是产品代数最后两个字母里U代表USB接口M代表黑白Mono如果是C就是彩色Color。所以MV-CU013-A0UC就是130万像素的USB3.0彩色工业相机MV-CU060-A0UM就是600万像素的黑白相机。我见过有人拿彩色相机做OCR识别结果因为拜耳阵列转换姿势不对图像一直是花的排查了半天才发现是像素格式处理错了。1.2 和网络摄像头本质上的差异安防摄像头自带编码器、Web服务、RTSP流你用VLC就能拉流看画面SDK更像是在访问一个现成的视频服务器。而MV-CU系列本质是一个纯感光设备它通过USB3.0把原始的图像数据Bayer阵列或者Mono灰度以极高的帧率传给主机没有任何内置的编码和处理能力。这意味着三件事所有图像数据都得靠你写的程序主动去取相机不会自己推流。图像格式是生肉Mono8就是灰度BayerRG8就是RAW彩色必须自己转成能显示的Bitmap或者交给OpenCV处理。采集掉线、USB带宽不够、SDK调用顺序错误都会导致画面出问题而这些在安防摄像头生态里基本不会遇到。1.3 为什么项目里选USB3.0而不是GigEMV-CU系列走USB3.0MV-CE系列走千兆网选哪个不只看相机本身。USB3.0的优势是带宽高实际可用带宽大约350MB/s跑130万像素的全分辨率60帧绰绰有余。但USB线缆的有效长度通常限制在3米以内好一点的主动延长线能到5米再长就不稳定了相机供电也需要通过USB口或者额外电源。GigE的优势是线缆可以拉到100米适合相机装在生产线上、工控机在电柜里的布局。如果你的视觉工位就是一台工控机加一个相机距离不超过两三米选MV-CU系列是最省事的如果相机要分散装在流水线多个工位优先考虑GigE。这是我做项目选型时最优先判断的一条先量物理距离再谈带宽。2. 环境准备与SDK部署最容易翻车的几个细节MVS软件的安装本身没什么好说的一路下一步就行。真正的坑在后面的C#工程引用和运行环境上我在帮同事排查时发现十个报错里有八个是环境问题根本不是代码问题。2.1 MVS安装后到底该看哪些目录MVS默认安装之后开发相关的关键目录在安装路径下的Development文件夹里。展开之后你会看到Development\SDK\win6464位C/C开发使用的库和头文件。Development\SDK\win3232位C/C开发使用的库和头文件。Development\Samples官方示例工程C#示例一般在Samples\C#里。C#开发者真正要引用的是MvCameraControl.dll这是一个托管DLL位于Development\Samples\C#\对应的Demo工程输出目录中或者在SDK的win64\或win32\下的MvCameraControl.dll。你不需要去管C那套头文件和lib直接把托管DLL引用进Winform项目就行。2.2 平台目标x64还是x86必须跟DLL一致这条我放在最前面提醒因为BadImageFormatException这个异常几乎每个新手都会遇到。MVS的SDK同时提供64位和32位版本你的C#项目在Visual Studio里必须设置对应的平台目标项目平台目标引用的MvCameraControl.dll版本操作系统x6464位版64位系统x8632位版任何一个系统都能跑32位程序AnyCPU默认运行时会自动选但容易莫名报错不推荐使用我的经验是直接改成x64。现在的工控机清一色64位系统工业相机SDK和图像处理库也优先支持64位与其在AnyCPU上纠结不如一开始就锁死。如果你的项目里还引用了其他32位组件那就统一改x86关键原则是整个进程的位数必须一致混搭就会炸。2.3 项目目标框架别太激进也别太低MVS的托管SDK对.NET Framework版本要求不太苛刻.NET Framework 4.5以上基本都能跑。如果还在用VS2015项目默认目标框架如果是4.5/4.6没问题用VS2019或2022的话Winform项目建议选.NET Framework 4.7.2或4.8不要选.NET Core/.NET 5以上因为海康官方C#示例目前仍以.NET Framework为主照搬Demo最省心。2.4 先跑通官方Demo再动自己的代码环境是否正常最快的验证方法是去Development\Samples\C#里找到一个最简单的取流示例通常是MvDemo或者GrabImage之类的用VS打开把平台目标改对直接F5运行。如果官方Demo能出画面说明驱动、DLL、USB链路都没问题如果官方Demo都黑屏那问题在硬件或者驱动层面不要急着写自己的程序。我见过太多人跳过这一步花一下午调试自己代码最后发现是USB线是一个劣质延长线把相机识别成了USB2.0设备。3. 最小可用的采集框架从枚举设备到画面显示这一节我直接给你一套能跑起来的最小框架然后逐步解释每一步为什么是必需的。代码基于MvCamCtrl.NET命名空间新老版本的API大同小异核心流程几十行就能走完。3.1 核心调用顺序为什么不能乱工业相机的SDK调用是一个典型的状态机枚举设备 → 创建设备句柄 → 打开设备 → 注册图像回调 → 开始取流结束时反过来停止取流 → 关闭设备 → 销毁句柄。顺序错了会出现各种玄学报错。比如有人跳过MV_CC_CreateHandle直接MV_CC_OpenDeviceSDK返回的错误码让你完全摸不着头脑有人采集没停止就拔USB线再插回来的时候相机就枚举不到了必须重启进程。我建议把整个流程写进一个CameraService类里用状态字段约束操作避免UI上乱点把状态搞乱。3.2 枚举设备和打开相机的代码下面这段是枚举并打开第一台USB相机的代码using MvCamCtrl.NET; using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class CameraService { private MyCamera _camera new MyCamera(); public bool OpenFirstCamera() { // 1. 枚举设备MV_USB_DEVICE表示只枚举USB3.0设备 MyCamera.MV_CC_DEVICE_INFO_LIST deviceList new MyCamera.MV_CC_DEVICE_INFO_LIST(); int ret MyCamera.MV_CC_EnumDevices(ref deviceList, MyCamera.MV_USB_DEVICE); if (ret ! MyCamera.MV_OK || deviceList.nDeviceNum 0) { MessageBox.Show(未枚举到设备请检查USB线和相机供电); return false; } // 2. 取第一个设备的信息结构创建句柄 MyCamera.MV_CC_DEVICE_INFO deviceInfo new MyCamera.MV_CC_DEVICE_INFO(); Marshal.PtrToStructure(deviceList.pDeviceInfo[0], deviceInfo); ret _camera.MV_CC_CreateHandle(ref deviceInfo); if (ret ! MyCamera.MV_OK) { MessageBox.Show(创建句柄失败错误码: ret); return false; } // 3. 打开设备独占访问 ret _camera.MV_CC_OpenDevice(MyCamera.MV_ACCESS_Exclusive); if (ret ! MyCamera.MV_OK) { MessageBox.Show(打开设备失败错误码: ret); return false; } return true; } }这里有个要点MV_CC_EnumDevices的第二个参数MyCamera.MV_USB_DEVICE是过滤条件。如果你的机器同时装了GigE网卡驱动和USB驱动不过滤会把所有类型设备都列出来取pDeviceInfo[0]可能会取到别的设备。MV-CU系列只用USB过滤即可。3.3 注册回调并启动取流相机取流有两种方式回调方式和主动拉流方式。Winform里最顺手的是回调方式SDK在自己的工作线程里拿到一帧图像后通过委托抛给你的方法你不用自己去轮询等待数据。public bool StartGrabbing() { // 注册图像回调回调运行在SDK内部线程不能直接操作UI int ret _camera.MV_CC_RegisterImageCallBack(OnImageCallback, IntPtr.Zero); if (ret ! MyCamera.MV_OK) { MessageBox.Show(注册回调失败错误码: ret); return false; } // 开始取流 ret _camera.MV_CC_StartGrabbing(); if (ret ! MyCamera.MV_OK) { MessageBox.Show(启动取流失败错误码: ret); return false; } return true; } private void OnImageCallback(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX frameInfo, IntPtr pUser) { // pData 是指向一帧图像数据的指针 // frameInfo 里包含宽、高、像素格式、帧长度等信息 // 这里先把指针数据拷贝到托管数组再交给UI线程 }关于MV_FRAME_OUT_INFO_EX你重点关心这几个字段nWidth、nHeight是图像宽高enPixelType是像素格式nFrameLen是数据长度字节数。真正的图像数据在pData这个非托管指针里必须在回调里尽快拷贝走因为SDK内部可能复用这块内存。3.4 把图像数据变成可以在Winform里显示的Bitmap拿到了pData和frameInfo接下来就是显示。我直接把Mono8灰度数据转成Bitmap的完整代码贴出来这是工业相机项目里最常遇到的格式private void OnImageCallback(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX frameInfo, IntPtr pUser) { // 1. 先把非托管数据拷贝成托管字节数组 byte[] imageData new byte[frameInfo.nFrameLen]; Marshal.Copy(pData, imageData, 0, (int)frameInfo.nFrameLen); // 2. 异步投递到UI线程处理避免阻塞回调线程 _form.BeginInvoke(new Action(() { ShowMono8Image(imageData, frameInfo); })); } private void ShowMono8Image(byte[] data, MyCamera.MV_FRAME_OUT_INFO_EX info) { // 用LockBits直接操作内存比SetPixel快两个数量级 Bitmap bmp new Bitmap((int)info.nWidth, (int)info.nHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb); BitmapData bmpData bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); unsafe { byte* dst (byte*)bmpData.Scan0; for (int i 0; i data.Length; i) { dst[i * 3] data[i]; // B dst[i * 3 1] data[i]; // G dst[i * 3 2] data[i]; // R } } bmp.UnlockBits(bmpData); // 显示到PictureBox记得先释放上一帧否则内存会持续上涨 pictureBox1.Image?.Dispose(); pictureBox1.Image bmp; }如果你不想开unsafe也可以用Format8bppIndexed加灰度调色板的方式构造8位位图但8位位图在PictureBox里显示的时候有的显卡驱动会强制转成32位性能反而不好。我最常用的还是上面这种转24位的写法显示稳定后面接OpenCV做处理也方便。关于彩色相机BayerRG8等RAW格式我不会推荐你在回调里自己写拜耳插值。最简单可靠的做法是用SDK自带的像素转换功能PixelTypeConverter或者MV_CC_ConvertPixelType或者直接在MVS客户端里把相机像素格式设成RGB8这样拿到的数据三个字节一个像素省掉转换步骤。自己写Bayer插值看起来很有技术含量实际效果很难追上厂商调优过的算法而且浪费时间。3.5 停止采集和释放停止顺序跟启动相反标准写法如下public void StopAndClose() { _camera.MV_CC_StopGrabbing(); _camera.MV_CC_CloseDevice(); _camera.MV_CC_DestroyHandle(); }MV_CC_DestroyHandle这一步很多人会忘但它负责释放SDK内部和句柄关联的资源。程序退出时如果不销毁句柄有时候会导致相机在短时间内无法被其他进程打开或者设备枚举异常。4. 曝光、增益、触发这些参数到底怎么控制才不踩坑相机能出画面只是第一步视觉项目里真正花时间的其实是把图像调到能用的状态。曝光、增益、帧率、触发这些参数的设置直接影响成像质量而这些设置在SDK里有一套统一的套路。4.1 SDK参数操作的三板斧海康工业相机的所有可配置参数曝光、增益、白平衡、触发模式等都是以节点Node的形式组织的每个节点有名字和类型。SDK提供了一套按名字操作的接口像字典查询一样。常用的三个类型是参数类型设置方法读取方法典型应用枚举类MV_CC_SetEnumValue(节点名, value)MV_CC_GetEnumValueTriggerMode, PixelFormat浮点类MV_CC_SetFloatValue(节点名, value)MV_CC_GetFloatValueExposureTime, Gain, FrameRate整型类MV_CC_SetIntValue(节点名, value)MV_CC_GetIntValueWidth, Height, PayloadSize命令类MV_CC_SetCommandValue(节点名)不需要读取TriggerSoftware我用得最多的是浮点参数比如设置曝光和时间、帧率// 曝光时间单位是微秒us5000表示5毫秒 _camera.MV_CC_SetFloatValue(ExposureTime, 5000f); // 增益单位dB数值越大画面越亮但噪点越多 _camera.MV_CC_SetFloatValue(Gain, 10f); // 帧率单位fps _camera.MV_CC_SetFloatValue(FrameRate, 60f);读出当前值使用对应的Get接口。这里有个实战细节设置参数前最好先调用MV_CC_Get***Value把当前值读出来因为不同类型黑白/彩色、不同型号的相机支持的参数范围不一样超出范围虽然不会崩溃但SDK返回的就不是MV_OK了你拿着返回值做日志排查会容易很多。4.2 触发模式为什么拍照不是你想的那样如果是做一个简单的Demo连续采集就够了。但真实视觉项目里触发模式是绕不过去的流水线上产品高速运动你不可能靠连续取流碰运气去抓拍必须在产品到达指定位置的瞬间让相机拍一张。这时候就要用触发。海康工业相机的TriggerMode节点控制触发开关TriggerMode 0连续采集相机自己按帧率一直出图。TriggerMode 1触发模式只有收到触发信号才拍一帧。触发源由TriggerSource节点控制TriggerSource值含义使用场景0Software软触发程序里用SetCommandValue(TriggerSoftware)发一次触发命令1Line0硬触发外部传感器或PLC通过相机的Line0引脚输入信号2Line1同Line0用第二条线路软触发是很多上位机项目的最佳起步方案视觉软件收到某个业务流程指令比如机械臂到位就调用一次软触发命令相机立刻采集一帧。代码很简单// 开启触发模式 _camera.MV_CC_SetEnumValue(TriggerMode, 1); // 触发源设为软触发 _camera.MV_CC_SetEnumValue(TriggerSource, 0); // 需要拍照的时候执行这条命令 _camera.MV_CC_SetCommandValue(TriggerSoftware);自己用软触发做测试没问题但是要注意软触发的响应时间受到USB链路和SDK调度的延迟影响高速流水线上必须用硬触发才能保证每次拍照的产品位置精度一致。硬触发的配置建议先在MVS客户端里设置了试拍确认信号接对了、每一帧都能触发出来再搬到代码里固化。我遇到过一回在代码里折腾了半天TriggerSource最后发现是接在Line0上的传感器供电电压不对根本没信号。用MVS客户端验证硬件链路是最快的定位方式。4.3 图像保存BMP、JPG还是RAW调试阶段经常需要保存图像做分析SDK提供了MV_CC_SaveImageEx3方法可以在回调里直接保存。下面是在回调里保存BMP图的思路private void SaveImage(IntPtr pData, MyCamera.MV_FRAME_OUT_INFO_EX frameInfo, string filePath) { MyCamera.MV_SAVE_IMAGE_PARAM_EX saveParam new MyCamera.MV_SAVE_IMAGE_PARAM_EX(); saveParam.pData pData; saveParam.nDataLen frameInfo.nFrameLen; saveParam.enPixelType frameInfo.enPixelType; saveParam.nWidth frameInfo.nWidth; saveParam.nHeight frameInfo.nHeight; saveParam.enImageType MyCamera.MV_IMG_MV_IMG_BMP; // 或 MV_IMG_JPG saveParam.pFileName filePath; saveParam.nJpgQuality 90; _camera.MV_CC_SaveImageEx3(ref saveParam); }注意保存动作不能做太频繁JPG编码和磁盘写入在回调线程里会拖慢出图速度最好把保存操作扔到后台线程队列里去。调试阶段我的习惯是保存BMP因为无损、解码快、能保留原始数据细节确认算法稳定之后改成JPG或者直接不落盘。4.4 参数持久化别在代码里写死所有参数每个项目的曝光、增益、ROI窗口都不一样我不建议每次启动程序都用代码硬编码。MVS客户端里调好参数后可以导出相机参数配置文件后缀一般是.mvcfg或者通过SDK的MV_CC_FeatureSave实现。你的程序可以在打开相机之后用SDK把相机当前参数保存到本地文件下次启动时加载这样现场调参的人可以在MVS里改完直接让程序读取。这个参数跟代码解耦的思路能让程序在不同的工位复用省掉很多改代码重新编译的麻烦。5. 回调、线程与性能图像数据流动的真相这部分是Winform 相机开发最容易出看起来卡死其实没死一类问题的根源。我单独拿出来讲透。5.1 回调线程不是UI线程跨线程操作是最大的坑OnImageCallback是SDK内部的工作线程执行的而Winform的控件只能在UI线程更新。直接在回调里写pictureBox1.Image xxx轻则偶发InvalidOperationException重则界面直接崩溃或者卡死。正确做法我用的是BeginInvoke把图像处理任务投递到UI线程的消息队列里。注意判断一下IsDisposed和IsHandleCreated因为窗体关闭时回调仍然可能在路上直接BeginInvoke偶尔会抛ObjectDisposedExceptionif (_form.IsDisposed || !_form.IsHandleCreated) return; _form.BeginInvoke(new Action(() { ShowImage(imageData); }));5.2 回调里千万别做耗时操作图像回调里的代码是越快返回越好。你可以在回调里做的事只有拷贝数据、投递UI线程、可能加一个计数器。不要在回调里写文件、跑算法、打印日志这些都会阻塞SDK内部线程直接后果就是画面帧率骤降、出图间隔拉长。我看过有个项目开发人员在回调里直接调用了Thread.Sleep(500)来降帧率结果操作界面UITextBox也卡得不能自理。想降帧率直接用MV_CC_SetFloatValue(FrameRate, 15f)控制就行别在回调里Sleep。5.3 帧率上不去先分清瓶颈在相机还是在处理排查帧率问题我一般按这个顺序排查项判断方法常见原因USB带宽看MVS客户端里的帧率和带宽统计线缆太差、USB口是2.0速度曝光时间曝光越长帧率上限越低曝光10ms时帧率自然上不去回调处理耗时在回调入口加计数器回调里做了耗时操作UI刷新频率去掉BeginInvoke显示光统计回调次数界面刷新跟不上连带着主线程卡工业相机的帧率上限由1 / (曝光时间 传感器读出时间)决定曝光20ms时理论帧率最高50fps左右你如果想着60fps硬拉SDK设置失败返回错误码也就正常了。另外Windows对USB3.0的带宽调度也有影响如果Total bandwidth超过了400MB/s左右SDK可能会自动丢帧优先通过降低分辨率或降低帧率来解决。5.4 多相机并发句柄分离回调独立有读者问我能不能用一台工控机带多台MV-CU相机答案是可以但要记住每台相机一个MyCamera实例每个实例单独创建句柄、单独注册回调、单独StartGrabbing。SDK的调用不是线程不安全的但不同相机之间的状态互不影响。你唯一需要关心的是当相机数量增多USB3.0总线的总带宽要够用以及UI线程同时收到多路图像时的刷新压力。我曾在一台工控机带四台MV-CU013CPU占用大约20%左右UI刷新用双缓冲PictureBox加60fps上限整体很稳。5.5 内存管理Bitmap必须及时释放这个坑比较隐蔽。PictureBox的Image属性每次赋新图如果不先Dispose掉旧图GDI对象会一直堆积程序跑几个小时之后内存占用飙升。我见过有人程序跑一个晚上内存从200MB涨到3GB最后系统弹内存不足。建议在显示新图之前先释放旧图if (pictureBox1.Image ! null) { pictureBox1.Image.Dispose(); pictureBox1.Image null; } pictureBox1.Image bmp;从代码里看就是个一行的事但漏掉它的代价非常大。6. 常见故障与调试经验我从现场带回来的几条教训最后分享几个高频故障的排查思路这些经验是我在真实现场踩过的坑也是我每次做相机项目都会提前规避的地方。6.1 相机枚举不到先怀疑线材和供电如果你在MVS客户端都看不到相机代码层面再怎么扒都是徒劳。我遇到的概率排序是USB线质量差或太长、USB口是2.0速率、相机没通电MV-CU系列某些型号需要外接电源、驱动没装好。解决方案按顺序换一根一米五以内的好USB3.0线插到主板原生的USB3.0口别用机箱前面板安装最新MVS驱动再重新枚举。USB3.0的插头颜色通常是蓝色蓝色不代表速度就是3.0要看设备管理器里连接速度那一栏是否显示5Gbps。6.2 图像花屏或条纹九成是带宽或线材画面出现横向条纹、半截图像、颜色异常优先排查USB带宽。到MVS客户端的网络/传输属性里看当前带宽占用如果占用率超过90%还不稳定降低帧率或分辨率。线材质量问题也会导致传输误码花屏正规渠道购买的工业级USB3.0线缆跟普通消费级线的差距在高速传输时体现得特别明显这个钱不建议省。6.3 掉线后的重连策略工业现场USB链路偶尔会抽风相机掉了之后程序不能就傻等着。我的做法是在UI线程启用一个定时器周期性检测连接状态_timer.Interval 1000; _timer.Tick (s, e) { if (_camera null) return; bool connected _camera.MV_CC_IsDeviceConnected(); if (!connected) { StopAndClose(); // 重新执行第3节的打开流程 OpenFirstCamera(); StartGrabbing(); } };注意重连后要重新设置一遍之前配置的参数曝光、触发模式等因为相机断开重连后参数会恢复默认值这是一个很容易被忽略的细节。另外我在重连前会把UI上的状态指示改成相机连接中断现场人员能直观看到不然你偷偷重连成功还好重连失败用户看到黑屏还以为程序死了。6.4 中文路径和字符串编码SDK里涉及文件名的接口比如MV_CC_SaveImageEx3的pFileName不同版本SDK对字符串的编码处理有差异。为了稳妥保存路径我建议全部用英文避免中文路径在一些SDK版本里保存失败或者文件名乱码。如果一定要用中文目录提前在测试环境里验证该版本SDK是否支持别在现场才发现。6.5 发布打包别忘了运行时DLL和驱动的部署Winform程序发布到没有装MVS的目标机器上需要带上对应位数的MvCameraControl.dll并且目标机器必须安装USB3.0相机的驱动Windows系统通常自带UVC类驱动但海康工业相机建议安装MVS安装包里的驱动以保证完整功能。最简单省心的做法是在部署时勾选MVS运行时安装或者把驱动文件一并打进去。之前帮朋友部署过一个项目开发机一切正常拷到工控机上打开相机报无法加载DLL就是漏了这一步。6.6 界面显示小优化PictureBox显示高帧率图像时设置DoubleBuffered属性可以减少闪烁。如果嫌麻烦也可以在PictureBox上再覆盖一层自绘控件。实在追求性能的可以用Bitmap的LockBits配合Pointer直接绘制到控件表面绕开PictureBox内部封装。不过90%的应用场景双缓冲PictureBox已经足够别一开始就上复杂的自绘方案。说到最后我现在的开发习惯已经固定成了一个套路拿到新相机之后第一件事是刷一遍MVS客户端的取流Demo确认相机硬件、驱动、USB链路全部健康再开始写Winform集成代码。第二个习惯是把相机的启停流程封装成独立的CameraService类UI层只负责调接口回调内部绝不碰控件。第三个是项目里永远保留一个相机状态日志区域把每一步SDK调用的返回值都打出来现场定位问题的时候能省一半时间。这个套路帮我扛过了好几个交付节点紧张的视觉项目也希望能帮你少踩几个坑。
返回列表