
FOC 入门最大的障碍不是公式难而是一上来就被电流环、观测器、补偿网络一起淹没。第十二期我们把问题拆小先做电机开环控制让转子转起来。开环控制不依赖编码器、不需要精确的转子初始位置代码量比完整 FOC 闭环少一个量级却能逼着你把 PWM 输出、三相全桥驱动、电压矢量、母线电压采集、电流采样通道全部打通。这一期跑通之后后面再做电流环和速度环只是在这条已经工作的电路上不断增加闭环逻辑而已。本文会沿着“原理 - 硬件准备 - 代码实现 - 实测验证 - 常见问题”这条线展开重点回答几个很实际的疑问开环控制为什么能让电机转起来三相占空比怎么算电流采样为什么要放在下桥Clarke 变换为什么只需要两相电流开环和闭环之间怎么衔接如果你是刚开始学 FOC、手上有 BLDC 或 PMSM 开发板但还没转过电机这一期可以直接照着做。1. 电机开环控制核心能力速览能力项说明项目类型电机控制算法教程FOC 系列第十二期核心主题在 FOC 框架下实现电机开环控制主要功能V/F 速度开环、电角度生成、三相 PWM 输出、正反转控制、软启动建议硬件带定时器 PWM 的 MCU、三相全桥驱动、BLDC/PMSM、直流稳压电源编码器依赖不依赖开环可以“盲转”软件基础C 语言、定时器中断、ADC 采样、串口打印是否支持接口调用嵌入式项目一般没有 REST API但可以设计串口调试协议是否支持批量任务不是互联网服务但可以用上位机脚本批量标定电压-转速曲线适合场景FOC 入门、驱动电路验证、PWM 相序检查、电流采样调试、无感算法前期准备不适合场景高精度转速控制、负载突变场合、需要大力矩锁止的场合2. 适用场景与使用边界开环控制最适合做两件事第一验证硬件链路是否正常第二给闭环控制铺路。很多板子焊完之后第一优先不是纸上谈兵聊 FOC 算法而是确认单片机能不能输出六路 PWM、驱动芯片能不能正确换相、电机相线有没有接反、母线电压采样是否准确。这些问题用开环控制排查比一上来就调电流环快得多。只要给一个固定电压矢量电机会跟着定子磁场方向旋转转得对不对、电流有没有异常一眼就能看出来。开环控制不适合做需要精确保持转速的任务。因为它本质上是“开环给定电压不测量实际转速也不修正误差”。负载增大时电机转速会下降但你程序里给出的电角度频率不会自动补偿。这个特性决定了它只适合空载或轻载实验不适合做产品级伺服控制。还需要强调安全边界电机转动时轴端、联轴器、齿轮都可能高速旋转实验台要固定好手边要放急停开关。电源要有限流功能避免驱动板异常时母线短路。三相全桥驱动在调试阶段可能因为程序 bug 导致上下桥直通烧管子是常见事故所以第一轮测试建议用小功率电机、低母线电压和限流电源。涉及商业开源代码时也要注意协议边界比如 VESC 源码可以作为无感 FOC 学习的参考但商用前要检查授权要求不能直接拷贝了事。3. 环境准备与前置条件3.1 硬件清单硬件作用备注MCU 开发板运行控制算法STM32、GD32、TC264 等带高级定时器更好三相全桥驱动板将 PWM 信号转成功率驱动检查是否自带母线电容、采样电阻、死区电路BLDC 或 PMSM 电机被控对象优先选择功率小、额定电压低的电机直流稳压电源提供母线电压必须有电流限制功能编码器或霍尔传感器可选开环控制阶段可以不用逻辑分析仪或示波器看 PWM 波形和相电流排查问题时很关键USB 转串口模块打印调试数据和下发指令建议至少保留一路串口3.2 软件环境开发环境Keil MDK、IAR 或 VSCode GCC 都可以选你熟悉的。库如果使用 STM32可以用 HAL 库或直接寄存器操作。FOC 对实时性有要求建议把核心控制在定时器中断里执行。代码版本管理建议开一个 git 仓库每次改完 PWM 逻辑先提交一次。开环实验经常改参数有历史版本方便回退。数据观察串口助手或 Python pyserial 都可以用于打印目标转速、电角度、母线电压、电流采样值。3.3 接线检查清单先不接电机测量母线电压是否正常。确认驱动板逻辑电源和功率电源分开供电。确认单片机 PWM 输出的地线和驱动板逻辑地共地。确认电机三相线 U/V/W 和驱动板三相输出一一对应。确认 ADC 采样通道没有被大电流干扰源紧贴走线。确认死区时间配置合理尤其是 6 路互补 PWM 输出时。4. 开环控制原理4.1 为什么先写开环控制FOC 闭环的最终目标是把定子电流矢量控制到与转子磁场成固定的关系这样才能产生恒定力矩。但一开始转子位置不可知观测器又不成熟强行闭环只会出现启动失败或者电流失控。开环控制的做法更直白不需要知道转子在哪个角度我直接给一个旋转的定子磁场让转子被这个磁场拖起来。这就好比你在冰面上拉一块重物不关心它朝哪个方向只要拉力的方向不断转圈重物就会跟着转。开环 FOC 正是用这种方式完成“启动”这个过程。4.2 电角度、机械角度和极对数电机每转一圈电角度并不一定转一圈中间相差一个极对数p。公式是theta_e theta_m * p其中theta_m是机械角度theta_e是电角度p是极对数。常见的多极电机极对数可能是 4、7、14电角度转速会比机械转速高很多。给定目标转速后电角速度的计算方法是omega_e omega_m * pomega_m 2 * pi * rpm / 60也就是说程序里要先把用户设定的rpm换算成机械角速度再乘以极对数得到电角速度。这一步做错了电机转速会和你预期差很多。4.3 三相电压矢量的生成开环 FOC 中最简单的实现是正弦电压驱动。假设我们维持一个幅值为U的电压矢量让电角度theta_e按电角速度匀速累加三相电压可以写成ua U * sin(theta_e) ub U * sin(theta_e - 2*pi/3) uc U * sin(theta_e 2*pi/3)三相电压相差 120 度合成出来的定子磁场就是旋转的。剩下的事情就是把这三个电压值换算成 PWM 占空比输出到三相全桥驱动。严格来说FOC 中更常用的是 SVPWM它对母线电压的利用率更高谐波更小。但开环阶段先用正弦 PWM 映射占空比代码简单容易验证“电机转不转”这个核心问题。后面换 SVPWM 时输入仍然是电压矢量只是占空比计算方式不一样。4.4 开环控制的电压选择开环启动时电压幅值不能给得太小否则定子磁场拖不动转子也不能给得太大否则电流会飙高甚至烧毁功率管。通常做法是让电压幅值随目标频率平滑上升也就是 V/F 控制。低速启动时电压小转速上升后逐步加大电压让电机在额定磁链附近运行。只给一个固定电压电机也能转但低速力矩不够高速又可能过流。5. 代码实现开环 FOC 控制下面以常见的 C 语言工程为例核心只做三件事用定时器中断产生固定控制周期。在中断里累加电角度。根据电角度算三相占空比并更新 PWM 比较寄存器。5.1 开环角度生成// 定义开环 FOC 结构体 typedef struct { float pole_pairs; // 极对数 float speed_rpm; // 目标转速 float angle_e; // 当前电角度单位 rad float angle_speed_e; // 电角速度单位 rad/s float voltage_q; // 电压幅值单位 V } OpenLoopFOC;初始化函数void openloop_init(OpenLoopFOC *foc, float pole_pairs, float speed_rpm, float voltage_q) { foc-pole_pairs pole_pairs; foc-speed_rpm speed_rpm; foc-voltage_q voltage_q; // 机械角速度 float speed_mech 2.0f * 3.1415926f * speed_rpm / 60.0f; // 电角速度 foc-angle_speed_e speed_mech * pole_pairs; foc-angle_e 0.0f; }控制周期中断函数void openloop_tick(OpenLoopFOC *foc, float dt) { // 电角度累加 foc-angle_e foc-angle_speed_e * dt; // 限制在 0 ~ 2*pi 范围内 if (foc-angle_e 2.0f * 3.1415926f) { foc-angle_e - 2.0f * 3.1415926f; } // 三相正弦电压 float ua foc-voltage_q * sinf(foc-angle_e); float ub foc-voltage_q * sinf(foc-angle_e - 2.0944f); float uc foc-voltage_q * sinf(foc-angle_e 2.0944f); // 写入 PWM pwm_set_duty_abc(ua, ub, uc); }注意这里的dt必须和定时器中断周期一致。如果控制周期是100us那么dt 0.0001f。如果中断周期不准确电机转速会有明显误差。5.2 三相电压转 PWM 占空比ua、ub、uc是电压值不能直接写到定时器 CCR 寄存器。需要根据母线电压做一次归一化。这里给出一个简化版本实际工程会使用 SVPWM 来获得更高的母线利用率。void pwm_set_duty_abc(float ua, float ub, float uc) { float vbus get_bus_voltage(); // 实际母线电压 float offset; // 中点偏移计算保证三相输出电压平衡 offset (ua ub uc) / 3.0f; float da (ua - offset) / vbus 0.5f; float db (ub - offset) / vbus 0.5f; float dc (uc - offset) / vbus 0.5f; // 限幅 if (da 0.0f) da 0.0f; if (da 1.0f) da 1.0f; if (db 0.0f) db 0.0f; if (db 1.0f) db 1.0f; if (dc 0.0f) dc 0.0f; if (dc 1.0f) dc 1.0f; // 写入定时器通道比较寄存器 tim_set_ccr(da, db, dc); }如果你的定时器是中心对齐模式占空比0.5f对应 50% 高电平。这个转换关系要结合定时器计数周期换算成 CCR 值。5.3 启动斜坡上电瞬间直接给目标转速电流冲击很大。更稳妥的做法是用斜坡让转速从 0 逐步上升到目标。float current_speed 0.0f; float target_speed 600.0f; // 目标转速单位 rpm float speed_step 200.0f; // 加速度单位 rpm/s void speed_ramp_periodic(float dt) { if (current_speed target_speed) { current_speed speed_step * dt; if (current_speed target_speed) { current_speed target_speed; } } openloop_update_speed(current_speed); }斜坡可以有效减少开环启动失败的概率。转速也不能升得太快否则转子跟不上定子磁场旋转速度会出现失步、抖动甚至反转。5.4 简单电流保护开环控制虽然没有电流环但一定要有过流保护。可以在每个 PWM 周期用 ADC 采样相电流或母线电流超过阈值后立即关闭 PWM 输出。void fault_check(void) { float current get_current_sensor(); if (current CURRENT_LIMIT) { pwm_disable(); set_error_flag(ERROR_OVERCURRENT); } }过流保护要独立于控制逻辑最好同时用硬件比较器做第二道保险。只靠软件 ADC 保护反应时间可能不够。6. 功能测试与效果验证6.1 测试前静态检查接好电机先不运行 PWM。万用表测量母线电压是否正常确认驱动板没有短路。然后给一个极小的占空比输出比如三相都是 50%用示波器观察 PWM 波形是否正常、死区是否生效。6.2 低速空载旋转测试把目标转速设为100rpm或200rpm电压幅值先取母线电压的 10% 到 20%。如果电机开始平滑旋转说明三相相序和代码逻辑匹配。如果电机抖动但不转多半是三相电压的相序反了。交换任意两相即可。比如把ub和uc的计算顺序对调或者调换物理接线。6.3 正反转与转速修改通过串口下发目标转速观察正转和反转是否对称。改变目标转速后实际转速是否跟随。在低速到中速范围内相电流波形是否平滑。是否有明显的失步噪声。开环控制下实际转速不一定等于目标转速。所以这次测试的重点不是“精确”而是观察系统趋势是否正常。6.4 堵转与异常测试用小力矩堵住电机轴观察母线电流是否上升。开环控制没有力矩环堵转时电流会持续增长最终触发过流保护。这个测试可以验证保护逻辑是否可靠但要注意控制时间和电流阈值避免烧管子。堵转之后 PWM 关闭重新使能前要把目标转速拉回 0等电机停稳后再启动。6.5 串口调试接口与批量标定嵌入式项目一般没有 REST API但可以用串口做一个小型调试协议。这样写出的开环控制才能方便后续标定电压-转速曲线。void uart_command_handler(uint8_t cmd, float param) { switch (cmd) { case S: openloop_set_speed(param); // 设置目标转速 break; case V: openloop_set_voltage(param); // 设置电压幅值 break; case T: pwm_disable(); // 紧急停车 break; default: break; } }上位机可以用 Python 脚本批量扫点import serial import time ser serial.Serial(COM3, 115200, timeout0.1) for rpm in [100, 200, 300, 400, 500, 600]: ser.write(fS{rpm}\n.encode()) time.sleep(3) data ser.readline().decode().strip() print(rpm, data)这样可以把不同电压、不同转速下的电流和母线电压数据保存下来用于后续设计 V/F 曲线。7. 关键工程问题FOC 电流采样为什么要设置在下桥这个问题的答案很实际FOC 需要知道任意时刻的三相电流但并不是所有时刻都能采样。7.1 上桥采样和下桥采样的区别采样方式优点缺点下桥采样采样电阻一端接在低边 MOSFET 源级参考地稳定共模干扰小成本低当该相上桥导通时无法采样该相电流上桥采样理论上可以直接测任意相电流共模电压高需要差分放大和隔离电路电路复杂成本高大多数低成本 FOC 驱动板都采用下桥采样。电机相电流在 PWM 周期内的不同时间段会流过不同桥臂。只有当下桥 MOSFET 导通时电流才会流过下桥采样电阻。因此电流采样通常被安排在 PWM 周期的特定时刻也就是“下桥导通窗口”内。7.2 为什么不能随便采样如果 PWM 上桥导通该相电机端子被接到母线高边这时候测量下桥采样电阻读到的电流与相电流不对应。甚至在死区时间内续流路径和采样窗口会发生偏移。如果采样点选错ADC 读到的不是真实相电流电流环再努力也没用。更具体的做法是配置定时器为中央对齐模式。在计数器计数到周期中心或特定计数值时触发 ADC 采样。避开死区时间。采样触发后等待一小段稳定时间再读值。7.3 下桥采样的窗口限制下桥采样的代价是当某相占空比接近 100% 时该相下桥导通时间很短采样窗口不足。常见解决方法是把 PWM 周期设置足够短同时使用双电阻或三电阻采样方案并在扇区边界时刻重新选择可用采样窗口。“电流采样放在下桥”不是某个人的个人习惯而是低成本 FOC 驱动器的工程取舍。理解了这一点你就知道为什么 FOC 调试经常要先看 PWM 波形和采样触发点而不是直接调 PID。8. 从开环走向闭环为电流环、速度环、无感 FOC 做准备开环控制稳定之后下一步就是逐步加入反馈闭环。8.1 为什么 Clarke 变换只需要两相电流三相电流ia、ib、ic并不是完全独立的。对于常见的星形连接三相电机如果没有中线基尔霍夫电流定律给出ia ib ic 0所以知道任意两相电流第三相可以直接求出来ic -ia - ib因此 FOC 驱动板上常见的电流采样设计是两相采样而不是装三个采样通道这样能节省 ADC 通道和采样电阻成本。从实现上看采样哪两相不一定要求必须是 A/B 相任意两相都可以。实际工程中两相采样时通常会选择当前 PWM 周期内下桥导通窗口更宽的相方便 ADC 触发。8.2 Park 变换与 iq 产生磁场有了两相电流后通过 Clarke 变换得到静止坐标系下的ialpha和ibeta再通过 Park 变换得到旋转坐标系下的id和iq。关键点在于iq才是真正产生力矩的电流分量。因为 FOC 控制的是转子坐标下的电流方向iq与转子磁场垂直自然能产生最大力矩。id和转子磁场同轴一般被控制在 0除非做弱磁控制。很多初学者搞不清楚“iq 是怎么产生磁场的”实际上 d 轴和 q 轴上的电流都会产生定子磁动势q 轴电流产生与转子磁链正交的交轴磁场交轴磁场和永磁体磁场相互作用就产生了电机力矩。8.3 转子初始位置检测开环阶段不需要转子位置但闭环阶段必须先知道转子在哪。带编码器上电直接读编码器角度乘以极对数得到电角度。带霍尔传感器读取三相霍尔状态得到 60 度或 120 度间隔的粗略位置。无感方案高频注入法、预定位法、开环强拖启动等都是常见方法。开环强拖先用固定电角度速度把转子带动转速达到一定值后切换到无感观测器。第十二期做开环控制的意义就在这里它是无感 FOC 启动流程的第一步。8.4 速度环和电流环频率配置完整 FOC 通常有三环控制环常见频率参考作用电流环10kHz 到 20kHz控制 d/q 轴电流产生力矩速度环1kHz 到 2kHz控制转速位置环100Hz 到 500Hz控制机械位置这个配置只是常见参考具体频率要以电机和工程需求为准。开环控制阶段不需要三环但你需要先让定时器中断跑到一个稳定频率方便后续直接插入电流环。8.5 弱磁控制当电机高速运行到电压极限时逆变器输出电压已经达到母线电压上限再提高转速就需要减小反电动势。弱磁控制通过给 d 轴一个负电流对永磁磁场进行去磁等效降低反电动势从而在高速区获得更高转速。弱磁控制是在电流环稳定后才能继续深入的方向第十二期先知道这个概念即可。9. 常见问题与排查方法问题现象可能原因排查方式解决方案电机完全不转PWM 没输出或驱动板无使能用示波器看六路 PWM、检查驱动板 EN 脚补齐使能信号或改正 PWM 配置电机抖动、卡顿三相相序反了交换任意两相相序程序里交换 ub、uc 输出或调换电机线转速比预期快很多极对数设置错误确认电机铭牌或编码器数据修改pole_pairs转速上升缓慢电压幅值太小观察母线电流和相电压增加电压幅值或调整 V/F 曲线母线电压瞬间跌落电源限流或者启动电流过大用限流电源观察电流值增加斜坡时间降低目标电压电流波形很乱采样窗口选错或死区干扰用示波器对比 PWM 和 ADC 触发点调整 ADC 采样触发点发热严重开关频率过高或死区不足看驱动芯片温度、PWM 波形降低 PWM 频率或增加死区时间堵转保护不触发电流阈值太高或 ADC 采样不准打印采样值对比万用表修正电流换算系数和阈值重新启动时需要手动复位故障标志未清除检查错误状态机增加故障复位逻辑切换到闭环后电机乱飞角度方向不对检查编码器方向和电角度映射修正机械角到电角度的方向10. 最佳实践与下一步开环控制阶段的工程质量直接决定闭环阶段好不好调。建议从一开始就建立起几个好习惯第一次上电先用最低电压空载测试别用大电机和大电压调 bug。所有关键参数用结构体集中管理不要散落全局变量比如极对数、母线电压、PWM 频率、电流阈值。串口日志至少要打印目标转速、实际角度、三相占空比、母线电压、电流采样值后续调闭环全靠这些数据。斜坡启动是标配不要直接跳变目标转速。每个功能验证通过后立刻提交一个 git 版本。改坏代码时可以快速回退。过流保护必须独立于主循环同时用硬件和软件两层保护。高压电机实验要注意隔离不要用手触摸母线端子电机旋转时保持安全距离。涉及开源算法源码时确认开源协议后再学习或商用。这一期的终点是把电机“转起来”并且确认 PWM、驱动、采样、保护这条链路是健康的。下一阶段可以直接把电流采样、Clarke 变换、Park 变换加到现有工程里从开环 V/F 模式平滑切换到闭环电流环模式。最开始跑通的那段斜坡启动和电压矢量生成逻辑会成为整个 FOC 工程里最底层的基石。