4. 基本矢量作用时间:计算相邻非零矢量与零矢量的作用时间

好,我们继续往下走。上一章我们把扇区判断搞定了,知道了参考电压矢量落在哪个60°扇区里。那接下来要解决的核心问题就是:在这个扇区里,两个相邻的非零矢量和零矢量,各自要作用多长时间?

说白了,就是怎么用时间配比来合成我们想要的电压矢量。这步算不对,后面的PWM波形全是错的。我记得刚入行那会儿,有次调试电机抖得跟筛糠似的,查了半天,就是作用时间算错了小数点。

4.1 伏秒平衡原理:时间计算的基石

在讲具体公式之前,得先理解一个基本原则——伏秒平衡。

什么意思?你想想看,在一个PWM周期 \( T_s \) 内,我们想让逆变器输出的平均电压等于参考电压 \( V_{ref} \)。但实际逆变器只能输出那几个基本矢量(比如 \( V_1 \)、\( V_2 \) 和零矢量 \( V_0/V_7 \))。

所以,我们让这几个矢量分别作用一段时间,只要它们产生的总效果(伏秒积)跟参考矢量连续作用一个周期一样,那就等效了。公式长这样:

\[ V_{ref} \cdot T_s = V_x \cdot T_x + V_y \cdot T_y + V_0 \cdot T_0 \]

其中:

  • \( V_x \)、\( V_y \) 是扇区边界的两个相邻非零矢量
  • \( T_x \)、\( T_y \) 是它们的作用时间
  • \( V_0 \) 是零矢量,\( T_0 \) 是零矢量作用时间

而且时间必须满足:\( T_x + T_y + T_0 = T_s \)。

核心思想: 用有限个离散的开关状态,在时间上平均出连续的电压效果。这就是SVPWM能输出正弦波的根本原因。

4.2 第一扇区(I扇区)的作用时间推导

我们拿第一扇区来举例。假设参考矢量 \( V_{ref} \) 落在 \( V_1(100) \) 和 \( V_2(110) \) 之间,夹角为 \( \theta \)。

根据伏秒平衡,在 \( \alpha\beta \) 坐标系下分解:

  • \( \alpha \) 轴: \( |V_{ref}| \cdot \cos\theta \cdot T_s = |V_1| \cdot T_1 + |V_2| \cdot \cos 60^\circ \cdot T_2 \)
  • \( \beta \) 轴: \( |V_{ref}| \cdot \sin\theta \cdot T_s = |V_2| \cdot \sin 60^\circ \cdot T_2 \)

这里 \( |V_1| = |V_2| = \frac{2}{3}V_{dc} \)(这是基本矢量的幅值,我们之前讲过)。

解这个方程组,得到:

\[ T_1 = \frac{\sqrt{3} \cdot |V_{ref}| \cdot T_s}{V_{dc}} \cdot \sin(60^\circ - \theta) \] \[ T_2 = \frac{\sqrt{3} \cdot |V_{ref}| \cdot T_s}{V_{dc}} \cdot \sin(\theta) \]

看着有点复杂?别急,我们一般会引入调制比 \( m \)**的概念:

\[ m = \frac{\sqrt{3} \cdot |V_{ref}|}{V_{dc}} \]

这样公式就清爽多了:

\[ T_1 = m \cdot T_s \cdot \sin(60^\circ - \theta) \] \[ T_2 = m \cdot T_s \cdot \sin(\theta) \]

我的习惯: 在实际代码里,我不会每次都算sin函数。我会提前算好一个正弦表,查表加线性插值,速度快很多。特别是对于低成本MCU,这能省下不少CPU周期。

4.3 扩展到所有扇区:通用计算公式

其他扇区的推导思路完全一样。我总结了一个通用方法,你直接套用就行:

  1. 确定当前扇区号 \( N \)(上一章我们讲过怎么算)
  2. 找到扇区对应的两个相邻矢量 \( V_x \)、\( V_y \)
  3. 计算参考矢量相对于扇区起始边的角度 \( \theta_{rel} \)
  4. 代入上面的公式,把 \( \theta \) 换成 \( \theta_{rel} \)

为了方便查,我整理了一张表:

扇区号 相邻矢量 \( T_x \)(第一个矢量) \( T_y \)(第二个矢量)
I \( V_1(100) \)、\( V_2(110) \) \( m \cdot T_s \cdot \sin(60^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta) \)
II \( V_3(010) \)、\( V_2(110) \) \( m \cdot T_s \cdot \sin(120^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta - 60^\circ) \)
III \( V_3(010) \)、\( V_4(011) \) \( m \cdot T_s \cdot \sin(180^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta - 120^\circ) \)
IV \( V_5(001) \)、\( V_4(011) \) \( m \cdot T_s \cdot \sin(240^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta - 180^\circ) \)
V \( V_5(001) \)、\( V_6(101) \) \( m \cdot T_s \cdot \sin(300^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta - 240^\circ) \)
VI \( V_1(100) \)、\( V_6(101) \) \( m \cdot T_s \cdot \sin(360^\circ - \theta) \) \( m \cdot T_s \cdot \sin(\theta - 300^\circ) \)

注意: 这里的 \( \theta \) 是参考矢量在静止坐标系下的绝对角度(0°~360°)。计算时注意角度范围,别搞混了。我曾经在扇区边界上因为角度跳变没处理好,导致电流波形出现毛刺,查了好久才找到原因。

4.4 零矢量作用时间与过调制处理

两个非零矢量的时间算出来了,零矢量的时间就简单了:

\[ T_0 = T_s - T_x - T_y \]

但这里有个坑——过调制。

如果 \( T_x + T_y > T_s \),说明参考电压矢量超出了六边形的内切圆范围(也就是调制比 \( m > 1 \))。这时候你算出来的 \( T_0 \) 是负数,物理上不可能实现。

怎么办?我一般这样处理:

  1. 先判断:如果 \( T_x + T_y \leq T_s \),正常计算
  2. 如果超了,按比例压缩:
\[ T_x' = \frac{T_x}{T_x + T_y} \cdot T_s \] \[ T_y' = \frac{T_y}{T_x + T_y} \cdot T_s \] \[ T_0 = 0 \]

这样虽然输出电压的幅值和相位会有点失真,但至少系统不会崩溃。实际项目中,我一般会把调制比限制在0.95左右,留点余量。

4.5 整体计算流程图

下面这张图把整个计算流程串起来了,你可以对照着看:

SVPWM作用时间计算流程图 SVPWM 作用时间计算流程 输入 Vα, Vβ, Ts 扇区判断 (N=1~6) 计算 Tx, Ty (查表/公式) Tx + Ty ≤ Ts ? 是 否 T0 = Ts - Tx - Ty 比例压缩 Tx', Ty' 输出 Tx, Ty, T0 过调制时 T0 = 0,电压会失真

4.6 代码实现示例

最后,给出一段C代码,把上面的逻辑落地。这是我个人比较喜欢的写法,清晰且高效:

// 计算基本矢量作用时间
// 输入: V_alpha, V_beta, Ts, Vdc
// 输出: T1, T2, T0

void calc_vector_times(float V_alpha, float V_beta, float Ts, float Vdc,
                       float *T1, float *T2, float *T0) {
    // 1. 计算扇区号 (假设已有函数)
    int sector = get_sector(V_alpha, V_beta);
    
    // 2. 计算中间变量 X, Y, Z (基于 Clarke 变换结果)
    float X = V_beta;
    float Y = (sqrtf(3)/2) * V_alpha - 0.5 * V_beta;
    float Z = (-sqrtf(3)/2) * V_alpha - 0.5 * V_beta;
    
    // 3. 根据扇区查表赋值
    switch(sector) {
        case 1: *T1 = Z; *T2 = Y; break;
        case 2: *T1 = Y; *T2 = -X; break;
        case 3: *T1 = -Z; *T2 = X; break;
        case 4: *T1 = -X; *T2 = Z; break;
        case 5: *T1 = X; *T2 = -Y; break;
        case 6: *T1 = -Y; *T2 = -Z; break;
        default: *T1 = 0; *T2 = 0; break;
    }
    
    // 4. 归一化到 Ts
    float t_factor = Ts / Vdc;
    *T1 *= t_factor;
    *T2 *= t_factor;
    
    // 5. 过调制处理
    float sum = *T1 + *T2;
    if (sum > Ts) {
        *T1 = (*T1 / sum) * Ts;
        *T2 = (*T2 / sum) * Ts;
        *T0 = 0;
    } else {
        *T0 = Ts - sum;
    }
}

避坑指南: 注意浮点运算的精度。在定点MCU上,我建议把所有计算都转成Q15或Q24格式,避免除法带来的性能开销。另外,sqrtf(3) 可以预编译成常量,别每次都算。

嗯,到这里,基本矢量的作用时间计算就讲完了。你只要把扇区判断和这套时间计算公式吃透,SVPWM的核心算法就算拿下了。下一块内容,我们会把这些时间值映射到实际的PWM比较寄存器上,生成真正的开关波形。