4、PI控制器原理:连续域PI与离散域PI、位置式与增量式算法、积分饱和与抗饱和策略

各位做电机控制的朋友,今天我们来聊聊PI控制器。这东西看着简单,但里面的门道真不少。我刚开始做电流环的时候,觉得PI不就是P加I嘛,有什么难的?结果第一次上机调试,电机抖得像筛糠一样,电流波形惨不忍睹。后来才明白,从理论到工程实现,中间还隔着好几道坎。

咱们今天就把这几道坎一一跨过去。从连续域到离散域,从位置式到增量式,再聊聊那个让人头疼的积分饱和问题。嗯,内容不少,但都是干货。

4.1 连续域PI与离散域PI

先说说连续域。在理想世界里,我们的控制器是连续的,公式长这样:

u(t) = Kp * e(t) + Ki * ∫e(τ)dτ

这个公式大家应该都见过。比例项负责快速响应,积分项负责消除静差。但问题是,我们的DSP或者MCU是数字芯片,它只能处理离散的采样点。所以必须把连续域的东西搬到离散域来。

离散化的方法有很多种,我个人最常用的是后向差分法。为什么?因为它实现简单,而且稳定性好。公式如下:

u(k) = Kp * e(k) + Ki * Ts * Σe(i)   (i从0到k)

这里Ts是采样周期。你看,积分变成了累加。这就是离散域PI的本质——用累加代替积分。

关键点:离散域PI的性能严重依赖采样周期Ts。Ts越小,离散化误差越小,但计算负担越重。我在项目中一般取Ts为开关周期的1/2到1倍,比如10kHz的开关频率,采样周期取50-100μs。

还有一种方法是双线性变换法(Tustin变换),精度更高,但计算量也更大。对于电流环这种对实时性要求极高的场景,我建议用后向差分就够了。别为了那一点点精度提升,把CPU时间都吃掉了。

4.2 位置式与增量式算法

好,离散域PI有了,接下来就是实现方式。这里有两个流派:位置式增量式

位置式PI,说白了就是直接算输出值:

u(k) = Kp * e(k) + Ki * Ts * Σe(i)

这个公式直接、直观。但有个问题——它需要记住所有历史误差的累加和。如果累加和出了错,或者发生了溢出,那输出就会直接跳变。我在一个项目中就吃过这个亏,累加器溢出了,电机直接飞车,吓得我赶紧按了急停。

增量式PI就不一样了。它算的是输出量的增量:

Δu(k) = u(k) - u(k-1)
       = Kp * [e(k) - e(k-1)] + Ki * Ts * e(k)

然后:

u(k) = u(k-1) + Δu(k)

你看,增量式只关心当前和上一次的误差,不需要累加所有历史值。这意味着什么?

  • 抗干扰能力强:即使某次计算出了错,影响也只是一次增量,不会让输出剧烈跳变
  • 容易实现手动/自动切换:切换时只需要把当前输出值赋给u(k-1)就行
  • 不会积分饱和? 嗯,这里要打个问号,我们后面细说

我的建议:电流环这种需要快速响应的场景,我推荐用增量式。位置式虽然直观,但累加器管理和抗饱和处理都比较麻烦。我这些年做的项目,90%以上用的都是增量式PI。

4.3 积分饱和与抗饱和策略

终于到了这个让人又爱又恨的话题——积分饱和

为什么会发生积分饱和?你想想看,当电机启动或者突加负载时,误差会很大。积分项拼命累加,很快就涨到天上去了。等误差变小了,积分项还维持着那个巨大的值,导致输出迟迟降不下来。表现出来的现象就是——超调大、响应慢、甚至振荡

我曾经在一个伺服项目中遇到过这种情况。电机启动时电流直接冲到额定值的2倍,然后花了将近100ms才稳定下来。对于伺服系统来说,这简直是灾难。

怎么解决?常用的抗饱和策略有这几种:

策略名称 实现方式 优点 缺点
积分限幅法 给积分项设置上下限 实现简单,计算量小 限幅值不好确定,可能影响稳态精度
积分分离法 误差大时停止积分,误差小时恢复 能有效抑制超调 切换阈值需要调试,可能引入抖动
反计算法 输出饱和时,将超出部分反馈回积分器 平滑性好,工程上最常用 需要额外计算反馈系数
变积分系数法 根据误差大小动态调整积分系数 灵活,适应性强 参数多,调试复杂

我个人最常用的是反计算法(也叫Back-Calculation或Conditional Integration)。它的核心思想是:当输出饱和时,把实际输出和计算输出的差值,按一定比例反馈到积分器输入端,让积分器"冷静"下来。

伪代码实现如下:

// 增量式PI + 反计算抗饱和
float Kp, Ki, Kc;  // Kc是反馈系数
float u_k, u_k_1, e_k, e_k_1;
float integral_term;
float output;

// 计算增量
float delta_u = Kp * (e_k - e_k_1) + Ki * Ts * e_k;
u_k = u_k_1 + delta_u;

// 输出限幅
if (u_k > U_MAX) {
    output = U_MAX;
    // 反计算:将饱和差值反馈回积分器
    integral_term -= Kc * (u_k - U_MAX);
} else if (u_k < U_MIN) {
    output = U_MIN;
    integral_term -= Kc * (u_k - U_MIN);
} else {
    output = u_k;
}

// 更新状态
u_k_1 = output;
e_k_1 = e_k;

注意:反馈系数Kc的选择很关键。Kc太大,积分器恢复太快,可能引起振荡;Kc太小,抗饱和效果不明显。我一般取Kc = 1/Ti,也就是积分时间的倒数。当然,具体值还是要根据实际系统调试。

还有一种简单粗暴的方法——积分限幅。直接给积分项设个上限,比如最大输出的20%-30%。这个方法虽然不够优雅,但在很多场合下够用了。我早期做的一个风机项目,就是用积分限幅搞定的,效果还不错。

最后说一句,增量式PI并不能完全避免积分饱和。虽然它没有显式的累加器,但u(k-1)这个历史值其实就隐含了积分效果。如果输出持续饱和,u(k-1)也会被限幅,导致积分效果丢失。所以,增量式PI同样需要抗饱和处理。

好了,关于PI控制器的这些门道,今天就聊到这里。从连续域到离散域,从位置式到增量式,再到积分饱和的坑,希望这些经验能帮你在实际项目中少走弯路。下一章我们聊聊参数整定的具体方法,到时候再细说。