第十八届智能车竞赛技术报告 - 负压电磁 - 哈尔滨工业大学

简 介: 本文详细介绍了哈尔滨工业大学“紫丁香三队”在第十八届全国大学生智能汽车竞赛负压电磁组中的系统方案。本次比赛采用自制车模,以宏晶公司生产的STC32为核心控制器,要求智能车根据电磁信息进行循迹与元素识别,以最快速度完成整个比赛,智能车采用高精度电感对赛道信息检测,根据偏差处理控制舵机循迹;通过编码器检测智能车的实时速度,利用陀螺仪完成小车出入库,通过TOF测距模块实时测距辅助判断坡道等元素;利用负压风扇提供向下的抓地力来提高车模上限;使用PID控制算法调节电机的转速和舵机的打角,实现智能车在运动过程中速度和方向的闭环控制;为了提高模型车的速度和稳定性,使用上位机、按键、OLED模块等调试工具,进行了大量硬件与软件测试。实验结果表明,该系统设计方案确实可行。

关键词 STC32电磁信号采集与处理PID负压

01   论


1.1 智能车研究背景

  智能车是一种高新技术密集型的新型汽车,以汽车电子为背景涵盖控制、模式识别、传感技术、电子、电气、计算机、机械等多科学的科技创意性设计,一般主要由路径识别、速度采集、角度控制及车速控制等模块组成。可实现自动驾驶、自动变速及自动识别道路等功能,是现代电子产业发展中一项重要的组成部分。

  在我国,教育部为了加强大学生实践、创新能力和团队合作精神的培养,委托教育部高等学校自动化专业教学指导分委员会主办了每年一度的全国大学生智能汽车竞赛。全国大学生智能汽车竞赛是在竞赛组委会提供的统一汽车模型平台上,使用飞思卡尔半导体公司的8位、16位、32位微控制器作为核心控制模块,通过设计道路识别传感器和电机驱动电路、编写相应软件及装配模型车,制作一个能够自主识别道路的模型汽车,按照规定路线(路线赛前未知)行进,以完成时间最短者为优胜。

1.2 方案总体介绍

  比赛跑道为表面白色,两边有连续黑线作为引导线,黑线宽为25mm。车模通过采集赛道图像进行路径检测比赛规则限定了跑道宽道区宽度40cm,拐角最小半径50cm,并且规定了各赛道标志的具体指标。参赛队员的目标是使智能车按照规则以最短时间完成单圈赛道。

  根据竞赛规则相关规定,本智能车系统采用自制车模,以STC32单片机作为核心控制器,在IAR开发环境中进行软件开发。通过电感采集电磁信息,经过整流检波后传入STC32微控制器,进行进一步处理获得主要的赛道信息;通过编码器来检测车速,并通过单片机进行正交解码进行脉冲计算获得速度和路程;转向舵机采用PD控制;驱动电机采用PI控制,通过PWM控制驱动电路调整电机的功率;而车速的目标值由默认值、运行安全方案和基于图像处理的优化策略进行综合控制;本次比赛中元素繁多,为了更好得采集赛道元素的信息增加传感器是必然的,我们选择红外模块进行测距,利用陀螺仪获取车身姿态实现出入车库,提高判断稳定性。
  根据比赛的基本要求,我们设计了系统总体思路如图1.1所示。

▲ 图1.2.1 系统结构图

▲ 图1.2.1 系统结构图

1.3 本文结构

  本文共分为六章。第一章主要是介绍了比赛的背景及智能车系统总体方案的介绍;第二章从智能车系统的机械结构出发,详细阐述了智能车系统各部分机械结构的安装和调整;第三章重点介绍了系统中所涉及的硬件设计方案和原理;第四章是介绍了智能系统的软件算法包括图像处理以及电机舵机的控制策略;第五章对调试过程中的一些手段进行了讲解;第六章则是对智能车一些物理参数进行了简单的汇报。

02 械结构调整


  能车的核心是控制策略和算法,但是,机械结构也是限制赛车速度的巨大瓶颈,如果一辆赛车的程序架构很好,但是机械部分做的不好的话,其速度也会被大大的限制。即当车速较高的时候,车模有明显的甩尾和侧滑现象,此时对车的机械结构要求很高。除了对车身姿态的影响外,机械性能影响车的加减速响应速度,运行的对称性和稳定性等,因此,我们在不违反规则的情况下对小车进行了多方面的改造以使小车具有良好的运行性能。

2.1 整车布局

  我们组采用的是自制车模,对称性好,尺寸为2901184196mm,轮胎尺寸为2961mm,通过对轮胎合适地填充海绵和进行软化处理,具有极好的减震性和耐磨性。驱动电机为RS-380,7.2V的电机功率可达19.25W,额定功率达到0.016kW,额定电压7.2V,额定电流0.5A,额定转速16200rpm,额定转矩可达10.9Nm,外形储存29.237.8mm。伺服电机为R12S舵机,6V电压时扭力可达6.5kgcm,动作速度快,车模整体质量较轻。智能车的外形大致如图2.1。

(1) 车模底盘降低,用来降低重心。
(2) 舵机采用竖直姿态,方便控制。
(3) 对前轮倾角进行调节,保证车直线行驶的稳定性。
(4) 调整电池位置使中心尽量在车体中心
(5) 用轻便坚固的碳杆作为电磁传感器的支撑材料。

▲ 图2.1.1 智能车外形图

▲ 图2.1.1 智能车外形图

2.2 电池选型与安装

  原装车模使用的是NiCd电池,由于其体积大,质量大,在前期的使用过程中造成车在速度快产生甩尾和侧滑现象,此外NiCd电池质量分布不均,相当于增加车身不对称因素。本届比赛支持使用格式航模锂电池的进行供电,放电能力强可以轻松带动两个380电机,通过合理选择电池位置以调节车体重心,使得整个车非常轻盈,加减速响应快。俯视图如图2.2所示。

▲ 图2.2.1 俯视图

▲ 图2.2.1 俯视图

2.3 转向舵机安装

  舵机转向是整个控制系统中延迟较大的一个环节,为了减小此时间常数,通过改变舵机的安装位置可以提高舵机的响应速度。通过分析舵机控制转向轮转向的原理可以发现,在相同的舵机转向条件下,转向连杆在舵机一端的连接点离舵机轴心距离越远,转向轮转向变化越快。这相当于增大力臂长度,提高线速度。

  舵机安装方式有立式和卧式两种,比较两种方式发现,立式安装效果更好。 舵机安装时要保证左右对称,这样可以保证舵机左右转向时力臂相等且处于最 大范围,提升了舵机的响应速度。经理论分析,功率等于速度与扭矩的乘积,加大转向速度必然减少输出扭矩,扭矩过小会造成迟钝,所以安装时必须考虑到转 向机构的响应速度与舵机扭矩之间的关系,获得最佳转向效果。经过实验,我们的舵机安装如图2.3所示。

