STM32F407 FOC位置闭环实战:三环级联与编码器反馈
2026/9/16 16:30:50 网站建设 项目流程

简介:一套基于STM32F407的FOC(磁场定向控制)位置闭环工程源码,面向STM32F4系列单片机开发者,用于解决直流无刷电机/永磁同步电机的高精度位置与速度控制问题。工程覆盖电机建模、d-q轴坐标变换、磁场定向、PID闭环调节、PWM逆变控制、死区补偿与过流过压等异常保护;利用STM32F407内置浮点运算单元和硬件乘法器,可高效执行复杂控制算法。代码已完成时钟、GPIO、ADC、DAC、PWM等外设初始化与中断服务程序编写,编译后可直接运行。压缩包共1296个文件,约42.5MB。文件以641个C源文件、347个头文件为主要源码,另有汇编启动文件、Keil工程文件、CubeMX配置文件、链接脚本、ARM数学库及hex固件等,覆盖源码阅读、重新编译、直接烧录和算法库依赖等多类需求。已有381人学习下载。通过这套工程,开发者可以快速理解FOC完整流程、位置闭环框架和关键外设配置方式,并基于其中完善的初始化与调试结构,进一步优化控制策略或扩展应用功能,是一份兼顾学习与工程落地的参考实现。

1. 位置闭环 FOC 在 STM32F407 上的应用价值

做云台、机械臂关节或数控滑台时,电机往往不是"转起来"就行,而是要快速转到一个指定角度并牢牢稳住。普通的六步换向加上速度环只能做到匀速或调速,位置一旦受到外力扰动就会偏出去,必须靠位置闭环来修正。STM32F407 是 F4 系列里最常用的一颗料,168MHz 主频带 FPU,跑 FOC 的三环(电流环、速度环、位置环)时,电流环 20kHz、位置环 1kHz 还有相当余量,这也是很多入门级伺服方案选它的原因。这篇文章围绕 FOC 位置闭环在 STM32F407 上的落地,讲清楚三环级联的架构、定时器与 ADC 的配置方式、编码器反馈的接法,以及调参和移植到其他 F4 型号时的注意事项。适合手里已经有电机驱动板、想从"能转"走向"能定位"的工程师,也适合正在评估 F407 做位置伺服方案的开发者。

2. FOC 位置闭环的控制架构:三个环路如何级联

2.1 电流环、速度环、位置环的带宽分配

FOC 位置闭环不是单独一个位置环,而是三个环路从内到外套在一起:电流环最内层,速度环在中间,位置环最外层。位置环的输出不是 PWM 占空比,而是速度给定;速度环的输出是电流给定,也就是 q 轴电流;电流环输出 d/q 轴电压,经过逆 Park 和 SVPWM 生成占空比。这个顺序不能乱,原因是每个环路的带宽必须从内到外递减,否则外环会给内环发一个它跟不上的指令,系统容易振荡。

常见执行频率是这样分配的:

环路典型执行频率带宽经验值输出量
电流环10kHz ~ 20kHz带宽的 1/10 ~ 1/20d/q 轴电压
速度环1kHz ~ 5kHz20Hz ~ 100Hzq 轴电流给定
位置环500Hz ~ 1kHz2Hz ~ 20Hz速度给定

位置环放在最外层,更新频率最低,但它决定系统的最终刚度。如果位置环给一个速度阶跃,速度环要平滑跟踪,电流环要在更短时间内把实际电流顶上去。三者频率相差太多会响应迟钝,相差太少则环间耦合振荡。我一般先把电流环固定在 20kHz,速度环取它的 1/10 即 2kHz,位置环再取 1kHz,保留分频余量便于调试。

2.2 坐标变换与转子电角度的关系

