PID 控制器:让系统学会纠偏

从误差、反馈和阶跃响应开始,建立 PID 控制器的第一套可解释模型。
Author

闭环思过

Published

August 29, 2026

Warning

这是控制算法讲解的第一讲,本人水平有限,尽可能地讲述我对PID的理解,与现实有所偏差请见谅。正文将按照“被控对象建模 → 开环响应 → 闭环反馈 → P / I / D 各项影响→ 限幅与抗积分饱和等补偿 → 实战演示”的顺序展开。

进入 PID 在线调试器,先动手调整参数

本课目标

  • 能写出误差 (e(t)=r(t)-y(t)) 并画出负反馈结构;
  • 能解释比例、积分和微分分别影响什么;
  • 能用阶跃响应判断超调、调节时间和稳态误差;
  • 能说明执行器限幅为什么会导致积分饱和;
  • 能把控制器放入 Simulink,并留下可复现的模型和指标。

先建立一个问题

控制系统真正要解决的问题,不是“让控制器输出一个很大的数”,而是让被控对象在目标变化、扰动和自身动态存在时,仍然按照预期运动。

例如,一个位置系统希望从 0 移动到 1。我们关心的不只是最后是否到达 1,还要问:

  • 到达目标之前有没有冲过头?
  • 需要多长时间才算稳定?
  • 控制输入有没有碰到执行器上下限?
  • 目标改变时,控制量是否出现尖峰?
  • 参数变化以后,系统是否仍然有足够的稳定裕度?

本课用一个简单的二阶对象回答这些问题。它不是某一个特定电机或变换器的完整模型,但足够用来建立可迁移的控制直觉。

被控对象:先看系统本身

当前实验场使用的名义对象为:

\[ G(s)=\frac{18}{s^2+5.2s+20} \]

把它写成时域方程:

\[ \ddot y(t)+5.2\dot y(t)+20y(t)=18u(t) \]

也可以写成更直观的“质量—阻尼—弹簧”形式:

\[ m\ddot y+b\dot y+ky=K_u u \]

在本实验中:

m = 1
b = 5.2
k = 20
K_u = 18

其中 y(t) 是输出,u(t) 是控制输入。k y(t) 表示偏离零点后产生的恢复力,b y'(t) 表示与速度相关的阻尼力,18u(t) 表示控制器对对象施加的作用。

这个模型有两个重要提醒:

  1. 对象本身会运动,不会因为目标值改变就瞬间到达目标;
  2. 控制器的输出最终还要经过执行器,实际控制量不一定等于 PID 的未限幅计算值。

从开环到闭环

开环:不看结果的控制

开环控制只根据预先设定的输入驱动对象:

\[ u(t)\longrightarrow G(s)\longrightarrow y(t) \]

它没有比较目标值和实际输出,因此无法主动纠正模型误差、负载变化或外部扰动。如果对象参数发生变化,原来计算好的输入可能就不再合适。

下面的 Simulink 模型在开环前向通道中加入了一个增益 K=0.8,并额外计算误差 e(t)=r(t)-y(t) 供观察:

开环系统与误差观测模型的阶跃响应

从图中可以同时看到目标输入 r(t)、对象输出 y(t) 和误差 e(t)。需要特别注意:这里的误差信号只被计算和显示,并没有回到前向通道,因此它不会参与控制,系统本质上仍然是开环。若被控对象为

\[ G(s)=\frac{18}{s^2+5.2s+20} \]

则其低频增益为 G(0)=0.9,加入 K=0.8 后,单位阶跃输入的理论稳态输出约为 0.72,稳态误差约为 0.28。这正是开环系统无法根据误差自动修正的表现。

下载开环 Simulink 模型

闭环:用误差驱动修正

闭环系统把输出反馈回来,与目标值比较:

\[ e(t)=r(t)-y(t) \]

其中:

  • r(t):目标值或设定值;
  • y(t):实际输出;
  • e(t):误差。