▲ 图2.3.1 转向舵机安装

▲ 图2.3.1 转向舵机安装

2.4 前轮倾角调节

  为了使汽车直线行驶稳定,转向轻便,转向后能自动回正,减少轮胎和转向系零件的磨损等,在转向轮、转向节和前轴之间须形成一定的相对安装位置,叫车轮定位。本系统所采用的智能车通过四条轮胎与地面接触,两个后轮同轴受到限位,无法调整,与智能车的前进方向保持平行,因此要改变智能车与地面的接触方式,调试出利于车转向、直线的四轮定位,只能通过调整前轮倾角各定位参数来实现。它的安装位置由主销内倾、主销后倾、前轮外倾和前轮前束四个项目决定。

2.4.1 主销后倾

  主销后倾如图2.4 所示,是指在纵向平面内主销轴线与地面垂直线之间的夹角,向前为负,向后为正。它在车辆转弯时会产生与车轮偏转方向相反的回正力矩,使车轮自动恢复到原来的中间位置上。所以,主销后倾角越大,车速越高,前轮自动回正的能力就越强,模型车通过增减黄色垫片的数量来改变 主销后倾角。

▲ 图2.4.1 主销后倾示意图

▲ 图2.4.1 主销后倾示意图

  由于竞赛所用的转向舵机力矩不大,过大的主销后倾角会使转向变得沉重,转弯迟滞。所以我们修改主销后倾大致1~2°如图2.5所示。

▲ 图2.4.2 修改后的主销后倾示意图

▲ 图2.4.2 修改后的主销后倾示意图

2.4.2 主销内倾

  主销内倾如图2.6所示。是将主销(即转向轴线)的上端向内倾斜。从车前方看去,主销轴线与通过前轮中心的垂线之间形成一个夹角,即主销内倾角。轮胎调整为倾斜以后直线行走的时候是轮胎内侧着地,而当过弯的时候,由于惯性车体会要向弯道外侧倾斜,而这时候的外侧轮胎如果倾斜角度事先调整得当则正好可以胎面着地,从而使车辆在弯道获得最佳抓地力。使车轮转向后能及时自动回正和转向轻便。经过多次尝试后我们主销内倾如图2.7所示。

▲ 图2.6 主销内倾示意图

▲ 图2.6 主销内倾示意图

2.4.3 前轮前束

  车轮前束如图2.7所示。是指两轮之间的后距离数值与前距离数值之差,也指前轮中心线与纵向中心线的夹角。从上往下看,两个车轮指向的方向在前端指向内称为车轮前束,指向外的则称为车轮后束。前轮前束的作用是保证汽车的行驶性能,减少轮胎的磨损。前轮在滚动时,其惯性力自然将轮胎向内偏斜,如果前束适当,轮胎滚动时的偏斜方向就会抵消,轮胎内外侧磨损的现象会减少。

▲ 图2.7 车轮前束示意图

▲ 图2.7 车轮前束示意图

2.5 底盘高度调节

  降低车模底盘可以降低重心,车模重心低可以使车模运行更加稳定,获得 更好的转弯特性。所以,在保证车模可以通过灯盘的情况下,底盘尽可能的降低,可以使车更加快速稳定。对于C车模,修改前轮滚动的轴心和车模底盘的高度差,可以修改车模底盘的高度,所以通过在前轮固定处垫垫圈来降低车模底盘高度,如图2.8所示。

▲ 图2.8 底盘高度调节

▲ 图2.8 底盘高度调节

03 件电路设计


  件是基础,只有一个良好、稳定的硬件环境才能保证车能平稳快速的行驶。我们在整个系统设计过程中严格按照规范进行。本着可靠、高效的原则,在满足各个要求的情况下,尽量使设计的电路简单,PCB的效果简洁。

3.1 单片机系统设计

  单片机最小系统是智能车系统的核心控制部件。我们采用了STC32芯片。原理图如图3.1所示:

▲ 图3.1 单片机系统原理图

▲ 图3.1 单片机系统原理图

3.2 电源模块设计

  电源模块为系统其他各个模块提供所需要的电源。设计中,除了需要考虑电压范围和电流容量等基本参数之外,还要在电源转换效率、降低噪声、防止干扰和电路简单等方面进行优化。可靠的电源方案是整个硬件电路稳定可靠运行的基础。

  全部硬件电路的电源由格式航模锂电池提供(额定电压11.1V,满电电压12.6V)。由于电路中的不同电路模块所需要的工作电压和电流容量各不相同,因此电源模块应该包含多个稳压电路,将充电电池电压转换成各个模块所需要的电压。为满足需要,本车模上存在6种供电电压:

(1) 智能车使用锂电池供电,选择带均衡功能的保护板可以同时给两节电池充电,正常使用时电压在12V。可直接用于电机、舵机供电。

(2) 使用稳压芯片MIC29302 输出电压6V,用于舵机的供电。原理图如图3.2所示。

▲ 图3.2 6V稳压电路

▲ 图3.2 6V稳压电路

(3) 使用稳压芯片AMS1117-3.3V 输出电压3.3V,用于OLED、蜂鸣器、陀螺仪、编码器、单片机等供电。

▲ 图3.3 3.3V稳压电路

▲ 图3.3 3.3V稳压电路

(4) 使用稳压芯片LM2596 输出电压5V.

▲ 图3.4 5V稳压电路

▲ 图3.4 5V稳压电路

(5) 使用稳压芯片LM2663来获得-5V电压。运放工作需要正负5V供电。

▲ 图3.5 -5V 稳压电路

▲ 图3.5 -5V 稳压电路

3.3 电机驱动电路

  在栅极驱动芯片选择方面,我们选择IR2104芯片,IR2104芯片可以驱动高端和低端两个沟道MOSFET,提供较大的驱动电流,并有硬件死区,防止同桥臂导通。使用两片IR2104可以构成一个MOS管全桥驱动电路,如图3.7所示。

▲ 图3.7 左侧电机驱动原理图

▲ 图3.7 左侧电机驱动原理图

3.4 电磁运放电路

  采用简单的同向比例放大电路,电位计可用来调节放大倍数,通过对电磁信号进行放大和检波处理和可以直接供单片机的AD口读取。运放芯片使用性价比较高的OPA4171,10M的通带带宽足以满足20kHz的电感采集需求。

▲ 图3.8 电磁信号检波放大原理图

▲ 图3.8 电磁信号检波放大原理图

3.5 电流环电路

  电流环指的是电流反馈系统。一般指的是将输出电流采用负反馈的方式接入处理环节的方法,来提高电流的稳定性。我们选用电流环,通过电流内环与速度外环相结合来提高系统的性能,有利于速度闭环的实现。电路如图3.9所示。

▲ 图3.9 电流环原理图

