我们计算出了三相电压的作用时间(Va、Vb、Vc),但实际应用中需限制占空比范围(0~100%),避免 “过调制” 导致电机失控。以下是完整的 SVPWM 生成代码及解析:核心代码片段(位于svpwm.c):
完整代码片段(位于svpwm.c):
c
运行
#include "svpwm.h"
#include "tim.h"
#include "math.h"
#define PWM_PERIOD 1000 // PWM周期(对应TIM1的ARR=999,占空比范围0~1000)
uint16_t duty_u = 0, duty_v = 0, duty_w = 0; // U、V、W相占空比(0~1000)
/**
* @brief SVPWM生成:根据Vα、Vβ生成三相PWM占空比
* @param Valpha:α轴电压(Q15格式)
* @param Vbeta:β轴电压(Q15格式)
*/
void svpwm_generate(int16_t Valpha, int16_t Vbeta)
{
int32_t Va, Vb, Vc;
int32_t t1, t2, t0; // 相邻有效矢量作用时间、零矢量作用时间
uint8_t sector; // 电压矢量所在扇区(1~6)
// 1. 计算电压矢量的扇区(1~6,通过Vα、Vβ的正负和大小判断)
sector = svpwm_calc_sector(Valpha, Vbeta);
// 2. 根据扇区计算t1、t2(相邻有效矢量作用时间)
switch (sector)
{
case 1:
// 扇区1:有效矢量为V0(001)和V1(011),t1对应Vβ,t2对应Vα-Vβ
t1 = (int32_t)Vbeta * PWM_PERIOD / 32768; // Q15格式转换为实际值(除以32768)
t2 = (int32_t)(Valpha - Vbeta) * PWM_PERIOD / 32768;
break;
case 2:
// 扇区2:有效矢量为V1(011)和V2(010),t1对应Vα,t2对应-Vβ
t1 = (int32_t)Valpha * PWM_PERIOD / 32768;
t2 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
break;
case 3:
// 扇区3:有效矢量为V2(010)和V3(110),t1对应-Valpha,t2对应-Valpha-Vbeta
t1 = (int32_t)(-Valpha) * PWM_PERIOD / 32768;
t2 = (int32_t)(-Valpha - Vbeta) * PWM_PERIOD / 32768;
break;
case 4:
// 扇区4:有效矢量为V3(110)和V4(100),t1对应-Vbeta,t2对应-Valpha
t1 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
t2 = (int32_t)(-Valpha) * PWM_PERIOD / 32768;
break;
case 5:
// 扇区5:有效矢量为V4(100)和V5(101),t1对应Valpha,t2对应Valpha+Vbeta
t1 = (int32_t)Valpha * PWM_PERIOD / 32768;
t2 = (int32_t)(Valpha + Vbeta) * PWM_PERIOD / 32768;
break;
case 6:
// 扇区6:有效矢量为V5(101)和V0(001),t1对应Vbeta,t2对应-Vbeta
t1 = (int32_t)Vbeta * PWM_PERIOD / 32768;
t2 = (int32_t)(-Vbeta) * PWM_PERIOD / 32768;
break;
default:
t1 = 0;
t2 = 0;
break;
}
// 3. 计算零矢量作用时间(t0 = 周期 - t1 - t2,零矢量用于补充周期,避免占空比溢出)
t0 = PWM_PERIOD - t1 - t2;
// 零矢量时间分配(上下桥臂均衡,减少MOS管发热:t0/2分配到周期开头和结尾)
int32_t t0_half = t0 / 2;
// 4. 根据扇区分配时间,计算三相占空比(Va、Vb、Vc为各相的导通时间)
switch (sector)
{
case 1:
Va = t1 + t2 + t0_half; // U相:t1 + t2 + 半零矢量
Vb = t2 + t0_half; // V相:t2 + 半零矢量
Vc = t0_half; // W相:半零矢量
break;
case 2:
Va = t1 + t0_half; // U相:t1 + 半零矢量
Vb = t1 + t2 + t0_half; // V相:t1 + t2 + 半零矢量
Vc = t0_half; // W相:半零矢量
break;
case 3:
Va = t0_half; // U相:半零矢量
Vb = t1 + t2 + t0_half; // V相:t1 + t2 + 半零矢量
Vc = t2 + t0_half; // W相:t2 + 半零矢量
break;
case 4:
Va = t0_half; // U相:半零矢量
Vb = t1 + t0_half; // V相:t1 + 半零矢量
Vc = t1 + t2 + t0_half; // W相:t1 + t2 + 半零矢量
break;
case 5:
Va = t2 + t0_half; // U相:t2 + 半零矢量
Vb = t0_half; // V相:半零矢量
Vc = t1 + t2 + t0_half; // W相:t1 + t2 + 半零矢量
break;
case 6:
Va = t1 + t2 + t0_half; // U相:t1 + t2 + 半零矢量
Vb = t0_half; // V相:半零矢量
Vc = t1 + t0_half; // W相:t1 + 半零矢量
break;
default:
Va = 0;
Vb = 0;
Vc = 0;
break;
}
// 5. 占空比限制:防止过调制(占空比必须在0~PWM_PERIOD之间,避免MOS管持续导通)
duty_u = (Va < 0) ? 0 : (Va > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Va;
duty_v = (Vb < 0) ? 0 : (Vb > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Vb;
duty_w = (Vc < 0) ? 0 : (Vc > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Vc;
// 6. 更新定时器CCR寄存器,输出PWM(TIM1的CCR1=U相,CCR2=V相,CCR3=W相)
tim_set_pwm_duty(TIM1, TIM_CHANNEL_1, duty_u);
tim_set_pwm_duty(TIM1, TIM_CHANNEL_2, duty_v);
tim_set_pwm_duty(TIM1, TIM_CHANNEL_3, duty_w);
}
/**
* @brief 定时器PWM占空比更新函数
* @param htim:定时器句柄
* @param channel:定时器通道(1~3)
* @param duty:占空比(0~PWM_PERIOD)
*/
void tim_set_pwm_duty(TIM_HandleTypeDef *htim, uint32_t channel, uint16_t duty)
{
switch (channel)
{
case TIM_CHANNEL_1:
htim->Instance->CCR1 = duty; // 更新CCR1寄存器(U相)
break;
case TIM_CHANNEL_2:
htim->Instance->CCR2 = duty; // 更新CCR2寄存器(V相)
break;
case TIM_CHANNEL_3:
htim->Instance->CCR3 = duty; // 更新CCR3寄存器(W相)
break;
default:
break;
}
}
代码解析:
- 过调制保护:通过
duty_u = (Va < 0) ? 0 : (Va > PWM_PERIOD) ? PWM_PERIOD : (uint16_t)Va限制占空比在 0~1000 之间,防止 MOS 管因持续导通(占空比 100%)过热烧毁; - 零矢量分配:将零矢量时间(t0)拆分为两半(t0_half),分别放在 PWM 周期的开头和结尾,确保上下桥臂的开关频率均衡,减少功率损耗;
- 寄存器更新:直接操作定时器的
CCR(捕获比较寄存器),实时更新占空比 ——MCU 每执行一次svpwm_generate(),就会刷新一次 PWM 信号,确保电机响应速度。
3.2.6 电流环 PID 控制:“精准控制电机转矩”
FOC 算法中,电流环是核心控制环节 —— 通过 PID 调节器,将实际电流(Id、Iq)与目标电流(Id_ref=0,Iq_ref 由油门指令换算)的偏差消除,输出精准的电压指令(Vd、Vq)。
电流环的控制精度直接决定电机的平顺性:若 PID 参数不当,会出现电机抖动、响应迟缓等问题。以下是 AM32 源码中电流环 PID 的实现与解析。
(1)PID 控制原理与 AM32 的优化
传统 PID 公式为:\(U(k) = K_p \cdot e(k) + K_i \cdot \sum_{0}^{k} e(i) + K_d \cdot [e(k) - e(k-1)]\)其中:
- \(U(k)\):PID 输出(电压指令 Vd/Vq);
- \(e(k)\):当前误差(目标电流 - 实际电流);
- \(K_p\)(比例系数):快速响应误差,越大响应越快,但易超调;
- \(K_i\)(积分系数):消除静态误差,越大静态误差越小,但易震荡;
- \(K_d\)(微分系数):抑制超调,越大抗干扰越强,但易放大噪声。
AM32 源码对传统 PID 做了两处优化:
- 积分分离:当误差较大时(如电机启动瞬间),关闭积分项,避免积分饱和导致超调;
- 抗积分饱和:当 PID 输出达到上限(如 Vd/Vq 最大电压)时,停止积分累积,防止后续误差反向时积分项 “拖后腿”。
(2)电流环 PID 源码实现
PID 核心代码位于pid.c,包含 PID 初始化、参数更新、计算三个函数。以下是完整代码:
代码片段(位于pid.c):
c
运行
#include "pid.h"
/**
* @brief PID初始化:设置PID参数、积分限制、输出限制
* @param pid:PID结构体指针
* @param kp:比例系数(Q15格式)
* @param ki:积分系数(Q15格式)
* @param kd:微分系数(Q15格式)
* @param integral_limit:积分上限(防止积分饱和)
* @param output_limit:输出上限(防止过调制)
*/
void pid_init(PID_HandleTypeDef *pid, int16_t kp, int16_t ki, int16_t kd,
int32_t integral_limit, int16_t output_limit)
{
pid->kp = kp;
pid->ki = ki;
pid->kd = kd;
pid->integral_limit = integral_limit; // 积分上限(如±10000)
pid->output_limit = output_limit; // 输出上限(如±32767,Q15格式)
pid->integral = 0;

最低0.47元/天 解锁文章
1053

被折叠的 条评论
为什么被折叠?



