目录

 
  • 前言
  • 电感排布方案
  • 舵机算法修正
    • 分段式PD算法
    • 使用函数曲线整定PD参数
  • 电机控制
    • 目标速度的确定
    • 差速控制算法
  • 特殊元素处理
    • 环岛处理
    • 防坡道误判
  • 其他辅助结构
    • 屏幕显示
    • 按键控制与拨码开关
  • 写在最后
 

前言

  随着省赛的落幕,近一年的智能车的生涯也就此画上了句号,虽然结果没有那么耀眼,但也算对得起三个大一萌新这么久以来不分昼夜所付出的心血。 在此我想对电磁车中的算法和赛场上一些的状况作进一步总结,也算是给自己一个交代。  

电感排布方案

  我们知道,电感的基本排布分为水平,竖直和八字几种方案,而电感摆放位置的不同,可能会影响车体循迹的姿态。 通过实测我们发现:水平电感有助于直道循迹,八字电感,则有赛车助于过弯,竖直电感在普通赛道上值几乎很小,只有在某些特殊元素(如环岛)才会突增。   我们采用的是水平和八字电感共同循迹的方式,即在左右两端分别放上八字和水平电感,在电磁杆的中间和靠近赛车的中心位置各放置一个水平电感,用于特殊元素判断。   另外,为了更精确的采集赛道电磁值以至于不失真,需将电磁前瞻的高度稍微降低。  

舵机算法修正

  基本的舵机控制算法在上篇文章中已经提过,这里对其部分优化方案进行说明。  

分段式PD算法

  而在PD参数中,P参数控制响应速度,D参数消除稳差,那么可以想到,如果全局使用同一个PD参数,则会出现无法控制既在直道上平稳运行,又能很好的过各种弯道的局面。这时候就需要我们对PD参数进行分段控制。   而由于电磁车只能依靠电感传回的电磁信号判断赛道路况,所以可以将左右两边的电感值差作为分段控制的依据。 具体代码如下:  
    /* * * 分段PD * * */
    if(backup<=15)    angle_P=60;
    else              angle_P=95;
  其中backup即为左右电感求得的偏差值,backup的值可以根据具体赛道及电感情况进行缩放处理。  

使用函数曲线整定PD参数

  上述的分段PD的办法是对其参数进行直接赋值,这种方法虽然可行,却不能达到很好的连续性效果,对此可以想到一种函数曲线的方法,将P的值关联backup,利用函数曲线对P值进行输出,从而更好的控制赛车在各个赛道元素上行驶的状态。   以下提供两种方案:  
    /* * * 舵机算法1 * * */
    if(backup<=15)    angle_P=63+backup*2;
    if(15<backup<22)  angle_P=33+backup*4;
    if(22<backup<=30) angle_P=backup*5.5;
 
	int k;
	angle_P=22+(int)(n*n*k);
 

电机控制

 

目标速度的确定

  上篇文章的末尾提到了目标速度的确定,由于不同路况赛车的最高速度不同,需要对不同路况分别设置目标速度,具体代码如下:  
	set_speed_L=90+(int)((30-backup)*1.2);   //弯道最低+直道加速
 

差速控制算法

  利用舵机控制赛车转向的同时,我们也可以利用差速对其进行辅助。同样根据backup的值对赛道路况进行判断,分别控制对赛车的两个轮子进行控制,达到差速的效果。程序设计上可以将上述set_speed_L计算公式中的90改为动态的bend_L/bend_R,再在具体bend_L/bend_R的计算中控制差速。   具体代码如下:  
      bend_L=(int)(90*cs_L);
      bend_R=(int)(90*cs_R);

      set_speed_L=bend_L+(int)((30-backup)*1.2);    
      set_speed_R=bend_R+(int)((30-backup)*1.2); 
  
  其中cs_L/cs_R为差速控制系数。  

特殊元素处理

 

环岛处理

  由于环岛的特殊性,可以选择在识别到环岛的同时令目标速度降低,增加其稳定性。识别到环岛后可以采用强制打角或切换曾八字电感循迹的方式进环,确定进环后再切换成普通元素循迹方式,出环前再判断一次标志位,采用编码器计步的方式强制出环,一段时间后清楚标志位。  

