学 校:三江学院
队伍名称:识别不别
参赛队员:占晨龙 
王汶洁 
王倩  
带队教师:陈春  
葛年明 

引 言


  本文以第十六届全国大学生智能汽车竞赛为背景,该比赛受教育部高等教育司委托,由教育部高等自动化专业教学指导分委员会主办全国大学生智能汽车竞赛。该竞赛以智能汽车为研究对象的创意性科技竞赛,是面向全国大学生的一种具有探索性工程实践活动,是教育部倡导的大学生科技竞赛之一,为加强大学生实践、创新能力和团队精神的培养,促进高等教育教学改革。该竞赛以“立足培养,重在参与,鼓励探索,追求卓越”为指导思想,旨在促进高等学校素质教育,培养大学生的综合知识运用能力、基本工程实践能力和创新意识,激发大学生从事科学研究与探索的兴趣和潜能,倡导理论联系实际、求真务实的学风和团队协作的人文精神,为优秀人才的脱颖而出创造条件。该竞赛由竞赛秘书处为各参赛队提供/购置规定范围内的标准硬软件技术平台,竞赛过程包括理论设计、实际制作、整车调试、现场比赛等环节,要求学生组成团队,协同工作,初步体会一个工程性的研究开发项目从设计到实现的全过程。该竞赛融科学性、趣味性和观赏性为一体,是以迅猛发展、前景广阔的汽车电子为背景,涵盖自动控制、模式识别、传感技术、电子、电气、计算机、机械与汽车等多学科专业的创意性比赛。该竞赛规则透明,评价标准客观,坚持公开、公平、公正的原则,保证竞赛向健康、普及,持续的方向发展。

  车模应主委会要求采用的是C车模,主控核心是NXP公司的MK60DN512ZVLQ10单片机做为核心主控制器,而应第十六届全国大学生智能汽车竞赛智能视觉组别的要求,我们采用OpenART mini摄像头视觉传感器进行图像识别,通过MT9V034神眼数字摄像头经过主控处理进行软件二值化,同时自制电感采集进行对赛道的元素识别,通过光电编码器来检测车速,并采用MK600的输入捕捉功能进行脉冲计算获得速度和路程;转向舵机采用PD控制;驱动电机采用增量式 PID控制,通过PWM控制驱动电路调整电机的功率;而车速的目标值由默认值、运行安全方案和基于图像处理的优化策略进行综合控制。车模上的主控板、驱动板等电路板均由队员自制。

  本文第一章中简要的介绍系统总体方案的选择,第二章对车模的硬件机械结构结构的设计进行简单的介绍,第三章对硬件设计方案选择进行介绍,第四章为软件系统的设计与实现,第五章介绍了系统的开发工具和调试工具

第一章 系统整体设计


  智能车系统的总体选取NXP的MK60DN512ZVLQ10单片机作为系统控制处理器,通过二值化摄像头检测赛道信息,提取黑线引导线,用于赛道识别;用PD方式对舵机进行控制,实现对车模的转向控制;同时通过编码器获取赛车的当前速度,并采用PID控制系统实现速度闭环控制,驱动电机采用 PID控制,通过PWM控制驱动电路调整电机的功率;而车速的目标值由默认值、运行安全方案和基于图像处理的优化策略进行综合控制。为提高车模的速度和稳定性,使用上位机、 拨码开关、按键模块等调试工具,进行了大量硬件与软件测试。对特殊元素的识别,采用OpenART mini进行识别,并于主控进行通讯,主控做出相应的任务完成比赛。

▲ 图1.1 系统框图

  系统的框图如上图(1.1)所示,该系统总共包括10个系统,电源模块,调试模块,赛道采集模块,坡道识别模块,电感模块,调试模块,舵机控制模块,电机控制模块,主控模块,特殊元素采集模块,摄像头模块。

  1. 电源模块,电源模块为整块电路提供有效的直流电源,包括5V,3.3V和可调的稳压,为求高质量的电源,我们采用等芯片。

  2. K60单片机,型号为: MK60DN512ZVLQ10,作为整个小车的核心控制,负责接收信号和驱动相关模块,并能对信号进行及时的处理和反馈。

  3. 坡道检测模块,光电传感器模块E18-D80NK红外光电。

  4. 舵机控制模,舵机通过LM1084S产生5.8V稳压,用单片机PWM输出端口便可以直接驱动。

  5. 直流电机采用MOSFET搭建双桥驱动,单片机输出PWM,然后通过编码器进行闭环控制电机

  6. 调试模块,调试模块包括TFT显示屏、蓝牙发送上位机等以搭建一个友好的交互环境,及时掌握小车状态.

  7. 电机控制模块,采用DRV8701ERGER、STL150N3LLH5等芯片搭建的电路,小巧与母板可直插大大的减小了占用面积同时方便检修,耐压过流能力强等优点。
  8 . 特殊元素采集检测模块,本次采用的是逐飞科技的OPENART mini 集成度高可编程摄像头,对赛道上的二维码数字标识符和ART码进行检测并通过串口通讯将信息发送至主控板上。
  9. 摄像头模块,采用的是龙邱的MT9V034神眼数字摄像头,选取125°的广角摄像头,采样速率高且不易出错等优点。
  10. 电感采集模块,可以对赛道上的电磁信号进行采集并放大,对赛道上的特殊元素如圆环进行判断。

▲ 图1.2整车布局图

第二章 机械系统设计及实现


2.1前轮定位

  前轮定位对赛车的速度有很大的影响,虽然车模比较小,但是前轮定位作用还是不容忽视的。轮定位的内容有:主销后倾、主销内倾、前轮前束。

2.1.1 主销后倾

  转向轮(前轮)围绕主销进行旋转,前轴的轴荷通过主销传给转向轮,具备这两点的就叫做主销。主销向后倾斜,主销轴线与地面垂直线在赛车纵向平面内的夹角称为主销后倾。如图所示:

▲ 图2.1.1

  前轮重心在主销的轴线上,由于主销向后倾斜使前轮的重心不在车轮与地面的接触点上,于是产生了离心力,主销后倾形成的离心力,可以保证汽车直线行驶的稳定性,还可以帮助车轮自动回正。

2.1.2 主销内倾

  主销在横向平面内向内倾斜,主销轴线与地面垂直线在赛车横向断面内的夹角称为主销内倾角。主销内倾角也有使轮胎自动回正的作用,当汽车转向轮在外力作用下发生偏转时,由于主销内倾,则车轮连同整个汽车的前部将被抬起一定高度,在外力消失后,车轮就会在重力作用下力图恢复到原来的中间位置。但主销内倾角不宜过大,否则在转弯时轮胎将与赛道间产生较大的滑动,从而会增加轮胎与路面间的摩擦阻力,使转向变得沉重,同时会加速轮胎的磨

2.1.3 前轮前束

  通过车轮中心的汽车横向平面与车轮平面的交线与地面垂线之间的夹角,称为前轮外倾角。 前轮外倾角是前轮的上端向外倾斜的角度,如果前面两个轮子呈现“V”字形则称正倾角,呈现“八”字则称负倾角。前轮外倾可以抵消由于车的重力使车轮向内倾斜的趋势,减少赛车机件的磨损与负重。前轮前束是前轮前端向内倾斜的程度,当两轮的前端距离小后端距离大时为内八字,前端距离大后端距离小为外八字。由于前轮外倾使轮子滚动时类似与圆锥滚动,从而导致两侧车轮向外滚开。但由于拉杆的作用使车轮不可能向外滚开,车轮会出现边滚变向内划的现象,从而增加了轮胎的磨损。前轮外八字与前轮外倾搭配,一方面可以抵消前轮外倾的负作用,另一方面由于赛车前进时车轮由于惯性自然的向内倾斜,外八字可以抵消其向内倾斜的趋势。外八字还可以使转向时靠近弯道内侧的轮胎比靠近弯道外侧的轮胎的转向程度更大,则使内轮胎比外轮胎的转弯半径小,有利与转向。

2.2 车模重心

  车体的重心可以用吊线法测出,车体重心的位置对赛车加减速性能、转向性能和稳定性都有较大的影响。重心调整主要包括重心高度和前后位置的调整。 理论上,车体重心越低稳定性越好,重心低有利于赛车在高速转弯的贴地性,可以有效防止发生侧翻,因此在车体底盘高度、舵机安装、电路板的安装等上尽量使重心放低。

  根据车辆运动学理论,车身重心前移,大部分重量压在前轮,转向负荷增大,会增加转向,对模型车的制动性能和操纵稳定性有益,但降低转向的灵敏度,同时降低后轮的抓地力;重心后移,会减少转向,但增大转向灵敏度,后轮抓地力也会增加。但综合起来看,重心应靠近后轴一点。

