FuARM 发表于 2014-1-3 11:07:13

增量式PID的程序和疑问

本帖最后由 FuARM 于 2014-1-3 11:07 编辑

这是本人按照公式写的一个增量式PID的程序,和测试程序,不知正确与否,恳请懂的人指出错误和建议。
想用在小车直流电机的控制上来,直流电机上有挡光片测速。MCU PWM调节转速。

按照推荐的调节PID的步骤,先将Ki=Kp*T/Ti置位0,将Kd=Kp*Td/T=0,当作纯比例控制器来调节,
因此让Ti=10000000.0(足够大就行,让Ki接近0),Td=0。此时A=Kp,B=-Kp;C=0;
增量计算(根据后面的程序改的伪代码):
        e_0 = Set - Get;//目标值-本次测量值
        delta = A*e_0 + B*e_1 + C*e_2 = Kp(e_0 - e_1) ;
        e_2 = e_1;
        e_1 = e_0;
        out += delta;
那么,按照以上推导,在纯比例控制中,增量=Kp*(前两次误差的差值);
我的疑问是:
        假设目标值是100,初始状态e_0=0,e_1=0;
        时刻1测量值是10,那么误差就是90,e_0=90,e_1=0;那么delta增量会有一个值,同时实际输出量out也有一个值
        如果时刻2测量值还是10,那么e_0=90,e_1=90; Kp(e_0 - e_1) =0;delta增量=0,此时out不增加。
        试想,当输出量out还不足以驱动电机或者电机被负载堵转时,误差恒定不变,但是delta却等于0,输出out不变,电机也不会增加力矩,这似乎有悖我们控制电机转速的初衷。

以上是我的理解,不知道正确么,恳请大家指导。

/************************************************************************/
#include <math.h>

#define Kp   (1.0)                //PID调节的比例常数
#define Ti   (10000000.0)       //PID调节的积分常数
#define Td   (0.0)            //PID调节的微分时间常数
#define T    (0.1)            //采样周期

#define AA(Kp * (1 + (T / Ti) + (Td / T)))   //A
#define BB   ((-Kp) * (1+(2 * Td / T)))      //B
#define CC   (Kp * Td / T)                  //C

//误差的阀值,小于这个数值的时候,不做PID调整,避免误差较小时频繁调节引起震荡
#define Emin 3

//调整值限幅,防止积分饱和
#define Umax 20
#define Umin -20

//输出值限幅
#define Pmax 245
#define Pmin 0


typedef struct PID
{
        int Set;//目标值
        int Get;//当前测量值

        int e_1;
        int e_2;
       
        double A;
        double B;
        double C;
       
        int delta;
        int out;
}PID_t;


void pid_init(PID_t * pPID);
void pid_ctrl(PID_t * pPID);


void pid_init(PID_t * pPID)
{
        pPID->Set = 0;
        pPID->Get = 0;
        pPID->e_1 = 0;
        pPID->e_2 = 0;
        pPID->delta = 0;
        pPID->out = 0;
       
        pPID->A=AA;
        pPID->B=BB;
        pPID->C=CC;
}


void pid_ctrl(PID_t * pPID)
{

        int e_0 = pPID->Set - pPID->Get;//求差值
       
        pPID->delta = (int)(pPID->A*e_0 + pPID->B*pPID->e_1 + pPID->C*pPID->e_2);
        pPID->e_2 = pPID->e_1;
        pPID->e_1 = e_0;
        pPID->out += pPID->delta;
}


PID_t PID_A;

void main(void)
{
        int i;
        pid_init(&PID_A);
       
        printf("Kp=%f,Ki=%f,Kd=%f\n",Kp,(Kp*T/Ti),(Kp*Td/T));
        printf("A=%f,B=%f,C=%f\n\n",AA,BB,CC);

        printf("input the set val\n");
        scanf("%d",&PID_A.Set);
        printf("Set=%d\n",PID_A.Set);


        while(1){
                scanf("%d",&PID_A.Get);
                pid_ctrl(&PID_A);
                printf("get=%8d\n",PID_A.Get);
                printf("delta=%8d, out=%8d\n",PID_A.delta,PID_A.out);
                printf("e_1=%8d, e_2=%8d\n",PID_A.e_1,PID_A.e_2);
       
        }       
        printf("leave");
}