FOC 的核心是坐标变换,把三相静止坐标系下的电流变换到随转子旋转的 d/q 坐标系。Clarke 变换把 ia/ib/ic 变成 iα/iβ,Park 变换再把 iα/iβ 变成 id/iq。这里面最关键的是转子电角度 θe,它等于机械角度 θm 乘以极对数 Pn。对位置闭环而言,还需要独立的机械角度做位置反馈,不能直接拿电角度当位置环输入,因为极对数关系会让位置环"看"到的角度和实际机械位置对不上。

// 三相电流合成到 d/q 轴 float ia = adc_val[0]; float ib = adc_val[1]; float ic = -ia - ib; float i_alpha = ia; float i_beta = (ia + 2.0f * ib) * 0.577350269f; // 1/sqrt(3) float cos_theta = arm_cos_f32(theta_e); float sin_theta = arm_sin_f32(theta_e); float id = i_alpha * cos_theta + i_beta * sin_theta; float iq = -i_alpha * sin_theta + i_beta * cos_theta;

参数说明:theta_e是从编码器机械角度乘以极对数得到的电角度,单位是弧度。arm_cos_f32是 CMSIS-DSP 提供的高效三角函数,STM32F407 带 FPU,用浮点算这三行代码在 20kHz 中断里只占很小一部分时间。实际工程中ic通常不采样而是用基尔霍夫定律算出来,可以省一路运放和 ADC 通道。

2.3 位置反馈选择:增量式编码器与磁编码器

位置闭环对角度反馈的分辨率和重复精度要求远高于速度环。霍尔信号一个电周期只有 6 个区段,机械角度分辨率太粗,低速时位置环会明显抖动。增量式编码器通过正交 A/B 信号,在 STM32F4 定时器编码器模式下能达到 4 倍频分辨率,1000 线的编码器配上 4 倍频就是 4000 个计数每圈。磁编码器如 AS5047 这类 SPI 输出 14 位绝对角度,零位信息直接读出来,不需要 Z 脉冲找原点,硬件接线更少。

选编码器时还要看机械安装位置。编码器装在电机尾部(负载侧)和装在输出轴(负载端)的区别很大:装在电机轴上,位置环控制的是电机角度,负载侧有减速比 N 时程序里要把目标角度除以 N 再做控制;装在负载端则位置环直接读负载角度,但编码器分辨率会被放大 N 倍,对安装精度要求更高。F407 的定时器编码器模式不能同时做 PWM 输出,所以一般用 TIM2/3/4/5 做编码器输入,TIM1/TIM8 做 PWM 输出,两者互不干扰。

3. 在 STM32F407 上搭建 FOC 位置闭环工程骨架

3.1 高级定时器 TIM1 输出互补 PWM 与死区配置

FOC 逆变器需要 6 路 PWM 驱动三相半桥,STM32F407 的 TIM1 和 TIM8 是两个高级定时器,可以直接输出互补 PWM 并带硬件死区插入。死区是防止上下桥臂直通的关键参数,一般取 0.5μs 到 2μs,具体看 MOS 管的关断延迟。PWM 频率我常用 20kHz,此时定时器时钟 168MHz,ARR 值为 8400-1,死区寄存器 DTG 设置取决于预分频倍率。

// TIM1 PWM 频率 20kHz,死区 1us TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_Period = 8400 - 1; // 168MHz / 8400 = 20kHz TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF; TIM_BDTRInitStructure.TIM_DeadTime = 168; // 168 / 168MHz = 1us TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; TIM_BDTRInit(TIM1, &TIM_BDTRInitStructure);

参数说明:TIM_CounterMode_CenterAligned1是中心对齐模式,计数器先递增再递减,PWM 波形左右对称,电流采样点恰好落在计数值为 0 或 ARR 的位置,这是 FOC 最常用的对齐方式。TIM_DeadTime为 168 对应死区时间 = 168 / 168MHz = 1μs。如果驱动板用的是光耦隔离或栅极驱动芯片,死区偏大一些更安全,但要注意死区太大会让电流波形产生明显畸变。

3.2 电流采样:ADC 注入组与 PWM 触发同步

