从外部触发到 sensor_msgs/msg/Image 的完整链路解析
在机器人视觉、多相机采集、双目测量、焊缝检测、工业质检这类系统中,硬触发 、PTP 时间同步 、ROS2 消息时间戳 ,几乎是绕不开的三个关键词。
它们都和"时间"有关,但很多项目在落地时,恰恰是因为没有把这三件事彻底分开理解,才导致后续出现各种看似玄学、实则必然的问题。
例如:
- 多台相机明明接了同一个外部触发源,结果时间还是对不上;
- 驱动里明明写了
header.stamp,但算法侧依然感觉时序不准; - 系统里看起来"同步采图"了,可一做多传感器融合、立体匹配、轨迹对齐,误差还是会冒出来;
- 现场出现偶发丢帧、过触发、延迟漂移时,日志里只有图像,没有任何时序诊断信息,根本无从定位。
这些问题表面上都指向"同步",但本质上,它们分属不同层次。
如果沿着整条链路来看,这个问题应该被拆成这样一条完整路径:
外部硬触发源 → 工业相机(海康 / Basler)→ Linux 厂家 SDK → ROS2 driver → 发布
sensor_msgs/msg/Image
而在这条链路中,需要分别回答三个问题:
- 硬触发解决的是什么?
- 时间戳同步解决的是什么?
- ROS2 driver 正确填写
header.stamp又解决的是什么?
先把结论放在最前面:
硬触发解决的是"什么时候开始曝光尽量一致";
时间戳同步解决的是"不同相机报出来的时间能不能放到同一时间轴上比较";
ROS2 driver 正确写header.stamp解决的是"下游拿到的是不是采集时刻,而不是到包时刻"。
这三件事互相补充,但谁也替代不了谁。
Basler 官方文档对于硬触发、PTP、Chunk 时间戳的定义非常清晰;海康公开手册中也能看到 Gev IEEE 1588、Timestamp Value、Exposure Start Active、Hard Trigger Active 等相关能力。[1]
一、真正需要先分清的,不是同步方案,而是"这里到底有几种时间"
在工业相机同步系统里,最容易造成混乱的,不是配置项,而是"时间 "这个概念本身。
因为在这条链路里,至少存在三套不同语义的时间。
1. 触发时间:外部信号真正到达相机输入脚的时刻
第一套时间,是外部触发沿真正到达相机输入脚的那个物理时刻。
比如你用 PLC、FPGA、IO 控制器或者主相机输出一根触发脉冲,把它接到目标相机的 Line1 或其他输入线。那么从物理意义上讲,触发时间就是这一下上升沿或下降沿到达输入引脚的时刻。
这个时间决定的是:相机什么时候开始响应这次采集动作。
Basler 文档中,Frame Start、Exposure Start、Exposure Active 都属于触发链的一部分。这里有一个非常重要但又经常被忽略的事实:
- 如果只启用了
Frame Start,相机会先进入"一帧采集流程开始"的状态; - 如果没有单独启用
Exposure Start,那么真正开始曝光,往往还要经过相机内部状态机的一段延迟; - 换句话说,触发到了,并不意味着曝光立刻开始了。
这也是很多系统里"明明同一时刻收到触发,结果图像实际时序还是差一点"的根本原因。[1]
2. 相机内部时间戳:设备自己记录这一帧发生在什么时候
第二套时间,是相机内部自己的 timestamp counter。
这套时间不是 Linux 主机给的,也不是驱动收到数据后临时打上的时间,而是工业相机自身维护的一套设备侧时间基。
GigE 工业相机通常都会有这样的计数器,只不过不同厂商在 API 和节点命名上的公开程度不同。
这时候就必须理解 PTP / IEEE 1588 的真实作用:
它不是拿来"触发拍照"的,而是拿来让多台相机各自内部的 timestamp counter 尽量同步到同一个时基上。
Basler 官方文档明确提到,启用 PTP 后,多台相机的 TimestampLatchValue 会尽量一致;海康公开手册也明确写到 Gev IEEE 1588 用于控制 timestamp register。[2]
也就是说,时间戳同步的本质不是"让相机同时动",而是:
让每台相机在记录时间时尽量使用同一块表。
3. Linux 主机时间:ROS2 最终最常接触的系统时间
第三套时间,是 Linux 主机自己的系统时钟。
例如:
CLOCK_REALTIMECLOCK_MONOTONIC
ROS2 中很多时间接口,最终都会落到主机时钟体系上。
但这里要注意,主机时间并不天然就和工业相机处在同一个时间域里。
Linux PTP 体系通常通过下面两个工具完成主机侧同步:
ptp4l:让网卡上的 PTP Hardware Clock(PHC) 跟 PTP 主时钟同步;phc2sys:再把 Linux 系统时钟同步到这个 PHC 上。
所以如果你的目标不仅仅是相机彼此同步,还希望相机时间、机器人控制时间、日志时间、其他传感器时间都落在同一条时间线上,那么 Linux 主机本身也需要纳入同步体系。[3]
图 1:整条系统链路示意
建立"全链路视角":触发、设备计时、主机接收、ROS2 发布,其实属于不同层级的问题。
外部硬触发源
PLC / FPGA / IO控制器
工业相机
海康 / Basler
相机内部状态机
FrameStart / ExposureStart
设备侧时间戳锁存
Chunk Timestamp / Timestamp Register
GigE Vision 传输
Linux 厂家 SDK
图像 + 元数据解析
ROS2 Driver
时间戳映射 / 组包
sensor_msgs/msg/Image
算法 / 融合 / 控制节点
说明:
- 左侧强调"动作从哪里来"
- 中间强调"真正的采集事件和设备时间戳在哪里产生"
- 右侧强调"ROS2 看到的只是链路后半段结果"
二、所谓"时间戳同步硬触发",本质上是两套机制叠加
工程中经常会听到一句话:"我们做了时间戳同步硬触发。"
这句话听起来像是一个功能,但实际上,它是两种机制叠加之后的结果:
- 一套机制负责动作一致
- 一套机制负责记时一致
这两者缺一不可。
1. 硬触发:解决"什么时候尽量同时开始动作"
所谓硬触发,就是你通过一个外部触发源,把同一根触发脉冲分发给多台相机的输入线。
这个触发源可以来自:
- PLC
- FPGA
- 专用 IO 板
- 主相机输出
- 分配器 / 扇出器
这样做的目的,是让多台相机在几乎相同的物理时刻收到触发脉冲 。
Basler 触发模型中,最常见的是:
TriggerSelector = FrameStartTriggerSelector = ExposureStart
海康公开手册中也能看到 Hard Trigger Active、Exposure Start Active 等事件源以及相应的 line output / strobe 配置能力。[1]
因此,硬触发本质上只解决了一件事:
让相机"尽量同时开始动作"。
但要特别强调,动作一致 ≠ 时间轴一致。
2. 时间戳同步:解决"不同设备按同一块表记时"
即使多台相机在几乎相同时刻收到了同一个触发脉冲,它们内部的 timestamp counter 仍然可能各走各的。
如果没有统一时基,那么你从两台相机里读到的"设备时间戳",即便看起来都很像采集时刻,也未必能直接横向比较。
这时候就需要 PTP / IEEE 1588 出场了。
它的核心作用,就是把多台设备内部的时间基尽量同步起来。这样,不同相机产生的时间戳,才真正具备"落在同一时间轴上"的条件。
Basler 文档明确说明,PTP 用于同步多个 GigE 相机;海康手册也建议在多相机精确执行 action command 时优先开启 Gev IEEE 1588。[2]
所以最准确的一句话总结应该是:
硬触发保证动作尽量一致,PTP 保证记时尽量一致。
图 2:硬触发与时间戳同步的职责分工
把"动作同步"和"时间同步"这两个概念一把拉开。
外部硬触发
多台相机几乎同时收到触发
动作尽量一致
PTP / IEEE 1588
多台相机内部 timestamp counter 对齐
记时尽量一致
同步采集系统
说明:
- 上半部分对应"物理动作"
- 下半部分对应"时间基一致性"
- 最下方合流,表达这两件事必须叠加
三、一帧图像从外部触发到 ROS2 发布,到底经历了什么
如果只从"相机拍了一张图"这个表象来看,整条链路好像很简单。
但从严格时序角度看,一帧图像的产生实际上要经过多个阶段,而且每个阶段的"时间"语义都不同。
下面按时间顺序拆开。
1. 外部触发沿到达相机输入线
假设你把 Line1 配成 trigger source。
当外部上升沿或下降沿进入相机后,它并不会立刻直达曝光模块,而通常会先经过:
- 输入电路响应
- 电平阈值判断
- 可选的去抖(debouncer)
- 触发状态机
Basler 官方曾将启动延迟拆成以下几部分:
Exposure Start DelayInput Line Response TimeLine Debouncer SettingFrame Start Trigger Delay
并举过一个示例:18 μs + 1.5 μs + 5 μs + 200 μs,总计约 224.5 μs。[4]
这个数值不代表所有型号都一样,但它非常清楚地说明了一件事:
硬触发绝不是"零延迟"系统。
也就是说,真正的工程误差分析里,你必须考虑:
- 输入响应时间
- 阈值 crossing
- 去抖延迟
- I/O jitter
- 状态机延迟
这些因素都会决定"同一根触发线进来之后,相机到底什么时候真正开始进入采集流程"。
2. 相机内部决定"采集开始"和"曝光开始"
这一层非常容易被混淆,因为很多人默认把"这一帧开始"与"曝光开始"当成同一个事件。
事实上,它们往往不是。
如果只启用了 Frame Start,那表示的是:这一帧采集流程开始 。
但"采集流程开始"并不等于"真正开始积光"。
如果没有单独启用 Exposure Start,相机会在内部状态满足条件后自动开始曝光,这一过程中可能会出现官方文档提到的 exposure start delay。
如果你希望曝光起止本身也严格由外部信号控制,那么通常需要使用:
Exposure StartExposure EndExposure Active
并把曝光模式切到 TriggerControlled。
如果使用 TriggerWidth,那么曝光时长则由外部脉宽决定。[1]
因此这一步最重要的认知是:
Frame Start 关注的是"这一帧流程何时开始",Exposure Start 关注的是"真正积光何时开始"。
这两者语义不同,后面所有时间戳解释都要基于这个前提。
3. 相机在某个事件点锁存 timestamp
这一环,才是真正决定"你最终读到的时间戳到底表示什么"的关键。
Basler 的 chunk timestamp 支持按不同语义选择:
FrameStartExposureStartExposureEnd[5]
这意味着,在谈同步之前,必须先回答一个问题:
要对齐的到底是哪一个事件点?
如果选 FrameStart,那么你对齐的是"这一帧进入采集流程的时间"。
如果选 ExposureStart,那么你对齐的是"真正开始曝光积光的时间"。
如果选 ExposureEnd,那更适合分析曝光结束、读出、后续传输链路的行为。
很多项目最后出现"明明做了同步却还是差一点"的情况,根本原因并不是同步机制没打开,而是:
不同模块理解的时间戳语义压根不是同一个。
4. 图像和元数据通过 GigE Vision 发送到主机
从图像开始通过网线传输的那一刻起,它就不再处于"采集阶段",而是进入了"传输阶段"。
这时,主机侧的到达时间会受到很多因素影响,例如:
- 网卡调度
- 交换机转发
- 网络包大小
- 带宽占用
- 重传
- 主机内核调度
- SDK 接收线程调度
这些因素会影响的是:
什么时候收到图像
但它们不会改变的是:
相机在采集时已经锁存好的设备侧时间戳
海康公开手册中可以看到 GEV Timestamp Tick Frequency、Timestamp Value、Gev SCPD 等参数;Basler 文档则说明 data chunks 会附加在图像后面一起发往主机侧。[6]
这一步非常值得单独强调,因为很多系统里最常犯的错误,就是把"数据到达主机的时间"误当成"图像采集的时间"。
5. 厂家 SDK 解析图像与元数据
图像到达 Linux 主机后,厂家 SDK 会把数据流解析成:
- image buffer
- chunk data
- frame metadata
Basler 文档中明确写到,Data Chunks 可以把附加信息跟随每幅图像一起送达主机,典型字段包括:
- timestamp
- frame counter
- CRC
更具体的 API 字段包括:
BslChunkTimestampValueChunkFrameIDChunkPayloadCRC16ChunkFrameTriggerCounterIgnoredCounter[6][9]
这些字段对 ROS2 driver 的价值非常大,因为它们不仅能帮助你"把图发出去",还能帮助你判断:
- 这一帧有没有丢
- 触发有没有被忽略
- 数据有没有损坏
- 时间序列有没有漂移
- 相机有没有发生过过触发
也就是说,从工程诊断角度看,SDK 给 ROS2 driver 的不应该只是像素数据,还应该是一整套跟这幅图绑定的时序信息。
6. ROS2 driver 组包并发布 sensor_msgs/msg/Image
到了 ROS2 driver 这一层,问题终于落到一个非常具体的接口上:
sensor_msgs/msg/Image.header.stamp应该怎么写?
ROS 官方文档对 sensor_msgs/msg/Image 的定义写得非常明确:
header.stamp should be acquisition time of image[7]
也就是说,header.stamp 应该尽量表示:
图像采集时刻
而不是:
- 驱动调用
publish()的时刻 - SDK 回调进入用户代码的时刻
- 图像进入缓存队列的时刻
- 图像到达主机用户态的时刻
因此,如果设备已经提供了 chunk timestamp,那么驱动层最不应该做的事情,就是简单粗暴地:
cpp
msg.header.stamp = node->now();
now() 可以作为兜底,但绝不应成为理想方案。
图 3:一帧图像的时间链路示意
完整链路解析示意图,建立时序层级感。
下游算法节点 ROS2 Driver Linux SDK 网络/GigE Vision 工业相机 外部触发源 下游算法节点 ROS2 Driver Linux SDK 网络/GigE Vision 工业相机 外部触发源 触发沿到达输入线 输入响应 / 去抖 / 状态机 FrameStart / ExposureStart 锁存设备时间戳 发送图像 + Chunk 元数据 数据到达主机 SDK 回调 / GrabResult 设备时间 ->> ROS 时间映射 发布 sensor_msgs/msg/Image
说明:
- 这张图最重要的作用,是把"采集时刻"和"到达时刻"彻底拉开
- 图像采集发生在相机内部,而 ROS2 发布发生在链路后端,两者不能混为一谈
四、为什么"只做硬触发"通常还不够
这是多相机系统中最常见的误区之一。
很多初级工程师会想:
"我已经把多台相机都接到同一个外部触发源了,那不就同步了吗?"
答案是:动作可能更接近同步了,但时间轴未必同步。
原因很简单。
即使两台相机在几乎相同的物理时刻收到了同一个触发脉冲,它们各自内部的 timestamp counter 仍然可能不在同一个时基上。
这时候,从 SDK 中分别读到的"设备时间戳",虽然都长得像采集时刻,但它们未必可以直接横向比较。
Basler 文档明确指出,PTP 的作用就是让多台相机的 timestamp 值尽量一致。[2]
所以这三种情况要严格区分:
只做硬触发,不做 PTP
结果是:
- 曝光动作可能很接近同步;
- 但设备时间戳可能各说各话。
只做 PTP,不做硬触发
结果是:
- 时间戳可能已经处在同一时间轴上;
- 但曝光动作并不一定在同一个物理时刻发生。
硬触发 + PTP 一起做
结果是:
- 动作尽量一致;
- 记时也尽量一致。
这才是工程上真正严谨的"时间戳同步硬触发"。
五、Linux 主机在这套体系里到底扮演什么角色
如果目标只是让多台相机彼此之间时序更一致,那么仅依赖相机侧 PTP + chunk timestamp,往往就已经很有价值。
但在实际机器人系统里,很多时候不只是在处理相机之间的关系,而是想把整套系统都纳入同一条时间轴中。
例如:
- 机器人控制器
- PLC
- 激光雷达
- IMU
- 日志系统
- 其他 ROS2 传感器节点
这时候,Linux 主机自身的时间就不能被排除在外。[3]
Linux PTP 体系通常按下面的方式工作:
ptp4l负责让网卡上的 PHC 跟 PTP 主时钟同步;phc2sys再把 Linux 系统时钟同步到这个 PHC;- 最终让主机时间尽量贴近设备所处的 PTP 时间域。[3]
因此,从 ROS2 driver 角度看,常见会有两种方案。
方案一:把设备时间映射成 ROS 时间
这是工程里最常见、最实用的方案。
做法是周期性记录:
- 设备时间戳
- 主机当前时间
建立两者之间的映射关系,求得 offset,或者进一步做线性拟合。
最终,驱动在发布图像时,不直接使用 now(),而是把设备时间戳映射为 rclcpp::Time。
这样做的好处是,即使整个主机系统没有完全成为 PTP 域中的"强同步成员",你也能让 header.stamp 比单纯的主机到包时间更接近真实采集时间。
方案二:让主机自己也加入同一个 PTP 时间域
如果网卡、交换机、链路支持得较好,那么可以进一步让 Linux 主机自身也尽量加入同一个 PTP 域。
这样一来,相机时间与主机时间之间的映射会更稳定,整个系统里的多传感器数据也更容易落到统一的时间线上。[8]
这个方案对机器人系统尤其有价值,因为最终往往不是只关心图像时间,而是关心:
- 图像时间
- 控制时间
- 机器人位姿时间
- 其他传感器时间
它们能否在系统层面真正对应起来。
图 4:三套时间之间的关系示意
作用于
对齐
拉齐
外部触发时间
物理触发沿到达相机
相机内部时间
Timestamp Counter
Linux 主机时间
ROS / 系统时钟
硬触发
PTP / IEEE1588
ptp4l + phc2sys
说明:
- 这张图强调"触发时间、设备时间、主机时间"是三级概念
- 驱动层真正需要做的,是把设备时间正确映射到 ROS 时间
六、站在 ROS2 驱动设计角度,应该怎样做才更靠谱
如果从实际工程可维护性和诊断能力出发,下面这几个优先级非常关键。
1. 能拿设备侧 chunk timestamp,就一定优先拿
Basler 在 chunk timestamp 支持方面公开资料最完整;海康公开手册虽然没有像 Basler 那样完整展开 API 细节,但至少可以确认它具备:
Timestamp ValueGev IEEE 1588- trigger / strobe 事件输出能力
至于某个具体海康型号能否直接把时间戳作为 chunk 跟图像一起带出,还要看具体型号和 SDK 节点定义。[9]
但不管厂商如何,原则都应该很明确:
只要设备能提供采集时刻,就优先使用设备时刻。
2. 必须统一时间戳语义:到底选 FrameStart 还是 ExposureStart
很多所谓"同步误差",其实不是误差,而是语义没有统一。
如果你的场景是:
- 双目 / 多目视觉
- 高精度几何对齐
- 焊缝检测
- 工业测量
那么多数情况下,ExposureStart 更符合真实采样意义 。
如果你关心的是"这一帧流程何时开始",那么 FrameStart 也可以。
最怕的是:
- 驱动拿的是
FrameStart - 算法却默认把它当成
ExposureStart
最后所有人都觉得系统差一点,但谁也说不清到底差在哪。[5]
3. 诊断元数据不要丢
如果开发的驱动只发图像而不保留诊断字段,那么一旦现场出现问题,排查成本会非常高。
建议至少把这些字段通过 diagnostic topic、自定义状态消息或者日志保留下来:
- 帧 ID
- 设备时间戳
- trigger counter
- ignored trigger counter
- line status
- CRC 校验结果
这些字段对于定位下面这些问题非常有帮助:
- 丢帧
- 过触发
- 某一路抖动
- 时序漂移
- 数据损坏[9]
对工业系统来说,"能看到图"不等于"系统可诊断" 。
真正可落地的驱动,必须把时序诊断能力一起设计进去。
4. 不要把到包时间当成采集时间
这一点值得再强调一次。
ROS 官方对 sensor_msgs/msg/Image 的定义已经明确要求:
header.stamp 应尽量表示 acquisition time。[7]
因此:
publish_time = now(),只能作为兜底;callback_arrival_time,也只是"主机拿到这张图的时刻";- 真正合理的做法,是:
header.stamp = map(device_chunk_timestamp)
这并不是"时间语义上的吹毛求疵",而是会直接影响视觉融合、时间对齐、控制回放和问题定位精度的设计点。
七、一个实用的 ROS2 driver 时间戳实现思路
如果把这个思路真正落到驱动代码里,通常可以抽象成下面这样:
cpp
// 伪代码:强调设计思路,不绑定某一家 SDK 的具体 API
while (running) {
GrabResult result = sdk.getFrame(); // 图像 + 元数据
// 1) 优先读取设备侧 chunk timestamp
uint64_t dev_ts = result.hasChunkTimestamp()
? result.chunkTimestamp()
: 0;
// 2) 读取帧号 / CRC / 触发计数等诊断信息
uint64_t frame_id = result.hasFrameID() ? result.frameID() : local_seq++;
bool crc_ok = result.hasCRC() ? result.checkCRC() : true;
// 3) 设备时钟 -> ROS 时间映射
rclcpp::Time stamp;
if (dev_ts != 0 && time_mapper.isReady()) {
stamp = time_mapper.deviceToRosTime(dev_ts);
} else {
stamp = node->now(); // 兜底方案
}
sensor_msgs::msg::Image msg;
msg.header.stamp = stamp;
msg.header.frame_id = optical_frame;
fillImageData(msg, result);
image_pub_->publish(msg);
publishDiagnostics(frame_id, dev_ts, crc_ok, ...);
}
这个框架真正重要的地方,不在于某个 API 叫什么,而在于它体现了一个正确顺序:
- 先拿设备侧采集时间;
- 再做时间映射;
- 再补齐诊断信息;
- 最后再发图。
图 5:驱动内部时间戳处理逻辑
代码思路视觉化。
是
否
是
否
SDK GrabResult
有设备 Chunk Timestamp?
读取 dev_ts
使用 node->now() 兜底
time_mapper 已就绪?
device timestamp -> ROS time
填充 Image.header.stamp
发布 Image
读取 FrameID / CRC / Trigger Counter
发布诊断信息
说明:
- "图像发布"和"时序诊断"最好并行设计,而不是只关注像素数据
八、实际项目里最容易踩的几个坑
如果把前面的所有内容压缩成工程经验,那么最值得反复提醒的,其实就是下面这几个坑。
坑一:把"触发同步"误当成"时间同步"
多台相机同一根触发线,只能说明它们"尽量一起动作",不能说明它们的设备时间戳已经在同一时间轴上。
没有 PTP 时,设备时间戳跨相机直接比较通常并不严谨。[2]
坑二:把"到包时间"当成"采集时间"
主机用户态拿到图像的时间,会受到网络传输、主机调度、SDK 线程调度、缓存排队等多种因素影响。
而 ROS Image.header.stamp 真正应该表达的是 acquisition time。[7]
坑三:时间戳语义选错
驱动拿的是 FrameStart,算法却默认它是 ExposureStart;或者反过来。
最后就会出现一个非常典型的现象:
"明明同步都开了,但系统就是差一点。"[5]
坑四:没有保留诊断字段
如果只发图像,不保留:
- frame id
- trigger counter
- ignored trigger counter
- CRC
- line status
那么现场一旦出现偶发丢帧、过触发、时序漂移、帧乱序,基本就很难高效定位。
Basler 的 chunk 元数据,本来就是为这种工程诊断准备的。[9]
总结
硬触发解决"同时拍",PTP 解决"同表记时",ROS2 driver 解决"把正确的采集时刻交给下游"。
再展开一点,就是:
- 最底层:硬件触发线让多台相机尽量同时动作;
- 相机内部:PTP 让多台相机按同一块表记录时间;
- Linux 主机 :
ptp4l + phc2sys让主机也尽量贴近同一时间域; - ROS2 driver :把设备侧采集时刻正确写进
Image.header.stamp。[3]
只要这四层关系没搞混,工业相机同步系统的大方向通常就不会错。
参考资料
1\] [Basler, Triggered Image Acquisition](https://docs.baslerweb.com/triggered-image-acquisition.html) \[2\] [Basler, Precision Time Protocol](https://docs.baslerweb.com/precision-time-protocol) \[3\] [Debian Manpages, phc2sys(8) --- linuxptp](https://manpages.debian.org/testing/linuxptp/phc2sys.8.en.html) \[4\] [Basler, Acquisition Timing Information](https://docs.baslerweb.com/acquisition-timing-information.html) \[5\] [Basler, PLCamera.BslChunkTimestampSelectorEnum Class](https://docs.baslerweb.com/pylonapi/net/T_Basler_Pylon_PLCamera_BslChunkTimestampSelectorEnum) \[6\] [Basler, Data Chunks](https://docs.baslerweb.com/data-chunks.html) \[7\] [ROS Documentation, sensor_msgs/msg/Image](https://docs.ros.org/en/humble/api/sensor_msgs/html/msg/Image.html) \[8\] [The Linux PTP Project](https://www.linuxptp.org/) \[9\] [Basler, PLChunkData Class](https://docs.baslerweb.com/pylonapi/net/T_Basler_Pylon_PLChunkData)