查看: 3635|回复: 1
打印 上一主题 下一主题

智能车与PID控制算法

[复制链接]
跳转到指定楼层
沙发
发表于 2015-1-4 18:50:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
原本是在学校就想做一个小车,结果面临毕业也就没有心思搞了(PS:真心觉得专科的学习时间好少)。然而今年公司这边又需要一个交通沙盘,于是做小车又落到我头上来了。以前也有很多做小车的想法,这次就一起将她实现了。本来觉得做小车不难,买个玩具车改装一下就好了结果好像不是那么尽如人意。小车涉及到的循迹,定位,速度控制等都不是说一个简单的直流电机控制通断能搞定的,关键还是在于智能车的模型。这次筹备了好久总算在马老师和李老师的指导下把大体方案定下来了,最近才买了一个车模回来,本来觉得应该不难很简单的,结果那个车模零件什么的有多少有多乱就不说了,最后我就成了小朋友在哪里玩组装车,零零碎碎花了好几个小时吧总算是拼凑起来了。好吧车模有了接下来局得要开始设计硬件了,我的方案采用的STC15W4K的64引脚的51单片机,前期准备先这样做一个版本吧,后面看有需要再做STM32的。其实在确定这个方案前就一直在看小车的控制,找了很多的资料,现在思路是挺明确的,就是动起来挺烦,看了别人做的车不用黑白线循迹于是要求用霍尔传感器做循迹的探头,去买的霍尔传感器模块又没有放大,只有将传感器贴到强磁1CM左右的距离才会有响应,这就让人郁闷了,现在还在准备查查一些好点的霍尔传感器,但是查到有一种是他的输出电压是根据输入电源来的,也就是说他会将你的输入电源等分成他感应磁场的强度最低等级,然后根据磁场的强度比例输出电压,这个算是我想要的吧,但是我就在考虑这样了要不要加比较放大电路了。
在做这个以前我就看了很多资料相关的,也是受到马老师的启发吧,智能车也是需要控制算法的,去查了下用的最多的就是PID算法,得了一查资料,PID算法好吧,比例+微分+积分;看到这几个词头就大了,但是在资料堆中还是找到了一些比较好的资源讲的比较通俗,PS:好像说的废话太多了,算了不想敲了我就把PID的算法中我看得比较明白的贴出来吧。
代码先来:
// 周茗隆




typedef struct PID


{


   int SetPoint; //设定目标  Desired Value


   double Proportion; //比例常数  Proportional Const


   double Integral; //积分常数  Integral Const


   double Derivative; //微分常数  Derivative Const


   int LastError; //Error[-1]


   int PrevError; //Error[-2]


} PID;


//*****************************************************


//定义相关宏


//*****************************************************


#define P_DATA 100


#define I_DATA  0.6


#define D_DATA  1




#define HAVE_NEW_VELOCITY 0X01


//*****************************************************


//声明 PID 实体


//*****************************************************


static PID sPID;


static PID *sptr = &sPID;


//*****************************************************


//PID 参数初始化


//*****************************************************


void IncPIDInit(void)


{


   sptr->LastError = 0; //Error[-1]


   sptr->PrevError = 0; //Error[-2]


   sptr->Proportion = P_DATA; //比例常数  Proportional Const


   sptr->Integral = I_DATA; //积分常数 Integral Const


   sptr->Derivative = D_DATA; //微分常数  Derivative Const


   sptr->SetPoint =100;


    目标是 100


}




int IncPIDCalc(int NextPoint)


{


   int iError, iIncpid; //当前误差


    iError= sptr->SetPoint - NextPoint; //增量计算


iIncpid =sptr->Proportion * iError //E[k]


-sptr->Integral * sptr->LastError //E[k?项


              + sptr->Derivative *sptr->PrevError; //E[k?项


    sptr->PrevError =sptr->LastError;   //存储误差?用于下次计算


    sptr->LastError = iError;


    return(iIncpid);                          //返回增量值


}


Intg_CurrentVelocity;


Int g_Flag;


void main(void)


