NXP

NXP智能车竞赛技术报告(室外电磁组)

2019-07-12 14:00发布

class="markdown_views prism-dracula">

一、概要

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

车的功能与实现:
在这里插入图片描述
本文脉络
在这里插入图片描述

二、硬件部分:

1.主控板 (1)原理图: K60引脚分配:
引脚通过查K60引脚功能来分配,除去特殊功能引脚外,其余引脚为普通I/O口
特殊引脚:
I2C通信:SDA,SCL
FTM(PWM输出):PWMA,DPWM
AD(模数转换):AD1~AD6
串口通信:TX2,RX2
在这里插入图片描述
电源部分及笔记:(7~8v输入,5v和3.3v输出) 在这里插入图片描述
以下为原理笔记:
在这里插入图片描述
在这里插入图片描述
按键模块:(SW2为4并排按键,用于设置车的模式)
在这里插入图片描述 在这里插入图片描述
电机输出(为排针插座,接驱动板,DIRA为电机方向控制,PWMA为速度控制): 在这里插入图片描述
舵机输出(为排针插座,5v供电电压,DPWM为脉冲输出,以控制舵机转角):
在这里插入图片描述
AD输入(为排针插座,接传感器板,6路电感值输入):
在这里插入图片描述
普通IO口:
在这里插入图片描述
串口:
在这里插入图片描述
蜂鸣器:
在这里插入图片描述
(2)PCB和3D图
PCB中,logo部分和敷铜(GND)已去除,以更清晰显示电路
在这里插入图片描述
在这里插入图片描述 2.电机驱动板:用于驱动电机 (1)原理图 H桥电机驱动:
VCC直接接电池(7~8V)
在这里插入图片描述
H桥原理:
在这里插入图片描述
12v升压(用于IR2104s驱动mos管):
在这里插入图片描述
电源部分:
VCC是电机供电
在这里插入图片描述
12V(用于IR2104s驱动MOS)和3.3v(逻辑芯片供电): 在这里插入图片描述
在这里插入图片描述
调速及转向:
Led显示电机方向及速度大小(速度越大越亮):
在这里插入图片描述
排针插座信号输入,3.3v用于逻辑芯片供电:
在这里插入图片描述
逻辑非和逻辑与芯片:
在这里插入图片描述
在这里插入图片描述
原理笔记: 在这里插入图片描述
(2)PCB(GND敷铜已去)和3D图
在这里插入图片描述
在这里插入图片描述 3.传感器板:处理电感数据 (1)原理图 电源部分:
在这里插入图片描述
信号放大:(opa2350为运放,根据电阻,放大倍数最大50倍,输入输出为正弦波,共6路)
在这里插入图片描述
信号整流(经实验,连接AD输出和GND的电容会严重削弱信号,故去除):
在这里插入图片描述
信号输入输出(为插座):
在这里插入图片描述 (2)PCB(GND敷铜已去除)和3D图
在这里插入图片描述
在这里插入图片描述 4.电磁探测板:探测电磁线信号 (1)原理图 谐振部分:谐振可以很好放大信号,电感电容值根据20K频率谐振计算。
在这里插入图片描述
其他部分(LED和排针插座):
在这里插入图片描述 (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)编码器 实物图
在这里插入图片描述
AB相增量式编码器原理:
在这里插入图片描述
在这里插入图片描述
发光二极管发射的光通过光栅到达光敏管,引起电平变化。如果正转,A相输出超前B相90度,如果反转A相滞后B相90度。
AB相正交解码原理:
在这里插入图片描述
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及其参数特点:
在这里插入图片描述
增量式PID介绍:
在这里插入图片描述
实现代码: 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[]) //输出汉字字符串