做无感 FOC 电机驱动时,很多人一上来就拼 PID 闭环,结果电机不是抖就是转不起来。实际工程里更稳妥的顺序是:先跑通开环控制,验证电流采样、坐标变换、SVPWM 输出、逆变器驱动链路都正常,再切入闭环。这篇文章就把 FOC 开环控制的原理、实现步骤、代码结构和排查方法完整过一遍,适合正在学习 FOC 算法、准备调无感 FOC、或者刚接触电机控制但被坐标变换劝退的开发者。看完之后你能独立写出一版可运行的 FOC 开环控制代码,也知道下一步切闭环时会在哪些环节出问题。
1. 先搞清楚 FOC 开环控制解决什么问题
1.1 从 FOC 控制框图看闭环和开环的边界在哪
FOC 全称 Field Oriented Control,磁场定向控制。它的核心思想是把三相交流电机的定子电流,通过坐标变换分解成两个独立的直流分量:一个负责产生磁场,一个负责产生转矩。映射到 dq 坐标系里,就是 Id 和 Iq。控制目标从“让三相电流按正弦变化”变成“让 Id 和 Iq 稳定在期望值”,前者很难直接调,后者可以套用一个又一个 PI 调节器。
完整的 FOC 闭环系统通常有三个环:电流环、速度环、位置环。电流环在最里面,采样三相电流,经过 Clarke 变换和 Park 变换得到实际的 Id、Iq,然后和给定值做差,经过 PI 控制器输出 Ud、Uq,再经过逆 Park 变换、逆 Clarke 变换和 SVPWM 生成六个 MOS 管的开关信号。速度环和位置环只是把目标转速或目标角度转换成电流环的 Iq 给定值。
开环控制则把这条链路上的反馈环节拿掉了一部分。它不依赖电流反馈,也不依赖速度反馈,只是按照人为设定的角度和电压,直接生成三相电压。系统框图大概是这样的:目标转速经过积分产生一个不断累加的电角度,给定的 Id、Iq 或者给定幅值的电压矢量,经过逆 Park 变换、逆 Clarke 变换、SVPWM,直接输出到逆变器。
从控制结构上看,开环和闭环的差别主要在两点:
| 对比项 | 开环控制 | 闭环控制 |
|---|---|---|
| 是否采电流 | 可以采也可以不采 | 必须采 |
| 是否用 PI 控制器 | 不需要(或只用简单限幅) | 电流环 PI、速度环 PI 都需要 |
| 电角度来源 | 程序内部累加生成 | 编码器/观测器估算 |
| 负载变化响应 | 转速跌落,无法自动修正 | 电流环自动补偿转矩 |
| 实现难度 | 低 | 高 |
开环控制的意义不在于“能用它驱动大负载”,而在于把 FOC 链路里的所有计算环节打通。写代码的时候,逆 Park 变换、逆 Clarke 变换、SVPWM 这些数学模块和闭环一模一样;唯一区别只是电角度从哪来、Ud/Uq 怎么定。
1.2 为什么做无感 FOC 要先跑通开环
无感 FOC 指的是没有编码器、没有霍尔传感器,全靠相电流和电压信息估算转子位置。但转子位置估算器是基于电机模型的,模型准不准、反电动势观测能不能收敛,取决于电流采样是否干净、电压输出是否对称、SVPWM 是否有明显畸变。这些基础问题如果不先解决,直接上无感算法,你根本分不清问题是出在观测器,还是出在采样链路。
开环阶段虽然不依赖位置反馈,但你可以用程序生成的理想电角度,去验证下面的链路是否通畅:
- 母线电压读取是否准确,这直接影响 SVPWM 输出幅值。
- 三相电流采样是否零点偏置、是否有波形畸变。开环转起来后用示波器或者 ADC 采集看电流波形,能发现采样电阻布局和放大电路的问题。
- 逆变器三个半桥的互补 PWM 和死区配置是否正确。
- 逆 Park、逆 Clarke、SVPWM 计算结果的极性和相序是否正确。
如果开环阶段这些链路不确认,后面切闭环时,电流环一个 PI 输出正反馈,电机立刻过流报警甚至炸 MOS 管。
另外,开环阶段也是验证“程序里生成的电角度方向和电机实际旋转方向是否一致”的最好时机。方向反了,只需要调整相序或者电角度累加方向,不需要去纠结观测器。
1.3 开环控制的三种实现思路
FOC 开环控制并不是只有一种写法。工程里常见的思路有三种,分别适合不同的学习阶段和应用场景。
第一种是 V/f 开环控制。给定一个随转速变化的电压幅值,频率跟着目标转速走。这种方法最简单,不需要电流采样也能转起来,但低速时电压幅值不够,带载能力很差;高速时电压幅值容易超出母线电压限制。适合最早期验证 PWM 链路和电机能不能转,不太适合做 FOC 学习的主线。
第二种是电流开环控制。直接把 Id 给定为 0,把 Iq 给定为一个固定值,经过逆 Park 变换和逆 Clarke 变换后输出目标电压。程序结构最接近闭环 FOC,电角度由目标转速积分得到。这是学习 FOC 最推荐的开环方案,因为它后续加电流环时,只需要把“给定电流”替换成“PI 输出”,整体结构不需要大改。
第三种是角度开环控制。直接给定一个不断变化的角度,同时给定固定幅值的电压矢量。这种方式比 V/f 更接近 FOC 的数学结构,但需要注意电压幅值要和转速匹配,否则电流很大但也转不快。
本文后面的代码以第二种电流开环为主,同时会解释 V/f 限幅的基本思路。它和真正的 FOC 闭环在结构上最接近,迁移成本最低。
2. 开环 FOC 涉及的关键概念:坐标变换、电角度和电流采样
2.1 Clarke 变换为什么只需要两相电流
很多初学者看到 Clarke 变换公式会有一个疑问:三相电机明明是三相电流,为什么要采样两相电流去算?第三相去哪了?
首先要明确:电机三相对称时,三相电流满足基尔霍夫电流定律,即 Ia + Ib + Ic = 0。这个等式在理想情况下是严格成立的,所以只要采样 Ia 和 Ib,就能算出来 Ic = -Ia - Ib。这也是大部分 FOC 方案只采样两相电流,第三相由软件计算的原因。
但这引出了两个前提条件:
第一,电流采样必须准确,尤其是偏置和增益一致性。如果两相采样的零点偏置不一致,计算出的第三相电流就会引入共模误差,最终在 dq 轴电流上表现为某个固定频率的波动。
第二,逆变器处于正常工作状态,不能出现一相桥臂不导通等异常情况。如果某一相因为驱动问题实际没有电流,而软件还按 Ia + Ib + Ic = 0 去推算,反馈电流就和实际完全对不上。
还有一个细节值得注意:Clarke 变换对采样的是哪两相没有硬性要求,只要是三相中的任意两相即可。但在电路板上,通常是采样 U 相和 V 相,或者 V 相和 W 相,具体取决于采样电阻放在哪个位置。真正有要求的是 Clarke 变换矩阵中的相序要和电机三相绕组的空间顺序保持一致。如果相序搞反了,变换结果会出现 120 度的相位偏差,最终表现为开环可以转,但电流波形不对称,闭环时电流环不稳定。
2.2 从两相静止坐标到旋转坐标
Clarke 变换把三相静止坐标系下的 Ia、Ib、Ic 转换到两相静止坐标系下的 Iα、Iβ,数学上就是一个矩阵乘法。关键是 Park 变换:它以电角度 θ 为旋转角,把 Iα、Iβ 变成旋转 dq 坐标系下的 Id、Iq。
为什么 FOC 非要做这一步?因为三相电流是交流量,频率随转速变化,用 PI 控制器去跟踪正弦给定很吃力。而经过 Park 变换后,稳态时 Id 和 Iq 是直流量,PI 控制器对直流量的跟踪能力非常强,而且具有无限增益特性,能实现无静差控制。
在开环控制里,我们需要的是逆过程。给定 Id、Iq,经过逆 Park 变换得到 Uα、Uβ,再经过逆 Clarke 变换得到三相电压目标。这里的电角度 θ 不是采样得到的,而是程序内部生成的。它本质上是对目标转速做积分:
θ = θ_prev + ω * dt
其中 ω 是目标电角速度,单位是 rad/s。换算关系是:
ω = rpm * pole_pairs / 60 * 2π
这里的 pole_pairs 是电机极对数,而不是极数,这个换算非常容易出错。
电角度累加结果会无限增长,所以一般会对 2π 取模。工程上更常见的做法是用 uint32_t 或者 float 累加,利用溢出或取模来保证角度始终在一个周期内,避免浮点角度无限增长导致精度丢失。
2.3 电流采样为什么经常设置在下桥
热词里有一句“FOC 电流采集为什么要设置在下桥”,这是很多硬件方案里的实际选择。
电流采样的目的是测量电机相电流,但相电流并不能随时采样。常见的采样方式是在逆变器下桥臂串联采样电阻,通过运放放大后送给 ADC。下桥采样只能在对应的下桥 MOS 管导通的时刻采集电流,因为此时采样电阻上才有电流流过。而在 SVPWM 的不同扇区、不同占空比条件下,三个下桥臂的导通时间是不一致的,采样窗口会随之变化。
开环阶段电流采样主要用来做电流波形观测,不一定参与控制,但它有两个价值:一是确认 ADC 采样时刻是否落在有效窗口内,二是确认 Clarke 变换计算出的三相电流是否满足 Ia + Ib + Ic = 0。
如果采样时刻选在上桥导通时间占比很高的区间,下桥没有导通,采样电阻上电压为 0,读到的电流就不是真实相电流。常见的处理办法是:
| 采样条件 | 处理方案 |
|---|---|
| 低占空比,下桥导通时间长 | 固定在下桥中点采样,简单可靠 |
| 高占空比,下桥导通时间短 | 使用三电阻采样加采样窗口扩展,或切换采样策略 |
| 单电阻采样 | 必须在两个非零电压矢量作用期间分别采样,计算量大 |
| 采样窗口不足 | 加大死区补偿,或限制最高调制比 |
学习阶段推荐使用三电阻下桥采样,代码简单,波形观察直观。开环阶段即使不参与闭环控制,也要把 ADC 采样和 Clarke 变换的代码写好,方便后面接电流环。
3. 环境准备:硬件、软件和参数表
3.1 学习阶段的最小硬件配置
做 FOC 开环控制并不需要昂贵设备,但有几样东西不能省:
- 一块 MCU 开发板,常见的是 STM32F103 或更高主频的芯片,重点是内置 ADC、定时器 PWM 输出和死区功能。
- 一个三相全桥逆变器驱动板,集成 MOS 管和驱动芯片。
- 一块直流无刷电机或者永磁同步电机,最好选择极对数已知、参数手册完整的型号。学习阶段不推荐自己绕电机,参数不明会浪费大量排查时间。
- 一套电流采样电路,三电阻下桥采样加运放放大,或者电流传感器。
- 直流稳压电源,带电流显示功能,方便观察母线电流变化。
- 必要的调试工具:逻辑分析仪或者示波器。如果没有示波器,至少要通过串口把 ADC 采样值发出到上位机软件,画成波形曲线。
连接关系上,MCU 的三相 PWM 输出接到逆变器驱动芯片输入,逆变器三相输出接到电机三相线,电流采样电阻输出电压经过运放接到 MCU 的 ADC 引脚。电机安装位置可以不编码器,但建议有一个手动转动的旋钮,方便后面做转子位置检测实验。
3.2 软件工具和工程结构
软件工具取决于你使用的 MCU,常用的是 Keil、IAR、STM32CubeIDE 或 GCC 工具链。如果你是学习 FOC 算法本身,建议选择有 HAL 库或者 LL 库支持、能找到参考资料较多的开发板。
工程结构上,不推荐把所有代码都堆在 main.c 里。FOC 相关的代码建议按模块拆分:
project/ ├── app/ │ ├── foc_control.c │ ├── foc_control.h │ └── motor_config.h ├── bsp/ │ ├── pwm.c │ ├── adc.c │ └── pwm.h ├── math/ │ ├── clarke.c │ ├── park.c │ ├── svpwm.c │ └── angle_ramp.c └── main.c这只是一个通用示例,实际项目要结合自己的包名、目录和编译环境调整。核心思想是把算法模块与底层硬件驱动分离,这样以后换 MCU 或者换驱动板时,算法部分基本不用动。
3.3 电机参数和开环给定值速查
开环控制开始时并不需要精确的电机参数,但需要知道几个关键值,用于设定开环电压幅值和转速范围:
| 参数 | 说明 | 学习阶段建议 |
|---|---|---|
| 极对数 | 影响电角度与机械转速换算 | 从电机手册查,不能猜 |
| 额定电压 | 决定母线电压设定 | 按额定电压的 70%-80% 设定 |
| 额定电流 | 决定 Iq 开环给定上限 | 开环给定不超过额定电流的 30%-50% |
| 额定转速 | 决定开环目标转速范围 | 先跑额定转速的 10%-30% |
| 相电阻 | 估算电压/电流关系 | 伏安法测量 |
开环电压幅值如果不确定,最安全的起点是:
Uq_ref = Iq_ref * R_phase
其中 R_phase 是相电阻。这样给定电流 Iq_ref = 1A 时,电压给定就是 1A 乘以相电阻,电流不会过大。实际转速起来后,反电动势会影响电流,但这个公式用来确定初始电压给定足够安全。
注意:这里的公式只用于确定开环初始电压,不是闭环控制里的电压方程。闭环时要考虑电感和反电动势,公式会复杂很多。
4. 开环 FOC 控制代码实现
4.1 主循环的整体流程
开环 FOC 的控制频率通常和 PWM 频率一致,典型值是 10kHz 到 20kHz。也就是说,每 50 到 100 微秒执行一次完整的 FOC 计算链路过一次。学习阶段可以把控制频率放宽到 10kHz,降低对 MCU 的要求。
主循环或中断里的执行顺序如下:
- 读取母线电压,计算电压标幺值或伏特值。
- 读取三相电流 ADC,转换成实际电流值。
- 根据当前目标转速,累加计算电角度。
- 给定 Id_ref = 0,Iq_ref = 固定值(或按斜坡给定)。
- 将 Id、Iq 经过逆 Park 变换得到 Uα、Uβ。
- 将 Uα、Uβ 经过逆 Clarke 变换得到三相电压目标 UA、UB、UC。
- 将三相电压目标送入 SVPWM 模块,计算出三个半桥的占空比。
- 更新 PWM 比较寄存器。
这里要注意,第 2 步的电流采样在开环阶段不参与控制,但要执行并保存,用于调试观测。第 5 步到第 7 步是 FOC 算法的核心链路,必须在一个控制周期内完成。
4.2 电角度累加器的实现
电角度累加器是开环 FOC 里最关键的一环。它决定了电机转多快、旋转方向、以及 dq 轴电压在定子坐标系里的相位。
// 角度累加器,输入目标转速 rpm,输出电角度 rad float g_angle_rad = 0.0f; void angle_ramp_update(float target_rpm, float pole_pairs, float dt) { float omega_rad_per_s = target_rpm * pole_pairs * 2.0f * 3.1415926f / 60.0f; g_angle_rad += omega_rad_per_s * dt; // 角度保持在 0 ~ 2PI 范围内 if (g_angle_rad > 2.0f * 3.1415926f) { g_angle_rad -= 2.0f * 3.1415926f; } else if (g_angle_rad < 0.0f) { g_angle_rad += 2.0f * 3.1415926f; } }这段代码有几个细节值得注意:
第一,target_rpm 乘以 pole_pairs 之后才是电角速度对应的 rpm。很多人在这里漏掉极对数,结果电机旋转速度完全不对。
第二,减法取模的方式在角度越过 2π 后有效。如果目标转速很大,dt 也大,一次累加可能超过 2π,需要用 while 循环或者更稳妥的 fmodf 处理。控制频率越高,这个问题的概率越低。
第三,旋转方向由 target_rpm 的正负决定。如果电机实际方向和你期望的相反,可以改变 target_rpm 的符号,也可以交换两相电机线。
4.3 电流开环的给定值生成
电流开环的意思是,我们给定 Id_ref 和 Iq_ref,经过坐标变换得到电压目标,但并不测量电流去修正电压。因为电机会产生反电动势,实际电流和给定电流不一定相等,所以它本质上是电压开环,电流只是名义上的给定。
void foc_openloop_control(float target_rpm, float target_iq, float dt) { float id_ref = 0.0f; float iq_ref = target_iq; // 更新电角度 angle_ramp_update(target_rpm, MOTOR_POLE_PAIRS, dt); // 逆 Park 变换:dq -> alpha/beta float ud = id_ref * MOTOR_R_PHASE; // 这里的电阻值只是初始电压估算 float uq = iq_ref * MOTOR_R_PHASE; float cos_theta = cosf(g_angle_rad); float sin_theta = sinf(g_angle_rad); float u_alpha = ud * cos_theta - uq * sin_theta; float u_beta = ud * sin_theta + uq * cos_theta; // 逆 Clarke 变换:alpha/beta -> 三相电压 float ua = u_alpha; float ub = -0.5f * u_alpha + 0.8660254f * u_beta; float uc = -0.5f * u_alpha - 0.8660254f * u_beta; // SVPWM 或直接用三相电压计算占空比 svpwm_update(ua, ub, uc, Vbus); }这段代码是学习 FOC 开环控制的核心骨架。它和闭环 FOC 的区别只在于:这里用电阻估算电压,闭环则用 PI 控制器输出 Ud、Uq。
有人会问,既然是电流开环,为什么不直接把 Id、Iq 输入到 SVPWM,而是经过逆 Park 和逆 Clarke?原因是 SVPWM 输入的应该是静止坐标系下的三相电压矢量,不能直接输入旋转坐标系下的 dq 值。定子线圈感受到的是静止坐标系下的电压,所以必须用当前电角度把 dq 电压旋转到静止坐标系。
4.4 逆 Park、逆 Clarke 和 SVPWM 的输出关系
逆 Park 变换的矩阵是:
Uα = Ud * cos(θ) - Uq * sin(θ) Uβ = Ud * sin(θ) + Uq * cos(θ)
逆 Clarke 变换常见形式是:
UA = Uα UB = -0.5 * Uα + (√3/2) * Uβ UC = -0.5 * Uα - (√3/2) * Uβ
这两步合在一起,本质上是把一个在旋转坐标系下的电压矢量,映射到三相静止坐标系的正弦电压。开环运行时,随着 θ 均匀增加,UA、UB、UC 就是三相互差 120 度的正弦波,频率由转速决定。
SVPWM 模块负责把这三相电压目标转换成三个半桥的占空比。SVPWM 比简单的正弦 PWM 多用了一个零序分量注入技巧,可以在同样母线电压下获得更高的线性调制范围。初学者可以使用等幅值 Clarke 变换配合空间矢量算法,也可以先用简单的三相占空比计算方法:
float duty_a = 0.5f + ua / Vbus; float duty_b = 0.5f + ub / Vbus; float duty_c = 0.5f + uc / Vbus;然后做限幅:
float duty_min = duty_a; float duty_max = duty_a; // 这里省略找 min/max 的代码 float mid = 0.5f * (duty_min + duty_max); duty_a -= mid; duty_b -= mid; duty_c -= mid;这段代码实现了中心对齐的 SVPWM 等效效果,原理是把三相电压同时平移,使占空比始终在 0 到 1 之间,并且可以让电压矢量的调制范围扩大。
实际项目里,SVPWM 还需要考虑死区时间补偿。死区会导致实际输出电压与目标电压的偏差,在开环阶段表现为电流波形过零点畸变,在闭环阶段表现为电流环力矩波动。学习阶段可以先不加死区补偿,但要意识到这个误差源存在。
4.5 电流采样和下桥采样的配置要点
开环阶段电流采样主要用于观测,但代码结构上建议从第一天就按闭环采样标准来写。
首先,ADC 采样触发时刻必须和 PWM 对齐。最常用的配置是在三角载波计数到 0 或者计数到周期值时触发 ADC。此时三个下桥臂都处于导通状态,采样电阻上有完整的相电流流过,ADC 读到的是稳定的电流值。
// PWM 初始化时配置 ADC 触发,伪代码示意 pwm_timer_configure_deadtime(DEADTIME_NS); pwm_timer_configure_update_interrupt(); adc_configure_injected_channel(ADC_CH_U, ADC_CH_V); adc_configure_external_trigger(PWM_UPDATE_EVENT);代码补全时要注意:三个采样通道必须在同一个 PWM 周期内完成采样,不能分散到不同周期。否则 Clarke 变换用到的三相电流就不属于同一时刻,变换结果会有固定角度误差。
实际项目里,下桥采样容易出现以下问题:
| 问题现象 | 常见原因 | 处理建议 |
|---|---|---|
| 采到的电流不是正弦波 | ADC 采样时刻落在下桥截止区间 | 检查 PWM 计数方式与 ADC 触发点 |
| 三相电流之和不为 0 | 采样通道偏置不一致 | 做校零处理 |
| 电流波形有周期性毛刺 | 采样时刻靠近开关噪声区域 | 调整采样触发点,避开 MOS 开关边沿 |
| Ia + Ib + Ic 明显大于 0 | 电机绕组接地短路 | 断电检查母线电流和互感器 |
如果开环阶段你是先写简单占空比控制而不是完整 SVPWM,也可以暂时不采电流。但一旦进入 FOC 开环阶段,就应该把电流采样完整跑起来,因为后面闭环要用的就是这条链路。
5. 运行验证与常见问题排查
5.1 开环运行前的验证检查清单
上电第一次跑开环 FOC 前,不要直接给大电压。按下面的顺序逐项检查:
- 母线电压是否在目标范围内,电压采样值是否和万用表读数一致。
- PWM 波形检查:用示波器看任意一个半桥的上下管 PWM,确认互补且带死区,不能有上下管同时导通。
- 三相输出电压是否互差 120 度:在电机不接的情况下,测量三相对中性点的电压波形。
- 电机相序与代码相序是否一致:先给一个小角度的固定电压矢量,用手感受电机是否往预期方向转动。
- 电流采样零点检查:电机静止时,三个 ADC 通道读到的值是否在零电流附近,偏差应该接近运放失调电压换算后的电流值。
检查完后再逐步增加目标转速和 Iq 给定。每增加一步,观察母线电流是否平滑上升,电机是否有异响,电流采样波形是否接近正弦。
5.2 预期波形和结果
开环 FOC 正常运行时,应该观察到以下现象:
- 电机匀速旋转,转速接近目标转速,但带负载后转速会跌落。
- 三相电流波形接近正弦波,频率等于电角频率。
- 若用串口把 Id 和 Iq 的采样值发送出来,由于没有闭环,Id 和 Iq 不是稳定的直流量,而是有波动的交流量。这个现象是正常的,开环控制不保证 dq 轴电流恒定。
如果目标转速是 300rpm,极对数是 4,电角频率是 300 * 4 / 60 = 20Hz,那么电流波形的周期应该是 50ms。这个换算关系可以用来快速判断电角度累加器计算是否正确。
5.3 开环阶段最常见的错误现象
开环阶段最容易遇到下面几个问题:
现象一:电机不转,但能听到嗡鸣声,母线电流明显偏大。常见原因是电压矢量的幅值太小,或者频率太低,电机无法克服静摩擦力矩。解决方式是提高 Iq 给定电压幅值,或用手轻拨电机帮它启动。如果开环起始位置恰好与电压矢量方向垂直,启动转矩接近零,这也是开环启动固有的问题。
现象二:电机旋转方向反了。解决方式是交换任意两相电机线,或者把 target_rpm 取反。注意交换两相线后,电流采样的相序判断也可能要调整。
现象三:电机能转,但电流非常大,发热严重。常见原因是初始电压给定就偏高,或者 SVPWM 的调制比超限。开环阶段不要盲目加大电压直到母线电流快速上升,应该让电流保持在额定电流三分之一以下。
现象四:电流采样波形完全不对,或者存在明显直流偏置。不要先怀疑电机,先检查 ADC 参考电压、运放偏置校准,再检查采样触发时刻。开环阶段电流波形异常,大概率是采样链路的问题,而不是控制算法的问题。
现象五:转速越快噪声越大,电流波形削顶。这通常是因为电压给定已经达到母线电压限制,SVPWM 进入非线性调制区。处理方法包括提高母线电压、降低目标转速、或者引入弱磁控制的思路。开环阶段不需要做真正的弱磁,但要意识到存在调制饱和这个边界。
5.4 排错顺序建议
遇到问题时,建议按下面的优先级排查,不要一上来就改算法参数:
- 先确认输入:目标转速、极对数、PWM 频率这些参数是否合理。
- 再确认硬件路径:PWM 是否真的输出到电机引脚,电机线是否接触良好。
- 再确认采样链路:ADC 值是否在合理范围,三相电流之和是否为 0。
- 再确认相序和方向:电压矢量和转子磁场方向是否可能有对齐问题。
- 再确认算法细节:角度累加步长是否正确,SVPWM 是否限幅。
- 最后看波形和日志:用串口、逻辑分析仪或示波器抓数据,计算实际电频率与目标是否一致。
反向排查也很有用:如果电机能转,说明 PWM 和逆变器链路没问题;如果电流波形正确,说明采样链路没问题。哪一级不正常就逐级向上定位。
6. 从开环到闭环的过渡,这些坑先想清楚
6.1 转子初始位置检测:无感 FOC 绕不过去的前置问题
开环控制用一个程序生成的电角度去驱动电机。只要电机在转,这个角度和真实转子位置之间可能有一个固定偏差,但转起来后系统能维持运行。到了无感 FOC,你必须知道转子实际位置才能让电流环正确收敛。转子初始位置检测就是为了解决电机静止时不知道转子在哪的问题。
常见方法有高频注入法和基于电感饱和效应的方法。高频注入法需要额外算法在低速下标定转子极性,实现复杂度高。电感饱和效应利用 d 轴和 q 轴电感差异判断转子位置,但对采样精度要求高。
从开环切闭环时,最常见的错误是:开环跑得好好的,一合上电流环,电机立刻抖动过流。原因通常是切换瞬间,程序生成的角度和真实转子位置之间存在一个不可忽略的偏差。解决办法是:在切换前先强制转子预定位,即给一个固定方向的电压矢量,让转子转到已知位置,然后再启动闭环。
热词里的“无感 FOC 转子初始位置检测”和“无感 FOC 堵转检测”都是实际工程里很难的点。开环阶段可以把预定位流程先实现出来:给定一个固定方向的小电压矢量,持续几百毫秒,检测转子是否停稳,再进入正常闭环。
6.2 开环切换闭环时的电流冲击
即使转子预定位完成,开环切换闭环时仍然可能产生电流冲击。原因是开环阶段没有电流环,Iq 可能偏离给定值;闭合电流环瞬间,PI 积分器初始值为零,但实际电流和给定电流的误差很大,如果不做积分限幅,输出会立即饱和。
推荐的做法是:切换瞬间把电流环 PI 输出限制在一个比较小的限幅范围内,同时给 Iq 参考值加斜坡,让电流从 0 缓慢上升。也可以用切换前最后一次开环输出的 Ud、Uq 来初始化 PI 控制器的积分器,避免切换瞬间电压跳变。
这个阶段最容易忽略的是,电流环 PI 参数不能直接从网上抄。电流环的带宽取决于采样频率、PWM 频率、电机电感和逆变器延迟。实际调试时先跑一个较小的比例系数,观察 Iq 是否稳定跟踪,再逐步加大。如果开环阶段不把电流采样做准,这一步会耗费大量时间查方向。
6.3 堵转检测在开环阶段就值得设计
堵转是指电机在通电状态下转子被外部卡住不转。无感 FOC 因为不知道转子位置,堵转时反电动势为零,电流会迅速上升,导致过热或烧毁。
开环阶段虽然没有闭环,但堵转检测逻辑可以先做起来。最简单的思路是:给定一个目标转速,检测母线电流或者相电流的变化率。如果目标转速不为零,但电机实际电频率和程序电角度累加频率严重不一致(表现为电流异常增大、母线电压被拉低),就可以判定为堵转。
更完整的实现需要速度估算或者位置观测器。开环阶段可以先实现电流阈值保护和母线电压跌落保护。无论当前是开环还是闭环,堵转保护的优先级都应该高于转速控制,因为电机烧毁不可逆。
6.4 高速时开环控制的局限性
开环控制进入高速区间后,问题会更明显。随着转速升高,反电动势增大,电机实际电流会下降;如果保持 Iq 给定不变,电压给定也要相应提高。但母线电压是有限的,SVPWM 进入过调制后,输出电压波形会失真,电流谐波增大,电机噪声变大。
FOC 弱磁控制解决的就是这个问题:在电压受限时,通过减小 Id 给定甚至让 Id 为负,削弱气隙磁场,从而在更高转速下仍然能保持足够的电压裕量。开环阶段不需要实现弱磁,但要知道开环高速时“给定 Iq 不等于实际 Iq”,不要被名义上的电流给定误导。
对学习路径的建议是:先把 10%-30% 额定转速下的开环和闭环跑顺,再考虑高速和弱磁。大部分无感 FOC 项目的难度都集中在低速和启动,高速反而是相对简单的区间。
7. 最佳实践与扩展方向
7.1 代码结构上的几个建议
第一个建议是把所有 FOC 算法模块写成纯数学函数,不依赖任何 MCU 头文件。比如输入输出都用 float 或结构体,不直接操作寄存器。这样可以在 PC 上写单元测试验证坐标变换、角度累加、SVPWM 限幅逻辑,再移植到 MCU。
第二个建议是定义清晰的数据结构。用结构体把电机参数、电流采样值、角度累加器状态、SVPWM 输出结果组织起来,避免大量全局变量散落各处。
typedef struct { float pole_pairs; float r_phase; float l_phase; float vbus_nominal; } motor_params_t; typedef struct { float id; float iq; float ud; float uq; float angle_rad; float duty_a; float duty_b; float duty_c; } foc_state_t;第三个建议是日志和调试信息要早做。开环阶段就要把目标转速、电角度、三相电流、占空比这些关键量通过串口或调试器导出。很多问题在波形图上一眼就能看出来,但在串口终端里看数据很难定位。
7.2 学习环境的调试技巧
7.2 学习环境的调试技巧
调试开环 FOC 时,示波器不一定能看到电流波形,因为电流采样一般是通过 MCU ADC 完成的。更实用的办法是:把 ADC 采集到的三相电流通过串口发送到 PC,在上位机或 Python 脚本里画波形。
Python 端可以用 Matplotlib 简单画图:
import matplotlib.pyplot as plt # 假设计算机从串口读取到采样后的电流值 # 数据格式:每行三个浮点数,用逗号分隔 time = [] ia = [] ib = [] ic = [] for line in open("current_log.txt"): parts = line.strip().split(",") time.append(float(parts[0])) ia.append(float(parts[1])) ib.append(float(parts[2])) ic.append(float(parts[3])) plt.figure() plt.plot(time, ia, label="Ia") plt.plot(time, ib, label="Ib") plt.plot(time, ic, label="Ic") plt.xlabel("time (ms)") plt.ylabel("current (A)") plt.legend() plt.grid(True) plt.show()如果 Ia、Ib、Ic 三者之和明显不为零,说明采样链路有系统性问题,优先检查偏置校准。如果波形频率和理论电频率不一致,优先检查极对数或目标转速换算。
7.3 从开环到闭环的学习路径
如果在看这篇文章之前你还没有接触过 SVPWM,建议先不要直接上开环 FOC,先完成下面几个小实验:
- 用固定占空比驱动电机,确认三相 PWM 输出正常。
- 用三相占空比按低频正弦变化驱动电机,观察电机缓慢旋转,确认相序正确。
- 实现角度累加器,在 PC 端用串口输出角度值,确认角度线性增长。
- 实现三相电压目标计算,用逻辑分析仪观察 PWM 占空比是否是三相互差 120 度的正弦波。
- 完成开环 FOC,观察母线电流和电流波形是否正常。
每一步都能独立验证,都通过后再进入闭环。这样即使最后闭环出问题,你也能快速定位是哪一层引入的故障。
对于新手,最值得练习的一个小项目是:实现一个用按键调整目标转速、用 OLED 或串口显示当前电流波形的开环 FOC 演示程序。这个项目麻雀虽小,但覆盖了 FOC 的完整链路:角度生成、坐标变换、SVPWM、PWM 输出、电流采样、数据回传。做透这个项目,你对 FOC 的理解会超过只背公式的人。
开环 FOC 是无感 FOC 学习路上第一块稳定的垫脚石。它不代表最终控制方式,但它能帮你把整个信号链路上的硬件、采样、变换和输出问题提前暴露出来。把这些基础问题解决了,后面加电流环、切入无感算法时,你才能聚焦在算法本身的调试上。如果只是照着别人的代码堆一个能转的电机,却不理解电角度来自哪里、三相电流为什么这样算,后续一旦遇到异常,很容易陷入盲调。