ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

单轴运行很稳定,一增加到四轴就开始超时,问题出在哪里?从 EtherCAT 周期预算到多轴实时调度的排查实战

单轴运行很稳定,一增加到四轴就开始超时,问题出在哪里?从 EtherCAT 周期预算到多轴实时调度的排查实战

在伺服控制、工业机器人和精密运动控制系统中,单轴调试通常是整个项目最容易取得进展的阶段。驱动器能够正常使能,位置反馈持续更新,控制周期基本稳定,运动轨迹也符合预期。但当系统从单轴扩展到四轴、六轴甚至更多轴时,原本稳定的控制程序却开始出现周期超时、位置跟随误差增大、偶发通信异常,甚至多个轴之间的动作不同步等问题。

工程师遇到这种情况,往往会先怀疑 EtherCAT 网络负载增加,尝试缩短通信周期、提高主站线程优先级,或者更换性能更强的 CPU。然而,实际故障未必来自总线带宽不足。多轴系统同时增加了过程数据规模、控制计算量、共享资源访问频率、线程间同步需求和异常处理复杂度。单轴运行时被掩盖的问题,也可能在多轴并发后集中暴露。

例如,一个控制周期目标为 1 ms 的系统,单轴运行时主站交换、数据解析和控制计算总共只需要 200 μs,表面上有较大余量。扩展到四轴后,控制程序需要处理更多反馈量、执行更多计算,还可能在每个轴的处理函数中访问同一把互斥锁。最终,周期耗时不一定只是原来的四倍,却可能因为锁竞争、缓存访问、调度延迟和任务执行顺序而出现明显长尾。

这里有一个重要原则:轴数增加带来的不是单纯的数据量增长,而是整条实时执行链路的负载、依赖关系和最坏执行时间发生了变化。要判断问题究竟出在哪里,必须分别测量总线交换时间、主站任务执行时间、控制计算时间和周期调度延迟,而不能只根据“单轴正常、多轴异常”就直接归因于 EtherCAT。

一、先算清楚周期预算:四轴增加的到底是什么?

在实时运动控制系统中,周期预算是排查超时问题的第一步。假设控制系统的目标周期为 1 ms,那么每个周期只有 1000 μs 的时间用于完成规定的工作。如果主站交换、过程数据处理、控制算法、数据同步和其他必要操作的总耗时超过这个预算,就可能出现周期违例。

需要注意,1 ms 是一个示例目标,并不代表所有运动控制系统都必须使用该周期。实际周期取决于驱动器能力、控制带宽、运动精度要求、网络配置和系统设计。

1. 将总周期拆分为可测量的阶段

一个典型的多轴控制周期,可以抽象为以下阶段:

周期定时器触发 ↓ 控制线程获得 CPU ↓ 读取并处理 EtherCAT 输入数据 ↓ 更新各轴状态与反馈变量 ↓ 执行运动学、轨迹或控制算法 ↓ 生成各轴输出过程数据 ↓ 主站发送 EtherCAT 帧 ↓ 接收和处理从站响应 ↓ 完成本周期必要工作

不同 EtherCAT 主站库的函数调用顺序可能不同。有些系统会在周期开始时处理上一轮收到的数据,再发送当前周期输出;另一些实现会采用不同的接收、处理和发送组织方式。因此,上面的流程只是用于预算分析的抽象模型,不能直接当作某个具体主站库的固定 API 调用顺序。

实际项目应根据所用主站的执行模型,明确每个阶段的起止点,避免将同一段时间重复计入预算,或遗漏异步执行阶段的耗时。

例如,可以分别统计:

  • 周期线程从预定触发时刻到真正开始执行的延迟。

  • 主站数据处理函数的执行时间。

  • 输入过程数据复制与解析时间。

  • 各轴控制计算时间。

  • 输出数据准备和发布所需时间。

  • EtherCAT 收发处理时间。

  • 周期结束时剩余的时间余量。

如果所有阶段都只记录一个笼统的“循环耗时”,即使发现某个周期从 300 μs 突然增加到 1100 μs,也无法判断究竟是主站处理变慢、控制算法计算量增加,还是线程没有及时获得 CPU。

2. 为什么单轴耗时乘以四,往往不等于四轴耗时?

