FOC 任务调度:时基、分层与编码器

一句话

FOC 的任务调度不是按”谁重要谁优先”排队,而是把所有任务锁相到 PWM 载波:载波是唯一时基,其余任务的频率都是它的整数分频,最内环必须在 内跑完、不许欠账到下一拍。

一、三条基本原则

原则含义违反的后果
时基唯一以 PWM 周期 为最小调度拍,不存在第二个独立时钟任务之间产生拍差,采样点与执行点错位
带宽分离外环带宽 ≈ 内环 ÷ (5~10),频率按同比例下切两个 PI 互相追相位 → 耦合振荡
硬截止电流环必须在 内跑完占空比更新迟到 → 转矩纹波,甚至过流

为什么不按"重要性"排优先级?

因为电流环的触发源不是软件,而是硬件链路(定时器 → ADC → ISR)。这条链路上插入任何调度器都会引入不确定延迟,而电流环要的恰恰是确定性。优先级排序解决的是”资源不够时牺牲谁”,解决不了”确定性”。

二、五层任务结构

频率谁触发做什么截止时间
硬件保护µs 级模拟比较器 / TIM Break直接封锁 PWM 输出,不经 CPU无软件参与
电流环ADC 转换完成中断Clarke → Park → 双 PI → 反 Park → SVPWM → 写 CCR
速度环电流环拍计数分频读编码器增量算转速,PI 输出
位置环速度环再分频位置 PI 输出
后台任务100 Hz ~ 1 kHz软定时 / RTOS 任务状态机、参数辨识、通信、故障记录软约束
graph LR
    B["后台任务<br/>100 Hz ~ 1 kHz"]
    P["位置环 · 1 kHz<br/>θm 转 ω*"]
    S["速度环 · 2 kHz<br/>Δθ / ΔT 转 Iq*"]
    C["电流环 ISR · 20 kHz<br/>ADC → Clarke → Park → PI → SVPWM"]
    H["PWM 载波 20 kHz<br/>+ 硬件保护"]
    P --> S
    S --> C
    C --> H
    B -.->|"参数 / 使能"| S

为什么频率必须拉开 5~10 倍

采样定理只要求 2 倍,但那只是保证”不混叠”。控制上要求内环频率 ≥ 外环 × 10:只有这样,外环穿越频率处的内环相位滞后才不显著,内环在外环眼里才能近似成”增益为 1、无延迟”的一个环节,两级 PI 才可能各自独立设计。拉开之后,带宽分离频率分频其实是同一件事的两种说法。

三、一个 PWM 周期内发生了什么

中心对齐载波下的固定顺序:

时刻事件由谁完成
计数器峰顶触发 ADC,采样相电流硬件
ADC 转换完成进入电流环 ISR硬件触发
ISR 执行中FOC 运算链软件
下一次下溢新的 CCR 生效硬件

从”电流真实值”到”它真正被作用”,中间隔着采样-执行延迟:

这不是软件写得不够快,而是”中心对齐 + 单次更新”的结构固有属性:采样在峰顶,运算在下降半周,新占空比要等下一次下溢才生效。

想缩短延迟

改用双次更新(下溢与溢出各更新一次 CCR),延迟可降到 ,代价是运算必须在半个周期内完成。

四、四种调度机制

机制怎么排适用场景
前后台(超级循环 + 中断)最内环进 ISR,其余全在 while 里轮询成本敏感的风机 / 水泵,无 OS
时基计数器软调度以 PWM 拍为 base tick,tick % N == 0 跑对应任务中小项目最常见
RTOS(FreeRTOS / RT-Thread / Zephyr)电流环留在中断,速度环 / 位置环 / 通信 / 状态机各建任务分优先级多外设并发、需要协议栈的产品
实时框架 / 协处理器待办队列 + 硬件分担(TI C2000 的 CLA、STM32 的 HRTIM)高带宽伺服、多电机

用了 RTOS 也不把电流环做成任务

RTOS 的 tick 通常是 1 ms(1 kHz),远低于 20 kHz,靠软件定时器根本触发不到那个精度;而任务切换引入的是”最坏情况不确定”延迟。RTOS 管的是外环和后台,最内环永远是中断的领地

典型的拍计数写法:

static uint16_t tick = 0;
 
void PWM_ISR(void)              /* 20 kHz,硬件触发,要求确定性 */
{
    /* 反馈:必须与采样锁存在同一瞬间 */
    ia = ADC_IA;  ib = ADC_IB;
    theta_e = enc_electrical_angle();
 
    /* 电流环:每拍必跑 */
    clarke(ia, ib, &i_alpha, &i_beta);
    park(i_alpha, i_beta, theta_e, &id, &iq);
    pid_calc(&pid_d, id_ref, id);
    pid_calc(&pid_q, iq_ref, iq);
    inv_park(vd, vq, theta_e, &v_alpha, &v_beta);
    svpwm(v_alpha, v_beta);
 
    /* 外环:只在整数分频的那一拍跑 */
    tick = (tick + 1) % 20;
    if (tick % 10 == 0) speed_loop();   /* 2 kHz */
    if (tick == 0)      pos_loop();     /* 1 kHz */
}