闭环模型的阶跃响应如下:

闭环 PID 阶跃响应

与开环模型不同,闭环中的误差 e(t) 会进入 PID 控制器。控制器根据误差产生控制量,输出 y(t) 再经过负反馈返回比较器,形成“比较—调节—响应—再比较”的持续纠偏过程。图中 y(t) 虽然先出现短暂过冲和回落,但最终能够回到目标值 r(t) 附近,体现了反馈调节对稳态误差的修正作用。

下载闭环 Simulink 模型

控制器根据误差产生控制输入:

\[ r(t)\longrightarrow e(t)\longrightarrow C(s)\longrightarrow u(t)\longrightarrow G(s)\longrightarrow y(t) \]

负反馈的关键不是“把输出简单地减掉”,而是让系统拥有一个持续的纠偏通道。只要输出还没有达到目标,误差就会继续影响控制器。

对于单位负反馈,开环传递函数和闭环传递函数分别是:

\[ L(s)=C(s)G(s) \]

\[ T(s)=\frac{Y(s)}{R(s)}=\frac{L(s)}{1+L(s)} \]

后面的 Bode 图主要观察 L(s),时域响应则观察闭环 T(s) 的实际表现。两者是同一个闭环问题的两种观察角度。

PID 三项分别在做什么

连续时间 PID 控制器写成:

\[ u(t)=K_p e(t)+K_i\int_0^t e(\tau)\,d\tau+K_d\frac{de(t)}{dt} \]

为了理解每一项,不要先把它们记成三个旋钮,而要分别问:它在看什么信息?它改变了什么行为?它可能带来什么代价?

P:现在差多少

比例项为:

\[ u_P(t)=K_p e(t) \]

误差越大,控制作用越强。增大 Kp 通常会让系统更快地响应,但也可能带来更大的超调、振荡和执行器压力。

只使用 P 控制时,系统不一定能消除阶跃目标下的稳态误差。对当前对象,直流增益为:

\[ G(0)=\frac{18}{20}=0.9 \]

如果只有比例控制,单位阶跃的稳态误差近似为:

\[ e_{ss}=\frac{1}{1+K_pG(0)} \]

Kp = 3 时:

\[ e_{ss}\approx\frac{1}{1+3\times0.9}=0.270 \]

这说明“把 Kp 调大”可以减小误差,但不等于从结构上保证误差为零。

I:过去积累了多少

积分项为:

\[ u_I(t)=K_i\int_0^t e(\tau)\,d\tau \]

只要误差长期存在,积分状态就会持续变化。它可以提供一个长期保持的控制量,消除许多类型的稳态误差。

积分项的代价是它有记忆:

  • 误差持续存在时,积分会不断累积;
  • 执行器饱和时,增加积分不一定能增加实际输出;
  • 目标值改变后,旧的积分状态可能仍然影响当前控制量。

因此,使用积分时必须同时考虑执行器限幅和抗积分饱和。

D:接下来会怎么变化

理想微分项为:

\[ u_D(t)=K_d\frac{de(t)}{dt} \]

它不主要关注误差有多大,而是关注误差正在变大还是变小、变化有多快。当系统快速接近目标时,误差正在减小,D 项可以提供反向作用,帮助系统提前减速,因此常表现为增加阻尼、减小超调。

微分项的代价是对快速变化和测量噪声敏感。若直接对误差微分,目标值突然变化也会进入 D 项:

\[ \frac{de}{dt}=\frac{dr}{dt}-\frac{dy}{dt} \]

这就是目标值阶跃时出现微分冲击的原因。当前交互演示保留了这个现象,方便观察;工程实现通常会让 D 项主要作用于测量值,或先对目标轨迹做平滑处理。

离散 PID:控制器每一拍做什么

数字控制器不是连续地计算,而是每隔采样周期 T_s 更新一次。令第 k 个采样点的目标、输出和误差分别为 r[k]y[k]e[k],则:

\[ e[k]=r[k]-y[k] \]

