
1. 从“升魂浩荡”说起这个标题到底在讲什么第一次看到“升魂浩荡FOC驱动~程序~电流环”这个标题我愣了几秒。“升魂浩荡”大概率是某个项目、某个板子或者某个社群的代号带着点中二气息但后面三个词非常实在FOC驱动、程序、电流环。把这三个词连起来指向的东西就非常明确了——一套永磁同步电机PMSM的磁场定向控制FOC程序而且重点落在电流环这个最内层、最要命的控制回路上。如果你正在做电机控制或者刚接手一个FOC项目大概率会遇到这样的场景电机能转但一加载就抖电流波形看着像锯齿PI参数调了半天转速一上去就啸叫。这些问题十有八九都出在电流环上。电流环是FOC三环控制电流环、速度环、位置环里最内环它的带宽决定了整个系统的响应上限。电流环没调好外面的速度环、位置环再怎么折腾都是白搭。这篇内容适合谁看如果你已经知道Clarke变换、Park变换是什么能看懂SVPWM的基本原理但一到实际调电流环就抓瞎那这篇就是写给你的。如果你是完全的新手也没关系我会把关键概念用生活化的方式讲清楚让你知道每一步在干什么、为什么这么干。整篇内容围绕FOC电流环的程序实现展开从采样时刻、坐标变换、PI整定、解耦补偿到实际调试中遇到的坑尽量把我知道的都倒出来。提示本文涉及的代码示例以STM32 HAL库为参考平台但控制逻辑和参数整定思路适用于任何MCU平台。2. 电流环在FOC里的位置为什么它是最不能糊弄的一环2.1 三环控制的嵌套关系与带宽分配FOC的经典结构是三个闭环嵌套最外层是位置环中间是速度环最内层是电流环。这三个环的带宽必须拉开差距通常遵循“内环带宽是外环的5到10倍”这个经验法则。什么意思假设你的速度环带宽是100Hz那电流环带宽至少要做到500Hz到1kHz。如果电流环带宽不够速度环的输出——也就是电流环的指令——还没来得及被电流环跟踪上速度环就已经开始修正了两个环互相打架系统必然振荡。我见过不少初学者速度环PI参数调得挺猛结果电机一转起来就发出刺耳的啸叫。用示波器看q轴电流指令和实际q轴电流发现实际电流根本跟不上指令相位滞后严重。这就是典型的电流环带宽不足。你把速度环参数降下来啸叫消失了但响应又慢得像蜗牛。问题的根子不在速度环而在电流环。电流环的带宽受什么限制主要三个因素PWM开关频率、采样频率、以及PI控制器的参数。PWM频率决定了你每个周期能更新几次电压矢量采样频率决定了你多久能拿到一次电流反馈。按照香农采样定理采样频率至少是信号频率的两倍但在实际工程中为了获得足够的相位裕度采样频率通常是电流环目标带宽的10倍以上。比如你的PWM频率是20kHz那电流环带宽做到1kHz左右是比较合理的再高就受限于采样和计算延迟了。2.2 电流环的输入输出到底是什么从程序的角度看电流环就是一个函数输入是d轴电流指令(i_d^)、q轴电流指令(i_q^)以及从ADC读回来的三相电流(i_a, i_b, i_c)或者两相输出是d轴电压指令(u_d^)和q轴电压指令(u_q^)再经过反Park变换变成(\alpha\beta)轴电压最后送给SVPWM模块去生成六路PWM波。这个函数每个PWM周期执行一次通常在PWM下溢中断或者中央对齐模式的中断里调用。执行顺序非常讲究先采样电流再做Clarke变换然后做Park变换得到实际的(i_d, i_q)接着和指令值做差误差送进PI控制器PI输出经过限幅和解耦补偿最后反Park变换输出。这个顺序不能乱尤其是采样时刻后面会专门讲。对于表贴式永磁同步电机SPMSM通常采用(i_d^* 0)的控制策略因为SPMSM的d轴和q轴电感相等磁阻转矩为零让d轴电流为零可以用最小的电流产生最大的转矩。对于内置式永磁同步电机IPMSM因为凸极效应存在可以利用磁阻转矩采用MTPA最大转矩电流比控制这时候(i_d^*)就不为零了需要根据转矩指令查表或者实时计算。不管哪种策略电流环的任务都是一样的让实际的(i_d, i_q)快速、准确地跟踪指令值。2.3 为什么电流环程序容易出问题电流环程序出问题通常不是逻辑写错了而是时序和参数没搞对。时序问题包括采样时刻不对导致采到开关噪声计算延迟导致输出电压滞后一个周期中断优先级冲突导致电流环执行时间抖动。参数问题主要是PI参数整定不当或者解耦补偿没做导致d轴和q轴互相干扰。还有一个容易被忽略的点电流传感器的校准。如果电流传感器的零漂没校准或者三相不平衡Clarke变换出来的(\alpha\beta)电流里就带着直流偏置Park变换后变成d轴和q轴上的周期性扰动电流环再努力也消不掉。我遇到过好几次电流波形上叠加了一个和电角度同频的波动查了半天以为是PI参数问题最后发现是B相电流传感器的零漂没校准。3. 电流采样时刻一个被很多人忽略的致命细节3.1 为什么采样时刻决定了电流环的成败在FOC程序里电流采样不是随便什么时候采都行的。PWM输出的是方波MOSFET在开关瞬间会产生大量的高频噪声如果在这个时刻采样ADC读回来的值全是毛刺电流环直接疯掉。正确的采样时刻应该避开开关瞬间选择在PWM周期中间、下桥臂全部导通的时候采样。具体来说对于中央对齐的PWM模式计数器从0向上计数到峰值再向下计数到0。当计数器等于峰值时上桥臂全开下桥臂全关这时候采样的是母线电流不是相电流。当计数器等于0时下桥臂全开这时候三相电流全部流过下桥臂的采样电阻或者电流传感器可以采到真实的相电流。所以采样时刻应该设置在计数器等于0的时刻也就是PWM周期的起点或终点。但这里有个坑如果你用的是单电阻采样那采样时刻还要考虑电流重构的问题。单电阻采样在一个PWM周期内只能采到两个相的电流第三个相靠基尔霍夫电流定律算出来。而且当两个有效电压矢量的作用时间太短时采样窗口不够重构出来的电流误差很大。所以单电阻采样对PWM占空比有最小限制通常要求有效矢量作用时间大于某个阈值比如2微秒。三电阻采样或者电流传感器采样就没有这个问题每个相都可以独立采样。3.2 采样与PWM更新的时序配合在STM32里通常用定时器的中央对齐模式产生PWM同时在定时器的更新事件UEV或者比较匹配事件里触发ADC采样。比较推荐的做法是用定时器的更新事件触发ADCADC转换完成后产生中断在中断里执行电流环计算计算出的新占空比在下一次更新事件时生效。这样整个时序是确定的每个PWM周期执行一次电流环没有抖动。但这里有一个周期的延迟你在第n个周期采样电流计算出的电压指令在第n1个周期才真正作用到电机上。这个延迟在控制理论里叫做“一拍延迟”它会降低系统的相位裕度。对于高带宽的电流环这个延迟的影响不可忽略。解决办法有两个一是提高PWM频率让延迟的绝对时间变小二是在控制器设计时考虑这个延迟比如在PI控制器里加入相位超前补偿。我实测下来20kHz的PWM频率下一拍延迟大约是50微秒。对于1kHz带宽的电流环50微秒的延迟对应18度的相位损失还在可接受范围内。但如果你的PWM频率只有10kHz延迟变成100微秒相位损失36度电流环的相位裕度就很紧张了容易振荡。3.3 采样窗口与ADC配置的实操细节在配置ADC的时候有几个参数需要特别注意采样时间和转换时间。采样时间太短采样电容充不满读回来的值偏小采样时间太长虽然精度够了但可能错过采样窗口。对于STM32G4或者F4系列ADC时钟通常配置在几十MHz采样时间设置为几个到几十个时钟周期。我的经验是采样时间设置为ADC时钟周期的10到20倍比较稳妥具体要看你的信号源阻抗。还有一个细节ADC的触发源。一定要用硬件触发不要用软件触发。软件触发的时间不确定会导致采样时刻抖动电流环的噪声会明显增大。用定时器的TRGO事件触发ADC时序最干净。另外如果用的是内置运放加采样电阻的方案还要注意运放的带宽和压摆率。运放带宽不够电流阶跃响应会变慢采样到的值不能真实反映电流的瞬时值。一般选择带宽在几MHz以上的运放压摆率在几V/微秒以上。4. 坐标变换与PI控制器电流环的核心运算4.1 Clarke和Park变换的程序实现Clarke变换把三相电流(i_a, i_b, i_c)变成两相静止坐标系下的(i_\alpha, i_\beta)。对于三相平衡系统(i_a i_b i_c 0)所以只需要采两相电流就够了。程序里通常这样写// Clarke变换等幅值变换 i_alpha i_a; i_beta (i_a 2.0f * i_b) * ONE_BY_SQRT3; // 1/sqrt(3) ≈ 0.57735这里用的是等幅值变换系数是(2/3)的变形。等幅值变换的好处是变换后的电流幅值和原三相电流幅值相等PI参数整定起来更直观。也有用等功率变换的系数不同但控制效果一样只是PI参数需要相应调整。Park变换把(i_\alpha, i_\beta)旋转到转子坐标系下的(i_d, i_q)// Park变换 i_d i_alpha * cos_theta i_beta * sin_theta; i_q -i_alpha * sin_theta i_beta * cos_theta;这里的(\theta)是转子电角度来自编码器或者观测器。(\sin\theta)和(\cos\theta)的计算可以用查表法或者CORDIC算法。查表法速度快但精度受表长限制CORDIC精度高但计算量大一些。在STM32G4或者F4这种带FPU的MCU上直接调用sinf()和cosf()也来得及但要注意这两个函数的执行时间。我一般用查表加线性插值速度和质量兼顾。反Park变换把(u_d^, u_q^)变回(\alpha\beta)轴// 反Park变换 u_alpha u_d * cos_theta - u_q * sin_theta; u_beta u_d * sin_theta u_q * cos_theta;然后(u_\alpha, u_\beta)送给SVPWM模块计算三个桥臂的占空比。4.2 PI控制器的离散化与限幅电流环的PI控制器在连续域是(G(s) K_p K_i/s)离散化到z域通常用后向差分或者双线性变换。后向差分最简单// 后向差分PI error i_ref - i_fb; integral Ki * error * Ts; // Ts是控制周期 output Kp * error integral;但这样写有个问题积分项没有限幅当误差持续存在时积分项会一直累加直到饱和。一旦饱和即使误差反向积分项也需要很长时间才能退回来这就是积分饱和。解决办法是加抗积分饱和逻辑当输出达到限幅值时停止积分累加或者只累加让输出退出饱和的方向。// 带抗积分饱和的PI error i_ref - i_fb; output Kp * error integral; if (output out_max) { output out_max; if (error 0) integral Ki * error * Ts; // 只允许退饱和方向积分 } else if (output out_min) { output out_min; if (error 0) integral Ki * error * Ts; } else { integral Ki * error * Ts; }还有一种更优雅的做法是反计算抗饱和把限幅前后的差值反馈到积分器输入端乘以一个系数(K_c)。这种方法不需要条件判断代码更简洁效果也不错。4.3 d轴q轴解耦为什么需要以及怎么做永磁同步电机的电压方程在d-q坐标系下是这样的[ u_d R i_d L_d \frac{di_d}{dt} - \omega_e L_q i_q ] [ u_q R i_q L_q \frac{di_q}{dt} \omega_e (L_d i_d \psi_f) ]注意方程右边的第三项(-\omega_e L_q i_q)和(\omega_e (L_d i_d \psi_f))。这两项是耦合项意思是d轴电压会影响q轴电流q轴电压也会影响d轴电流。当电机转速升高时耦合项变大如果不补偿d轴和q轴会互相干扰电流环的动态性能变差。解耦补偿的做法很简单在PI控制器的输出上加上耦合项的估计值。对于d轴[ u_d^* PI_d(i_d^* - i_d) - \omega_e L_q i_q ]对于q轴[ u_q^* PI_q(i_q^* - i_q) \omega_e (L_d i_d \psi_f) ]这样PI控制器只需要负责克服电阻和电感引起的动态耦合项被前馈补偿掉了。程序里就是多两行乘法和加法// 解耦补偿 u_d_out pi_d_output - omega_e * Lq * i_q; u_q_out pi_q_output omega_e * (Ld * i_d psi_f);但这里有个坑电感参数(L_d, L_q)和磁链(\psi_f)的准确性。如果参数不准解耦补偿反而会引入误差。尤其是(L_d, L_q)随电流变化饱和效应用固定值补偿在重载时效果会打折扣。我的做法是在低速时耦合项小可以不做解耦在高速时耦合项大必须做解耦而且电感参数要用额定电流附近的饱和值。如果实在拿不准可以只补偿(\omega_e \psi_f)这一项因为这一项最大而且(\psi_f)相对稳定。5. PI参数整定从理论计算到实际调试5.1 用电机参数算出PI的初始值PI参数不是瞎试出来的理论上可以根据电机的电阻和电感算出一个初始值。对于电流环如果采用“零极点对消”的整定方法让PI控制器的零点对消掉电机传递函数的极点那么[ K_p \omega_c L ] [ K_i \omega_c R ]其中(\omega_c)是期望的电流环带宽单位rad/s(L)是d轴或q轴电感(R)是定子电阻。比如你的电机(L 1mH)(R 0.5\Omega)期望带宽1kHz(\omega_c 2\pi \times 1000 \approx 6283 rad/s)那么[ K_p 6283 \times 0.001 6.28 ] [ K_i 6283 \times 0.5 3141 ]这个计算值作为初始值然后在实际调试中微调。注意这里的(K_i)是连续域的积分系数离散化后要乘以采样周期(T_s)。如果(T_s 50\mu s)那么离散域的积分系数是(3141 \times 50e-6 \approx 0.157)。但实际调试时你会发现理论值往往偏大或偏小。偏大的原因是没考虑PWM延迟和采样延迟偏小的原因是电机参数不准。所以理论值只是一个起点真正的参数要在实验中调。5.2 实际调试中的“听声音、看波形”法调电流环PI参数最直接的方法是给q轴一个阶跃指令看q轴电流的响应。具体操作让电机处于堵转状态或者空载但转子锁住给(i_q^*)一个从0到额定电流20%的阶跃用示波器或者上位机看(i_q)的波形。如果(i_q)上升太慢说明(K_p)太小增大(K_p)。如果(i_q)有超调说明(K_p)太大或者(K_i)太大先减小(K_i)再微调(K_p)。如果(i_q)有稳态误差说明(K_i)太小增大(K_i)。如果(i_q)振荡说明带宽太高或者相位裕度不够降低(K_p)和(K_i)。我个人的经验是先把(K_i)设为0只调(K_p)让电流响应快速但不振荡然后逐渐增大(K_i)直到稳态误差消除但不要引入明显的超调。整个过程要反复迭代因为(K_p)和(K_i)是耦合的。还有一个“听声音”的技巧电机在电流环振荡时会发出特定频率的啸叫。啸叫频率越高说明振荡频率越高通常是(K_p)太大啸叫频率低可能是(K_i)太大导致的低频振荡。当然这个方法需要经验新手还是看波形更靠谱。5.3 不同电机和工况下的参数差异PI参数不是一成不变的。同一个电机空载和满载时的电感可能差一倍饱和效应所以重载时可能需要降低(K_p)。不同电机的参数差异更大小功率电机的电阻大、电感小(K_p)和(K_i)的绝对值都比较大大功率电机的电阻小、电感大(K_p)和(K_i)的绝对值比较小。另外母线电压也会影响电流环的动态。母线电压高电流上升快同样的PI参数下响应更快但超调也可能更大。如果应用场景中母线电压变化范围大可能需要根据母线电压调整PI参数或者至少调整输出电压的限幅值。我一般会在程序里做两套PI参数一套用于低速大电流启动、堵转一套用于高速小电流巡航。切换点根据转速或者电流幅值来定。这样可以在不同工况下都获得不错的动态性能。6. 那些年我在电流环上踩过的坑6.1 电流波形上的“毛刺”从哪来有一次调试一台低压大电流的PMSM电流波形上全是毛刺PI参数怎么调都消不掉。用示波器看ADC输入引脚发现信号上确实有高频噪声。排查过程先怀疑是PWM噪声耦合把采样时刻从PWM边沿移到周期中间毛刺小了一点但没消失然后怀疑是电流传感器供电不稳换了LDO供电还是没解决最后发现是采样电阻的走线太长而且没有用地线包围引入了空间噪声。解决办法把采样电阻尽量靠近运放走线尽量短用地线包围信号线运放输入端加RC低通滤波截止频率设在电流环带宽的5到10倍。改完之后毛刺基本消失电流环的PI参数也能调上去了。这个坑的教训是电流环的噪声问题一半在软件一半在硬件。软件上能做的有限硬件设计不好软件再怎么滤波都是治标不治本。6.2 积分饱和导致的启动过流另一个坑是启动时的过流保护。电机启动瞬间转速为零反电动势为零电流环的指令是额定电流但实际电流上升需要时间。如果积分项没有限幅误差会一直累积积分项饱和到很大值。等电流达到指令值后积分项需要很长时间才能退回来导致电流冲过头触发过流保护。解决办法就是前面说的抗积分饱和。我现在的做法是积分项单独限幅限幅值根据母线电压和电机参数计算确保积分项不会超过物理上可能的电压输出。同时在启动阶段给电流指令加一个斜坡让指令慢慢上升而不是直接给阶跃。这样电流环的跟踪误差小积分项也不会饱和。6.3 电角度偏差带来的转矩波动还有一个隐蔽的坑电角度偏差。如果编码器的零位没校准或者观测器估计的角度有偏差Park变换用的(\theta)就不准。角度偏差会导致d轴和q轴电流的交叉耦合表现为转矩波动和电流波形畸变。我遇到过一次电机在低速时转矩波动很大电流波形上叠加了一个和电角度同频的波动。查了半天PI参数最后用示波器同时看编码器Z信号和电角度发现零位偏了大约10度。重新校准编码器零位后转矩波动明显减小。校准编码器零位的方法给d轴一个固定电压q轴电压为零让电机转到d轴对齐的位置这时候的电角度就是零位。或者用高频注入法在d轴注入高频电压检测q轴电流响应找到最大响应对应的角度就是零位。具体方法取决于你的硬件和精度要求。6.4 中断优先级冲突导致的时序抖动在多任务系统中电流环中断的优先级必须是最高的否则会被其他中断打断导致执行时间抖动。我见过一个项目电流环中断优先级设得比较低结果串口接收中断一来电流环就延迟了几十微秒电流波形上出现周期性的抖动。把电流环中断优先级提到最高后问题消失。另外电流环中断服务程序里不要做耗时操作比如浮点除法、查表、打印调试信息。所有能提前算好的东西都提前算好中断里只做最必要的运算。如果实在需要调试输出用DMA把数据搬到内存在主循环里再处理。7. 从“能转”到“转得好”电流环的进阶优化7.1 电流环带宽的实测方法想知道你的电流环带宽到底是多少可以用频率响应法。给q轴电流指令叠加一个小幅度的正弦扰动频率从低到高扫同时记录指令和实际电流的幅值和相位差。当实际电流的幅值降到指令的0.707倍-3dB时对应的频率就是带宽。实际操作中可以用上位机发送不同频率的正弦指令用示波器或者数据采集卡记录波形然后离线分析。如果嫌麻烦也可以用阶跃响应的上升时间来估算带宽约等于(0.35 / t_r)其中(t_r)是上升时间从10%到90%。比如上升时间是0.5ms带宽大约是700Hz。实测带宽和理论计算值对比可以判断你的电流环还有多少优化空间。如果实测带宽远低于理论值说明有额外的延迟或者噪声限制了性能需要从采样、计算、硬件等方面找原因。7.2 前馈补偿让电流环“预判”指令变化电流环的PI控制器是反馈控制只有误差出现了才动作。如果指令变化很快反馈控制总是慢半拍。前馈补偿的思路是把指令的变化直接加到输出上让系统提前动作。对于电流环前馈项可以是指令的微分乘以电感或者反电动势前馈。反电动势前馈特别有用当电机转速变化时反电动势变化如果不前馈电流环需要等误差出现才调整电压响应会滞后。加上反电动势前馈后电压指令里直接包含了反电动势的估计值电流环只需要处理误差部分动态性能明显提升。程序里就是多一行u_q_out pi_q_output omega_e * (Ld * i_d psi_f) omega_e * psi_f; // 最后一项是反电动势前馈注意不要重复补偿解耦项里已经包含了(\omega_e \psi_f)如果再加就重复了。实际上解耦补偿本身就起到了前馈的作用所以做好解耦前馈的大部分工作就完成了。7.3 弱磁工况下的电流环调整当电机转速超过基速反电动势接近母线电压时需要弱磁控制。弱磁的本质是给d轴一个负电流削弱磁场从而降低反电动势。这时候(i_d^)不再为零而是负值。电流环的任务变成了同时跟踪(i_d^)和(i_q^*)而且d轴和q轴的耦合更强。弱磁工况下电流环的PI参数可能需要调整因为工作点变了电感也变了。另外电压限幅变得更重要如果电压指令超过母线电压能提供的最大值电流环就饱和了实际电流跟不上指令。这时候需要电压闭环或者电流指令修正把电压控制在限幅以内。我一般会在弱磁区降低电流环带宽牺牲一点动态性能换取稳定性。同时电压限幅值要留足够的裕量避免进入饱和。8. 写在最后一些零散但有用的经验调试FOC电流环这些年我最大的体会是理论指导方向但细节决定成败。书本上讲的Clarke变换、Park变换、PI控制器都是理想情况下的模型。实际系统中采样延迟、PWM延迟、参数变化、噪声干扰每一个都会影响最终性能。把这些问题一个一个解决掉电流环自然就稳了。如果你刚开始做FOC我的建议是先用最简单的开环V/F控制让电机转起来确认硬件没问题然后加上电流环用理论计算的PI参数看电流能不能跟踪上最后再加速度环和位置环。每一步都确认稳定了再往下走不要一上来就把三个环全加上出了问题根本不知道是哪里的问题。还有示波器是你的好朋友。看电流波形、看PWM波形、看角度信号很多问题一眼就能看出来。不要只盯着代码看代码逻辑对不代表实际运行就对。硬件和软件的交互只有用仪器才能看清楚。最后分享一个小技巧在调试电流环时把d轴和q轴的PI参数分开调。先调d轴因为d轴指令通常是零或者固定值响应好坏容易判断d轴调好了再调q轴。两个轴的参数可以不一样因为d轴和q轴的电感可能不同IPMSM尤其明显。分开调比一起调容易得多。