xihua13104 发表于 2015-9-19 19:23:25

基于PID的51巡线小车,PID调节完全没有效果,求指导!

本帖最后由 xihua13104 于 2015-9-19 19:31 编辑

我刚接触PID不久,查阅了不少pid算法的资料,对pid基础有大致的了解,目前想把pid算法实现在巡线小车上。最开始我只用到了P来调节,调试过程中两个电机都震荡的很厉害,完全看不出调节的效果,从理论上分析我觉得有没有什么错误,出现的问题也是很奇怪,请大家帮我看看。
程序代码如下
/*软件实现的pwm调速h文件*/
#ifndef __pwm_h__                                        //单片机晶振12MHZ,pwm频率50hz(即周期20ms)
#define __pwm_h__                                        //定时器初值计算                       
#define uint int                                               //(1/100/100)/(12/12000000)=100
#define uchar char
uint timer;
sbit left1 =P2^0;        //电机控制端
sbit left2 =P2^1;
sbit right1=P2^2;
sbit right2=P2^3;
void init_timer0()
{
       TMOD|=0x01;
        TL0= (65536-100);
       TH0= (65536-100)>>8;        
        ET0= 1;        
        EA   = 1;
        TR0= 1;
}

void PWMSET(uint pwm1,uint pwm2,uint pwm3,uint pwm4)
{       
                if(timer < pwm1)        //改变pwm1控制电机1速度
                {
                        left1=1;
                }
                else
                {
                  left1=0;
                }
                               
                if(timer < pwm2)        //改变pwm2控制电机2速度
                {
                        left2=1;
                }
                else
                {
                        left2=0;
                }               
       
                if(timer < pwm3)        //改变pwm3控制电机3速度
                {
                        right1=1;
                }
                else
                {
                        right1=0;
                }
               
               
                if(timer < pwm4)        //改变pwm4控制电机4速度
                {
                        right2=1;
                }
                else
                {
                        right2=0;
                }
        }
#endif
#include<reg52.h>
#include"math.h"
#include"pwm.h"
#include"stdio.h"
#include"intrins.h"
#define uint int
#define uchar char
uint sampledata=0;   //采样数据
uint offset;         //当前赛道位置
uint lastoffset={0};//上一次赛道位置
static int SPEED1=100;//电机pwm初值
static int SPEED2=100;
long clean1=0,clean2=1;//采样周期控制位
sbit eye5=P1^0;       //传感器1-5
sbit eye4=P1^1;
sbit eye3=P1^2;
sbit eye2=P1^3;
sbit eye1=P1^4;
void delay(unsigned char d);
void uart_init();
void sample();
void confirlocation();
void filter();
void savedata();
float LocPIDCalc(uint NextPoint);
void motorctl();
struct PID
{
    uint    SetPoint;         //设定目标 Desired Value
    uint    SumError;             //误差累计
    float   Proportion;         //比例常数 Proportional Cons
//    float   Integral;         //积分常数 Integral Const
    float   Derivative;         //微分常数 Derivative Const
    uint    LastError;               //Error[-1]
    uint    PrevError;               //Error[-2]
}   locapid={0,0,1.5,0.01,0,0} ;

void delay(unsigned char e)   //误差 0us,延时ms
{
    unsigned char a,b,c,d;
        for(d=0;d<e;d++)
        {
   for(c=1;c>0;c--)
      for(b=142;b>0;b--)
            for(a=2;a>0;a--);
        }
}

