尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32无刷电机FOC无感控制实战:从算法原理到双电阻采样调试

STM32无刷电机FOC无感控制实战:从算法原理到双电阻采样调试 之前在调试 STM32 无刷电机驱动时我在双电阻电流采样与无感角度估算上反复折腾了很久。网上的 FOC 资料虽然多但大多数要么只讲控制理论要么只给一套闭源库的调用方法真正能照着搭起来、看到波形、跑起来转场的完整教程并不多。这段时间把 FOC 从算法原理到板级调试重新梳理了一遍也结合项目中双电机同步驱动的经验做了一次系统复盘。如果你正准备入门无刷电机 FOC 控制或者在用 STM32G4、STM32H7 做无感 FOC 项目可以参考这篇文章的完整路径。FOC 全称是 Field-Oriented Control即磁场定向控制也叫矢量控制。它解决的核心问题是让无刷电机像直流电机一样“好控制”。直流电机只需要控制电枢电压或电流就能线性调节转矩和转速而无刷电机的三相绕组电流是交流量转矩和电流之间的关系并不直观。FOC 的做法是通过坐标变换把三相交流量转换成与转子磁场同步旋转的 d-q 轴直流分量从而用类似直流电机的思路去控制转矩和励磁。本文会从 FOC 的基础概念讲起然后拆解核心算法变换、硬件采样结构、无感角度估算最后给出基于 STM32 的从零实现流程和常见调试排错方法适合有 STM32 基础、想深入电机控制的开发者阅读。1. FOC 是什么解决什么问题1.1 为什么无刷电机需要 FOC无刷直流电机BLDC和永磁同步电机PMSM在结构上非常相似定子都是三相绕组转子都是永磁体但驱动方式差异很大。常见的六步换相法方波驱动只是一段时间内给两相绕组通电利用转子位置信号按 60° 电角度逐步换相。这种方式简单但会产生明显的转矩脉动高速时噪声大低速动态响应也一般。FOC 则是对三相电流做连续正弦控制让定子电流矢量始终与转子磁场保持理想角度。这样从电磁转矩公式来看[ T_e \frac{3}{2} p \psi_f i_q ]转矩只与 q 轴电流成正比在 ( i_d 0 ) 控制策略下。这意味着我们可以像控制直流电机一样实时控制转矩动态响应和稳态精度都远好于六步换相。通俗地说FOC 让电机的“每一刻”都处于最佳出力角度而不是“每 60° 换一次”。1.2 有感与无感 FOC 的区别有感 FOC 依赖编码器或霍尔传感器获取转子位置。位置信息直接参与 Park 变换和速度反馈控制实现相对简单启动和低速表现好。无感 FOC 不依赖位置传感器通过电机模型和相电流、母线电压等信息估算转子位置与速度。常见方法有反电动势过零检测适用于方波驱动简单但低速效果差。扩展卡尔曼滤波器EKF精度高计算量大。滑模观测器SMO对参数变化鲁棒性好是工程中常用的无感方案。磁链观测器通过定子磁链估算转子位置对中高速效果较好。无感 FOC 的难点集中在低速启动和零速带载。因为静止时反电动势为零无法通过反电动势获得角度需要采用 I/F 开环启动、高频注入等方法完成初始定位和加速。1.3 FOC 完整控制链路无论有感还是无感FOC 的控制流程都可以用下面的链路概括三相电流采样 - Clark 变换 - Park 变换 - d/q 轴电流 PI - 逆 Park 变换 - SVPWM - 三相逆变器 - 电机 ^ | ---------- 位置/速度反馈 --------------电流环是内环速度环是外环部分应用还会增加位置环。整个系统里最关键的两个环节是电流采样是否准确直接影响变换结果和 PI 输出。转子角度是否可靠Park 变换和逆 Park 变换都依赖角度角度误差会造成 d-q 轴耦合严重时会导致失控。2. FOC 算法核心原理拆解2.1 Clark 变换与 Park 变换先从电流说起。假设采样得到三相电流 ( i_a )、( i_b )、( i_c )由于电机绕组通常为星形连接有[ i_a i_b i_c 0 ]所以实际只需要采样两相电流即可。Clark 变换把三相静止坐标系abc变换到两相静止坐标系αβ[ i_{\alpha} i_a ] [ i_{\beta} \frac{i_a 2i_b}{\sqrt{3}} ]写成矩阵形式就是// Clark 变换核心代码 i_alpha i_a; i_beta (i_a 2.0f * i_b) * 0.57735026919f; // 1/sqrt(3)Park 变换把 αβ 静止坐标系变换到 d-q 旋转坐标系需要转子电角度 θ[ i_d i_{\alpha} \cos\theta i_{\beta} \sin\theta ] [ i_q -i_{\alpha} \sin\theta i_{\beta} \cos\theta ]// Park 变换核心代码 i_d i_alpha * cos_theta i_beta * sin_theta; i_q -i_alpha * sin_theta i_beta * cos_theta;实际编程中为了减少 CPU 开销通常用查表或 CORDIC 硬件加速计算三角函数。STM32G4 系列内置 CORDIC 外设可以大幅加快三角函数和开方运算。2.2 SVPWM 调制电流环 PI 的输出是 d-q 轴电压指令 ( u_d^* )、( u_q^* )经过逆 Park 变换得到 αβ 轴电压 ( u_{\alpha}^* )、( u_{\beta}^* )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空间矢量脉宽调制要做的事。SVPWM 的基本思路是8 个基本电压矢量6 个非零矢量 2 个零矢量可以在空间合成任意方向和幅值的电压矢量。通过判断目标电压所在的扇区计算相邻两个基本矢量的作用时间再分配到三个桥臂的占空比上。扇区判断代码// 根据 u_alpha、u_beta 判断扇区 float v1 u_beta; float v2 (1.7320508f * u_alpha - u_beta) * 0.5f; float v3 (-1.7320508f * u_alpha - u_beta) * 0.5f; int sector 0; if (v1 0) sector 1; if (v2 0) sector | 2; if (v3 0) sector | 4;SVPWM 相比 SPWM 的优势是母线电压利用率更高谐波更小是目前 FOC 驱动的主流调制方式。2.3 PID 调节器FOC 的电流环和速度环都使用 PID或 PI调节器。电流环要求响应快一般只用 PI不加微分项避免放大采样噪声。离散 PID 位置式实现typedef struct { float Kp; float Ki; float Kd; float integral; float prev_error; float limit; } PID_TypeDef; float PID_Update(PID_TypeDef *pid, float target, float current) { float error target - current; pid-integral error; // 积分限幅防止积分饱和 if (pid-integral pid-limit) pid-integral pid-limit; if (pid-integral -pid-limit) pid-integral -pid-limit; float output pid-Kp * error pid-Ki * pid-integral pid-Kd * (error - pid-prev_error); pid-prev_error error; // 输出限幅 if (output pid-limit) output pid-limit; if (output -pid-limit) output -pid-limit; return output; }工程中常见的坑是积分饱和。当电机堵转或启动瞬间误差持续较大积分项会不断累积导致恢复时超调严重。因此积分限幅和输出限幅必须同时做。2.4 磁链与有效磁链计算“FOC 控制中有效磁链怎么计算”这是很多人在无感 FOC 中遇到的疑问。磁链观测的基本公式来自定子电压方程[ u_s R_s i_s \frac{d\psi_s}{dt} ]所以定子磁链[ \psi_s \int (u_s - R_s i_s) dt ]在 αβ 坐标系下flux_alpha (u_alpha - R_s * i_alpha) * dt; flux_beta (u_beta - R_s * i_beta) * dt;定子磁链包含两部分永磁体磁链和电枢反应磁链。有效磁链active flux的定义是[ \psi_{active} \psi_s - L_s i_s ]它的方向与转子 d 轴一致可以通过有效磁链直接在静止坐标系下估算转子角度theta_est atan2f(flux_beta - L_s * i_beta, flux_alpha - L_s * i_alpha);有效磁链法的优点是不需要知道永磁体磁链的精确值。对电感饱和有一定鲁棒性。实现比 EKF 简单适合中高速运行。缺点是低速时定子电阻压降占比大积分漂移明显需要配合补偿和启动策略。2.5 无感角度估算反电动势与观测器无感 FOC 的中高速段最常用的是反电动势法。PMSM 在 αβ 坐标系下的电压方程为[ u_{\alpha} R_s i_{\alpha} L_s \frac{di_{\alpha}}{dt} e_{\alpha} ] [ u_{\beta} R_s i_{\beta} L_s \frac{di_{\beta}}{dt} e_{\beta} ]其中反电动势[ e_{\alpha} -\psi_f \omega_e \sin\theta_e ] [ e_{\beta} \psi_f \omega_e \cos\theta_e ]所以反电动势包含转子位置和速度信息。如果能提取出反电动势角度就自然得到了。滑模观测器SMO的思路是构造一个电流观测器用滑模面把反电动势项“逼”出来。实际工程中会配合锁相环PLL提取角度和速度因为直接对反电动势做反正切计算容易受噪声和谐波影响。PLL 角度提取示意// SMO 输出 e_alpha、e_beta // 用锁相环跟踪角度 error e_alpha * cos_theta_est e_beta * sin_theta_est; // 如果 error 被 PI 调节到 0则 theta_est 收敛到真实电角度需要注意的是反电动势法在零速和极低速下无法工作因为此时反电动势幅值接近零。工程上常见的做法是零速定位施加固定电压矢量把转子拉到已知位置。I/F 开环加速用电流环控制电流幅值按斜坡给定频率强制转子跟随同步。切换到闭环观测器当转速超过切换阈值一般 5% 额定转速以上反电动势足够大切换到无感闭环。这套流程是所有无感 FOC 都必须处理的核心工程问题。3. FOC 硬件架构与采样方案3.1 采样电阻与电流采样方案FOC 需要实时获取三相电流常见的电流采样方案有三种采样方案采样方式优点缺点三电阻采样三个下桥臂分别串联采样电阻每相独立采样算法简单成本高布线复杂双电阻采样两相下桥臂串联采样电阻成本低能满足大部分应用低调制比下采样窗口紧张单电阻采样母线串联一个采样电阻成本最低重构算法复杂低速性能差双电阻采样是工程中最常用的折中方案。因为三相电流满足基尔霍夫定律采样两相电流后第三相通过计算得到i_c -i_a - i_b;双电阻采样需要特别注意采样时刻。电流采样必须在 PWM 下桥臂导通、电流流过采样电阻时进行一般选择在 PWM 计数器的中心点或特定边沿触发。3.2 双电阻采样与定时器触发 ADCSTM32 做双电阻采样经典做法是用高级定时器的 TRGO 事件触发 ADC 注入组采样。在 PWM 中心对齐模式下计数器向上计数到 ARR 再向下计数。下桥臂在计数值大于比较值时导通因此定时器计数到 0或 ARR附近时下桥臂导通时间最长电流采样最可靠。配置思路如下高级定时器 TIM1 输出六路互补 PWM。配置 TIM1 的重复计数器和 TRGO 事件在计数器上溢/下溢时触发 ADC。ADC 注入组同时采样两路电流通道。HAL 库中关键的触发配置// TIM1 配置 TRGO 为更新事件 sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE; HAL_TIMEx_MasterConfigSynchronization(htim1, sMasterConfig);ADC 采样保持时间需要足够长。实际项目里常见问题是采样点太靠近 PWM 边沿电流尚未稳定导致采样值偏小或噪声大。建议使用示波器抓取采样电阻两端波形确认采样窗口内的电流平台期。3.3 双电机采样组扫描双触发当项目需要同步控制两个电机比如双轮机器人、双轴云台时两个电机的 PWM 和 ADC 采样需要协调。“双电机采样组扫描双触发”可以理解为两个电机共用或分时使用 ADC 采样组通过扫描方式依次采集两路电机的相电流并且依靠两次触发事件完成一轮完整采样。常见实现方式有两种方案一一个 ADC 扫描两个采样组使用两个定时器TIM1 和 TIM8分别输出两路 PWM各自产生 TRGO 触发 ADC 注入组。但同一个 ADC 同一时刻只能执行一个转换序列因此需要错开触发时刻。方案二两个 ADC 分别采样STM32 的 ADC1 和 ADC2 可以组成双重模式也可以各自独立工作。每个电机使用独立的 ADC 和定时器触发互不干扰适合对实时性要求高的场景。// 伪代码示意双电机电流采样 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM1) { // 电机1电流采样完成执行 FOC 计算 Motor1_FOC_Update(); } else if (htim-Instance TIM8) { // 电机2电流采样完成执行 FOC 计算 Motor2_FOC_Update(); } }双电机 FOC 需要注意两个电机的 PWM 频率和死区时间尽量一致便于调试。如果共用一个母线需要考虑母线电压跌落对两个环路的影响。同步触发会导致瞬时电流过大尽量错相 180° 触发。3.4 芯片选型STM32G4 与 STM32H7当前做 FOC 主控STM32G4 和 STM32H7 是两个热门选择。STM32G4 系列的优势内置 CORDIC 硬件加速器三角函数计算速度大幅提升。内置数学加速器支持除法、平方根等运算。170MHz 主频适合单电机乃至双电机 FOC。内置比较器、运放部分型号可以简化电流采样电路。有专用电机控制套件和参考代码。STM32H7 系列的优势主频高400MHz 以上算力强适合复杂观测器EKF、非线性观测器。可以做多电机控制加其他复杂算法。12 位 ADC 性能更好支持高精度采样。从性价比看如果项目只做单电机或双电机 FOCSTM32G4 是更合适的选择CORDIC 外设在实时控制中非常实用。如果需要在 FOC 基础上叠加机器人运动学、路径规划等重负载算法可以考虑 STM32H7。4. 从零开始实现 STM32 无刷电机 FOC4.1 环境准备与工程结构本文示例以 STM32G4 系列为主控开发环境使用 STM32CubeIDEHAL 库版本以你实际创建的工程为准。硬件上需要一块带三相逆变桥和双电阻采样电路的驱动板以及一个带霍尔或编码器的 PMSM 电机调试无感算法时有位置传感器做对比非常有用。工程目录建议按模块拆分FOC_Demo/ ├── Core/ │ ├── Inc/ │ └── Src/ │ ├── main.c │ ├── foc.c │ ├── foc.h │ ├── pid.c │ ├── pid.h │ ├── smo.c │ ├── smo.h │ └── motor_control.c ├── Drivers/ └── Debug/模块职责划分foc.c坐标变换、SVPWM、FOC 主循环。pid.cPID 控制器实现。smo.c滑模观测器和锁相环。motor_control.c根据电机状态执行启动、开环、闭环切换。4.2 ADC 采样与坐标变换先定义一个电机控制结构体// foc.h typedef struct { float i_a; float i_b; float i_c; float i_alpha; float i_beta; float i_d; float i_q; float v_alpha; float v_beta; float v_d; float v_q; float theta_elec; float theta_mech; float speed; } FOC_HandleTypeDef;ADC 采样完成中断中读取两相电流// 文件路径motor_control.c void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc-Instance ADC1) { uint32_t raw_a HAL_ADCEx_InjectedGetValue(hadc1, ADC_INJECTED_RANK_1); uint32_t raw_b HAL_ADCEx_InjectedGetValue(hadc1, ADC_INJECTED_RANK_2); // 转换成实际电流值需要根据采样电阻和运放增益标定 float i_a (raw_a - ADC_MID_POINT) * CURRENT_SCALE; float i_b (raw_b - ADC_MID_POINT) * CURRENT_SCALE; FOC.i_a i_a; FOC.i_b i_b; FOC.i_c -i_a - i_b; // 触发 FOC 计算 FOC_Update(); } }注意ADC_MID_POINT是零电流时的 ADC 采样值一般接近 204812 位 ADC。CURRENT_SCALE由采样电阻阻值和运放放大倍数共同决定。这两个参数对 FOC 控制质量影响很大建议用恒流源或高精度万用表标定。4.3 开环代码实现在闭环之前先实现开环控制验证硬件和 PWM 输出是否正常。开环控制不依赖电流采样和角度反馈而是按照给定的电频率直接输出电压矢量。// foc.c void FOC_OpenLoop(float voltage, float frequency, float dt) { static float theta 0.0f; float omega 2.0f * 3.14159265f * frequency; theta omega * dt; if (theta 2.0f * 3.14159265f) { theta - 2.0f * 3.14159265f; } // 在 d 轴施加电压让转子跟随同步旋转 float u_alpha -voltage * sinf(theta); float u_beta voltage * cosf(theta); SVPWM_Update(u_alpha, u_beta); }开环测试时母线电压不要给太高频率从低到高缓慢增加。需要注意开环强行拖动负载较大或加速度过快时电机会失步。4.4 闭环 FOC 代码实现闭环 FOC 的主循环由电流采样中断触发执行一次完整的电流环计算// foc.c void FOC_Update(void) { // 1. Clark 变换 FOC.i_alpha FOC.i_a; FOC.i_beta (FOC.i_a 2.0f * FOC.i_b) * 0.57735026919f; // 2. Park 变换 float cos_theta arm_cos_f32(FOC.theta_elec); float sin_theta arm_sin_f32(FOC.theta_elec); FOC.i_d FOC.i_alpha * cos_theta FOC.i_beta * sin_theta; FOC.i_q -FOC.i_alpha * sin_theta FOC.i_beta * cos_theta; // 3. 电流 PI 控制id0 控制策略 float v_d_ref PID_Update(pid_d, 0.0f, FOC.i_d); float v_q_ref PID_Update(pid_q, speed_ref_to_iq, FOC.i_q); // 4. 逆 Park 变换 FOC.v_alpha v_d_ref * cos_theta - v_q_ref * sin_theta; FOC.v_beta v_d_ref * sin_theta v_q_ref * cos_theta; // 5. SVPWM 输出 SVPWM_Update(FOC.v_alpha, FOC.v_beta); }速度环可以放在主循环中低频执行// main.c 主循环 while (1) { if (speed_loop_ready) { speed_loop_ready 0; // 速度 PI 输出作为 q 轴电流给定 float speed_error speed_ref - FOC.speed; iq_ref speed_pid.Kp * speed_error speed_pid.integral; // 限制最大电流保护 if (iq_ref MAX_CURRENT) iq_ref MAX_CURRENT; if (iq_ref -MAX_CURRENT) iq_ref -MAX_CURRENT; } }角度来源可以是编码器也可以是无感观测器。有感调试时直接读取编码器角度FOC.theta_elec encoder_angle * POLE_PAIRS; FOC.speed encoder_speed_filtered;无感切换时替换成观测器输出FOC.theta_elec SMO_GetAngle(); FOC.speed SMO_GetSpeed();4.5 运行与波形验证通过串口或 DAC 输出调试关键变量建议至少观察以下波形三相电流波形应该是平滑正弦波。( i_d )、( i_q ) 波形稳态时 ( i_d ) 应接近 0( i_q ) 平稳。角度估算值与真实编码器角度误差无感模式下应保持在小范围内。电流波形有明显畸变时优先检查ADC 采样时刻是否正确。采样电阻电流平台是否稳定。扇区切换点是否存在跳变。5. 常见问题与排查思路问题现象常见原因解决思路电机不转有嗡嗡声PWM 输出异常或相序不对用示波器检查 PWM 波形和死区确认 ABC 相序与电机接线一致电流采样值严重偏大/偏小ADC 采样时刻不在电流平台期调整触发时刻加大采样保持时间电机运行时电流波形畸变扇区切换点处理不当或电流零点漂移未校准启动时做 ADC 零点校准检查 SVPWM 扇区计算无感模式切闭环瞬间失步开环加速终点与闭环估算角度不连续在切换点做角度和速度的平滑过渡高速时电流噪声大采样电阻寄生电感、地线干扰优化 PCB 布局采样回路远离功率走线电机发热明显( i_d ) 不为 0 或载波频率设置不当检查 ( i_d ) 是否收敛 0调整 PID 参数启动时抖动无法定位I/F 开环电流幅值不足加大启动电流延长定位时间高频排查技巧启动前把开环电压幅值设为 0手动转动电机轴观察 ADC 采样值是否随转子位置正弦变化。如果波形正常说明电流采样链路基本没有问题。无感 FOC 的启动失步问题是最常见的也是最难排查的。建议按“定位 - 开环拖动 - 闭环切换”三步细化调试每一步单独验证// 状态机伪代码 switch (motor_state) { case STATE_ALIGN: // 输出固定电压矢量定位 500ms SVPWM_Update(align_angle, align_voltage); if (align_timer 500ms) motor_state STATE_OPENLOOP; break; case STATE_OPENLOOP: // 频率斜坡加速 freq ramp_rate * dt; FOC_OpenLoop(voltage, freq, dt); if (freq SWITCH_SPEED) { // 记录当前角度为闭环初始角度 SMO_ResetAngle(openloop_theta); motor_state STATE_CLOSEDLOOP; } break; case STATE_CLOSEDLOOP: FOC_CloseLoop(); break; }切换时的关键是让开环角度与观测器角度尽量连续否则会有冲击。可以先把观测器放在一旁实时运行但不用它的角度控制切换瞬间让观测器角度跟踪当前开环角度。6. 工程最佳实践建议6.1 电流采样标定每次上电应执行 ADC 零点校准把当前静止状态的采样值记录为偏移量。如果运放存在温漂还需要定期校正。零点偏移是电流波形不对称、转矩脉动的常见来源。6.2 母线电压保护在 FOC 控制中增加母线电压监测。母线电压过低时SVPWM 输出会进入过调制区电流波形恶化母线电压过高时可能损坏功率器件。建议实现欠压、过压两级保护。6.3 温度与过流保护利用 MCU 内置比较器或 ADC 实现硬件过流保护。软件过流保护速度不够快建议过流信号直接触发 PWM 刹车Break功能同时进入故障状态必须手动复位才能恢复。6.4 观测器参数整定无感观测器的参数整定需要和电机参数匹配。定子电阻 ( R_s ) 和电感 ( L_s ) 可以通过以下方式获取电阻万用表测量两相间电阻再除以 2。电感使用 LCR 表在 1kHz 左右频率测量。电机参数不准时无感角度会有系统性误差表现为带载能力下降或高速时噪声增大。6.5 调试和数据记录将 FOC 内部变量按结构体集中管理方便通过调试器或串口查看。推荐在代码中实现一个简单的变量监视接口可以在线修改 PID 参数、电流限幅和切换速度大幅提升调试效率。// 简单的在线调参结构 typedef struct { float kp_current; float ki_current; float kp_speed; float ki_speed; float current_limit; float switch_speed; } MotorParams_TypeDef;6.6 安全注意事项调试点动电机前确认机械机构固定可靠避免旋转部件伤人。母线电容放电完成后才能触碰功率板高压实验务必使用隔离电源和隔离探头。7. 后续学习路线掌握本文内容后可以继续深入以下方向学习高频注入HFI方法解决零速和低速无感控制问题。研究模型预测控制MPC在电机控制中的应用替代传统 PI SVPWM。学习 STM32G4 的 Cordic 和数学加速器的进一步用法实现更高载波频率的 FOC。尝试多电机同步控制在双电机方案中对比同步触发与错相触发的实际效果。研究弱磁控制让 PMSM 在额定转速以上运行。FOC 是一个理论与实践结合很强的领域。建议先在有感模式下把电流环调顺再切换无感先跑通开环再切入闭环。每一步都保留可回退的测试节点这样项目推进会更稳。如果这篇文章对你有帮助可以收藏备用后续我也会继续发布更多 FOC 驱动和 STM32 电机控制相关的实战记录。
返回列表