最简单的估算方法,是假设每个轴的处理成本相同且彼此独立。假设单轴控制计算需要 40 μs,那么四轴可能需要约 160 μs。

但这个模型忽略了共享开销。

多轴系统通常需要维护统一的轴状态表、共享轨迹对象、公共运动学数据、统一报警状态和通信状态。各轴可能共用同一组日志接口、内存分配器、数据队列或同步对象。随着轴数增加,这些共享路径的访问频率也会增加。

此外,CPU 执行时间还会受到指令缓存、数据缓存、内存访问、分支预测和线程迁移等因素影响。一个轴的控制数据能够留在缓存中,不代表四个轴的数据也能获得同样的缓存命中率。

因此,系统总耗时可以抽象为:

T_{\mathrm{fixed}}
+
T_{\mathrm{axis}}(N)
+
T_{\mathrm{shared}}(N)
+
T_{\mathrm{sync}}(N)
+
T_{\mathrm{interference}}
]

其中,(N) 表示轴数;固定开销、单轴相关工作、共享资源开销、同步开销和外部干扰分别构成不同部分。这个公式用于帮助建立分析框架,不代表所有系统都能用简单线性模型精确预测。

如果共享资源竞争随轴数增长而明显增加,或者某个任务必须等待多个轴处理完成,系统耗时就可能呈现非线性增长。

3. 先确认是否真的超时,而不是只看周期执行时间

周期执行时间与周期违例不是同一个指标。

假设控制线程每 1 ms 被触发一次,某次执行花费 600 μs,看起来没有超过 1 ms。但如果该线程直到周期开始后 500 μs 才获得 CPU,那么它完成本周期工作的时刻就可能已经超出预定截止时间。

相反,如果某次任务执行了 700 μs,但系统的实际截止时间允许它在更长的窗口内完成,那么这次执行未必构成截止时间违例。

因此,至少需要区分:

  • 周期触发或预定唤醒时刻。

  • 线程实际开始运行的时刻。

  • 周期任务完成的时刻。

  • 系统规定的截止时间。

周期线程的实际开始延迟可以表示为:

[
J_k = t_{\mathrm{start},k} - t_{\mathrm{release},k}
]

其中,(t_{\mathrm{release},k}) 是第 (k) 次任务的预定释放时刻,(t_{\mathrm{start},k}) 是实际开始执行时刻。这个差值反映释放到执行之间的延迟,但不应在没有明确测量定义的情况下直接等同于所有场景中的“周期抖动”。

如果任务有明确的截止时间 (D_k),则可以进一步检查:

[
t_{\mathrm{finish},k} \leq D_k
]

只有把执行时间、开始延迟和截止时间放在同一时间线上,才能准确判断问题属于计算量超限,还是线程启动过晚。

二、EtherCAT 总线是不是瓶颈?先测量,不要凭轴数下结论

多轴系统扩展后,过程数据总量确实会增加。但轴数增加并不必然意味着 EtherCAT 带宽不足。是否构成瓶颈,取决于每个从站的数据映射、实际帧组织方式、网络速率、帧数量、协议开销和主站执行策略。

1. 先估算过程数据规模

假设一个伺服轴每个周期需要交换以下数据:

  • 输出方向:控制字、目标位置、目标速度等,共 16 字节。

  • 输入方向:状态字、实际位置、实际速度等,共 16 字节。

以上只是简化的示例,实际 PDO 内容和字节数应以设备映射配置为准。按每轴输入、输出合计 32 字节计算,四轴合计为:

[
4 \times 32 = 128\ \text{Byte}
]

如果扩展到八轴,单纯按该假设计算,过程数据就会增加到 256 Byte。

但这并不意味着网络上的每个 EtherCAT 帧只包含这些字节。实际通信还涉及 Ethernet 帧头、EtherCAT 帧和数据报头、帧校验序列、帧间隔,以及可能存在的多个数据报、其他从站过程数据和诊断信息。

因此,不能直接将 PDO 字节数除以网络速率,就得出完整的总线周期时间。该计算最多只能作为有效负载的初步估算,真正的线路占用时间需要结合实际帧格式、帧数量和主站实现计算或测量。

2. 分清三个不同的“网络时间”