2.3 车模底盘高度

  车模底盘的高度主要由赛道中的坡决定,在顺利过坡的前提下底盘越低越好。这样会使得重心更低,赛车的稳定型更强。

2.4舵机安装

  舵机有立式和卧式两种安装方案(立式:转轴处于水平方向;卧式:转轴处于竖直方向)。

  • 舵机立式安装方式的优点

  (1) 转向响应速度快,转向角较为符合阿克曼转向原理,它由舵机臂竖直平面的运动转化为拉杆水平方向的运动,减少了在同一平面上运动的死区;(2) 方便安装舵机臂,有利于调节赛车转向的中值。

  • 舵机立式安装方式的缺点

  (1) 不好安装固定;
  (2) 安装后较高,占用竖直方向的空间,会挡到摄像头
  (3) 重心较高。

  • 舵机立式安装的优点

  (1) 转向响应速度快,转向角较为符合阿克曼转向原理,它由舵机臂竖直平面的运动转化为拉杆水平方向的运动,减少了在同一平面上运动的死区;
  (2) 方便安装舵机臂,有利于调节赛车转向的中值。

  • 舵机立式安装方式的缺点

  (1) 不好安装固定;
  (2) 安装后较高,占用竖直方向的空间,会挡到摄像头
  (3) 重心较高。

  • 舵机卧式安装方式的优点

  (1) 转向响应速度较快;
  (2) 高度较低
  (3) 重心低。

  舵机卧式安装方式的缺点:转向角部分符合阿克曼转向原理(转角小时),舵机臂和拉杆都在水平平面内运动,当舵机臂长度与转角臂长度相等时会导致内、外侧轮不符合阿克曼转角。经过仿真分析后得出舵机立式安装方式较好,立式安装方式的转角较符合阿克曼转角。

  同时舵机的摆杆的长度也会直接影响到舵机的转矩。由公式舵机转矩 = 舵机摆杆作用力 * 摆杆长度,得:舵机摆杆作用力越大,反应越灵敏,转向速度越快。转矩一定时,摆杆越长,输出的作用力越小,所以摆杆不能太长,不然会拉不动轮胎左右转向。经过多次实验测量,我们决定采用3-4cm的长度。

2.5 编码器的安装

  编码器是用来对车速进行测算的仪器,目前很多学校使用的是通过光电编码的编码器,其精度较高、安装较为方便,但体积有点大。光电编码器的安装精度较高,要求编码器轴与赛车后轴同轴,且编码盘必须与赛车的中心线共线。我们是直接装在车上,利用齿轮与差速器上的大齿轮相咬合。这种安装方法方便快捷,更能较准确的测出电机的转速。

▲ 图2.5.1 编码器安装

2.6摄像头的固定

  对于整个车来说,摄像头的安装影响到整个车采集信息的准确性。在摄像头安装过程中,我们在简洁,保证强度的基础上,严格控制摄像头的安装位置和增量。整套装置具有很高的定位精度和刚度,使摄像头便于调节、拆卸和维修,具有赛场快速保障能力。

▲ 图2.6.1

2.7电池的固定

  由于电池可以影响重心的位置,而且固定在板子上,要求是要易于拆卸,因此,我们采用用铜柱将它卡死,及时车子需要很大的向心力,也可以保持,而且在易于拆卸

第三章 硬件系统设计及实现


3.1硬件设计方案

  我们主要从系统的稳定性、可靠性、高效性、实用性、简洁等方面来考虑硬件的整体设计。从最初方案设定到最终方案的敲定,我们经历各种讨论与大的改动才有了如下的硬件方案。可靠性与稳定性是一个系统能够完成预设功能的最大前提。电路进行单片化设计,将电源模块、调试模块、图像信号处理模块、速度 反馈信息处理模块、驱动模块和各个接口电路设计在一块主板上。主板直接用螺 丝螺母固定于底盘上。电路做好了模拟部分和数字部分的隔离、各个电源的滤波, 以及对干扰信号的屏蔽工作。各个接口连接可靠牢固。 简洁。主板外形设计根据车模尺寸、形状量身打造。器件排放整齐。减少了 各个电路板之间的连线,使整车看上去简洁美观。如下图

▲ 图3.1.1 母板PCB

▲ 图3.1.2 母板PCB

3.2运放模块设计

  电磁传感器由六路 10mH 电感和 6.8nF 电容构成谐振,采集到的信号在离赛道高 2cm 以上可检测到正常信号,并且传输给板载 LMV358 运算放大器放大整流后通过输出信号接口,接 到单片机 ADC 接口。离赛道中心 2-30cm(实测不止这个距离)均可看到单片机采集的值明显的连 续变化,并且在同一位置检测的值基本稳定,是较为理想的赛道信号电磁传感器。电感布局方面根 据不同赛道元素需求,对电感合理布局,如下图。

▲ 图3.2.1 电磁传感器PCB

▲ 图3.2.1 电磁传感器PCB

3.3驱动模块

  • 驱动方案优势

  1、无需升压电路,简化设计,应用更加广泛。在以往的智能车电机驱动方案 中,使用了栅极驱动芯片 IR2104 + IR7843,但由于栅极驱动芯片需要 10V 以上 电压供电,因此需要在驱动电路上加一个 BOOST 升压电路,而在使用中发现升压 电路在电磁组中可能对电磁信号采集产生一定干扰。而此方案中的门极驱动芯片 DRV8701E 采用了内部自举升压,而不需要外部升压电路,可以适用于更加广泛的 输入电源(5.9V-45V)。

  2、带过流保护功能,保护电机,保护智能车,使用更加安全。门极驱动芯片 DRV8701E 内部自带 OCP(Overcurrent Protection)功能,当芯片内部检测到触 发过流事件。此时芯片内部将禁止使能,达到控制输出保护驱动板以及电机的效果。

  3、驱动性能更加强劲,24V 大电机也能搞定。驱动性能强劲主要由以下几面 体现:
  (1) 首先 N-MOS 管 TPH1R403NL 的 DS 导通内阻更小,只有 1.7 毫欧,而之 前的 IR7843 的为 3.3 毫欧。且 TPH1R403NL 的瞬间峰值电流可达 200A,持续电流 可达 60A,均高于 IR7843 的对应参数。
  (2) 其次 DRV8701E 芯片的理论输入电压范围为 5.9V-45V,这就可使用更高 电压的输入电源,进而轻松驱动 12V-24V 的电机。
  (3) 内阻小减小了内部损耗,驱动板长时间驱动高速电机,大电流使用也 不发烫,不影响性能。

  4、驱动信号更加简单,解放 PWM 资源。在以往的 IR2104+IR7843 方案中,若 控制两个电机的正反转,则需要 4 路 PWM 信号来作为输入信号。而在此 DRV8701E+TPH1R403NL 方案中,只需要 2 路 PWM 信号+2 路普通 IO 口引脚,即可 进行控制。即控制 1 个电机只需要输入一个 PWM 信号控制转速,通过一个普通 IO 口输出高低电平来控制转向。

  5、体积更小,适用于各组别智能车搭建。

▲ 图3.3.2 驱动模块PCB

第四章 软件系统设计及实现


4.1 各种元素的扫线方法

4.1.1 黑线提取

(1)二值化

  原图像采集来为灰度,经我们多次算法修改发现将图像进行二值化后,处理起来更为方便,而采用动态阈值,固定曝光。阈值用大津阈值算的,曝光根据实时的 AD 值调节,最好控制在 200 左右为最佳。采用动态阈值的好处是能适应光强不均的环境比如体育场等。判断黑白与跳边沿的方法一样,于阈值判断为白 ,小于阈值判断为黑(背景和黑线)。代码如下

