基于STM32F401与EG2133的无刷电机FOC驱动器设计:从硬件到有感/无感算法全解析

📅 发布时间:2026/7/12 8:23:43 👁️ 浏览次数:
基于STM32F401与EG2133的无刷电机FOC驱动器设计:从硬件到有感/无感算法全解析
基于STM32F401与EG2133的无刷电机FOC驱动器设计从硬件到有感/无感算法全解析很多朋友想学习无刷电机的FOC磁场定向控制算法但面对复杂的理论和代码往往无从下手。今天我就以自己实际做的一个项目为例带大家从零开始手把手搭建一个基于STM32F401和EG2133驱动芯片的FOC驱动器。这个项目支持有感使用MT6701/AS5600磁编码器和无感两种控制模式我会把硬件设计、软件架构、核心算法和调试过程都掰开揉碎了讲清楚。1. 项目概述与硬件选型这个项目最初就是为了学习FOC算法而设计的。我的思路是从最基础的电路设计开始一步步实现控制算法这样理解会更深刻。整个项目的视频教程和源代码我都开源了大家可以在文末找到链接。1.1 核心硬件清单咱们先来看看需要用到哪些核心器件。选型时我主要考虑了性价比和学习的便利性。器件名称型号关键参数/说明主控MCUSTM32F401RCT6Cortex-M4内核带DSP和FPU主频84MHz256KB Flash64KB SRAM。FOC算法计算量大M4内核的DSP指令和硬件FPU是刚需。三相半桥驱动芯片EG2133集成三路半桥驱动支持3.3V/5V逻辑电平输入自带死区控制典型100ns能有效防止上下桥臂直通。功率MOS管NCE4060KN沟道MOS耐压40V持续电流60A导通电阻仅13mΩ10V。驱动12V电机绰绰有余。电流采样芯片TP181A1高精度电流检测放大器增益50V/V共模电压范围0-36V非常适合做相电流采样。磁编码器有感方案MT6701 (SPI) / AS5600 (I2C)用于检测电机转子位置。前期测试用了AS5600后期主要用MT6701因为它输出更稳定。降压芯片SY8205FCC同步降压输入4.5-30V输出5V/5A给MCU和驱动芯片供电。1.2 电机与传感器选型为了控制成本我选了两款很便宜的无刷电机做实验360度无刷云台电机约6.8元12V供电极对数7。2804无刷电机约14.8元12V供电极对数7。对于有感控制需要在电机轴上安装径向磁铁。我用的磁铁尺寸是8mm直径 x 2mm厚度N35材质然后用M2.5的尼龙柱把磁编码器PCB固定在电机尾部让磁铁正对编码器芯片。注意磁铁一定要是径向充磁的磁极在侧面这样编码器才能正确检测旋转角度。轴向充磁的磁极在上下表面不行。2. 硬件电路设计详解硬件是算法跑起来的基础这里有几个关键电路需要特别注意。2.1 核心驱动与逆变电路驱动部分的核心是EG2133芯片和三相全桥逆变电路。EG2133它接收来自STM32的3路PWM信号HIN1, LIN1, HIN2, LIN2, HIN3, LIN3然后输出驱动能力更强的信号HO1, LO1, HO2, LO2, HO3, LO3去控制6个MOS管。它内部集成了死区控制省去了我们软件配置的麻烦非常方便。三相逆变桥由6个NCE4060K MOS管组成EG2133驱动它们交替导通将电源的直流电“逆变”成电机所需的三相交流电。EG2133驱动电路原理图三相全桥逆变电路原理图2.2 电流采样电路FOC需要实时检测两相电流第三相可通过计算得出这里用的是TP181A1芯片。它在每相的下桥臂MOS和地之间串联一个采样电阻R_shunt通过测量电阻两端的压降来换算出电流。这里有个非常重要的坑点原理图中芯片的GND信号地和功率地PGND最初是直接连在一起的。在实际调试中大电流开关会在功率地上产生很大的噪声干扰小信号采样。正确的做法是用一个0欧姆电阻或者磁珠将GND和PGND单点连接这样可以有效隔离噪声。电流采样电路原理图2.3 磁编码器接口电路我设计了两种编码器的底板可以插接使用。AS5600通过I2C通信电路简单。MT6701通过SPI通信我最终主要用它因为SPI读数速度更快更稳定。电路上需要注意在电源入口加滤波电容信号线串联小电阻以防过冲。MT6701磁编码器接口电路3. 软件架构与核心配置硬件搭好了接下来就是让STM32“动”起来。我使用STM32CubeMX进行初始化配置然后用HAL库开发这样效率高。3.1 关键外设配置定时器输出PWM使用高级定时器TIM1的CH1、CH2、CH3通道输出三路互补PWM用来驱动EG2133。频率设置为15kHz。计算关键系统时钟84MHz定时器预分频器设为84-1则计数时钟为1MHz。要产生15kHz的PWM自动重载值ARR 1MHz / 15kHz ≈ 66。死区时间需要根据EG2133和MOS管的开关特性设置这里通过CubeMX配置即可。ADC采样配置电流环控制需要高频率、精准的电流采样。模式使用ADC1的注入组Injected Group。注入组可以打断常规转换序列优先级更高适合定时触发。触发源用TIM1的CH4一个不用于驱动的通道输出PWM来触发ADC转换。这样PWM和ADC采样就能严格同步。通道配置两个注入通道分别采样Ia和Ib两相电流的ADC值。中断使能注入组转换完成中断在中断里读取电流值并进行后续的FOC计算。SPI读取编码器配置SPI1与MT6701通信。关键点MT6701数据手册说CPOL1但我实测发现设为CPOL0才能正确读取0-2π的全角度范围。SPI速度不要超过15MHz。串口调试使用UART1并开启DMA发送模式方便用VOFA等工具观察波形。波特率坑点我用的WCH-Link仿真器波特率最高只能稳定在576000再高就会乱码。如果换用更好的仿真器可以尝试更高波特率。3.2 串口打印封装DMA方式调试时打印信息很重要我封装了一个基于DMA的log_DMA函数比用printf重定向更灵活不占用CPU时间。// DMA模式发送串口数据 void log_DMA(const char *format, ...) { va_list args; // 定义参数列表变量 va_start(args, format); // 从format位置开始接收参数表 char strBuf[256]; // 定义输出的字符串 vsprintf(strBuf, format, args); // 使用vsprintf将格式化的数据写入缓冲区 va_end(args); // 结束可变参数的使用 // 等待上次的DMA发送完成防止数据覆盖 while (HAL_UART_GetState(huart1) HAL_UART_STATE_BUSY_TX) { // Wait for DMA transfer to complete } // 启动DMA发送 HAL_UART_Transmit_DMA(huart1, (uint8_t *)strBuf, strlen(strBuf)); }4. FOC核心算法实现与调试这是整个项目的灵魂咱们一步步来。4.1 大脑SVPWM算法SVPWM空间矢量脉宽调制是FOC控制中将计算出的电压矢量Uα, Uβ转换成三路PWM占空比的核心算法。它的目标是让电机产生圆形的旋转磁场。// FOC核心函数输入Uq、Ud和电角度输出三路PWM占空比 void setPhaseVoltage(float Uq, float Ud, float angle_el) { Uq _constraint(Uq, -5.0f, 5.0f); // 限制电压幅值保护电机 static float Ts 1.0f; // SVPWM周期这里归一化为1 float Ta, Tb, Tc; // 最终计算出的三路PWM比较值 float t1, t2, t3, t4, t5, t6, t7; float sum, k_svpwm; // 1. Park逆变换将旋转坐标系下的(Ud, Uq)变回静止坐标系(Uα, Uβ) float U_alpha -Uq * arm_sin_f32(angle_el) Ud * arm_cos_f32(angle_el); float U_beta Uq * arm_cos_f32(angle_el) Ud * arm_sin_f32(angle_el); // 2. 扇区判断 float K sqrt3 * Ts / Udc; // Udc是母线电压sqrt3是根号3 float u1 U_beta * K; float u2 (0.8660254f * U_alpha - 0.5f * U_beta) * K; // 0.8660254 √3/2 float u3 (-0.8660254f * U_alpha - 0.5f * U_beta) * K; // 根据u1, u2, u3的正负判断电压矢量落在哪个扇区共6个 uint8_t sector (u1 0.0f) ((u2 0.0f) 1) ((u3 0.0f) 2); // 3. 计算各扇区非零矢量和零矢量的作用时间 switch (sector) { case 3: // 扇区1 t4 u2; t6 u1; sum t4 t6; if (sum Ts) // 过调制处理如果计算时间超过周期等比例缩小 { k_svpwm Ts / sum; t4 * k_svpwm; t6 * k_svpwm; } t7 (Ts - t4 - t6) / 2.0f; // 零矢量作用时间 Ta t4 t6 t7; Tb t6 t7; Tc t7; break; // ... 其他扇区case 1, 5, 4, 6, 2 类似代码已省略 default: break; } // 4. 将计算出的Ta, Tb, Tc设置到定时器的比较寄存器生成PWM Set_PWM(Ta, Tb, Tc); }代码解读Park逆变换FOC算法在旋转坐标系d-q轴下计算控制量Ud, Uq但PWM作用在静止的三相上所以需要变换回来。扇区判断根据Uα和Uβ判断目标电压矢量在哪个60度扇区不同扇区用的基本电压矢量不同。时间计算用相邻的两个非零矢量如扇区1的U4和U6的合成来逼近目标矢量并插入零矢量U0或U7来填充整个PWM周期。过调制处理当目标电压幅值太大时等比例缩小保证合成不超限。4.2 眼睛MT6701编码器数据读取有感FOC需要精确的转子位置我们通过SPI读取MT6701。// 获取MT6701原始14位角度数据 uint16_t MT6701_GetRawData(void) { uint16_t rawData; uint16_t txData 0xFFFF; // 发送全1读取数据 uint16_t timeOut 200; // 等待SPI总线就绪 while (HAL_SPI_GetState(hspi1) ! HAL_SPI_STATE_READY) { if (timeOut-- 0) { log_DMA(SPI state error!\r\n); return 0; } } MT6701_CS_Enable(); // 拉低片选 HAL_SPI_TransmitReceive(hspi1, (uint8_t *)txData, (uint8_t *)rawData, 1, HAL_MAX_DELAY); MT6701_CS_Disable(); // 拉高片选 return rawData 2; // 数据在高14位右移2位得到 } // 获取带圈数的完整机械角度弧度 float MT6701_GetFullAngle(void) { static float angle_Last 0.0f; float angle MT6701_GetRawAngle(); // 当前周期内的角度0~2π float deltaAngle angle - angle_Last; // 圈数计数角度突变超过0.8圈认为发生了跨圈 if (fabsf(deltaAngle) (0.8f * _2PI)) { rotationCount (deltaAngle 0) ? -1 : 1; } angle_Last angle; return rotationCount * _2PI angle_Last; // 总角度 圈数*2π 本周期的角度 } // 计算转速弧度/秒假设调用周期为1ms float MT6701_GetVelocity(void) { static float full_Angle_Last 0.0f; float full_Angle MT6701_GetFullAngle(); float delta_Angle full_Angle - full_Angle_Last; full_Angle_Last full_Angle; return delta_Angle * 1000.0f; // 因为Ts0.001s速度 Δθ / Ts Δθ * 1000 }关键点MT6701_GetRawAngle()返回的是0~2π对应0~16384的原始角度。MT6701_GetFullAngle()通过检查角度突变来累加圈数解决了编码器单圈测量的限制。MT6701_GetVelocity()通过角度差分计算速度简单有效。实际使用时最好加个低通滤波。4.3 校准电角度零位标定电机安装时编码器的零位和电机D轴直轴即转子永磁体磁场方向的零位不一定对齐。我们需要找到这个偏移量。我试了两种方法推荐第二种// 方法二注入D轴电流进行标定更易理解 setPhaseVoltage(0.0f, 1.0f, 0.0f); // 给定一个小的UdD轴电压Uq0 HAL_Delay(100); // 等待电机转子对齐到D轴 zeroElectricAngleOffset MT6701_GetElectricalAngle(); // 此时读取的电角度就是偏移量原理只给D轴电压不给Q轴电压电机转子会像磁铁一样被“吸”到D轴方向。稳定后编码器读出的角度与理论电角度0度的差值就是零位偏移。以后所有读取的电角度都要减去这个偏移量。4.4 心脏PID控制器FOC包含电流环、速度环、位置环通常都用PID控制。这里以位置式PID为例typedef struct { float Kp, Ki, Kd; // PID参数 float Umax; // 输出限幅 float I; // 积分项 float last_Error; // 上次误差 uint8_t saturation_flag; // 积分抗饱和标志 float Ts; // 控制周期 } PID_Typedef; float PIDCalculate(PID_Typedef *pid, float SetValue, float ActualValue) { float Error SetValue - ActualValue; // 抗饱和积分只有输出未饱和时才积分 if (pid-saturation_flag 0) { pid-I pid-Ki * Error * pid-Ts; } float P pid-Kp * Error; float D pid-Kd * (Error - pid-last_Error) / pid-Ts; float Uo P pid-I D; pid-last_Error Error; // 输出限幅 if (Uo pid-Umax) { pid-saturation_flag 1; Uo pid-Umax; } else if (Uo -pid-Umax) { pid-saturation_flag 1; Uo -pid-Umax; } else { pid-saturation_flag 0; } return Uo; }4.5 闭环控制实现有了上面的基础我们就可以搭建完整的闭环了。通常是从内环到外环依次闭合。电流环最内环在ADC中断中执行。采样两相电流经过Clark、Park变换得到Iq和Id与目标值Iq_set, Id_set做PID计算输出Uq和Ud再经过Park逆变换和SVPWM输出。void close_Current_Control(void) { float el_Angle MT6701_GetElectricalAngle(); Id_Iq_calculate(current, el_Angle); // 计算Iq, Id float Uq PIDCalculate(pid_Iq, Iq_set, current.q); float Ud PIDCalculate(pid_Id, Id_set, current.d); setPhaseVoltage(Uq, Ud, el_Angle); }速度环电流环的基础上速度环PID的输出作为电流环Iq_set的给定。void close_Velocity_Control(void) { actual_Velocity MT6701_GetVelocity(); LowPassFilter(actual_Velocity); // 速度需要滤波 float Iq_target PIDCalculate(velocity_PID, Set_Velocity, actual_Velocity); // 然后将Iq_target作为电流环的输入... }位置环最外环位置环PID的输出作为速度环的给定。void close_Angle_Control(void) { actual_Angle MT6701_GetRawAngle(); float velocity_target PIDCalculate(angle_PID, Set_Angle, actual_Angle); // 然后将velocity_target作为速度环的输入... }5. 性能测试与算力分析用STM32F401做FOC大家最关心的是性能够不够。我做了详细的算力测试开启硬件FPU-O3优化84MHz主频运算类型1亿次耗时单次耗时float 加减乘16677 ms166.77 nsfloat 除法32154 ms321.54 nsarm_sin_f32(DSP库)64313 ms643.13 nsfast_sin(快速算法)66694 ms666.94 nssinf(标准库)1204 ms (100万次)1.204 us结论常规浮点运算很快除法稍慢。三角函数是性能瓶颈FOC中需要大量计算sin和cos。务必使用CMSIS-DSP库的arm_sin_f32函数它比标准库的sinf快近一倍。如果没有DSP库可以自己实现fast_sin快速算法性能与DSP库相当。Cordic算法在MCU上软件实现没有优势更适合FPGA。对于15kHz的FOC控制频率周期约66.7us一次循环中执行多次三角运算、PID计算和Park变换等STM32F401是完全可以胜任的。6. 调试心得与常见问题电机抖动/不转检查PWM输出用示波器看6路PWM是否正常死区时间是否足够。检查编码器读数静止时角度值是否稳定转动时是否线性变化。检查电角度零位零位没标定好相当于给电机的指令全是错的必然抖动。务必做好校准。电流采样噪声大硬件上确保采样运放的GND和功率地单点连接采样电阻两端并联小电容滤波。软件上在ADC中断中读取值后可以进行软件滤波如均值滤波、低通滤波。速度/位置环震荡PID参数问题这是最常见的原因。遵循“先内环后外环”、“先P后I再D”的原则调试。电流环响应最快参数可以大一些速度环和位置环响应慢参数要小尤其是积分项。机械负载实际负载和仿真模型不符需要重新整定PID。开环测试在闭环之前一定要做开环测试。给定一个小的Uq和固定的电角度增量看电机能否平稳缓慢转动。这是验证SVPWM和驱动电路是否正常的最简单方法。这个项目从画板、焊接到调代码我踩遍了能踩的坑。希望这份详细的总结能帮你少走弯路。FOC入门有门槛但拆解成硬件、采样、SVPWM、PID这几个模块后逐个击破你会发现它并没有那么神秘。最重要的是动手做用示波器和调试器观察波形和数据理解每一个信号的变化。项目的所有源码和设计文件都已开源链接在文首祝你调试顺利