FOC 参考手册 06 工程实现

软件架构

控制算法是驱动系统中最容易进行理论分析、却最难保持简洁的部分。它只是几百行算术运算,不涉及任何 I/O——但在大多数固件中,它最终会被紧密耦合到厂商 SDK、定点库、RTOS 和显示栈上,此时若想在没有电机的试验台上对其进行测试已变得不可能。

本页介绍了一种避免上述问题的软件架构,它并非抽象的理论构想,而是提炼自一台实际运行的驱动器:由 MCX-N947 驱动 Teknic PMSM,在逐步从 NXP SDK 示例中剥离出独立于厂商的控制库的过程中,每一步之间电机都保持正常运转。

三层架构,以及关于依赖方向的铁律

控制律居中的三层架构:应用层向下指向它,端口层向上指向它,且只有端口层涉及具体芯片名称。 无芯片厂商头文件,无 I/O 调用 API 实现接口 同一接口契约 唯一含厂商 SDK 的代码 app/ 任务调度、UI、日志、整定——产品应用层 core/ 坐标变换、PI、调制器、观测器、状态机 port/<chip> 采样、驱动、位置、计时——仅此四项 芯片厂商 SDK 底层驱动、时钟配置、寄存器头文件 port/sim 本网站在此仿真层上运行
控制律位于软件栈的中间层,而非最底层。地基是建筑物所依托之物;而控制律无所依托,因此所有依赖箭头都汇聚于它,而非穿过它。端口层的箭头是整个架构中唯一指向外部的箭头——那唯一一条向外的连线,就是驱动器对特定芯片硬件所知的全部。

依赖关系的箭头全部指向内部,正是这一条约束条件支撑起了其他所有部分。核心代码不能包含厂商提供的头文件,因此不会引入对芯片的依赖。它没有I/O接口,所以可以在任何地方运行——包括在你的笔记本电脑上以实际速度的一万倍进行单元测试。

这并非一种假设性的优势。本网站本身就是这一测试的实例。 此处的每一幅交互图表都在浏览器中对电机模型运行着完全相同的闭环控制算法,因为控制代码与它通常所驱动的硬件是完全解耦的。标有 port/sim 的模块并非示意图,而是这些网页实际正在执行的模块。

在试验台上 迁移顺序比最终架构更重要。试图一次性剥离出整个控制库,只会得到一份从未跑通的固件。分步推进——先抽离纯数学变换,再定义硬件接口契约,接着迁移电流环,最后迁移状态机——每完成一步都在实验台上实测运行,这样一旦出现回归问题,可疑原因就只有一个。在上述实验台上,第 3 步先在后台证明了新调制器与厂商原版调制器的输出偏差不超过 0.009%,随后才允许它真正接管逆变桥驱动。

端口契约就是整个设计

所有与平台相关的内容都归结为少数几个操作:

操作核心层所需内容
测量来自当前周期的两相电流与直流母线电压
驱动三个占空比,以相干方式施加
位置编码器计数及索引,若存在的话
时间控制周期,以数值表示

仅此而已。无需初始化,无需时钟配置,无需中断优先级——这些都属于应用程序的职责范围,将它们排除在接口之外,才能防止端口层膨胀成第二个 SDK。

在移植任何代码之前,先明确这个接口规范。这也是实现第二个版本几乎无需额外成本的地方:通过一个基于电机模型的仿真端口为核心模块提供输入,从而无需搭建硬件平台即可验证控制算法。

两个时钟域,以及它们各自的职责

驱动器中有两个时钟,混淆它们是导致神秘行为的常见原因。

快速路径:每个 PWM 周期在中断服务程序中执行一次。 依次采集电流、执行 Clarke 与 Park 变换、运行两个电流 PI、解耦、逆 Park 变换、调制并写入占空比。仅做这些;不要在中断里分配内存、记录日志或等待。实测这部分耗时约 12 µs,而 PWM 周期为 62.5 µs;这是在该硬件上测得的数值,不是模型估计。

慢速路径:在任务或主循环中执行,周期通常为 1–10 ms。 速度环与位置环、斜坡发生器、故障处理策略、用户界面、遥测。这些模块的运行频率只需与机械系统的实际响应能力相匹配,它们都不应放在 16 kHz 的高频中断中执行。

两者通过简单的共享状态进行通信——一方传递命令,另一方传递测量值的小型结构体。如果这一边界需要互斥锁,则说明划分的位置不当。