int GetOSTU(uint8_t tmImage[LCDH][LCDW])
{ int16_t i,j; uint32_t Amount = 0; uint32_t PixelBack = 0; uint32_t PixelIntegralBack = 0; uint32_t PixelIntegral = 0; int32_t PixelIntegralFore = 0; int32_t PixelFore = 0; float OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 类间方差; int16_t MinValue, MaxValue; uint8_t Threshold = 0;uint8_t HistoGram[256];              //  for (j = 0; j < 256; j++)  HistoGram[j] = 0; //初始化灰度直方图 for (j = 0; j < LCDH; j++) { for (i = 0; i < LCDW; i++) { HistoGram[tmImage[j][i]]++; //统计灰度级中每个像素在整幅图像中的个数} } for (MinValue = 0; MinValue < 256 && HistoGram[MinValue] == 0; MinValue++) ;        //获取最小灰度的值for (MaxValue = 255; MaxValue > MinValue && HistoGram[MinValue] == 0; MaxValue--) ; //获取最大灰度的值if (MaxValue == MinValue)     return MaxValue;         // 图像中只有一个颜色    if (MinValue + 1 == MaxValue)  return MinValue;        // 图像中只有二个颜色for (j = MinValue; j <= MaxValue; j++)    Amount += HistoGram[j];        //  像素总数PixelIntegral = 0;for (j = MinValue; j <= MaxValue; j++){PixelIntegral += HistoGram[j] * j;//灰度值总数}SigmaB = -1;for (j = MinValue; j < MaxValue; j++){PixelBack = PixelBack + HistoGram[j];   //前景像素点数PixelFore = Amount - PixelBack;         //背景像素点数OmegaBack = (float)PixelBack / Amount;//前景像素百分比OmegaFore = (float)PixelFore / Amount;//背景像素百分比PixelIntegralBack += HistoGram[j] * j;  //前景灰度值PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值MicroBack = (float)PixelIntegralBack / PixelBack;   //前景灰度百分比MicroFore = (float)PixelIntegralFore / PixelFore;   //背景灰度百分比Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//计算类间方差if (Sigma > SigmaB)                    //遍历最大的类间方差g //找出最大类间方差以及对应的阈值{SigmaB = Sigma;Threshold = j;}}return Threshold;                        //返回最佳阈值;
}

(2)提取赛道边沿

  得到一幅完好的二值化后的图片后需要对赛道的边沿进行提取与显示,这里我们采用的是从中间向左右扫,以俩个点为步长基准,当从中间往俩边扫时,有俩个点的跳变便标为左右赛道边界。跳变对光线要求挺高,但是容易提取,便于控制。提取代码如下:

for(y=58;y>=0;y--)//扫描完58,整副图像处理完毕{for(x=my_lastzhongjian;x<=93;x++)//从中间向右扫描{if(Pixle[y][x-1]==0&&Pixle[y][x]==0)//gaidong{right_heixian[y]=x;shaobudaoyou_flag[y]=1;break;}}for(x=my_lastzhongjian;x>=0;x--)//向左扫描{if(Pixle[y][x+1]==0&&Pixle[y][x]==0)//gaidong{left_heixian[y]=x;shaobudaozuo_flag[y]=1;break;}}

(3)布线

  当赛道缺失或光线暗淡图像都会有明显的缺失,这时我们应当对图像进行布线,在原先图像的基础上进行向上拟合中线,而当面对不同的元素我们采取的布线方法都是不一样的。

if(y>48)//丢边补线,加整个赛道补线{if(shaobudaoyou_flag[y]==0&&shaobudaozuo_flag[y]==1)//扫不到右{right_heixian[y]=left_heixian[y]+2*half_width_group[y];//y为数组中的x值}else if(shaobudaoyou_flag[y]==1&&shaobudaozuo_flag[y]==0)//扫不到左{left_heixian[y]=right_heixian[y]-2*half_width_group[y];}else if(shaobudaoyou_flag[y]==0&&shaobudaozuo_flag[y]==0)//都扫不到{left_heixian[y]=0;//如果两边都扫不到,直接从中间提取中线right_heixian[y]=93;}}else if(y<=48)//更远的补线,{if(shaobudaoyou_flag[y]==0&&shaobudaozuo_flag[y]==1)//扫不到右{right_heixian[y]=right_heixian[y+1]+(abs)(left_heixian[y]-left_heixian[y+1])-1;//根据左边这一点与上一点,//(y+1)为数组中上一次的x坐标,//left_heixian[y]-left_heixian[y+1]算左边偏移量,减一为后期补偿}else if(shaobudaoyou_flag[y]==1&&shaobudaozuo_flag[y]==0)//扫不到左{left_heixian[y]=left_heixian[y+1]-(abs)(right_heixian[y+1]-right_heixian[y])+1;//abs为取绝对值}else if(shaobudaoyou_flag[y]==0&&shaobudaozuo_flag[y]==0)//都扫不到{left_heixian[y]=0;right_heixian[y]=93;}}

(3)坡道

  当摄像头架得高时上坡的时候会丢线并且还看到的图像还会和人字的重复误判,为了防止误判我们用了红外识别坡道。在上坡、下坡的时候,红外能感应到,进入坡道模式。而实现红外检测也十分简单,下面为红外检测代码。

if(GPIO_PinRead(PTB17)==0)  podao_flag=1;

(4)起跑线

  起跑线的在赛道中观测还较为明显,且作为主标识十分方便,而我们采用的是对某一行的黑白值进行计算,这样不仅处理起来十分的迅速且不易出错,下面为起跑线检测代码:

void star_line_judg()
{
// int kk,bai_flag=0,hei_flag=0,heibai_flag=0,baihei_flag=0;bai_flag=0;hei_flag=0;heibai_flag=0;baihei_flag=0;for(kk=20;kk<=92;kk++){if(Pixle[20][kk]>0)bai_flag=1;elseif(bai_flag&&Pixle[20][kk]==0){baihei_flag++;bai_flag=0;}if(Pixle[20][kk]==0)hei_flag=1;elseif(hei_flag&&Pixle[20][kk]>0){heibai_flag++;hei_flag=0;}}if(baihei_flag>=4&&heibai_flag>=4&&baihei_flag-heibai_flag<=2)          //第二次停车检测到star_lineflag=1;
}

4.1.2元素的处理方法

(1)圆环处理方法

  圆环的处理方法较为复杂,如果单用摄像头的话需要许多的标识位,且算法十分的复杂,因此我们决定采用电磁辅助入环,电磁归一化后,对入环的特殊位置进行标记,图4.1-1中2位置为我们检测的入环时机,当在此位置检测到后摄像头对圆环进行布线,入环后仅需要摄像头继续跑。

▲ 图4.1.1 入环的位置

  电磁归一化代码如下:

    LnowADC[0] = (uint16_t)(ADC_Get(0)*0.806);  LnowADC[1] = (uint16_t)(ADC_Get(1)*0.806);  ,LnowADC[4] = (uint16_t)(ADC_Get(2)*0.806);  LnowADC[5] = (uint16_t)(ADC_Get(3)*0.806);
//    BatVolt       = ADC_Read(ADC7);  // 刷新电池电压if (LnowADC[0] < ad_min[0])ad_min[0] = LnowADC[0];     // 刷新最小值else if (LnowADC[0] > ad_max[0])ad_max[0] = LnowADC[0];     // 刷新最大值if (LnowADC[1] < ad_min[1])ad_min[1] = LnowADC[1];else if (LnowADC[1] > ad_max[1])ad_max[1] = LnowADC[1];if (LnowADC[4] < ad_min[4])ad_min[4] = LnowADC[4];else if (LnowADC[4] > ad_max[4])ad_max[4] = LnowADC[4];if (LnowADC[5] < ad_min[5])ad_min[5] = LnowADC[5];else if (LnowADC[5] > ad_max[5])ad_max[5] = LnowADC[5];Lleft1 = (LnowADC[0] - ad_min[0]) * 100 / (ad_max[0] - ad_min[0]);     // 各偏移量归一化到0--100以内Lleft2 = (LnowADC[1] - ad_min[1]) * 100 / (ad_max[1] - ad_min[1]);Lright1 = (LnowADC[4] - ad_min[4]) * 100 / (ad_max[4] - ad_min[4]);
Lright2 = (LnowADC[5] - ad_min[5]) * 100 / (ad_max[5] - ad_min[5]);
入环布线代码如下://进出环岛补线if(ruhuandao_you_flag==1||chalu_buxian_you==1  ){left_heixian[y]=right_heixian[y]-2*half_width_group[y];}if(ruhuandao_zuo_flag==1||chalu_buxian_zuo==1  ){right_heixian[y]=left_heixian[y]+2*half_width_group[y];//y为数组中的x值}

(2)十字路口

  十字路口相对来说较为简单,仅需根据摄像头拍摄到的两边断点,并根据断点进行相应的布线即可,具体实现代码如下:

if(duandian<16) // *****************************十字{for(int i=58;i>0;i--){if(i>45&&(shaobudaozuo_flag[i]==1||shaobudaoyou_flag[i]==1))rushizi++;if((i>16&&i<=40)&&shaobudaozuo_flag[i]==0&&shaobudaoyou_flag[i]==0)shizishu++;if(i<45&&(shaobudaozuo_flag[i]==1||shaobudaoyou_flag[i]==1)&&shangshizidiyidian==0)shangshizidiyidian=i;}if(shizishu>2&&rushizi>8)//刚入十字{shizi_flag=1;xielv_flag=1;for(int i=55;i>0;i--){if(shaobudaozuo_flag[i]==0&&shaobudaoyou_flag[i]==0){shizidiyidian=i;break;}}if(shizidiyidian<30){yy1=shizidiyidian+14;yy2=shizidiyidian+20;}else if(shizidiyidian<45){yy1=shizidiyidian+8;yy2=shizidiyidian+13;}else if(shizidiyidian>=45&&shizidiyidian<=49){yy1=shizidiyidian+5;yy2=shizidiyidian+9;}yy1_pinjun=(currentzhongjian[yy1]+currentzhongjian[yy1+1]+currentzhongjian[yy1-1])/3;yy2_pinjun=(currentzhongjian[yy2]+currentzhongjian[yy2+1]+currentzhongjian[yy2-1])/3;huaxian(yy1,yy1_pinjun,yy2,yy2_pinjun);}else if(shizishu>5&&rushizi<=8&&shangshizidiyidian>=20)//出十字{chushizi_flag=1;xielv_flag=1;shizi_flag=1;huaxian2(shangshizidiyidian-6,currentzhongjian[shangshizidiyidian-6],58,currentzhongjian[58]);}}