相电流采样必须和 PWM 同步,否则采样点落在开关噪声区,读到的电流全是毛刺。常见做法是用 TIM1 的 TRGO 事件触发 ADC1 的注入组,在中心对齐计数器的下溢点(计数值 0)启动采样。此时下桥臂导通,电流经过采样电阻,运放输出已经稳定。F407 上常用双电阻采样,采集两相电流,第三相用基尔霍夫定律计算。

ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 42MHz ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(ADC1, &ADC_CommonInitStructure); ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigInjecConv_T1_TRGO; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; ADC_InitStructure.ADC_InjectedScanConvMode = ENABLE; ADC_Init(ADC1, &ADC_InitStructure);

参数说明:ADC_ExternalTrigInjecConv_T1_TRGO表示注入组由 TIM1 的 TRGO 上升沿触发,触发边沿设为 Rising 保证每次计数周期只采一次。ADC_ScanConvMode在注入组这里配置两路注入通道,分别对应采样电阻两相电流。采样完成后在注入组中断里读取ADC1->JDR1ADC1->JDR2,再做零点偏移校准和增益换算。常见坑是 ADC 输入引脚上的 RC 滤波时间常数过大,导致采样值滞后于实际电流,表现为电流环高频振荡。

3.3 编码器接口:TIM 正交解码与 Z 脉冲处理

增量式编码器 A/B 两路信号接到 F407 某个定时器的 CH1/CH2 引脚,配置为编码器模式后,定时器计数器会随电机正反转自动加减。速度环和位置环直接读计数器的值就能得到累计位置,方向的符号由硬件自动处理。

TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0x0F; // 输入滤波,防抖动 TIM_ICInit(TIM2, &TIM_ICInitStructure);

参数说明:TIM_EncoderMode_TI12让定时器同时检测 A、B 两路信号的边沿,实现 4 倍频计数。TIM_ICFilter设置为 0x0F 开启输入滤波,可滤掉编码器线缆引入的毛刺,但滤波会引入延迟,高转速下可能丢步,需要根据实际转速调整。Z 脉冲可以用来找绝对零位,F407 上 Z 信号接到另一个定时器的输入捕获或直接接外部中断,在捕获中断里把位置计数器清零。

3.4 三环任务在定时器中断里的调度

FOC 控制主体在 PWM 中断里执行,因为电流环必须紧随采样点,延迟越短越好。位置环不需要 20kHz 这么高的频率,用计数分频方式每 N 次中断执行一次。下面是一个完整的中断骨架。

volatile uint16_t loop_divider = 0; void TIM1_UP_TIM10_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update)) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update); // 1. 读取 ADC 注入组采样值,换算成实际电流 read_current(&ia, &ib); // 2. 读取编码器计数,换算成机械角度和电角度 encoder_angle = (int32_t)TIM2->CNT * angle_scale; set_theta_e(encoder_angle * pole_pairs); // 3. 电流环:Clarke/Park + PI + 逆Park foc_current_loop(ia, ib, theta_e, id_ref, iq_ref); // 4. 速度环和位置环分频执行,1kHz if (++loop_divider >= 20) { loop_divider = 0; speed = (encoder_angle - last_angle) * speed_scale; last_angle = encoder_angle; speed_loop(speed, speed_ref, &iq_ref); position_loop(encoder_angle, position_ref, &speed_ref); } // 5. 更新 TIM1 比较寄存器 set_pwm_duty(pwm_compare); } }

参数说明:loop_divider每次中断加 1,到 20 时执行一次速度环和位置环,这样电流环 20kHz、速度环和位置环 1kHz。实际工程里速度环可以单独用 2kHz 分频,位置环再分频,灵活度更高。speed_scaleangle_scale是把编码器计数值换算成 rad/s 和 rad 的缩放系数,用浮点乘比做除法快,168MHz 主频下能省就是省。这段代码里还没有做电流环的过流保护和使能逻辑,工程化时要先判断 PWM 是否解锁再进环。

