2. 相机选型与驱动:面阵相机与线阵相机、分辨率与帧率、GigE Vision与USB3 Vision协议、SDK初始化与图像采集

相机选型,说白了就是给项目配眼睛。眼睛选错了,后面的算法再牛也白搭。我见过太多项目,算法调了三个月发现是相机分辨率不够,或者帧率跟不上产线速度。嗯,咱们今天就把这事聊透。

2.1 面阵相机 vs 线阵相机:怎么选?

先搞清楚一个核心问题:你的被测物体是静止的,还是在高速运动?

面阵相机,就像你手机摄像头,一次拍一张完整的矩形图像。适合拍小件、静止件、或者运动速度不快的场景。比如电子元器件检测、PCB焊点检查、药品泡罩包装检测。

线阵相机,每次只拍一条线,靠物体运动或相机扫描来拼成一张长图。适合拍大幅面、连续运动、圆柱体展开的场景。比如印刷品检测、纺织布匹、锂电池极片、钢管表面。

对比项 面阵相机 线阵相机
成像方式 一次曝光,整幅图像 逐行扫描,拼接成图
适用场景 静止或低速运动、小视场 高速连续运动、大幅面
分辨率 固定宽高比(如1920×1080) 行分辨率高(如8K、16K),长度不限
光源要求 面光源或环形光源 线光源,光照均匀性要求高
触发方式 软触发或外触发 必须配合编码器或外触发
成本 相对较低 相机+镜头+光源+编码器,整体较高
我的经验: 如果物体宽度超过300mm且产线速度大于0.5m/s,我建议优先考虑线阵。面阵在高速下容易产生运动模糊,补光又很难做到大视场均匀。我曾经在一个印刷检测项目里硬用面阵,结果补光花了三周才搞定,后来换成线阵两天就解决了。

2.2 分辨率与帧率:别被参数忽悠了

分辨率决定你能看清多小的缺陷,帧率决定你一秒能拍多少张。但这两个参数是互相制约的——分辨率越高,帧率通常越低。

怎么算?我一般按这个流程来:

  1. 先定精度:客户要求检测的最小缺陷是0.1mm?那你的像素精度至少得0.05mm/pixel。
  2. 再算分辨率:视场宽度100mm ÷ 0.05mm/pixel = 2000像素。所以至少选200万像素的相机。
  3. 最后算帧率:产线节拍是每秒5个产品,每个产品拍2张,那帧率至少10fps。留点余量,选15fps以上。
避坑指南: 别只看标称帧率。实际帧率受曝光时间、传输带宽、处理延迟影响很大。我曾经被一个标称60fps的相机坑过——实际跑到30fps就开始丢帧了。后来发现是曝光时间设太长,带宽也撑不住。

分辨率与帧率的常见搭配,我整理了一张表:

分辨率 像素数 典型帧率(GigE) 典型帧率(USB3) 适用场景
640×480 30万 100-200fps 200-300fps 高速定位、条码读取
1920×1080 200万 30-60fps 60-120fps 通用检测、尺寸测量
2448×2048 500万 15-30fps 30-60fps 精密测量、缺陷检测
4096×3000 1200万 8-15fps 15-30fps 大视场高精度检测

2.3 GigE Vision vs USB3 Vision:协议怎么选?

这两个协议是工业相机的主流接口。选哪个?我个人的习惯是:

  • GigE Vision:传输距离远(100米),抗干扰强,适合分布式部署。但CPU占用高,需要优化驱动。
  • USB3 Vision:带宽高(5Gbps),延迟低,CPU占用小。但传输距离短(3-5米),线缆贵。

你想想看,如果你的相机离工控机只有1米,那USB3是更好的选择。如果相机分布在产线各处,距离超过10米,那只能走GigE。

注意: GigE Vision的网卡和交换机必须支持巨帧(Jumbo Frame),否则实际带宽会打折扣。我遇到过客户用普通千兆网卡,结果带宽只有300Mbps,丢帧丢到怀疑人生。

协议对比表:

特性 GigE Vision USB3 Vision
带宽 1Gbps(实际约900Mbps) 5Gbps(实际约3.5Gbps)
传输距离 100米(Cat6) 3-5米(USB3.0线缆)
CPU占用 较高(需优化) 较低(硬件卸载)
线缆成本 低(网线) 高(专用线缆)
多相机同步 支持IEEE 1588 需外部触发
热插拔 支持 支持

2.4 SDK初始化与图像采集:从零开始跑通第一帧

协议选好了,相机买回来了,接下来就是写代码。我以最常见的GigE相机为例,用C++演示SDK初始化和图像采集的流程。

先看整体流程:

GigE相机SDK初始化与图像采集流程 枚举设备 创建句柄 打开设备 设置参数 开始采集 获取图像 停止采集 销毁句柄 初始化阶段 采集阶段 释放阶段

下面是一段完整的SDK初始化代码,我用的是HikRobot的MVS SDK,其他品牌(Basler、Daheng、AVT)流程大同小异:

#include <MvCameraControl.h>
#include <iostream>

int main() {
    // 1. 枚举设备
    MV_CC_DEVICE_INFO_LIST stDeviceList;
    memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
    int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE, &stDeviceList);
    if (nRet != MV_OK || stDeviceList.nDeviceNum == 0) {
        std::cerr << "未找到GigE相机" << std::endl;
        return -1;
    }

    // 2. 创建句柄
    void* handle = nullptr;
    nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[0]);
    if (nRet != MV_OK) {
        std::cerr << "创建句柄失败" << std::endl;
        return -1;
    }

    // 3. 打开设备
    nRet = MV_CC_OpenDevice(handle);
    if (nRet != MV_OK) {
        std::cerr << "打开设备失败" << std::endl;
        MV_CC_DestroyHandle(handle);
        return -1;
    }

    // 4. 设置参数:分辨率、帧率、曝光
    MV_CC_SetEnumValue(handle, "PixelFormat", PixelType_Gvsp_BayerRG8);
    MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", 30.0f);
    MV_CC_SetFloatValue(handle, "ExposureTime", 5000.0f); // 5000us

    // 5. 开始采集
    nRet = MV_CC_StartGrabbing(handle);
    if (nRet != MV_OK) {
        std::cerr << "开始采集失败" << std::endl;
        MV_CC_CloseDevice(handle);
        MV_CC_DestroyHandle(handle);
        return -1;
    }

    // 6. 获取一帧图像
    MV_FRAME_OUT stFrame = {0};
    nRet = MV_CC_GetImageBuffer(handle, &stFrame, 1000);
    if (nRet == MV_OK) {
        std::cout << "采集成功,图像大小: " << stFrame.stFrameInfo.nFrameLen << " bytes" << std::endl;
        // 处理图像...
        MV_CC_FreeImageBuffer(handle, &stFrame);
    } else {
        std::cerr << "采集超时或失败" << std::endl;
    }

    // 7. 停止采集
    MV_CC_StopGrabbing(handle);

    // 8. 关闭设备并销毁句柄
    MV_CC_CloseDevice(handle);
    MV_CC_DestroyHandle(handle);

    return 0;
}
我的习惯: 每次采集完一定要调用 MV_CC_FreeImageBuffer 释放缓冲区,否则内存会越涨越高。我见过有人忘了释放,跑了一晚上内存爆了,整个产线停摆。

2.5 图像采集的常见坑与优化

代码跑通了,但实际部署时还有一堆坑等着你。我列几个最常见的:

  • 丢帧:检查网卡是否开启巨帧,检查CPU是否过载,检查曝光时间是否太长。
  • 图像撕裂:线阵相机必须配合编码器,否则行频和运动速度不匹配。
  • 缓存溢出:采集线程和处理线程要分离,用环形缓冲区解耦。
  • 触发延迟:外触发信号要用差分信号(RS422),别用单端信号。
我曾经踩过的坑: 在一个锂电池检测项目里,相机一直丢帧,查了两天发现是网线质量太差,用了普通五类线。换成六类屏蔽线后,问题立刻解决。嗯,网线这东西真不能省。