(3)三岔路的识别

  三岔路识别因我们摄像头的选取的高度,导致检测十分方便,但是需要对其逻辑有着一定的掌握,同时也需要注意与其他的元素会出现误判的现象。
  具体代码如下:

  if(abs(duandian)<18&&abs(duandian)>7&&Wide>65&&Wide<120&&y_duandian_zuo+y_duandian_you>100&&Chalu==0&&K60_jishi>300&&bai_jishu>1350&&bai_jishu<1700&&a==1){dierchi_chalu=1;}if(dierchi_chalu==1)chalu_jishi_cont++;if(dierchi_chalu==1&&chalu_jishi_cont<100&&fang_flag_cont==1)//设置岔路补线时间,速度越快补线时间越短{chalu_buxian_zuo=1;}else if(dierchi_chalu==1&&chalu_jishi_cont<100&&fang_flag_cont==2){chalu_buxian_you=1;}__________________________________________________________________________________________________________________if( Chalu==1) chalu_jishu++;   if(chalu_jishu>200)     Chalu=0,chalu_jishu=0,chalu_chu=1;if(chalu_chu==1){chalu_chu_flag++;if(Chalu==0&&chalu_chu_flag<80){my_piancha=-18;}}
// __________________________________________________________________________________________________________________else if(chuankou==7&&Chalu==0&&fang_flag_cont&&chalu_chu_flag<50) my_piancha=-35;   else if(chuankou==7&&Chalu==0&&fang_flag_cont==2&&chalu_chu_flag<50) my_piancha=35;else if(chuankou==7&&Chalu==0&&(fang_flag_cont!=1||fang_flag_cont!=2)&&chalu_chu_flag<50)my_piancha=25;else{chalu_buxian_zuo=0;chalu_buxian_you=0;chalu_chu=0;chalu_chu_flag=0;}

(4)二维码的识别与扫描

  应主办方的要求我们采用用摄像头检测赛道上的二维码,然后经过处理停车,但是鉴于会与其他元素出现误判,因此我们采用取赛道上的黑白像素点的比值,对赛道像素点的比值进行加权,与其他条件共同约束,以免出现误判的现象,而实现检测二维码只需通过OpenART mini即可,当然也可以通过OpenART来检测控制停车但由于OpenART的性能导致需要将它帧率调制10帧左右,导致我们仅能用摄像头来识别,二维码检测代码如

void erweima()
{int i=0,j=0,jiashi_flag;erweima_heidian=0,erweima_baidian=0,bizhi=0;for(i=QRH_star;i<QRH_end;i++){for(j=QRW_star;j<QRW_end;j++){if(Pixle[i][j]==0){erweima_heidian++;}else if(Pixle[i][j]!=0){erweima_baidian++;}}}bizhi=erweima_baidian / erweima_heidian ;if(fang!=0) {fang_flag++;if(fang_flag>200)fang=0;}if(shizi_flag==1)  {fang_shizi++;if(fang_shizi>100)fang_shizi=0;}if(bizhi>0&&bizhi<14&&fang_shizi<100&&fang==0&&erweima_heidian>20&&erweima_heidian<80&&erweima_baidian>320&&erweima_baidian<370&&K60_jishi>500//&&abs(duandian)<10// &&K60_yanshi==0&&Chalu!=1&&podao_flag!=1&&bai_jishu<950&&bai_jishu>700&&abs(my_piancha)<18&&!shizi_flag)//&&qvlv_quanju<10 )  erweima_saodao=1;if(erweima_saodao){saodao_jishi++;if(saodao_jishi<500)     UART_PutStr(UART4, "1");
//      if(chuankou==5)   jiashi_flag=1;jiashi_flag=chuankou*200+800;if(saodao_jishi>jiashi_flag){      saodao_jishi=0;erweima_saodao=0;
//        fang++;//防止二次误判UART_PutStr(UART4, "0");}}下:

4.1.3舵机控制

  我们通过测试发现用 PD 控制来控制舵机打角可以取得较好的效果。将图象经过算法处理后得到的黑线位置和对应的舵机 PD 参照角度处理成二次线性关系。在Ki 置零的情况下,舵机在这种动态随动系统对动态响应性能要求更高。更重要的是,我们通过合理调节 Kp 参数,发现车能在直线高速行驶时仍能保持车身非常稳定,没有震荡,所以基本没有必要使用 Ki 参数;在测试中,我们发现增加 P 项系数可以最强车模的沿线能力,并且可以使车模的转向提前,实现切弯效果。算法中加入 D项后,可以使车模入弯时转向提前,出弯时转向减少,对大 S 弯切线很有好处。降低 P 系数而增加 D 系数可以使车模在大 S 弯内切线的程度增加,在大半径弧线中的切线量减少。通过选择转向参考行、设置 PD 系数以及调整转角曲线,可以将车模的行车线调整到一个较为理想的状态。当车模与黑线的偏差增大时,给定速度降低,当车模与黑线的偏差减小时,定速度增加。这样可以在一定程度上使车模入弯时减速提前,出弯时加速提前。
  舵机打角PID代码如下

void servo()//舵机控制函数
{if(1){jiao=(uint32)((kp1*duojijiaodu-kd*(lastpiancha_4-duojijiaodu))/10);//舵机的pid算法公式,没有小数,所以kp1要写整数再除以10来实现小数ServoDuty=765-jiao;//SERVO_MID        750為種植//左右限幅if(ServoDuty > 865)        //根据自己的实际情况对舵机打角进行限制,防止卡死ServoDuty = 865;if(ServoDuty < 675)        //根据自己的实际情况对舵机打角进行限制,防止卡死ServoDuty = 675;CMT_PwmDuty(ServoDuty);     // 舵机PWM输出,转向// FTM_PwmDuty(FTM0, FTM_CH7, ServoDuty);}

4.1.4速度控制

  在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称 PID 控制。PID 控制器问世至今已有近 70 年历史,它以其结构简单、稳定性好、工作可靠、调整方便而成为工业控制的主要技术之一。当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用 PID 控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用 PID 控制技术。PID 控制,实际中也有 PI 和 PD 控制。PID 控制器是一种线性控制器,它根据给定值与实际输出值构成控制偏差。将偏差的比例§、积分(I)和微分(D)通过线性组合构成控制量,对被控对象进行控制,故称 PID 控制器,原理图如下:

▲ 图4.1.2 PID工作原理

4.1.5 数字PID算法的选择

  我们智能车控制系统使用的是数字 PID,其控制规律是:

  简单说来,PID 控制器各校正环节的作用如下:

  • 比例环节:及时成比例地反映控制系统的偏差信号,偏差一旦产生,控制器立即产生控制作用,以减少偏差。
  • 积分环节:主要用于消除静差,提高系统的无差度。积分作用的强弱取决于积分时间常数,越大,积分作用越弱,反之则越强。
  • 微分环节:能反映偏差信号的变化趋势(变化速率),并能在该偏差信号变得太大之前,在系统中引入一个有效的早期修正信号,从而加快系统的动作速度,减小调节时间。

  数字 PID 控制算法通常分为位置式 PID 控制算法和增量式 PID 控制算法。位置式 PID 中,由于计算机输出的 u (k) 直接去控制执行机构(如阀门),u(k)的值和执行机构的位置(如阀门开度)是一一对应的,所以通常称公式(4.2)为位置式 PID控制算法。

  位置式 PID 控制算法的缺点是:由于全量输出,所以每次输出均与过去的状态有关,计算时要对过去 e(k)进行累加,计算机工作量大;而且因为计算机输出的 u(k)对应的是执行机构的实际位置,如计算机出现故障,u(k)的大幅度变化,会引起执行机构位置的大幅度变化,这种情况往往是生产实践中不允许的,在某些场合,还可能造成严重的生产事故。因而我们选择了增量式 PID 控制算法,所谓增量式 PID是指数字控制器的输出只是控制量的增量 u(k)。

  • 增量式 PID 具有以下优点

(1) 由于计算机输出增量,所以误动作时影响小,必要时可用逻辑判断的方法关掉。

(2) 手动/自动切换时冲击小,便于实现无扰动切换。此外,当计算机发生故障时,由于输出通道或执行装置具有信号的锁存作用,故能保持原值。

(3) 算式中不需要累加。控制增量 u(k)的确定仅与最近 k 次的采样值有关,所以较容易通过加权处理而获得比较好的控制效果。
  经过反复的测试我们发现增量式PID在速度的急停和启动都有着良好的效果。而应本次比赛的要求需要车子在高速行进的时候急停与启动,因此我们选择增量式PID,具体代码如下:

void PID(int QWMC)
{int cs=0,men=-100;//menNOW_SPEED=(speed_a+speed_b)/2;//左轮a加右轮b除以2为实际速度if(abs(my_piancha)>11)//差速的弯道{if(long_speed > 100)cs =my_piancha*CS/10;//CS差速系数,cs差速比例  //0.9else cs =(signed int)(0.9*(float)(my_piancha*CS/10));}if(erweima_saodao) //erweima{dut=2000;//1000men=-2;}
//                                    //***************电机控制pid公式****************************//MC_piancha=QWMC-NOW_SPEED;//脉冲偏差=期望脉冲-实际脉冲d_MC_piancha=MC_piancha-last_MC;last_MC=MC_piancha;dianji_zhankongbi=last_dianji_zhankongbi+dianji_kp*MC_piancha+dianji_kd*d_MC_piancha;//last_dianji_zhankongbiif(dianji_zhankongbi>7000)//8000dianji_zhankongbi=7000;if(dianji_zhankongbi<0)dianji_zhankongbi=0;last_dianji_zhankongbi=dianji_zhankongbi;DSYJ_dianji_zhengzhuan(dianji_zhankongbi,cs);if(erweima_saodao&&saodao_jishi<90)DSYJ_dianji_fanzhuan(5000);if(Chalu==1&&chalu_jishu<110)DSYJ_dianji_fanzhuan(5000);
}

4.1.6 OpenART mini控制

  OpenART的控制也是这次比赛的主要任务,经过训练的人工神经网络需要部署在小车上,也就是需要一个平台。在我们的方案中,将人工神经网络部署在OpenArt平台上。OpenArt可以加载经过量化的人工神经网络,将传感器采集的图片送入网络中进行识别,而对Openart我们从以下几个层面进行叙述:

1)数据增强

  常用的数据增强方法有翻转、旋转、缩放、移位、高斯模糊、椒盐噪声、运动模糊以及对比度和亮度的随机改变等。
  也可以通过from keras.preprocessing.image import ImageDataGenerator来就行数据增强。

  数据增强代码

rows, cols, chn = img_noise.shape
noise_num = np.random.randint(rows * cols / 8, rows * cols / 5)
# 加噪声
for _ in range(noise_num):x = np.random.randint(0, rows)  # 随机生成指定范围的整数y = np.random.randint(0, cols)img_noise[x, y, :] = np.random.randint(0, 255)
# 高斯模糊
img_gas = cv2.GaussianBlur(img, (5, 5), 0)
# 调低亮度,调高亮度
img_shadow[x, y, c] = np.clip(int(img_shadow[x, y, c] - r), a_max=255, a_min=0)img_highlight[x, y, c] = np.clip(int(img_highlight[x, y, c] + r), a_max=255, a_min=0)img_shadow[x, y, c] = np.clip(int(img_shadow[x, y, c] * 0.6), a_max=255, a_min=0)img_highlight[x, y, c] = np.clip(int(img_highlight[x, y, c] * 1.5), a_max=255, a_min=0)
# keras数据增强
datagen = ImageDataGenerator(featurewise_center=True,  # set input mean to 0 over the datasetsamplewise_center=True,  # set each sample mean to 0featurewise_std_normalization=True,  # divide inputs by std of the datasetsamplewise_std_normalization=True,  # divide each input by its stdzca_whitening=True,  # apply ZCA whiteningrotation_range=20,  # randomly rotate images in the range (degrees, 0 to 180)width_shift_range=0.2,  # randomly shift images horizontally (fraction of total width)height_shift_range=0.2,  # randomly shift images vertically (fraction of total height)shear_range=0.2,horizontal_flip=True,  # randomly flip imagesvertical_flip=True  # randomly flip images)

2)Depthwise separable convolution

  MobileNet 的基本单元是深度级可分离卷积(depthwise separable convolution),其实这种结构之前已经被使用在 Inception 模型中。深度级可分离卷积其实是一种可分解卷积操作(factorized convolutions),其可以分解为两个更小的操作:depthwise convolution 和 pointwise convolution,如图 4.1.3 所示。Depthwise convolution 和标准卷积不同,对于标准卷积其卷积核是用在所有的输入通道上(input channels),而 depthwise convolution 针对每个输入通道采用不同的卷积核,就是说一个卷积核对应一个输入通道,所以说 depthwise convolution 是 depth 级别的操作。而 pointwise convolution 其实就是普通的卷积,只不过其采用 1x1 的卷积核。图 4.1.4中更清晰地展示了两种操作。对于 depthwise separable convolution,其首先是采用 depthwise convolution 对不同输入通道分别进行卷积,然后采用 pointwise convolution 将上面的输出再进行结合,这样其实整体效果和一个标准卷积是差不多的,但是会大大减少计算量和模型参数量。

▲ 图4.1.3

3)MobileNet 网络结构

  前面讲述了 depthwise separable convolution,这是 MobileNet 的基本组件,但是在真正应用中会加入 batchnorm,并使用 ReLU 激活函数,所以 depthwise separable convolution 的基本结构如图4.1.5所示。

  MobileNet 的网络结构如图4.1.6 所示。首先是一个 3x3 的标准卷积,然后后面就是堆积 depthwise separable convolution,并且可以看到其中的部分 depthwise convolution 会通过 strides=2 进行 down sampling。然后采用 average pooling 将 feature 变成 1x1,根据预测类别大小加上全连接层,最后是一个 softmax 层。如果单独计算 depthwise
  convolution 和 pointwise convolution,整个网络有 28 层(这里 Avg Pool 和 Softmax 不计算在内)。我们还可以分析整个网络的参数和计算量分布,如图4.1.7 所示。可以看到整个计算量基本集中在 1x1 卷积上,如果你熟悉卷积底层实现的话,你应该知道卷积一般通过一种 im2col 方式实现,其需要内存重组,但是当卷积核为 1x1 时,其实就不需要这种操作了,底层可以有更快的实现。对于参数也主要集中在 1x1 卷积,除此之外还有就是全连接层占了一部分参数。

▲ 图4.1.6

▲ 图4.1.7

  MobileNet 的 TensorFlow 实现

def depthwise_separable(x, params):# f1/f2 filter size, s1 stride of conv(s1, f2) = paramsx = DepthwiseConv2D((3, 3), strides=(s1[0], s1[0]), padding='same',depthwise_initializer="he_normal", depthwise_regularizer=regularizers.l2(weight_decay))(x)x = BatchNormalization(momentum=0.9, epsilon=1e-5)(x)x = Activation('relu')(x)x = Conv2D(int(alpha * f2[0]), (1, 1), strides=(1, 1), padding='same',kernel_initializer="he_normal", kernel_regularizer=regularizers.l2(weight_decay))(x)x = BatchNormalization(momentum=0.9, epsilon=1e-5)(x)x = Activation('relu')(x)return xdef MobileNet(img_input, shallow=True, classes=10):# change stridex = Conv2D(int(alpha * 32), (3, 3), strides=(1, 1), padding='same',kernel_initializer="he_normal", kernel_regularizer=regularizers.l2(weight_decay))(img_input)x = BatchNormalization()(x)x = Activation('relu')(x)x = depthwise_separable(x, params=[(1,), (32,)])x = depthwise_separable(x, params=[(1,), (64,)])  # change strideif not shallow:for _ in range(2):x = depthwise_separable(x, params=[(1,), (64,)])x = depthwise_separable(x, params=[(2,), (64,)])x = depthwise_separable(x, params=[(1,), (64,)])x = GlobalAveragePooling2D()(x)out = Dense(classes, activation='softmax')(x)
return out

4)随便图片

  将训练模型H5文件转为tflite,在openmv运行

for obj in tf.classify(path_mobilenet, img1, min_scale=1.0, scale_mul=0.5, x_overlap=0.0, y_overlap=0.0):sorted_list = sorted(zip(labels, obj.output()), key = lambda x: x[1], reverse = True)for i in range(5):print("%s = %f" % (sorted_list[i][0], sorted_list[i][1]))

5)Openmv图像预处理

