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(纯比例控制)。
- 你的推理:
- 第一轮:误差e=60度,输出u = kp * e = 1 * 60 = 60度。舵机直接转到60度(正确位置)。
- 第二轮:误差e=0(目标在中心),输出u = kp * 0 = 0度。舵机转到0度(初始位置)。
- 第三轮:误差再次出现(目标不在中心),系统开始震荡。
问题核心:
- 你认为PID输出
u直接对应舵机的绝对角度命令。当误差e=0时,输出u=0被解释为“转到0度”,而不是“保持当前位置”。 - 但在纯比例控制中,输出
u与误差e成正比。当e=0时,u=0表示“不需要改变”,而不是“转到绝对0度”。你的实现错误地将u视为绝对位置,而忽略了PID输出的本质是控制增量或调整量。
- 你认为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度”。这样,系统在达到理想位置后能保持稳定。
为什么你的想法出错了:根本原因
输出解释错误:
- 你将PID输出
u直接视为舵机的绝对角度命令。这违反了PID控制的原则:输出是控制努力,用于驱动系统减少误差,而不是直接设置最终状态。在纯比例控制中,输出应与误差成正比,但执行时需作为增量。
- 你将PID输出
纯比例控制的局限性:
- 在位置式PID中,纯比例控制(ki=0, kd=0)会导致稳态误差或震荡,尤其在非零设定点场景。
- 原因:当e=0时,u=0,但u=0被映射到机械零点(如0度),而非当前所需位置。
- 增量式实现解决了这个问题,因为它隐含了“记忆”当前状态(通过累加
θ_cmd)。
- 在位置式PID中,纯比例控制(ki=0, kd=0)会导致稳态误差或震荡,尤其在非零设定点场景。
系统动力学忽略:
- 你的假设中,系统无延迟、无惯性(理想场景)。但现实中,摄像头采样、处理延迟或舵机响应时间可能加剧震荡。即使增益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度),因此命令正确。
- 即使kp=1,纯比例控制在位置式实现中仍有风险。加入积分项(ki > 0)可消除稳态误差。
实际建议
使用增量式输出:在舵机系统中,PID输出应作为角度增量Δθ。命令角度
θ_cmd需在控制器内部累加。- 代码示例(伪代码):
1
2
3
4
5
6
7theta_cmd = 0 # 初始命令角度
while True:
e = get_error() # 从摄像头获取误差(角度偏差)
delta_theta = kp * e # 纯比例控制输出(增量)
theta_cmd += delta_theta # 累加命令
send_to_servo(theta_cmd) # 发送绝对角度给舵机
wait(sample_time)
- 代码示例(伪代码):
调整PID参数:
- kp过大可能导致超调或震荡(即使增量式),建议从较小kp开始(如0.5),逐步调参。
- 加入小ki(如0.01)以消除残留误差。
- 加入kd(微分项)抑制震荡(尤其在系统有延迟时)。
系统校准:
- 确保误差映射正确:画面偏差(像素)需校准为角度偏差(例如,100像素对应10度)。
- 设定点明确:画面中心对应误差e=0,但舵机命令不一定是0度(可能为任意值)。
处理现实因素:
- 如果系统有延迟,考虑离散采样时间或预测机制。
- 测试阶跃响应:观察是否超调,调整参数。
总结
你的想法出错点在于:将PID输出(u)直接解释为舵机的绝对角度命令,而忽略了在纯比例控制中,u=0应表示“无变化”,而非“转到0度”。正确方法是使用增量式实现,将输出作为角度变化量,并通过累加θ_cmd来生成舵机命令。这样,当误差为零时,系统保持稳定。加入积分项(ki)可进一步鲁棒化系统。PID控制原理是强大的,但实现细节至关重要——务必区分位置式和增量式输出。