▲ 图3.9 电流环原理图

3.6 传感器的选择

3.6.1 电磁杆

  车模需在铺设电磁线的轨道上行驶,导线内通有20KHZ的交变电流,从而在电磁线周围产生交变电场,电磁杆(电感电容)组成的LC振荡回路能够感应磁场,产生的感应电动势与电感与中心导线的距离有关。单片机采集所有电感传感器的AD值,通过偏差计算程序能够计算出小车与赛道中线的偏差,通过控制小车的姿态和速度,保证小车平稳的在赛道上自动行驶。以下为团队自行设计的电磁杆。

▲ 3.10 设计完成的电磁杆

▲ 3.10 设计完成的电磁杆

3.6.2 编码器

  光电编码器是一种通过光电转换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器, 这也是目前应用最多的测速传感器之一。其获取信息准确、精度高、应用简单。

  采用增量式1024线光电编码器,其供电电压为3.3V或5V,输出为小幅值的正弦信号。为了将此信号放大整形,设计了信号调理电路,其基本原理是使用一个运放做成比较器电路,调节参考电压,使输出变为0V-3.3V的方波信号,送入单片机进行运算。

3.6.3 TOF测距模块

  TOF测距基于时间飞行原理。具体为产品周期性的向外发出近红外光调制波,调制波遇物体后反射。产品通过测量调制波往返相位差,得到飞行时间,再计算出产品与被测目标之间的相对距离。

  其供电电压为5V,平均电流0.12A,峰值电流可达0.8A,对电源性能要求较高,平均功率低于0.6W。

▲ 3.11 TOF测距模块

▲ 3.11 TOF测距模块

3.7 主驱一体板

  在本系统中我们的电路板的制作主要思想是分立工作、追求简洁,便于电路板的调试和安装等工作。主控由电源稳压电路,OLED屏与按键交互电路,单片机系统以及各个传感器接口等组成。实物图如图3.12所示

▲ 3.12 主驱一体实物图

▲ 3.12 主驱一体实物图

3.8 运放板

  使用运放板和电磁杆相连,电位器可用来调节放大倍数,通过对电磁信号进行放大和检波处理和可以直接供单片机的AD口读取。

▲ 3.13

▲ 3.13

3.9 无刷风扇驱动板

  使用FD6288Q作为驱动的驱动芯片,MOS管为TPN2R703NL,对无刷电机的三相进行控制,达到负压风扇驱动的目的。TPN2R703NL为高效直流-直流转换器,开关稳压器,高速切换。

▲ 3.14 无刷驱动板实物图

▲ 3.14 无刷驱动板实物图

04 件控制设计


  效的软件程序是智能车高速平稳自动寻线的基础。我们设计的系统采用CMOS摄像头进行赛道识别,图像采集及处理是整个软件的核心内容。在智能车的转向和速度控制方面,我们使用PID控制算法,使智能车能够稳定快速在赛道中循迹并完成比赛。

4.1 程序的运行

4.1.1 总流程

  开机后,对所有硬件进行初始化,完成之后,PIT定时中断对电感采回来的数值进行分析控制。正式发车起跑后定时的采集感应得到的电压值,第一排的水平放置的传感器数值通过差比和的计算公式来得到导线与车子正中间的偏差(以下简称中心偏差),再通过PID算法将和采集到的中心偏差计算得出返回值,将计算得到的返回值输入方向环控制中,以此控制舵机,这样就完成了赛道上的基础循迹部分。同时根据赛道元素的不同列出状态机,单片机在查表获得当前车处于哪一种状态,根据状态机的规则给定电机的目标转速和一些特殊设定的舵机打角大小。在获取到目标转速后,通过速度环控制器迅速稳定的控制电机达到目标转速。

4.1.2 循迹子程序设计

  路径识别包括对传感器的控制以及接受信号的处理。对传感器的控制就是定时采集电感数值,对采样结果的分析与判断。

  我们利用PIT定时中断,中断处理中主要是对AD值进行一个连续序列的转换,并将数值进行存储。对数据的有效性判断比较简单,当传感器采回来的数值低于某一个特定阈值时直接判断为无效信息。

  根据毕奥萨伐尔定理可知,如果赛道的电流不发生变化时,电感的感应电压通过后级电路后产生的直流电平正比于sinθ/(h²+l²),其中h为传感器距离导线的竖直距离,1为传感器距离导线的水平距离,θ为工字电感与导线的夹角。代入水平方向放置的四个电感数值就可以得到L和θ的具体数值。

  倾斜放置的电感只是感性的估算。我们假象一下,比赛的赛道全部都是直道,那么当车身平行于导线时,第一排电感检测到的数值具有一定对称性,而当赛道前面出现弯道时第一排电感检测到的数值上会有较大的差别,将这个差值按照前面得到的θ进行软件放大得到一个前方赛道的变化率λ。

  对于赛道元素的判断,我们经过不断地尝试与改进,最终确定了一套方案,包括通过两个放置在前瞻第二排的两颗与竖直方向夹角为45°的倾斜电感的变化趋势来判断环岛元素,通过干簧管检测车库旁边的磁铁来判断车库,通过我们的实践发现,车辆对元素的判断和完成,很大程度上取决于车身的状态,如果车子状态稳定性出现了问题,那么对元素的识别判断和元素内的行驶轨迹都会有很大的影响,我们认为,这一次的赛道规则难度重点在于如何让元素的识别更加稳定。再加上多变的车辆状态,赛道陌生的赛场环境等,这些种种因素,更加考验小车程序的稳定性。

4.1.3 舵机打角子程序设计

  关于舵机打角是建立在赛道分析的基础上的。上面得到的L和λ在接下来的控制中起到到关键的作用。我们分析了一下数据然后给出了一个经验公式:PWMDTY PRE=n_L+D_λ。其中参数系数是结合长期的调试结果的来的。其中二次项的系数越大贴黑线就越严格,一次项系数越大前瞻性就越高。但是一次项中的人计算的结果极其不精确,所以如果这一项占得比例太大会导致PWMDTY PRE数值的严重抖动。

4.1.4 速度控制子程序设计

  速度控制部分是智能车除了舵机控制之外最为核心的内容。一个好的速度控制就是能十分准确的给出目标速度,电机对目标速度响应迅速,系统在干扰下速度依然稳定。

  第一步要求有合理的速度决策。我们最终采用的速度决策方法是一个简单的分段两数。将赛道分为直道,小半径弯道,大半径弯道,丢失路线。而且这些速度可以根据赛道的具体情况通过按键在比赛准各时设定,其次差速的决策也是非常重要的,合理的差速能使得过弯更加流畅,速度更快,行驶的姿态也更好。

  速度给定了之后执行也大有学问。直接列出速度和占空比的关系是一种十分不稳定的做法。这种做法受电池电量影响严重,而且只能适应某一种摩擦力的赛道。所以我们决定根据编码器反馈回来的数值进行换算。当编码器反馈回来的速度没有达到目标速度,那么正转占空比自加,反之则自滅。只要调节自加和自减的步进就能很好的对速度进行控制。