工程师在讨论 EtherCAT 周期时,容易把三个概念混在一起。

第一是物理链路上的帧传输时间,涉及实际发送的帧长度、线路速率和协议开销。

第二是主站的收发处理时间,包括准备过程数据、提交发送、接收帧和执行相关协议处理所需的时间。具体执行模型取决于主站库和驱动实现。

第三是从主站开始本周期工作到数据可以被应用程序使用的完整时间。这个时间还可能包含调度延迟、任务执行和数据同步等待。

如果多轴系统的超时发生在主站线程获得 CPU 之前,那么单纯减少 PDO 字节数未必有明显效果。如果超时发生在主站收发处理期间,就应重点检查帧数量、网络负载、网卡驱动和主站处理路径。如果主站已经完成数据更新,而控制线程仍然迟迟没有开始执行,那么根因可能主要在应用任务调度和共享资源竞争。

因此,诊断时应把“网络传输慢”“主站处理慢”和“控制任务执行晚”分别测量。

3. WKC 正常,也不能证明总线周期一定满足要求

工作计数器(WKC)主要用于判断相关 EtherCAT 数据报是否获得了预期的处理结果。它对发现部分通信问题非常有价值,但并不是周期执行时间或数据新鲜度指标。

如果某个周期的 WKC 正常,只能说明对应数据报满足了相应的工作计数器检查条件。它不能证明主站按时开始执行,也不能证明整个控制任务按时完成。

同样,如果周期超时但 WKC 仍然正常,问题可能发生在通信之外。例如,周期线程因 CPU 竞争而延迟,随后仍然成功完成数据交换,只是完成时刻已经晚于系统规定的截止时间。

因此,建议同时记录 WKC、从站状态、主站执行时间、线程开始延迟和周期完成时间。不要用单个通信指标代替完整的实时性评估。

4. 如何验证总线是否真的成为瓶颈?

可以采用分阶段扩展测试:

测试阶段主要变化需要记录的指标
单轴基准配置周期时间、数据更新、超时次数
双轴增加一个从站帧组织变化、主站处理时间
四轴增加到四个从站周期耗时分布、任务开始延迟
多轴压力测试增加其他业务负载最大耗时、WKC、截止时间违例
长时间运行延长持续时间长尾事件、资源变化、队列积压

每个阶段应尽可能保持 CPU 配置、日志策略、测试轨迹和外部负载一致。如果增加轴数时同时改变了线程优先级、控制算法和网卡配置,就很难判断到底是什么因素导致结果变化。

如果主站收发处理时间随轴数增长明显,但线程调度延迟基本稳定,就可以进一步分析过程数据量、帧数量、主站实现和驱动开销。反之,如果主站处理时间变化不大,控制任务的开始延迟却明显增加,就应把调查重点转向 CPU 竞争、同步等待和实时线程组织。

三、CPU 利用率不高,四轴系统为什么还是会超时?

在多轴实时系统中,CPU 利用率是一个有用但不充分的指标。总体利用率较低,并不代表实时任务一定能够按时开始和完成。

例如,四轴控制线程可能大部分时间都在等待一个共享互斥锁。由于等待期间线程处于睡眠状态,CPU 利用率不一定高,但控制任务的端到端延迟却可能非常大。

另一种情况是,系统中存在少量高优先级实时线程,它们只在特定时刻集中运行,却刚好与多轴控制任务的关键时间窗口重叠。平均 CPU 利用率可能看起来正常,最坏情况下的任务响应时间却发生明显变化。

1. 检查线程运行在哪个 CPU 上

先列出系统线程及其调度属性:

ps -eLo pid,tid,psr,cls,rtprio,pri,ni,stat,comm

再观察各 CPU 的利用率:

mpstat -P ALL 1

必要时,检查主站线程和控制线程的 CPU 亲和性:

taskset -pc <TID>

需要关注的不是简单地把所有实时线程放到某个 CPU,而是确认关键任务之间的关系。

例如,主站线程与四轴控制线程都运行在同一个 CPU 上时,它们可能按照各自的优先级、可运行状态和同步关系竞争执行时间。如果某个更高优先级的实时任务长期处于可运行状态,低优先级任务就可能被延迟。

