承接上文,我们已经得到了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算法,并伴随多次调试。

本文为原创,仅做学习只用,不做任何商业用途,转载请引用链接。