首页 > 编程语言 > 详解PID控制器原理
2021
09-22

详解PID控制器原理

一、P - Proportional 比例

想象一下一个全速行进的机器人,假设传感器上的值为1000。 现在,由于它的速度和惯性,它可能会超过一点, 当编写程序时,这可能是一个大麻烦,你想尽可能的准确。这个问题如图所示(x轴上的绿色标记代表理想距离):
在理想世界中,您告诉机器人在哪里停止,它就停止在哪里

这里写图片描述

但是,我们不是理想世界,如果我们突然告诉它停止,我们会有超调的问题,结果可能是这样的:

这里写图片描述

现在这个超调不会是一个问题,如果它的距离总是相同的。然而,有很多变量可以改变它超出的距离。 例如:

  • 电池电压。 如果电池电量不足,则电机不能快速运行,会有较少的惯性, 在这种情况下,机器人超调变小
  • 如果机器人碰到一些东西,那么超调会变小
  • 如果某些东西将机器人朝向想要行进的方向推动,则超调将会 变得更大

所以你可以看到,超调不好。 所以P控制器控制速度, 所以P控制器控制速度平稳地,让它在接近目标时减速,以缩小超调。 这就是为什么它被称为比例控制器 ? 输出速度与要更改的值成比例,我们称之为误差(error)。

它是怎么做的?

你有一个很好的变量,叫做误差(error)。这将是根据这个值对应的传感器的读书,它还在变化,就像前文提到的。例如,误差可以是剩下要走的距离,剩下的要提升的高度,需要继续加热的温度,等等。
为了计算误差,我们只需减去传感器给我们的读数,这个读书我们希望传感器在完成后立即告诉我们。

误差 =(目标值) - (传感器读数)

error = (target value) ? (sensor reading)

因此,通过给出一些示例值来说明你想要的距离和它实际走过的距离,你会看到当它接近目标时,误差会越来越小。

下面是几个示例值:

目标值 当前传感器读数 误差
1000 200 800
1000 400 600
1000 600 400
1000 800 200
1000 1000 0

我们可以用这个来控制应用程序的速度,如果你想用一个简单的P控制器,没有I和D项。为此,我们可以写:

error = (target value) ? (sensor reading);
speed = error;

另外,误差值可能不太像我们想要的那样。 这个值可能太高了,所以它超标了很多。 如果过调,它会尝试和纠正过调(误差将变成负数),所以你可以在在调试器或窗口观察值和发生的情况,你将看到误差在振荡,超调然后过度校正。

或者,误差值太小,但通常不会遇到这个问题。

对于任一问题,您可以更改误差,以保持其比例因子,但你可以将错误乘以(或除)另一个数字,一个常数。 你可以称之为任何你喜欢的名字,但它通常被称为“Kp” ? 比例组件的常数。

Kp = 0.5;
while (condition)
{
    error = (target value) ? (sensor reading);
    speed = Kp * error;
}

二、I - Integral 积分

所以代码的比例部分已经得到了,所以剩下的误差是相当小的。比例太小,不能产生很大的差异。这就是积分。积分是之前的误差的总和。所以当你的误差非常小,积分起到作用,但它实际上如何工作的呢?

积分想要得到它,使其行进足够快以缩小误差,但不要太快,因为那样可能会有超调的风险.它通过慢慢加速的方式去决定走多快,积分可以想这样计算

积分=积分+误差* 时间增量

integral = integral + error*dT

以上的设置是这样的,所以“积分”的新值等于(等号左侧)
先前的“积分”值,加上(误差*时间增量)。忽略时间增量部分,我稍后再来讨论这个问题。
积分增加的方式可以如下表中所示(使用误差为2 做例子):

Cycle # Previous value for integral Error New value for integral
0 0 2 2
1 2 2 4
2 4 2 6
3 6 2 8
4 8 2 10

粗体的数字(New value for integral积分的新值)在不断增加.

那么我们如何将它添加到现有代码中呢? 我们把它加起来。所以现在的速度是:

speed = (Kp * error) + integral

那么现在加了积分伪代码就变成这样:

Kp = 0.5;
while (condition)
{
    error = (target value) ? (sensor reading);
    integral = integral + error;
    speed = Kp*error + integral;
}

就像比例部分的代码一样,我们需要对积分加上一个常数项。我将使用0.2作为Ki的一个例子值,尽管和K一样,它只是一个数字我随便举例的。

Kp = 0.5;
Ki = 0.2;
while (condition)
{
    error = (target value) ? (sensor reading);
    integral = integral + error;
    speed = Kp*error + Ki*integral;
}      

前文告诉过你忽略时间增量,但我现在就解释一下

Integral = integral + error*dT

增量时间是必须的,因为循环不会总是花费同样的时间完成每个周期,但是当每个周期花费同样的时间时, dT可以合并到Ki中,如果每个周期不花费同样的时间,只需要将代码用于每个周期,这样你可以使用这个周期的时间作为你的增量时间

