PID

PID的疑问

我现在对PID的原理产生了很大的疑问。现在假设我有一套舵机瞄准系统,使用一个舵机,其旋转的角度范围为正负90度之间,初始为0度。然后假设在某一次任务中,给定了一个目标,然后系统就可以通过其摄像头测得目标的坐标(即一帧画面中的相对坐标),然后可以计算该目标和准星坐标(比如是画幅的正中央)的差值,然后将此差值进行计算下一次的输入,输入给舵机。这里我有很大的疑问。假设目标的位置的理想舵机角度为60度左右(当然系统是不知道这个角度的,它要通过反馈系统来一步步逼近),那么刚开始的误差通过坐标差值的计算和比例放缩到角度为60度,假设kp=1,ki=0,kd=0即只考虑比例分量,那么下一次舵机输入60度,直接转动到了正确的位置。但是,下一轮时,误差为0,那么输入给舵机的角度值为0度,又回到了原始位置,这样就会一直震荡。我的想法在哪里出错了?


你的疑问非常合理,这涉及到PID控制器的基本原理和实现方式中的一个常见误解。在你的描述中,错误的关键点在于对PID控制器输出的理解:PID输出通常不应直接作为舵机的绝对角度命令,而是作为角度增量(变化量)或控制努力,用于调整当前角度。让我一步步解释你的疑问、错误所在,以及正确的实现方式。

你的想法分析

  • 场景描述

    • 舵机初始角度:0度。
    • 目标理想角度:60度(系统未知,需通过反馈逼近)。
    • 误差计算:目标坐标与画面中心(准星)的偏差,转换为角度偏差(例如,偏差60度)。
    • PID参数:kp=1, ki=0, kd=0(纯比例控制)。
    • 你的推理:
      1. 第一轮:误差e=60度,输出u = kp * e = 1 * 60 = 60度。舵机直接转到60度(正确位置)。
      2. 第二轮:误差e=0(目标在中心),输出u = kp * 0 = 0度。舵机转到0度(初始位置)。
      3. 第三轮:误差再次出现(目标不在中心),系统开始震荡。
  • 问题核心

    • 你认为PID输出u直接对应舵机的绝对角度命令。当误差e=0时,输出u=0被解释为“转到0度”,而不是“保持当前位置”。
    • 但在纯比例控制中,输出u与误差e成正比。当e=0时,u=0表示“不需要改变”,而不是“转到绝对0度”。你的实现错误地将u视为绝对位置,而忽略了PID输出的本质是控制增量调整量

错误所在:PID输出的误解

在PID控制中,输出u(t)的含义取决于控制器的实现方式:

  • 位置式PID(绝对输出)u(t)直接输出执行器的目标位置(如绝对角度)。但在纯比例控制(P-only)中,当误差e=0时,u=0,这会导致系统将“0”解释为机械零点(如0度),而不是当前正确位置(如60度)。这会引起震荡,因为:

    • 当系统达到正确位置(e=0)时,输出u=0强制舵机返回机械零点。
    • 但机械零点(0度)不是理想位置,因此误差重新出现,系统震荡。
  • 增量式PID(速度式PID)u(t)输出角度的变化量(Δθ),而不是绝对位置。这才是正确的实现方式,尤其适用于舵机等执行器。具体来说:

    • 控制器输出u(t) = Δθ,表示角度增量。
    • 舵机的新命令角度是:θ_cmd = θ_cmd + Δθ
    • 当误差e=0时,Δθ=0,因此θ_cmd不变,舵机保持当前位置,不会震荡。

在你的例子中,错误地使用了位置式PID(输出绝对角度),而没有积分项(ki=0),导致系统无法“记住”正确位置。纯比例控制在位置式实现中,只适合设定点为0(机械零点)的场景。但这里设定点是动态的(目标在中心时舵机角度未知),因此必须避免输出绝对角度。

正确实现:增量式比例控制

让我们用你的场景(kp=1, ki=0, kd=0)和增量式实现来演示:

  • 初始化:舵机命令角度θ_cmd = 0度(当前实际角度也为0度)。
  • 第一轮
    • 测量误差:目标偏差60度,e = 60。
    • PID输出:Δθ = kp * e = 1 * 60 = 60度(变化量)。
    • 更新命令:θ_cmd = θ_cmd + Δθ = 0 + 60 = 60度
    • 舵机执行:转到60度(正确位置)。
  • 第二轮
    • 测量误差:目标在中心,e = 0。
    • PID输出:Δθ = kp * e = 1 * 0 = 0度(无变化)。
    • 更新命令:θ_cmd = 60 + 0 = 60度
    • 舵机执行:保持在60度(无移动)。
  • 结果:系统稳定在60度,无震荡。

