一、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项。为此,我们可以写:
1
2
|
error = (target value) – (sensor reading);
speed = error;
|
另外,误差值可能不太像我们想要的那样。 这个值可能太高了,所以它超标了很多。 如果过调,它会尝试和纠正过调(误差将变成负数),所以你可以在在调试器或窗口观察值和发生的情况,你将看到误差在振荡,超调然后过度校正。
或者,误差值太小,但通常不会遇到这个问题。
对于任一问题,您可以更改误差,以保持其比例因子,但你可以将错误乘以(或除)另一个数字,一个常数。 你可以称之为任何你喜欢的名字,但它通常被称为“kp” – 比例组件的常数。
1
2
3
4
5
6
|
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
那么现在加了积分伪代码就变成这样:
1
2
3
4
5
6
7
|
kp = 0.5 ;
while (condition)
{
error = (target value) – (sensor reading);
integral = integral + error;
speed = kp*error + integral;
}
|
就像比例部分的代码一样,我们需要对积分加上一个常数项。我将使用0.2作为ki的一个例子值,尽管和k一样,它只是一个数字我随便举例的。
1
2
3
4
5
6
7
8
|
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时重新设置积分,如下所示
1
2
3
4
5
6
7
8
9
10
11
12
|
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 –限制积分所能达到的值。如果太高,为什么不给它加个限度?一个限制可以写成如下:
1
2
3
4
|
if (integral is greater than or equal to the maximum value)
{
integral = maximum value;
}
|
但是,如果积分太大,但它是负的形式(即,使速度相反)快速地,你需要重写和上面一样的,但是为负的版本的积分。
解决方案#2 - 限制积分允许建立的范围。因此,如果错误对于积分来说太大了,我们可以禁用该范围的积分。
1
2
3
4
|
if ( error is greater than useful for the integral )
{
disable the integral (set the integral to 0 );
}
|
但同样,就像在解决方案1中一样,你需要重写相同的但要是积分的负值。或者,如果您的编程语言支持使用一个绝对值的工具,您可以使用它使代码更短,也许更简单。
如何在代码中实现绝对值的工具:
1
2
3
4
|
if ( abs(error) is greater than useful for the integral)
{
disable the integral (set the integral to 0 );
}
|
解决方案#3–限制积分允许积累的时间。这样做有点复杂,但仍然可行。
对于本教程,我们将使用解决方案#2,因为它是最简短的.
适合计算积分的范围将为+/- 40,但只是一个随机数。 下面代码的完整版(如果剩下的话会被称为“pi控制“[比例积分控制])将是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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的伪代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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控制器的资料请关注服务器之家其它相关文章!
原文链接:https://blog.csdn.net/xmy306538517/article/details/72771269