机器人感知---工业相机硬触发、时间戳同步( PTP)与 ROS2 驱动时间戳设计

从外部触发到 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 1588Timestamp ValueExposure Start ActiveHard Trigger Active 等相关能力。[1]


一、真正需要先分清的,不是同步方案,而是"这里到底有几种时间"

在工业相机同步系统里,最容易造成混乱的,不是配置项,而是"时间 "这个概念本身。

因为在这条链路里,至少存在三套不同语义的时间。


1. 触发时间:外部信号真正到达相机输入脚的时刻

第一套时间,是外部触发沿真正到达相机输入脚的那个物理时刻。

比如你用 PLC、FPGA、IO 控制器或者主相机输出一根触发脉冲,把它接到目标相机的 Line1 或其他输入线。那么从物理意义上讲,触发时间就是这一下上升沿或下降沿到达输入引脚的时刻。

这个时间决定的是:相机什么时候开始响应这次采集动作。

Basler 文档中,Frame StartExposure StartExposure 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_REALTIME
  • CLOCK_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 = FrameStart
  • TriggerSelector = ExposureStart

海康公开手册中也能看到 Hard Trigger ActiveExposure 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 Delay
  • Input Line Response Time
  • Line Debouncer Setting
  • Frame 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 Start
  • Exposure End
  • Exposure Active

并把曝光模式切到 TriggerControlled

如果使用 TriggerWidth,那么曝光时长则由外部脉宽决定。[1]

因此这一步最重要的认知是:

Frame Start 关注的是"这一帧流程何时开始",Exposure Start 关注的是"真正积光何时开始"。

这两者语义不同,后面所有时间戳解释都要基于这个前提。


3. 相机在某个事件点锁存 timestamp

这一环,才是真正决定"你最终读到的时间戳到底表示什么"的关键。

Basler 的 chunk timestamp 支持按不同语义选择:

  • FrameStart
  • ExposureStart
  • ExposureEnd[5]

这意味着,在谈同步之前,必须先回答一个问题:

要对齐的到底是哪一个事件点?

如果选 FrameStart,那么你对齐的是"这一帧进入采集流程的时间"。

如果选 ExposureStart,那么你对齐的是"真正开始曝光积光的时间"。

如果选 ExposureEnd,那更适合分析曝光结束、读出、后续传输链路的行为。

很多项目最后出现"明明做了同步却还是差一点"的情况,根本原因并不是同步机制没打开,而是:

不同模块理解的时间戳语义压根不是同一个。


4. 图像和元数据通过 GigE Vision 发送到主机

从图像开始通过网线传输的那一刻起,它就不再处于"采集阶段",而是进入了"传输阶段"。

这时,主机侧的到达时间会受到很多因素影响,例如:

  • 网卡调度
  • 交换机转发
  • 网络包大小
  • 带宽占用
  • 重传
  • 主机内核调度
  • SDK 接收线程调度

这些因素会影响的是:

什么时候收到图像

但它们不会改变的是:

相机在采集时已经锁存好的设备侧时间戳

海康公开手册中可以看到 GEV Timestamp Tick FrequencyTimestamp ValueGev SCPD 等参数;Basler 文档则说明 data chunks 会附加在图像后面一起发往主机侧。[6]

这一步非常值得单独强调,因为很多系统里最常犯的错误,就是把"数据到达主机的时间"误当成"图像采集的时间"。


5. 厂家 SDK 解析图像与元数据

图像到达 Linux 主机后,厂家 SDK 会把数据流解析成:

  • image buffer
  • chunk data
  • frame metadata

Basler 文档中明确写到,Data Chunks 可以把附加信息跟随每幅图像一起送达主机,典型字段包括:

  • timestamp
  • frame counter
  • CRC

更具体的 API 字段包括:

  • BslChunkTimestampValue
  • ChunkFrameID
  • ChunkPayloadCRC16
  • ChunkFrameTriggerCounter
  • IgnoredCounter[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 体系通常按下面的方式工作:

  1. ptp4l 负责让网卡上的 PHC 跟 PTP 主时钟同步;
  2. phc2sys 再把 Linux 系统时钟同步到这个 PHC;
  3. 最终让主机时间尽量贴近设备所处的 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 Value
  • Gev 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 叫什么,而在于它体现了一个正确顺序:

  1. 先拿设备侧采集时间;
  2. 再做时间映射;
  3. 再补齐诊断信息;
  4. 最后再发图。

图 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)

相关推荐
小则又沐风a2 小时前
深入理解进程概念 第三章 进程调度切换
java·linux·服务器·前端
承渊政道2 小时前
【贪心算法】(经典实战应用解析(四):分发饼干、最优除法、跳跃游戏、跳跃游戏Ⅱ、加油站)
数据结构·c++·算法·leetcode·贪心算法·动态规划·哈希算法
liu-yonggang2 小时前
ROS2 性能优化指南
性能优化·ros2
z202305082 小时前
RDMA之路由算法介绍 (6)
linux·服务器·网络·人工智能·ai
楼兰公子2 小时前
# RK3588 Linux 驱动开发完整学习指南RK3588_Linux_Driver_Development.md
linux·驱动开发
光电笑映2 小时前
Linux 文件 IO:从概念到系统调用
linux·运维·服务器
历程里程碑3 小时前
53 多路转接select
linux·开发语言·数据结构·数据库·c++·sql·排序算法
WYH2873 小时前
一、驱动基础
linux·嵌入式硬件
j7~3 小时前
【MYSQL】 mysql库和表的操作--详解
数据库·c++·mysql·数据库表的操作·数据库库的操作