智能车浅谈–软件布局篇
前言
智能车省赛已经过去将近一个月,为了纪念一个多学期的努力,在此做一个小总结。
软件上的结构布局
(基于C车模说明,其他车模也可做参考)
1:x路ADC读取。(针对电感)
读出来的值,最好做个滤波处理,bz这里用的均值滤波。
参考代码(主控WCH32V307,用的龙邱商家的库):
/*电感 ADC读取初始化*/
ADC1Init(ADC1ch12_C2);
ADC1Init(ADC1ch13_C3);
ADC1Init(ADC1ch14_C4);
/*电感读取 10次数据取均值*/
ADC[0] = ADC1_Read_Average(ADC1ch12_C2,10); //左1
ADC[1] = ADC1_Read_Average(ADC1ch13_C3,10); //中间
ADC[2] = ADC1_Read_Average(ADC1ch14_C4,10); //右1
摄像头一帧数据裁剪,再通过大津法(也可是其他算法)获取阈值,最后二值化。(针对灰度摄像头,本章不细讲)
2:一路IO口作PWM输出。
用来控制舵机打脚转向。
参考代码:
/*舵机信号线IO口初始化PWM模式 频率50HZ */
ServoInit(50);
/*pwm控制 当前输出对应舵机中值*/
ServoCtrl(Servo_Center_Mid);
注:舵机中值需要自己用按键一点点控制pwm输出找到,同时找到左打脚极限和右打脚极限的值,根据这个值做好限幅处理,这样就不会使舵机转到极限位置卡齿轮从而损坏舵机。
3:倆路IO口作PWM输出,倆路IO口作高低电平输出。
用来通过电机驱动模块间接控制电机转动
注:有的电机驱动模块是需要4路IO口输出PWM进而控制电机转动。
参考代码:
/* 电机驱动的频率为10Khz */
MotorInit(10000);
/*双电机控制*/
MotorCtrl(dutyL, dutyR);
4.定时器输入捕获模式,获取编码器的一定时间内的脉冲数,进而获取当前电机转速
(这个配置一般商家给的例程会有,不需要自己配置)
参考代码:
/*编码器模式初始化*/
Encoder_Init(TIM2_ENCA_A15,TIM2_ENCB_B3);
Encoder_Init(TIM3_ENCA_B4, TIM3_ENCB_B5);
基于以上四点,软件上的基本配置就好了。布局完成后,现在谈谈让车跑起来的思路。
软件上的编程思路:
1.舵机转向环:
电感相当于车的眼睛,比如车前瞻架倆路电感,一左一右对称。通过电磁感应(赛道上有铺设电磁线)和ADC通道读取获得俩个感应电压Lv(左电感产生的感应电压),Rv(右电感产生的感应电压)。具体原理参考这里:传送门
理论上小车放在赛道正中间,Lv ≈Rv
小车水平向左移动(至右电感垂直投影在赛道正中间),Lv < Rv
小车水平向右移动(至左电感垂直投影在赛道正中间),Lv > Rv
获取的值有了这种关系,我们就可以利用俩个电感值(感应电压值)作差,得到一个偏差值Error。
Error = Lv – Rv;(当然还能用差比和,归一化等使算出来的Error值更稳定)
将这个error乘以一定比例kp加在舵机中值上,最终算出来的值就是要给到舵机的pwm。这样,小车就能正常寻迹了。(后期想要优化寻迹,可能就需要PID,模糊控制等其它算法了)
参考代码:
/*************************电磁的舵机打角PID*************************/
//差比和 分母加上中间电感产生的稳定效果相对较好
dainci_piancha = 100*(ADC[0] - ADC[2])/(ADC[0] + ADC[2] +ADC[1]);
//舵机的偏差PID
jiao= (dianci_P*dainci_piancha + dianci_D * (dainci_piancha - Last_dainci_piancha));
Last_dainci_piancha = dainci_piancha;
DUOJI_PWM=(u16)(Servo_Center_Mid+jiao);
ServoCtrl(DUOJI_PWM); // 舵机PWM输出,转向
2.电机开环
电机的速度无非是主控输出倆路PWM给到电机驱动,再驱动电机转动。 我最开始让小车跑的时候就是给一个固定的PWM,也就是全程跑一个速度,这个属于开环控制。开环控制有一定的缺陷,比如不能让小车在直道上跑相对更快的速度,速度快了弯道和各类元素可能走不好,停车也是用惯性停的。总结来说,开环跑速度上不去。
3.电机闭环(速度环)
闭环利用编码器传回来的脉冲数,计算出实时速度(bz这里是直接用的脉冲数来当做速度与自己的预期作PID)。
ECPULSE1 = Read_Encoder(TIM3); //左电机 获取脉冲值(间接的速度值)
ECPULSE2 = Read_Encoder(TIM2); //右电机 获取脉冲值
与我们的预期速度作差算出一个速度偏差error值,然后作PID。算出一个新的PWM输出到电机上。举个例子:比如实际速度小于我的预期速度,那说明小车速度没达到我的预期,那么就要基于当前速度再加速。理论上通过PID运算,最后我输出的PWM就会比当前的PWM值要大。电机速度环调的差不多的时候,赛道不同的路段跑不同的预期速度就好写了。
参考代码:
/************* 电机增量式PID控制 ***************/
S_error=(int)(speed-Current_speed); //speed为我的预期速度,Current_speed为算出来的当前速度
duty=duty+(S_error-S_error_pre)*Motor_P+S_error*Motor_I + Motor_D*(S_error - 2*S_error_pre + S_error_prepre);
S_error_prepre = S_error_pre;
S_error_pre=S_error;
Car_control_dianji(); //PWM输出duty值 作用到电机上
上面代码通过PID算出来的PWM,在输出到电机前可以做一个限幅,因为如果PID参数没有调好导致超调情况,小车电机可能会疯转,所以为了保护好自己的爱车,没调好前可以限个幅。(如下)
/************* 电机增量式PID控制 ***************/
S_error=(int)(speed-Current_speed);
duty=duty+(S_error-S_error_pre)*Motor_P+S_error*Motor_I + Motor_D*(S_error - 2*S_error_pre + S_error_prepre);
S_error_prepre = S_error_pre;
S_error_pre=S_error;
if(duty>=4500) duty=4500; //限幅值要自己设置
else if(duty<=-4500) duty=-4500;
Car_control_dianji(); //PWM输出duty值 作用到电机上
注:电机的闭环可以每个电机单独写一个闭环,也可以把获取的左右编码器的脉冲值取平均,然后只写一个闭环。(个人建议写每个电机单独写,若你是B车模当我没说doge。因为单独写好写差速)
调速度环要注意的点:
一定要让主控能够区分电机的正反转。编码器有个dir脚是用来区分正反转的,比如正转输出高电平,则反转就是低电平。在代码上写个判断即可。(由于大伙编码器的装法大概率是镜像分布在左右,若基于上述条件,如果左编码器正转为1,反转为0。则右编码器正转为0,反转为1。这个自己试试就知道了,因为对于编码器本身只识别是顺时针转还是逆时针转。若顺时针转输出1,则逆时针转肯定输出为0。)参考如下:
/*令正转数值为正数,反转数值为负数*/
/*若正转数值读出来的数值为负,则通过代码修正为正数。反之一样*/
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5) == 1) //满足说明为正转
{
if(ECPULSE1 < 0)
{
ECPULSE1 = -ECPULSE1;
}
else
{
ECPULSE1 = ECPULSE1 * 1;
}
}
else //不满足说明为反转
{
if(ECPULSE1 < 0)
{
ECPULSE1 = ECPULSE1 * 1;
}
else
{
ECPULSE1 = -ECPULSE1;
}
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3) == 1)//满足说明为反转
{
if(ECPULSE2 < 0)
{
ECPULSE2 = ECPULSE2 * 1;
}
else
{
ECPULSE2 = -ECPULSE2;
}
}
else//不满足说明为正转
{
if(ECPULSE2 < 0)
{
ECPULSE2 = -ECPULSE2;
}
else
{
ECPULSE2 = ECPULSE2 * 1;
}
}
4.电机闭环(方向环)
如果说电机速度环是为了控速,那么电机方向环就是用来辅助小车转向。你可以想想F车(三轮)是怎么寻迹的,没有舵机的F车只能通过电机差速(俩个轮子转速不同)来转向。但有舵机的C车模同样可以过弯道利用差速辅助舵机,从而使小车弯道速度更快通过。(弯道快才是真的快,谁直道不会加速?(doge))。如何写方向环这里就提供一个思路,因为是弯道差速,可以参考舵机转向的PID。
最后给各位第一次准备智能车的软件手一些建议:
1.电机速度环和电机方向环不能重叠,代码上要理清楚
2.PID控制最好写在定时器里
3.能写结构体就写结构体,不然后面变量多的怀疑当初是不是自己加的。
4.模块化编程不用多说了吧,然后就是写注释。
如果想了解小车的硬件布局,可以参考这篇文章–智能车浅谈——硬件篇
以上就是bz对软件上的思路和布局,希望能对您有所帮助,如果有写错的地方望指正。