防坡道误判

  在调试过程中经常会看到有赛车在坡道的位置突然转向,很多情况就是因为在坡道时误判成了环岛,这就需要我们对环条件加以限制,来避免此类问题的发生。   此时加在前瞻以及车头位置的中间电感便发挥了作用,一般来说,在坡道的位置,这两个电感的值不会与两侧水平电感同时增大,所以这也就是我们解决坡道误判问题的方法之一。   部分代码如下:  
    /* * * 环岛处理 * * */ 
    if(huan_dw==1)   //拨码开关2向下 执行环岛程序
    {
      if((AD_M_Left[0]>90&&AD_M_Left[1]>90&&AD_M_Right[1]>=65&&AD_M_Right[1]<=70&&AD_M_Right[0]>=65&&AD_M_Right[0]<=70)   //环标志
         ||(AD_M_Right[0]>90&&AD_M_Right[1]>90&&AD_M_Left[1]>=65&&AD_M_Left[1]<=70&&AD_M_Left[0]>=65&&AD_M_Left[0]<70))
         //||(AD_M_Right[0]>=99&&AD_M_Right[1]>=99&&AD_M_hou<=25&&AD_M_hou>=13))
      {
        if(huanw_a==1)
        {
          huan_ru=1;
          set_speed_L=80;
          set_speed_R=80;
          huanw_a=0;
          LED_ON(3);
        }
      }
      
      if(huan_ru==1)
      {
        if((int)(AD_M_hou-AD_M_qian)==-2||(int)(AD_M_hou-AD_M_qian)==-1)
        {
          huan_ru=2;        
          set_speed_L=80;
          set_speed_R=80;
          force_angle=0;
        }
      }
    }    
      
  到此,环岛识别结束,由于不同路况以及赛车本身的不同,环岛标志位都不同,以上标志位仅供参考。另外,出环结束后一定不要忘记清标志位,否则无法二次进环。  
        if(force>huanchu_over)  
        {         
          LED_OFF(2); 
          LED_OFF(3); 
          LED_OFF(4); 
          LED_OFF(5); 
          
          huan_ru=0;
          huan_zhong=0;
          huan_chu=0;
          huanw_a=1;  
          force=0;         
        }
 

其他辅助结构

 

屏幕显示

  由于比赛现场调试时间短暂,如果用IAR里的live watch观察数据不太方便,所以我们选择将需要观察的数据显示在OLED上,尤其是由于信号发生器的不同而造成的电磁值误差,若将其获取的值打印到OLED上,则方便现场调试。 具体代码如下:  
// 电感值输出在OLED //
void AD_oled()      
{
    char  txt[30];
    
    OLED_Print(15,0,"[满天星]智能车");   //显示队伍名称
    
    sprintf((char*)txt,"L1 :%4d",AD_M_Left[0]);
    OLED_P6x8Str(0,2,(u8*)txt);
    sprintf((char*)txt,"L2 :%4d",AD_M_Left[1]);
    OLED_P6x8Str(0,3,(u8*)txt);  

    sprintf((char*)txt,"R2 :%4d",AD_M_Right[1]);
    OLED_P6x8Str(80,3,(u8*)txt);    
    sprintf((char*)txt,"R1 :%4d",AD_M_Right[0]);
    OLED_P6x8Str(80,2,(u8*)txt);

    sprintf((char*)txt,"QZ :%4d",(int)(AD_M_qian));
    OLED_P6x8Str(0,4,(u8*)txt);  
    sprintf((char*)txt,"HZ :%4d",(int)(AD_M_hou));
    OLED_P6x8Str(80,4,(u8*)txt);    
    
    sprintf((char*)txt,"P  :%4d",angle_P);
    OLED_P6x8Str(0,6,(u8*)txt);    
    sprintf((char*)txt,"b  :%4d",(int)backup);
    OLED_P6x8Str(80,6,(u8*)txt); 
    
    delayms(100);
    
}
 

按键控制与拨码开关

  两者原理基本相同,都是先读取接口状态(如电平的高低),再根据此状态对具体程序进行控制,比赛中我用了两个拨码开关,分别控制高低速和环岛屏蔽与否,并在发车时利用按键选择出入库方向,最大可能的为正式比赛提供便利。 具体代码如下:  
// 车档位选择 //
void Select_car()    //向上为0,向下为1
{    
    V_dw=GPIO_PinRead(PTC8);      //速度档,向上为低速,向下为高速
    huan_dw=GPIO_PinRead(PTC9);   //环岛档,向上屏蔽环岛程序

    char  txt[30];
    
    sprintf((char*)txt,"start_direction :%4d",start_direction_dw);   //出库方向:
    OLED_P6x8Str(0,2,(u8*)txt);                                      //1为左出库 左按键触发  2为右出库 右按键触发
    while(start_direction_dw==0)
    {
      switch(KEY_Read(0)) 
      {
        case 1:       
          break;           
        case 2:  
          start_direction_dw=1;
          break;
        case 3:      
          start_direction_dw=2;
          break;
        default:
          break;
      }
    }
    sprintf((char*)txt,"start_direction :%4d",start_direction_dw);
    OLED_P6x8Str(0,2,(u8*)txt);
    
    delayms(2000);

}
   

写在最后

  十五届到这里就结束了,怀念的同时,也感谢那些日子为梦想而星夜兼程的自己。我们行于黑暗,奉于光明。希望每位智能车人,都能摘到属于自己的那颗星星。   如有疑问或错误,欢迎和我私信交流指正。 W.By Xyq