在这个教程里,我们将假设周期之间的时间总是相同的,因此dT将被并入Ki。

这里是关于积分的几个问题:

问题1:

当你的误差几近于0时,你的积分可能任然是一个可以保持速度足够高保持错误的变化的值,方程只会自己达到0,如果它超过一个等于0 的误差,那么负误差就会减去现有的积分.所以,如果速度任然很高来保持误差,我们将面临一个问题,是吧?

对于这个题,有一个非常简单的解决方案,那就是在误差达到0时重新设置积分,如下所示

Kp = 0.5;
Ki = 0.2;
while (condition)
{
    error = (target value) ? (sensor reading);
    integral = integral + error;
    if (error is 0)
    {
        integral = 0;
    }
    speed = Kp*error + Ki*integral;
}

问题2:

它被称为integral wind-up.它可以从一个大误差开始,一旦循环开始运行,积分将开始构建。所以,在这个积分需要的时候

使用时,它的值已经远远超过可用的值。有一些简单的解决办法, 我列举三个解决方案:

解决方案#1 ?限制积分所能达到的值。如果太高,为什么不给它加个限度?一个限制可以写成如下:

if (integral is greater than or equal to the maximum value)
{
    integral = maximum value;
}

但是,如果积分太大,但它是负的形式(即,使速度相反)快速地,你需要重写和上面一样的,但是为负的版本的积分。

解决方案#2 - 限制积分允许建立的范围。因此,如果错误对于积分来说太大了,我们可以禁用该范围的积分。

if ( error is greater than useful for the integral )
{
    disable the integral (set the integral to 0);
}

但同样,就像在解决方案1中一样,你需要重写相同的但要是积分的负值。或者,如果您的编程语言支持使用一个绝对值的工具,您可以使用它使代码更短,也许更简单。
如何在代码中实现绝对值的工具:

if ( abs(error) is greater than useful for the integral)
{
    disable the integral (set the integral to 0);
}

解决方案#3?限制积分允许积累的时间。这样做有点复杂,但仍然可行。

对于本教程,我们将使用解决方案#2,因为它是最简短的.
适合计算积分的范围将为+/- 40,但只是一个随机数。 下面代码的完整版(如果剩下的话会被称为“PI控制“[比例积分控制])将是这样的:

Kp = 0.5;
Ki = 0.2;
while (condition)
{
    error = (target value) ? (sensor reading);
    integral = integral + error;
    if (error = 0)
    {
        integral = 0;
    }
    if ( abs(error) > 40)
    {
        integral = 0;
    }
    speed = Kp*error + Ki*integral;
}

三、D - Derivative 导数

PID代码的最后一点——导数!导数的工作就是预测未来的误差价值,然后进行相应的速度行为。例如,如果它认为它会过调,会使它慢下来。

为了能够预测下一个误差,我们需要知道先前的误差,然后找到这两者的区别。
derivative = ( (current error) ? (previous error) ) / dT

这个公式将发现当前误差和之前的误差之间的变化,然后我们可以通过将其添加到当前误差中来预测下一个误差。就像积分一样,导数是由dT影响的,但是一个循环的周期话费的时间总是相同的,dT可以合并到Kd。

这里有一个表,展示了未来可能发生的误差的例子,通过导数计算的:

Current error Previous error Next error (error + derivative)
50 55 45
20 30 10
2 3 1
5 15 -5

在我们的代码中,导数是用上面的方程计算出来的,然后加到速度上(在这里也乘以Kd来达到缩放的目的)。

我们需要创建一个新的整数,来命名先前的误差(或者任何一个你喜欢的方式,只要它表示之前的误差值),我们让它更新自己在我们我们计算完导数后。我们可以用简单的方式让它更新,设置它的值为误差值。

一下是完整的控制PID的伪代码:

Kp = 0.5;
Ki = 0.2;
Kd = 0.1;
while (condition)
{
    error = (target value) ? (sensor reading);
    integral = integral + error;
    if (error = 0)
    {
        integral = 0;
    }
    if ( abs(error) > 40)
    {
        integral = 0;
    }
    derivative = error ? previous_error;
    previous_error = error;
    speed = Kp*error + Ki*integral + Kd*derivative;
}

四、调整常数项

这是费时又费力的工作。 有很多不同的方法来调整Kp,Ki和Kd,我会尽我所能地解释一下他们。 调整PID常数可以通过计算机程序完成,通过数学计算或通过手动调整, 我强烈建议您随时查看误差,速度等等,所以你可以看到距离到达目标还剩下多少需要改变。 使用调试器或类似的监视工具来检查结果。

首先,了解调节PID控制器的规则很重要。 当每个常数增加时有什么改变如下表所示。 常数 术语在左侧的列中,并且它们具有的效果在顶行列出。

效果如下:

  • Rise time - 从起点到目标点所需的时间
  • Overshoot - 改变的量太大了; 值比错误更大
  • Settling time - 遇到变化时需要解决的时间
  • Steady-state error - 均衡时的误差
  • Stability - 速度的“平滑度”