同一套控制律,两种调度方式

这里有一个常被当作个人偏好来讨论的部分,但事实并非如此。在实时操作系统(RTOS)下和在裸机超循环中,快速控制环路完全相同:两者都是通过中断实现的,在两种情况下都会抢占所有其他任务,并且包含完全一致的计算逻辑。磁场定向控制中没有任何部分需要调度器。

不同之处在于底层结构,而且这种差异并非仅是形式上的。

RTOS 架构下:顶层为控制 ISR,其下是周期性控制任务,再往下是 UI 任务,中间通过一个共享命令块通信。 测量反馈 参考给定 只读 ADC 转换完成 硬件触发,每 62.5 µs 一次 控制中断(ISR) 仅执行电流环——无内存 分配、无阻塞等待 共享命令块 普通结构体,每个字段 唯一写入者 慢速控制任务 速度环、斜坡、故障策略 —— 1 kHz 定时器触发 UI 与遥测任务 最低优先级,允许 长时间运行
三个优先级。控制中断不是 RTOS 任务,也永远不应当成为任务。在其之下,有一个周期性任务负责处理允许延迟一毫秒的工作;再往下,则是所有允许延迟一秒的工作。命令块只是一个普通结构体,每个字段有且仅有一个写入者——一旦这个边界需要加锁,就说明快慢速率之间的职责划分出了问题。
裸机架构下:顶层仍是同一个控制 ISR,底层的单一主循环以相同优先级承载慢速控制任务与所有后台工作。 测量反馈 参考给定 随后依次执行 下一轮主循环 ADC 转换完成 硬件触发,每 62.5 µs 一次 控制中断(ISR) 唯一关键的高频中断 —— 控制代码完全相同 共享命令块 普通结构体,每个字段 唯一写入者 主循环(Main loop) 慢速控制逻辑,等主循环 转到这里时才执行 其他所有后台工作 UI、遥测、通信 —— 处于同一优先级
在同一张图上绘制,因此只有差异部分会变化。上半部分完全相同。而在其下方,只有一个优先级,而不是两个,于是慢速控制工作与其他所有任务共享该优先级——并且慢环的周期不再是一个由任何人配置的数值。此时,它的执行时间将取决于整个循环的实际耗时。

最后一句话道出了全部权衡,且这一权衡是可测量的,而非主观争论的问题。给两个模型分配相同的中断、相同的慢速任务以及相同的后台处理块,观察它们各自的表现:

快速环路
慢速环路
顶部:按真实时间比例绘制的一个 PWM 周期,在两种调度模型下完全相同——中断会先于任何其他代码抢占属于它的 CPU 时间。底部:同一驱动器在两种模型下运行数毫秒的时序轨迹,每次慢速环路触发执行时均标有刻度。将 后台任务 滑块向上拖动,FreeRTOS 的慢环触发点纹丝不动,而裸机超循环的触发间隔则会按比例被拉宽;将其向下拖到足够小时,超循环的执行频率反而会超过设定速率——因为超循环根本没有“固定速率”的概念,它只有循环耗时。

在默认设置下——16 kHz 开关频率、12 µs 中断耗时、1 kHz 慢速环路以及 3 ms 的后台任务块——两种模型下中断均占用 19.2% 的 CPU 资源。RTOS 准确交付了 1 kHz 的慢环频率;而超循环实际仅交付了约 266 Hz,因为主循环跑完一圈的 3.04 ms 工作量被高频中断拉长到了 3.76 ms 的实际挂钟时间。系统并没有坏,只是这个速率从一开始就不存在调度保障。

在试验台上 这并非思想实验。在本页面所描述的试验台上,转速 参考信号发生器 在用户界面任务中以标称 10 Hz 运行。 实测表明,在图表绘制负载下,该任务的实际运行频率仅为 2–3 Hz,因此,当指令要求 500 rpm/s 的加速度时,实际输出约为 80 rpm/s——驱动系统的响应显得迟缓,而这与电机、控制参数或机械本身无关。

解决方案并非加快 UI 任务,而是将参考信号生成及外环控制移入控制中断内,使执行频率成为硬件定时器的固有属性,而不再受显示任务繁忙程度的影响。在动念引入 RTOS 之前,这一点值得深思:提升任务的执行层级,往往比升级调度器更为经济。

