FOC(Field Oriented Control,磁场定向控制)是当前无刷直流电机、永磁同步电机高性能驱动中最常用的控制方式。在很多项目中,STM32 因为外设资源丰富、生态成熟,成了实现 FOC 的主流选择。“狗哥FOC”这个项目主题在社区里讨论得很多,核心内容不外乎这几块:从零搭建 FOC 固件、双电阻电流采样、定时器触发 ADC、开环调试、无感反电动势角度估算,以及 STM32G4/H7 上的 CORDIC 加速。这篇文章就按一条完整的实践路径来写:先讲清楚 FOC 的组成,再准备硬件与采样方案,然后跑通开环,接着实现电流闭环,最后进入无感角度估算和排错。
在实际项目里,很多初学者并不是被 FOC 公式难住的,而是不知道代码从哪里开始写:什么时候该配置定时器,什么时候该采电流,电流采完之后又该送去哪里。这些问题的根子在于没有把“控制链路”和“外设触发链路”对应起来。只要先把这条链路梳理清楚,代码实现就是按步骤填充细节。
1. 先弄清楚 FOC 控制链路由哪些环节组成
1.1 方波控制和 FOC 的本质差异
无刷电机最简单的一种驱动方式是六步换相,也叫方波控制。它在一个电角度周期内,把三相桥臂分成 6 个扇区,每 60 度切换一次通电方向,每次让两相导通、一相悬空。这种方式实现简单,但电流不是连续正弦波,换相瞬间会产生转矩脉动,转速越高越明显。
FOC 的思路完全不同。它不是简单切换导通相,而是通过实时控制三相电流的合成矢量,让定子产生的磁场始终与转子磁场保持接近 90 度的正交关系。这样做的好处是力矩平稳、响应快、噪音低,可以在宽速度范围内做到精确控制。理解这句话很关键:FOC 的最终控制对象,不是某一相电压,而是“三相电流合成的矢量”。
为了做到这一点,FOC 需要三个东西:准确的转子角度、实时的相电流、足够快的控制周期。转子角度可以用编码器、霍尔或者无感算法得到;相电流通过采样电阻和 ADC 得到;控制周期一般做到 10kHz 到 20kHz。三者缺一不可。
1.2 FOC 最小闭环链路
一套完整 FOC 电流环,可以拆成下面这条链路:
母线电压和相电流采样 -> 三相电流重构 -> Clarke 变换 -> Park 变换 -> 电流 PI 调节 -> 反 Park 变换 -> SVPWM -> 三相逆变桥 -> 电机。
Clarke 变换把三相静止坐标系下的电流ia、ib、ic转换到两相静止坐标系ialpha、ibeta。Park 变换再把ialpha、ibeta转换到随转子旋转的d、q坐标系。在旋转坐标系下,id对应励磁分量,iq对应转矩分量。控制目标通常设定为id=0,通过调节iq来控制转矩。
| 变换 | 输入 | 输出 | 作用 |
|---|---|---|---|
| Clarke 变换 | ia、ib、ic | ialpha、ibeta | 把三相静止坐标转到两相静止坐标 |
| Park 变换 | ialpha、ibeta、角度 | id、iq | 把静止坐标转到旋转坐标 |
| 反 Park 变换 | Vd、Vq、角度 | Valpha、Vbeta | 把旋转坐标转回静止坐标 |
| SVPWM | Valpha、Vbeta | 三相占空比 | 生成逆变桥需要的开关信号 |
为什么非要做坐标变换?因为在三相静止坐标下,ia、ib、ic都是正弦变化量,PI 调节器很难做到无静差跟踪正弦信号。转到旋转坐标后,id、iq在稳态时都是直流量,用普通 PI 就能很好控制。这是 FOC 的核心思想。
1.3 热词背后对应 FOC 的哪个模块
社区里围绕 FOC 的热词很多,它们并不是并列的概念,而是落在 FOC 工程的不同层面。把热词归类到具体模块,有助于判断自己当前卡在哪一环。
| 热词 | 对应模块 | 说明 |
|---|---|---|
| FOC 算法 | 整体控制框架 | 坐标变换、PI 调节、SVPWM 的总称 |
| FOC 双电阻采样 | 电流采样 | 通过两相采样电阻重构三相电流 |
| FOC 双电机采样组扫描双触发 | ADC 采集链路 | 多电机场景下两组 ADC 分组扫描,PWM 双触发 |
| FOC 定时器触发 ADC 采样 | 外设联动 | 用定时器事件自动触发 ADC,避免 CPU 频繁干预 |
| stm32g4 cordic foc | 计算加速 | G4 系列 CORDIC 外设加速三角函数运算 |
| pmsm 无感 FOC | 控制模式 | 去掉位置传感器,用估计算法得到转子角度 |
| 无感 FOC 如何获取反电动势 | 角度估算 | 利用电机模型从电压电流估算反电动势 |
| FOC 代码开环 | 调试阶段 | 不给角度反馈,先按给定角度驱动电机 |
| FOC 硬件 | 驱动板 | 三相桥、采样电阻、驱动芯片、保护电路 |
理解这个分类后,你会发现 FOC 不是一个单一算法,而是一套软硬件协作系统。写代码之前,先确定自己做的是哪个层次:只做开环验证,做电流环闭环,还是直接上无感。
1.4 学习环境与生产环境的差异
学习环境追求快速跑通,生产环境追求稳定可靠。两者在 FOC 工程上的差异非常明显。
学习开发板通常用 48V 以下电源,配一个带采样电阻的驱动板,电机选小功率 PMSM 或 BLDC,调试时可以接编码器辅助观察角度。只要电流波形正常、电机能转、电流环不振荡,就算达到目标。
生产环境要额外考虑:母线电压检测、过流保护、过温保护、欠压保护、故障上报、参数标定、固件升级、日志记录、回滚方案。采样电阻的温漂、死区时间、PWM 占空比边界、开关噪声抗干扰能力,都会影响量产稳定性。文章后面给出的示例代码用于说明思路,实际工程要根据自己的主控型号、驱动板硬件和电机参数调整。
2. 硬件准备与采样方案:双电阻采样为什么是工程主流
2.1 一套 FOC 开发环境需要哪些硬件
准备硬件时不要只盯主控芯片,采样电阻的位置、驱动芯片的响应速度、电机的极对数和电阻电感参数都会影响控制效果。常见开发环境如下:
| 组件 | 作用 | 注意事项 |
|---|---|---|
| STM32 主控板 | 运行 FOC 算法 | G4 系列带 CORDIC,H7 系列算力更高 |
| 三相驱动板 | 把 PWM 信号变成电机三相电压 | 包含驱动芯片、MOS 管、采样电阻 |
| PMSM 或 BLDC 电机 | 被控对象 | 确认极对数、额定电压、额定电流 |
| 稳压电源 | 提供母线电压 | 建议带限流,避免误接线烧板 |
| 编码器或霍尔 | 调试期提供参考角度 | 无感调试时用于对比估算角度是否准确 |
| 示波器或电流探头 | 观察 PWM 和电流波形 | 排查采样时序和噪声的关键工具 |
2.2 单电阻、双电阻、三电阻采样对比
相电流采样是 FOC 的第一道关口。采样不准,后面所有控制都失去意义。三种方案各有取舍。
| 方案 | 原理 | 优势 | 局限 | 适用场景 |
|---|---|---|---|---|
| 三电阻采样 | 三个下桥臂各放一个采样电阻,采集三相电流 | 每周期信息丰富,采样窗口好安排 | 成本高,布线复杂 | 高性能伺服、大功率驱动 |
| 双电阻采样 | 采集两相电流,第三相由基尔霍夫电流定律计算 | 成本适中,主流方案 | 低调制区和高调制区存在采样盲区 | 大多数中小功率电机驱动 |
| 单电阻采样 | 在母线或下桥臂单点采样,通过 PWM 状态重构三相电流 | 成本最低 | 重构算法复杂,小占空比时采样困难 | 低成本家电、风扇类应用 |
双电阻采样之所以成为工程主流,是因为平衡了成本、算法复杂度和采样可靠性。只要 PWM 中心对齐模式下触发点安排合理,大多数工况都能采到有效的两相电流。
2.3 双电阻采样为什么要看 PWM 状态
双电阻采样不是任何时候都能采到电流。相电流只在下桥臂 MOS 管导通时流过对应采样电阻,如果某一相下管关断,该相采样电阻上的电流就是零。因此,采样窗口必须选在目标相下管导通的时间段内。
在中心对齐 PWM 模式下,计数器从 0 向上计数到 ARR,再从 ARR 向下计数回 0。一个完整的 PWM 周期包含两个三角波行程,分别在计数上溢和下溢附近存在有效的低端导通窗口。如果只用一次触发,可能正好落在某一相下管关断的区域,导致采到无效电流。
正确做法是:在 PWM 周期内选择两个采样点,分别采集 A 相和 B 相电流。这就是“一个 PWM 周期两次触发 ADC”的由来。某些驱动板会使用两组采样保持电路或两个 ADC 通道同步转换,目的都是在一个足够窄的时间窗口内完成两相电流采集。
2.4 CORDIC 在 FOC 里能做什么
FOC 坐标变换中有大量正弦、余弦计算。传统做法是用查表或标准数学库,在 20kHz 控制频率下,CPU 占用率会明显上升。STM32G4 系列和部分 STM32H7 系列集成了 CORDIC 外设,可以用硬件流水线计算三角函数、模长和相位。
使用 CORDIC 时要注意:不是所有型号都有该外设,落地前要查对应参考手册的数据手册外设清单。同时,CORDIC 计算结果是定标格式,需要和自己代码里的 Q 格式或浮点格式做转换。如果整个工程都使用单精度浮点,接入 CORDIC 反而会增加格式转换代码,需要根据实际 CPU 占用情况决定是否启用。
3. 第一步先跑开环:验证 PWM、定时器和 ADC 链路
3.1 开环调试的目标
无感 FOC 很难一上来就闭环成功。更稳妥的顺序是:先做开环,不给任何角度反馈,按照自己给定的角度和速度去驱动电机。开环目标有三个:验证 PWM 输出是否正常、验证定时器触发 ADC 链路是否正确、验证电机能否在给定电压下连续旋转。
开环阶段不需要编码器,也不需要无感观测器。只要三相电压按照设定电角速度旋转,电机就会跟着转。此时如果电机不转、抖动、反转或者电流异常偏大,说明 PWM 配置、相序或者采样链路有问题,应该先解决这些基础问题,再进入闭环。
3.2 定时器配置:中心对齐 PWM 与 ADC 触发
FOC 的 PWM 建议使用中心对齐模式。和边沿对齐相比,中心对齐模式下 PWM 波形对称,电流纹波较小,同时计数上溢和下溢都能作为 ADC 触发源,为双电阻采样提供了两个天然采样时刻。
下面是一段思路示例,实际初始化要根据主控型号、引脚和时钟频率调整:
/* 思路示例:配置 TIM1 为中心对齐模式,输出三相互补 PWM */ static void tim1_pwm_init(void) { /* 关闭定时器后再修改配置 */ TIM1->CR1 &= ~TIM_CR1_CEN; /* 中心对齐模式,选择模式 2 */ TIM1->CR1 |= TIM_CR1_CMS_1; /* 自动重装值,决定 PWM 频率 */ /* 频率 = 定时器时钟 / (2 * ARR) */ TIM1->ARR = PWM_ARR - 1; /* 三通道输出 PWM1 模式 */ TIM1->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; TIM1->CCMR1 |= TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; TIM1->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; /* 主输出使能 */ TIM1->BDTR |= TIM_BDTR_MOE; /* 启动定时器 */ TIM1->CR1 |= TIM_CR1_CEN; }不同系列芯片的寄存器位定义有差异,写代码前要对照参考手册确认。重点是理解:ARR 决定 PWM 频率,中心对齐模式让计数上溢和下溢成为对称采样点。
3.3 开环角度与电角速度的给定
开环时,控制器并不知道电机里转子的实际位置,它只是按照固定的电角速度累加一个角度,然后用这个角度生成三相电压。如果电机是永磁同步电机,转子会被定子磁场拖动旋转。
/* 开环角度累加 */ #define ELECTRICAL_ANGLE_PER_TURN 360.0f static float open_loop_speed_hz = 5.0f; /* 电频率,单位 Hz */ static float open_loop_angle_deg = 0.0f; static float dt = 1.0f / 20000.0f; /* 20kHz 控制周期 */ static void open_loop_tick(void) { open_loop_angle_deg += 360.0f * open_loop_speed_hz * dt; if (open_loop_angle_deg >= 360.0f) { open_loop_angle_deg -= 360.0f; } }这里的open_loop_speed_hz是电角度频率,不是电机机械转速。对于一个极对数为 4 的电机,电频率 5Hz 对应机械转速是 75 转每分钟。实际调试时,从很小的电频率开始,比如 2Hz 到 5Hz,确认电机能平稳转动后再逐步加大。
3.4 生成三相电压并输出给 SVPWM
有了开环角度,下一步就是根据角度生成三相正弦电压。最简单的做法是让三相电压相位差 120 度,然后送入 SVPWM 计算占空比。
/* 根据开环角度生成三相电压目标值 */ static void open_loop_voltage_generate(float angle_deg, float voltage_amp, float *va, float *vb, float *vc) { float a_rad = angle_deg * 3.14159265358979f / 180.0f; *va = voltage_amp * cosf(a_rad); *vb = voltage_amp * cosf(a_rad - 2.09439510239f); *vc = voltage_amp * cosf(a_rad + 2.09439510239f); }这段代码输出的是三相电压目标值,还不是最终占空比。要变成 PWM 占空比,还需要经过 SVPWM 计算。开环阶段也可以先用简化正弦 PWM,把三相反电势换算成占空比,验证链路后再升级为 SVPWM。
3.5 开环阶段的检查点
开环跑通后,不要急着进入电流环,先确认以下几点:
- 电机是否能连续旋转,而不是卡顿或抖动。
- 相电流波形是否接近正弦。用示波器电流探头观察最直观。
- 电机是否异常发热。过热说明电压幅值过大或相序存在严重不匹配。
- 旋向是否符合预期。如果电机反转,交换三相中的任意两相即可。
如果电机不转,先检查 PWM 是否有输出、死区时间是否合理、供电电压是否正常。很多人会忽略死区配置,死区太短容易烧桥,死区太长电流波形畸变大。开环是整个 FOC 工程的地基,这一步不要图快。
4. 双电阻采样与组扫描双触发:电流什么时候采最关键
4.1 为什么一个 PWM 周期需要两次触发 ADC
FOC 电流环需要每个控制周期都拿到id、iq,而电流是连续变化的。理论上每个 PWM 周期采集一次三相电流就够,但双电阻采样受到采样窗口限制:一次采样只能反映此刻某相下管导通时的电流,不同 PWM 状态下,各相电流在采样电阻上的可见性不同。
中心对齐 PWM 一个周期包含上溢和下溢两个特殊时刻。在这两个时刻附近,三相下管导通状态比较确定,适合安排 ADC 触发。一次触发采 A 相和 B 相,另一次触发同样采 A 相和 B 相,但角度位于 PWM 周期不同位置,可以互相验证,也能在低调制区弥补采样窗口不足的问题。
“双触发”的本质不是多采一次数据,而是让电流采样在 PWM 周期内更完整。有些实现会把两次采样结果做平均,也有些实现会在不同 PWM 扇区切换采样通道,保证不进入采样盲区。
4.2 双触发在代码里怎么设计
定时器触发 ADC 的意义是:ADC 转换不再依赖 CPU 一步步启动,而是由硬件事件自动发起。这样可以保证采样时刻和 PWM 精确对齐,减少软件延迟。
/* 思路示例:定时器触发两通道 ADC 注入组转换 */ /* 这里只描述事件关系,不绑定具体寄存器位 */ /* 触发源 1:定时器计数上溢 */ /* 触发源 2:定时器计数下溢 */ /* 注入组序列:通道 1 采 A 相电流,通道 2 采 B 相电流 */在代码结构上,可以写两个标志位,分别表示上溢触发完成和下溢触发完成。ADC 转换完成后进入中断,把两相电流保存到全局变量,并通知控制环可以开始计算。
volatile float phase_current_a; volatile float phase_current_b; void adc_injected_complete_isr(void) { uint32_t adc_val_a = adc_get_injected_result(1); uint32_t adc_val_b = adc_get_injected_result(2); phase_current_a = adc_to_current(adc_val_a); phase_current_b = adc_to_current(adc_val_b); }判断链路是否正确,最直接的方法是看 ADC 每次转换后,电流值是否落在合理范围。如果采到的数据跳变很大,基本可以确定是触发时刻和 PWM 状态不匹配。
4.3 组扫描双触发在多电机场景的含义
如果系统需要同时控制两个电机,就需要两套电流采样链路。一种做法是每个电机分配一组 ADC 和一组采样通道,一组 ADC 内使用扫描模式依次转换多个通道。
“组扫描双触发”可以这样理解:一个电机对应一组 ADC 序列,序列内扫描两相电流;每个电机由同一个 PWM 定时器的上溢和下溢双触发。这样两个电机在时间上是同步的,电流环计算也可以放在同一个控制周期内。
多电机实现时要注意 ADC 转换时间的分配。一个 ADC 规则的扫描转换是串行的,如果两路电流加上其他检测通道,总转换时间不能超过 PWM 周期。超出时要么提高 ADC 时钟,要么把检测项拆分到不同周期,避免阻塞控制环。
4.4 电流采样值如何变成 FOC 可用的 id、iq
ADC 读到的原始值不是电流,需要经过两步转换:零点校正和标幺化。
零点校正解决的是硬件偏置问题。即使电流为零,ADC 通道也会有接近 1.65V 或者 2.5V 的直流偏置,这取决于运放电路设计。上电后先采样一组数据作为零点偏移,后续每次采样都减去这个偏移。
标幺化解决的是量纲问题。把 ADC 值换算成实际电流值,再换算成软件中的标幺值,可以让 PI 参数不随硬件量程变化。
/* 思路示例:ADC 原始值转标幺电流 */ #define ADC_FULL_SCALE 4095.0f #define CURRENT_SCALE 10.0f /* 满量程对应电流, 根据硬件调整 */ static float adc_offset_a; static float adc_offset_b; float adc_to_current(uint32_t adc_val) { float current = (adc_val - adc_offset_a) / ADC_FULL_SCALE * CURRENT_SCALE; return current; }零点校正完成后,再用 Clarke 和 Park 变换把两相电流变成id、iq。注意第三相电流不需要额外采样,直接按ic = -ia - ib计算即可,前提是采样值确实是同一个 PWM 周期内的瞬时值。
4.5 采样链路的排查顺序
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 电流值跳变很大 | 采样时刻落在下管关断窗口 | 用示波器对比 PWM 和 ADC 触发信号 | 调整触发时刻或切换采样通道 |
| 电流值固定偏移 | 运放零点未校正 | 用万用表测运放输出静态电压 | 上电后执行零点校正流程 |
| 两相电流不对称 | 采样电阻或者运放增益不一致 | 分别测试两个通道的静态电压 | 做通道增益标定 |
| 高频噪声严重 | 采样时刻靠近开关边沿 | 调整死区或增加 RC 滤波 | 把触发点移动到 PWM 中部 |
5. 闭合电流环:从开环切换到 FOC
5.1 电流环的控制结构
开环跑通后,电流环的任务就变得很清晰:每个控制周期里,采样相电流,经过坐标变换得到id、iq,和给定值id_ref、iq_ref比较,PI 调节器输出Vd、Vq,再经过反 Park 变换得到Valpha、Vbeta,最后通过 SVPWM 生成占空比。
在表贴式永磁同步电机中,通常采用id_ref = 0策略。这样定子电流主要用来产生转矩,电流利用率最高。iq_ref就是转矩指令,由速度环或者上位机给出。
电流环的响应速度直接决定整个系统的动态性能。常见控制频率是 10kHz 到 20kHz。频率太高会增加 CPU 负担,频率太低会导致电流波形畸变。
5.2 一个可运行的电流环代码骨架
下面是一个控制周期内的处理函数,它把采样、变换、PI、输出串在一起。实际工程中还要加母线电压前馈、死区补偿和保护逻辑。
/* 电流环主函数, 假设在 20kHz 定时器中断中调用 */ static float id_ref = 0.0f; static float iq_ref = 0.0f; static PiController id_pi = {0.05f, 1.0f, 0.0f}; static PiController iq_pi = {0.05f, 1.0f, 0.0f}; void current_loop_tick(float rotor_angle_deg) { float ia = phase_current_a; float ib = phase_current_b; float ic = -ia - ib; /* Clarke 变换 */ float ialpha = ia; float ibeta = (ia + 2.0f * ib) * 0.577350269f; /* Park 变换 */ float angle_rad = rotor_angle_deg * 3.14159265f / 180.0f; float cos_a = cosf(angle_rad); float sin_a = sinf(angle_rad); float id = ialpha * cos_a + ibeta * sin_a; float iq = -ialpha * sin_a + ibeta * cos_a; /* 电流 PI 调节 */ float vd = pi_run(&id_pi, id_ref - id, DT_SEC); float vq = pi_run(&iq_pi, iq_ref - iq, DT_SEC); /* 反 Park 变换 */ float valpha = vd * cos_a - vq * sin_a; float vbeta = vd * sin_a + vq * cos_a; /* SVPWM 输出 */ svpwm_apply(valpha, vbeta); }这段代码是典型的 FOC 电流环骨架。每个函数的输入输出都很清晰,方便单独测试。调试时可以先固定iq_ref为一个很小的值,观察电机是否能稳定出力。
5.3 零点偏移和标幺化对电流环的影响
电流环的效果高度依赖采样精度。零点偏移没有校准好,会导致id、iq始终包含直流偏置,PI 输出会一直修正这个错误,最终表现为电机静止时有嗡嗡声、空载电流偏大。
标幺化不统一,会导致套用别人的 PI 参数时完全失效。不同硬件满量程电流不同,同样一个 PWM 占空比对应的电流值不同,PI 参数自然无法直接迁移。建议在代码注释里写清楚硬件量程和换算系数。
校准零点偏移的常用做法是:电机不转、桥臂不输出时,连续采样几十次电流通道,取平均值作为偏移值。这样能把运放温漂和电源噪声的静态部分抵消掉。
5.4 PI 参数怎么调
电流环 PI 调试遵循“先比例、后积分”的顺序。先给一个很小的比例系数,观察电流响应是否跟得上给定值;加大比例直到电流出现轻微振荡,再回退一点;然后逐步加积分,消除稳态误差。
| 调试目标 | 调整方式 | 现象 |
|---|---|---|
| 响应慢 | 增大 Kp | 电流跟踪更快 |
| 振荡 | 减小 Kp 或 Ki | 电流波形出现高频抖动 |
| 稳态误差大 | 增大 Ki | 给定和反馈之间长期存在偏差 |
| 积分饱和 | 限制积分上限 | 输出长时间顶在限幅值 |
调试时不要同时改多个参数。建议先固定id_ref = 0,只调节iq_ref,观察一相电流波形和iq跟踪情况。id、iq两个环结构相同,参数可以先用同一组,再根据实际响应微调。
6. 无感 FOC:反电动势、有效磁链与角度估算
6.1 为什么无感方案越来越常见
编码器和霍尔传感器会增加成本、体积和接线复杂度,而且在振动、高温、油污等恶劣环境下可靠性下降。无感 FOC 通过电机的电压电流模型估算转子角度和速度,省去位置传感器,因此在许多家电、风机、无人机和车用电机场景中成为首选。
无感方案的难点在于:电机静止和极低速时,反电动势信号很小,角度估算精度差。因此无感 FOC 通常需要特殊启动策略,比如开环拖动到一定转速后再切换到闭环角度估算。
6.2 有效磁链是什么,怎么计算
无感 FOC 中经常提到“有效磁链”,它和反电动势是同一个物理量的两种表达。反电动势是磁链随时间的变化率,数学上可以写成:
反电动势 e = dψ/dt
有效磁链 ψ 可以通过对反电动势积分得到:
ψ = ∫(u - R·i) dt
其中u是相电压,R是相电阻,i是相电流。从电压模型中去掉电阻压降,剩下的就是反电动势,再积分得到磁链。转子角度可以从磁链在αβ坐标系中的方向推算出来。
下面是一段思路示例,忽略电感压降和饱和效应,实际工程中还需要滤波和补偿:
/* 思路示例:通过电压电流积分估算有效磁链 */ static float psi_alpha = 0.0f; static float psi_beta = 0.0f; void flux_observer_update(float u_alpha, float u_beta, float i_alpha, float i_beta, float rs, float dt) { float e_alpha = u_alpha - rs * i_alpha; float e_beta = u_beta - rs * i_beta; psi_alpha += e_alpha * dt; psi_beta += e_beta * dt; }这个思路的关键是电压和电流必须精确。电压可以从 PWM 占空比和母线电压重建,电流来自双电阻采样。电阻R会随温度变化,如果估算角度在中高速段一直有偏差,可以先怀疑电阻参数是否准确。
6.3 反电动势估算角度的方法对比
| 方法 | 原理 | 优点 | 局限 |
|---|---|---|---|
| 反电动势过零检测 | 检测不导通相反电动势过零点 | 简单,适合方波控制 | 转矩脉动大,不适合高性能正弦控制 |
| 反电动势锁相环 | 用反电动势相位锁定转子角度 | 实现适中,中高速稳定 | 低速时信号小,容易失锁 |
| 滑模观测器 | 构造滑模面估计反电动势 | 鲁棒性强 | 存在抖振,需要滤波 |
| 扩展卡尔曼滤波 | 对电机状态做最优估计 | 精度高 | 计算量大,调参复杂 |
| 磁链积分法 | 对反电动势积分得到磁链角度 | 概念直观 | 积分漂移,需要补偿 |
在中高速段,反电动势锁相环和磁链观测器都是常用方案。低速段则普遍采用开环拖动或高频注入等策略,等反电动势信号足够大了再切入闭环。
6.4 无感切换闭环的调试思路
无感 FOC 调试不建议一步到位。比较稳妥的路径是:
第一步,用开环角度拖动电机到一定转速,比如 500 转每分钟以上。第二步,让观测器在后台运行,持续计算估算角度。第三步,把估算角度和开环角度做对比,如果两者误差很小,并且估算速度稳定,再把控制环切换到估算角度。
切换时要注意两个角度之间的连续性。如果开环角度和估算角度相差 180 度,切换瞬间会产生很大的电流冲击。解决办法是在切换前做角度对齐,把开环角度强制设为估算角度,或通过锁相环缓慢过渡。
观察估算角度是否准确,最简单的方法是带着编码器调试。把编码器读取的实际角度和观测器估算角度同时打印出来,用串口或绘图工具比较。如果两条曲线在中高速段基本重合,说明观测器参数基本合理。
7. 常见问题与可复用排查清单
7.1 排查顺序
FOC 问题排查很像排查一台不工作的机器:先确认基础层,再深入算法层。推荐的排查顺序是:输入电压是否正常,PWM 是否输出,ADC 是否采到有效电流,电流环是否震荡,角度估算是否准确,最后检查控制参数和逻辑。
很多时候问题不是出在算法,而是出在外设配置。例如电机抖动,可能是 PWM 三相没有按正确相序接到驱动板;电流波形异常,可能是 ADC 触发时刻不对。不要一上来就怀疑无感观测器参数,先把信号链路的每一环确认好。
7.2 常见问题现象与处理方案
| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 开环电机不转 | PWM 未输出或相序错误 | 示波器查三相输出 | 确认主输出使能、死区配置;交换任意两相 |
| 电机抖动 | 开环电压频率太快或电压幅值太低 | 降低电频率,增大电压 | 从 2Hz 起步,逐步增大电动功率 |
| 电流采样跳变 | 采样窗口落在无效区域 | 对比 PWM 与 ADC 触发时刻 | 调整触发点或增加二次触发切换 |
| 电流环振荡 | PI 比例系数过大 | 录制电流波形观察高频抖动 | 减少 Kp,检查积分限幅 |
| 无感切闭环失败 | 切换角度不连续 | 打印开环角度和估算角度 | 切换前做角度对齐或缓慢过渡 |
| 估算角度漂移 | 磁链积分漂移或 R 不准 | 对比编码器角度 | 加入高通滤波或角度锁相环补偿 |
| 双电机采样异常 | 两组 ADC 通道串扰 | 分别屏蔽一组测试 | 错开转换时刻,检查通道复用配置 |
这组问题覆盖了从开环到无感的主要卡点。每个问题的关键都是先定位到具体链路,再针对性处理。
7.3 上电前的可复用检查清单
| 检查项 | 具体内容 | 确认结果 |
|---|---|---|
| 供电 | 母线电压符合电机额定值 | 已确认 |
| 相序 | 电机三相线和驱动板输出一一对应 | 已确认 |
| 限流 | 电源限流打开,防止短路大电流 | 已确认 |
| PWM 频率 | ARR 和时间基准计算正确 | 已确认 |
| 死区时间 | 大于驱动芯片最小死区要求 | 已确认 |
| ADC 触发 | 上溢/下溢触发已配置 | 已确认 |
| 零点偏移 | 无输出状态下已采集 | 已确认 |
| 电流方向 | 采样电阻正方向和软件符号一致 | 已确认 |
| 控制周期 | 中断频率和 dt 一致 | 已确认 |
| 保护值 | 过流阈值和过压阈值已设置 | 已确认 |
上电前按这个清单逐项打勾,可以避免大多数基础问题。尤其是“电流方向”和“采样电阻正方向”,在硬件设计和软件方向不一致时,电流环会表现为正反馈,现象很迷惑。
8. 工程化落地的几条建议与下一步扩展方向
8.1 把参数外置,把保护加上
FOC 工程很容易被写成一个只有开发者自己能看的单文件程序。进入多人协作或量产阶段,必须把参数外置。电机极对数、相电阻、相电感、额定电流、母线电压、PI 参数、过流阈值,应该统一放到配置结构体中,通过配置文件或上位机标定写入。
保护逻辑不能等到出问题再补。母线过压、欠压、过流、过温、堵转、失速,每一项都要有明确的触发条件和恢复策略。过流保护建议放在 PWM 硬件比较器或 ADC 中断里,软件主循环里的保护响应太慢,可能已经烧毁功率管。
8.2 代码结构建议:从单文件到模块化
早期调试可以先用一个.c文件把链路跑通,但后续要拆成模块。常见的模块划分是:电流采样模块、坐标变换模块、PI 调节器模块、SVPWM 模块、观测器模块、通信模块、保护模块。
每个模块尽量只通过接口函数交互。例如电流采样模块只负责输出ia、ib,不直接修改 PI 参数;SVPWM 模块只接收Valpha、Vbeta,不主动关断 PWM。这样每个模块都能单独测试,也方便移植到不同主控。
8.3 下一步可以研究的方向
把基础 FOC 跑通之后,扩展方向很多:
- MTPA(最大转矩电流比)控制:在内置式永磁电机中提高效率。
- 弱磁控制:在超出额定转速后继续升速。
- 死区补偿和母线电压前馈:改善低转速和小电流下的波形质量。
- 参数辨识:自动测量相电阻、相电感和磁链常数。
- 多电机同步控制:在双电机场景下做同步误差补偿。
- 状态观测器对比:深入研究滑模观测器、龙伯格观测器、扩展卡尔曼滤波的差异。
FOC 的学习路径并不短,从开环到电流环再到无感,每一步都会碰到看起来像算法问题、实际却是外设配置问题的坑。不要怕踩坑,关键是踩坑之后能沿着“电压、PWM、电流采样、角度估算、控制参数”这条链路定位问题。把每个环节都验证清楚了,一套可复现的 FOC 工程才能真正立起来。