sensor.set_windowing((200, 100))
sensor.skip_frames(time=2000)
sensor.set_auto_gain(True)
sensor.set_auto_whitebal(True)img = img.histeq(adaptive=True, clip_limit=4)
img = img.gamma_corr(1.5)

寻找矩形

    for r in img.find_rects(threshold=38000):  # 在图像中搜索矩形img.draw_rectangle(r.rect(), color=(255, 255, 255))if r.w()/r.h() > 1.7 or r.h()/r.w() > 1.7:continuenow_val = (r.corners()[0][0]+r.corners()[1][0])/2now_err = exp_val - now_val#change_val, last_last_err, last_err = cmd_pid(last_err,now_err,exp_val,now_val)print(now_err)servo_right = servo_right + int(now_err*0.3)if servo_right > 240:servo_right = 240if servo_right < 10:servo_right = 10pwm4.duty(servo_right)time.sleep(200)

5)串口通信

  与母板进行通讯我们采用串口的收发,当然这对于Openart来说仅需调用串口的包即可。

//if uart.any():a = uart.readline()b = str(a)[2]
//发
uart.write(waitMore)

第五章 系统开发及调试工具


5.1开发工具

  程序开放在IAR Embedded Workbench IDE下进行, Embedded Workbench for ARM 是IAR Systems 公司为ARM 微处理器开发的一个集成开发环境(下面简称IAR EWARM)。比较其他的ARM 开发环境,IAR EWARM 具有入门容易、使用方便和代码紧凑等特点。

  EWARM 中包含一个全软件的模拟程序(simulator)。用户不需要任何硬件支持就可以模拟各种ARM 内核、外部设备甚至中断的软件运行环境。从中可以了解和评估IAR EWARM 的功能和使用方法。

5.2调试工具

5.2.1 DemokTool

  智能车摄像头上位机,是一款置于 PC 即上,将 MCU 采集到的图像像素数据 进行原图和二值化显示,并且能够根据自身需要进行参数配置的智能软件。 DEMOK 经过潜心研究,设计开发了一款最适合飞思卡尔智能车摄像头使用的上位机软件—DemokTool,该软件不仅可以显示黑白图像和彩色图像,更能通过灵 活得配置阀值,进行二值化图像显示,这为智能车摄像头的开发,提供了极大的 便捷,并提高了摄像头学习与开发的进度,是一款比不可少的软件。

5.2.2 匿名上位机

  • 匿名上位机的优点

  1.高效率:程序流程不断优化,收发效率高,协议解析速度快、UI更新速度快、波形刷新效率高。特别是V6版本以来,在多线程的同时,上位机将程序不同功能改为多进程模式,更加提升了匿名上位机的性能。

  2.高速波形:上位机有一项很重要的功能,就是对接收到的数据进行分析,那么画出不同数据的波形图进行观察分析就是最常用和有效的方法了。匿名上 位机提供超高速波形绘制功能,可以以每秒不低于1000hz的速度,实时将接收到的多个数据画出其波形图,一般的传感器采样、滤波、PID计算输入、输出等应用场景,1000hz的速度完全满足,不会丢掉采样数据。相比将数据保存至TF卡然后插到电脑进行读取的方法,实时高速波形显示将大大缩短数据分析时间。

  3.自定义数据:匿名上位机对飞控常用的数据已经做好了定义,比如各个传感器的原始值、姿态角、PWM输出量等等,但是在大家的开发过程中,这些是远远不够的。大家总是有自己的数据想要上传到上位机,并进行波形绘制,以便分析数据。匿名上位机为这样的应用场景提供了用户数据帧,可以讲uint8、int16、uint16等数据类型的变量发送至上位机,并可实现这些数据的实时波形绘制、数据存储为excel数据等功能,大大拓宽匿名上位机的应用范围。

  4.完善的协议:最开始匿名上位机的通信都是单向、开环的,比如发送一个传感器校准指令,上位机只管发送,而下位机是否收到正确的数据,上位机是不知道的。V6.5版本上位机具有完善的验证协议,上位机发送指令后,会等待下位机返回正确的验证信息,只有上位机收到正确验证信息后,表示命令发送成功,反之上位机会进行命令重发。同时验证逻辑非常简单,方便大家移植使用。

▲ 图5.2.1 匿名上位机界面

  当然与匿名上位机应当满足相应的串口通讯的协议,协议代码如下:

void ANO_DT_send_int16(short data1, short data2, short data3, short data4, short data5, short data6, short data7, short data8 /*,short data7, short data8, short .....可根据需要自行添加 */)
{uint8_t _cnt=0;data_to_send[_cnt++] = 0xAA;      //匿名协议帧头  0xAAAAdata_to_send[_cnt++] = 0xAA;data_to_send[_cnt++] = 0xF1;      //使用用户协议帧0xF1  data_to_send[_cnt++] = 16;        //8个short 长度 16个字节data_to_send[_cnt++]=BYTE1(data1);data_to_send[_cnt++]=BYTE0(data1);data_to_send[_cnt++]=BYTE1(data2);data_to_send[_cnt++]=BYTE0(data2);data_to_send[_cnt++]=BYTE1(data3);data_to_send[_cnt++]=BYTE0(data3);data_to_send[_cnt++]=BYTE1(data4);data_to_send[_cnt++]=BYTE0(data4);data_to_send[_cnt++]=BYTE1(data5);data_to_send[_cnt++]=BYTE0(data5);data_to_send[_cnt++]=BYTE1(data6);data_to_send[_cnt++]=BYTE0(data6);data_to_send[_cnt++]=BYTE1(data7);data_to_send[_cnt++]=BYTE0(data7);data_to_send[_cnt++]=BYTE1(data8);data_to_send[_cnt++]=BYTE0(data8);/*,short data7, short data8, short .....可根据需要自行添加 */uint8_t sum = 0;for(uint8_t i=0;i<_cnt;i++)sum += data_to_send[i];data_to_send[_cnt++]=sum;ANO_DT_Send_Data(data_to_send, _cnt);
}

第六章 结论


  智能车竞赛是一个多学科、综合性的比赛,其中设计涉及了控制、传感技术、电子信息、模式识别、汽车电子、机械等多个学科,在整个准备的过程中,我们不仅仅把所学的理论知识应用于实际,还自学了大量的新知识。不仅开拓了视野,使我们的动手能力、运用知识的能力、分析解决问题的能力也有了很大的提高。本文主要介绍了 MT9V034智能车的总体设计方案,包括机械结构的安装调整,硬件电路设计和软件的设计。在设计和制作车模的过程中,我们翻阅了大量的书籍和资料 ,并仔细阅读了多份技术报告。结合前人的经验我们也进行了创新,并且取得了一定的成果。而对于车模的设计要本着严谨的态度,综合考虑各种问题;在整个设计过程中我们不断发现问题,及时分析问题,克服种种困难最终完成了这辆车的制作。从机械结构的设计,到控制算法研究、程序编写,整个过程无不凝聚了我们辛勤的汗水。

  我们也有着不足和存在的问题。三岔路口无论我们怎么修改算法都完美出入环,这浪费了挺多的时间。圆环路线也不够完美,会有一些外飘。这些方面还需要改进。还有二维码的始停始终不能让其停的非常完美。

  随着智能车竞赛影响力不断扩大,参赛学校也越来越多,竞争也随之变得异常激烈,而要在这样的大赛中脱颖而出不仅仅需要激情,更需要毅力,只有全身心的投入才能获得回报。半年以来我们过着两点一线的生活,在放假时间里我们没日没夜的调试,除了吃饭睡觉就是做车,我们把实验室当成了我们自己的家。通过这次比赛,经历了那么多;我们收获不仅仅是学科上的知识,更是一份青春的记忆。每个人都有属于自己的青春故事。不管我们的青春是灰暗还是灿烂,那些生命里最初的记忆是定然忘却不了的,或许直至生命的最后一刻还依然铭刻在心。

参考文献


[1]童诗白.华成英.模拟电子技术基础 [M] .北京: 高等教育出版社,2001.
[2]阎石.数字电子技术基础 [M] .北京: 高等教育出版社,2000.
[3]谭浩强著.C程序设计.北京:清华大学出版社,2003.
[4] [1]臧杰,阎岩.汽车构造[M] .北京:机械工业出版社,2005.
[5]尹怡欣,陶永华.新型PID控制及其应用.北京:机械工业出版社,1998年.
[6]张男, 张迪洲, 李永科.智能车路径识别系统对虚线的预测拟合处理.
[7]邵贝贝.单片机嵌入式应用的在线开发方法 [M].北京.清华大学出版社.2004.
[8]卓晴,黄开胜,邵贝贝.学做智能车 [M].北京:北京航空航天大学出版社.2007.
[9]张文春.汽车理论 [M].北京.机械工业出版社.2005.
[10]夏克俭.数据结构及算法 [M] .北京:国防工业出版社, 2001.王晓明.电动机的单片
[11]机控制 [M] .北京:北京航空航天大学出版社. 2002.
[12]殷剑宏,吴开亚.图论及其算法 [M] .中国科学技术大学出版社,2003.
[13]蔡述庭.“飞思卡尔”杯智能汽车竞赛设计与实践 [M].北京:北京航空航天大学出版社. 2012.

附录A车模技术指标:

表A-1 车模主要技术指标表

  • 车模基本参数 长 300mm
  • 宽 185mm
  • 高 410mm
  • 重量(带电池) 2400g
  • 功耗 空载 10W
  • 带载 大于12W
  • 电路电容总容量 2200uF
  • 感器 编码器 2个
  • MT9V034摄像头 1个
  • 工字电感 4个
  • 光电管 1个
  • 除了车模原有的驱动电机、舵机之外伺服电机个数 1个
  • 赛道信息检测 视野范围 55cm*110cm
  • 检测精度 5mm
  • 频率 100Hz

附录D OpenART mini代码

import sensor, image, time, tf
from machine import PWM # 导入PWM
from machine import UART
from pyb import LEDpwm4 = PWM(2, 4, 400, 160)def colours_img_init():sensor.reset()sensor.set_pixformat(sensor.RGB565)sensor.set_framesize(sensor.QVGA)sensor.skip_frames(time=500)sensor.set_auto_gain(True)sensor.set_auto_whitebal(True)def gray_img_init():sensor.reset()sensor.set_pixformat(sensor.GRAYSCALE)sensor.set_framesize(sensor.QVGA)  # we run out of memory if the resolution is much bigger...#sensor.set_brightness(2000)  # 设置图像亮度 越大越亮sensor.set_windowing((140, 140))sensor.skip_frames(time=500)sensor.set_auto_gain(True)  # must turn this off to prevent image washout...sensor.set_auto_whitebal(True)  # must turn this off to prevent image washout...
uart = UART(1)
tag_num = -1
net_path_mobilenet = "mobilenet_num_1324.tflite"
net_path = "mobilenet_v1.tflite"labels=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]pwm2 = PWM(2, 2, 125, 0)led = LED(4)odd = bytearray([0x03])
even = bytearray([0x04])waitMore = bytearray([0x05])err_rate = 0.3PWM(2, 4, 400, 160)
colours_img_init()
PWM(2, 4, 400, 160)while(True):b = ""if uart.any():a = uart.readline()#print(a)b = str(a)[2]if(b == "1"):colours_img_init()time.sleep(200)tag_num = -1for _ in range(0,3):img = sensor.snapshot()img = img.histeq(adaptive=True, clip_limit=4)img = img.gamma_corr(1.5)for tag in img.find_apriltags(families=image.TAG25H9):  # 如果没有给出家族,默认TAG36H11。tag_num = tag.id()if tag_num >-1 and tag_num < 10:breakrot_err = 0.3img1 = -1for _ in range(0,10):uart.write(waitMore)if tag_num >-1 and tag_num < 10:if tag_num%2==0:# 往左servo_right = 220else:# 往右servo_right = 80PWM(2, 4, 400, servo_right)time.sleep(200)target = -1for _ in range(0, 5):img = sensor.snapshot()img = img.histeq(adaptive=True, clip_limit=4)  # 越高,越接近标准自适应直方图均衡,并产生巨大的对比度波动。img = img.gamma_corr(1.5)exp_val = img.width()/2for r in img.find_rects(threshold=30000):  # 在图像中搜索矩形if r.w()/r.h() > 1.7 or r.h()/r.w() > 1.7:continue#img.draw_rectangle(r.rect())now_val = (r.corners()[0][0]+r.corners()[1][0])/2now_err = exp_val - now_val#print(now_err)servo_right = servo_right + int(now_err*err_rate)if servo_right > 255:servo_right = 255if servo_right < 46:servo_right = 46PWM(2, 4, 400, servo_right)time.sleep(100)img1 = img.copy(r.rect())if img1 != -1:for obj in tf.classify(net_path_mobilenet, img1, min_scale=1.0, scale_mul=0.5, x_overlap=0.0,y_overlap=0.0):sorted_list = sorted(zip(labels, obj.output()), key=lambda x: x[1], reverse=True)if sorted_list[0][0] < 5:target = 0else :target = 1if target == 1:pwm2.duty(127)time.sleep(1200)pwm2.duty(0)PWM(2, 4, 400, 160)time.sleep(2000)if(b == "2"):gray_img_init()led.on()oddNum = 0evenNum = 0num=0while(True):#for _ in range(0,100):num = num + 1img = sensor.snapshot()img = img.gamma_corr(1.5)img = img.histeq(adaptive=True, clip_limit=4)for r in img.find_rects(threshold = 28000):             # 在图像中搜索矩形if r.w()/r.h() > 1.7 or r.h()/r.w() > 1.7:continue#img.draw_rectangle(r.rect(), color = (255, 255, 255))   # 绘制矩形外框,便于在IDE上查看识别到的矩形位置img1 = img.copy(r.rect()) # 拷贝矩形框内的图像for obj in tf.classify(net_path, img1, min_scale=1.0, scale_mul=0.5, x_overlap=0.0, y_overlap=0.0):sorted_list = sorted(zip(labels, obj.output()), key = lambda x: x[1], reverse = True)if sorted_list[0][0]%2 == 0 :evenNum=evenNum+1else:oddNum=oddNum+1if num > 250 or evenNum > oddNum + 3 or oddNum > evenNum + 3 :breakif oddNum!=evenNum:if oddNum>evenNum :for _ in range(0,10):uart.write(odd)#print(odd)else:for _ in range(0,10):uart.write(even)#print(even)led.off()附录E K60主要程序
void PIT0_IRQHandler()
{PIT_Flag_Clear(PIT0);       //清中断标志位/* 用户添加所需代码 */  g_ucFlag5ms = 1;get_maichong();//获取电机转速if(K60_jishi_flag==0)K60_jishi++;if(K60_jishi==200)//起步延时1秒Go=1;//小车前进if(K60_jishi>=1900){K60_jishi_flag=1;K60_jishi=1000;}if(K60_jishi_flag==1)star_line_judg();erweima();lkcongzhongjiansaomiao();   //扫描函数hexian();  //画中线if(star_lineflag==1&&erweima_saodao!=1&&bai_jishu>1000&&podao_flag!=1){K60_yanshi++;if(a==1&&K60_yanshi>30){K60_yanshi=0;star_lineflag=0;a=2;jishu=1;}if(K60_yanshi>100&&(a==0))//||a==1{K60_yanshi=0;star_lineflag=0;a=1;jishu=1;}}if(star_lineflag==1){K60_yanshi++;if(K60_yanshi>2000&&tingche==0)tingche=1,K60_yanshi=0,star_lineflag=0,out_huandao=0,erweima_flag = 0,  UART_PutStr(UART4, "8");}if(star_lineflag==1&&tingche==1){K60_yanshi++;if(K60_yanshi>2000&&tingche==0)tingche=1,K60_yanshi=0,star_lineflag=0,out_huandao=0,erweima_flag = 0,  UART_PutStr(UART4, "8");}if(!star_lineflag&&Go){lukuangudge();//路况判断}  else if(K60_yanshi>50&&tingche)//检测到起跑线{mubiao_speed=0;}else if(a==2)//检测到起跑线&&TC264_yanshi>10&&TC264_yanshi>20{tingche_flag_1++;mubiao_speed=0;}//MotorCtrl(2000, 2000); //电机PWM固定功率输出(开环)       東西都被注釋了DSYJ_dianji_PID(mubiao_speed);//电机速度控制(闭环)InductorNormal();pianchachuli();   //偏差处理
//ANO_DT_send_int16( speed_a, speed_b,dut,0,0,0,0,0);}void main(void)
{   int16_t encnt = 0;char txt[32];PLL_Init(PLL180);         //初始化PLL为180M /* 开启systick定时器 */systime.init();/* 设置中断优先级组  0: 0个抢占优先级16位个子优先级 * 1: 2个抢占优先级 8个子优先级 2: 4个抢占优先级 4个子优先级 * 3: 8个抢占优先级 2个子优先级 4: 16个抢占优先级 0个子优先级*//* 配置优先级组 3: 16个抢占优先级 */NVIC_SetPriorityGrouping(NVIC_Group3);     LED_Init();TFTSPI_Init(1);                //TFT1.8初始化  0:横屏显示  1:竖屏显示  TFTSPI_CLS(u16BLUE);           //清屏
//  Uart4_DMA_Init();UART_Init(UART4, 115200);      //串口初始化/* 优先级配置 抢占优先级1  子优先级2   越小优先级越高  抢占优先级可打断别的中断 */NVIC_SetPriority(UART4_RX_TX_IRQn,NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1,2));NVIC_EnableIRQ(UART4_RX_TX_IRQn);                    //使能UART4_RX_TX_IRQn的中断KEY_Init();GPIO_PinInit(PTC2, GPO, 0);  GPIO_PinInit(PTC4, GPO, 0);/* 电机PWM PID参数 初始化 */FTM_PwmInit(FTM0, FTM_CH0, 12000, 0);
//    FTM_PwmInit(FTM0, FTM_CH1, 12000, 0);FTM_PwmInit(FTM0, FTM_CH2, 12000, 0);
//    FTM_PwmInit(FTM0, FTM_CH3, 12000, 0);/* 舵机PWM初始化 */CMT_PwmInit(50, 0);/* 电机参数初始化 */dianji_canshu_init();/* 编码器接口初始化 */EncInit();  ADC_Init(ADC0);ADC_Init(ADC1);//LPTMR_Delayms(500); // systime.delay_ms(500); CAMERA_Init();                //摄像头初始化/* 摄像头初始化结束 */PIT_Init(PIT0,5);/* 优先级配置 抢占优先级7  子抢占优先级可打断别的中断 */NVIC_SetPriority(PIT0_IRQn,NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 7, 0));    NVIC_EnableIRQ(PIT0_IRQn);EnableInterrupts;while(1){/* 获取一帧图像  进行图像处理 */ImageProcess();sprintf(txt,"%02d,%02d,%03d", tingche,star_lineflag,K60_yanshi);TFTSPI_P8X8Str(0,8,(u8*)txt,u16RED,u16BLUE);sprintf(txt,"%03d,%03d,%03d,%01d", duandian,Wide,y_duandian_zuo+y_duandian_you,Chalu);TFTSPI_P8X8Str(0,9,(u8*)txt,u16RED,u16BLUE);  sprintf(txt,"%03d,%03d,%02d",bai_jishu,shizi_flag,Chalu_number);TFTSPI_P8X8Str(0,11,(u8*)txt,u16RED,u16BLUE);     sprintf(txt,"%04d,%03d,%03d,%03d",bai_jishu,erweima_saodao,chalu_jishu,chuankou,wait_num);TFTSPI_P8X8Str(0,13,(u8*)txt,u16RED,u16BLUE);   sprintf(txt,"%04d,%04d,%02d",left_heixian[20],left_heixian[40],youhuandaoli_count);TFTSPI_P8X8Str(0,17,(u8*)txt,u16RED,u16BLUE);sprintf(txt,"%02d,%02d,%02d,%02d,%03d",Lleft1,Lleft2,Lright1,Lright2,saodao_jishi);TFTSPI_P8X8Str(0,19,(u8*)txt,u16RED,u16BLUE);  }
}


● 相关图表链接:

  • 图1.1 系统框图
  • 图1.2整车布局图
  • 图2.1.1
  • 图2.5.1 编码器安装
  • 图2.6.1
  • 图3.1.1 母板PCB
  • 图3.1.2 母板PCB
  • 图3.2.1 电磁传感器PCB
  • 图3.2.1 电磁传感器PCB
  • 图3.3.2 驱动模块PCB
  • 图4.1.1 入环的位置
  • 图4.1.2 PID工作原理
  • 图4.1.3
  • 图4.1.6
  • 图4.1.7
  • 图5.2.1 匿名上位机界面

智能车竞赛技术报告 | 智能车视觉 - 三江学院 - 识别不别相关推荐

  1. 智能车竞赛技术报告 | 智能车视觉 - 山东大学(威海) - 山魂五队

    简 介: 本文详细介绍了山东大学(威海)山魂五队在第十六届"恩智浦"杯全国大学生智能汽车竞赛智能视觉组中的系统方案,包括硬件电路设计.机械结构设计.巡线算法以及识别算法的设计等.本 ...

  2. 智能车竞赛技术报告 | 智能车视觉 - 温州大学- 春华秋实

    简 介: 目前自动驾驶话题非常火热,近几年华为.特斯拉.谷歌.百度等大型互联网公司都在进行关于这方面的研究.本文设计了基于恩智浦 RT1064芯片的智能小车,以 keil5为开发环境,利用基于 MT9 ...

  3. 智能车竞赛技术报告 | 智能车视觉 - 西南科技大学 - 西科二队

    简 介: 本车以恩智浦公司的 32 位单片机MI.MXRT1064DV高内核处理速度核心控制器,可提供卓越的CPU性能和最佳实时响应.以MT9V034摄像头为核心传感器,通过摄像头采集道路图像信息送入 ...

  4. 智能车竞赛技术报告 | 智能车视觉 - 新余学院 - 开放艺术队

    简 介: 该智能车设计以 32位的 RT1064微控制器作为控制单元,通过 MT9V034数字摄像头传感器采集赛道信息,将采集到的图像进行处理:通过 OpenARTmini模块部署神经网络识别图像:另 ...

  5. 智能车竞赛技术报告 | 智能车视觉 - 中南林业科技大学 - 弃车人队

    简 介: 本文根据第十六届智能车大赛的要求,经过在实验室的不断调试,研究并设出了拥有自主循迹功能及数字和物种识别功能的以摄像头传感器为主导的 AGV实体.在循迹算法上,为了加快小车对赛道信息的采集速度 ...

  6. 智能车竞赛技术报告 | 智能视觉组 - 北京科技大学智能视觉组

    简 介: 本文设计的智能车系统以 为核心控制单元,通过CMOS摄像头检测赛道信息,使用数字摄像头采集赛道的灰度图,通过动态阈值算法生成二值化图像数组,提取黑色引导线,用于赛道识别:通过光电编码器检测模 ...

  7. 智能车竞赛技术报告 | 智能车视觉 - 南京邮电大学 - 栅库砸车跑路队

    学 校:南京邮电大学  队伍名称:删库砸车跑路队 参赛队员:刘乐      孙锐      甘翠      带队教师:江兵      刘烨      第一章 方案设计   本章主要介绍智能汽车系统总体 ...

  8. 智能车竞赛技术报告 | 智能车视觉 - 青岛工学院 - 青工战斗

    简 介: 本文设计的智能车系统以LPC54606J512微控制器为核心控制单元,基于MT9V034摄像头的图像采样获取赛道图像信息,提取赛道中心线,计算出小车与黑线间的位置偏差,采用PD方式对舵机转向 ...

  9. 智能车竞赛技术报告 | 智能车视觉 - 上海大学 - 猫耳麻花

    简 介: 本文设计的智能车系统以NXP RT1064微控制器为核心控制单元,通过车体前方的摄像头检测赛道信息:通过齿轮编码器检测模型车的实时速度,使用PID 控制算法调节驱动左右电机的转速,PD算法控 ...

最新文章

  1. 请求处理传入原生的API || 请求处理CharacterEncodingFilter解决乱码问题
  2. 2021-04-07 In literature VS In the literature
  3. 【Angular】MVVM
  4. 『C#基础』调用CMD的一个小工具
  5. Linux下如何避免误操作执行 rm
  6. 文本识别新王者CharNet:卷积字符网络
  7. redis-cli 命令总结
  8. 老李推荐:第8章2节《MonkeyRunner源码剖析》MonkeyRunner启动运行过程-解析处理命令行参数...
  9. Android抓包方法(一) 之Fiddler代理
  10. 初识Jasima-Jasima中的事件和消息
  11. JavaScript基础-前端开发
  12. ansible常见模块
  13. 1分钟搞定 OneNote自己账号扩容到15G永久免费空间
  14. 快速实现大量数据匹配来电号码归属
  15. zoj 1101 Gamblers 为什么总是WA?
  16. vue项目用qrcodejs2生成多个二维码的实现
  17. 网站托管收费是否有标准
  18. 《棒球殿堂》:棒球联盟LEAGUE·东北乐天金鹫
  19. Exception in thread “main“ java.lang.ArrayStoreException解决方案(记录一下)
  20. 《C++大学教程》学习笔记(九)

热门文章

  1. 基于微软企业库的AOP组件(含源码)
  2. 第二十二回  基础才是重中之重~ThreadStatic静态字段在每个线程里的唯一性
  3. Struts2获取request三种方法
  4. Android开发环境搭建与起步--太详细了,一步一步图文教你
  5. 对话IT:火狐4.0正式版发布 庆功会上听宫博士“酒后真言”
  6. ACE线程管理机制-并发控制(4)
  7. 五大常用算法之二:动态规划算法
  8. [数据结构] 时间复杂度计算
  9. 如何解决KEIL 5 编KEIL4同RTX系统的project解
  10. 解决Linux中文乱码