4.2 出入车库策略

4.2.1 入车库位置判定

  我们小队通过图象识别来判定车库位置,主要通过对斑马线最低行的判断来限定开始入库时车的位置,经过我们的尝试,这种判断方法在低速时很稳定,对入库时机把握良好,速度越快,对应入库时机会有所偏差。

4.2.1 出入车库方案

  对于出库,只需要简单的给出舵机一个打角,并且利用陀螺仪记角度,便可以实现稳定的出库,但是对于入库来说,则需要图象与控制相配合,由图象确定入库实际,再通过舵机给出一定打角实现入库。

4.3 PID控制算法介绍

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

  PID控制器是一种线性控制器,它根据给定值与实际输出值构成控制偏差。将偏差的比例§、积分(I)和微分(D)通过线性组合构成控制量,对被控对象进行控制,故称PID控制器,原理框图如图4.19所示。

▲ 4.19 PID 控制原理图

▲ 4.19 PID 控制原理图

4.3.3 PID参数整定

  运用PID控制的关键是调整KP、KI、KD三个参数,即参数整定。PID参数的整定方法有两大类:一是理论计算整定法。它主要是依据系统的数学模型,经过理论计算确定控制器参数;二是工程整定方法,它主要依赖工程经验,直接在控制系统的试验中进行,且方法简单、易于掌握,在工程实际中被广泛采用。由于智能车系统是机电高耦合的分布式系统,并且要考虑赛道的具体环境,要建立精确的智能车运动控制数学模型有一定难度,而且我们对车身机械结构经常进行修正,模型参数变化较为频繁,理论计算整定法可操作性不强,最终我们采用了工程整定方法。此外,我们先后实验了几种动态改变PID参数的控制方法。

4.4 转向舵机的PD控制算法

  对于舵机的闭环控制,我们采用了位置式PD控制算法,根据往届的技术资料和实际测试,将每场图像中线上部分中点加权平均值与舵机PD参考角度值构成非线性关系。

  在较低速(2m/s以下)试验时,在偏离黑线很大的某个范围,将Kp直接置100%,在偏离黑线较少的某个范围,将Kp值减小为原来的一半。取得的实际效果在弯道较多、直道较短的赛道上,车子转弯流畅,直道也能基本保持直线加速,车身左右抖动较小。

  在提高车速至高速(2.5m/s以上)时,我们发现车身在直道上特别是长直道上时,车身左右震荡比较严重,究其原因,硬件上,我们认为首先是轮轴本身的松动并且转向机构左右转向性能可能存在不对称性,设计有待改进,软件上,是小偏差时放大倍数偏大导致稳定性较差在从弯道到直道的过程中,由于小车寻赛道本质上是一个随动系统,因为没有积分项,造成在进入直道时转向不够准确,跑直道时虽然能跟踪黑线,但是转向调整往往超调,导致车身在直道上左右震荡,这种震荡严重影响了车的整体速度。此外,我们对S弯的控制也过于简单,没有特别的处理,导致车在跑S弯的时候,几乎完全沿弯走,没有明显的直冲S弯的效果,原因是在前瞻有限的情况下,在采集的图像中S弯入弯和普通弯道是一样的,导致小车开始转向,由于中间一直检测到弯道,小车会沿S弯道左右震荡,同时相应会减速。

  经过反复调试PD参数,我们发现只调整PD参数很难使车在跑S弯和长直道时都选择最佳路径,同时不影响在普通弯处的转向。这就要求系统能够智能地识别出当前赛道是哪种类型,我们没有选择赛道记忆等方法,而采取在不降低远处分辨率的情况下,尽量让摄像头看得更远的方法。最后,在MCU超频的条件下,在透视问题影响远处分辨率的制约下,使视场长度(视场最远处和最近处的距离)达到1.6m多,最远前瞻达到2.0m,足以覆盖赛道中的各种赛道类型,使得我们在程序中并没有加入了对S弯、长直道以及大弯进行可靠识别的算法,仅仅根据中心位置动态改变PD参数,就得到了较好的控制效果。

  经过反复测试,我们选择的PD调节策略是:

(1) 将微分项系数置零,单独调节Kp,发现在2.4m/s以下单独调节Kp就能取得一个良好的跑车效果;
(2) 2.5m/s以上微分项系数Kd随速度增大而增大,原因是速度越快舵机在一般赛道中越需要较好的动态响应能力;
(3) 对Kp,我们使用了在程序中具体代码如下:

  其中,error是中心位置与中心值的偏差,bas_kp为基准kp

▲ 4.20 中点偏差和动态Kp值的函数曲线

▲ 4.20 中点偏差和动态Kp值的函数曲线

  经不断调试,最终我们选择了一组PD参数,得到了较为理想的转向控制效果。

4.5 驱动电机的PI控制算法

  对于速度控制,我们采用了增量式PI控制算法,基本思想是直道加速,弯道减速。经过反复调试,将每场图像得到的黑线位置与速度PI参考速度值构成二次曲线关系。在实际测试中,我们发现小车直道和弯道相互过渡时加减速比较灵敏,与舵机转向控制配合得较好。

▲ 4.21 重点偏差和给定速度的函数曲线

▲ 4.21 重点偏差和给定速度的函数曲线

  在程序中具体代码如下:

  SetSpeed = (124 - 3 * error) * bas_speed (公式4.5)

  但是,该方法存在一定的局限。一方面是车在从弯道入直道时加速和从直道入弯道时减速达不到最好的控制效果,直道入弯道减速不够快速,弯道出直道加速的时机不够及时。因此我们做了进一步的改进,加速的曲线与减速的曲线分开形成滞回的效果,结果表明,控制效果更好。另一方面是没有考虑到实际比赛中长直道急速冲刺的情况,赛前在程序中人为设定直线速度不够灵活不够合理,所以我们在程序中根据图像识别长直线提高了直线速度HighestSpeed,使车能够在长直道上充分发挥潜能。

05 统调试


5.1 开发调试工具

  软件开发工具选用的是Embedded Workbench for ARM。是IAR Systems公司为ARM 微处理器开发的一个集成开发环境(下面简称IAR)。比较其他的ARM开发环境,IAR具有入门容易、使用方便和代码紧凑等特点。它为用户提供一个易学和具有最大量代码继承能力的开发环境,以及对大多数和特殊目标的支持。嵌入式IAR Embedded Workbench 有效提高用户的工作效率,通过IAR工具,可以大大节省软件调试时间。调试界面如图5.1所示:

▲ 5.1 IAR调试界面

▲ 5.1 IAR调试界面

