一、无感观测的锁相环 (PLL)
1.1 反电动势是什么
反电动势 (Back EMF) 是导体在磁场中运动时产生的感应电压,方向总是反抗原来的驱动电压。
在电机转动的情况下,转子永磁铁扫过定子绕组,使其定子绕组的磁链在变化产生感应电压。这个电压总是阻碍电流继续增加,也就是和外部提供的电压方向相反,所以称之为反电动势。
对定子和转子的磁链取微分即可得到反电动势:
= 反电动势
= 永磁体在定子绕组中建立的磁链
同样的,转速乘以磁链常数也可以得出反电动势,速度越高,反电动势越大:
= 反电动势
= 电角速度
= 磁链常数
电机的电压方程是:
- v = 我们施加的相电压
= 铜损压降
= 电感压降
- e = 反电动势
根据此公式我们也可以看出,如果电机堵转,反电动势为 0,电压会全部加到绕组上引起绕组铜线发热。
1.2 包含角度信息的反电动势
电机的反电动势和电机当前的电角度是有关系的,从下图我们看出 ABC 三相反电动势和角度的关系:

如果我们转化为 αβ 轴电压,关系如下:
= 电机真实电角度
= 电机角速度
= 反电动势常数
对 cos 和 sin 相除可得 arctan,所以我们通过 αβ 轴求反电动势可得真实的角度:
= 电角度
= αβ 轴的反电动势
1.3 PLL 基本原理
PLL 的作用是将观测器观测到的 αβ 反电动势波形中的噪音过滤掉,从而更好地提取出其中的角度。
PLL 的本质就是:
-
用
和 0 的差值作为相位误差信号
-
通过 PI 计算,逼着估计角度
跟真实角度对齐
1.4 为什么反电动势需要锁相环 (PLL) 滤波
理想情况下,我们的 dq 轴反电动势是这样的:
= d 轴反电动势
= q 反电动势
= 电角速度
= 永磁体磁链常数
如果我们通过观测器观测出的 dq 轴反电动势是纯净无误的,自然就不需要 PLL 这个东西。
不过根据 αβ 轴电压公式可知:
- v = 我们施加的相电压
= 铜损压降
= 电感压降
- e = 反电动势
所以,我们所提取的反电动势 e 一定是包括铜损和电感噪音的,这可能来自观测器的电机参数不准所污染导致,也可能来自 PWM 开关谐波等... 总之我们所观测到的反电动势不可能是完全正确的。
二、锁相环 (PLL) 的实现
2.1 反电动势的帕克变换
这是帕克变换的变换公式:
= dq 轴电流
= αβ 轴电流
= 电角度
同样的,对于 d、q 轴的反电动势来说,可以进行帕克变换:
= dq 轴电流
= αβ 轴电流
= 电角度
我们提取其中的其中 Ed 可得:
2.2 鉴相器
我们正常运行的情况应该是 Ed = 0 的控制办法,也就是说,我们的Ed = 0 应该是等于 0 的,所以其 error 就等于:
= 估算 d 轴反电动势和实际 d 轴反电动势的误差值
= dq 轴反电动势
= αβ 轴反电动势
=用当前角度估算的反电动势
= 估算的角度
2.4 环路滤波器
对误差取积分得到速度:
= 估算 d 轴反电动势和实际 d 轴反电动势的误差值
= kp
= ki
离散化后我们分成两个公式,首先累加误差积分:
= 积分累加
= ki
然后在加上 kp 得到估算的电角速度:
= 估算 d 轴反电动势和实际 d 轴反电动势的误差值
= kp
= ki
= 估算的转速
2.5 压控振荡器
对估算的速度求积分:
= 估算的转速
= 估算的角度
= 间隔时间
2.6 总体框图与流程
下图是总体的 PLL 锁相环框图:

鉴相器得出 error:

对 error 取积分得到速度:

对速度取积分得到角度:

2.7 代码演示
整理的计算流程如下:
代码如下:
cpp
void PLL_Update(PLL_Handle_t *pll, float E_alpha, float E_beta)
{
/* 1. 计算 sin / cos */
float sin_t = sinf(pll->theta_hat);
float cos_t = cosf(pll->theta_hat);
/* 2. 相位误差 Δe = (-Eα)*cosθ̂ + Eβ*sinθ̂ */
float phase_err = (-E_alpha) * cos_t + (E_beta)*sin_t;
/* 3. PI:Δe → ω̂ */
pll->integrator += pll->ki * phase_err;
pll->omega_hat = pll->kp * phase_err + pll->integrator;
/* 4. 积分 ω̂ → θ̂ */
pll->theta_hat += pll->omega_hat;
/* 5. 角度归一化(很重要) */
if (pll->theta_hat > 2.0f * M_PI)
{
pll->theta_hat -= 2.0f * M_PI;
}
else if (pll->theta_hat < 0.0f)
{
pll->theta_hat += 2.0f * M_PI;
}
}