{


    DisableInterrupt//上电关中断,防止采样PID


    InitMCu();


    IncPIDInit();//PID的参数初始化, 以设定工作需要的P,I,D参数.


    g_CurrentVelocity=0;   //全局变量也初始化


    g_Flag=0;                //全局变量也初始化


    EnableInterrupt;//打开定时器中断,这样才能在定时器中不断的采集小车的速度.


    While(1)


    {




        if (g_Flag&HAVE_NEW_VELOCITY)//g_Flag& HAVE_NEW_VELOCITY==1,说明新的采样,


已经有了,要多新的采样速度,进行PID运算


        {


            PWMOUT+=IncPIDCalc(CurrentVelocity);//根据采样的当前速度,进行pid


,计算pwm脉冲的增量数值,然后复制给PWMOUT以调节速度


            g_Flag&=~ HAVE_NEW_VELOCITY;//设置完新的速度,将标志位复位,防止没有采


样新的速度,再次进入if,重复进行PID运算.


}


}


}


Interrrupt TIMEvoid


{


    CurrentVelocity =GetCurrentVelocity;//定时器中断,采样速度,复制给


CurrentVelocity


    g_Flag|= HAVE_NEW_VELOCITY;//每次进入中断,将标志位置一


}
很多同学都不清楚 PID 是个什么东西,因为很多不是自动化的学生。他们开口就要资料,
要程序。这是明显的学习方法不对,起码,首先,你要理解 PID 是个什么东西。
本文以通俗的理解,以小车纵向控制举例说明 PID 的一些理解。
首先,为什么要做 PID?由于外界原因,小车的实际速度有时不稳定,这是其一,要让小车
以最快的时间达达到既定的目标速度,这是其二。速度控制系统是闭环,才能满足整个系统
的稳定要求,必竟速度是系统参数之一,这是其三.
小车调速肯定不是线性的,外界因素那么多,没人能证明是线性的。如果是线性的,直接
用 P 就可以了。比如在 PWM=60%时,速度是2M/S,那么你要它3M/S,就把 PWM 提
高到90%。因为90/60=3/2,这样一来太完美了。完美是不可能的。
那么不是线性的,要怎么怎么控制 PWM 使速度达到即定的速度呢?即要快,又要准,
又要狠。(即快准狠)系统这个速度的调整过程就必须通过某个算法调整,一般 PID 就是这
个所用的算法。
可能你会想到,如果通过编码器测得现在的速度是2.0m/s,要达到2.3m/s 的速度,那
么我把 pwm 增大一点不就行了吗?是的,增大 pwm 多少呢?必须要通过算法,因为 PWM
和速度是个什么关系,对于整个系统来说,谁也不知道。要一点一点的试,加个1%,不够,
再加1%还是不够,那么第三次你还会加1%吗?很有可能就加2%了。通过 PID 三个参数
得到一个表达式: △ PWM=a *△ V1+b *△ V2+c *△ V3,a b c 是通过 PID 的那个长长的
公式展开,然后约简后的数字, △ V1 , △ V2 , △ V3 此前第一次调整后的速度差 ,第二
次调整后的速度差,第三次。。。。。一句话, PID 要使当前速度达到目标速度最快,需要建立
如何调整 pwm 和速度之间的关系。
输入输出是什么:
输入就是前次速度,前前次速度,前前前次速度。
输出就是你的 PWM 应该增加或减小多少。
为了避免教科书公式化的说明,本文用口语化和通俗的语言描
述。虽然不一定恰当,但意思差不多,就是那个事。如果要彻头彻尾
地弄 PID,建议多调试,写几个仿真程序。
PID 一般有两种:位置式 PID 和增量式 PID。在小车里一般用
增量式,为什么呢?位置式 PID 的输出与过去的所有状态有关,计算
时要对 e(每一次的控制误差)进行累加,这个计算量非常大,而明
没有必要。而且小车的 PID 控制器的输出并不是绝对数值,而是一个
△,代表增多少,减多少。换句话说,通过增量 PID 算法,每次输出
是 PWM 要增加多少或者减小多少,而不是 PWM 的实际值。
下面均以增量式 PID 说明。
这里再说一下 P、 I、 D 三个参数的作用。 P=Proportion,比例的意
思, I 是 Integral,积分, D 是 Differential 微分。
打个比方,如果现在的输出是1,目标输出是100,那么 P 的作用
是以最快的速度达到100,把 P 理解为一个系数即可;而 I 呢?大家
学过高数的, 0的积分才能是一个常数, I 就是使误差为0而起调和作
用; D 呢?大家都知道微分是求导数,导数代表切线是吧,切线的方
向就是最快到至高点的方向。这样理解,最快获得最优解,那么微分
就是加快调节过程的作用了。
公式本来需要推导的,我就不来这一套了。直接贴出来:
看看最后的结果:
△Uk=A*e(k)+B*e(k-1)+C*e(k-2)
这里 KP 是 P 的值, TD 是 D 的值, 1/Ti 是 I 的值,都是常数,哦,
还有一个 T, T 是采样周期,也是已知。 而 A B C 是由 P I D 换算
来的,按这个公式,就可以简化计算量了,因为 P I D 是常数,那
么 A B C 可以用一个宏表示。这样看来,只需要求 e(k) e(k-1)
e(k-2) 就可以知道△Uk 的值了,按照△Uk 来调节 PWM 的大小就
OK 了。 PID 三个参数的确定有很多方法,不在本文讨论范围内。
采样周期也是有据可依的,不能太大,也不能太小。
........................
........................
PID 实际编程的过程的,要注意的东西还是有几点的。 PID 这东西可以做得很深。
1 PID 的诊定。凑试法,临界比例法,经验法。
2 T 的确定,采样周期应远小于过程的扰动信号的周期,在小车程序中一般是 ms 级别。
3 目标速度何时赋值问题,如何更新新的目标速度?这个问题一般的人都乎略了。目标速度
肯定不是个恒定的,那么何时改变目标速度呢?
4 改变了目标速度,那么 e(k) e(k-1) e(k-2)怎么改变呢?是赋 0 还是要怎么变?
5 是不是 PID 要一直开着?
6 error 为多少时就可以当速度已达到目标?
7 PID 的优先级怎么处理,如果和图像采集有冲突怎么办?
8 PID 的输入是速度,输出是 PWM,按理说 PWM 产生速度,但二者不是同一个东西,有
没有问题?
9 PID 计算如何优化其速度?指针,汇编,移位?都可以试!
//*****************************************************
//定义 PID 结构体
//*****************************************************
typedef struct PID
{
int SetPoint; //设定目标 Desired Value
double Proportion; //比例常数 Proportional Const
double Integral; //积分常数 Integral Const
double Derivative; //微分常数 Derivative Const
int LastError; //Error[-1]
int PrevError; //Error[-2]
} PID;
//*****************************************************
//定义相关宏
//*****************************************************
#define P_DATA 100
#define I_DATA 0.6
#define D_DATA 1
#define HAVE_NEW_VELOCITY 0X01
//*****************************************************
//声明 PID 实体
//*****************************************************
static PID sPID;
static PID *sptr = &sPID;
//*****************************************************
//PID 参数初始化
//*****************************************************
void IncPIDInit(void)
{
sptr->LastError = 0; //Error[-1]
sptr->PrevError = 0; //Error[-2]
sptr->Proportion = P_DATA; //比例常数 Proportional Const
sptr->Integral = I_DATA; //积分常数 Integral Const
sptr->Derivative = D_DATA; //微分常数 Derivative Const
sptr->SetPoint =100; 目标是 100
}
//*****************************************************
//增量式 PID 控制设计
//*****************************************************
int IncPIDCalc(int NextPoint)
{
int iError, iIncpid; //当前误差
iError = sptr->SetPoint - NextPoint; //增量计算
iIncpid = sptr->Proportion * iError //E[k]项
- sptr->Integral * sptr->LastError //E[k- 1]项
+ sptr->Derivative * sptr->PrevError; //E[k- 2]项
sptr->PrevError = sptr->LastError; //存储误差,用于下次计算
sptr->LastError = iError;
return(iIncpid); //返回增量值
}
Int g_CurrentVelocity;
Int g_Flag;
void main(void)
{
DisableInterrupt
InitMCu();

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入因仑

本版积分规则

快速回复 返回顶部 返回列表