比例项直接计算:

\[ P[k]=K_p e[k] \]

积分项把当前误差累加到内部状态:

\[ I_{state}[k]=I_{state}[k-1]+T_s e[k] \]

输出形式的积分分量为:

\[ I[k]=K_i I_{state}[k] \]

最简单的后向差分微分为:

\[ D[k]=K_d\frac{e[k]-e[k-1]}{T_s} \]

因此,最基本的离散 PID 计算顺序是:

e[k] = r[k] - y[k]
P[k] = Kp * e[k]
Istate[k] = Istate[k-1] + Ts * e[k]
I[k] = Ki * Istate[k]
D[k] = Kd * (e[k] - e[k-1]) / Ts
u_raw[k] = P[k] + I[k] + D[k]
u[k] = limit(u_raw[k])

这里的 u_raw[k] 是控制器想要的控制量,u[k] 是经过执行器限幅后真正施加给对象的控制量。

从离散公式到 STM32 代码

前面的公式说明了 PID 每一拍要算什么,但数字控制器真正落地时,还要回答四个工程问题:反馈值从哪里来?什么时候计算?输出如何交给执行器?积分和历史量如何保存?STM32 代码正是把这四件事接起来。

先看参考工程的 PID 结构

你提供的 STM32F103C8 标准外设库工程把 PID 状态集中放在 PID_t 结构体中。核心字段可以这样对应:

C 变量 离散控制含义 在循环中的作用
Target 目标值 r[k] 希望系统达到的速度、位置或角度
Actual 反馈值 y[k] 来自编码器或 ADC 的测量量
Err0 当前误差 e[k] Target - Actual
Err1 上一次误差 e[k-1] 为差分微分保留历史
ErrInt 积分累加状态 累积长期误差
Out 控制输出 u[k] 送给电机驱动器或 PWM
OutMax / OutMin 输出限幅 模拟执行器的最大正、负能力

参考代码的核心计算是:

p->Err1 = p->Err0;
p->Err0 = p->Target - p->Actual;
p->ErrInt += p->Err0;
p->Out = p->Kp * p->Err0
       + p->Ki * p->ErrInt
       + p->Kd * (p->Err0 - p->Err1);

if (p->Out > p->OutMax) { p->Out = p->OutMax; }
if (p->Out < p->OutMin) { p->Out = p->OutMin; }

这段代码与离散公式是一一对应的:先保存上一拍误差,再读取当前反馈,更新积分和微分,最后对总输出限幅。它短小、容易移植,很适合第一次理解“控制算法如何变成 C 语言”。

但要注意,它是简化实现:积分和微分都隐含了采样周期,微分直接作用于误差,ErrIntThreshold 超限时会把积分清零,而且没有把执行器饱和反馈到积分器。课程示例会保留这段代码作为参考,同时在下载版本中使用显式 T_s、测量值微分、积分限制和条件积分。

标准库工程的调用链

参考工程中,控制链可以画成:

TIM1 更新中断(1 ms)
        │
        ├─ 每 40 次进入一次 Mode21_Tick / Mode22_Tick
        │
        ├─ TIM3 编码器计数 → 位置反馈或位置差分 → Actual
        │
        ├─ PID_Update() → Out
        │
        └─ Motor_SetPWM() → 方向 GPIO + TIM2 PWM → 电机

Timer.c 将 TIM1 配成:

TIM_Prescaler = 72 - 1;
TIM_Period = 1000 - 1;

在定时器时钟为 72 MHz 时,更新频率为:

\[ f_{update}=\frac{72\ \mathrm{MHz}}{72\times1000}=1\ \mathrm{kHz} \]

所以中断间隔是 1 msMode21_Tick()Mode22_Tick() 每 40 次中断才计算一次 PID,因此控制器实际更新周期约为:

\[ T_s=40\times1\ \mathrm{ms}=40\ \mathrm{ms} \]

这就是为什么 T_s 不能只写在公式里:它必须和定时器调度真正一致。