但如果把主站和控制线程全部分散到不同 CPU,又可能引入跨核数据共享、缓存一致性和额外同步成本。CPU 亲和性应当服务于明确的执行模型,而不是单纯追求“每个线程绑定一个核”。

2. 高优先级线程过多,也会增加调度风险

SCHED_FIFO可以帮助实时任务在满足调度条件时获得更明确的执行优先级,但它不会自动保证系统中所有实时任务都能在截止时间内完成。

假设系统存在多个高优先级线程,每个线程都在某些时刻运行较长时间,而且这些线程的执行窗口重叠。即使它们分别设置了实时优先级,系统仍然可能因为任务之间的执行时间预算没有协调好而出现超时。

因此,不能只根据优先级数字判断调度配置是否合理。还应分析每个任务的执行频率、最坏执行时间、截止时间、阻塞时间以及它与其他任务之间的依赖关系。

对多轴系统而言,还应明确哪些任务必须串行执行,哪些任务可以并行执行,哪些任务必须在数据交换完成后才能开始。如果四个轴的控制逻辑实际上都依赖一个统一的全局状态,那么把它们拆成四个线程不一定比单一周期线程更高效。

3. 多轴共享锁,是很值得优先检查的方向

在单轴程序中,一些简单的同步设计可能不会表现出明显问题。例如,控制逻辑每次读取轴状态时都会获取一个全局互斥锁,更新完状态后再释放锁。

但当四个轴在同一个周期内集中访问该锁时,锁竞争就可能增加。更糟糕的是,如果锁持有期间还执行日志写入、动态内存分配或复杂计算,其他线程就必须等待更长时间。

可以重点审查以下代码模式:

pthread_mutex_lock(&state_mutex); /* 读取或更新共享轴状态 */ /* 如果这里包含耗时操作,其他线程也可能长时间等待 */ pthread_mutex_unlock(&state_mutex);

示例只用于说明锁的边界。实际项目中应检查每一处加锁和解锁的位置,尤其是所有可能提前返回、发生错误或调用其他函数的路径,避免出现锁未释放的问题。

改进方向包括缩短临界区、减少共享状态、避免在持锁期间执行不确定耗时的操作,以及重新评估数据所有权和发布方式。

如果确实存在实时线程等待低优先级线程释放锁的情况,还应评估适用的优先级继承机制。但优先级继承不是万能修复:它无法消除过长的临界区,也无法解决所有死锁和错误的锁依赖设计。

4. 内存分配和日志,也可能随着轴数增加而放大

假设单轴程序每周期只输出少量日志,多轴程序却为每个轴分别输出状态、位置、速度、误差和报警信息。那么日志量会随轴数明显增加,格式化和输出开销也可能集中出现在同一个周期。

同样,如果每个轴的处理函数都动态创建临时对象、分配缓存或访问复杂的共享容器,系统在多轴负载下可能出现更大的执行时间波动。

对于有严格截止时间要求的周期路径,建议在初始化阶段完成必要的内存准备,尽量使用容量可控的数据结构,并避免在每个周期执行同步文件输出。

但这些优化应当建立在测量证据之上。若跟踪显示超时主要来自线程唤醒延迟,就不应该把主要精力花在减少字符串格式化上;如果主站收发本身已经占满周期预算,优化应用层日志也无法单独解决问题。

四、四轴同步运行时,真正的难点是任务之间的依赖关系

多轴系统与单轴系统还有一个重要区别:多个轴之间可能存在严格的同步要求。

例如,四轴机器人执行同一条轨迹时,各轴的目标位置不仅要在每个周期计算完成,还要在正确的时间窗口内被发布到对应驱动器。如果某一个轴的计算结果晚到,系统可能面临数据不一致、轨迹误差或整体周期延迟。

这时,问题就不再只是“某个线程够不够快”,而是多个任务能否按照正确顺序完成。

1. 不要让各轴各自运行,却没有统一的周期边界

一种容易出现问题的设计,是为每个轴建立独立的周期线程。每个线程分别读取反馈、计算控制量并更新输出数据,但没有明确规定各轴在什么时候开始计算,以及什么时候必须完成输出发布。

在负载较低时,这种设计可能看起来工作正常。但随着轴数增加,线程唤醒时刻、执行时间和同步等待之间的差异会逐渐显现。