5.2 人机交互工具

  在调试过程中需要不断地修改变量的值来达到整定参数的作用,对此我们选用了液晶屏配合按键和拨码开关的调试方法。此外,比赛的时候,修改参数我们同样用这个模块进行修改。

  其中液晶屏我们选用OLED液晶,该液晶具尺寸小,高分辨率等特点。界面如图5.2所示。按键采用五个按键便于操作。设计五个按键以及6个拨码开关进行调参,显示,方案选择等部分

▲ 5.2 液晶屏显示界面

▲ 5.2 液晶屏显示界面

5.3 蓝牙及上位机调试

  车在同样的赛道上走过的路都是不一样的,所以无论怎么考虑车的状态都是不够完全的,因此需要对运行中的车进行实时监控。为了解决这个问题,我们使用蓝牙模块配合上位机进行实时观测车模运行状态。如图5.3所示。

▲ 5.3 上位机监测实时电流

▲ 5.3 上位机监测实时电流

  论 ※


  报名参加智能汽车竞赛以来,我们小组成员从查找资料、设计机构、组装车模、编写程序一步一步的进行,最后终于完成了最初目标,定下了现在这个设计方案。

  在此份技术报告中,我们主要介绍了准备比赛时的基本思路,包括机械、电路以及最重要的控制算法的创新思想。在机械结构方面,我们分析了舵机转向系统的改进办法,对前轮倾角进行一系列的改动。在电路方面,我们以模块形式分类,在最小系统、主板、电机驱动、电池使用、电磁采集等模块分别设计,经过不断实验,最后决定了最终的电路图。在程序方面,我们使用C语言编程,利用开发工具调试程序,经过小组成员不断讨论、改进,终于设计出一套比较通用稳定的程序。在这套算法中,我们结合路况调整车速,做到直道加速、弯道减速,保证在最短时间内跑完全程。

  在备战过程中,我们遇到了很多挫折,一次次的校内比赛见证我们这个小队艰难的蜕变。非常感谢哈工大智能车创新俱乐部这个像家一样的地方,同时也感谢在场地和经费方面都得到了学校和学院的大力支持,更要特别感谢一直支持和关注智能车比赛的学校和学院领导以及各位指导老师、指导学长,同时也感谢比赛组委会能组织这样一项有意义的比赛。

附录一:部分源代码

  • 第一C文件:
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include <ifxCpu_Irq.h>
#include "IfxScuWdt.h"
#include "SmartCar_Oled.h"
#include "SmartCar_Flash.h"
#include "SmartCar_GPIO.h"
#include "SmartCar_Systick.h"
#include "SmartCar_PIT.h"
#include "common.h"

void Menu_Init(int Power_on_counting)
{
    
    
    uint32_t buff[1024] = {
    
    0};
    Power_on_counting++;
    memcpy(buff, &Power_on_counting, sizeof(uint32_t));
    Sector_Erase(1);
    Page_Program(1, 1, buff);//将buff中的数据存入1号扇区
}

void Menu_print(int Directory_layers,
        int First_cursor,int second_cursor, int third_cursor,
        int Power_on_counting,
        int int_data,int int_rate,
        float float_data,float float_rate)
{
    
    
    int m=0,count=0;
    //书写for循环,m为当前光标所在行的反应,count检测记满了

    char *first_floor[9]={
    
    "list1","list2","list3","list4","list5","list6","data_change","gearing","save"};
    //一级菜单
    char *second_floor[9][3]={
    
    
            {
    
    "1-1","1-2","1-3"},
            {
    
    "2-1","2-2","2-3"},
            {
    
    "3-1","3-2","3-3"},
            {
    
    "4-1","4-2","4-3"},
            {
    
    "4-1","5-2","5-3"},
            {
    
    "6-1","6-2","6-3"},
            {
    
    "int","float","times_cou"},
            {
    
    "gear1","gear2","gear3"},
            {
    
    "yes","no","no"}
    };
    //二级菜单
    char *third_floor[9][3][3]={
    
    {
    
    {
    
    "1-1-1","1-1-2","1-1-3"},
                       {
    
    "1-2-1","1-2-2","1-2-3"},
                       {
    
    "1-3-1","1-3-2","1-3-3"}},
                      {
    
    {
    
    "2-1-1","2-1-2","2-1-3"},
                       {
    
    "2-2-1","2-2-2","2-2-3"},
                       {
    
    "2-3-1","2-3-2","2-3-3"}},
                       {
    
    {
    
    "3-1-1","3-1-2","3-1-3"},
                        {
    
    "3-2-1","3-2-2","3-2-3"},
                        {
    
    "3-3-1","3-3-2","3-3-3"}},
                      {
    
    {
    
    "4-1-1","4-1-2","4-1-3"},
                       {
    
    "4-2-1","4-2-2","4-2-3"},
                       {
    
    "4-3-1","4-3-2","4-3-3"}},
                      {
    
    {
    
    "5-1-1","5-1-2","5-1-3"},
                       {
    
    "5-2-1","5-2-2","5-2-3"},
                       {
    
    "5-3-1","5-3-2","5-3-3"}},
                      {
    
    {
    
    "6-1-1","6-1-2","6-1-3"},
                       {
    
    "6-2-1","6-2-2","6-2-3"},
                       {
    
    "6-3-1","6-3-2","6-3-3"}},
                      {
    
    {
    
    "now","*10","/10"},
                       {
    
    "now","*10","/10"},
                       {
    
    "nothing","nothing","nothing"}},
                      {
    
    {
    
    "OK","",""},
                       {
    
    "OK","",""},
                       {
    
    "OK","",""}},
                      {
    
    {
    
    "success","(-   -)","   -"},
                       {
    
    "Not saved","",""},
                       {
    
    "Not saved","",""}}};
    //三级菜单

    uint32_t Data_Buff[1024] = {
    
     0 };
    SmartCar_OLED_P6x8Str(0, 0 ,"->");
    SmartCar_OLED_P6x8Str(110, 6,"/3");
    SmartCar_OLED_Printf6x8(105, 6, "%d",Directory_layers + 1);

    if(Directory_layers== 0)
    {
    
    
        for (m = First_cursor; count < 9 ; count++)
        {
    
    
            SmartCar_OLED_Printf6x8(15, count, "%s",first_floor[m%9]);
            m++;
        }
        SmartCar_OLED_P6x8Str(110, 0,"/9");
        SmartCar_OLED_Printf6x8(105, 0, "%d",First_cursor + 1);
    }
    else if(Directory_layers == 1)
    {
    
    
        count = 0;
        for (m = second_cursor;count < 3;count++)
        {
    
    
            SmartCar_OLED_Printf6x8(10, count, "%s",second_floor[First_cursor][m%3]);
            m++;
        }
        SmartCar_OLED_P6x8Str(110, 7,"/3");
        SmartCar_OLED_Printf6x8(105, 7, "%d",second_cursor + 1);
    }
    else
    {
    
    
        count = 0;
        for (m = third_cursor;count<3;count++){
    
    
            SmartCar_OLED_Printf6x8(10, count, "%s",third_floor[First_cursor][second_cursor][m%3]);
            m++;
        }
        SmartCar_OLED_P6x8Str(110, 7,"/3");
        SmartCar_OLED_Printf6x8(105, 7, "%d",third_cursor+1);
    }

    m = 0;
    count = 0;

    if(Directory_layers == 1 && First_cursor == 6)//data_change部分
    {
    
    
        if(second_cursor == 0)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 0, "%d",int_data);
            SmartCar_OLED_Printf6x8(70, 1, "%f",float_data);
            SmartCar_OLED_Printf6x8(70, 2, "%d",Power_on_counting);
        }
        else if(second_cursor == 1)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 2, "%d",int_data);
            SmartCar_OLED_Printf6x8(70, 1, "%d",Power_on_counting);
            SmartCar_OLED_Printf6x8(70, 0, "%f",float_data);
        }
        else
        {
    
    
            SmartCar_OLED_Printf6x8(70, 0, "%d",Power_on_counting);
            SmartCar_OLED_Printf6x8(70, 1, "%d",int_data);
            SmartCar_OLED_Printf6x8(70, 2, "%f",float_data);
        }
    }
    if(Directory_layers == 2 && First_cursor == 6 && second_cursor == 0)
    {
    
    
        if(third_cursor==0)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 0, "%d",int_data);
            SmartCar_OLED_Printf6x8(40, 5, "%d",int_rate);
        }
        else if(third_cursor==1)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 2, "%d",int_data);
            SmartCar_OLED_Printf6x8(40, 5, "%d",int_rate);
        }
        else
        {
    
    
            SmartCar_OLED_Printf6x8(70, 1, "%d",int_data);
            SmartCar_OLED_Printf6x8(40, 5, "%d",int_rate);
        }
    }
    else if(Directory_layers==2 &&First_cursor==6 && second_cursor==1)
    {
    
    
        if(third_cursor==0)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 0, "%f",float_data);
            SmartCar_OLED_Printf6x8(40, 5, "%f",float_rate);
        }
        else if(third_cursor==1)
        {
    
    
            SmartCar_OLED_Printf6x8(70, 2, "%f",float_data);
            SmartCar_OLED_Printf6x8(40, 5, "%f",float_rate);
        }
        else
        {
    
    
            SmartCar_OLED_Printf6x8(70, 1, "%f",float_data);
            SmartCar_OLED_Printf6x8(40, 5, "%f",float_rate);
        }
    }
    else
    {
    
    

    }

    if(Directory_layers == 2 && First_cursor == 8 && second_cursor == 0)//save用于数据存储
    {
    
    
        memcpy(Data_Buff, &int_data, sizeof(uint32_t));
        memcpy(Data_Buff+1, &float_data, sizeof(uint32_t));
        Sector_Erase(0);            //先擦除
        Page_Program(0, 0, Data_Buff);   //将Data_buff中的数据存入0号扇区,注意buff为指针
        Page_Program(0, 1, Data_Buff+1);//(Data_buff + 1)中的数据存入0号扇区,注意buff为指针
    }
}