速度环和位置环的反馈不同

参考工程的定速控制并没有直接测量速度,而是用两个采样时刻的编码器位置差近似速度:

PrevLocation = CurrLocation;
CurrLocation = Encoder_GetLocation(1);
SpeedPID.Actual = (int16_t)(CurrLocation - PrevLocation);
PID_Update(&SpeedPID);
Motor_SetPWM(1, SpeedPID.Out);

可以把它理解为:

\[ v[k]\approx\frac{q[k]-q[k-1]}{T_s} \]

原工程把 T_s 和比例关系吸收到参数与单位定义中,因此 Actual 不是标准国际单位制的速度。教学和调参时必须说明反馈单位,否则同样的 K_pK_iK_d 不能直接搬到另一个编码器或减速比的系统。

定位置控制则直接使用编码器计数:

LocationPID.Actual = Encoder_GetLocation(1);
PID_Update(&LocationPID);
Motor_SetPWM(1, LocationPID.Out);

两者共用 PID 结构,但控制对象、反馈单位、参数和采样解释并不相同。倒立摆模式进一步使用 ADC 角度反馈作为内环,并用位置环输出修改角度目标,这已经是串级控制的雏形。

PID 输出如何变成带方向的 PWM

Motor_SetPWM() 对 PID 输出的正负号做了方向分离:

Out > 0:方向 GPIO 设为正向,PWM 使用 Out
Out < 0:方向 GPIO 设为反向,PWM 使用 -Out
Out = 0:PWM 为零,电机停止

因此 PID 输出的范围必须和 PWM 比较寄存器的范围对应。例如参考工程的 PWM 周期是 100,PID 输出限幅是 -100100,两者正好可以用百分比方式对应。换一个定时器周期,就必须重新设计输出映射,不能直接把 PID 数字写入比较寄存器。

标准库版本:推荐的教学模板

标准库完整示例可查看:STM32 标准库 PID 示例说明

下面是它的核心调用方式。与参考工程相比,这个版本把 T_s=0.04 s 明确写进 PID 初始化,并默认对测量值微分,以减小目标值阶跃造成的微分冲击:

static PID_t speed_pid;
static volatile uint8_t control_tick_pending;
static int16_t previous_location;

PID_Init(&speed_pid,
         0.30f, 0.30f, 0.00f,
         0.040f,
         -100.0f, 100.0f,
         100.0f);

/* 每 1 ms 调用;每 40 次产生一个控制更新请求。 */
void ControlLoop_On1msTick(void)
{
    static uint8_t divider;

    if (++divider >= 40U)
    {
        divider = 0U;
        control_tick_pending = 1U;
    }
}

/* 主循环中调用;这里完成一次采样、计算和 PWM 更新。 */
void ControlLoop_Run(void)
{
    int16_t location;
    int16_t speed_feedback;

    if (control_tick_pending == 0U)
    {
        return;
    }

    control_tick_pending = 0U;
    location = Encoder_GetLocation(1);
    speed_feedback = location - previous_location;
    previous_location = location;

    Motor_SetPWM(1,
        (int16_t)PID_Update(&speed_pid,
                            speed_pid.target,
                            (float)speed_feedback));
}

这里需要区分“节拍”和“计算”:定时器中断只负责按固定时间产生请求,主循环完成反馈读取、PID 计算和输出更新。这样中断较短,也更容易加入串口记录、故障处理和其他任务。真正的高频电流环如果主循环无法保证时序,则应使用更严格的中断或 DMA 调度,但仍然要避免在中断里放阻塞操作。

HAL 版本:基于 CubeMX 的等价实现

HAL 完整示例可查看:STM32 HAL PID 示例说明

HAL 版本的初始化由 CubeMX 生成,应用代码只在 USER CODE 区域调用。示例的主要关系是:

static PID_Controller_t speed_pid;
static volatile uint8_t control_tick_pending;

