[success]本来是不打算参加这届的比赛的,上一届因为意外导致和比赛失之交臂。所以不打算在参加了。。但是我们实验室的几个人要我和他们组队,然后我负责算法,考虑在三我就打算参加了,毕竟大学就这一次,趁着美好的四年时光,多参加比赛还是有很多好处的。[/success]
PID算法
这个是一个控制电机的算法。下图是PID算法的一般形式:根据上图我们考虑在某个特定的时刻t,此时输入量为rin(t)
【预定值】,输出量为rout(t)
【实际值】,于是偏差就可计算为err(t)=rin(t)-rout(t)。于是PID的基本控制规律就可以表示为如下公式:
其中Kp为比例带,TI为积分时间,TD为微分时间。
当然,单片机是无法计算积分的,所以我们需要将其离散化,即积分部分我们用加和的形式来实现(这里表示为err(k)+err(k-1)+….),微分部分我们用斜率(这里表示为[err(k)-err(k-1)]/T)来表示。
我们稍微化简一下可以变成下面这个形式。
KP是比例,Ki是积分,KD是积分系数。
直接上代码把,其实我也对这个算法的具体实现也是一脸懵逼。
[highlight lanaguage=”C”]
//定义PID结构体 struct _pid{ float SetSpeed; //定义设定值 float ActualSpeed; //定义实际值 float err; //定义偏差值 float err_last; //定义上一个偏差值 float Kp,Ki,Kd; //定义比例、积分、微分系数 float voltage; //定义电压值(控制执行器的变量) float integral; //定义积分值 }pid; /*初始化结构体变量*/ /*注意:统一初始化变量,尤其是 Kp,Ki,Kd 三个参数,调试过程当中,对于要求的控制 效果,可以通过调节这三个量直接进行调节。*/ void PID_init(){ printf("PID_init begin "); pid.SetSpeed=0.0; pid.ActualSpeed=0.0; pid.err=0.0; pid.err_last=0.0; pid.voltage=0.0; pid.integral=0.0; pid.Kp=0.2; pid.Ki=0.015; pid.Kd=0.2; printf("PID_init end "); } /*编写控制程序*/ /*注意:这里用了最基本的算法实现形式,没有考虑死区问题,没有设定上下限, 只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。*/ float PID_realize(float speed){ pid.SetSpeed=speed; pid.err=pid.SetSpeed-pid.ActualSpeed; pid.integral+=pid.err; pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);//这个是这个算法的核心部分 pid.err_last=pid.err; pid.ActualSpeed=pid.voltage*1.0; return pid.ActualSpeed; } /*测试程序*/ int main(){ printf("System begin "); PID_init(); int count=0; while(count<1000) { float speed=PID_realize(200.0); printf("%f ",speed); count++; } return 0; }
[/highlight]
增量型PID算法
上面这个只是一个非常简单的算法,实际应用中我们常常需要对这个算法进行改进。比如我们想控制量的增量时(比如驱动步进电机的时候)。[highlight lanaguage=”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 ",speed); count++; } return 0; }
[/highlight]
磁场检测
赛道是由一根很长的导线组成的,我们可以通过检测磁场强度然后在通过比奥- 萨伐尔
定律就可以求出距离导线的距离。
下面是这个公式的示意图:
下面我们就要想办法实现磁场的检测。我们可以用很多方法来检测磁场,比赛是用电感来测量电场的。
双水平线检测方案
(下面这个是两个电感的分布图):单独一个电感的电动势与导航电线距离和电动势的关系图:
从图中可以看出离导航线越,电动势就越大。我们可以通过两个电感相互抵消然后通过计算合电动势就可以判断大致方向了。
电动势差值计算公式:
通过这个图我们可以发现当我们的小车刚好在跑道中央的时候,电动势为0,往左电动势减小,往右电动势增大。
电路设计