第4章:经典PID姿态控制

各位同学,今天咱们来聊聊PID控制。说实话,PID这东西在飞控领域已经用了好几十年了,但你别小看它。我见过不少团队,上来就搞什么自适应、神经网络,结果飞起来还不如一个调好的PID稳当。

这一章,我会把PID的原理、串级结构、参数整定,还有那个让人头疼的积分饱和问题,一次性给你讲透。

4.1 PID控制器原理

PID说白了就是三个字:比例、积分、微分。你想想看,无人机要稳住姿态,靠的就是这三个环节的配合。

  • 比例(P):当前偏差有多大,我就给多大的力。偏差大,输出大;偏差小,输出小。简单粗暴。
  • 积分(I):把过去的偏差累加起来。如果一直有稳态误差,积分项就会慢慢增大,直到把误差消除。
  • 微分(D):预测未来的趋势。偏差变化快了,微分项就出来压一压,防止超调。

公式长这样:

u(t) = Kp * e(t) + Ki * ∫e(t)dt + Kd * de(t)/dt

嗯,这里要注意,实际飞控里用的是离散形式。我早期做飞控时,直接把连续公式套进去,结果发现采样周期一变,参数全得重调。后来才明白,离散化处理才是关键。

核心要点:P是现在,I是过去,D是未来。三个环节配合好了,飞机才能稳如老狗。

4.2 角度环与角速度环串级PID

为什么不用单环?我刚开始做飞控时也这么想,一个PID搞定所有不香吗?结果试了才知道,单环PID对外部扰动(比如阵风)的响应太慢了。

串级PID的结构是这样的:

  • 外环(角度环):输入是期望角度,输出是期望角速度。说白了,就是告诉内环「你该转多快」。
  • 内环(角速度环):输入是期望角速度,输出是电机控制量。它负责快速响应,把实际的角速度拉回来。

我个人的习惯是,内环频率跑高一点(200-400Hz),外环频率低一些(50-100Hz)。为什么?因为角速度变化快,角度变化慢。你想想看,飞机稍微一晃,角速度传感器就跳了,但角度可能还没变多少。

实战技巧:内环先调稳,再调外环。我曾经遇到过内环没调好就急着调外环,结果飞机在天上像抽风一样,差点炸机。

4.3 PID参数整定方法

参数整定是门手艺活。我见过有人靠蒙,有人靠公式,有人靠玄学。这里我介绍两种靠谱的方法。

4.3.1 Ziegler-Nichols方法

这个方法适合快速找到一组可用参数。步骤很简单:

  1. 先把积分和微分关掉(Ki=0, Kd=0)。
  2. 慢慢增大Kp,直到系统开始等幅振荡。
  3. 记录此时的临界增益Ku和振荡周期Tu。
  4. 按表格计算参数。
控制器类型 Kp Ki Kd
P 0.5 * Ku - -
PI 0.45 * Ku 1.2 * Kp / Tu -
PID 0.6 * Ku 2 * Kp / Tu Kp * Tu / 8

注意:Z-N方法给出的参数通常偏激进,实际飞行时可能需要手动微调。我建议你把它当作起点,而不是终点。

4.3.2 试凑法

这个方法更依赖经验,但调出来的参数往往更贴合实际。我的步骤是这样的:

  • 先调P:从小到大加,直到飞机有轻微振荡。然后退回来一点。
  • 再加D:D能抑制振荡,让飞机更稳。但D太大会引入噪声,电机嗡嗡响。
  • 最后加I:I消除稳态误差。但I太大会导致超调,甚至积分饱和。

我记得有一次调一架大四轴,P加到0.8时飞机开始抖,我退到0.6,然后加D到0.2,飞机稳得像块石头。最后加I到0.05,悬停误差从5度降到了0.5度以内。

避坑指南:我曾经在试凑时犯过一个低级错误——每次只改一个参数,但改完没等系统稳定就继续调。结果参数越调越乱。记住,每次改完参数,至少等3-5秒观察响应。

4.4 抗积分饱和

积分饱和是个坑,很多新手会栽在这里。说白了就是:当执行器已经饱和(比如电机已经满油门了),积分项还在累加,导致偏差消除后,积分项还要花很长时间才能退回来。

后果是什么?飞机超调严重,甚至振荡发散。

解决办法有几种:

  • 积分限幅:给积分项设个上限,别让它无限累加。
  • 积分分离:当偏差大于某个阈值时,停止积分。
  • 反计算法:当执行器饱和时,把多余的积分量减掉。

我个人最常用的是积分限幅加反计算法。代码实现也不复杂:

// 反计算抗积分饱和
float pid_update(float setpoint, float measurement) {
    float error = setpoint - measurement;
    float p_term = Kp * error;
    
    // 计算未限幅的输出
    float u_unsat = p_term + integral + Kd * (error - prev_error);
    
    // 限幅
    float u_sat = constrain(u_unsat, -limit, limit);
    
    // 反计算:如果输出饱和,减小积分项
    if (u_unsat != u_sat) {
        integral -= Ki * (u_unsat - u_sat) * dt;
    } else {
        integral += Ki * error * dt;
    }
    
    prev_error = error;
    return u_sat;
}

经验之谈:积分限幅的值我一般设为输出限幅的10%-20%。太小了没效果,太大了等于没限。这个比例是我炸了两次机之后才悟出来的。

好了,这一章的内容就到这里。PID看似简单,但真正调好需要大量实践。下一章我们会讲如何把PID应用到实际的姿态解算中,到时候再聊。