原理图:
(1)位置式PID
是1:当前系统的实际位置,与你想要达到的预期位置的偏差, 2:进行PID控制,误差会一直累加,会使当前输出与过去的所有输入相关,输入uk出错,会导致系统大幅波动 3:位置式PID在积分项达到饱和时,误差仍然会在积分作用下继续累积,一旦误差开始反向变化,系统需要一定时间从饱和区退出,所以在u(k)达到最大和最小时,要停止积分作用,并且要有积分限幅和输出限幅, 4:用位置式PID时,一般我们直接使用PD控制,不使用积分项
实际应用中,用差分代替微分,连加代替积分,也就是离散型PID
令:
(1)实现:位置模式PID
#include <math.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include "matplotlibcpp.h"
#include <vector>
#include <math.h>
#include <string>
#include<stdlib.h>
namespace plt = matplotlibcpp;
class pid_p
{
private:
float ki;
float kp;
float kd;
float ek;
float ek_1;
float actual;
float de;
float target;
float yk;
public:
pid_p();
~pid_p();
pid_p(float p,float i,float d);
void get_error();
void get_value(float act,float tar);
float update();
};
pid_p::pid_p():kp(0),ki(0),kd(0),ek(0),ek_1(0),de(0),actual(0),yk(0)
{
}
pid_p::pid_p(float p,float i,float d):ek(0),ek_1(0),de(0),actual(0),yk(0)
{
kp=p;
ki=i;
kd=d;
}
pid_p::~pid_p()
{
}
void pid_p::get_value(float act,float tar)
{
actual=act;
target=tar;
get_error();
printf("actual:%f,target%f",actual,target);
}
void pid_p::get_error()
{
ek=target-actual;
}
float pid_p::update()
{
de+=ek;
yk=kp*ek+ki*de+kd*(ek-ek_1);
printf("p:%f,i:%f,d:%f,act:%f,yk:%f,ek:%f\r\n",kp,ki,kd,actual,yk,ek);
ek_1=ek;
return yk;
}
//输入三个参数kp,ki,kd
int main(int argc,char ** argv)
{ float target=1000;
std::string str_p=argv[1];
std::string str_i=argv[2];
std::string str_d=argv[3];
// std::string str_p="0.35";
// std::string str_i="0.0001";
// std::string str_d="0.0001";
float act=0;int N=100;
float kp=atof(str_p.c_str());
float ki=atof(str_i.c_str());
float kd=atof(str_d.c_str());
pid_p a(kp,ki,kd);
std::vector<float> x,y;
for (int i=0;i<N;i++)
{
x.push_back(i);
y.push_back(act);
a.get_value(act,target);
act+=a.update();
a.pid_printf();
//if(act>target)break;
}
plt::plot(x,y);
plt::show();
}
(2)增量式PID
原理:使控制器输出为增量,尽量使每次数据均与过去数据无关,没有积分项。
公式:
实现
class pid_add
{
private:
float kp,ki,kd,ek,ek_1,ek_2,uk,yk,delta_u;
public:
pid_add();
pid_add(float p,float i,float d);
void get_value(float act,float tar);
void update_error();
float update();
};
pid_add::pid_add():kp(0),ki(0),kd(0),uk(0),ek(0),ek_1(0),yk(0)
{
};
pid_add::pid_add(float p,float i,float d):uk(0),ek(0),ek_1(0),yk(0)
{
kp=p;
ki=i;
kd=d;
};
void pid_add::get_value(float act,float tar)
{
uk=act;
yk=tar;
}
void pid_add::update_error()
{
ek_1=ek;
ek=yk-uk;
}
float pid_add::update()
{
update_error();
delta_u=kp*(ek-ek_1)+ki*ek+kd*(ek-2*ek_1+ek_2);
return delta_u;
}
(3) 积分分离式PID
原理:在系统误差较大时,取消积分环节;当误差较小时,引入积分环节。这样既不影响控制器的动态性能,又可以提高控制器的稳态精
实现:在位置式/增量式PID加入积分环节一个阈值,实现略
(4) 抗饱和积分式PID
原理:在计算U(k)的时候,先判断上一时刻的控制量U(k-1)是否已经超出了限制范围。若U(k-1)>Umax,则只累加负偏差;若U(k-1)<Umin,则只累加正偏差。从而避免控制量长时间停留在饱和区。
实现:
class pid_antisaturation
{
private:
float kp,ki,kd,uk,uk_1,yk,ek,ek_1,ek_2;
const float max_uk_1=500,min_uk_1=-500;
public:
pid_antisaturation():kp(0),ki(0),kd(0),uk(0),yk(0),ek(0),ek_1(0),ek_2(0),uk_1(0)
{};
pid_antisaturation(float p,float i,float d):kp(p),ki(i),kd(d),uk(0),yk(0),ek(0),ek_1(0),ek_2(0),uk_1(0)
{};
void get_value(float act,float target);
void update_error();
float update();
};
void pid_antisaturation::get_value(float act,float target)
{
uk_1=uk;
uk=act;
yk=target;
}
void pid_antisaturation::update_error()
{
ek_2=ek_1;
ek_1=ek;
ek=yk-uk;
}
float pid_antisaturation::update()
{
float increase;
update_error();
if((uk_1>max_uk_1)&(ek>0))
{
ek=0;
}
if((uk_1<min_uk_1)&(ek<0))
{
ek=0;
}
increase=kp*(ek-ek_1)+ki*ek+kd*(ek-2*ek_1+ek_2);
printf("p:%f,i:%f,d:%f,act:%f,yk:%f,ek:%f\r\n",kp,ki,kd,uk,yk,ek);
return increase;
}