void ControlLoop_HAL_Init(void)
{
    PID_Controller_Init(&speed_pid,
                        0.30f, 0.30f, 0.00f,
                        0.040f,
                        -100.0f, 100.0f,
                        100.0f);

    HAL_TIM_Base_Start_IT(&htim_control);
    HAL_TIM_PWM_Start(&htim_pwm, TIM_CHANNEL_1);
    HAL_TIM_Encoder_Start(&htim_encoder, TIM_CHANNEL_ALL);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    static uint8_t divider;

    if (htim == &htim_control)
    {
        if (++divider >= 40U)
        {
            divider = 0U;
            control_tick_pending = 1U;
        }
    }
}

void ControlLoop_HAL_Run(void)
{
    int16_t feedback;
    float output;

    if (control_tick_pending == 0U)
    {
        return;
    }

    control_tick_pending = 0U;
    feedback = Encoder_ReadDelta();

    output = PID_Controller_Update(&speed_pid,
                                   speed_pid.target,
                                   (float)feedback);
    Motor_SetSignedPwm(output);
}

在 HAL 工程中,htim_controlhtim_pwmhtim_encoder 是 CubeMX 生成的定时器句柄;MOTOR_IN1_GPIO_PortMOTOR_IN1_Pin 等方向引脚宏也需要换成你的工程名称。HAL_TIM_PeriodElapsedCallback() 只置位标志,主循环再执行 PID,可以避免在中断中调用 HAL_ADC_PollForConversion()HAL_Delay()printf() 或显示刷新。

参考代码与工程版本的差异

处理点 参考工程 教学模板
积分 ErrInt += Err0,采样周期隐含在参数中 integral += Ki * error * Ts
微分 对误差做差分 默认对测量值微分,减少目标值微分冲击
条件积分 超过阈值时直接清零积分 饱和且误差会继续加重饱和时暂停积分
积分保护 没有独立积分限幅 对积分项设置限幅
输出 OutMin / OutMax 限幅 同样限幅,并保留未限幅计算逻辑供诊断
调度 TIM1 中断内直接执行模式 Tick 定时器产生请求,主循环完成控制计算
反馈单位 编码器计数或位置差 仍需由工程师根据编码器和采样周期明确标定

这不是说参考代码“不能用”,而是要知道它的适用边界。它适合帮助入门和快速验证;当系统加入噪声、执行器饱和、采样延迟、多个控制环或功率级保护后,就需要把采样周期、单位、限幅和调度写得更明确。

ADC、编码器和 PWM 的接入检查表

把 PID 接到真实硬件前,按下面顺序检查:

  1. 先单独读取 ADC 或编码器,确认静止时反馈稳定、方向正负正确、计数不会异常跳变。
  2. 再单独输出低占空比 PWM,确认正负输出对应的电机方向正确,且 0 输出确实会停止。
  3. 固定控制周期,记录实际 T_s,不要用主循环“运行得差不多快”代替采样时基。
  4. 先令 Ki=0Kd=0,只用 P 项确认误差方向和对象响应方向正确。
  5. 再加入 I 项,观察积分项、输出限幅和解除饱和后的恢复过程。
  6. 最后加入 D 项;优先对测量值微分,并对反馈噪声进行滤波。
  7. 用串口或示波器同时记录 targetactualerroroutput 和积分项,才能判断问题来自传感器、方向、参数还是饱和。

代码练习

建议把本节变成一次可验证的练习:

  • 把参考工程的 ErrInt += Err0 改写为显式 ErrInt += Err0 * Ts,说明 Ki 的数值为什么也要重新整定;
  • 将直接对误差微分改为对测量值微分,对比目标阶跃时的输出尖峰;
  • 将“误差超过阈值就清零积分”改为“饱和方向不利时暂停积分”,观察解除饱和后的恢复差异;
  • Ts40 ms 改成 20 ms,重新检查速度反馈单位、积分作用和稳定性;
  • 在不改变 PID 核心函数的情况下,更换编码器和 ADC 反馈接口,体会算法层与硬件层的边界。