FuARM 发表于 2014-1-3 11:17:04


发个公式,以上程序,就是按照这个公式来写的,应该是没有违背公式的。

and001 发表于 2014-1-3 14:30:55

没弄过PID,来学习

Gorgon_Meducer 发表于 2014-1-4 16:25:20

FuARM 发表于 2014-1-3 11:17
发个公式,以上程序,就是按照这个公式来写的,应该是没有违背公式的。 ...

先别管这些,先上实际系统,调试好电机驱动,旋转编码器以后再说如何转化理论公式的问题。
纸上谈兵对PID学习一点用都没有。

FuARM 发表于 2014-1-5 22:39:10

Gorgon_Meducer 发表于 2014-1-4 16:25
先别管这些,先上实际系统,调试好电机驱动,旋转编码器以后再说如何转化理论公式的问题。
纸上谈兵对PID ...

嗯,在实际板子上调试,电机速度基本调稳了,感觉效果还是很好的,速度比较稳定,施加一定的阻力给电机,速度也能稳定。
用的就是我发的这个代码的一个变形pPID->delta = pPID->Kp*((e0 - pPID->e_1)+pPID->Ki*e0+pPID->Kd*(e0-2* pPID->e_1+ pPID->e_2));
公式一样,只是把Kp,Ki,Kd分离出来调。
但是还是不明白,如果Ki=0,Kd=0;那么增量就是pPID->Kp*((e0 - pPID->e_1);电机堵转,或者速度被人为的限制死,那么前后两次的误差,应该就是相等的啊,于是增量岂不是0了?
我想应该是我的理解有问题。。。

chenbelief_007 发表于 2014-1-5 23:12:45

增量为零不代表输出为零。增量式PID的输出是所有以前的累加量,而不是当前的增量。

yuyanlzh 发表于 2014-1-6 10:08:57

学习贴,准备自己尝试一下

FuARM 发表于 2014-1-6 10:23:09

chenbelief_007 发表于 2014-1-5 23:12
增量为零不代表输出为零。增量式PID的输出是所有以前的累加量,而不是当前的增量。 ...

嗯,增量式PID的输出是所有以前的累加量,这个是对的。
但是我说的前提是,此时的转速已经被外力限定了,因此前后两次的误差之差应该是等于0,那么PID的输出就不会变(Ki=0;Kd=0),
我的理解是,此时如果PID的输出没有达到最大的话,如果电机速度没有达到目标速度,那么PID控制器理应继续增大输出量试图提高转速才对。

FuARM 发表于 2014-1-6 10:35:17

谢谢。。。。。。。

FuARM 发表于 2014-1-6 10:36:16

chenbelief_007 发表于 2014-1-5 23:12
增量为零不代表输出为零。增量式PID的输出是所有以前的累加量,而不是当前的增量。 ...

这么举个例子吧,还是看Ki,Kd都为0的纯比例的增量式。

假如PID输出量在增加的过程中,使得电机力矩和阻力恰好平衡了,而这时候还没有达到输出量的最大值,转速也没有达到目标速度,
根据公式计算,增量为0,输出量不再增加。

我感觉不合理的地方是:此时既然没有达到目标速度,而且输出量也没有达到最大,那么对于一个理想的控制器而言,应该继续增大输出才对啊,应该“竭尽全力”。

我想我的理解一定是错在哪里。恳请帮忙。

chenbelief_007 发表于 2014-1-6 14:38:00

这时需要积分项来达到你的效果,一旦存在误差,积分项会不断增大,这时的输出就会增加,直到当前误差为零。

chenbelief_007 发表于 2014-1-6 14:40:48

不应该说是积分项在增大,应该说增量不为零,输出就可以一直增加。

FuARM 发表于 2014-2-11 09:32:22

chenbelief_007 发表于 2014-1-6 14:40
不应该说是积分项在增大,应该说增量不为零,输出就可以一直增加。

关键现在看来,增量是0呀

waterx3 发表于 2014-2-24 10:20:07

反馈啊

lidreamer 发表于 2014-3-29 07:54:32

正在准备自己弄一个~

alexbird 发表于 2014-5-23 14:38:09

楼主你好,先借你的程序用一下,呵呵 ,在写毕设。

不知这个问题后来你思考的结果如何?我空想了一下,你设定的前提条件有问题,因为系统不可能凭空由一种状态突变到你所说的恒定误差状态。哪怕真的是从误差0突变到误差X,那也是一种阶跃,比例环节必然要做出响应,类似于系统启动t=0的时刻。

除非你debug然后自己改变量数值 {:biggrin:}

你觉得呢?

FuARM 发表于 2014-5-29 11:48:33

alexbird 发表于 2014-5-23 14:38
楼主你好,先借你的程序用一下,呵呵 ,在写毕设。

不知这个问题后来你思考的结果如何?我空想了一下,你 ...

我赶脚,也是这么个事儿。{:smile:}

FuARM 发表于 2014-5-29 11:48:59

alexbird 发表于 2014-5-23 14:38
楼主你好,先借你的程序用一下,呵呵 ,在写毕设。

不知这个问题后来你思考的结果如何?我空想了一下,你 ...

我赶脚,也是这么个事儿。 {:smile:}

FuARM 发表于 2014-5-29 11:51:06

FuARM 发表于 2014-5-29 11:48
我赶脚,也是这么个事儿。

但是,如果测量环节突然出错了,出现我说的恒定误差呢?

FuARM 发表于 2014-5-29 11:51:28

alexbird 发表于 2014-5-23 14:38
楼主你好,先借你的程序用一下,呵呵 ,在写毕设。

不知这个问题后来你思考的结果如何?我空想了一下,你 ...

但是,如果测量环节突然出错了,出现我说的恒定误差呢?

alexbird 发表于 2014-5-30 09:47:19

FuARM 发表于 2014-5-29 11:51
但是,如果测量环节突然出错了,出现我说的恒定误差呢?

都出错了,还谈什么呢
位置式PID出错的话,输出是错误的绝对位置,机器就直接飞了,比你这错误的后果严重多了。。。

kaderpan 发表于 2014-6-7 19:40:05

还在努力学习中

waymcu 发表于 2014-6-9 16:06:29

学习了!

sunzehua 发表于 2014-8-4 18:40:01

FuARM 发表于 2014-1-5 22:39
嗯,在实际板子上调试,电机速度基本调稳了,感觉效果还是很好的,速度比较稳定,施加一定的阻力给电机, ...

楼主感觉到了这个时候就变成类似位置式的"静态误差" 了   对PID也不怎么理解来着里求解答~

zzccyy 发表于 2014-8-26 10:22:27

最近也在学习这个,非常感谢楼主。

silence_sky 发表于 2014-12-16 16:11:02

楼主还在否?也在想找个问题。
Kp_speed*(devk-devk_1)+Ki_speed*devk
纯Kp下如果2次误差相等,输出就为0啊。

zhangyh89 发表于 2015-3-6 10:06:12

我也遇到了楼主这种问题,电机在启动前检测转速为0,pwm输出为0,如果设定一个很小的目标值,这时如果纯比例环节的话Kp(e_0 - e_1) 很小不足以使电机转动,因此下一次计算时Kp(e_0 - e_1)变为0 ,pwm输出不变,电机还是转不起来,以后电机永远转不起来了。
页: [1]
查看完整版本: 增量式PID的程序和疑问