4. pylon SDK核心对象模型

pylon SDK 的 C++ 接口围绕一组职责清晰的核心类构建。理解这些类之间的生命周期依赖、所有权关系和异常传播路径,是写出稳定工业视觉程序的前提。本节从初始化与终止的边界条件出发,依次梳理设备枚举、相机控制、格式转换、抓取结果持有以及异常处理的对象模型。

PylonInitialize / PylonTerminate:进程级生命周期边界

pylon 的运行时环境由 PylonInitialize() 和 PylonTerminate() 成对管理。前者加载传输层(如 GigE、USB3 Vision)、初始化底层驱动与资源池;后者释放这些全局资源。二者必须在同一进程内严格配对,且 PylonTerminate() 之后不应再创建任何 pylon 对象。

典型用法是在 main() 入口调用 PylonInitialize(),在退出前调用 PylonTerminate()。若使用 RAII 风格,可借助 PylonAutoInitTerm 对象,在构造时自动初始化、析构时自动终止,避免异常路径下遗漏终止调用。需要注意:PylonTerminate() 会阻塞直到所有相机对象、抓取结果和转换器被释放,因此若仍有 CInstantCamera 实例存活,终止可能挂起或返回错误。

不要在 DLL 的 DllMain 中调用 PylonInitialize(),也不要在多个线程中重复初始化。pylon 的初始化是进程级、非线程安全的,重复调用虽可能返回成功,但终止次数必须与初始化次数匹配。

CDeviceInfo:设备发现与信息载体

CDeviceInfo 是设备枚举的结果对象,由 CTlFactory::EnumerateDevices() 填充。它不直接控制相机,而是承载设备的静态描述信息,常用属性包括:

  • GetFriendlyName():用户可读的设备名,如 "Basler acA1920-40gc";
  • GetDeviceClass():设备类别,如 "BaslerGigE"、"BaslerUsb";
  • GetSerialNumber():序列号,用于唯一标识;
  • GetUserDefinedName():用户自定义名称,便于多相机场景区分;
  • GetModelName()、GetInterfaceID():型号与接口标识。

在多相机系统中,通常先枚举所有 CDeviceInfo,再按序列号或用户自定义名筛选目标设备,最后通过 CInstantCamera::Attach() 或构造函数绑定。设备信息对象本身不持有硬件资源,可安全复制和缓存。

CInstantCamera:相机控制与抓取入口

CInstantCamera 是应用层最核心的类,封装了设备连接、参数配置、抓取循环和事件回调。它继承自 CPylonImageEventHandler 等基类,但日常使用主要关注以下接口:

  • Open() / Close():打开或关闭与设备的会话;
  • IsOpen():判断当前连接状态;
  • GetNodeMap():获取 GenICam 节点映射,用于读写曝光、增益、触发模式等参数;
  • StartGrabbing() / StopGrabbing():启动或停止抓取引擎;
  • RetrieveResult():从抓取队列取出一个结果,返回 CGrabResultPtr;
  • RegisterImageEventHandler():注册回调,实现事件驱动抓取。

抓取模式分为连续抓取(Continuous)和单帧抓取(OneByOne)。连续模式下相机持续采集,应用按需取帧;单帧模式下每次 RetrieveResult() 后需重新触发。选择哪种模式取决于触发源和实时性要求。

CImageFormatConverter:像素格式转换

相机输出的原始像素格式(如 BayerRG8、Mono12、YUV422)往往不能直接用于显示或算法处理。CImageFormatConverter 负责将抓取结果转换为目标格式,典型目标包括 BGR8、RGB8、Mono8。

使用流程为:创建转换器实例,调用 SetOutputPixelFormat() 指定目标格式,再调用 Convert() 将 CGrabResultPtr 转换为 CPylonImage。转换器可复用,不必每帧重建。对于 Bayer 格式,转换器会自动执行去马赛克;对于高位深格式,会按比例缩放到 8 位。若目标格式与源格式相同,转换器会直接拷贝,此时应避免不必要的转换开销。

CGrabResultPtr 与智能指针资源管理

CGrabResultPtr 是 CGrabResultData 的智能指针封装,采用引用计数管理底层缓冲区。当最后一个 CGrabResultPtr 析构时,缓冲区自动归还给 pylon 的抓取池。这一机制使得多线程传递抓取结果时无需手动加锁释放,但必须注意:

  • 不要长期持有 CGrabResultPtr,否则会耗尽抓取池导致 RetrieveResult() 超时;
  • 在回调中处理完图像后应及时释放引用,或将其转换为 CPylonImage 深拷贝;
  • CGrabResultPtr 可安全复制,复制操作仅增加引用计数,不拷贝像素数据。

pylon 的智能指针体系还包括 CPylonImage、CPylonImageWindow 等,均遵循相同的引用计数语义。理解这一点,就能避免“图像突然变黑”或“抓取卡死”这类由缓冲区生命周期引发的典型问题。

异常类层次结构

pylon 的异常体系以 GenericException 为基类,派生出的常用异常包括:

  • LogicalException:逻辑错误,如参数越界、状态非法;
  • RuntimeException:运行时错误,如设备断开、超时;
  • TimeoutException:RetrieveResult() 超时;
  • InvalidArgumentException:传入参数不合法;
  • AccessException:访问被拒绝,如设备被占用。

所有异常均提供 GetDescription() 和 GetErrorCode(),便于日志记录和错误分类。推荐在抓取循环外层捕获 GenericException,根据具体类型决定重试、重连或终止。例如,TimeoutException 通常可忽略并继续下一帧,而 RuntimeException 往往意味着设备已离线,需要重新枚举和连接。

pylon SDK 核心对象模型与生命周期 PylonInitialize() 加载传输层 / 初始化资源池 PylonTerminate() 释放全局资源 / 阻塞等待 进程生命周期边界(必须配对) CDeviceInfo 设备枚举结果 序列号 / 型号 / 接口 CInstantCamera 连接 / 配置 / 抓取 Open / StartGrabbing CGrabResultPtr 智能指针 / 引用计数 自动归还缓冲区 Attach RetrieveResult CImageFormatConverter 像素格式转换 BayerRG8 → BGR8 Convert() CPylonImage 转换后图像 可显示 / 可算法处理 输出 异常类层次结构 GenericException LogicalException RuntimeException TimeoutException AccessException InvalidArg... catch (GenericException &e) → GetDescription() / GetErrorCode()

对象协作与资源释放顺序

一个完整的抓取周期涉及多个对象的协作:PylonInitialize() 建立运行时环境后,CDeviceInfo 描述可用设备,CInstantCamera 绑定并打开设备,StartGrabbing() 启动采集,RetrieveResult() 返回 CGrabResultPtr,再经 CImageFormatConverter 转换为 CPylonImage 供后续处理。释放顺序与创建顺序相反:先停止抓取、关闭相机,再释放转换器和图像,最后调用 PylonTerminate()。

若在抓取循环中发生异常,应确保 StopGrabbing() 和 Close() 被调用。推荐使用 RAII 包装类或在 catch 块中显式清理。对于长时间运行的应用,还应定期检查 IsOpen() 和抓取统计信息,及时发现设备掉线或缓冲区泄漏。

调试时可在 PylonTerminate() 前打印所有存活对象的引用计数,若计数不为零,说明存在未释放的 CGrabResultPtr 或 CInstantCamera,这是定位资源泄漏最直接的手段。