我们现在假设一个实例:要求让小车从垂直于一条直线的位置开始,控制他沿着这条直线前进。
技术难点:控制轮子的电机不同,无法从硬件方面保证他们前进距离相同
相信大家对于这个问题都有思路,就是控制小车开到线上,发现车偏左了就往右边开,反之向左。
但是我们在实际操作的时候就会发现,由于车具有惯性,我们很容易就冲过了线,然后往回开,又过了,再回头。。。。
把轨迹画出来,我们会发现这个轨迹就是自动控制原理里系统的单位阶跃响应的时域曲线,而我们在此刻的曲线,在稳态值附近剧烈波动,显然这不是我们想要的
所以PID应运而生:
P 比例
我们先来看第一个问题,我们容易冲过线的问题一部分是惯性,但也是因为我们太莽了,有经验的人都知道,在接近线的时候就开始刹车减速,这样即便超过线,那幅度也大大减小,所以:
P:比例环节,相当与是一个放大倍数可调的放大器,这里放大的是输入,也就是现在和目标之间的差
由于放大倍数和增益成反比的关系,所以
比例越大,放大倍数越小,曲线震荡幅度越小,越平稳
比例越小,放大倍数越大,曲线震荡幅度越大,越波动
说白了,就是在靠近线的时候开始慢慢靠过去,离得远的时候加速冲
D 微分
上述的靠近则减速的方法,虽然优化了我们的裸控,但是显然还是打不到要求的,因为最终还是会在靠近稳态值的附近波动,理论上是可以到达稳态,但我们不知道要等到什么时候,
所以我们懒得等了
D最好的比喻就是想象一个弹簧挂重物,我们把它从某个正常的地方放开重物,显然,这个弹簧会上下晃悠,如果我们把它放在水里,可以想象到,他稳定下来的速度会比在空气里快的多
这里就表达了,D扮演了一个“阻尼”的作用,在小车模型里,就是摩擦力
D的作用在于减小反映时间,在远的时候加速,在靠近的时候减速
在PD的时候,如果参数设的非常好,就可以完成我们的任务了,但有一种情况不可能靠调参完成目标,就是在未到达目标但很靠近的时候,P已经起不到很大作用了,D因为曲线平稳也罢工了,那我们岂不是永远到不了终点?
I 积分
I的积分对象还是那个目标与现实的差
随着时间的过去,如果出现了还没到终点就止步的情况,I就会因为积分值还在上升而发现问题,然后控制曲线继续上升
所以I就是为了减小静态误差,让受控对象尽可能靠近目标值,再到达目的之后积分停止
积分时间太长,会导致曲线偏离,并且很缓慢的靠近目标
积分时间太短,会导致曲线震荡幅度大
这里再给出一个PID位置控制的算法
#include <bits/stdc++.h>
3 using namespace std;
4
5 struct PID{
6 double init;
7 double out;
8 double err;
9 double kp,ki,kd;
10 double lerr;
11 double In;
12 double Vd;
13 }pid;
14
15 void chushi()
16 {
17 cout<<"begin"<<endl;
18 pid.init = 0;
19 pid.out = 0;
20 pid.err = 0;
21 pid.lerr = 0;
22 pid.In = 0;
23 pid.Vd = 0;
24 pid.kp = 0;
25 pid.ki = 0;
26 pid.kd = 0;
27 }
28
29 void PID_P(double last)
30 {
31 pid.init = last;
32 pid.err = pid.init - pid.out;
33 pid.In += pid.err;
34 pid.Vd = pid.kp * pid.err +pid.ki * pid.In + pid.kd * (pid.err - pid.lerr);
35 pid.lerr = pid.err;
36 pid.out = pid.Vd * 1.0;
37 cout<< pid.out<<endl;;
38 }
39
40 int main()
41 {
42 chushi();
43 int last;
44 cout<<"wentaizhi : ";
45 cin>>last;
46 cout<<"kp: ";
47 cin>>pid.kp;
48 cout<<"ki: ";
49 cin>>pid.ki;
50 cout<<"kd :";
51 cin >>pid.kd;
52 for(int i=0 ; i<1000; i++)
53 {
54 cout<<i<<" ";
55 PID_P(last);
56 }
57 return 0;
58 }
一个自己输入P I D以及稳态值的代码,自己写出来画出图来看看
博客到这里吧,以后再补充