void uart_init()
{
    SCON=0x40;                                        //串口通信工作方式1
        REN=1;                                                //允许接收
        TMOD|=0x20;                                        //定时器1的工作方式2
        TH1=0xfd;TL1=0xfd;               
        TI=1;                     //这里一定要注意
        TR1=1;
}
void sample()          //数据采样函数
{
       sampledata=0;
       if(eye1==1)
           sampledata|=0x01;
        if(eye2==1)
           sampledata|=0x02;
        if(eye3==1)
           sampledata|=0x04;
        if(eye4==1)
           sampledata|=0x08;
        if(eye5==1)
           sampledata|=0x10;
}
void confirlocation()        //确定赛道位置
{
        switch(sampledata)
        {
          case 0x10:offset=4; break; //10000最左边1个传感器检测到黑线
          case 0x18:offset=3; break; //11000最左边2个传感器检测到黑线
          case 0x08:offset=2; break; //01000
          case 0x0c:offset=0; break; //01100
          case 0x04:offset=0; break; //00100
          case 0x06:offset=0;break; //00110
          case 0x02:offset=-2;break; //00010
          case 0x03:offset=-3;break; //00011
          case 0x01:offset=-4;break; //00001
          default:offset=5; break; //其余情况包括跑道丢失于检测错误
        }
}
void filter() //赛道位置数据滤波(减少某次采样错误对系统的干扰
{
          if(offset==5)//滤除错误信号,没有检测到黑线(是需要保持上一次测量值的)
          {
             offset=lastoffset;
          }
          else if(abs(offset-lastoffset)>4)
          {
             offset=lastoffset;
          }
}
void savedata()
{
      static uint TCnt2=0;
          TCnt2++;
          lastoffset=offset;
      if(TCnt2==20)   
           {      
             lastoffset=lastoffset;                
             TCnt2=0;   
           }                
}
float LocPIDCalc(uint NextPoint)                //增量式pid
{
    floatError,dError;
        struct PID *sptr;
        sptr=&locapid;
    Error = sptr->SetPoint - NextPoint;       //偏差
    sptr->SumError += Error;                  //积分
    dError = sptr->SumError -2* sptr->LastError+sptr->PrevError; //微分
    sptr->PrevError = sptr->LastError;
        sptr->LastError = Error;
        return (sptr->Proportion * Error      //比例项
             /*+sptr->Integral * sptr->SumError *///积分项
             /*+sptr->Derivative * dError*/ );    //微分项
}
void motorctrl()    //电机pid算法控制
{
   if((offset>=2&&offset<=4)||(offset>=-4&&offset<=-2))
    {
          SPEED1=(int)(SPEED1+LocPIDCalc(offset));
          if((SPEED1>=170)||(SPEED1<=30))
          { SPEED1=100; }
          SPEED2=(int)(SPEED2-LocPIDCalc(offset));
          if((SPEED2>=170)||(SPEED2<=30))
          { SPEED2=100; }
        }
        else
    {
           SPEED1=100;SPEED2=100;          
    }
        PWMSET(0,SPEED1,0,SPEED2);
        delay(5);
}
void main()
{
   init_timer0();
   uart_init();
   PWMSET(0,SPEED1,0,SPEED2);
   while(1)
   {
          sample();                //数据采样
          confirlocation();          //确认赛道信息
          filter();                          //滤波                        
          printf("SPEED1=%d,SPEED2=%d,offset=%d,LocPIDCalc=%.1f\n",SPEED1,SPEED2,offset,LocPIDCalc(offset));       
          while(clean2)
       {
          motorctrl();                    //电机控制
          clean2=0;
       }
          savedata();                  //保存赛道信息
   }
}
void timer0(void) interrupt 1                //100us
{
        TL0= (65536-100);
       TH0= (65536-100)>>8;
        timer++;
        clean1++;
        if(timer>200)//PWM周期为 2/100S=20ms
                {
                        timer=0;
                }
        if(clean1==500)   //控制采样周期,50ms
{
                clean2=1;
          clean1=0;
}   
}
小车实物连接如图



因为网上都说先只用p来调节,直到小车大致可以巡线了再加上I和D,慢慢进行微调,所以我最开始只用的P

xihua13104 发表于 2015-10-19 19:47:57

好吧,大神们都很忙。。。。。。。。

ahong2hao 发表于 2015-10-19 23:18:06

pwn才50hz,不振才怪了。
pid调试的一个技巧,把pid的所有变量打印出来,你就知道怎么变的了

xihua13104 发表于 2015-10-29 16:53:21

ahong2hao 发表于 2015-10-19 23:18
pwn才50hz,不振才怪了。
pid调试的一个技巧,把pid的所有变量打印出来,你就知道怎么变的了 ...

为什么pwm频率太低会震荡的这么厉害,频率应该多少才合适呢?这个是不是要看电机的具体参数?

ahong2hao 发表于 2015-10-29 17:13:15

你拿个低音炮就可以体验到低频的震动{:lol:},没搞过电机,不过给个1K,2K的PWM肯定是没有问题的。

xihua13104 发表于 2015-10-29 19:57:58

ahong2hao 发表于 2015-10-29 17:13
你拿个低音炮就可以体验到低频的震动,没搞过电机,不过给个1K,2K的PWM肯定是没有问题的。 ...

OK,,我去试一下,thank you
页: [1]
查看完整版本: 基于PID的51巡线小车,PID调节完全没有效果,求指导!