NXP智能车竞赛笔记(室外电磁组)

189
0
2020年11月3日 08时53分

一、概要

比赛规则与赛道:
比赛没有赛道,只有电磁线。但赛道元素包括有直道、弯道、坡道、十字路口以及横断路障。室外电磁组原则上选择室外的马路、草坪、体育场组织比赛,场地内可能会存在高度不大于 2 厘米的硬质路坎、沙坑、深度不超过 2 厘米的水坑等。
选手制作的车模完成赛道运行一周。比赛时间从车模冲过起跑线到重新回到起跑线为止。

越野车外形

 

1

 

车的功能与实现:

 

2

 

本文脉络

 

3

 

二、硬件部分:

1.主控板

(1)原理图:

K60引脚分配:
引脚通过查K60引脚功能来分配,除去特殊功能引脚外,其余引脚为普通I/O口
特殊引脚:
I2C通信:SDA,SCL
FTM(PWM输出):PWMA,DPWM
AD(模数转换):AD1~AD6
串口通信:TX2,RX2

 

4

 

电源部分及笔记:(7~8v输入,5v和3.3v输出)

 

5

 

以下为原理笔记:

 

6

 

7

 

按键模块:(SW2为4并排按键,用于设置车的模式)

 

8

 

9

 

电机输出(为排针插座,接驱动板,DIRA为电机方向控制,PWMA为速度控制):

 

10

 

舵机输出(为排针插座,5v供电电压,DPWM为脉冲输出,以控制舵机转角):

 

12

 

AD输入(为排针插座,接传感器板,6路电感值输入):

 

13

 

普通IO口:

 

14

 

蜂鸣器:

 

15

 

(2)PCB和3D图
PCB中,logo部分和敷铜(GND)已去除,以更清晰显示电路

 

16

 

17

 

2.电机驱动板:用于驱动电机

(1)原理图

H桥电机驱动:
VCC直接接电池(7~8V)

 

18

 

H桥原理:

 

19

 

12v升压(用于IR2104s驱动mos管):

 

20

 

电源部分:
VCC是电机供电

 

21

 

12V(用于IR2104s驱动MOS)和3.3v(逻辑芯片供电):

 

22

 

23

 

调速及转向:
Led显示电机方向及速度大小(速度越大越亮):

 

24

 

排针插座信号输入,3.3v用于逻辑芯片供电:

 

25

 

逻辑非和逻辑与芯片:

 

26

 

27

 

原理笔记:

 

28

 

(2)PCB(GND敷铜已去)和3D图

 

29

 

30

 

3.传感器板:处理电感数据

(1)原理图

电源部分:
在这里插入图片描述
信号放大:(opa2350为运放,根据电阻,放大倍数最大50倍,输入输出为正弦波,共6路)

 

31

 

信号整流(经实验,连接AD输出和GND的电容会严重削弱信号,故去除):

 

32

 

信号输入输出(为插座):

 

33

 

(2)PCB(GND敷铜已去除)和3D图

 

34

 

 

35

 

4.电磁探测板:探测电磁线信号

(1)原理图

谐振部分:谐振可以很好放大信号,电感电容值根据20K频率谐振计算。

 

36

 

其他部分(LED和排针插座):

 

37

 

 

2)PCB(GND敷铜已去除)和3D图

 

在这里插入图片描述
在这里插入图片描述

电感与电磁线垂直时信号最强,故六路电感位置摆放:

在这里插入图片描述

这样放置电感,可以满足在多方位的检测需求。

 

三、软件部分

智能车主要用到的几样硬件需要软件驱动,包括电机,舵机,编码器,ADC采集和超声波模块;还有一些调车的辅助模块,如OLED,蓝牙,串口,这些部分都采用了相应的例程做了需要的修改。

1.电机和舵机

(1).电机
电机部分需要主控芯片输出PWM波控制电机转速,编写了电机初始化及控制函数,包括了电机正反转的设置。

 

void Motor_Init()    
{
	GPIO_Init(PORTA,7,1,1);//GPIO初始化
	FTM_PWM_Init(FTM0, FTM_CH2, 0,0);//PWM初始化
	Motor_Control(0,0);
}

void Motor_Control(int16 PWM,int DIR)//PWM为速度参数,DIR为正反转
{
  if(DIR==0)
  {
    GPIO_Ctrl(PORTA,7,0);
  }
  else
  {
    GPIO_Ctrl(PORTA,7,1);
  }
  FTM_PWM_Duty(FTM0,FTM_CH2,PWM);//PWM最大1000
}

 

 

(2).舵机

舵机正常工作需要的50hz的PWM波来控制转角,通过控制占空比在5%到15%之间,可以实现舵机180度范围的旋转。在前期的调试过程中确定了舵机状态处于中点的占空比值,在这一值的基础上直接加减实现舵机控制左右转。

测试代码:使轮子到达两个最大转向角

 

     FTM_PWM_Duty(FTM2,FTM_CH0,730);
     time_delay_ms(2000);
     FTM_PWM_Duty(FTM2,FTM_CH0,835);
     time_delay_ms(2000);

 

 

2、传感器(电感,编码器和超声波)

越野组采用电感电容谐振来感应电磁引导线的磁场信号,信号经过谐振放大以及运放放大后通过芯片的ADC模块采集。由于采集的信号可能存在较大波动,采用了均值滤波的方式,选取四次采样平均值作为实际使用的数据。考虑到车前的三组电感相对电磁线的位置有区别,为了使收集到的数据尽量趋于线性变化,对数据做了线性化处理,根据三组电感的位置乘上了参数用作矫正。

 

void ADC_6()
{
  int i;
  volt[0]=ADC_Ave(ADC1,ADC1_SE14,ADC_16bit,4);//均值滤波
  volt[1]=ADC_Ave(ADC1,ADC1_SE15,ADC_16bit,4);
  volt[2]=ADC_Ave(ADC1,ADC1_SE12,ADC_16bit,4);
  volt[3]=ADC_Ave(ADC1,ADC1_SE13,ADC_16bit,4);
  volt[4]=ADC_Ave(ADC1,ADC1_SE10,ADC_16bit,4);
  volt[5]=ADC_Ave(ADC1,ADC1_SE11,ADC_16bit,4);
  sprintf(txt,"0%06d",volt[0]);
  LCD_P6x8Str(5,1,(uint8 *)txt); //LCD屏显示
  sprintf(txt,"1%06d",volt[1]);
  LCD_P6x8Str(5,2,(uint8 *)txt);
  sprintf(txt,"4%06d",volt[4]);
  LCD_P6x8Str(5,3,(uint8 *)txt);
  sprintf(txt,"5%06d",volt[5]);
  LCD_P6x8Str(5,4,(uint8 *)txt);
  sprintf(txt,"2%06d",volt[2]);
  LCD_P6x8Str(54,1,(uint8 *)txt);
  sprintf(txt,"3%06d",volt[3]);
  LCD_P6x8Str(54,2,(uint8 *)txt);
}

 

 

均值和终值滤波代码:

 

u16 ADC_Ave(ADCn_e adc_n,ADCn_Ch_e adc_ch,ADC_nbit bit,u16 N) //均值滤波
{
    u32 tmp = 0;
    u8  i;
    for(i = 0; i < N; i++)
       tmp += ADC_Mid(adc_n,adc_ch,bit); 
    tmp = tmp / N; 
    return (u16)tmp;
}
u16 ADC_Mid(ADCn_e adc_n,ADCn_Ch_e adc_ch,ADC_nbit bit) //中值滤波
{
    u16 i,j,k,tmp;
    //1.取3次A/D转换结果
    i = ADC_Once(adc_n,adc_ch,bit); 
    j = ADC_Once(adc_n,adc_ch,bit); 
    k = ADC_Once(adc_n,adc_ch,bit); 
        //2.取中值
        if (i > j)
        {
          tmp = i; i = j; j = tmp;
         }
         if (k > j) 
           tmp = j;
         else if(k > i) 
           tmp = k; 
         else 
          tmp = i;
     return tmp;
}

 

 

(2)编码器

实物图

 

38

 

AB相增量式编码器原理:

 

39

 

40

 

发光二极管发射的光通过光栅到达光敏管,引起电平变化。如果正转,A相输出超前B相90度,如果反转A相滞后B相90度。
AB相正交解码原理:

 

41

 

CNT寄存器:寄存器通过正解解码的得到的脉冲值,最后只要读取CNT的值就可以得到编码器转数(正值为正转,负值为反转)。
CNTIT寄存器:CNT计数的初始值。
AB相的电平和跳变沿决定了CNT的加数和减数。
CNT增计数时:
A上升沿,B逻辑低
B上升沿,A逻辑高
B下降沿,A逻辑低
A下降沿,B逻辑高
CNT减计数是:
A下降沿,B逻辑低
B下降沿,A逻辑高
B上升沿,A逻辑低
A上升沿,B逻辑高

编码器数值的读取我们使用了k60芯片的FTM模块中的正交解码功能来实现,直接调用库中的正交解码函数就可以获得脉冲数,通过比例换算可以获得当前速度。

 

SPEED=FTM_AB_Get(FTM1);    //读取正交解码数值

 

获取FTM正交解码脉冲数的函数:

 

int16 FTM_AB_Get(FTMn_e ftmn)
{
    uint32 val;
    val = FTM_CNT_REG(FTMN[ftmn]);// 计数器,只有低16位可用(写任何值到此寄存器,都会加载 CNTIN 的值)
    FTM_CNT_REG(FTMN[ftmn]) = 0;             
    return val;
}

 

 

(3).超声波
超声波模块的使用主要根据模块的测距原理编写,首先使Trig口输出不少于的10us的高电平信号,随后通过k60低功耗计时器(lptmr)计算Echo端高电平持续时间,即超声波从发送到接收的时间,最后用时间计算出距离。

 

void US_Init()
{
  GPIO_Init(PORTA,27,1,0);
  GPIO_Init(PORTA,29,0,0);
}

void Distance_Get()
{
  i=0;
  char txt[20]=' ';
  GPIO_Ctrl(PORTA,27,1);
  LPTMR_delay_us(20);
  GPIO_Ctrl(PORTA,27,0);
  while(GPIO_Get(PTA29)==0);
  LPTMR_time_start_us();
  while(GPIO_Get(PTA29)==1);
  i = LPTMR_time_get_us();
  if(i > 65535)
  {
    d=10000;
  }
  else
  {
    d=(int)(i*0.2);
  }
  sprintf(txt,"dis= %06dmm",d);
  UART_Put_Str(UART4,txt); //串口输出
  LCD_P6x8Str(28,7,(uint8 *)txt); //LCD输出
}

 

 

3、控制部分:舵机和电机

(1)PID代码
智能车的控制主要采用了经典的PID控制算法,分为电机速度控制和舵机方向控制。

 

舵机的控制是最重要的部分,是巡线的关键。通过ADC采集的三组电感的电压值来判断当前车身的状态,将当前值与期望值比较得到误差,代入增量式PID,调节出合适的参数,从而能够输出调整量使车能够始终巡线运行。

 

电机控制要求精度不高,只采用了PI控制,保证车速相对稳定,不至于产生太大波动,主要为了应对粗糙路面以及上下坡的情况。通过编码器传回的速度值判断当前车速,调整至稳定值。主要应用于上坡时,通过PID增大电机电流。

 

PID及其参数特点:

 

42

 

增量式PID介绍:

 

43

 

实现代码:

 

typedef struct //把PID定义为结构体形式
{
  float Kp,Ki,Kd;
  float Feedback;
  float Target;
  float error0,error1,error2;
  float Output;
  float Range; //控制输出范围,防止输出过大
}Type_PID;

void PID(Type_PID* PID)
{
 PID->error0 = (PID->Target) - (PID->Feedback);
PID->Output = (PID->Kp)*(PID->error0)+PID->Kd*(PID->error0-2*PID->error1+PID->error2)+PID->Ki*(PID->error0+PID->error2); 
	//更新偏差
	PID->error2 = PID->error1;
	PID->error1 = PID->error0;
	//对PID死区控制,因输出电流太小不足以驱动电机
	if (abs(PID->Output) < 20)
		PID->Output = 0;
	PID->Output = limit(PID->Output, PID->Range, -PID->Range);//把output限制在+-range之内
}

 

 

(2)主函数中PID代码

 

//pid初始设置
pid=(Type_PID *)malloc(sizeof(Type_PID));
   memset(pid,0,sizeof(Type_PID));
   pid_speed=(Type_PID *)malloc(sizeof(Type_PID));
   memset(pid_speed,0,sizeof(Type_PID));
   //电机速度PID
   pid_speed->Kp=1;
   pid_speed->Ki=4;
   pid_speed->Range=700;
   //舵机转角PID
   pid->Kp=1.5;
   pid->Ki=0.7;
   pid->Kd=7.5;
   pid->Range=142;

 

 

4.显示部分

(1)串口与蓝牙
蓝牙模块用的是HC-05,可以与串口共用。
下面给出库中的串口发送字符串函数,不赘述。

 

//例子:uart_putchar (UART4, "123456789");实际发送9个字节
void UART_Put_Str (UARTn_e uratn, char *str) //参数: uratn:模块名如:UART0 ,str: 发送的地址   
{
    while(*str)
    {
        UART_Put_Char(uratn, *str++);
    }
}

 

 

(2)LCD屏
列出库中函数,具体实现不赘述。

 

LCD_P6x8Str(unsigned char x,unsigned char y,unsigned char *p)// 写入一组标准ASCII字符串, 显示的位置(x,y),y为页范围0~7,要显示的字符串
void LCD_P14x16Str(unsigned char x,unsigned char y,unsigned char ch[])  //输出汉字字符串

 

 

 

发表评论

后才能评论