4、扇区判断实战:基于ADC采样的三相电压获取、软件实现扇区判断、边界条件处理与防抖
扇区判断,说白了就是让控制器知道「转子现在在哪个60°区间里」。没有这一步,后面的电压矢量合成全是瞎蒙。我刚开始做SVPWM时,总觉得扇区判断不就是几个比较器的事吗?结果一上硬件,噪声、采样延迟、边界抖动全来了。嗯,这一章咱们就把这些坑一个一个填上。
4.1 三相电压的ADC采样基础
要判断扇区,首先得拿到三相电压的真实值。实际系统中,我们通常用电阻分压加运放调理,把母线电压和相电压降到ADC可采的0~3.3V范围。
假设我们有三相电压 Ua、Ub、Uc,ADC转换后得到数字量 ADC_Ua、ADC_Ub、ADC_Uc。需要先做一下标幺化处理:
// 假设ADC为12位,参考电压3.3V,分压比K
#define ADC_MAX 4095
#define V_REF 3.3f
#define K_DIV 10.0f // 分压系数
float Ua = (float)ADC_Ua / ADC_MAX * V_REF * K_DIV;
float Ub = (float)ADC_Ub / ADC_MAX * V_REF * K_DIV;
float Uc = (float)ADC_Uc / ADC_MAX * V_REF * K_DIV;
这里有个细节:三相电压是浮地的,你得先确认参考地是母线负端还是电机中性点。我在项目中遇到过把中性点电压当参考地,结果算出来的扇区全反了——折腾了两天才发现是参考点选错了。
4.2 软件实现扇区判断
拿到三相电压后,我们通常先做Clark变换,把三相静止坐标系转到两相静止坐标系:
float Ualpha = Ua - 0.5f * Ub - 0.5f * Uc;
float Ubeta = (sqrtf(3)/2.0f) * (Ub - Uc);
然后根据 Ualpha 和 Ubeta 的符号及比例关系,就能判断扇区。经典方法是计算三个中间变量:
float B1 = Ubeta;
float B2 = -0.5f * Ubeta + 0.866f * Ualpha;
float B3 = -0.5f * Ubeta - 0.866f * Ualpha;
int sector = 0;
if (B1 > 0) sector |= 1;
if (B2 > 0) sector |= 2;
if (B3 > 0) sector |= 4;
这样得到的 sector 值(1~6)直接对应六个扇区。你想想看,这个判断逻辑其实就是在看电压矢量落在哪个60°楔形里。
| sector值 | 对应扇区 | 角度范围 |
|---|---|---|
| 3 | 扇区I | 0° ~ 60° |
| 1 | 扇区II | 60° ~ 120° |
| 5 | 扇区III | 120° ~ 180° |
| 4 | 扇区IV | 180° ~ 240° |
| 6 | 扇区V | 240° ~ 300° |
| 2 | 扇区VI | 300° ~ 360° |
4.3 边界条件处理
理想情况下,扇区边界是清晰的。但实际采样值总有噪声,当电压矢量正好落在扇区边界附近时,B1、B2、B3 会在0附近来回跳变,导致扇区频繁切换。这就是所谓的「边界抖动」。
我曾经在一个风机项目上遇到过这个问题:扇区在边界处每秒切换上千次,电机发出尖锐的啸叫声。后来加了滞环才解决。
滞环处理的思路很简单:给比较阈值加一个死区。比如原来判断 B1 > 0,现在改成 B1 > THRESHOLD,其中 THRESHOLD 是一个很小的正数(比如0.01)。
#define HYST_THR 0.01f
int sector = 0;
if (B1 > HYST_THR) sector |= 1;
if (B2 > HYST_THR) sector |= 2;
if (B3 > HYST_THR) sector |= 4;
但这样会引入一个副作用:边界附近的扇区判断会「延迟」一点点。不过对于大多数电机控制应用,这个延迟完全可以接受。
4.4 防抖策略:软件滤波与状态保持
除了滞环,还有几种常用的防抖手段:
- 连续确认法:连续N次采样都判断为同一扇区,才更新扇区值。N一般取3~5。
- 一阶低通滤波:对
Ualpha、Ubeta做滤波,再判断扇区。适合噪声频率较高的场景。 - 状态机保持:扇区变化时,强制保持当前扇区一段时间(比如几个PWM周期),避免频繁切换。
我个人最常用的是「连续确认+滞环」的组合。代码实现大概是这样:
static int last_sector = 0;
static int confirm_count = 0;
int get_sector_with_debounce(float Ualpha, float Ubeta) {
int raw_sector = calc_sector(Ualpha, Ubeta);
if (raw_sector == last_sector) {
confirm_count++;
if (confirm_count >= 3) {
return raw_sector;
}
} else {
confirm_count = 0;
last_sector = raw_sector;
}
return last_sector; // 保持旧值
}
你想想看,这个防抖逻辑其实和按键消抖是一个道理——都是「别信单次采样,多确认几次再说」。
4.5 扇区判断整体流程
把上面所有环节串起来,完整的扇区判断流程如下:
- ADC触发采样,读取三相电压数字量
- 标幺化、滤波(可选)
- Clark变换得到
Ualpha、Ubeta - 计算中间变量
B1、B2、B3 - 滞环比较 + 防抖处理
- 输出最终扇区号(1~6)
下面这张图把整个流程串起来了,你可以对照着看:
Ualpha、Ubeta 和扇区号通过DAC或串口输出,用示波器或上位机观察。如果扇区切换时有毛刺,说明防抖参数需要调整。