Basler 相机、Moxa 交换机都上了 PTP,机器人为什么还是抓偏 5mm?
首发于公众号「如是有为」 · 查看原文

我第一次在项目会上意识到“PTP 不是救命药”,不是看白皮书,是看一张验收表。
那张表上写着:时间同步:PTP(IEEE 1588)。下一行写着:系统端到端延迟 ≤ 5 ms。再下一行空着:各子系统时间戳口径:________。
没人能填。
会议室里很安静。相机厂家说,我们支持 PTP;交换机厂家说,我们也支持 PTP;工控机那边说,Linux 上 ptp4l 已经跑起来了;伺服那边说,我们驱动器内部也有同步机制。
轮到编码器链路时,问题来了:这个位置值,到底对应哪个时刻?
是前端采样的时刻?是 ADC 转换完成的时刻?是插值算法算完的时刻?是数据寄存器锁存的时刻?还是主控通过 SPI、BiSS、SSI 把它读走的时刻?
同一个“位置”,在不同人嘴里,可能差了好几个时钟域。这时候你再看验收表上那句“时间同步:PTP”,就会觉得它像一句安慰剂。
不是 PTP 没用,而是你把它当成了救命药。
表上写了 PTP,真正的雷却在空白处
PTP,也就是 Precision Time Protocol,确实是工业系统里非常重要的时间同步协议。IEEE SA 的标准页显示,IEEE 1588-2019 的 Board Approval 日期是 2019-11-07,Published 日期是 2020-06-16。NIST 的介绍页也提到,IEEE 1588-2019 在 2021 年 6 月被 IEC 采纳为 IEC 61588:2021。
这说明 PTP 是正经东西,是工业网络、测量控制、分布式系统里绕不开的东西。但问题是:标准的存在,不会自动替你定义系统边界。
工程现场里最常见的误会是:上了 PTP,就等于系统时间统一了。
真正发生的事情通常是这样:相机和交换机在一个 PTP 域里,时间戳很漂亮;工控机用 ptp4l 把网卡硬件时钟对上了;MCU 或 FPGA 里跑着另一个本地计数器;伺服驱动器里的采样节拍来自另一个 PLL;编码器芯片给你一个数据就绪信号,但它代表的是“采样完成”,还是“数据可读”,没人写清楚。
最后,所有时间戳都被扔进同一个数据库,然后大家指望它们自己讲人话。
这就是很多项目里最危险的地方:钟好像对齐了,事件却没有对齐。
同样叫 timestamp,Basler 文档里就不止一种意思
先声明一句:下面提到的品牌和型号,不是说它们有问题。恰恰相反,正规厂商把功能、参数、寄存器、时间戳语义写出来,是很好的事情。
真正的问题在系统集成侧:你不能看到“支持 PTP”“支持时间戳”“支持同步采样”,就默认全系统已经可复盘。
拿工业相机举例。Basler ace 2 系列里,比如 Basler ace 2 a2A1920-51gcBAS,官方资料显示它是基于 Sony IMX392 的 2.3 MP GigE 相机,默认分辨率 1920 × 1200,帧率 51 fps。
这类相机在视觉系统里很常见。但真正有意思的不是“多少像素、多少帧”,而是它的时间戳语义。
Basler 的 Data Chunks 文档里,BslChunkTimestampSelector 可以选择时间戳对应的事件。例如 FrameStart 表示当前图像采集开始,ExposureStart 表示当前图像曝光开始,ExposureEnd 表示当前图像曝光结束。
这件事本身就很说明问题。如果你的日志里只写:
camera_timestamp = 1234567890 ns
那它到底是什么意思?
是曝光开始?是曝光结束?是帧开始?是图像传到主机?还是应用层线程拿到图像?
这些在算法复盘里完全不是一回事。
假设输送线速度是 1 m/s,时间口径差 3 ms,就是 3 mm 的空间误差。如果你在做视觉抓取、贴装、分拣,3 mm 已经足够让项目会上开始互相看对方。
所以我看到相机支持时间戳,不会马上放心。我会先问一句:你给我的这个 timestamp,是 ExposureStart,还是 FrameStart?
这不是抠字眼,这是事故复盘时能不能对上账。
Blackfly S 提醒你:PTP 管的是设备,不是你的应用线程
再看一个常见的 GigE 工业相机例子:Teledyne FLIR Blackfly S BFS-PGE-23S3。
Teledyne FLIR 的相机参考文档里,GevIEEE1588 的描述很直接:启用 IEEE 1588 PTP,用来控制 timestamp register。
这句话很关键。PTP 控制的是相机设备侧的时间戳寄存器,但你项目里的“图像时间”,可能至少有五种。
第一种,是相机侧时间戳 t_cam。第二种,是网卡收到数据包的硬件时间戳 t_nic。第三种,是驱动层交付 buffer 的时间 t_driver。第四种,是应用层线程拿到图像的时间 t_app。第五种,是数据入库时间 t_db。
这五个时间,都可能被工程师口头叫成“图像时间”。但它们不是同一个时间。
所以问题不应该只写成“相机是否支持 PTP”,而应该写成:相机时间戳对应曝光、帧开始还是传输完成?主机侧 t_rx 是网卡硬件时间戳,还是应用层时间?两者之间的最大偏差是多少?这个偏差在高负载下测过没有?
这就是我说的:PTP 是工具,时间戳语义才是交付物。
Moxa + Intel I210 + ptp4l 都很正规,但还没走到终点
有些系统配置看起来已经很正规了。
比如交换机用 Moxa EDS-405A-PTP。Moxa 官方页面写明,这是一款 5 口工业以太网交换机,支持硬件 IEEE 1588 PTPv2。
工控机网卡用 Intel Ethernet Controller I210。Intel 产品资料里也写到,I210 支持 IEEE 1588 和 802.1AS 数据包的硬件时间戳。
Linux 上再跑 ptp4l。Red Hat 文档解释过,在硬件时间戳场景下,ptp4l 用来同步网卡上的 PTP Hardware Clock,phc2sys 再把系统时钟同步到网卡硬件时钟。
这套配置当然比“随便拿系统时间打日志”认真得多。但它依然只说明一件事:你的网络时间链路开始认真了。
它还没有回答这些问题:编码器芯片内部位置值是哪一拍锁存的?伺服驱动器的采样时钟和 PTP 域有没有关系?应用层算法拿到图像时,距离曝光开始已经过了多久?数据库里的 timestamp_ns 是网卡硬件时间、系统时间,还是应用层时间?主控收到编码器数据时,编码器芯片里那个角度值已经“老”了多少微秒?
所以我不反对项目里写 Moxa、Intel I210、ptp4l。我反对的是写完这些,就以为系统时间问题已经结束了。
真正的验收条款应该继续写清楚:PTP 域由谁组成,主机系统时钟是否由 phc2sys 同步到网卡 PHC,相机时间戳是 ExposureStart 还是 FrameStart,应用层时间戳是 buffer 到达还是算法结束,编码器时间戳是采样锁存还是寄存器更新,伺服时间戳是电流采样还是控制量生效。
这些话比“支持 PTP”有用得多。
机器人抓偏 5mm,可能不是钟不准,而是时刻说错了
做机器人、伺服、视觉引导、位置传感的人,真正关心的往往不是“钟有多准”,而是:这个动作、这个位置、这一帧图像、这一拍电流采样,到底对应现实世界里的哪个时刻?
比如一套视觉引导抓取系统,抓偏了 5 mm。表面上看,所有模块都在打时间戳。相机有时间戳,工控机有时间戳,驱动器有时间戳,编码器也有位置数据。
但复盘时你会发现,大家说的“时间”根本不是一个东西。
相机时间戳可能是曝光开始,也可能是图像帧完成;工控机时间戳可能是网卡收到包,也可能是应用层线程拿到数据;驱动器时间戳可能是电流环采样,也可能是通信报文更新;编码器时间戳可能对应传感器采样,也可能对应位置值输出。
这些在日志里都可能被写成 timestamp_ns。看起来都叫 ns,但它们不是同一种 ns。
一个是硬件时间戳,一个是软件时间戳;一个在 PTP 域里,一个在本地晶振域里;一个代表采样时刻,一个代表接收时刻;一个抖动主要来自网络,一个抖动主要来自队列调度;一个可以追到硬件锁存点,另一个只是应用层随手打的时间。
所以我现在反而更相信一句土话:你要的不是更快,而是可解释。
纳秒级同步听起来很高级,但项目出问题时,救你的往往不是“纳秒”,而是“这个纳秒到底是谁定义的”。
编码器最危险的一句话:我读到的就是现在
我长期做编码器和位置传感相关芯片,越来越觉得,位置数据最怕被当成一个静态数字。
很多人看到主控读回来一个角度值,比如 123.456°,第一反应是:编码器告诉我现在的位置是 123.456°。
但工程上更准确的问法应该是:这个 123.456°,对应的是哪个时刻的机械位置?
这背后至少有六层时间。
第一层,是传感器前端看到磁场、光栅、感应信号的时刻。第二层,是 ADC 或比较器完成采样的时刻。第三层,是数字滤波、插值、CORDIC 或补偿算法处理完成的时刻。第四层,是位置寄存器更新或锁存的时刻。第五层,是接口把数据吐出去的时刻。第六层,是主控真正读到并写入日志的时刻。
在低速场景里,这些差异可能被忽略。但到了高速电机、机器人关节、闭环伺服、视觉同步抓取,这些差异就会变成真实的位置误差。
比如 ams OSRAM AS5047P。它的数据手册里强调了 DAEC 动态角度误差补偿,宣称 almost 0 latency,并且可以通过标准 4 线 SPI 读出 14-bit 绝对角度位置数据。
为什么一个磁编码器芯片要专门强调 latency?因为高速旋转时,角度不是一个静态数字。
假设电机 3000 rpm,也就是 50 rps。如果位置链路里有 50 μs 的有效延迟,对应的机械角度就是:
50 rps × 360° × 50 μs = 0.9°
0.9° 对很多伺服系统来说,已经不是“小误差”。
所以编码器项目里,我最怕别人只问:多少位?SPI 多快?ABI 多少线?最高转速多少?这些当然重要,但还不够。
真正要问的是:这个角度值,对应的是磁场采样那一刻,还是 CORDIC/插值算完那一刻,还是寄存器更新那一刻,还是主控 SPI 读完那一刻?
AS5047P 这类芯片把 latency 拿出来讲,本质上就是在提醒系统工程师:位置值不是“现在”。位置值一定属于某一个被定义过的时刻。
你不定义这个时刻,后面再谈 PTP、同步、日志、复盘,都会变虚。
EtherCAT DC 也很准,但它和 GigE PTP 不是一家人
还有一个特别容易被混在一起的场景:视觉走 GigE PTP,运动控制走 EtherCAT Distributed Clocks。
比如 Beckhoff EL5101-0090 这种 EtherCAT 增量编码器接口。Beckhoff 官方资料写到,EL5101-0090 支持 Distributed Clocks;在增强工作模式下,输入数据可以与其他 Distributed Clock 从站同步记录,系统精度小于 100 ns。
这当然是很强的同步能力。但注意,它说的是 EtherCAT DC 域里的同步。
如果你的相机在 GigE PTP 域里,编码器/驱动器在 EtherCAT DC 域里,那你系统里至少已经有两个时间世界:一个是 GigE PTP 域,里面有相机、交换机、工控机网卡;另一个是 EtherCAT DC 域,里面有驱动器、编码器接口、运动控制周期。
这两个世界都可以很准。但两个“很准”的世界,放在一起不一定自动可解释。
所以在这种系统里,验收表最该多问一句:GigE PTP 时间与 EtherCAT DC 时间的对齐方式是什么?
是同一个主时钟?是主控周期性校准?是硬件同步线?是事后标定?最大偏差是多少?异常时有没有记录?
这些不写,最后就会出现一种很尴尬的情况:视觉说,我的曝光时间很准;运动控制说,我的编码器采样也很准;但系统问,那为什么抓偏了?
答案可能不是谁不准,而是两个时间域之间没人负责。
三类坑最常见:看起来同步了,其实没同步
第一类坑,是把接收时刻当成采样时刻。上位机收到一帧数据,应用层打一个 t_rx。日志看起来很完整,曲线也很漂亮。可一旦出问题,你才发现自己真正需要的是 t_sample。收到数据的时间,不等于数据发生的时间。这句话在调试现场非常值钱。
第二类坑,是平均延迟看起来可控,最坏抖动没人负责。很多系统会写“端到端延迟 ≤ 5 ms”。但这个 5 ms 是平均值、典型值、P95,还是最坏值?测的是空载,还是满负载?测的是常温,还是高温老化后?测的是单设备,还是多设备同时上报?平均延迟好看,不代表事故窗口里那一下不出问题。机器人抓偏、关节振一下、伺服偶发报警,往往不是平均值造成的,而是最坏那一拍造成的。
第三类坑,是所有字段都叫 ns,但背后的时钟质量完全不同。硬件时间戳和软件时间戳不一样,PTP 锁定的时钟和本地自由运行的晶振不一样,低抖动同步线和操作系统线程调度不一样,芯片内部锁存点和上位机数据库入库时间更不一样。
你在日志里看到的可能都是 64 位整数,单位都是 ns,但它们背后的分辨率、稳定性、漂移和语义,可能差一个数量级。
这就是为什么我越来越不喜欢只在验收表里写一句:时间同步,支持 PTP。
这句话太轻了,轻到无法承担事故复盘的重量。
别验收“支持 PTP”,要验收“谁对哪个时刻负责”
后来我把那张验收表改了一版。我没有把“PTP”删掉,因为 PTP 当然有价值。但我把它从一个孤零零的功能项,改成了一组必须填满的问题。
我不再让供应商只填“支持”。我会让他们填下面这些东西。
第一张卡:相机时间戳。 相机字段叫什么?是 t_cam 还是其他名字?它代表曝光开始、曝光结束,还是帧开始?相机时间来自 PTP 锁定,还是本地时钟?验收时怎么测?是触发光源加示波器,还是用 chunk 数据对齐?
第二张卡:网卡时间戳。 主机侧 t_nic 是网卡收到包的硬件时间戳,还是驱动层时间?如果用 Intel I210 这类支持 PHC 的网卡,PHC 和系统时钟之间的偏差有没有记录?ptp4l 和 phc2sys 的状态有没有进日志?
第三张卡:应用层时间戳。 t_app 到底表示 buffer 到达、算法开始,还是算法结束?高负载下最大抖动是多少?CPU 满载、磁盘写日志、网络高流量时,有没有重新测?
第四张卡:编码器时间戳。 t_enc_sample 对应传感器采样锁存、寄存器更新,还是接口输出?编码器本地晶振、同步输入、主控读取之间的关系是什么?主控读到位置值时,这个位置值已经延迟了多少?
第五张卡:驱动器时间戳。 t_servo 是电流采样时刻、位置环更新时刻,还是控制量生效时刻?它的时钟来自驱动器 PLL,还是 EtherCAT DC?控制周期 GPIO 翻转能不能测出来?
第六张卡:运动控制时间戳。 t_motion 是轨迹点下发、插补周期开始,还是编码器采样?如果运动控制在 EtherCAT DC 域里,它和 GigE PTP 域怎么对齐?最大偏差是多少?
第七张卡:端到端延迟。 起点是谁?是相机曝光开始,还是编码器采样锁存?终点是谁?是算法输出,还是执行器控制量生效?典型值是多少?最大值是多少?测试条件是什么?
这七张卡不复杂,但它们能逼每个供应商承认一件事:我的时间戳,只对我这个边界内的事件负责。
只要这句话说清楚,系统集成就有了对账的基础。
正救命的不是纳秒,而是定义
很多验收表喜欢写“支持”:支持 PTP,支持硬件时间戳,支持同步采样,支持低延迟输出,支持高速接口,支持纳秒级时间戳。
但“支持”两个字,工程含义太弱。
支持,不代表默认打开;默认打开,不代表配置正确;配置正确,不代表跨域定义清楚;定义清楚,不代表最坏场景测过;测过,不代表事故复盘能对上账。
所以我现在更倾向于把验收语言改成“可复盘”的语言。
不要只写“系统支持 PTP”,要写“系统需提交时钟域边界说明,明确所有时间戳的时钟源、语义、分辨率、稳定性、跨域对齐方式和最坏抖动上限”。
不要只写“端到端延迟 ≤ 5 ms”,要写“端到端延迟需定义起点和终点;起点为传感器采样锁存时刻,终点为执行器控制量生效时刻;需分别给出典型值、最大值和测试条件”。
不要只写“编码器数据带时间戳”,要写“编码器时间戳需明确对应采样、计算完成、寄存器更新或通信输出中的哪一个事件,并说明与主控读取时刻之间的最大偏差”。
不要只写“相机支持时间戳”,要写“相机时间戳需明确对应 ExposureStart、ExposureEnd、FrameStart 或其他事件;若主机侧另行打时间戳,需同时记录相机侧时间戳与主机侧时间戳,并给出两者偏差统计”。
这些话看着麻烦,但它能少开很多甩锅会。
最后问一句:你的日志,真的能复盘事故吗?
后来再有人问我:“我们项目已经上 PTP 了,为什么日志还是对不上?”我一般不会先问交换机,也不会先问 ptp4l 配置。
我会先问:你们系统里有几套时钟?每个时间戳到底是什么意思?哪些是硬件打的,哪些是软件打的?相机时间戳是曝光开始,还是帧开始?编码器位置值对应采样时刻,还是输出时刻?EtherCAT DC 和 GigE PTP 是怎么对齐的?端到端延迟的起点和终点是谁定义的?最坏抖动是谁测的?
这些问题答不上来,PTP 配得再漂亮,也只是把网络里的钟对齐了一部分。不是没用,是不够。
PTP 解决的是“钟准不准”。时钟域边界表解决的是“事能不能说清楚”。一个是工具,一个是证据。一个让系统更同步,一个让责任更可复盘。
工程里最贵的,往往不是那几个纳秒。是事故发生后,所有人看着同一份日志,却解释不出同一个现场。
所以我后来越来越相信这句话:PTP 不是救命药。真正救命的,是你提前把时间边界写清楚。
你把边界写清楚,PTP 才有意义。你不写边界,PTP 只会让你更确信自己“已经同步了”,然后在最贵的那一下里,输得更干净。

关注公众号「如是有为」
微信扫一扫,新文章先在公众号发出。