一、背景
pid理解
前几天看了bilibili的视频,链接:更好的理解PID【通过推小车的过程】,那个人使用matlab调整pid,另外使用推小车这个假设来解释pid。我其实当时也只是听懂一些,然后后面自己又琢磨。感觉是理解了。这边记录一下。
首先回顾一下视频的内容,就是有一个小车,一开始是静止的。要让小车以5m/s的速度运动。所以要先给小车推力。让小车从v=0到v=5;一开始使用比例积分,Vtarget = PV,p是比例积分。p越大,达到5m/s目标速度越快;p越小,达到5m/s目标速度越慢。但是加上车的力一定不会正好让车达到5m/s。如果速度小于5m/s还是会继续加大力,最终会超调,然后将往回拉一些,即让v速度减小,可以预见的是也一定会超调小于5m/s。所以如果调整速度足够快,调整时间t足够小,速度一定会越发靠近目标值。但是一定是需要很多次以及不一定很好的收敛。
关于这边为什么比例调节会出现不能收敛的情况,还没想明白。
如果是这样其实。
过了几天我在看这个问题,一开始百度"pid比例积分为什么会达不到目标值",然后看了写帖子,看到这个应该是稳态误差,我之前就觉得是有专业名词的,但是这时候才想起来。
查了个帖子,pid比例积分为什么会有稳态误差:解释如下
简单的说,如果有一个水缸,有一根水管一直往里面加水,当水平面高度距离水缸顶部越来越近,这时距离目标值也越近,P的目的就在于加快水平面到水缸顶部的速度。
如果这个水缸上面有一个洞,在注水的同时,水缸在不断向外面漏水,这时由于进水的速度P是一定的,最后会产生一个这样的情况,在进水速度P不是非常大的情况下,
进水和出水的过程会达到一个动态平衡,这时水平面距离水缸顶部还有一定的距离无法弥补,这时就需要在增加一个水管I来进水,消除这个误差,这个误差就是稳态误差。
但是我还是不能明白,虽然有个洞,但是为什么一开始把水流开的足够大就行了啊。然后写上面文字的时候我突然想到也许会不会是因为p的值设置的不够大。然后负反馈周期是一定的。如果p的值比较小,就达不到目标值。(事实上这边的理解是不对的,但是这是在做个验证之后才知道的)
下面验证上面的疑问。首先编写比例积分代码,实际映射的是推小车使小车目标达到5m/s。
二、 验证比例调节疑惑
2.1 比例调节仿真
比例调节代码如下:
void pid_test()
{float p = 1.5;int t = 1; // 负反馈周期t = 1s;int target = 5; // 目标速度5m/sfloat output = 0;std::cout << "output: " << std::endl;for(int i = 0; i < 30; i++){float error = target - output;output += p * error * t;std::cout << output << std::endl;}
}
output输出情况如下:
0, 7.5, 3.75, 5.625, 4.6875, 5.15625, 4.92188, 5.03906, 4.98047, 5.00977, 4.99512, 5.00244, 4.99878, 5.00061, 4.99969, 5
.00015, 4.99992, 5.00004, 4.99998, 5.00001, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
仿真显示如下:
p值设置为1.5,比例调节情况没有出现稳态误差。
现在想模拟出出现稳态误差的情况,按照解释所述,玻璃缸有一个出水口,那么在代码中增加一个差值radis。
2.2 模拟比例调节稳态误差
代码如下
// 稳态误差
void pid_test_wentaiwucha()
{float p = 1.5;p = 1.8;int t = 1; // t = 1s;int target = 5; // 5m/sfloat output = 0;float radis = 3; // 稳态误差std::cout << "output: " << std::endl;for(int i = 0; i < 30; i++){std::cout << output << ", ";float error = target - output;output += p * error * t - radis;}
}
output输出情况如下:
0, 6, 1.2, 5.04, 1.968, 4.4256, 2.45952, 4.03238, 2.77409, 3.78073, 2.97542, 3.61966, 3.10427, 3.51659, 3.18673, 3.45061
, 3.23951, 3.40839, 3.27329, 3.38137, 3.2949, 3.36408, 3.30874, 3.35301, 3.31759, 3.34593, 3.32326, 3.34139, 3.32689, 3.
33849
仿真显示如下:
仿真出现了稳态误差。
按照我之前的理解,如果稳态误差是因为比例p的值太小了,导致出现稳态误差,只要增加p的值就行了。下面验证这个想法。
2.2.1 将比例的值增大为2。
output输出情况如下:
0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7, 0, 7,
仿真显示如下:
p的值设置为2,找值失败。没有参考价值。
2.2.2 p的值设置为1.8
output输出情况如下:
0, 6, 1.2, 5.04, 1.968, 4.4256, 2.45952, 4.03238, 2.77409, 3.78073, 2.97542, 3.61966, 3.10427, 3.51659, 3.18673, 3.45061
, 3.23951, 3.40839, 3.27329, 3.38137, 3.2949, 3.36408, 3.30874, 3.35301, 3.31759, 3.34593, 3.32326, 3.34139, 3.32689, 3.
33849
仿真结果如下
2.3 结论
出现稳态误差是因为有外界环境影响,不是增加p的值就可以解决的。计算的output值感觉是趋近于差值。导致出现稳态误差。
另外我现在对比例调节的理解又多了一些。还是拿水缸加水假设,其实入水口的水量是变化的,都是目标值与当前值的差值和比例值相乘,而随着调节过程目标值与当前值的差值逐渐变小,那么入水口的水量是变化的,或者是逐渐变小的。所以这才是比例调节的真正解释啊!
对于稳态误差,可能的原因就是因为误差值的存在,导致最终的值趋于一个稳定值,而这个值等于是将原来的5m/s的区间整体向下移动了,就出现了稳态误差。
三、pid调节
这篇文章好像有点东西:
仅使用pid中的p控制输出为什么无法达到设定输出值
根据文章的信息拷贝下述代码
// pid.cpp文件
#include "pid.h"#define Kp 1.0 // 比例系数
#define Ki 0.1 // 积分系数
#define Kd 0.01 // 微分系数float g_temp = 0;float setPoint = 50.0; // 设定
float input = 0;
float output = 0;float error = 0;
float last_error = 0;
float intergral = 0;float readTemperature()
{std::cout << g_temp << std::endl;return g_temp;
}void controlHeater(float data)
{g_temp = data;
}void loop()
{input = readTemperature();error = setPoint - input;intergral += error;float derivative = error - last_error;output += Kp * error + Ki * intergral + Kd * derivative;controlHeater(output);last_error = error;
}
// main文件
#include "pid.h"int main()
{std::cout << "output: " << std::endl;int count = 100;while (count--){loop();}return 0;
}
得到output输出信息如下
0, 55.5, 53.895, 54.0765, 53.651, 53.292, 52.9621, 52.6656, 52.3987, 52.1586, 51.9424, 51.748, 51.5729, 51.4155, 51.2737
, 51.1462, 51.0314, 50.9282, 50.8352, 50.7516, 50.6764, 50.6086, 50.5477, 50.4929, 50.4435, 50.3991, 50.3591, 50.3232, 5
0.2908, 50.2617, 50.2355, 50.2119, 50.1907, 50.1716, 50.1544, 50.139, 50.1251, 50.1125, 50.1013, 50.0911, 50.082, 50.073
8, 50.0664, 50.0598, 50.0538, 50.0484, 50.0435, 50.0392, 50.0353, 50.0317, 50.0286, 50.0257, 50.0231, 50.0208, 50.0187,
50.0168, 50.0152, 50.0136, 50.0123, 50.011, 50.0099, 50.0089, 50.0081, 50.0072, 50.0065, 50.0059, 50.0053, 50.0047, 50.0
043, 50.0038, 50.0035, 50.0031, 50.0028, 50.0025, 50.0023, 50.002, 50.0018, 50.0017, 50.0015, 50.0013, 50.0012, 50.0011,50.001, 50.0009, 50.0008, 50.0007, 50.0006, 50.0006, 50.0005, 50.0005, 50.0004, 50.0004, 50.0003, 50.0003, 50.0003, 50.
0002, 50.0002, 50.0002, 50.0002, 50.0002,
仿真如图所示
如果将语句改成这样
output += Kp * error + Ki * intergral + Kd * derivative - 20; // 增加20的损耗
output信息如下:
0, 35.5, 36.095, 37.8345, 39.0396, 40.141, 41.128, 42.0163, 42.8157, 43.535, 44.1823, 44.7648, 45.289, 45.7606, 46.1851,46.5671, 46.9108, 47.2201, 47.4984, 47.7489, 47.9743, 48.1771, 48.3596, 48.5239, 48.6717, 48.8047, 48.9243, 49.032, 49.
129, 49.2162, 49.2946, 49.3653, 49.4288, 49.486, 49.5375, 49.5838, 49.6255, 49.663, 49.6967, 49.7271, 49.7544, 49.779, 4
9.8011, 49.821, 49.8389, 49.8551, 49.8696, 49.8826, 49.8944, 49.905, 49.9145, 49.923, 49.9307, 49.9377, 49.9439, 49.9495
, 49.9546, 49.9591, 49.9632, 49.9669, 49.9702, 49.9732, 49.9759, 49.9783, 49.9805, 49.9824, 49.9842, 49.9858, 49.9872, 4
9.9885, 49.9896, 49.9907, 49.9916, 49.9924, 49.9932, 49.9939, 49.9945, 49.995, 49.9955, 49.996, 49.9964, 49.9968, 49.997
1, 49.9974, 49.9976, 49.9979, 49.9981, 49.9983, 49.9984, 49.9986, 49.9987, 49.9989, 49.999, 49.9991, 49.9992, 49.9993, 4
9.9993, 49.9994, 49.9995, 49.9995,
也是收敛的,是因为有积分的存在。仿真如下图所示: