拓冰建站拓冰建站
首页 / 资讯中心 / 正文

FOC无感电机控制工程实战:坐标变换、SVPWM与电流环调参

简介这是一套面向嵌入式开发者的FOC电机控制算法源码工程覆盖单电压闭环与电压电流双闭环两种实现方案。资源围绕磁场定向控制原理包含Clarke/Park变换、PID调节、SVPWM调制等核心模块并针对有传感器与无传感器FOC的差异、电流采样高低端方式选择给出可运行的程序框架适合正在学习无刷电机控制、渴望从理论过渡到工程落地的单片机开发者。压缩包共687个文件以C/C源码与头文件为主含216个h文件、119个c文件另有STM32CubeIDE工程相关的.ioc、.mxproject、.uvprojx等项目文件以及.hex、.bin、.axf等编译产物可辅助核验烧录结果整个资源包约27.76MB目录结构完整清晰。已有677人学习下载在FOC入门与调试中具有较高参考价值。资料不只是一堆零散代码而是完整的项目级工程既包含SVPWM和坐标变换等算法源文件也包含基于STM32F1 HAL库的底层驱动与中间层配置文件读者可借此查看样例工程实际组成理解闭环控制流程和不同反馈路径的实现细节缩短自行搭建FOC控制程序的时间。1. 单电压闭环为什么不稳FOC开环控制的边界与适用场景拿到这个工程时最显眼的是my_foc_project.axf和myFOCDriver.bin这两个编译产物周围散落着libarm_cortexM4lf_math.a和一堆stm32f1xx_hal_i2c.c、stm32f1xx_hal_tim.c。把程序烧进板子电机能转但一加负载转速就肉眼可见地往下掉往上抬 Vq 又开始啸叫。原因不复杂所谓单电压闭环是把目标 Vq 直接送进反 Park 变换和 SVPWM完全不经过电流 PI电机处于电流开环状态。转子的反电动势会随着负载和转速变化给定电压不变时相电流就随负载漂移力矩自然不受控。这个模式适合验证坐标变换、PWM 时序和驱动板硬件也适合无感 FOC 启动阶段的拉入同步但要做真正的速度和力矩控制必须在中间补上电流环。这篇文章把这个工程的代码路径完整拆一遍从坐标变换到 SVPWM再到电流采样和双闭环调参最后落在堵转检测和初始位置辨识两个实战问题上。2. Clark/Park变换代码拆解两相电流采样的数学依据与实现2.1 只采两相电流的数学前提三相电流之和为零FOC 采样里最经典的问题就是 Clarke 变换为什么只需要两相电流。原因是星形连接电机的中性点不外接基尔霍夫电流定律直接给出 iu iv iw 0所以第三相永远可以算出来。实际工程里ADC 偏置和采样噪声会让这个等式不精确直接算出来的 iw 会带直流偏置高速时表现为转矩脉动。常见做法是低端三电阻全部采样再取一遍平均或者至少在主循环里对 ADC 零电流偏置做滑动平均。#include arm_math.h #define SQRT3_2 0.86602540378f /* sqrt(3) / 2 */ typedef struct { int16_t adc_iu_raw; int16_t adc_iv_raw; int16_t adc_offset; /* 零电流时刻的ADC读数12位下通常接近2048 */ float current_scale; /* ADC码到安培的比例系数 */ float i_alpha; float i_beta; float id; float iq; } clarke_park_t; void clarke_park_transform(clarke_park_t *foc, float theta_elec) { /* 采样得到的是原始ADC码先减偏置再乘比例换算成安培 */ float iu (foc-adc_iu_raw - foc-adc_offset) * foc-current_scale; float iv (foc-adc_iv_raw - foc-adc_offset) * foc-current_scale; float iw -(iu iv); /* 三相电流之和为零第三相直接算出 */ /* Clarke变换等幅值形式系数2/3保证输出幅值与相电流一致 */ foc-i_alpha (2.0f / 3.0f) * (iu - 0.5f * iv - 0.5f * iw); foc-i_beta (2.0f / 3.0f) * (SQRT3_2 * (iv - iw)); /* Park变换把交流量转到与转子同步旋转的dq坐标系 */ float cos_t arm_cos_f32(theta_elec); float sin_t arm_sin_f32(theta_elec); foc-id foc-i_alpha * cos_t foc-i_beta * sin_t; foc-iq -foc-i_alpha * sin_t foc-i_beta * cos_t; }代码里用等幅值形式因为后面的 SVPWM 输出限幅、调制比计算都是按等幅值展开的系数统一不容易乱。注意一个容易忽略的细节把 iw -(iuiv) 代入 Clarke 公式后i_alpha 实际就等于 iui_beta 等于 (iu 2*iv)/sqrt(3)所以只采两相在数学上是严格可行的只是 iu 的偏置误差会完整投射到 alpha 轴这就是为什么零电流偏置校准比很多人想象的重要。这个工程里能看到libarm_cortexM4lf_math.a但 STM32F103 是 Cortex-M3 没有 FPUM4F 的 DSP 库在这里无法直接调用我一般会把它替换成libarm_cortexM3l_math.a否则arm_cos_f32这类函数在链接阶段就可能出问题。变换形式系数特点等幅值2/3输出幅值与相电流一致SVPWM 调制比计算直观等功率sqrt(2/3)变换前后功率守恒适合电机损耗分析和观测器推导2.2 从静止坐标系到同步旋转坐标系变换代码与角度来源Park 变换的意义是把 Clark 输出的两个交流量转成直流量这样 PI 控制器才能做到无静差跟踪。iq 对应力矩分量id 对应励磁分量对于表贴式永磁同步电机目标通常是 id 0让全部电流都用来出力。电角度从哪来取决于有感和无感有感接霍尔或 ABZ 编码器工程里stm32f1xx_hal_i2c.c多半是 I2C 读磁编码器比如 AS5600 这类 12 位角度传感器无感则用滑模观测器加锁相环估算。/* 电角度 机械角度 * 极对数 */ float theta_mech encoder_get_mech_angle(); /* 单位rad */ float theta_elec theta_mech * MOTOR_POLE_PAIRS;极对数不能搞错否则电流环会发散。ABZ 编码器的问题是上电时刻机械角度未知一般做法是上电先给一个固定 Vd 电压矢量拉转子到已知位置再清零编码器计数磁编码器没有这个问题但 I2C 读取延迟要考虑进去读取频率太低时电角度滞后会导致 iq 实际变小。这个工程里 i2c 文件和 tim 文件同时出现说明角度链路大概率是 I2C 磁编码器加定时器捕获做速度计算速度由电角度差分得到而不是单独的光电编码器脉冲计数。2.3 符号约定Park变换的正负号怎么自查FOC 代码里最隐蔽的问题是符号约定不一致。Park 变换有的资料写成 iq i_beta * cos_t - i_alpha * sin_t有的写成 iq -i_alpha * sin_t i_beta * cos_t两种写法只差一个符号但对同一个电机闭环之后的表现一个是收敛一个是正反馈。我踩过一次坑双闭环一使能iq 给定 0.1A电机直接加速到失控就是角度方向反了。自查方法很简单离线给一个固定电压矢量比如让 Vd 20% 母线电压、Vq 0用示波器看 id 反馈的正负如果 id 反馈随 Vd 增加反而变负且越来越大说明 Park 变换的角度方向或者 dq 轴定义反了把 iq 公式里的负号去掉再试。这个检查最好放在闭环之前做五分钟能省掉一晚上的定位时间。3. SVPWM电压矢量调制七段式实现与STM32F1定时器3.1 逆变器的八个开关状态与SVPWM的调制增益电压型逆变器只有六个功率管上桥和下桥互补导通所以开关状态一共八种六个非零电压矢量加两个零矢量。SVPWM 的思想不是把三相电压分别调出来而是用相邻两个非零矢量和零矢量在一个 PWM 周期内做时间平均合成任意方向和幅值的电压矢量。相比正弦 SPWMSVPWM 的母线电压利用率可以提升约 15%也就是同样的母线电压电机能跑到的最高转速更高这是它成为 FOC 标配的原因。3.2 零序注入法实现七段式SVPWM代码与CCR映射教科书里讲 SVPWM 通常从扇区判断开始很直观但代码分支多。工程里更常见的是零序注入法先由 v_alpha 和 v_beta 算出三相虚拟相电压再注入一个零序分量把电压拉回到可调制范围内数学上等价于中心对齐的七段式 SVPWM但代码量少很多也方便后续做五段式不连续调制。/* v_alpha/v_beta单位V幅值范围[-VDC/2, VDC/2]vdc为母线电压 */ void svpwm_zero_injection(float v_alpha, float v_beta, float vdc, uint16_t arr, uint16_t *ccr) { /* 反Clarke从αβ坐标变回三相坐标系下的虚拟电压 */ float va v_alpha; float vb -0.5f * v_alpha SQRT3_2 * v_beta; float vc -0.5f * v_alpha - SQRT3_2 * v_beta; /* 注入零序分量v0 -(vmaxvmin)/2等效七段式SVPWM */ float vmax fmaxf(fmaxf(va, vb), vc); float vmin fminf(fminf(va, vb), vc); float v0 -0.5f * (vmax vmin); va v0; vb v0; vc v0; /* 电压转占空比再映射到定时器比较寄存器 */ ccr[0] (uint16_t)((va / vdc 0.5f) * (float)arr); ccr[1] (uint16_t)((vb / vdc 0.5f) * (float)arr); ccr[2] (uint16_t)((vc / vdc 0.5f) * (float)arr); }说明几点。第一这里的 va、vb、vc 不是真正的相电压而是把 v_alpha、v_beta 投影回三相坐标得到的中间量注入零序分量后实际加到电机三相上的线电压不变只是把中性点电位抬高了。第二v0 取 -(vmaxvmin)/2 就是七段式 SVPWM谐波小、电流纹波低如果固定取正值或负值变成五段式不连续调制开关次数少三分之一适合大功率驱动器降低开关损耗但电流谐波会变大。第三CCR 映射里 arr 对应定时器自动重装载值中心对齐模式下CCR 比 arr/2 大多少对应高电平占空比大多少占空比超过 90% 之后低端采样窗口就会消失后面电流采样部分会细说。3.3 TIM1互补PWM与死区配置STM32F103 里能做 FOC 的定时器只有 TIM1 和 TIM8因为高级定时器才有互补输出和死区插入。常规通道的下一章会配置死区但要注意半桥驱动的硬件逻辑有的驱动芯片自带死区代码里就配置为 0只输出单边 PWM有的需要 MCU 输出互补带死区的信号死区时间一般取 0.5 到 2 微秒取决于功率管的关断延迟。工程里出现stm32f1xx_hal_tim.c就是在做这件事。TIM_OC_InitTypeDef sConfigOC {0}; sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse arr / 2; /* 初始50%占空比 */ sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCNPolarity TIM_OCNPOLARITY_HIGH; /* 互补通道上桥 */ HAL_TIM_PWM_ConfigChannel(htim1, sConfigOC, TIM_CHANNEL_1);这里有个工程上容易忽略的点高级定时器默认关闭主输出必须在初始化最后调用__HAL_TIM_MOE_ENABLE(htim1)否则 TIM1 的 PWM 波形根本出不来电机一点反应都没有。很多入门者卡在代码明明配置了但输出口一直是低电平就是漏了这一行。另外刹车输入 BKIN 是硬件级故障关断通道可以利用起来做硬件过流保护MCU 检测到故障引脚电平变化会直接封锁 PWM 输出比软件里延迟几个周期的判断快得多。4. 从电压闭环到电流双闭环低端采样与Iq/Id双PI调参路径4.1 单电压闭环的局限与双闭环结构单电压闭环模式下目标 Vq 直接进入反 Park 和 SVPWM电压给定和实际电流之间没有约束关系。电机轻载时相电流小重载时反电动势变化同样的 Vq 对应完全不同的 iq。双闭环的做法是让电压指令来自电流 PI 的输出速度环或转矩指令给出 iq_ref电流环 PI 根据 iq_fb 误差算出 Vqid_ref 默认给 0同样用 PI 算出 Vd。这样最终输出的不再是盲目给定的电压而是能自动补偿反电动势和阻抗压降的电压。弱磁控制也是在这个结构上扩展的当转速高到母线电压不足以维持当前 iq 时往 id 负方向注入去磁电流牺牲一部分力矩换取更高速范围。4.2 低端电流采样与PWM同步电流采样位置决定了整个系统的噪声基底。高端采样在上桥臂导通期间采相电流能拿到完整的电流波形但需要隔离放大电路成本高低端采样直接在下桥臂 MOSFET 的源级串联采样电阻电路简单是入门板最常见的方案。低端采样的核心约束是必须在下桥臂导通、电流续流的窗口内启动 ADC 转换所以采样时刻要和 PWM 同步常见做法是用 TIM1 的 TRGO 信号触发 ADC 注入组。TIM_MasterConfigTypeDef sMasterConfig {0}; sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE; HAL_TIMEx_MasterConfigSynchronization(htim1, sMasterConfig); hadc1.Init.ExternalTrigConv ADC_EXTERNALTRIGCONV_T1_TRGO; hadc1.Init.ExternalTrigConvEdge ADC_EXTERNALTRIGCONVEDGE_RISING;低端采样在不同占空比区间的表现差异很明显直接决定电流环能跑多快占空比状态采样状况实际影响接近 0%零矢量时间长下桥全开方便采样相电流小信噪比差50% 附近有效矢量时间均衡采样窗口对称最佳工作区采样值最稳定接近 100%下桥几乎不导通续流窗口消失低端采样盲区电流反馈失真针对采样盲区常见做法是限制调制比不超过 90%或者切换到高端采样或者改用单电阻采样重构三相电流。F1 的 ADC 只有 12 位采样时刻如果正好落在死区边缘开关噪声会直接叠加到采样值上表现为电流环的毛刺这时候不要急着调 PI先把采样时刻往前挪几个定时器计数周期往往比调参数更有效。4.3 Iq/Id双PI的实现与抗积分饱和电流环 PI 实现不复杂但工程细节都在边界处理上。积分项必须有独立的限幅否则堵转时积分器会积分到很深恢复时产生严重的超调也就是常说的积分饱和。输出限幅要低于 SVPWM 线性调制区上限一般取 Vdc/2 的 90%。typedef struct { float Kp; float Ki; float integral; float out_limit; } PI_t; static float pi_run(PI_t *pi, float ref, float fb) { float err ref - fb; /* 积分项先独立限幅这是抗积分饱和的关键 */ pi-integral pi-Ki * err; if (pi-integral pi-out_limit) pi-integral pi-out_limit; if (pi-integral -pi-out_limit) pi-integral -pi-out_limit; float out pi-Kp * err pi-integral; if (out pi-out_limit) out pi-out_limit; if (out -pi-out_limit) out -pi-out_limit; return out; } /* 每个PWM中断周期内调用一次 */ float vq pi_run(pi_q, iq_ref, foc.iq); float vd pi_run(pi_d, id_ref, foc.id); /* 反Park生成SVPWM输入 */ float v_alpha vd * cos_t - vq * sin_t; float v_beta vd * sin_t vq * cos_t;调参路径我一般从 Kp 开始Ki 先给 Kp 的十分之一。电流环的 Kp 调大直到 iq 跟踪出现轻微超调然后退回一点Ki 继续加直到静差消除且没有低频振荡。如果 q 轴电流出现几赫兹的振荡先怀疑积分太强或速度环和电流环频率太接近不要盲目降 Kp因为电流环带宽直接决定系统刚度。故障现象优先排查项q 轴电流低频振荡Ki 过大或速度环频率接近电流环电流波形毛刺ADC 采样时刻落在死区开关沿上id 随转速升高失控Park/反 Park 符号反或电角度滞后带载时 iq 饱和输出限幅超过 Vdc/2进入过调制区4.4 三环频率分配F1跑FOC的边界STM32F103 主频 72MHz没有硬件浮点单次 FOC 运算包括两次坐标变换、两路 PI、SVPWM用 float 裸算大概要十几微秒。PWM 频率取 20kHz 时中断周期只有 50 微秒留给其他代码的空间很小。工程上常见做法是 PWM 和电流环取 12 到 16kHz速度环降到 1kHz位置环再降到 500Hz形成一个 16:1:0.5 的降频结构。这不是随便定的电流环要和 PWM 同步保证采样不混淆速度环响应慢一个量级则保证相位裕度。如果看到工程里把arm_math.h的浮点函数替换成定点 Q15 版本多半就是主频或者中断负载扛不住了。5. 无感FOC的堵转检测与初始位置辨识两个实用调试技巧5.1 组合判据实现堵转保护无感 FOC 的堵转检测比有感麻烦很多因为低速下反电动势信噪比差观测器给出的电角度可能是漂的单看电流大小不够用。我常用的判据是低速 大电流持续一定时间估算转速低于阈值说明转子实际没转起来或转得很慢iq 反馈长时间超过额定值说明所有电压都耗在电阻和漏感上而没有转换成机械功。两者同时满足判定堵转。static uint16_t stall_cnt; float spd get_elec_speed(); /* 无感时来自观测器有感时来自编码器差分 */ float iq_abs fabsf(iq_fb); if (spd 5.0f iq_abs IQ_LIMIT * 0.9f) { stall_cnt; if (stall_cnt 500) { /* 20kHz循环下约25ms */ set_fault(FAULT_STALL); disable_pwm(); /* 触发TIM1刹车或直接关闭输出 */ } } else { stall_cnt 0; }这个判据里延时计数是关键直接去掉会误触发。电机启动瞬间转速还没建立但电流可能已经超过限幅这时候是正常的加速过程不是堵转所以必须让条件持续一段时间再动作。500 拍的数值不是固定的启动时间长的系统要适当加大反过来如果保护动作太慢要根据功率管的耐流时间压缩窗口。5.2 短脉冲注入法做初始位置辨识有感 FOC 可以通过强制拉位置来获得初始角但无感启动时对反转要求高的场合位置不对会短暂倒转。这时可以给 U、V、W 三相各注入一个等宽的有效电压脉冲分别测量相电流的峰值响应。磁路饱和效应下脉冲电流越大代表该方向电感越小而电感最小的方向对应转子磁极方向。判断出磁极轴后再在该轴正方向和反方向各注入一次比较峰值电流电流大的一次是 N 极所在方向这就是完整的短脉冲注入定位。整个过程在启动阶段几百微秒内完成三相各一次脉冲配合 ADC 同步采样就能实现不需要额外的硬件。F1 这个算力和外设跑这套流程是够的只要把采样时刻对准每个脉冲的中间位置就行。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门