int Menu_down(int Directory_layers,int First_cursor,int second_cursor,int third_cursor)
{
    
    
    SmartCar_OLED_Fill(0);
    Delay_ms(STM0, 100);
    if(Directory_layers==0)
    {
    
    
        return (First_cursor+1) % 9;
    }
    else if(Directory_layers == 1)
    {
    
    
        return (second_cursor+1) % 3;
    }
    else{
    
    
        return (third_cursor+1) % 3;
    }
}

int Menu_up(int Directory_layers,int First_cursor,int second_cursor,int third_cursor)
{
    
    
    SmartCar_OLED_Fill(0);
    Delay_ms(STM0, 100);
    if(Directory_layers == 0)
    {
    
    
        if(First_cursor == 0)
        {
    
    
            First_cursor = 8;
        }
        else
        {
    
    
            First_cursor = First_cursor - 1;
        }
        return First_cursor;
    }
    else if(Directory_layers == 1)
    {
    
    
        if(second_cursor == 0)
        {
    
    
            second_cursor = 2;
        }
        else
        {
    
    
            second_cursor = second_cursor - 1;
        }
        return second_cursor;
    }
    else{
    
    
        if(third_cursor == 0)
        {
    
    
            third_cursor = 2;
        }
        else{
    
    
            third_cursor=third_cursor-1;
        }
        return third_cursor;
    }
}

int Menu_left(int Directory_layers)
{
    
    
    SmartCar_OLED_Fill(0);
    if(Directory_layers==0)
    {
    
    
        return 0;
    }
    else
    {
    
    
        return Directory_layers - 1;
    }
}

int Menu_right(int second_cursor ,int Directory_layers)
{
    
    
    SmartCar_OLED_Fill(0);
    return (Directory_layers+1)%3;
}

int Menu_date_int(int second_cursor)
{
    
    
        int int_data;
        SmartCar_OLED_Fill(0);
        if(second_cursor == 0)
        {
    
    
            int_data=10;
        }
        else if(second_cursor == 1)
        {
    
    
            int_data = 100;
        }
        else
        {
    
    
            int_data = 1000;
        }
        return int_data;
}

float Menu_date_float(int second_cursor)
{
    
    
        float float_data;
        SmartCar_OLED_Fill(0);
        if(second_cursor == 0)
        {
    
    
            float_data = 5.342;
        }
        else if(second_cursor == 1)
        {
    
    
            float_data = 53.42;
        }
        else
        {
    
    
            float_data= 534.2;
        }
        return float_data;
}

  第二个.C文件

/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 * 
 * Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of 
 * business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
 * are agreed, use of this file is subject to following:
 * 
 * Boost Software License - Version 1.0 - August 17th, 2003
 * 
 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and 
 * accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
 * and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
 * Software is furnished to do so, all subject to the following:
 * 
 * The copyright notices in the Software and this entire statement, including the above license grant, this restriction
 * and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all 
 * derivative works of the Software, unless such copies or derivative works are solely in the form of 
 * machine-executable object code generated by a source language processor.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE.
 *********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include <ifxCpu_Irq.h>
#include "IfxScuWdt.h"
#include "SmartCar_Oled.h"
#include "SmartCar_Flash.h"
#include "SmartCar_GPIO.h"
#include "SmartCar_Systick.h"
#include "SmartCar_PIT.h"
#include "common.h"
#include "my_menu.h"
#pragma section all "cpu0_dsram"
//IfxCpu_syncEvent g_cpuSyncEvent;

