1. 从一堆热搜词里看永磁同步电机与FOC的真实需求翻了一圈热搜词永磁同步电机、FOC、TI、逆变器、死区时间这几个词反复出现再加上STM32F103RCT6无刷电机FOC驱动代码、CubeMX配置FOC无刷电机、PMSM无感FOC控制、FOC滑模观测器、FOC转子初始位置检测这些长尾词基本能勾勒出关注这个方向的人到底在找什么。说白了大家不是想听教科书上讲永磁同步电机有多好而是想知道这东西到底怎么转起来的FOC算法为什么能把转矩做得这么平稳TI和STM32两条技术路线各自怎么落地逆变器那六个开关管到底怎么开合死区时间设多少才不炸管。我自己是从六步换相一路踩坑踩到FOC的。最早拿STM32F103RCT6搭无刷电机驱动用六步换相能转但低速抖动大、噪声明显、转矩脉动让人难受。后来切到FOC电流环一上电机立刻安静下来低速也能平稳输出大转矩。再后来接触TI的InstaSPIN方案和C2000系列发现TI在电机控制上的积累确实深很多底层细节人家已经封装好了但如果不理解原理出了问题照样抓瞎。这篇内容适合几类人看正在做电机控制项目但被FOC数学推导卡住的工程师想从六步换相升级到FOC但不知道从哪下手的嵌入式开发者用STM32或TI芯片做逆变器方案但被死区时间和电流采样搞晕的人以及想理解PMSM无感FOC控制、滑模观测器、转子初始位置检测这些具体技术点的朋友。我会把原理、FOC控制、TI方案实现这三块串起来讲中间穿插参数计算、实操步骤和我自己踩过的坑尽量让不同基础的人都能拿走能用的东西。2. 永磁同步电机原理先搞懂它为什么值得用FOC去控2.1 永磁同步电机的结构特点与运行机理永磁同步电机英文缩写PMSM核心特征就是转子上的磁场不是靠电流激励产生的而是靠永磁体。这一点和异步电机有本质区别。异步电机转子磁场需要定子电流感应出来所以转子总有转差而PMSM的转子磁场是现成的只要定子旋转磁场和转子磁场保持同步电机就能稳定输出转矩。从结构上看PMSM分两大类表贴式和内置式。表贴式永磁体贴在转子表面交直轴电感基本相等磁路对称控制模型简单适合做表贴式永磁同步电机DSP控制这类入门方案。内置式永磁体嵌在转子内部交直轴电感不等会产生磁阻转矩适合做FOC MTPA控制也就是最大转矩电流比控制能进一步榨出转矩。定子这边是三相绕组通入三相正弦电流后产生旋转磁场。转子的永磁体磁场被定子旋转磁场拖着走两者同步旋转。这里有个关键点如果定子磁场和转子磁场夹角为90度电角度产生的转矩最大。FOC的核心目标之一就是通过坐标变换和电流控制让定子电流矢量始终垂直于转子磁链矢量从而获得最大转矩输出。注意很多人一开始不理解“电角度”和“机械角度”的区别。机械角度是转子实际转过的物理角度电角度等于机械角度乘以极对数。比如4极电机极对数是2转子机械转一圈电角度转了720度。FOC算法里用的sin和cos全部是电角度搞错这个电机要么不转要么乱转。2.2 从abc到dq坐标变换到底在干什么FOC算法原理里最绕人的就是坐标变换。我当初看逆变器从abc到dq坐标下的数学推导公式一堆看完还是不知道为什么要这么变。后来想明白了坐标变换的本质就是“换一个视角看问题”。三相电流ia、ib、ic在空间上互差120度它们合成的电流矢量是一个在平面上旋转的矢量。Clarke变换把三相静止坐标系abc变成两相静止坐标系αβ相当于把三个分量压缩成两个正交分量减少计算量。Park变换再把αβ坐标系旋转到跟随转子磁场的dq坐标系d轴对准转子磁链方向q轴垂直于d轴。变换到dq坐标系之后原本交流变化的电流量变成了直流量。d轴电流控制磁场强弱q轴电流控制转矩大小。对于表贴式PMSMd轴电流通常控制为0所有电流都用来产生转矩这就是最简单的id0控制。对于内置式PMSM可以给d轴一个负电流利用磁阻转矩这就是MTPA控制的思路。这个变换过程用到的角度就是转子电角度。所以FOC算法中电角度和转子角度的关系必须搞清楚转子角度是机械角度电角度等于机械角度乘以极对数再加上初始位置偏移。无感FOC里转子角度靠观测器估算有感FOC里靠编码器或霍尔传感器获取。2.3 为什么PMSM需要FOC而不是六步换相六步换相永磁同步电机的做法很简单用霍尔传感器判断转子位置每60度电角度换一次相每次只给两相通电。这种方式实现容易成本低但问题很明显电流不是正弦的转矩脉动大低速时尤其明显噪声也大。FOC则完全不同。它通过SVPWM调制出三相正弦电流电流连续平滑转矩脉动小低速性能好效率也高。FOC和SVPWM配合能让逆变器的直流电压利用率比正弦PWM高出约15%。这就是为什么现在做PMSM无感FOC控制、做电流速度FOC控制基本都选FOC而不是六步换相。当然FOC的代价是算法复杂、对MCU算力有要求、电流采样和转子位置检测更讲究。但STM32F103RCT6这类带DSP指令和硬件浮点单元的芯片跑FOC已经够用了。TI的C2000系列更是专门为电机控制设计带CLA协处理器和高速ADC做FOC非常顺手。3. FOC控制核心细节电流环、速度环与观测器3.1 电流环与速度环的双闭环设计逻辑FOC控制通常采用双闭环结构外环是速度环内环是电流环。速度环根据目标转速和实际转速的偏差输出q轴电流参考值。电流环再根据q轴和d轴电流的偏差输出电压参考值最终交给SVPWM模块生成开关信号。为什么电流环要放在内环因为电流响应比速度响应快得多。电流环带宽通常在1kHz到5kHz速度环带宽在100Hz到500Hz。内环快、外环慢这样系统稳定抗扰动能力强。如果反过来速度环响应太快电流环跟不上系统容易振荡。电流环的PI参数整定有个经验公式。对于表贴式PMSMd轴和q轴电感相等电流环PI参数可以按以下方式估算// 电流环PI参数估算示例 // Kp_i Ls * bandwidth_i // Ki_i Rs * bandwidth_i // 其中Ls为定子电感Rs为定子电阻bandwidth_i为电流环带宽(rad/s) float Ls 0.0005f; // 定子电感 0.5mH float Rs 0.1f; // 定子电阻 0.1欧姆 float bw_i 2.0f * 3.14159f * 2000.0f; // 电流环带宽 2kHz float Kp_i Ls * bw_i; // 约6.28 float Ki_i Rs * bw_i; // 约1256实际调试时先把Ki设为0逐渐增大Kp直到电流响应出现轻微超调然后再加Ki消除稳态误差。速度环同理但速度环的Kp和Ki要比电流环小一个数量级左右。实操心得我调电流环的时候习惯先用一个很小的直流电压给电机让电机堵转然后给q轴电流一个阶跃指令用示波器看电流响应。这样能快速判断PI参数是否合适。如果电流上升太慢增大Kp如果振荡减小Kp或增大Ki的积分时间。3.2 SVPWM调制与死区时间的设置要点SVPWM是FOC算法的输出级。它把dq坐标系下的电压参考值转换回αβ坐标系再判断电压矢量落在哪个扇区计算相邻两个基本矢量的作用时间最终生成六路PWM信号驱动三相逆变器。三相两电平逆变器有六个开关管上下桥臂各三个。同一桥臂的上下管不能同时导通否则直流母线直接短路瞬间炸管。所以必须设置死区时间也就是在上管关断和下管开通之间插入一段延迟确保上下管不会同时导通。IGBT的死区时间通常设1到3微秒MOSFET可以设小一些0.5到1微秒。死区时间太短上下管可能直通太长输出电压失真电流波形畸变低速时尤其明显。死区时间造成的电压误差大约是// 死区电压误差估算 // V_error (T_dead / T_pwm) * V_dc // T_dead为死区时间T_pwm为PWM周期V_dc为直流母线电压 float T_dead 2e-6f; // 死区时间 2微秒 float T_pwm 50e-6f; // PWM周期 50微秒 (20kHz) float V_dc 48.0f; // 直流母线电压 48V float V_error (T_dead / T_pwm) * V_dc; // 约1.92V这个误差在低速小电压输出时占比很大会导致电流波形畸变。解决办法是做死区补偿根据电流方向在电压参考值上叠加一个补偿量。TI的电机控制库里有现成的死区补偿模块STM32这边需要自己写。3.3 无感FOC与滑模观测器的实现思路PMSM无感FOC控制的核心难点是转子位置估算。没有编码器或霍尔传感器怎么知道转子现在在哪常用方法有滑模观测器、龙伯格观测器、高频注入法等。FOC滑模观测器的思路是根据电机电压方程和电流方程构造一个观测器估算反电动势再从反电动势中提取转子角度。滑模观测器的优点是鲁棒性强对参数变化不敏感缺点是存在抖振需要加低通滤波器而滤波器又会引入相位延迟需要做角度补偿。滑模观测器的基本方程如下// 滑模观测器简化实现 // 估算电流与实际电流的偏差驱动滑模面 // 反电动势估算值从滑模控制量中提取 float e_alpha_est 0.0f; float e_beta_est 0.0f; float i_alpha_est 0.0f; float i_beta_est 0.0f; // 滑模增益 float K_slide 100.0f; // 电流估算 i_alpha_est (V_alpha - Rs * i_alpha_est - e_alpha_est) / Ls * Ts; i_beta_est (V_beta - Rs * i_beta_est - e_beta_est) / Ls * Ts; // 滑模控制量 float z_alpha K_slide * sign(i_alpha - i_alpha_est); float z_beta K_slide * sign(i_beta - i_beta_est); // 反电动势估算经过低通滤波 e_alpha_est (z_alpha - e_alpha_est) * wc * Ts; e_beta_est (z_beta - e_beta_est) * wc * Ts; // 转子角度估算 float theta_est atan2f(-e_alpha_est, e_beta_est);FOC转子初始位置检测是另一个难点。无感FOC在零速和低速时反电动势很小滑模观测器无法准确估算角度。常用方法是高频注入法在d轴注入高频电压信号通过检测q轴电流响应来判断转子凸极位置。表贴式PMSM凸极率小高频注入效果有限所以很多方案会先用开环强拖启动等转速起来再切换到闭环。4. TI方案实现从InstaSPIN到C2000的落地路径4.1 TI电机控制方案的整体架构与选型TI在电机控制领域有两套主流方案一套是基于C2000系列DSP的InstaSPIN方案另一套是基于MSP430或SimpleLink系列的通用方案。做PMSM FOC控制C2000是首选尤其是F28004x、F28002x这些带CLA和高速ADC的型号。TI官网有完整的电机控制SDK叫MotorControl SDK里面包含FOC、SVPWM、滑模观测器、死区补偿、电流采样校准等全套模块。用SysConfig配置TI的SCI、ADC、PWM等外设生成初始化代码能省很多时间。TI的CCFG配置文件用来设置芯片的时钟、引脚复用和安全选项配合SysConfig使用很方便。不过TI的工具有时候会出幺蛾子。比如TI XDS100V3 Channel A的驱动有感叹号这是驱动签名问题需要在设备管理器里手动更新驱动或者换用XDS110调试器。TI德州仪器的PPC3软件安装包有时候和最新版Windows不兼容建议在虚拟机里装或者用较老版本的Windows。注意TI的InstaSPIN-FOC库是封装好的调用起来简单但如果你想改观测器参数或者加自定义补偿需要理解底层原理。我建议先用InstaSPIN把电机转起来再逐步替换里面的模块这样学习曲线最平滑。4.2 基于C2000的FOC代码框架与关键配置TI C2000的FOC代码框架通常包含以下几个模块ADC采样与校准、Clarke/Park变换、电流环PI、速度环PI、SVPWM生成、观测器、保护逻辑。下面是一个简化的代码框架// TI C2000 FOC主循环框架 void main(void) { // 系统初始化 InitSysCtrl(); InitGpio(); InitPieCtrl(); InitPieVectTable(); // 外设配置 InitEPwm(); InitAdc(); InitSci(); // 电机参数初始化 motor_params.Rs 0.1f; motor_params.Ls 0.0005f; motor_params.flux 0.01f; motor_params.pole_pairs 2; // FOC初始化 FOC_init(foc_handle, motor_params); // 使能中断 EINT; ERTM; while(1) { // 后台任务通信、状态机、参数更新 Sci_Communication(); State_Machine(); } } // PWM中断服务函数执行FOC算法 __interrupt void epwm1_isr(void) { // 读取相电流 foc_handle.Ia Adc_ReadPhaseA(); foc_handle.Ib Adc_ReadPhaseB(); foc_handle.Ic -foc_handle.Ia - foc_handle.Ib; // 读取转子角度有感或观测器估算无感 foc_handle.theta Encoder_GetAngle() * pole_pairs; // Clarke变换 foc_handle.Ialpha foc_handle.Ia; foc_handle.Ibeta (foc_handle.Ia 2.0f * foc_handle.Ib) * 0.577350269f; // Park变换 foc_handle.Id foc_handle.Ialpha * cosf(foc_handle.theta) foc_handle.Ibeta * sinf(foc_handle.theta); foc_handle.Iq -foc_handle.Ialpha * sinf(foc_handle.theta) foc_handle.Ibeta * cosf(foc_handle.theta); // 电流环PI foc_handle.Vd PI_Calc(pi_d, foc_handle.Id_ref - foc_handle.Id); foc_handle.Vq PI_Calc(pi_q, foc_handle.Iq_ref - foc_handle.Iq); // 反Park变换 foc_handle.Valpha foc_handle.Vd * cosf(foc_handle.theta) - foc_handle.Vq * sinf(foc_handle.theta); foc_handle.Vbeta foc_handle.Vd * sinf(foc_handle.theta) foc_handle.Vq * cosf(foc_handle.theta); // SVPWM生成 SVPWM_Calc(foc_handle); // 清除中断标志 PieCtrlRegs.PIEACK.all PIEACK_GROUP3; }这段代码里Clarke变换用的是等幅值变换系数是2/3和1/√3。Park变换用的角度是电角度。SVPWM模块根据Valpha和Vbeta计算三个比较寄存器的值最终输出六路PWM。4.3 逆变器硬件设计与电流采样方案做逆变器程序设计硬件设计是绕不开的。三相逆变器的主电路是六个IGBT或MOSFET组成的三相全桥直流母线加电容滤波输出接电机三相。栅极驱动芯片选型很关键IR2136、DRV8301、DRV8323这些都是常用型号。DRV8301自带电流采样放大器和Buck降压集成度高适合小功率方案。电流采样有三种常见方式单电阻采样、双电阻采样、三电阻采样。单电阻采样成本最低但采样时机要求严格需要在PWM特定时刻触发ADC算法复杂。双电阻采样在上下桥臂各放一个采样电阻能重构三相电流是最常用的方案。三电阻采样每相都有采样电阻精度最高但成本也最高。采样电阻的阻值计算要考虑功耗和信噪比。假设最大相电流10A采样电阻0.01欧姆功耗是1W采样电压是0.1V。这个电压太小需要运放放大。DRV8301内部放大器增益可设通常设10到20倍把0.1V放大到1V到2V正好落在ADC量程内。实操心得电流采样最容易被忽略的是采样时刻和PWM的同步。如果ADC采样时刻和PWM开关时刻没对齐采到的电流要么是开关噪声要么是续流二极管导通时的电流完全不能用。TI的方案里ADC触发源直接配成PWM计数器过零或峰值STM32这边用定时器触发ADC也要配成同样的逻辑。5. STM32方案实现从CubeMX配置到FOC代码落地5.1 STM32F103RCT6的FOC资源评估与CubeMX配置STM32F103RCT6是Cortex-M3内核72MHz主频256KB Flash48KB RAM带3个ADC、4个定时器、多个USART和SPI。跑FOC够用但要注意算力分配。FOC算法本身大概需要10到20微秒如果PWM频率设20kHz周期50微秒留给FOC的时间很充裕。但如果还要跑无感观测器、通信协议、保护逻辑就要精打细算。CubeMX配置FOC无刷电机的步骤大致如下先配置时钟树把系统时钟设到72MHzADC时钟设到14MHz或12MHz。然后配置TIM1为PWM输出三个通道互补输出死区时间设1到2微秒。配置ADC1和ADC2为规则组扫描模式由TIM1触发采样相电流和母线电压。配置USART用于调试和通信。最后配置GPIO用于使能驱动芯片和读取霍尔传感器。// CubeMX生成的TIM1 PWM配置关键参数 // Prescaler 0 // Counter Period 3599 (72MHz / 3600 20kHz) // Dead Time 72 (72 * (1/72MHz) 1微秒) // PWM Mode PWM Mode 1 // CH Polarity High // Complementary Output Enable死区时间寄存器DTG的值和实际死区时间的关系是DTG值乘以时钟周期。72MHz时钟周期约13.9纳秒DTG72对应约1微秒。这个值要根据驱动芯片和开关管特性调整。5.2 STM32 FOC代码实现与电流环调试STM32的FOC代码可以用ST的Motor Control SDK也可以自己写。ST的SDK叫MC SDK支持FOC和六步换相配置工具叫Motor Profiler能自动测量电机参数。但ST的SDK封装得比较深想改底层不太方便。我自己的做法是参考ST的SDK自己写一套精简的FOC代码这样每个环节都清楚。// STM32 FOC电流环中断服务函数 void TIM1_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_IT_Update) ! RESET) { // 读取电流 Ia (ADC_GetValue(ADC_Channel_0) - Ia_offset) * Ia_gain; Ib (ADC_GetValue(ADC_Channel_1) - Ib_offset) * Ib_gain; Ic -Ia - Ib; // 读取电角度 theta Encoder_GetElecAngle(); // Clarke变换 Ialpha Ia; Ibeta (Ia 2*Ib) * 0.577350269f; // Park变换 Id Ialpha * cosf(theta) Ibeta * sinf(theta); Iq -Ialpha * sinf(theta) Ibeta * cosf(theta); // 电流环PI Vd PI_Calc(pi_d, Id_ref - Id); Vq PI_Calc(pi_q, Iq_ref - Iq); // 反Park变换 Valpha Vd * cosf(theta) - Vq * sinf(theta); Vbeta Vd * sinf(theta) Vq * cosf(theta); // SVPWM SVPWM_Calc(Valpha, Vbeta, CCR1, CCR2, CCR3); TIM_SetCompare1(TIM1, CCR1); TIM_SetCompare2(TIM1, CCR2); TIM_SetCompare3(TIM1, CCR3); TIM_ClearITPendingBit(TIM1, TIM_IT_Update); } }调试电流环的时候我习惯先用开环模式给一个固定的电压矢量让电机堵转然后观察电流波形。如果电流波形是正弦的说明SVPWM和采样没问题。然后切到电流闭环给q轴电流阶跃指令调PI参数。最后切到速度闭环调速度环PI。5.3 无感FOC在STM32上的实现与启动策略STM32上跑无感FOC观测器可以用滑模观测器或龙伯格观测器。滑模观测器计算量小适合STM32F103这类没有硬件浮点的芯片。但F103没有FPU浮点运算靠软件模拟速度慢。建议用定点数运算或者用STM32F4系列带FPU的芯片。无感FOC的启动策略通常分三段预定位、开环加速、闭环切换。预定位是给一个固定的d轴电流把转子拉到已知位置。开环加速是按一定斜率增加电角度和电压让电机转起来。等转速达到一定值反电动势足够大再切换到滑模观测器闭环控制。// 无感FOC启动状态机 typedef enum { STATE_IDLE, STATE_PREPOSITION, STATE_OPENLOOP, STATE_CLOSEDLOOP, STATE_FAULT } FOC_State_t; void FOC_StateMachine(void) { switch(foc_state) { case STATE_IDLE: if(start_cmd) foc_state STATE_PREPOSITION; break; case STATE_PREPOSITION: Id_ref 2.0f; // 给d轴电流 Iq_ref 0.0f; theta 0.0f; // 固定角度 if(preposition_timer 500) // 500ms foc_state STATE_OPENLOOP; break; case STATE_OPENLOOP: theta openloop_speed * Ts; Vq openloop_voltage; if(theta 2*PI) theta - 2*PI; if(openloop_speed switch_speed) foc_state STATE_CLOSEDLOOP; break; case STATE_CLOSEDLOOP: theta Observer_GetAngle(); // 正常FOC控制 break; case STATE_FAULT: // 关断PWM等待复位 break; } }开环加速的斜率很关键。斜率太大电机可能失步斜率太小启动太慢。一般根据电机惯量和负载转矩来调。我通常从10rad/s²开始试慢慢加大到电机能稳定启动为止。6. 常见问题与排查技巧实录6.1 FOC调试中的典型故障与排查思路做FOC调试遇到的问题五花八门但大部分集中在几个地方电流采样不对、角度不对、PI参数不对、死区时间不对。下面整理一个速查表方便对照排查。现象可能原因排查方法解决办法电机不转电流很大角度错误或相序错误检查电角度计算和相序重新校准编码器零位交换任意两相电机抖动噪声大电流环PI参数不合适观察电流波形减小Kp增大Ki积分时间低速时电流畸变死区时间过长测量死区电压误差减小死区时间或加死区补偿高速时失控观测器带宽不足检查反电动势估算增大观测器增益减小滤波截止频率启动失败开环加速斜率太大观察启动电流减小加速斜率增大预定位时间电流采样跳变ADC触发时刻不对用示波器看采样时刻调整ADC触发源为PWM过零或峰值母线电压波动大母线电容不足测量母线电压纹波增大母线电容加制动电阻注意排查故障时一定要先确保硬件没问题。我遇到过好几次调了半天软件最后发现是驱动芯片坏了或者采样电阻虚焊。所以第一步永远是量电压、量波形、确认硬件。6.2 死区时间与逆变器环流问题的处理经验逆变器环流是另一个让人头疼的问题。多台逆变器并联运行时如果载波不同步或者死区时间不一致会在逆变器之间产生环流。环流不仅增加损耗还可能烧毁器件。解决环流问题首先要确保所有逆变器的载波同步。可以用同一个时钟源或者用通信总线同步载波相位。其次死区时间要一致补偿策略也要一致。如果环流还是大可以在逆变器输出加共模电感增加环流阻抗。光伏逆变器和储能逆变器里环流问题更复杂因为涉及光伏板到逆变器到储能到配电箱接电原理整个系统的接地和绝缘设计都会影响环流。做组网逆变器PI控制的时候电流环的PI参数要兼顾环流抑制有时候需要加谐振控制器专门抑制特定频率的环流。6.3 工具链与开发环境常见问题TI的CCS和STM32的Keil是两大主流开发环境。Keil5绑定TI的芯片支持包有时候会出问题需要手动安装Device Family Pack。TI的SysConfig配置SCI的时候要注意引脚复用和时钟使能配错了串口不通。STM32CubeMX配置FOC的时候ADC采样时间要设够太短了采样不准太长了影响控制周期。一般设1.5到7.5个ADC时钟周期。PWM死区时间在CubeMX里直接填纳秒值工具会自动算DTG寄存器值。还有个小坑STM32F103RCT6的ADC1和ADC2可以同步采样但配置起来比单ADC复杂。如果只是做双电阻采样用ADC1扫描两个通道就够了没必要上双ADC。双ADC适合三电阻采样或者需要同时采样电压和电流的场景。7. 个人实操体会与后续扩展方向我在实际做PMSM FOC控制这些年最大的体会是原理要懂但不要陷在公式里出不来。FOC的核心就是坐标变换加PI控制加SVPWM把这三块搞透剩下的都是工程细节。工程细节里电流采样和转子角度是最容易出问题的把这两个搞定FOC就成功了一大半。TI的方案适合快速落地尤其是InstaSPIN基本不用调参数就能转。但如果你想深入理解FOC建议自己从零写一遍代码哪怕先用STM32F103跑定点数版本跑通了再换F4或TI的C2000。自己写一遍比看十遍书都管用。这个内容后续还可以往几个方向扩展一是无感FOC的观测器优化比如用扩展卡尔曼滤波替代滑模观测器低速性能更好二是MTPA控制和弱磁控制适合内置式PMSM和高速应用三是多电机同步控制做组网逆变器或者多轴运动控制四是故障诊断比如永磁同步电机匝间短路模型构建用来做早期故障检测。每个方向都够写一篇长文有机会再展开聊。
