承接上文,我们已经得到了pid的连续型公式:
时间内的矩形面积相加,即为时间内的两点斜率,为,用迭代次数模拟时间。这就是pid离散化的位置式:
可以得到增量式:
代码如下:
#include #include struct pid_para{ float target; // 目标值 float current;// 测量值 float error; // 误差 float last_error; // 上一次的误差 float last_lerror;// 前一次的误差 //float integral; // 积分 float kp,ki,kd; float out;};pid_para pid;float pid_increment(float target){ pid.target = target; pid.error = pid.target - pid.current; //pid.integral += pid.error; float increment = pid.kp * (pid.error - pid.last_error) + (pid.ki * pid.error) + (pid.kd * (pid.error - 2*pid.last_error + pid.last_lerror)); pid.out += increment; pid.last_lerror = pid.last_error; pid.last_error = pid.error; return pid.out;}void pid_init(){ pid.target = 0; pid.current = 60; pid.error = 0; pid.last_error = 0; pid.out = 0; pid.kp = 0.8; pid.ki = 0.2; pid.kd = 0.4; printf("kp = %.2f , ki = %.2f , kd = %.2f\n", pid.kp,pid.ki,pid.kd);}int main(void){ int i = 0; float u = 0; float StaEr = rand()%10-5; pid_init(); for(i = 0; i < 100; i++){ u = pid_increment(100); pid.current = pid.current + u - StaEr;// 调用pid算法,并输入目标值printf("%f \n",pid.current); } system("pause"); return 0;}
其他改进思路
在工程中实际情况是十分复杂的,只用经典pid可能也有不足。例如我们要控制一个四轴无人机从30米飞到100米,以高度误差作为e(t)。pid输出的电机数据可能是从2000r/s到8000r/s(随便编的数字)。但是电机的最大转速就是5000r/s,达不到pid输出的要求,那他只能以最大转速运转。这导致e的函数图像不能快速下降,积分部累计了非常多。这样即使e(t)很小时,比例部分对电机输出作用很小,积分部却成为主要矛盾,让电机仍然保持很大的转速,引起严重的超调。这就是积分饱和现象。我们无法改变比例项和微分项,就只能从积分项入手,用积分分离的思路解决。给系统设置一个最大值,若pid输出超过最大值,就令ki=0,不累加积分,等到pid输出可以接受了,再累加积分。
if(pid.error > 50){ flag = 0; } else{ pid.integral += pid.error; } pid.out = (pid.kp * pid.error) + flag * (pid.ki * pid.integral) + (pid.kd * (pid.error - pid.last_error));
当然我们也可以依照情况赋给ki不同的值而不只是0,这就是变积分的思想。
一个pid不够,还可以引入双环pid,以外环的输出作为内环的输入。工程模型会变得很复杂,这里不做详细说明了。
结语
pid入门学习到此就结束了,真正将pid应用于工程,需要我们弄清整个工程,建立完整的控制模型,选择合适的pid算法,并伴随多次调试。
本文为原创,仅做学习只用,不做任何商业用途,转载请引用链接。