当前实验场使用一阶滤波微分,避免直接差分把高频噪声无限放大:

\[ x_D[k]=x_D[k-1]+N T_s\left(e[k]-x_D[k-1]\right) \]

\[ D[k]=K_dN\left(e[k]-x_D[k]\right) \]

其中 N = 30 rad/s 是微分滤波器参数,x_D[k] 是误差的平滑状态。误差变化很慢时,x_D[k] 会接近 e[k],D 项接近零;误差突然改变时,两者短时间内存在差异,D 项就会形成一个快速衰减的动作。

当前采样周期为:

Ts = 0.01 s

这意味着控制器每秒计算 100 次。采样周期越大,离散近似越粗;采样周期越小,计算量和噪声敏感性通常会提高。离散控制的参数不能脱离采样周期单独讨论。

为什么目标值变化会出现尖峰

假设实际输出暂时没有变化,目标值从 1.0 调到 1.1

\[ e[k-1]=1.0-y[k-1] \]

\[ e[k]=1.1-y[k] \]

y[k]y[k-1] 很接近时,误差会在一个采样点内增加约 0.1。对误差做差分后:

\[ \frac{e[k]-e[k-1]}{T_s}\approx\frac{0.1}{0.01}=10 \]

如果 Kd=0.5,理想微分项可能瞬间达到约 5。带滤波后尖峰会被压低并拉宽,但不会自动消失。

如果使用滑块连续拖动,浏览器会连续触发多个目标值变化:

1.0 -> 1.1 -> 1.2 -> 1.3 -> 1.4

每一档都会形成一次短暂的微分动作,所以图上会看到一串尖峰。观察 D 项时,应把“目标值变化造成的瞬态”和“系统输出变化造成的阻尼”分开理解。

执行器限幅与抗积分饱和

现实中的 PWM、驱动器、电机电流环或功率开关都存在输出范围。实验场用 outputLimit 表示执行器限幅:

\[ u[k]=\operatorname{sat}(u_{raw}[k]) \]

例如:

u_raw = 4
执行器上限 = 1
实际控制量 u = 1

这时控制器内部还可能继续计算更大的 u_raw。如果误差仍然朝同一方向,积分器会继续累积,形成积分饱和。

条件积分的条件

条件积分判断的是:当前已经饱和,并且继续积分会不会把饱和推得更深。

当前实时实现的判断可以写成:

\[ \begin{aligned} &u_{raw}>u_{max}\ \text{且}\ e>0 \quad\Rightarrow\quad \text{暂停积分}\\ &u_{raw}<u_{min}\ \text{且}\ e<0 \quad\Rightarrow\quad \text{暂停积分} \end{aligned} \]

其他情况下允许积分。对于对称限幅 u_min=-u_max,它的方向含义是:

状态 积分动作
正向饱和,误差为正 暂停,避免继续推向正方向
正向饱和,误差为负 允许,让积分释放并退出饱和
负向饱和,误差为负 暂停,避免继续推向负方向
负向饱和,误差为正 允许,让积分释放并退出饱和
未饱和 正常积分

条件积分的用途不是让 I 项永远停止,而是让积分只在“有帮助”的方向上工作。它通常可以减少超调、缩短解除饱和后的恢复时间。

交互控件中的四种模式可以这样区分:

  • 无抗饱和:积分照常累加,用来观察 windup;
  • 积分限幅:把积分状态限制在设定范围内;
  • 条件积分:积分会加重饱和时暂停,能帮助退出饱和时继续;
  • 反算回馈:根据 u - u_raw 主动修正积分状态。

只有在控制量真正触碰执行器限幅,或者积分状态达到积分限幅时,这些模式才会出现明显差别。公平比较时,每切换一次模式都要点击“复位”,否则旧的输出、速度、积分状态和历史曲线会影响观察。

用时域指标读懂一条曲线

调节参数时,不要只看曲线“快不快”,至少记录下面四个指标:

