4. FOC速度环调试
任务目标
在上一案例电流环调试的基础上,增加速度环(PI控制器),使电机能够按照给定目标速度稳定运行,并通过阶跃响应测试验证速度闭环控制效果。本案例聚焦于速度环的软件实现与调试方法,不涉及上位机通信与波形显示工具的开发。
必要环境与依赖
- 硬件:带编码器的直流无刷电机(PMSM)、FOC驱动板(如STM32F405 + DRV8301)、24V电源、USB转串口模块
- 软件:STM32CubeIDE 或 Keil MDK,已具备上一案例的电流环FOC代码工程
- 依赖:上一案例已实现的电流环PI控制器、SVPWM、编码器位置读取、ADC电流采样
速度环控制结构
速度环位于电流环外部,级联结构如下:目标速度 ωref 与编码器实测电角速度 ωfb 的误差经速度PI控制器输出 q轴电流参考值 iq_ref,该值作为电流环的输入。速度环采样频率通常为电流环的1/10~1/5,本案例设定速度环频率1kHz,电流环频率10kHz。
关键完整代码
以下代码在上一案例电流环工程基础上新增速度环控制逻辑,文件speed_control.c:
// speed_control.c
#include "speed_control.h"
#include "main.h"
// 速度环PI参数(需根据电机调试)
#define KP_SPEED 0.15f
#define KI_SPEED 0.02f
#define SPEED_LOOP_HZ 1000.0f
#define RAD_PER_SEC_TO_RPM (60.0f / (2.0f * 3.14159f))
static float speed_ref_rpm = 0.0f; // 目标速度 RPM
static float speed_integral = 0.0f;
static float speed_meas_rpm = 0.0f; // 实测速度 RPM
static float iq_ref_output = 0.0f; // 速度环输出(电流环q轴参考)
// 编码器线数(根据实际电机修改)
#define ENC_LINES 1024
#define POLE_PAIRS 7
// 获取电角速度(rad/s),由编码器位置差分计算
float get_electrical_speed_rads(void) {
static int16_t last_enc_raw = 0;
static uint32_t last_tick = 0;
int16_t enc_raw = (int16_t)(TIM2->CNT); // 编码器计数器
uint32_t now = DWT->CYCCNT; // 使用DWT时钟周期计数
float dt = (float)(now - last_tick) / (float)SystemCoreClock;
if (dt <= 0.0f) dt = 0.001f;
int16_t delta = enc_raw - last_enc_raw;
// 处理溢出
if (delta > (ENC_LINES/2)) delta -= ENC_LINES;
if (delta < -(ENC_LINES/2)) delta += ENC_LINES;
float mech_speed_rads = (float)delta / (float)ENC_LINES / dt * 2.0f * 3.14159f;
last_enc_raw = enc_raw;
last_tick = now;
return mech_speed_rads * POLE_PAIRS; // 电角速度
}
// 速度环PI控制器,1kHz调用
void speed_loop_task(void) {
// 读取实测电角速度并转换为RPM
float elec_speed_rads = get_electrical_speed_rads();
speed_meas_rpm = elec_speed_rads / POLE_PAIRS * RAD_PER_SEC_TO_RPM;
// 误差计算
float err = speed_ref_rpm - speed_meas_rpm;
// PI运算(带积分限幅)
speed_integral += KI_SPEED * err / SPEED_LOOP_HZ;
if (speed_integral > 3.0f) speed_integral = 3.0f;
if (speed_integral < -3.0f) speed_integral = -3.0f;
iq_ref_output = KP_SPEED * err + speed_integral;
// 输出限幅(电流环q轴参考最大值,单位A)
if (iq_ref_output > 2.0f) iq_ref_output = 2.0f;
if (iq_ref_output < -2.0f) iq_ref_output = -2.0f;
// 将速度环输出写入电流环参考
set_iq_reference(iq_ref_output);
}
// 设置目标速度(RPM)
void set_speed_reference(float rpm) {
speed_ref_rpm = rpm;
}
// 获取当前实测速度(RPM)
float get_speed_measurement(void) {
return speed_meas_rpm;
}
// 速度环使能/复位
void speed_loop_reset(void) {
speed_integral = 0.0f;
iq_ref_output = 0.0f;
set_iq_reference(0.0f);
}
在main.c中,使用定时器中断以1kHz周期调用speed_loop_task(),电流环仍保持10kHz中断。初始化时调用speed_loop_reset()。
运行与调试步骤
- 将上述代码加入工程,确保
DWT->CYCCNT已使能(在SystemInit()后执行CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;)。 - 编译烧录,连接串口调试助手(波特率115200)。
- 电机上电,先不使能PWM输出,手动旋转电机轴,通过串口打印
get_speed_measurement()确认速度反馈方向与数值正确。 - 使能PWM输出,调用
set_speed_reference(500)设置目标速度500RPM,观察电机是否加速到目标附近。
阶跃响应测试与验证
编写简单测试代码,在串口收到字符's'时执行一次阶跃:
// 串口接收中断回调中
if (rx_char == 's') {
speed_loop_reset();
set_speed_reference(500.0f); // 阶跃到500RPM
}
同时以10ms周期通过串口发送get_speed_measurement()数值,记录从阶跃开始到速度稳定的过程。
通过判定标准:
- 电机能稳定运行在500RPM附近,稳态误差不超过±5%(即475~525RPM范围内);
- 阶跃响应超调量小于20%(即速度峰值不超过600RPM);
- 调节时间(进入±5%误差带)小于1秒。
若速度振荡发散,减小KP_SPEED;若稳态误差过大或响应过慢,适当增大KI_SPEED。积分限幅3.0对应最大约3A电流输出,需根据电机额定电流调整。
常见问题
- 速度反馈方向错误:电机正转时速度为负,交换编码器A/B相或取反
delta值。 - 速度波动大:检查编码器线数与极对数是否设置正确,电流环带宽是否足够(电流环PI参数需已调好)。
- 启动时反转:速度环输出限幅过大导致启动电流冲击,适当降低
iq_ref_output上限。