五、编码器:横切的数据源

编码器在分层图里”看不见”,因为它不是一层任务,而是被每一层消费的数据源。任务有周期和截止时间;数据源没有周期,但有时刻的瞬间性——“这个角度值对应的是哪一瞬间的转子位置”。

graph TD
    E["编码器 / 位置传感器<br/>增量 · 绝对值 · 旋变 · 霍尔"] --> D["角度解算 + 延迟外推<br/>θe = θm × p"]
    D --> C["电流环<br/>每拍都要 θe"]
    D --> S["速度环<br/>Δθ / ΔT 求转速"]
    D --> P["位置环<br/>θm 多圈绝对值"]
    D --> F["故障诊断<br/>断线 / 超速"]

瞬间性:角度晚读一拍就出事

机械角与电角度由极对数 联系:

角度每滞后一拍,电角度就偏掉:

代入一组实数:6 极对、3000 rpm,则电频率 Hz;20 kHz 载波下

Park 变换转错 后, 中有 的部分跑到 d 轴上去——它不产生转矩,只发热;转矩分量再少掉 。滞后两拍,误差翻倍。

症状不是"略不准"

角度晚读一拍,示波器上看到的是转矩纹波 + 额外铜损,而不是一个轻微的精度偏差。所以角度的采样瞬间必须与 的采样瞬间重合。

六、四类编码器

类型读取方式CPU 开销注意
增量式 ABZ定时器编码器模式硬件 4 倍频计数,ISR 只读 CNT近零低速时每拍脉冲太少,M 法失效 → 切 T 法或观测器;Z 相锁绝对零点
绝对值 BiSS-C / EnDat / 多摩川串行帧,需提前发起 + DMA 预取,峰顶取影子寄存器唯一”读取需要时间”的类型,同步裸读会吃掉大半个周期
旋变 + RDC解调在专用芯片里做,CPU 只读并行口抗恶劣环境最强,成本最高
霍尔仅 6 个 60° 电角度区间必须靠观测器插值成连续角度,否则低速完全不平顺

绝对值编码器的取舍

帧长(BiSS 几 µs,EnDat 几十 µs)与 20 kHz 载波冲突时,两条路:① 保持 20 kHz,改成”上一拍 ISR 尾部发起请求 + 影子寄存器 + 角度外推”;② 把电流环降到 10 kHz,让串行帧在周期内从容跑完。前者保带宽,后者保简单。

偏移角标定同样吃预算

增量式编码器不知道电角度零点,上电需进入 ALIGN 状态,用固定 d 轴电流把转子吸到已知位置,读回偏移量存进 Flash。这一步一次性的,但它跑在电流环的节拍里——属于状态机的活、电流环的算力。

无感方案

去掉编码器后”角度”并不会消失,它变成电流环内部的一个观测器(滑模 / 龙伯格),每拍都要算,直接吃 ISR 的时间预算。有感省算力、无感省硬件,调度的紧张程度正好反过来。

七、工程检查清单

关注点做法
执行时间预算电流环 ISR 占用 的 50~70% 以内;ISR 首尾翻转一个空闲 GPIO 用示波器读脉宽,或用 DWT 周期计数器
抖动ISR 内不做长循环;临界区(关中断)控制在微秒级,否则会推后 ADC 触发
共享数据、角度快照等跨层变量用双缓冲或单写单读 + 关中断保护,别在外环里读一个正被内环改的结构体
采样-执行延迟中心对齐单更新 ≈ ,双更新 ≈
多电机同一 MCU 用同一载波让两个 ISR 串行执行,保证相位一致;不要开两个不同频率的定时器
先定哪个数先定 PWM 频率—— 决定其余频率的上限与分辨率,反着来会被迫拉高载波、白烧开关损耗

八、常见陷阱

把角度读在慢速环里

在 2 kHz 的速度环里读编码器、再给 20 kHz 的电流环用,等于让电流环用 10 拍前的角度。低速时看不出,一上转速就是纹波和发热。

ISR 里做阻塞式串行读

绝对值编码器的帧 + CRC 校验直接写在 ISR 里同步等,会把周期吃穿。改成预取 + 影子值。

两个环带宽贴近

速度环与电流环同频、或只差 2~3 倍时会耦合振荡。检查内环频率是否 ≥ 外环 × 10。

九、待补充

  • 多电机共用载波时的相位分配方案
  • 观测器(滑模 / 龙伯格)的执行时间实测数据
  • 双次更新下的 CCR 写入时机与影子寄存器行为