4. FOC 位置闭环调试:参数整定与常见坑

4.1 电流环 PI 参数估算与整定

电流环参数可以按电机定子电感和电阻估算。已知电机的 Ls 和 Rs,设定期望电流环带宽,PI 参数近似为:

float bandwidth = 2000.0f; // 电流环带宽 2kHz float Ls = 0.00012f; // 定子电感 120uH float Rs = 0.5f; // 定子电阻 0.5ohm float kp_i = Ls * bandwidth; float ki_i = Rs * bandwidth;

参数说明:kp_i的单位是 V/A,ki_i的单位是 V/(A·s)。这个估算法基于电机的一阶 RL 模型,忽略反电动势和数字延迟,实际调试时 Kp 和 Ki 通常要乘 0.5 到 0.8 的系数。如果电流环响应有超调,先降 Ki;如果高频啸叫,降 Kp 或减小采样滤波。给一个经验表:

带宽目标Kp 估算Ki 估算适用场景
1kHzLs × 1000Rs × 1000调试初期,保守
2kHzLs × 2000Rs × 2000常规伺服,响应适中
4kHzLs × 4000Rs × 4000高动态,需低延迟采样

注意不要把带宽设到 PWM 频率的一半以上,奈奎斯特限制会导致环路不稳定。

4.2 位置环 PD 控制与前馈补偿

位置环一般用 P 控制就够,比例增益高了会抖动,低了会有跟随误差。实际设备里位置环输出是速度给定,所以位置环增益 Kp_pos 的单位是(rad/s)/rad,也就是1/s。如果负载惯量大,纯 P 控制在匀速跟踪目标时会有固定滞后,加上速度前馈能明显改善。

float kp_pos = 10.0f; // 位置环增益 10 (1/s) float kd_pos = 0.02f; // 微分项,阻尼机械振荡 float ff_vel = 1.0f; // 前馈系数,通常 0.8 ~ 1.0 float pos_err = position_ref - encoder_angle; float vel_ref_ff = derivative_of(position_ref); // 目标速度前馈 // 位置环输出到速度环 speed_ref = kp_pos * pos_err + kd_pos * position_err_dot + ff_vel * vel_ref_ff;

参数说明:derivative_of是目标位置对时间求导,实际代码里可以用前后两次目标位置之差乘频率算出。kd_pos这一项不是必须的,只有在位置反馈噪声小、机械结构有弹性时才有明显效果;增量式编码器在高频抖动下微分项会放大噪声,反而让电流环跟着抖,建议先不加。前馈系数建议从 0.8 开始,调得太满会造成过冲,因为速度环和电流环的跟踪本身有相位滞后,前馈系数需要和滞后程度匹配。

4.3 相序、编码器方向与过流这三类问题的定位

FOC 调试中十有八九是先遇到电机啸叫或反转,而不是环参数问题。最经典的坑是编码器方向与电机相序不匹配:电机正转时电角度递增方向若与编码器计数递增方向相反,电流环就会变成正反馈,表现为电流一路增大触发过流。检查方法很简单,让系统开环给一个固定占空比(不要进电流环),观察电角度和编码器计数是否同方向变化。

现象可能原因检查手段
上电即过流相序接反、编码器方向反开环给 5% 占空比,对比电角度增量与编码器增量
电流有低频振荡电流环 Ki 过大或采样点偏移用示波器看 d/q 轴电流,降低 Ki 测试
位置稳定但抖动位置环增益过高、机械间隙减小 Kp_pos,加低通滤波
高速丢位置编码器滤波过强、Z 脉冲干扰示波器测 A/B 波形,降低 TIM_ICFilter 值

排查顺序建议是先确认编码器方向和相序,再看电流环能否稳住一个固定 q 轴电流,然后看速度环阶跃响应,最后才调位置环。跳过前面直接调位置环,往往会把电流环问题掩盖在位置环振荡里,难以定位。