稳态误差

系统稳定后,目标与输出之间的差:

\[ e_{ss}=|r-y(\infty)| \]

稳态误差小,说明系统长期跟踪目标的能力较好,但不代表瞬态一定稳定。

超调量

对正阶跃目标,超过最终目标的最大部分通常写成百分比:

\[ M_p=\frac{y_{max}-r}{|r|}\times100\% \]

超调过大可能意味着阻尼不足、比例增益过高、积分作用过强或执行器饱和后积分没有释放。

上升时间

输出从目标值的低百分比区间进入高百分比区间所需的时间。它反映系统启动响应的快慢,但不同教材对区间定义可能不同,记录结果时要写清楚定义。

调节时间

输出进入并保持在目标附近某个误差带内所需的时间。实验场使用约 2% 的误差带来给出教学估计。调节时间长,通常说明振荡衰减慢或系统阻尼不足。

用 Bode 图检查频域证据

在实验场切换到“Bode 频响”,观察的是名义开环:

\[ L(s)=C(s)G(s) \]

重点看四个量。

低频增益

低频增益反映系统对慢变化目标和慢变化扰动的作用能力。加入积分后,控制器在低频附近的增益会显著提高,因此更有利于减小稳态误差。

当前页面显示 0.1 rad/s 附近的开环增益,适合作为不同 Ki 设置之间的直观比较,但它不是所有工况下的完整性能结论。

增益穿越频率

增益穿越频率 ωgc 是满足下式的频率:

\[ |L(j\omega_{gc})|=1 \]

用 dB 表示就是幅值穿过 0 dB 的位置。它通常与闭环响应速度和带宽相关,但不是“越大越好”。穿越频率越高,对采样、延迟、噪声和未建模高频动态的要求通常越高。

相位裕度

在增益穿越频率处,相位距离 -180° 还剩多少:

\[ PM=180^\circ+\angle L(j\omega_{gc}) \]

相位裕度较大,通常表示系统对额外相位延迟更有余量,时域中往往更不容易振荡;相位裕度过小,则需要警惕超调、振荡和参数变化后的失稳。

增益裕度

在相位达到 -180° 的频率处,距离 0 dB 还剩多少增益余量。它反映系统还可以承受多大的开环增益变化。

需要注意,Bode 图给出的指标基于未限幅、未加噪声的名义线性模型。即使 Bode 图显示裕度充足,执行器饱和、采样延迟、死区、摩擦和持续扰动仍可能让实时系统表现变差。因此必须把频域指标和时域波形放在一起看。

参数调节顺序

建议使用“先 P、再 I、最后 D”的教学顺序,但每一步都要观察控制输入:

  1. Ki=0Kd=0,只调 Kp,让系统先有明显且不过分的响应。
  2. 逐渐增加 Kd,观察超调、振荡和 D 分量如何变化;同时避免目标值快速拖动造成的微分冲击干扰判断。
  3. 增加 Ki,观察稳态误差下降以及积分分量逐步建立的过程。
  4. 降低执行器限幅,比较无抗饱和、积分限幅、条件积分和反算回馈。
  5. 切换到 Bode 图,记录低频增益、ωgc、相位裕度和增益裕度。
  6. 回到时域,确认超调、调节时间、稳态误差和执行器状态是否仍然满足要求。

不要在一次实验中同时改变所有参数。否则即使曲线发生变化,也很难判断变化来自哪一个机制。

实验任务:用证据而不是感觉调参

实验 A:只用比例控制

设置:

Ki = 0
Kd = 0
目标量 = 1

记录:

  • 最终输出是多少?
  • 稳态误差是否为零?
  • 提高 Kp 后,响应速度和超调怎样变化?

实验 B:加入积分

保持 Kp 不变,逐步增加 Ki。观察:

  • 稳态误差是否减小;
  • I 分量是否逐步积累;
  • 响应是否变得更容易振荡;
  • 控制量是否更容易接近执行器限幅。

