3. 开环电压控制

任务目标

本案例实现无传感器开环电压控制:通过设定电压矢量幅值(Vq)和电角度(θ),在STM32上生成SVPWM波形驱动PMSM/BLDC电机旋转。重点在于建立“电压→角度→PWM占空比”的完整链路,不依赖任何位置传感器或电流反馈。

核心原理与实现路径

开环电压控制将逆变器输出的电压矢量视为一个以设定角速度旋转的向量。实现分为三步:

  1. 根据目标电频率计算每个PWM周期递增的电角度;
  2. 将电压幅值与角度转换为αβ轴分量(Vα=Vq·cosθ,Vβ=Vq·sinθ);
  3. 通过SVPWM算法将Vα/Vβ映射为三相互补PWM占空比。

本案例使用STM32G4系列高级定时器TIM1产生中心对齐的6路PWM,在更新中断中实时更新比较寄存器。代码聚焦核心控制链,ADC采样与保护逻辑不在本案例范围。

硬件依赖

  • STM32G431最小系统板(或任意带TIM1高级定时器的STM32)
  • DRV8301或类似三相栅极驱动器 + 逆变桥
  • PMSM电机(极对数4,或按实际修改)
  • 直流母线电压:24V(用于SVPWM标幺化计算)

关键完整代码

// 开环电压控制核心实现(TIM1更新中断中调用)
// 依赖:TIM1已配置为中央对齐PWM模式1,周期为PWM_PERIOD

#define PWM_PERIOD        6000    // 对应20kHz PWM(168MHz时钟,分频后)
#define POLE_PAIRS        4       // 电机极对数
#define VD_CALIB          0.85f   // 电压幅值标幺值(0~1.0,相对VDC/2)
#define OPEN_LOOP_FREQ    10.0f   // 目标电频率 Hz

static float v_alpha = 0.0f, v_beta = 0.0f;
static float theta_elec = 0.0f;
static float theta_step = 0.0f;

void OpenLoop_Init(void)
{
    // 每PWM周期电角度增量 = 2π * f_elec / f_pwm
    theta_step = 2.0f * 3.14159265f * OPEN_LOOP_FREQ / 20000.0f;
}

void OpenLoop_Update(void)
{
    float sin_val, cos_val;
    float ta, tb, tc;
    float t1, t2;
    int sector;
    float vdc_half = 12.0f;  // 24V母线的一半,用于标幺化

    // 1. 更新电角度(累积)
    theta_elec += theta_step;
    if (theta_elec > 2.0f * 3.14159265f)
        theta_elec -= 2.0f * 3.14159265f;

    // 2. 电压矢量 → αβ轴
    cos_val = cosf(theta_elec);
    sin_val = sinf(theta_elec);
    v_alpha = VD_CALIB * cos_val;
    v_beta  = VD_CALIB * sin_val;

    // 3. SVPWM:标幺化到PWM周期
    ta = v_alpha;
    tb = -0.5f * v_alpha + 0.8660254f * v_beta;
    tc = -0.5f * v_alpha - 0.8660254f * v_beta;

    // 4. 扇区判断与矢量作用时间
    sector = 0;
    if (ta > 0) sector |= 1;
    if (tb > 0) sector |= 2;
    if (tc > 0) sector |= 4;

    switch (sector)
    {
        case 1: // 扇区1: 0-60°
            t1 = tc; t2 = -tb; break;
        case 2: // 扇区2: 60-120°
            t1 = ta; t2 = tc; break;
        case 3: // 扇区3: 120-180°
            t1 = -ta; t2 = tb; break;
        case 4: // 扇区4: 180-240°
            t1 = -tc; t2 = ta; break;
        case 5: // 扇区5: 240-300°
            t1 = tb; t2 = -ta; break;
        case 6: // 扇区6: 300-360°
            t1 = -tb; t2 = -tc; break;
        default:
            t1 = 0; t2 = 0; break;
    }

    // 5. 归一化到PWM比较值(中心对齐)
    float t_sum = t1 + t2;
    float t_zero = 1.0f - t_sum;
    float t_on = t_zero * 0.25f;  // 零矢量分配

    float cmp_a = (t_on + t1 + t2) * PWM_PERIOD;
    float cmp_b = (t_on + t2) * PWM_PERIOD;
    float cmp_c = t_on * PWM_PERIOD;

    // 6. 写入TIM1比较寄存器(CH1/2/3对应A/B/C相)
    TIM1->CCR1 = (uint16_t)cmp_a;
    TIM1->CCR2 = (uint16_t)cmp_b;
    TIM1->CCR3 = (uint16_t)cmp_c;
}

// 在TIM1更新中断服务函数中调用:
void TIM1_UP_IRQHandler(void)
{
    if (TIM1->SR & TIM_SR_UIF)
    {
        TIM1->SR = ~TIM_SR_UIF;
        OpenLoop_Update();
    }
}

编译与运行

  1. 使用STM32CubeMX生成工程,配置TIM1:PWM Generation Channel 1/2/3,频率20kHz,中心对齐模式;使能更新中断。
  2. 将上述代码加入工程,在main()中调用OpenLoop_Init(),并确保TIM1更新中断已全局使能。
  3. 编译烧录后,用示波器观察电机三相端电压(或PWM输出引脚),应看到幅值恒定、频率为10Hz的三相正弦调制波形。

验证方法与判定标准

  • 波形验证:示波器测量U/V/W任意两相间电压,应为正弦波,频率10Hz±0.5Hz,幅值约为0.85×VDC×调制比(约20.4V峰峰值)。
  • 旋转验证:电机应平稳旋转,转速约为 10Hz×60/4 = 150 RPM(极对数4)。用手轻触转轴,能感受到持续转矩,无失步或抖动。
  • 动态响应:修改OPEN_LOOP_FREQ为20Hz重新编译运行,电机转速应同步加倍至约300RPM,波形频率同步变为20Hz。

常见问题

  • 电机不转但PWM有输出:检查直流母线电压是否足够(建议≥12V),确认电机三相线序与PWM通道对应关系。
  • 电机振动不旋转:电角度方向反了,交换任意两相电机线,或将theta_step取负值。
  • 电流过大:降低VD_CALIB至0.5以下,确认电机额定电压匹配。