例如,轴 1 已经完成本周期计算并发布了目标值,轴 2 却仍然在等待共享状态锁。此时,如果主站发送线程读取输出过程映像,可能出现部分轴已经更新、部分轴仍然保留旧目标值的情况。

这类问题不一定造成 WKC 异常,因为总线帧本身可能仍然完整、有效。真正的问题在于,应用层在一个周期中发布的数据可能来自不同的计算阶段。

因此,多轴控制应明确数据快照和周期发布边界。必要时,先计算所有轴的输出,再统一发布本周期的数据;或者使用经过严格同步设计的缓冲区,确保主站读取到的是完整且一致的一组输出数据。

2. 多线程并行,不一定比单周期线程更快

如果四个轴之间几乎没有依赖关系,而且控制计算量较大,那么并行处理可能有价值。但如果每个轴的计算都需要频繁访问共享状态、等待统一轨迹规划结果,或者在结束时必须同步,那么并行化可能增加调度和同步开销。

假设四个轴分别需要 50 μs 的独立计算时间,理想情况下并行执行可能缩短部分计算阶段。但真实系统还需要考虑线程唤醒、任务分发、共享数据访问、结果汇总和同步等待。对于很短的计算任务,这些固定成本可能抵消并行带来的收益。

因此,在决定是否拆分多轴线程之前,应先测量单线程执行时间和共享开销。如果每个轴的计算只占整个周期的一小部分,真正的瓶颈却在主站数据处理或锁等待,那么并行化未必是最有效的改进方向。

3. 区分反馈同步、输出同步和轨迹同步

多轴控制中至少存在三个不同的同步问题。

反馈同步关注多个轴的实际位置、速度和状态是否属于可比较的时间窗口。即使应用程序在同一时刻读取四个轴的变量,也不代表这些变量一定来自完全相同的物理采样时刻。

输出同步关注多个轴的目标指令是否在同一个预期周期内被正确发布,并由主站按照设计完成发送。

轨迹同步关注多个轴的目标值是否遵循同一个轨迹规划和时间基准。某一轴晚完成计算,可能不仅影响单轴响应,还会影响整个机械系统的协调运动。

如果只检查每个轴的通信状态,而不检查这三个同步层面,就可能出现各轴分别正常、组合运行却不正常的现象。

对于支持分布式时钟(Distributed Clocks,DC)的设备,还应核对主站和从站的同步配置、参考时钟选择、同步信号与设备内部任务之间的关系。DC 有助于协调相关设备的时钟和执行时序,但不能自动解决应用层线程之间的竞争,也不能替代对数据发布顺序的验证。

4. 多轴系统需要明确数据过期时的行为

假设四轴控制器在某个周期中成功获得了三轴反馈,第四轴的反馈却仍然是上一周期的数据。程序是否应该继续计算整组控制输出?

这个问题不能仅通过“通信正常”来回答。系统应当根据控制算法、机械结构和安全要求,明确数据过期或不同步时的处理策略。

在一些场景中,控制器可以在规定范围内容忍短暂的数据延迟;在另一些场景中,数据不同步可能直接影响轨迹精度或设备安全,需要触发报警、停止特定动作或进入预先定义的安全状态。

因此,工程设计应明确每个关键变量的有效期、各轴数据的一致性要求,以及出现异常时允许采取的动作。具体阈值必须根据系统需求和验证结果确定,不能用一个统一的毫秒数适用于所有多轴设备。

五、从单轴到多轴:建立可复现的定位与验收流程

要解决单轴正常、四轴超时的问题,最有效的方法不是一次性修改所有配置,而是按照可验证的顺序逐步扩展负载。

1. 先建立单轴基线,再逐步增加轴数

建议先记录单轴运行时的完整周期统计,包括线程开始延迟、主站处理时间、控制计算时间、周期完成时间、WKC 和异常次数。

随后在保持其他配置尽可能不变的情况下,依次扩展到双轴、四轴和目标轴数。每个阶段都应记录相同的指标,以便比较增长趋势。