5. 位置闭环 FOC 的工程化:初始定位、限位与跨型号移植

5.1 转子初始位置检测与预定位

FOC 启动时如果不让转子先对齐,电角度是未知的,电流环会给出错误的力矩方向,常见表现是一上电电机猛抽一下或抖动后过流。最可靠的初始定位方法是预定位:给定一个固定的 d 轴电流和零 q 轴电流,让转子被定子磁场拉到已知角度。具体做法是在电角度 0 的位置给一个恒定的 id 电流,持续 300ms 到 1s,等待机械振荡收敛。

// 预定位:固定电角度 0,id 给定 1A,iq 给定 0 theta_e = 0.0f; id_ref = 1.0f; iq_ref = 0.0f; for (int i = 0; i < 500; i++) { foc_current_loop(ia, ib, theta_e, id_ref, iq_ref); delay_ms(1); } encoder_angle = 0.0f; // 预定位完成后把当前位置清零作为位置原点

参数说明:预定位的id_ref大小按电机额定电流的 10% 到 30% 取,太小拉不动负载,太大冲击过大。带减速箱时这个过程会带动负载移动,机械上需要有让位空间。若使用绝对值磁编码器,可以直接读到 0 到 360 度的绝对位置,不需要预定位,上电即知道电角度,这是它比增量式编码器更省事的主要原因。

5.2 软限位、硬限位与 Z 脉冲归零

位置闭环的应用场景必然有限位需求。硬限位靠机械撞块或光电开关直接切断 PWM,软限位在位置环里做:当位置给定或当前位置接近边界时,把速度给定限制在一个很小的安全值。增量式编码器丢失原点后要归零,常见做法是:先低速朝限位方向运行,撞到限位开关后反转,再找 Z 脉冲清零。

if (encoder_angle < soft_limit_min) { speed_ref = 0.0f; position_ref = soft_limit_min; }

参数说明:软限位不能只限制位置给定,还需要把速度给定钳位到零,否则位置环的积分项会把速度顶到边界上。Z 脉冲归零时,限位开关的位置精度只要到一两圈以内即可,最终零位由 Z 脉冲确定,因为 Z 脉冲每圈固定且重复精度远高于限位开关。这里要注意 Z 脉冲在电机停转时也可能因为振动误触发,代码里要先确认当前在限位开关区域内再接受 Z 脉冲清零。

5.3 从 STM32F407 移植到 F411、F401、F446 的要点

标题里写了"支持 STM32F4 系列单片机",实际移植时主要改三处:定时器资源、ADC 通道和 DMA 请求映射。F407 有 TIM1/TIM8 两个高级定时器,F401 和 F411 同样有,但 F401 最高主频只有 84MHz,电流环中断执行时间要控制在 20kHz 周期内就比较紧张,建议把 PWM 降到 16kHz,并且把电流环里的浮点运算精简,或者把速度环降频。F446 可以跑到 180MHz,资源相对宽裕,移植时几乎不需要动中断代码。

芯片型号主频PWM 定时器ADC 位数移植关注点
STM32F407168MHzTIM1/TIM812bit基准平台
STM32F40184MHzTIM1/TIM812bit降低电流环频率
STM32F411100MHzTIM1/TIM812bit检查中断执行时间
STM32F446180MHzTIM1/TIM812bit基本无需改动

移植流程上,先把system_stm32f4xx.c里的时钟配置换成目标芯片的 PLL 参数,再用 CubeMX 重新生成 GPIO 和定时器的初始化,把原来 F407 代码里涉及 DMA 的 ADC 通道映射改为目标芯片对应的 DMA 请求号。F4 系列外设寄存器结构体基本相同,控制算法代码可以原样复用,风险点集中在时钟频率不同导致的定时器分频系数和 ADC 采样时间变化,改完先用开环测试确认 PWM 频率和死区符合预期,再进闭环调试。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询