当每个常数增加时会发生什么?

Constant: Rise time: Overshoot: Settling Steady-state Stability: Stability:
Kp decrease increase Small change decrease degrade
Ki decrease increase increase decrease degrade
Kd minor change decrease decrease No effect Improve (if small enough)

手工调优:

手动调优是完全由你自己完成的——没有涉及到数学,但有时也会有些低效。我个人使用手动调优方法,因为我可以温和地增加每一个常量,并且知道什么时候会变得太高,而像ziegler - nichols方法这样的数学方法,你永远不会知道事情会怎样发展,直到你尝试之后。毕竟,在理论上,实践和理论是一样的,但在实践中,它们不是。我调整常量的方式如下:

1.将Kp、Ki和Kd设置为0。这将使他们暂时瘫痪。

2.增加Kp直到误差相当小,但是它仍然从开始到结束足够快。

3.增加Kd,直到任何超过你可能拥有的覆盖。但是小心Kd——太多会使它过度

4.增加Ki,直到任何仍然存在的错误被消除。从一个非常小的数字开始,不要惊讶,如果它小到0.0001甚至更小。

5.使用调整常量的规则(在上一页的表格中),您可以稍微更改一些常量,以使其工作到最佳性能。

数学方式

@todo 试过再补充

工具方式

@todo试过再补充

五、补充

while循环:

您可能已经注意到,在我的所有伪代码示例中,我将主代码放在while循环中。 这是因为误差变化时需要重新计算误差,积分,微分和速度。 例如,如果您将速度计算一次,但不再次计算,则无法重新刷新并相应地更改速度 - 它将以原始速度继续运行!

循环对PID控制器至关重要 - 不要忘记添加一个!

那么,你如何让它最终退出循环,每次当它完成了它的工作?

其中的一个常见的方法是,如果你知道它需要多长时间才能完成,你 可以将循环设置为指定的时间量(但显然比它需要的稍微多一点),以确保它确实完成了循环。

另一种方法是检测一旦误差达到零,并且已经完成。如果你选择这种方式,一定要注意确保它已经完全停止。举个例子,如果你告诉它运行循环直到误差达到0,如果有任何过度,没什么能做的,因为它将会停止(过度,错误必须通过一个点(0)。所以,你可以得到循环多长时间内误差保持在0。如果它只是在一个非常短的时间内处于0,那么很有可能它已经被过度并且需要重新调整自己。或者,如果它在一段较长时间内保持在0的值,那么说它已完全停止是安全的。

重新设置积分和之前的错误:

我前面已经讲过了,有时你需要把积分重置为0,但是最后一次需要0的值。当您开始循环时,代码会自动假设这个积分是0,之前的误差是它应该的值。但是,如果循环已经运行,那么这个积分的值和之前的错误仍然是原来的值。

这可以通过在循环开始之前设置积分值和前一个错误的值来确定。

六、总结

Proportional-你的误差,在真实值和预期值之间。

​ error = (target value) ? (sensor reading)

Integral ? 先前误差的运行和,用于在误差很小的时候进行精细的调整。

​ integral = integral + error*dT

Derivative ? 误差的变化,用来预测下一个误差可能是什么。

​ derivative = ( (current error) ? (previous error) ) / dT

The loop ? 所有的计算都需要在循环中运行-不要忘记包含它!

将三个组件放在一起,再加上一些对Kp、Ki和Kd的精确值,您将拥有一个非常一致和精确的控制器。

七、调试口诀

参数整定找最佳,从小到大顺序查,先是比例后积分,最后再把微分加,
曲线振荡很频繁,比例度盘要放大,曲线漂浮绕大湾,比例度盘往小扳,
曲线偏离回复慢,积分时间往下降,曲线波动周期长,积分时间再加长,
曲线振荡频率快,先把微分降下来,动差大来波动慢,微分时间应加长,
理想曲线两个波,前高后低4比1

八、具体方法

(1)确定比例系数Kp

确定比例系数Kp时,首先去掉PID的积分项和微分项,可以令Ti=0、Td=0,使之成为纯比例调节。输入设定为系统允许输出最大值的60%~70%,比例系数Kp由0开始逐渐增大,直至系统出现振荡;再反过来,从此时的比例系数Kp逐渐减小,直至系统振荡消失。记录此时的比例系数Kp,设定PID的比例系数Kp为当前值的60%~70%。

(2)确定积分时间常数Ti

比例系数Kp确定之后,设定一个较大的积分时间常数Ti,然后逐渐减小Ti,直至系统出现振荡,然后再反过来,逐渐增大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。

(3) 确定微分时间常数Td

微分时间常数Td一般不用设定,为0即可,此时PID调节转换为PI调节。如果需要设定,则与确定Kp的方法相同,取不振荡时其值的30%。

(4) 系统空载、带载联调

对PID参数进行微调,直到满足性能要求。

以上就是详解PID控制器原理的详细内容,更多关于PID控制器的资料请关注自学编程网其它相关文章!

编程技巧