int int_data=0,int_rate=1;
float float_data=0.0000,float_rate=1.0;
int core0_main(void)
{
    
    
    int Directory_layers=0,First_cursor=0,second_cursor=0,third_cursor=0,Power_on_counting=0;
    //Directory_layers表示当前层数,取值012;First_cursor,second_cursor,third_cursor表示是从第012层哪一行进入的;Power_on_counting表示上电计数的次数
    /** 关闭总中断*/
    IfxCpu_disableInterrupts();
    /** 初始化时钟*/
    get_clk();
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    /** 初始化OLED*/
    SmartCar_Oled_Init();
    SmartCar_Buffer_Upload(DISP_image_HSIC_lilac);

    /** ↓↓↓↓↓在这里初始化GPIO↓↓↓↓↓*/
    // 8个LED初始化为点亮
    GPIO_Init(P21,4, PUSHPULL, 1);   //LED1
    GPIO_Init(P21,2, PUSHPULL, 1);   //LED2
    GPIO_Init(P22,2, PUSHPULL, 1);   //LED3
    GPIO_Init(P22,0, PUSHPULL, 1);   //LED4
    GPIO_Init(P33,5, PUSHPULL, 0);   //LED5
    GPIO_Init(P33,7, PUSHPULL, 0);   //LED6
    GPIO_Init(P33,9, PUSHPULL, 0);   //LED7
    GPIO_Init(P33,11, PUSHPULL, 0);   //LED8
    // 五项按键初始化为没有按下
    GPIO_Init(P11,2, PULLUP, 1);   //left
    GPIO_Init(P11,3, PULLUP, 1);   //down
    GPIO_Init(P11,6, PULLUP, 1);   //ok
    GPIO_Init(P13,3, PULLUP, 1);   //right
    GPIO_Init(P13,2, PULLUP, 1);   //up
    // 拨码
//    GPIO_Init(P11,11, PULLUP,0);   //sw1
//    GPIO_Init(P11,12, PULLUP,0);   //sw2
//    GPIO_Init(P11,9, PULLUP,0);    //sw3
//    GPIO_Init(P11,10, PULLUP,0);   //sw4

    /** ↑↑↑↑↑在这里初始化GPIO↑↑↑↑↑*/

    Delay_ms(STM0, 1000);
    SmartCar_OLED_Fill(0);
    /** 开启总中断*/
    IfxCpu_enableInterrupts();

    /** 主循环 */

    int_data = Page_Read(0, 0, int);
    float_data =Page_Read(0, 1, float);
    Power_on_counting = Page_Read(1, 1, int);
    Menu_Init(Power_on_counting);//读取int_data,float_data和上电次数计数器Power_on_counting
    while(1)
    {
    
    
        Menu_print(Directory_layers,First_cursor,second_cursor,third_cursor,Power_on_counting,int_data,int_rate,float_data,float_rate);
        //打印所有的菜单
        if (!GPIO_Read(P13, 2) || !GPIO_Read(P11, 6) || !GPIO_Read(P13,3) || !GPIO_Read(P11, 2) || !GPIO_Read(P11, 3))
            //按键检测
        {
    
    
            Delay_ms(STM0,100);
            if(!GPIO_Read(P11,3) )//按键“下”
            {
    
    
                if(Directory_layers == 0)
                {
    
    
                    First_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else if(Directory_layers == 1)
                {
    
    
                    second_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else
                {
    
    
                    third_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
            }
            else if (!GPIO_Read(P13,2))//按键“上”
            {
    
    
                if(Directory_layers == 0)
                {
    
    
                    First_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else if(Directory_layers == 1)
                {
    
    
                    second_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else
                {
    
    
                    third_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
              }
            else if (!GPIO_Read(P13,3))//按键呀“右”,进入下一级菜单
            {
    
    
                if(First_cursor == 7)
                {
    
    
                    Directory_layers = Menu_right(second_cursor,Directory_layers);
                    int_data = Menu_date_int(second_cursor);
                    float_data=Menu_date_float(second_cursor);
                }
                else
                {
    
    
                    Directory_layers = Menu_right(second_cursor,Directory_layers);
                    SmartCar_OLED_Fill(0);
                }
            }
            else if (!GPIO_Read(P11,2))//按键呀“左”,进入上一级菜单
            {
    
    
                Directory_layers = Menu_left(Directory_layers);
            }
            else//按键“中间”,进行调参数档位的选择和加
            {
    
    
                Delay_ms(STM0,100);
                if(!GPIO_Read(P11,6))
                {
    
    
                    if(Directory_layers == 2 && First_cursor == 6 &&second_cursor == 0)//说明进入了data_change层,对整型变量修改
                    {
    
    
                        SmartCar_OLED_Fill(0);
                        if(third_cursor == 0)
                        {
    
    
                            int_data = int_data + int_rate;
                        }
                        else if(third_cursor == 1)
                        {
    
    
                            int_rate = int_rate*10;
                        }
                        else{
    
    
                            if(int_rate == 1)
                            {
    
    
                                int_rate = 1;
                            }
                            else
                            {
    
    
                               int_rate = int_rate/10;
                            }
                        }
                    }
                    else if(Directory_layers == 2 && First_cursor == 6 && second_cursor == 1)//data_change层,对浮点变量修改
                    {
    
       SmartCar_OLED_Fill(0);

                        if(third_cursor == 0)
                        {
    
    
                            float_data = float_data + float_rate;
                        }
                        else if(third_cursor == 1)
                        {
    
    
                            float_rate = float_rate * 10.0;
                        }
                        else
                        {
    
    
                            float_rate = float_rate / 10.0;
                        }
                     }
                  }
                 else
                 {
    
    

                 }
              }
           }
    }
}

#pragma section all restore
/**********************************************************************************************************************
 * \file Cpu0_Main.c
 * \copyright Copyright (C) Infineon Technologies AG 2019
 * 
 * Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of 
 * business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
 * are agreed, use of this file is subject to following:
 * 
 * Boost Software License - Version 1.0 - August 17th, 2003
 * 
 * Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and 
 * accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
 * and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
 * Software is furnished to do so, all subject to the following:
 * 
 * The copyright notices in the Software and this entire statement, including the above license grant, this restriction
 * and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all 
 * derivative works of the Software, unless such copies or derivative works are solely in the form of 
 * machine-executable object code generated by a source language processor.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
 * COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE.
 *********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include <ifxCpu_Irq.h>
#include "IfxScuWdt.h"
#include "SmartCar_Oled.h"
#include "SmartCar_Flash.h"
#include "SmartCar_GPIO.h"
#include "SmartCar_Systick.h"
#include "SmartCar_PIT.h"
#include "common.h"
#include "my_menu.h"
#pragma section all "cpu0_dsram"
//IfxCpu_syncEvent g_cpuSyncEvent;

int int_data=0,int_rate=1;
float float_data=0.0000,float_rate=1.0;
int core0_main(void)
{
    
    
    int Directory_layers=0,First_cursor=0,second_cursor=0,third_cursor=0,Power_on_counting=0;
    //Directory_layers表示当前层数,取值012;First_cursor,second_cursor,third_cursor表示是从第012层哪一行进入的;Power_on_counting表示上电计数的次数
    /** 关闭总中断*/
    IfxCpu_disableInterrupts();
    /** 初始化时钟*/
    get_clk();
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    /** 初始化OLED*/
    SmartCar_Oled_Init();
    SmartCar_Buffer_Upload(DISP_image_HSIC_lilac);

    /** ↓↓↓↓↓在这里初始化GPIO↓↓↓↓↓*/
    // 8个LED初始化为点亮
    GPIO_Init(P21,4, PUSHPULL, 1);   //LED1
    GPIO_Init(P21,2, PUSHPULL, 1);   //LED2
    GPIO_Init(P22,2, PUSHPULL, 1);   //LED3
    GPIO_Init(P22,0, PUSHPULL, 1);   //LED4
    GPIO_Init(P33,5, PUSHPULL, 0);   //LED5
    GPIO_Init(P33,7, PUSHPULL, 0);   //LED6
    GPIO_Init(P33,9, PUSHPULL, 0);   //LED7
    GPIO_Init(P33,11, PUSHPULL, 0);   //LED8
    // 五项按键初始化为没有按下
    GPIO_Init(P11,2, PULLUP, 1);   //left
    GPIO_Init(P11,3, PULLUP, 1);   //down
    GPIO_Init(P11,6, PULLUP, 1);   //ok
    GPIO_Init(P13,3, PULLUP, 1);   //right
    GPIO_Init(P13,2, PULLUP, 1);   //up
    // 拨码
//    GPIO_Init(P11,11, PULLUP,0);   //sw1
//    GPIO_Init(P11,12, PULLUP,0);   //sw2
//    GPIO_Init(P11,9, PULLUP,0);    //sw3
//    GPIO_Init(P11,10, PULLUP,0);   //sw4

    /** ↑↑↑↑↑在这里初始化GPIO↑↑↑↑↑*/

    Delay_ms(STM0, 1000);
    SmartCar_OLED_Fill(0);
    /** 开启总中断*/
    IfxCpu_enableInterrupts();

    /** 主循环 */

    int_data = Page_Read(0, 0, int);
    float_data =Page_Read(0, 1, float);
    Power_on_counting = Page_Read(1, 1, int);
    Menu_Init(Power_on_counting);//读取int_data,float_data和上电次数计数器Power_on_counting
    while(1)
    {
    
    
        Menu_print(Directory_layers,First_cursor,second_cursor,third_cursor,Power_on_counting,int_data,int_rate,float_data,float_rate);
        //打印所有的菜单
        if (!GPIO_Read(P13, 2) || !GPIO_Read(P11, 6) || !GPIO_Read(P13,3) || !GPIO_Read(P11, 2) || !GPIO_Read(P11, 3))
            //按键检测
        {
    
    
            Delay_ms(STM0,100);
            if(!GPIO_Read(P11,3) )//按键“下”
            {
    
    
                if(Directory_layers == 0)
                {
    
    
                    First_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else if(Directory_layers == 1)
                {
    
    
                    second_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else
                {
    
    
                    third_cursor = Menu_down(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
            }
            else if (!GPIO_Read(P13,2))//按键“上”
            {
    
    
                if(Directory_layers == 0)
                {
    
    
                    First_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else if(Directory_layers == 1)
                {
    
    
                    second_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
                else
                {
    
    
                    third_cursor = Menu_up(Directory_layers,First_cursor,second_cursor,third_cursor);
                }
              }
            else if (!GPIO_Read(P13,3))//按键是“右”,进入下一级菜单
            {
    
    
                if(First_cursor == 7)
                {
    
    
                    Directory_layers = Menu_right(second_cursor,Directory_layers);
                    int_data = Menu_date_int(second_cursor);
                    float_data=Menu_date_float(second_cursor);
                }
                else
                {
    
    
                    Directory_layers = Menu_right(second_cursor,Directory_layers);
                    SmartCar_OLED_Fill(0);
                }
            }
            else if (!GPIO_Read(P11,2))//按键呀“左”,进入上一级菜单
            {
    
    
                Directory_layers = Menu_left(Directory_layers);
            }
            else//按键“中间”,进行调参数档位的选择和加
            {
    
    
                Delay_ms(STM0,100);
                if(!GPIO_Read(P11,6))
                {
    
    
                    if(Directory_layers == 2 && First_cursor == 6 &&second_cursor == 0)//说明进入了data_change层,对整型变量修改
                    {
    
    
                        SmartCar_OLED_Fill(0);
                        if(third_cursor == 0)
                        {
    
    
                            int_data = int_data + int_rate;
                        }
                        else if(third_cursor == 1)
                        {
    
    
                            int_rate = int_rate*10;
                        }
                        else{
    
    
                            if(int_rate == 1)
                            {
    
    
                                int_rate = 1;
                            }
                            else
                            {
    
    
                               int_rate = int_rate/10;
                            }
                        }
                    }
                    else if(Directory_layers == 2 && First_cursor == 6 && second_cursor == 1)//data_change层,对浮点变量修改
                    {
    
       SmartCar_OLED_Fill(0);

                        if(third_cursor == 0)
                        {
    
    
                            float_data = float_data + float_rate;
                        }
                        else if(third_cursor == 1)
                        {
    
    
                            float_rate = float_rate * 10.0;
                        }
                        else
                        {
    
    
                            float_rate = float_rate / 10.0;
                        }
                     }
                  }
                 else
                 {
    
    

                 }
              }
           }
    }
}

#pragma section all restore

参考文献

[1] 卓晴,黄开胜,邵贝贝.学做智能车 [M].北京:北京航空航天大学出版社.2007.
[2] 王淑娟,蔡惟铮,齐明.模拟电子技术基础 [M].北京:高等教育出版社.2009
[3] 张军.AVR单片机应用系统开发典型实例 [M].北京:中国电力出版社,2005.
[4] 张文春.汽车理论 [M].北京.机械工业出版社.2005.
[5] 殷剑宏,吴开亚.图论及其算法 [M] .中国科学技术大学出版社,2003.
[6] 夏克俭.数据结构及算法 [M] .北京:国防工业出版社, 2001.
[7] 邵贝贝.单片机嵌入式应用的在线开发方法 [M].北京.清华大学出版社.2004.
[8] 蔡述庭.“飞思卡尔”杯智能汽车竞赛设计与实践 [M].北京:北京航空航天大学出版社. 2012.


● 相关图表链接:

猜你喜欢

转载自blog.csdn.net/zhuoqingjoking97298/article/details/132534760