STM32G4 + MT6816磁编码器 + FOC 三闭环无刷电机伺服控制系统实战
摘要:基于STM32G474硬件CORDIC加速器与MT6816 14位磁编码器,从零构建FOC三闭环(电流/速度/位置)无刷电机伺服控制系统。核心覆盖Clarke/Park变换硬件加速、SVPWM扇区调制、ADC-PWM同步采样、MT6816 SPI绝对角度读取与电角度对齐、PID三环串联调试,是机器人关节驱动与云台控制的完整工程指南。
一、项目背景与功能介绍
无刷电机(BLDC/PMSM)的FOC(磁场定向控制)是机器人关节、云台稳定器、CNC主轴等精密运动场景的核心技术。相比传统六步换相,FOC通过解耦d/q轴电流实现平滑转矩输出,噪音低、效率高、力矩线性。本文从硬件设计到算法实现,完整构建一个 电流-速度-位置三闭环伺服系统,目标定位精度 ±0.05°,速度带宽 > 500 Hz。
系统功能:
- 电流环:10 kHz 控制频率,力矩精确跟踪
- 速度环:1 kHz,PI调节 + 前馈补偿
- 位置环:500 Hz,PID调节 + 速度前馈
- MT6816 14位绝对角度反馈,上电即知转子位置
- UART/CAN 指令接口,支持位置/速度/力矩三种模式
二、硬件选型与电路设计
2.1 核心器件
| 器件 | 型号 | 关键参数 |
|---|---|---|
| MCU | STM32G474RET6 | 170 MHz, 硬件CORDIC+FMAC, 3×ADC, HRTIM |
| 栅极驱动 | DRV8305 | 三半桥预驱动, 内置3×LDO, SPI诊断 |
| 功率MOSFET | CSD18540Q5B × 6 | 60V/8mΩ, NFET |
| 磁编码器 | MT6816 | 14位绝对角度, SPI 4线, AMR技术 |
| 电流采样 | 0.01Ω采样电阻 × 3 | 运放INA240A3 (100V/V增益) |
| 电源 | 12~24V DC | 驱动电机 + 5V/3.3V LDO |
2.2 关键电路设计要点
电流采样:采用三相下桥臂电阻采样方案。DRV8305内置运放,增益通过SPI配置为10/20/40 V/V。采样信号接ADC1 IN1/IN2/IN3,需在PWM下桥臂导通期中心触发ADC(由定时器TRGO事件同步)。
MT6816 SPI接口:CS→PA4, SCK→PA5, MISO→PA6, MOSI→PA7。SPI时钟建议 ≤ 15 MHz。磁铁使用径向充磁钕铁硼,磁铁中心距IC表面 1.5~2.0 mm。
DRV8305 SPI:独立SPI2连接,CS→PB12, SCK→PB13, MISO→PB14, MOSI→PB15。上电后需配置栅极驱动电流(建议1.0 A源/2.0 A灌)并使能三个半桥。
三、FOC算法核心实现
3.1 Clarke与Park变换(硬件CORDIC加速)
STM32G474内置CORDIC协处理器,可硬件加速sin/cos与atan2计算,单次运算仅需6个时钟周期(35 ns @ 170 MHz):
#include "stm32g4xx_hal.h"
// CORDIC硬件加速: 计算sin(theta)和cos(theta)
void cordic_sincos(float theta, float *sin_val, float *cos_val) {
// 将角度归一化到 [-1, 1) 范围 (CORDIC输入格式)
int32_t angle_q31 = (int32_t)(theta / (2.0f * M_PI) * 0x80000000LL);
// 配置CORDIC: sin/cos模式, q1.31格式, 6次迭代
WRITE_REG(CORDIC->CSR, CORDIC_CONFIG_SINCOS);
WRITE_REG(CORDIC->WDATA, angle_q31);
// 等待结果就绪
while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
int32_t cos_q31 = READ_REG(CORDIC->RDATA);
int32_t sin_q31 = READ_REG(CORDIC->RDATA);
*cos_val = (float)cos_q31 / 0x80000000LL;
*sin_val = (float)sin_q31 / 0x80000000LL;
}
// Clarke变换: 3相 -> 2相静止坐标系
void clarke_transform(float Ia, float Ib, float *Ialpha, float *Ibeta) {
*Ialpha = Ia;
*Ibeta = (Ia + 2.0f * Ib) * 0.57735026919f; // 1/sqrt(3)
}
// Park变换: 2相静止 -> 2相旋转坐标系
void park_transform(float Ialpha, float Ibeta,
float theta_e, float *Id, float *Iq) {
float sin_theta, cos_theta;
cordic_sincos(theta_e, &sin_theta, &cos_theta);
*Id = Ialpha * cos_theta + Ibeta * sin_theta;
*Iq = -Ialpha * sin_theta + Ibeta * cos_theta;
}
// 逆Park变换
void inv_park_transform(float Vd, float Vq,
float theta_e, float *Valpha, float *Vbeta) {
float sin_theta, cos_theta;
cordic_sincos(theta_e, &sin_theta, &cos_theta);
*Valpha = Vd * cos_theta - Vq * sin_theta;
*Vbeta = Vd * sin_theta + Vq * cos_theta;
}
3.2 SVPWM扇区调制
SVPWM比SPWM高15%直流母线利用率,是FOC标准调制方式:
// SVPWM扇区判断与占空比计算
void svpwm_generate(float Valpha, float Vbeta, float Vdc,
float *Ta, float *Tb, float *Tc) {
float Uref1 = Vbeta;
float Uref2 = 0.5f * Valpha + 0.866025f * Vbeta; // sqrt(3)/2
float Uref3 = -0.5f * Valpha + 0.866025f * Vbeta;
uint8_t sector = (Uref1 > 0 ? 1 : 0)
| (Uref2 > 0 ? 2 : 0)
| (Uref3 > 0 ? 4 : 0);
// T1/T2为相邻两个基本矢量的作用时间
float T1, T2;
float Ts = 1.0f; // 归一化PWM周期
float k = Ts / Vdc;
switch (sector) {
case 3: T1 = k * Uref2; T2 = k * Uref1; break;
case 1: T1 = -k * Uref2; T2 = k * Uref3; break;
case 5: T1 = k * Uref1; T2 = -k * Uref2; break;
case 4: T1 = -k * Uref1; T2 = -k * Uref3; break;
case 6: T1 = -k * Uref3; T2 = k * Uref1; break;
case 2: T1 = k * Uref3; T2 = -k * Uref1; break;
default: T1 = 0; T2 = 0; break;
}
// 过调制处理
if (T1 + T2 > Ts) {
float ratio = Ts / (T1 + T2);
T1 *= ratio;
T2 *= ratio;
}
float T0 = Ts - T1 - T2;
// 三相占空比 (零矢量分割)
switch (sector) {
case 3: *Ta = (T1+T2+T0/2)/Ts; *Tb = (T2+T0/2)/Ts; *Tc = T0/(2*Ts); break;
case 1: *Ta = (T1+T0/2)/Ts; *Tb = (T1+T2+T0/2)/Ts; *Tc = T0/(2*Ts); break;
case 5: *Ta = T0/(2*Ts); *Tb = (T1+T2+T0/2)/Ts; *Tc = (T1+T0/2)/Ts; break;
case 4: *Ta = T0/(2*Ts); *Tb = (T1+T0/2)/Ts; *Tc = (T1+T2+T0/2)/Ts; break;
case 6: *Ta = (T1+T0/2)/Ts; *Tb = T0/(2*Ts); *Tc = (T2+T1+T0/2)/Ts; break;
case 2: *Ta = (T1+T2+T0/2)/Ts; *Tb = T0/(2*Ts); *Tc = (T1+T0/2)/Ts; break;
}
}
3.3 三闭环PID控制器
typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
float integral_limit;
float output_limit;
} PID_t;
float pid_compute(PID_t *pid, float error, float dt) {
pid->integral += error * dt;
// 积分限幅
if (pid->integral > pid->integral_limit)
pid->integral = pid->integral_limit;
else if (pid->integral < -pid->integral_limit)
pid->integral = -pid->integral_limit;
float derivative = (error - pid->prev_error) / dt;
pid->prev_error = error;
float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
// 输出限幅
if (output > pid->output_limit) output = pid->output_limit;
else if (output < -pid->output_limit) output = -pid->output_limit;
return output;
}
// 三闭环串联: 位置环 -> 速度环 -> 电流环
// 10kHz电流环 (TIM1 UP中断)
void foc_current_loop(void) {
// 1. ADC采样三相电流
float Ia = adc_to_current(ADC1_BUF[0]);
float Ib = adc_to_current(ADC1_BUF[1]);
// Ic = -(Ia + Ib) 基尔霍夫定律
// 2. 读取电角度
float theta_e = mt6816_get_electrical_angle();
// 3. Clarke + Park变换
float Ialpha, Ibeta, Id, Iq;
clarke_transform(Ia, Ib, &Ialpha, &Ibeta);
park_transform(Ialpha, Ibeta, theta_e, &Id, &Iq);
// 4. 电流环PID (d轴目标电流=0, q轴由速度环输出)
float Vd = pid_compute(&pid_id, 0.0f - Id, CURRENT_LOOP_DT);
float Vq = pid_compute(&pid_iq, target_Iq - Iq, CURRENT_LOOP_DT);
// 5. 逆Park变换
float Valpha, Vbeta;
inv_park_transform(Vd, Vq, theta_e, &Valpha, &Vbeta);
// 6. SVPWM生成PWM占空比
float Ta, Tb, Tc;
svpwm_generate(Valpha, Vbeta, Vdc, &Ta, &Tb, &Tc);
pwm_set_duty(Ta, Tb, Tc);
}
// 1kHz速度环 (TIM6中断)
void foc_speed_loop(void) {
float current_speed = mt6816_get_speed(); // 角度差分
target_Iq = pid_compute(&pid_speed,
target_speed - current_speed, SPEED_LOOP_DT);
}
// 500Hz位置环 (TIM7中断)
void foc_position_loop(void) {
float current_pos = mt6816_get_position();
target_speed = pid_compute(&pid_position,
target_pos - current_pos, POSITION_LOOP_DT);
}
3.4 MT6816磁编码器SPI驱动
#define MT6816_SPI_TIMEOUT 10 // ms
// 读取14位绝对角度 (0~16383 对应 0~360°)
uint16_t mt6816_read_angle(SPI_HandleTypeDef *hspi) {
uint8_t tx_buf[2] = {0x83, 0x00}; // 角度寄存器地址
uint8_t rx_buf[2] = {0};
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(hspi, tx_buf, rx_buf, 2, MT6816_SPI_TIMEOUT);
HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);
uint16_t raw = ((uint16_t)rx_buf[0] << 8) | rx_buf[1];
// 检查奇偶校验位
if ((raw & 0x4000) == 0) { // bit14=0表示数据有效
return raw & 0x3FFF; // 取低14位
}
return 0xFFFF; // 读数无效
}
// 转换为电角度 (考虑极对数)
float mt6816_get_electrical_angle(void) {
uint16_t raw = mt6816_read_angle(&hspi1);
if (raw == 0xFFFF) return prev_theta_e; // 读数失败用上次值
float mech_angle = (float)raw / 16384.0f * 2.0f * M_PI;
float theta_e = mech_angle * POLE_PAIRS + angle_offset;
// 角度归一化到 [0, 2π)
theta_e = fmodf(theta_e, 2.0f * M_PI);
if (theta_e < 0) theta_e += 2.0f * M_PI;
prev_theta_e = theta_e;
return theta_e;
}
// 电角度对齐: 上电时锁定d轴,读取零点偏移
void foc_angle_alignment(void) {
// 注入d轴电压,让转子对齐到已知电角度0
float align_Vd = 2.0f; // 对齐电压 (V)
pwm_set_duty_by_voltage(align_Vd, 0.0f, 0.0f); // Vq=0
HAL_Delay(1000); // 等待转子稳定
// 读取当前机械角度作为电角度零点
uint16_t raw = mt6816_read_angle(&hspi1);
float mech_zero = (float)raw / 16384.0f * 2.0f * M_PI;
angle_offset = -(mech_zero * POLE_PAIRS);
pwm_set_duty(0.0f, 0.0f, 0.0f); // 释放
HAL_Delay(100);
}
四、踩坑记录与解决方案
坑1:ADC采样时刻不同步导致电流波形毛刺
现象:电流环波形出现周期性尖峰,Id波动达 ±1.5A。
原因:ADC在任意时刻触发采样,可能落在开关切换瞬间,采样到开关噪声。
解决:利用TIM1的TRGO事件在PWM计数器峰值(中心对齐模式向上计数到顶)触发ADC注入组转换。ADC采样点距PWM翻转 > 2 μs,毛刺消除。
// CubeMX: TIM1 -> TRGO Event = Update Event
// ADC1 -> Injected Conv Trigger = TIM1 TRGO
// 关键:中心对齐模式,在计数器顶部采样
坑2:MT6816上电首次读数全零
现象:系统上电后第一次SPI读取返回0x0000,导致电角度对齐错误。
原因:MT6816上电需约5 ms初始化时间,MCU先于编码器就绪。
解决:上电后延时10 ms,然后连续读取直到连续2次非零且差值 < 50 LSB才认为有效。
坑3:CORDIC运算精度不足导致低速爬行
现象:低速(< 50 RPM)时电机抖动严重,力矩脉动明显。
原因:CORDIC默认6次迭代(精度约9.2 bit),sin/cos在0°附近精度不够。
解决:将CORDIC迭代次数从6次提高到10次(精度约15 bit),代价是增加4个时钟周期(总共10周期 = 59 ns),完全可接受。
坑4:速度环积分饱和导致位置超调
现象:大角度阶跃响应时位置超调达30%,稳态后振荡。
原因:速度环积分项在大误差时累积过大,到达目标位置后积分项释放缓慢。
解决:实现条件积分(Conditional Integration)——仅当误差小于阈值时允许积分增长;同时加入速度前馈项 ff_speed = Kff * (target_pos - prev_target_pos) / dt,大幅减小跟踪延迟。
五、性能优化建议
-
FMAC加速PID运算:STM32G474的FMAC(滤波器数学加速器)可硬件执行IIR/FIR滤波,将PID的乘加运算卸载到FMAC,电流环执行时间从3.2 μs降至1.8 μs。
-
双采样点平均:在PWM上升和下降斜坡各采样一次取平均,可消除PWM纹波对电流测量的影响,信噪比提升约6 dB。
-
电流环抗积分饱和:采用Back-calculation方式,当PID输出饱和时将超出部分反算到积分项,防止Windup。
-
速度环观测器:用PLL(锁相环)替代简单角度差分计算速度,带宽更高且延迟更低,特别适合低速平稳运行。
-
HRTIM高分辨率PWM:STM32G474的HRTIM分辨率达184 ps(相比通用定时器的5.88 ns),在低电感电机中可有效降低电流纹波。
六、总结
本文从硬件设计到算法实现,完整构建了STM32G474 + MT6816 + DRV8305的FOC三闭环伺服系统。核心要点:
- 硬件CORDIC替代软件三角函数,6周期完成sin/cos,是G4系列的核心差异化优势
- ADC-PWM同步是FOC稳定运行的前提,采样时刻错误是一切毛刺的根源
- MT6816电角度对齐通过d轴锁定法实现,简单可靠
- 三环串联调试顺序:先电流环(方波响应无超调)→ 再速度环 → 最后位置环,每一步确认稳定后再开外环
本系统可直接用于机器人关节驱动、云台稳定器、精密转台等场景,定位精度 ±0.05°,速度带宽 > 500 Hz,电流环响应 < 200 μs。
本文代码基于STM32G474 + HAL库 + CubeMX开发,硬件平台可参考开源项目 FalconFoc 与 AxDrive-L 学习板。
更多推荐


所有评论(0)