这里的关键是**θ_cmd的累加**。当误差e=0时,输出Δθ=0表示“不改变当前位置”,而不是“转到0度”。这样,系统在达到理想位置后能保持稳定。

为什么你的想法出错了:根本原因

  1. 输出解释错误

    • 你将PID输出u直接视为舵机的绝对角度命令。这违反了PID控制的原则:输出是控制努力,用于驱动系统减少误差,而不是直接设置最终状态。在纯比例控制中,输出应与误差成正比,但执行时需作为增量。
  2. 纯比例控制的局限性

    • 在位置式PID中,纯比例控制(ki=0, kd=0)会导致稳态误差震荡,尤其在非零设定点场景。
      • 原因:当e=0时,u=0,但u=0被映射到机械零点(如0度),而非当前所需位置。
    • 增量式实现解决了这个问题,因为它隐含了“记忆”当前状态(通过累加θ_cmd)。
  3. 系统动力学忽略

    • 你的假设中,系统无延迟、无惯性(理想场景)。但现实中,摄像头采样、处理延迟或舵机响应时间可能加剧震荡。即使增益kp=1,位置式输出也会因错误解释而失败。

完整PID控制器的正确实现

为了系统稳定,推荐使用增量式PID(或位置式PID加上积分项):

  • 增量式PID公式

    • Δθ(t) = kp * [e(t) - e(t-1)] + ki * e(t) + kd * [e(t) - 2e(t-1) + e(t-2)](离散形式)。
    • 或简化:Δθ(t) = PID计算的结果(变化量)。
    • 然后:θ_cmd(t) = θ_cmd(t-1) + Δθ(t)
    • 舵机命令:发送θ_cmd(t)
  • 加入积分项(ki)

    • 即使kp=1,纯比例控制在位置式实现中仍有风险。加入积分项(ki > 0)可消除稳态误差。
      • 例如:ki=0.1, kp=1, kd=0。
      • 当e=0时,积分项维持输出u不为零(u = ki * ∫e dt),确保舵机保持在正确位置。
    • 位置式PID公式:u(t) = kp * e(t) + ki * ∫e(t)dt + kd * de(t)/dt
    • 在正确位置时,e=0,但∫e dt历史值使u ≈ 理想角度(如60度),因此命令正确。

实际建议

  1. 使用增量式输出:在舵机系统中,PID输出应作为角度增量Δθ。命令角度θ_cmd需在控制器内部累加。

    • 代码示例(伪代码):
      1
      2
      3
      4
      5
      6
      7
      theta_cmd = 0  # 初始命令角度
      while True:
      e = get_error() # 从摄像头获取误差(角度偏差)
      delta_theta = kp * e # 纯比例控制输出(增量)
      theta_cmd += delta_theta # 累加命令
      send_to_servo(theta_cmd) # 发送绝对角度给舵机
      wait(sample_time)
  2. 调整PID参数

    • kp过大可能导致超调或震荡(即使增量式),建议从较小kp开始(如0.5),逐步调参。
    • 加入小ki(如0.01)以消除残留误差。
    • 加入kd(微分项)抑制震荡(尤其在系统有延迟时)。
  3. 系统校准

    • 确保误差映射正确:画面偏差(像素)需校准为角度偏差(例如,100像素对应10度)。
    • 设定点明确:画面中心对应误差e=0,但舵机命令不一定是0度(可能为任意值)。
  4. 处理现实因素

    • 如果系统有延迟,考虑离散采样时间或预测机制。
    • 测试阶跃响应:观察是否超调,调整参数。

总结

你的想法出错点在于:将PID输出(u)直接解释为舵机的绝对角度命令,而忽略了在纯比例控制中,u=0应表示“无变化”,而非“转到0度”。正确方法是使用增量式实现,将输出作为角度变化量,并通过累加θ_cmd来生成舵机命令。这样,当误差为零时,系统保持稳定。加入积分项(ki)可进一步鲁棒化系统。PID控制原理是强大的,但实现细节至关重要——务必区分位置式和增量式输出。


PID
https://mingzaitown.github.io/2025/10/29/PID/
作者
MingZai
发布于
2025年10月30日
许可协议