戳戳戳 发表于 2017-8-5 19:30:33

PID详细总结

经过很久的战争,终于PID被我拿下,下面我将就增量式PID介绍,位置式的就是公式不一样,套一下就可以了:lol
PID控制算法的C语言,增量型PID的C语言实现
这里直接给出代码了。
#include<stdio.h>
#include<stdlib.h>

struct _pid{
    float SetSpeed;            //定义设定值
    float ActualSpeed;      //定义实际值
    float err;                //定义偏差值
    float err_next;            //定义上一个偏差值
    float err_last;            //定义最上前的偏差值
    float Kp,Ki,Kd;            //定义比例、积分、微分系数
}pid;

void PID_init(){
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.err_next=0.0;
    pid.Kp=0.2;
    pid.Ki=0.015;
    pid.Kd=0.2;
}

float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    float incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
    pid.ActualSpeed+=incrementSpeed;
    pid.err_last=pid.err_next;
    pid.err_next=pid.err;
    return pid.ActualSpeed;
}

int main(){
    PID_init();
    int count=0;
    while(count<1000)
    {
      float speed=PID_realize(200.0);
      printf("%f\n",speed);
      count++;
    }
    return 0;
}

这个只是c语言程序,还要修改一下成为单片机的,但是我们可以参考她的公式及思路,




下面是我窜眼很久写出来的pid代码,虽然不好,但大致可以实现控制电机,加了限幅滤波算法
废话不多说,直接上代码,不足之处望大佬指正

采用298驱动的,板子型号是Arduino UNO,以下是接线(为了省事,298的IN1和IN2我没有通过程序给出高低电平,直接接在arduino UNO的5v和gnd,)
/*
        IN1-----+5V;
        IN2------GND;
        ENA-----D9;
        光耦测速模块D0-----2;
*/

#include <MsTimer2.h>   
int pwm = 9;
float kp = 8,ki = 4,kd = 2;
float e,e1,e2;
int cnt;//存储电机触发的脉冲次数
int m;//记录进入定时器中断的次数
int v;//存储电机500ms内触发的脉冲次数
int uk,uk1,duk,out=20;
int SpeedSet = 200;
int v1;//用于滤波记录当前值

void setup()
{
Serial.begin(9600);
pinMode(9,OUTPUT);
pinMode(2,INPUT);
attachInterrupt(0, blink, CHANGE);
MsTimer2::set(2, flash);
MsTimer2::start();         //开始计时
}

void PIDControl()      
{
e=SpeedSet-v;   
duk=(kp*(e-e1)+ki*e+kd*(e-2*e1+e2))/50;    //增量式PID输出   
uk=uk1+duk;   
out=(int)uk;
if(out>1000)
{
    out=1000;
}
else if(out<0)
{
    out=0;
}
uk1=uk;         
e2=e1;
e1=e;         
}

// 限幅滤波法(又称程序判断滤波法)
int Filter()
{
int NewV;
NewV = v;
if(abs(NewV - v) > 10)
    return v;
else
    return NewV;
}

void loop()
{
v1 = Filter();
v = v1;
Serial.println(v1);
analogWrite(pwm,out);
}

void blink()
{
    cnt++;
}

void flash()
{
    m++;
    if(m>100)
    {
      v = 2.5*cnt;//计算500ms内的脉冲数
      PIDControl();   
      cnt = 0;
      m = 0;
      }
}

电机通过L298N驱动,这里为了省事,298的IN1和IN2我没有通过程序给出高低电平,直接接在arduino UNO的5v和gnd,298的ENA接在D9脚,才用光藕计脉冲,接在2脚

把程序上传到你的板子上,通过串口监视器就可以看到变化啦,当然pid参数需要根据你的实际情况调整。

希望能帮到大家!

zjz5717 发表于 2017-8-5 22:30:23

那个MsTimer2的库,能不能打个包发一下

戳戳戳 发表于 2017-8-6 13:00:55

这个是Arduino UNO 的定时器库文件,稍候上传Arduino 2560的库文件

戳戳戳 发表于 2017-8-6 22:13:51

zjz5717 发表于 2017-8-5 22:30
那个MsTimer2的库,能不能打个包发一下

这个是Arduino 2560板子适用的定时器的库,UNO的已经上传了

leisd 发表于 2017-8-7 08:13:26

收藏了,谢谢分享!

戳戳戳 发表于 2017-8-8 09:50:40

leisd 发表于 2017-8-7 08:13
收藏了,谢谢分享!

哈哈,第一次发帖,感谢支持

Damn_intuition 发表于 2017-8-9 18:31:54

PID对新人来说还是挺复杂的,而且在很多地方都有用到,继续加油!

ps:我记得有标准PID库,可以看一下。

戳戳戳 发表于 2017-8-10 00:32:58

Damn_intuition 发表于 2017-8-9 18:31
PID对新人来说还是挺复杂的,而且在很多地方都有用到,继续加油!

ps:我记得有标准PID库,可以看一下。

库我也使用过,可能会我还没完全弄会把,测出来的实际转速和设定值总是误差很大

BeTe 发表于 2017-8-16 09:24:45

多谢分享哦

neebourne 发表于 2017-8-16 18:57:50

nt Filter()
{
int NewV;
NewV = v;
if(abs(NewV - v) > 10)
    return v;
else
    return NewV;
这里有点问题把,一直是返回 NewV;


还有 void flash()
{
    m++;
    if(m>100)
    {
      v = 2.5*cnt;//计算500ms内的脉冲数
      PIDControl();   
      cnt = 0;
      m = 0;
      }
}
应该是200毫秒吧

戳戳戳 发表于 2017-8-18 17:31:11

neebourne 发表于 2017-8-16 18:57
nt Filter()
{
int NewV;


随着程序的不断改进,注释还是一开始的,也懒得改了

戳戳戳 发表于 2017-8-23 10:53:10

neebourne 发表于 2017-8-16 18:57
nt Filter()
{
int NewV;


测试过程中,程序一直在修改,那些注释还是一开始的,后面改好了,也就没有改过来了,比较懒:lol

yangjiaqing 发表于 2017-8-28 17:00:03

亲,我也是被pid困扰好久好久的人了。有个问题想请教,
 就是你在pid控制电机是选用带编码器的吗?能发个图片看看吗?

hebjean 发表于 2017-9-2 16:25:00

问问。这个2560定时库文件该怎么使用呢?我 的想法是升温到设定温度在恒温。到设定的时间后循环升温恒温。

neebourne 发表于 2017-9-7 12:54:31

戳戳戳 发表于 2017-8-23 10:53
测试过程中,程序一直在修改,那些注释还是一开始的,后面改好了,也就没有改过来了,比较懒

觉得你已经很厉害啦,我搞啦几天pid又放下啦
页: [1]
查看完整版本: PID详细总结