因此:只有当产品中的其他部分确实需要时才使用实时操作系统——比如网络栈、文件系统,或者那些会阻塞的图形库。不要为了使控制环路具有确定性而引入 RTOS。控制环路本来就是确定的,而且这种确定性是由中断向量而非调度器决定的。

逆变器的唯一所有者

本页面背后那台设备上最难解决的 bug 就是因为违反了这一规则: 两段代码在同一中断中写入占空比寄存器,而硬件的重新加载机制决定了哪个写操作生效。表现出来的现象是根本不可能发生的物理现象;原因在于所有权问题,而非算术错误。

因此,应将其设计为结构化方式。仅有一个函数负责写入占空比。所有希望影响逆变器的模块都向该函数返回数值,而非直接操作外设。对于存在两个写入者的驱动系统,仅靠合理安排两者的执行顺序并不能保证正确性——请参阅 外设页面,其中逐周期展示了相关机制,您可以观察是硬件而非您的代码决定了最终结果。

掌管状态转换的状态机

驱动器的故障往往出现在模式切换时,因为真正值得关注的失效发生在状态之间的过渡阶段,而不是稳定状态下:比如在ADC仍在校准时就进行对齐,在错误的角度从开环切换到闭环,或者在电机高速运转时断开接触器并产生再生电流。

驱动器的模式状态机:从 BOOT 到 READY、CALIBRATE、ALIGN 直至 RUN,任何状态均可跳入 FAULT,并通过斜坡降流路径返回 READY。 初始化完成 启动指令 偏置校准完成 初始角度已知 斜坡减速停机 无硬件保护 防护触发跳闸 故障清除 BOOT 时钟、外设初始化 与上电自检 READY PWM 输出禁用, 等待启动 CALIBRATE ADC 零漂校准, 逆变桥空闲 ALIGN 将转子拉至已知角度 FAULT 安全关断输出, 锁存故障原因 RUN 电流闭环 正常运行
图中仅绘制了两条进入 FAULT 状态的路径;在实际的监控器中,每个状态都会退出到它,并且每条这样的退出路径都会在切除调制之前对电流进行衰减。该图中的两条箭头分别标注了其所修复的缺陷:偏移量,因为监控器曾于校准尚未完成时启动,并将电流注入到其自身的零点读数中;停止,斜坡式,因为在额定转速下发出的阶跃零速指令会反馈至直流母线,导致过电压保护动作。

一个小型的显式模型,所有状态转移都集中在一个文件中,其价值远胜于任何单一模式的复杂性。标题中提到的两次故障实际上都是状态转移错误,而非控制错误,且在任何一个单独状态的代码中都无法发现。

异常防护与独立见证

有两件事值得在一开始就予以实现,而不是在出现问题后再添加。

异常防护(Guards)。 带有观测器的驱动系统可能会发生“自我投毒”:在试验台上,一次停滞的无感启动曾在观测器中产生了一个 NaN(非数值),该值随后传播到电压指令中,导致驱动器悄无声息地死锁,直到重新上电才恢复。在快速环路中增加非有限值(NaN/Inf)检查,一旦发现便立即清零 PI 积分器并强制触发故障状态——这只需寥寥数条指令,却能将一起离奇的死机转化为明确的故障码。

独立见证(Witness)。 保留一项控制律完全不使用的独立测量值。在无位置传感器驱动器中,编码器就是最天然的见证者——当观测器自信满满地报告电角速度为健康的 435 rad/s,而编码器计数值却纹丝不动时,你立刻就能判断是谁在撒谎。未被控制环使用的“真值”绝非浪费;它是唯一能够拆穿一个过度自信的估算器的铁证。

要点回顾

  • 使所有依赖关系均指向内部,这样控制律便可在无需硬件的情况下进行测试。
  • 保持端口契约:测量、驱动、定位、计时。
  • 快速环仅执行电流环控制,其余较慢的任务则交由一个任务或循环处理。
  • 电流环无需RTOS。真正需要RTOS的是那些耗时较长的任务,而且只有当某些操作确实会导致环路周期过长时才需使用——在超循环模式下,慢周期就是环路周期,而非一个单独的设置。
  • 占空比寄存器有且仅有一个写入者,并从软件结构上强制保证这一点。
  • 将状态切换逻辑集中处理,防范非有限值,并保留一项虽未使用但必要的测量数据。