1. 实时运动控制软件架构总览

运动控制软件与普通应用软件的根本差别,在于它必须对物理世界负责。一个插补周期晚到 200 微秒,可能只是界面卡顿;但在多轴联动加工中,同样的 200 微秒会让刀具偏离预定轨迹,在高速高精场景下直接表现为工件过切、振纹或伺服报警。因此,理解实时运动控制软件架构,不能从"分几层、用什么框架"开始,而应从"物理过程对软件提出了什么约束"开始。

三大目标:实时性、确定性、可扩展性

实时性指系统对外部事件或内部时基的响应必须在一个可接受的时间上限内完成。它不等于"快",而等于"不超时"。一个平均耗时 50 微秒、但偶尔抖动到 2 毫秒的插补任务,比一个稳定耗时 150 微秒的任务更危险,因为前者会破坏轨迹连续性。

确定性指相同输入在相同状态下产生相同的时间行为和相同的输出序列。确定性包含时间确定性与逻辑确定性两层:时间上,周期任务的执行时刻和抖动有界;逻辑上,同一段 G 代码、同一组参数,每次运行应得到一致的轨迹与状态迁移。实时性回答"会不会晚",确定性回答"晚多少、是否每次都一样"。

可扩展性指在不推翻既有实时路径的前提下,增加轴数、更换总线、接入新工艺或新伺服品牌的能力。它决定了架构的生命周期成本:如果每加一个轴都要改动插补内核,系统很快会退化为不可维护的定制代码。

三者存在张力。为追求极致确定性,常采用静态内存、固定周期、无锁数据交换;而可扩展性倾向于动态注册、插件化、抽象接口,这些都会引入额外分支与不确定性。架构设计的本质,是在明确的时间预算内为扩展性划定安全边界。

分层架构:把时间约束分配到不同层

典型实时运动控制软件可划分为四层,每层承担不同的时间尺度与职责:

  • 硬件与驱动层:伺服驱动器、编码器、IO、现场总线(EtherCAT、CANopen、PowerLink 等)。时间尺度为微秒级,由硬件与固件保证。
  • 实时内核与运动引擎层:插补、加减速规划、位置环/速度环接口、轴状态机。周期通常为 125 微秒至 1 毫秒,是确定性的主战场。
  • 任务调度与通信层:周期任务、非周期任务、事件任务的调度,实时与非实时域的数据交换。
  • 应用与工艺层:G 代码解析、工艺参数、HMI、日志、诊断。时间尺度为毫秒至秒级,允许非确定性。

分层的核心价值不是"看起来整齐",而是把硬实时约束收敛到尽可能小的范围内。应用层可以卡顿、可以动态分配内存,但绝不能让它阻塞插补周期。因此层间接口必须遵循两条规则:实时域到非实时域的数据传递采用无锁环形缓冲或双缓冲快照;非实时域到实时域的指令必须经过参数校验与预计算,在周期任务中只做确定性赋值。

实时运动控制软件分层架构与时间尺度 1 µs 125 µs 1 ms 10 ms+ 硬件与驱动层 伺服驱动器 · 编码器 · IO · EtherCAT / CANopen 从站 时间尺度:µs 级 · 由硬件与固件保证 · 抖动来源:总线周期、时钟同步 实时内核与运动引擎层 插补器 · 加减速规划 · 位置/速度环接口 · 轴状态机 · 前瞻 周期:125 µs ~ 1 ms · 确定性主战场 · 静态内存、无锁、固定执行路径 硬实时域 任务调度与通信层 周期任务 · 事件任务 · 非周期任务 · 实时/非实时域数据交换 无锁环形缓冲 · 双缓冲快照 · 优先级继承 · 时间预算分配 软实时/混合域 应用与工艺层 G 代码解析 · 工艺参数 · HMI · 日志 · 诊断 · 配方管理 时间尺度:ms ~ s 级 · 允许非确定性 · 动态内存、文件 IO、网络通信 ↓ 指令下发 ↑ 状态反馈 三大目标:实时性(不超时) · 确定性(抖动有界、行为可复现) · 可扩展性(轴数/总线/工艺可增)

任务模型:周期、事件与非周期

实时运动控制的任务模型通常由三类任务构成,它们共享 CPU 但拥有不同的时间契约。

周期任务是确定性的骨架。最典型的是插补任务,以固定周期 T 触发,例如 T = 250 微秒。它在每个周期内完成:读取编码器反馈、执行插补计算、输出位置指令、更新轴状态。周期任务必须满足最坏执行时间 WCET < T,且抖动 J 远小于 T。工程上常把 T 的 60%~70% 作为计算预算上限,剩余时间留给中断响应和调度开销。

事件任务由外部信号触发,如限位开关、探针捕获、报警输入。它们的时间要求通常是"从事件到响应"的端到端延迟有界,而非固定周期。探针捕获(Touch Probe)是典型例子:从探针信号跳变到锁存编码器值,要求延迟稳定在微秒级,否则测量重复性会恶化。

非周期任务包括参数下载、日志写入、HMI 刷新、诊断采集。它们不参与硬实时路径,但必须被隔离,防止其突发占用破坏周期任务的时序。常用手段是给非周期任务分配独立线程与较低优先级,并通过消息队列与实时域通信。

一个可参考的周期预算分配示例如下(示例参数,非实测):插补周期 250 微秒,其中插补计算 80 微秒、总线数据交换 60 微秒、状态更新与 IO 映射 30 微秒、预留余量 80 微秒。若某次测量发现插补计算达到 190 微秒,即使平均只有 80 微秒,也说明存在需要定位的抖动源,例如缓存未命中、动态内存分配或优先级反转。

硬件抽象:让实时路径与硬件解耦

硬件抽象层(HAL)的目标是让运动引擎不直接依赖具体伺服品牌或总线类型。它通常定义一组稳定的接口:read_encoder(axis)、write_position(axis, value)、read_io(channel)、write_io(channel, value)、get_axis_status(axis)。上层插补器只调用这些接口,具体实现由驱动适配器完成。

但硬件抽象不能以牺牲确定性为代价。常见错误是在实时路径中使用虚函数或动态分派,导致执行时间不可预测。更稳妥的做法是:在初始化阶段完成适配器绑定,运行期通过函数指针表或编译期模板静态分派;所有缓冲区在初始化时静态分配,运行期不做内存申请。

架构检查要点:实时路径上是否存在动态内存分配?是否存在可能阻塞的系统调用?是否存在无界循环?层间数据交换是否无锁?周期任务的 WCET 是否留有足够余量?这五个问题能快速暴露大多数确定性隐患。

从需求到架构的映射

把三大目标落到架构上,可以形成一条清晰的映射链:实时性要求周期任务可调度、WCET 有界,对应固定优先级抢占式调度与静态内存;确定性要求时间抖动有界、行为可复现,对应无锁数据交换、固定执行路径、参数预计算;可扩展性要求轴数、总线、工艺可增,对应 HAL 接口、驱动适配器、工艺插件。三者共同约束了分层边界与任务模型。

需要强调的是,架构不是一次设计完成的静态图纸。实际项目中,往往先以最小实时闭环(单轴、固定周期、直接 IO)验证时序,再逐步引入总线、多轴插补、前瞻与工艺层。每引入一层新能力,都应重新测量周期任务的最坏执行时间与抖动,确认时间预算仍然成立。这种"先闭环、再扩展、持续测量"的推进方式,比一开始就搭建完整分层更可靠。