重点观察以下变化:

  • 主站收发处理时间是否随轴数增长。

  • 控制计算时间是否接近线性增长。

  • 周期开始延迟是否出现明显长尾。

  • 共享锁等待时间是否随轴数增加。

  • 输出数据是否出现跨周期或跨轴不一致。

  • WKC 异常是否与超时发生在同一时间窗口。

  • 队列积压、日志量和内存使用是否同步变化。

如果只记录平均周期耗时,很可能看不到真正影响实时性的少量长尾事件。建议同时统计 P50、P95、P99、最大值和截止时间违例次数,并记录测试持续时间和总周期数。

2. 用调度跟踪判断任务究竟晚在哪里

在 Linux 环境中,可以先确认调度跟踪事件是否存在:

trace-cmd list -e | grep -E 'sched_wakeup|sched_switch'

在测试窗口中记录相关事件:

sudo trace-cmd record \ -e sched:sched_wakeup \ -e sched:sched_switch \ sleep 10

结束后查看记录:

trace-cmd report

分析时应区分线程被唤醒与线程真正开始运行的时刻。sched_wakeup反映唤醒或进入可运行状态相关的事件,sched_switch则可用于观察任务何时被切入 CPU。

如果主站线程或控制线程在预定周期之后才开始运行,应进一步调查 CPU 竞争、任务优先级和内核路径。如果线程已经及时开始执行,却在某个阶段花费了很长时间,则应继续测量函数耗时、锁等待、系统调用和数据处理过程。

需要注意,调度跟踪不能单独证明锁竞争,也不能单独证明 EtherCAT 帧的物理收发时刻。它应与应用程序时间戳、主站统计和网络诊断数据结合使用。此外,跟踪工具会引入一定观测开销,测试时应尽可能缩小记录范围,并比较跟踪开启前后的性能表现。

3. 建立周期预算表,明确每个阶段的时间上限

可以为每个关键阶段建立预算表:

阶段测量指标验证目标
周期释放到线程开始启动延迟是否满足任务时序要求
主站数据处理执行时间是否随轴数出现异常增长
输入数据处理解析和复制时间是否存在不必要的重复工作
多轴控制计算执行时间及长尾是否超出计算预算
数据同步与发布等待时间、队列深度是否存在竞争或跨周期问题
周期完成完成时刻与截止时间是否存在真正的周期违例

预算值不能脱离具体项目随意设定。它们应由总周期要求、任务截止时间、实测执行时间以及必要的安全余量共同确定。

对于硬实时任务,仅仅证明平均执行时间低于周期是不够的。还需要评估最坏情况下的执行时间、任务阻塞、共享资源竞争和外部干扰。有限次数的测试可以发现问题,却不能在缺乏额外分析的情况下证明所有未来执行都不会超时。

4. 按照证据选择优化方案

当数据表明主站处理时间是主要瓶颈时,可以进一步分析过程数据规模、帧数量、驱动路径和主站实现;当控制计算时间明显增长时,应检查算法复杂度、重复计算和数据结构;当线程开始延迟增加时,应检查实时任务竞争、CPU 亲和性和内核调度行为;当锁等待时间占比明显时,应优先调整共享状态设计和临界区长度。

如果需要采用核心隔离,应明确要隔离的任务、CPU、相关中断和后台工作,并通过实际运行状态验证配置是否生效。核心隔离并不意味着 CPU 上的所有干扰都会自动消失,也不能消除跨核共享资源、内存访问和驱动路径带来的延迟。

在评估望获rtLinux或其他实时 Linux 平台时,也应使用相同的硬件、驱动、主站库、PDO 配置、轴数和测试负载进行对比。只有在相同测量口径下,才能判断不同系统配置是否改善了主站周期、控制任务时序或长尾延迟。

总结:单轴稳定、四轴超时,不能直接等同于 EtherCAT 带宽不足。轴数增加不仅会增加过程数据量,还会改变主站处理负载、控制计算成本、共享资源竞争和多轴同步关系。真正有效的排查方式,是把周期预算拆成可测量的阶段,通过逐步增加轴数、统一测试条件和记录长尾事件,确定哪个环节首先突破时间预算。

对于多轴实时控制系统,通信正常只是必要条件之一。只有主站能够按时完成数据交换、控制任务能够按时执行、各轴数据满足一致性要求,并且整个系统在规定负载下持续满足截止时间,才能认为多轴实时性真正达到了工程要求。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进