实验 C:加入微分

保持 KpKi 不变,逐步增加 Kd。分别用“阶跃目标”和“手动目标”观察:

  • 系统接近目标时,D 项是否产生制动作用;
  • 快速拖动目标滑块时,控制量是否出现多个尖峰;
  • 增加传感器噪声后,D 项是否更敏感。

实验 D:观察抗积分饱和

建议设置:

目标量 = 1.5
执行器限幅 = 1
Kd = 0
Ki = 3 或 5

分别选择四种抗饱和模式。每次切换后先点击“复位”,再点击“开始”,并勾选 I 和“限幅前”曲线。重点比较:

  • 积分分量是否持续增长;
  • 未限幅控制量与实际控制量差多少;
  • 解除饱和后超调有多大;
  • 系统恢复到目标附近需要多久。

实验 E:目标值为零的状态检查

将目标量设为 0,目标信号设为“手动目标”,点击实时仿真区域的“复位”后再运行。此时如果输出仍有明显运动,应依次检查:

  • 是否存在持续扰动;
  • 是否加入了传感器噪声;
  • 是否保留了之前的输出、速度或积分状态;
  • 是否使用了正弦或方波目标模式;
  • Kd 是否因为目标突变带来微分瞬态。

目标值为零只表示最终参考值为零,不代表已有的输出和速度会瞬间消失。

前馈与复合控制:下一步的工程扩展

本课先把反馈 PID 讲清楚。后续如果已知对象模型,可以加入前馈:

\[ u(t)=u_{ff}(t)+u_{fb}(t) \]

其中 u_ff 根据目标或可测扰动提前计算,u_fb 由 PID 根据误差进行修正。对于当前的静态弹簧对象,恒定目标下可以用一个简单的静态前馈抵消弹簧力:

\[ u_{ff}=\frac{k r}{K_u} \]

反馈仍然不可省略,因为模型参数、摩擦、负载和扰动不可能完全准确。前馈负责“提前做”,反馈负责“纠偏”,这就是复合控制的基本思想。它属于 PID 基础完成后的进阶内容,不作为本课参数调节的前置条件。

本课验收标准

完成本课后,应该能够不看答案解释下面几件事:

  • 为什么 P 项响应快,但单独使用 P 可能留下稳态误差;
  • 为什么 I 项能消除稳态误差,却可能引起积分饱和;
  • 为什么 D 项可以增加阻尼,却容易放大噪声并产生微分冲击;
  • 离散积分为什么每次只增加 K_i T_s e[k]
  • 条件积分在什么情况下暂停、什么情况下恢复;
  • 为什么 ωgc、相位裕度和时域超调需要一起判断;
  • 为什么目标值为零时,系统仍可能因为已有状态、扰动或初始瞬态而运动。

视频讲解:用伯德图整定 PID 参数

下面的视频从频域角度介绍如何利用伯德图整定 PID 参数。观看时可以重点留意低频增益、增益穿越频率和相位裕度,它们与本课前面介绍的稳态误差、响应速度和稳定裕度相互对应。

进入课程实验

请先打开控制基础系列首页,完成参数实验场,然后再在本页补充推导、仿真模型和练习记录。

预备材料

  • 胡寿松《自动控制原理》:控制系统基本概念、数学模型和时域分析;
  • Åström and Murray 的 Feedback Systems
  • Brian Douglas 的 Control Systems Lectures
  • 本项目的 PID 阶跃响应实验
  • 《控制之美(卷1)》:补充传递函数、频率响应和状态空间之间的联系,在线阅读
  • 《控制之美(卷2)》:为后续学习最优控制、MPC 和卡尔曼滤波做准备,在线阅读
  • 《自动控制原理(第八版)》:作为经典控制、时域分析、频域分析和系统校正的主参考书,在线阅读
Note

第一版正文不追求一次写完。每次增加一个推导或仿真结果,都应同步补充参数、假设、指标和验证边界。