基于RT-Thread开发智能视觉组智能车 - 温州大学 - 春华秋实
Posted 卓晴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于RT-Thread开发智能视觉组智能车 - 温州大学 - 春华秋实相关的知识,希望对你有一定的参考价值。
参赛队员:陈俊达、程先春、倪世杰
带队教师:王峰
简 介: 目前自动驾驶话题非常火热,近几年华为、特斯拉、谷歌、百度等大型互联网公司都在进行关于这方面的研究。本文设计了基于恩智浦 RT1064芯片的智能小车,以 keil5为开发环境,利用基于 MT9V03X的总钻风数字摄像头作为传感器实现道路识别,使用 S3010转向舵机控制小车转向,依靠 RS380大电机和编码器构成闭环回路控制小车速度。该小车车模利用摄像头采集到赛道数据,根据差比和判断出图像上灰度跳变点即为赛道边缘,计算出赛道中线后,再控制舵机转向前行,同时利用 OpenArt mini摄像头进行靶标图像的识别,还使用了 RT-Thread操作系统进行创新使用,充分发挥出了芯片的性能,提高了车模完成任务的成功率。该操作系统功能强大,操作便捷,学习方便,在车模成绩提高以及车模软件开发过程中发挥了出色的作用。
关键词
: RT-Thread,智能车竞赛
§01 引言
1.1智能汽车制作情况
本文以全国大学生智能车竞赛官方指定的 C型车模作为主体,该车模采用两个直流电机驱动后轮,舵机驱动梯形转向机构完成前轮转向。在硬件电路方面,围绕 RT1064微处理器为主控制单元, OpenARTmini为识别动物、水果和数字的主控制单元,其次包含有 MT9V032摄像头采集,全桥驱动电路驱动直流电机,512线编码器作为速度反馈, LCD屏和按键组成的人机交互平台等。其次,运用差比和像素阈值算法检测出赛道边界线,然后图像处理得到中心边界线,进而完成路径识别和决策规划。最后,运用自动控制理论,建立相应模型,设计出跟踪控制器,其包括有 PID速度控制器和 PD方向控制器。通过大量试验数据优化控制器,如模糊 PID控制,对 kp、kd值进行动态改变等。
1.2 RT-Thread技术概述
RT-Thread,全称是 Real Time-Thread,顾名思义,它是一个嵌入式实时多线程操作系统,基本属性之一是支持多任务,允许多个任务同时运行并不意味着处理器在同一时刻真地执行了多个任务。事实上,一个处理器核心在某一时刻只能运行一个任务,由于每次对一个任务的执行时间很短、任务与任务之间通过任务调度器进行非常快速地切换(调度器根据优先级决定此刻该执行的任务),给人造成多个任务在一个时刻同时运行的错觉。在 RT-Thread系统中,任务通过线程实现的, RT-Thread中的线程调度器也就是以上提到的任务调度器。
RT-Thread主要采用 C语言编写,浅显易懂,方便移植。它把面向对象的设计方法应用到实时系统设计中,使得代码风格优雅、架构清晰、系统模块化并且可裁剪性非常好。针对资源受限的微控制器( MCU)系统,可通过方便易用的工具,裁剪出仅需要 3KB Flash、1.2KB RAM内存资源的 NANO版本(NANO是 RT-Thread官方于 2017年 7月份发布的一个极简版内核);而对于资源丰富的物联网设备, RT-Thread又能使用在线的软件包管理工具,配合系统配置工具实现直观快速的模块化裁剪,无缝地导入丰富的软件功能包,实现类似 android的图形界面及触摸滑动效果、智能语音交互效果等复杂功能。
相较于 Linux操作系统, RT-Thread体积小,成本低,功耗低、启动快速,除此以外 RT-Thread还具有实时性高、占用资源小等特点,非常适用于各种资源受限(如成本、功耗限制等)的场合。虽然 32位 MCU是它的主要运行平台,实际上很多带有 MMU、基于 ARM9、ARM11甚至 Cortex-A系列级别 CPU的应用处理器在特定应用场合也适合使用 RT-Thread。
近年来,物联网( InternetOfThings,IoT)概念广为普及,物联网市场发展迅猛,嵌入式设备的联网已是大势所趋。终端联网使得软件复杂性大幅增加,传统的 RTOS内核已经越来越难满足市场的需求,在这种情况下,物联网操作系统( IoTOS)的概念应运而生。物联网操作系统是指以操作系统内核(可以是 RTOS、Linux等)为基础,包括如文件系统、图形库等较为完整的中间件组件,具备低功耗、安全、通信协议支持和云端连接能力的软件平台, RT-Thread就是一个 IoTOS。
RT-Thread与其他很多 RTOS如 FreeRTOS、uC/OS的主要区别之一是,它不仅仅是一个实时内核,还具备丰富的中间层组件,如下图所示。
▲ 图1.1 RT-Thread 中间层组件
我们采用 RT-Thread来对代码进行开发和运行,极大的提高了开发的效率,任务调度的功能可以让我们极大的提高利用 CPU的效率,提高了摄像头的帧率,提高了车辆前进的上限,且在各个方面能够轻松的切换任务,实实在在的减少了不必要的代码算力浪费和代码开发的速度。
1.3本文的概况及结构安排
- 第一章,叙述本设计的研究背景,对智能车技术发展状况进行了阐述,得出本设计的主要研究工作和论文结构安排。
- 第二章,智能车系统总体设计,先介绍设计思路,进一步简述本设计的系统结构,最后展示了智能车的整车布局情况。
- 第三章,设计与优化机械结构,其中包括基于阿克曼转向原理的转向结构优化,
- 得到结构合理的转向梯形结构,并优化其他结构部分。第四章,硬件电路的设计与优化,重点介绍了 RT1064最小系统部分,电源部分,视频信号处理部分,驱动电路部分,速度检测部分。
- 第五章,路径识别是视觉导航的关键技术,运用差比和算法检测出赛道两条黑色边界线,经过特殊处理得到赛道中心边界线,进而完成路径识别和决策规划。还介绍了如何运用 OpenArtmini实现功能和任务。
- 第六章,本章主要介绍对于速度,方向的控制,简要介绍了 PID控制的方法。第七章,重点介绍了如何完成动物水果动物以及 Apriltag码的识别。
- 第八章,重点介绍了如何对 RT-Thread的移植和运用。
- 第九章,重点介绍了 RT-Thread操作系统的创新使用
- 第十章,总结。
§02 系统总体设计
2.1系统概述
智能车的核心单元和 OpenARTmini的核心单元都是采用了恩智浦公司推出的 32位微控制器 RT1064。其采用 ARM-Cortex-M7芯片内核,芯片频率为 600Mhz,是 STM32F103系列的 8倍多,有 1M片内 SRAM和 4M片内 FLASH和丰富 IO口.
智能车系统的工作原理:
摄像头自动循迹识别智能车系统硬件分为 6大模块:主控制器模块、电源模块、赛道信息采集模块、智能视觉识别模块、方向控制模块、速度控制电路模块,摄像头自动循迹智能车系统硬件整体设计框图如图 2.1所示。主控制器模块采用恩智浦公司推出的 RT1064系列的 32位微控制器 NXPRT1064作为智能车嵌入式系统的核心控制单元,是整个摄像头智能车的"大脑",完成各项任务的分配与协调。电源模块负责提供各子模块所需的电压。赛道信息采集模块采用摄像头,通过 MT9V03X总钻风采样灰度图,将得到的数据储存在二维数组中,通过路径识别算法提取偏差。智能视觉识别模块采用在 OpenARTmini上部署神经网络模型识别数字水果和动物,然后再将识别信息传回小车主控模块,再由主控模块判断下一步的行为.
方向控制模块使用的是 S3010转向舵机,该舵机转向灵活、反应速度快且力矩较大,通过赛道中心线部位与小车位置的偏差,由相应控制算法控制舵机灵活转弯。通过 PID算法以及灵活调整 PID参数,通过编码器读到的数值进行一定运算得到现场速度。停车由摄像头采集图像内是否遇到斑马线然后判断黑白跳变条数来决定。
▲ 图2.1 系统结构框图
2.2整车布局
小车的布局本着轻量化的设计原则,尽可能降低重心以提高稳定性,具体包括具有以下特点
1 ) 舵机位置在前,立式安装,既保证响应速度又大多数符合阿克曼转角原理;
2 ) 电池用扎带固定在后方,平衡重心;
3 ) 摄像头通过碳杆、金属支架和 AB胶固定在舵机与主板中间,有效的采集赛道信息;
4 ) 云台在前,控制激光发射器在 15cm高度,将 OpenArt mini倒置安装减少打
靶的误差,同时后方的总钻风采用 33cm高度可以完美避开前方云台的视野遮挡。下图为详细的整车布局:
▲ 图2.2 整体布局
§03 机械安装
3.1智能车整体参数调校
智能车的整体参数,都对整个智能车系统的稳定运行起着至关重要的作用,通过对小车的布局,尽量保证小车左右平衡,以及寻找一个合适的重心,保证小车既能够可靠地抓牢地面,又能够对前轮舵机,后轮电机有较快的响应。机械的变化可以提升小车的速度上限,另外,机械的稳定性非常重要,因此要尽量保证小车既能够可靠地抓牢地面,又能够对前轮舵机,后轮电机有较快的响应。
3.2前轮倾角调节
3.2.1主销内倾
主销在转向轴线上向内倾斜,主销轴线与地面垂直线在赛车横向断面内的夹角称为主销内倾角。主销内倾角也有使轮胎自动回正的作用,当汽车转向轮在外力作用下发生偏转时,由于主销内倾,则车轮将整辆车抬起,在外力消失后,车轮就会在重力作用下力图恢复到原来的中间位置,主要在低速时可利用内倾回复转向。
3.2.2主销后倾
主销后倾角的作用是当汽车直线行驶时,若转向轮受到外力作用而发生偏转,汽车会转向。这时,后倾角会使路面对车轮产生一个侧向反作用力,使车轮回正,从而保证在中高速行驶中汽车直线行驶的稳定性,适当的加大主销后倾角可以帮助转向轮自动回正,可有效扼制转向器的摆振。
3.2.3前轮前束调节
通过车轮中心的汽车横向平面与车轮平面的交线与地面垂线之间的夹角,称为前轮外倾角,前轮外倾可以抵消由于车的重力使车轮向内倾斜的趋势,减少赛车机件的磨损与负重,保证车辆行驶性能,前轮在滚动时,其惯性力自然将轮胎向内偏斜,抵消轮胎偏斜的力,从而减小磨损
3.2.4车轮外倾角
轮外倾角是指通过车轮中心的汽车横向平面与车轮平面的交线与地面垂线之间的夹角,对汽车的转向性能有直接影响,它的作用是提高前轮的转向安全性和转向操纵的轻便性。在汽车的横向平面内,轮胎呈 “八”字型时称为 “负外倾 ”,而呈现 “V”字形张开时称为正外倾。如果车轮垂直地面一旦满载就易产生变形,可能引起车轮上部向内倾侧,导致车轮联接件损坏。所以事先将车轮校偏一个正外倾角度,一般这个角度约在 1°左右,以减少承载轴承负荷,增加零件使用寿命,提高汽车的安全性能。
模型车提供了专门的外倾角调整配件,近似调节其外倾角。由于竞赛中模型主要用于竞速,所以要求尽量减轻重量,其底盘和前桥上承受的载荷不大,所以外倾角调整为 0°即可,并且要与前轮前束匹配。
3.3车模重心
在完成车模的搭建后,通过测量小车轮胎位置的质量测量出整个车模的质心分布。从而保证整个小车的稳定性,车体重心的位置对赛车加减速性能、转向性能和稳定性都有较大的影响,同时还应该尽量降低车模重心高度,防止车模行驶时发生侧翻
3.4舵机安装
舵机的安装方式有立式和卧式两种,经过分析,我们沿用经典的立式装法。舵机是具有较大延迟特性的器件,其延迟与其转角大小成正比,但如果能使舵机安装图机转过一个较小的角度而使车轮转过一个越大的角度,则会大大提高舵机过弯的响应速度。这不仅与舵机的安装方式有关,而且也与舵机输出臂的长度有关。
▲ 图3.1 舵机安装
安装时将舵机竖起,使得两条舵机拉杆几乎成一字型直线,某种程度上增长了拉杆的有效长度,可提高智能车转向控制的速度。
3.5摄像头安装
摄像头我们采用前后分配的安装模式,前方云台架起 15cm高的 OpenArt mini,尽可能的低的将摄像头高度降低,降低整车重心,因为云台虚位的原因,便在后方使用碳棒架起到一个更高的高度安装总钻风摄像头,并且调整俯视角度,尽量减少对于视野盲区的影响,同时保证了前方视野的前瞻来减少舵机的滞后性影响。
§04 电路设计
4.1.总体结构图
▲ 图4.1.1 电路设计思路
硬件系统是整个智能车系统可靠运行的基础,更是软件程序得以稳定运行的基础,所以硬件系统的设计是非常重要的,我们的硬件系统主要包括电源管理模块,电机驱动模块,舵机驱动模块,电磁循迹模块,主要使用三块板子分别为母版,电机驱动以及电磁。
4.2.电源管理模块
▲ 图4.2 5V降压电路
图 4.2 5v降压电路首先是电源管理模块,我们采用的 7.2V2000mAh的供电电池。电机驱动需
要的就是 7.2v的电压,所以我们采用开关稳压芯片进行稳压输出,舵机工作电压范围为 4V到 6V,我们需要通过调节电位器使他的电压维持在 5v即可正常工作,单片机系统,电磁,以及下面的 oled等均使用的是 3.3V的电源。通过以上电路将 7.2v的 vccbat输入降到 5v进行输出。
▲ 图4.3.3 3.3V 降压电路
图 4.3 3.3v降压电路再通过如上电路将 5v降为 3.3v,其中 u6使用的是 rt9013-33gb
4.3单片机
▲ 图4.4 单片机电路
我们使用的是恩智浦公司推出的 RT1064系列的 32位微控制器 NXP.RT1064,更大的 RAM可以放更多的数组或者变量, RT1064通过快速 GPI0可以实现 150M的 I0翻转速度,由于 RT1064片内自带了 flash,因此 IDE (IAR、 MDK等)都会自带相应的下载算法,不需要自己手动添加,使用更加的方便, RT1064自带 ROMAPI可以非常方便的操作片内的 flash。
4.4电机驱动
将直接输入的电压先转化成 5v稳定电压,再通过单片机输入的 pwm波信号达到电机驱动目的。
▲ 图4.5 电机驱动原理图
▲ 图4.6 PWM 接口电路
▲ 图4.7 整体PCB电路板
4.5电磁模块
因为在路上有时仅凭摄像头会出现小误差导致偏移出赛道的情况,可能会冲撞导致损坏摄像头,所以我们使用电磁模块,来有效防止这一情况的出现,电路图如下:
▲ 图4.8 电磁模块原理图
▲ 图4.9 电磁整体PCB
第五章图像信息获取及处理
5.1赛道信息采集
赛道信息获取是智能车稳定快速运行的根本保障。赛道信息通过 MT9V03X总钻风摄像头进行采集, MT9V03X总钻风摄像头采集速率最高可达每秒 498帧,它具有 FPS可调,全局快门、高动态范围( High dynamic range)、可进行自动曝光等优点。同时 MT9V03X总钻风摄像头采集的是灰度图,具有更多的赛道信息。
图 5.1差比和寻线 (图片来自逐飞科技公众号,侵删 )
往最长白列的左右开始扫描边界黑点,边界黑点的确认方式也采用差比和的方式,公式等于 abs(a-b)/(a+b)*100,与上面一样。例如我们现在需要找最近一行的左边界,我们取最近一行与最长白色列交点处的灰度值为 a,然后取该点左侧点的灰度值为 b,然后根据公式计算得出结果,如果差比和的值大于我们设置的阈值则认为 b点是边界黑点。如果不是我们将 b点的值赋值给 a,然后 b点左侧点的灰度值赋值给 b,再次计算并判断。
图 5.2差比和举例 (图片来自逐飞科技公众号,侵删 )
上表列出了 4种情况
情况 1:黑点为 30,白点为 100,我们通过差比和公式计算并放大100倍,得到的结果为 53。
情况 2:白点 1为 90,白点 2为 100,我们通过差比和公式计算并放大 100倍,得到的结果为 5。
情况 3:黑点 1为 30,黑点 2为 35,我们通过差比和公式计算并放大 100倍,得到的结果为 7。
通过对这三种情况进行分析,我们可以看到当选取的两个点,像素值差距较大时计算出来的值比较大,
像素值差距较小的时候计算出来的值比较小,那么我们就可以设置一个阈值,当差比和计算出来的值超过这个阈值的时候(这个阈值就需要自己上赛道观察白点与黑点的值,然后再自己计算一下这个黑点与白点的差比和值是多少,然后计算出来的值乘以 0.7来作为阈值,也可以根据自己的测试对这个阈值再稍加调试),我们认为找到了一个黑点与一个白点。
情况 4:可以看得出来白点与黑点都比较暗,但是通过差比和计算出来的值却还是和正常图像计算出来的值差距不大,
这样我们就可以通过使用差比和提高我们的稳定性,不像二值化那样光线稍有变化二值化图像就会跟随着变化。所以,使用这样的方法也相当于提高了对光线的适应能力。
5.2消除寻线误判
对于利用差和比寻找赛道边界来说,判断出何处为赛道边界其实是容易误判的,举例说明,当寻找右边界时,假如处于弯道,或者车身不正进入直道时候,图片就会如下图 1所示,可以发现,左边界和有边界在远处一定距离已经被扫线到一处去了,因为差比和算法,每次都是从中间开始往两边扫线的,当赛道已经完全处于中心点外便会导致赛道误判。于是进行优化的方式是通过每次找到边界之后,对下一次起始的点做偏移修正,并且对差和比中加入方向的概念,通过判断像素值的大小一定是从大到小来进行做限制条件,这样同时可以减少运算工作量,同时完美解决了这种现象的发生,提高了寻找中线的稳定性。
图 5.3弯道错误寻找边界
图 5.4扫线效果图
5.3算法优化
简单使用差比和对每一行像素点扫线寻找赛道边界后会发现在弯道曲率较大的时候,会出现单边寻找赛道边线丢线的情况,如下图所示,左边边线已经丢失了,蓝色为计算出的赛道中线,导致中线,明显畸变,会导致计算出来的偏差位置过小,导致舵机打角过小,从而向外侧冲出赛道。解决办法有两个,一种为修改摄像头角度,另一种是通过算法修正。
图 5.5弯道扫线情况
我们采用半宽补线法对赛道中线进行修复,通过计算赛道一行的像素点长度,取一半对赛道中线开始丢线的地方进行修复,首先可以确定的是,这种情况下,只有单边丢线,那么计算中线的方式就是由右边边界减去半宽的赛道像素点长度。并且只要确定底部的第一行中线起点即可,后续行的中线可以根据右边界的变化来进行对应变化,以下是修复过中线的丢边界线的效果图。
对比可以发现,效果显著,并且配合赛道上边界的判断,可以很好的区分出赛道,不会受到其他外界赛道的干扰,扛干扰能力强,且降低了对外界地板颜色的依赖程度,一定程度上提高了容错率,适应性更强。
5.4修复结果
图 5.6修复后结果
可以发现,即使现在图像发生了严重的丢线情况,赛道中线也能被精准的定位。效果良好,极大的提高了弯道过弯时,舵机打角的准确性。
5.5灰度图中的八领域寻线应用
传统的八领域寻线都是将图像二值化之后再进行寻线,其基本思想是:遍历图像找到第一个非零像素点,那么这个点一定是边界点。设这个点为起始点 x,顺时针查找该点八邻域内遇到的第一个非零像素点 x’(点 x’也为边界点)。令x=x’,再进行邻域内的顺时针查找,查找的起点为刚刚 x到 x’过程中 x’前一个零点。
对图( a),在 X点时,按照 0->1->2->3->4…….的顺序进行查找。 6处为查找到的第一个非零像素点,则 6处为边界点, X’查找的起始位置是( a)中的 5,即 X’对应的 4,则 X’的查找顺序是 4->5->6->7->0->1->2->3。
由于我们并没有对图像进行二值化操作,所以我们在传统八领域寻线的基础上进行改进,使之适用于灰度图。
主要改进的点就是在二值化图中八领域寻线的赛道边界判断是判断该点在数组中存的值是 0还是 255,而在灰度图中的赛道边界判断就是采用上面所讲的差比和算法,如果该点和辅助点差比和的结果大于阈值,那该点就是赛道边界
而在顺时针(或逆时针)遍历八个邻近像素的时候,用于辅助判断该点是否为赛道边界的辅助点不能向上面讲解的一样选取右边(或左边)第五个点作为辅助点,而是也要随着当前点顺时针(或逆时针)旋转。
下面是我的方向数组和辅助点方向数组
图 5.7代码
图 5.8代码
第一个为当前点方向数组,第二个为顺时针八领域寻线当前点的辅助点方向数组,第三个为逆时针八领域寻线当前点的辅助点方向数组。在顺时针八领域和逆时针八领域的时候,顺时针时,方向数组加加,逆时针时方向数组减减即可。而赛道边界和赛道的相对位置是相反的,所以对应的
辅助点方向数组是刚好是对称的。
5.6车库图像
1 )在出库方面,由于是可以由参赛人员自由放置,因此我们采用了开环控制。对编码器进行积分就可以得到其运行距离,在多次测试之后设置一个固定打角、固定出库速度和固定行驶距离,行驶到固定距离时再恢复扫线,正常行驶。以上方法只要保证每次放小车的时候都大概在那个区域,就能完美出库。
2 )在入库方面,我们参考了杭电双车组三轮图像处理总结。因为我们的摄像头前瞻比较远,可以在很远判别到斑马线,但不稳定,有时候很远就能判别到,有时候较近才能判别到。,于是我将判别区域限制在图像 30行到 100行,当斑马线起始行或者总止行大于 35行时,开始采用灰度图八领域寻线找到车库入口的两个拐点,然后再进行拉线处理,具体拉线方式如下图
(图片来自卓大大公众号,侵删)而当左上大于一定行数时直接打死并且后轮加差速即可入库。
5.7总钻风检测赛道上 Apriltag码
赛道上加 Apriltag码是今年新增加的元素,这对于各个学校来说都是一个全新的挑战,而我们采用的方案是用识别斑马线的方法来识别赛道上的黑块。这样能在较远的地方准确且快速识别到。 Apriltag码一行有 2-6个黑块,一般都是 2-5个,当扫到某一行黑块大于 2小于 6并且间隔小于该行赛道的 2/3,并且这样的行有大于四个就判定为 Apriltag码,同时为了防止在斑马线哪里误判,当识别到斑马时不识别 Apriltag码。当一行黑块数大于 7个重新识别。
当识别到 Apriltag码后再用沿着赛道中线找 Apriltag码边界,找到起始点后用灰度图八领域将 Apriltag边框找到,然后边框上的点的 y坐标最大和 y坐标最小分别为 Apriltag码的上下边界,即使当 Apriltag码较远,图像上的点跳变,灰度图八领域无法将 Apriltag码边框完全找全,用这个方法也能大概确定找 Apriltag码的上下边界,从而帮助小车调整到一个合适位置,便于 OpenART在合适的距离识别 Apriltag码到底是奇数还是偶数,然后再控制云台将 OpenART拉直,向左(或向右),然后再由 OpenART识别靶标的蓝色边框,然后判断与小车的相对位置,通过串口中断发送回来控制小车前后移动,然后 OpenART继续识别动物还是水果。
第六章运动控制
6.1方向控制
摄像头智能车能够围绕着赛道中心线稳定运行依靠舵机转向功能。方向控制模块采用的是 S3010转向舵机,该舵机具体高扭矩、响应速度快的优点。舵机有三根接线,红色线接 5.5V电源、黑色线接 GND、白色线为舵机信号控制线,接 PWM输出端,通过单片机的控制信号来调节舵机的脉冲占空比来实现舵机的转向功能。系统框图如下图 6.1所示。
舵机采用位置式 PID进行控制,同时为了适应各种赛道元素,增加舵机响应速度,我们采用了模糊 pid进行改良,同时为了防止把舵机的齿轮打坏,我们对舵机的打角进行限幅处理。根据中线误差通过模糊 pid算法得到打角输出,有效而且快速的完成了对赛道正确的打角,能保证在 2.8m/s速度以下正确的进行转向,再快因为舵机滞后性的原因,采取了速度控制策略来使车辆略微减速,使舵机的滞后能够不至于小车冲出赛道。
6.2电磁控制
电磁传感器放置位置受限于摄像头视野范围,无法距离车头很远,不然会影响摄像头寻线,实际上我们直接安装在车头上。我们电磁传感器主要用于检测小车是否冲出赛道和是否遇到环岛。
当小车冲出赛道时,电磁信号归一化之后的值几乎在 10以下,因此用电磁来判断小车是否冲出赛道,快速又准确。
当小车运行到环岛区域,由于电磁线数量变成四根,电磁感应出来的数值会是普通赛道的两倍,实际测量中因为一些干扰,达不到两倍,但相对于普通赛道而言,电磁在一开始入环的区域感应出来的数值还是明显变大,因此可以通过这个特点来辅助摄像头进行对环岛的判断。
6.3速度控制
速度控制模块主要由电机、电机驱动电路、编码器等组成,与主控器一起
构成闭环控制系统。如果单片机输出的 PWM信号直接给到电机,会造成电机转动产生过大的电流可能将单片机烧毁,故需要通过电机驱动电路来控制电机。因为电机采用的 RS380直流大电机,电流较大,驱动电路主要芯片采用 DRV8701E和 TOH1R403NL。
后轮的速度,采取增量式的 PI,通过匿名科创的上位机进行调参,通过跑赛道采集数据,最终确立了合适的后轮 PI,后轮有着较好的响应速度,且不至于超调严重。
图 6.1方向控制流程图
第七章图像识别
7.1 OpenART mini整体代码思路
OpenARTmini兼容了几乎所有的 Openmv的库文件,对图像的处理只要调用相应的 api即可,十分的简介方便。智能视觉组赛题的识别要求包括三项,数字识别, Apriltag码识别以及水果动物的分类,这三个任务我们都基于官方建议的 OpenARTmini进行实现,早期也使用了龙邱的 openmv来实现,但由于仅支持 nncu神经网络模型并不支持 tf模型,并不如 OpenARTmini方便,于是后期更换了摄像头。
部署神经网络模型有两种方式,一种是官方介绍的,通过 nncu工具将 h5模型量化转换进行部署,还有一种就是通过 tensorflow将 h5模型转换为 tflite模型,再部署到 OpenARTmini上。
最后我们采用第二种方案,首先我们通过学习谷歌发布的 moblienetv3.0轻量型模型,改进了自己的 tensorflow模型,显著提高了模型的识别效率和准确率,之后通过爬虫从互联网上爬取了足够的动物水果的图片,通过大量的训练和参数调试,最终得到了良好的识别动物水果的模型,最后通过 tensorflow官方学习了模型量化的方法,转换为 tflite模型之后部署到了摄像头上,做到了即使是识别逐飞官方数据集之外的动物水果图片,在复杂背景之下也能做到对靶标类型正确识别。
相同的对数字靶标也做了相似的模型训练,训练了一个十一分类 tflite神经网络模型,同样取得成效。
7.2数字识别
首先会打开摄像头的打光灯来保证光线恒定,再通过 openmv的色块二值化函数 find_blobs,通过颜色的设置将紫色矩形边框二值化显示出来,同时运行 find_rects来寻找矩形框,因为二值化图形的处理非常迅速,整个寻找边框的过程效率大大提升达到了每秒近 40fps,再次通过对矩形边框的限制条件,如正方形和大小显著,来准确定位矩形位置,获得矩形边框位置后,再通过二值化之前的图像,定位矩形图片位置并且进行神经网络模型识别,通过识别之后向小车发送数字的信息。整个过程只需要大概 200ms,可以迅速的识别并通过三叉路口,唯一遗憾的是,真实赛场上因为光线的变化,在远离靶标的时候,摄像头的打光灯不能很好的照射到靶标上,于是识别三叉路口会存在找不到色块的问题,最终采用了停车策略,来保证稳定通过三叉路口,但在光线良好的赛场,通过三叉路口的速度将会很快。
7.3动物水果识别
同样的动物和水果的识别也和数字识别一样,但会是在识别边框到中心位置之前一直返回矩形边框的中心位置,直到到达图片的中心,发送指令让小车停下并识别靶标,识别完成后再通过,打靶或者直接通过。
图 7.1动物水果识别结果
7.4模型训练
因为模型训练的比较早,保存了两个版本,一种是爬虫爬下来互联网上的图片进行训练的模型,识别能力强,但为了保证没有意外,又针对官方发布的训练集训练了所需要的模型,图片分类模型的训练最重要的就是图片的训练集质量,于是我们为了提高模型的泛化能力,对数据集进行了增强,通过预处理图片,产生更多的图像来补充训练集,从而增强模型的鲁棒性,我们利用了翻转,旋转,缩放,移位,高斯模糊,噪声,运动模糊,以及各种亮度和对比度的数据增强方法。
7.5 Apriltag码识别
Apriltag的识别必须是全摄像头下的识别,暂时没能想到较好的改进办法,采用我们采用总钻风摄像头识别黑块,然后停下检测 Apriltag码内容,并返回给小车指令,小车收到指令后对云台进行控制转向,同时摄像头进入识别图片功能。
§08 RT-Thread移植应用
本部分移植演示是基于逐飞 RT1064的裸机开源库和 RT-Tread源码 rt-thread-v4.0.2文件进行的。逐飞 RT1064的裸机开源库可从 gitee上下载,如下图 8-1所示
图 8-2RT-Tread源码可从 RT-Tread官网->资源->下载处免费下载,如图 9-2所示
8.1移植
1)首先将 rt-tread源码放到逐飞工程目录的 Libraries里,如图 8-3所示
2)打开逐飞工程,打开 keil,在工程目录下建立 rttread_src、rttread _components、 rttread_inc、rttread_lib四个分组。如图 8-4所示
3)将 Libraries->rt-tread-> src中的 C文件全部添加进 rttread_src分组中,如图 8-5所示
4)将 Libraries->rt-tread-> include中的 .h头文件全部添加进 rttread_inc分组中,如图 8-6所示
▲ 图8.6
图 8-6将 Libraries->rt-tread->include->libc中的 .h头文件全部添加进 rttread_inc分组中,如图 8-7所示
5)将内核部分汇编支持的文件 Libraries->rt-tread->libcpu->arm->cortex-m7中的 c文件和 context_rvds.S(如果是用的 IAR则应该添加 context_iar.S)添加进 rttread_lib分组中,如图 8-8所示
6)加载组件文件 Libraries->rt-tread-> components-> finsh,将里面的 c文件全部添加到 rttread_componets分组中如图 9-8所示
图 8-9
7)将 Libraries->rt-tread-> bsp-> imxrt->imxrt1064-nxp-evk中的 rtconfig.h加入
user.h分组中,如图 8-9所示
图 8-10
8)将刚刚添加文件的头文件路径包含进来,如下图 8-10所示
…\\Libraries\\rt-thread\\bsp\\imxrt\\imxrt1064-nxp-evk …\\Libraries\\rttherad_libraries\\include\\libc …\\Libraries\\rttherad_libraries\\components\\finsh …\\Libraries\\rttherad_libraries\\include
图 8-10把汇编的头文件路径也包含进来如下图 8-11所示
…\\Libraries\\rt-thread\\libcpu\\arm\\cortex-m7
图 8-11
9)将 RT-Tread头文件包含进来,如下图 9-12所示
图 8-12
10)打开 seekfree_libraries分组下的 zf_systick.c文件,将 RT-Tread头文件 "rtthread.h"包含进来 ,再将 void systick_delay_ms(uint32 ms)函数里的内容替换为下图 8-13
图 8-13 11)修改 board分组下的 board.c文件将 <rthw.h>头文件和 <fsl_sdmmc_event.h>头文件包含进来,在 board.c文件中添加如图 8-14所示内容
图 8-14再修改 board_init(void)函数,将里面的内容修改为如下图 8-15所示
图 9-15
继续在 board.c文件中添加三个函数,如下图 8-16和 8-17所示
图 8-16
图 8-17在 board.h头文件中添加宏定义以及一些声明如下图 8-18所示
图 8-18
12)
打开 sdmmc分组下的" fsl_sdmmc_event.c"文件将 SysTick_Handler函数注释掉再打开 seekfree_libraries分组下的" common.c"和" common.h"将里面 HardFault_Handler( void)函数和 MemManage_Handler( void)函数以及 PendSV_Handler(void)函数的声明和定义都注释掉即可
打开 sdmmc分组下的 "fsl_sdmmc_event.c"文件,添加 void sdmmc_tick_handler(void)函数,在"fsl_sdmmc_event.h"头文件里面声明该函数, c文件里的实现方式如下图 8-19所示
图 8-19 13)再次编译之后发现还是会出现两个错误,这是分散加载文件错误导致的
图 8-20
只需点开这个 Edit,如图 9-21所示
图 8-21
进入编辑界面在图示位置加入下面两段代码即可,如图 8-22所示 #define RTT_HEAP_SIZE(m_data_size-ImageLength(RW_m_data)-ImageLength(ARM_LI B_HEAP)-ImageLength(ARM_LIB_STACK))
;ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack regiongrowing down ;} ARM_LIB_STACK+0 EMPTYStack_Size{};Stackregion growingdown RTT_HEAP+0EMPTYRTT_HEAP_SIZE{}
图 8-22
14)点击编译, 0错误 0警告,大功告成,连接开发板即可运行 main任务,然后就可以尽情使用 RT-Tread了。如图 8-23所示
8.2 RT-Tread的使用
使用 RT-Tread我们有如下任务 //main优先级为 10 //recognitionTag优先级为 18 //recognition斑马线优先级为 17 //buzzer优先级为 20 //send优先级为 21 //display优先级为 30 //out优先级为 13 //view优先级 16 //两个软件 pit定时器
//多个信号量和多个邮箱
图 8-24
图 8-25
RT-Tread作为一个嵌入式实时多线程操作系统,基本属性之一是支持多任务,然后它还有定时器、信号量、邮箱、队列等可以使用。我们主要使用了它的多任务、信号量、邮箱和定时器。
1)main任务
我们将赛道寻线任务放在了 main()任务,这是我们最核心的任务,所以它的优先级最高为 10。在它里面对其它所有任务和软件定时器进行初始化,而在初始化的时候会执行 rt_sem_take(OUT_sem, RT_WAITING_FOREVER),语句来获取来自出库任务发来的信号量,当没有来自出库任务发来的有效信号量, main任务一直等待,等待出库任务完成。我们出库是开环出库,固定打角和速度,不需要对赛道识别。当出库完成,出库任务发送信号量给 main任务, main任务接收信号量,才继续往下执行。
在 main任务的 while(1)循环体里面还需要执行 rt_sem_take(camera_sem, RT_WAITING_FOREVER);语句,等待获取来自摄像头中断发来的信号量,每次摄像头拍完一次照,发送一个信号量给 main任务,任务一接收到该信号量就开始分别发送信号量给 ApriTag识别任务和斑马线识别任务,因为这两个任务优先级较低,所以这两个任务还是处于等待状态。然后 main任务就进行赛道识别。否则就等待,保证能及时分析赛道,且不会重复分析同一张图片。
2)out任务
出库任务,它的优先级是 13,我们出库是开环控制,当小车未完全出库 main任务因为未接收到信号量就一直等待。
而同时因为初始化了 display任务以及用于按键的软件 pit等其它功能,使得我们可以在屏幕上可视觉化的按按键调整参数,调整完毕,打开电机电源,当 ”out”出库任务判定出库成功即发送信号量给 main任务, main任务接收到信号量继续执行下一条 rt_thread_delete(tid_OUT),删除“ out”任务,因为它已经完成了它的使命。
3)smotor2舵机调整任务
当识别靶标时需要微调云台,让 openARTmini对准靶标,以提高识别准确率,当其它任务发送相应信号量,该任务接收到其它任务发来的信号量后,就可以根据 openARTmini发来的数字调整云台。因为只有当识别靶标的时候才需要运行该任务,所以这里加入信号量来控制它。
4)Apriltag码任务和识别车库斑马线任务这两个任务里面都各有一个获取信号量
rt_sem_take(camera_semMain2, RT_WAITING_FOREVER);
rt_sem_take(camera_semMain, RT_WAITING_FOREVER);
main任务里面以及提到,每次拍照完,获取拍照中断里发送的信号量之后,main任务会发送 camera_semMain2和 camera_semMain两个信号量,告诉这两个任务拍照完成,这里使用信号量是因为每一张图片只需识别一次,多次识别是没有意义的。
识别赛道上的 Apriltag码任务和识别车库斑马线任务,他们的任务优先级为 17、 18,相对靠后,这是因为识别赛道上的黑块要花费比较多的时间,优先级低一点就不会影响其它任务的执行。
5)view任务
该任务为处理 openARTmini发送过来的数据任务,优先级为 16,并且里面有一个获取信号量 rt_sem_take(view_sem, RT_WAITING_FOREVER)。该信号量由 rt1064负责和 OpenARTmini通信的串口接收中断发送, OpenARTmini每发送一个字节在串口接收中断中都会对其分析是否为结束符号,当串口接收中断判断 OpenARTmini发送过来一个结束字符,就发送信号量给 view任务, view任务开始执行下面的代码,根据 OpenARTmini发来的内容进行判断,进行模式选择。切换一些标志位,影响其它任务。使用信号量是因为该任务只有 OpenART mini向 rt1064发送一段完整信息时才需要执行,别的时候不需要执行在,这样就能方便其它低优先级任务处理信息。
6)buzzer任务
该任务是蜂鸣器任务,优先级为 20,并且里面有一个从消息邮箱获取消息的语句 rt_mb_recv(buzzer_mailbox,(rt_uint32_t*)&mb_data,RT_WAITING_FOREVER);否则就无限等待。别的任务需要使用该任务时,只需发送相应的消息到该消息邮箱,该任务接收到消息,根据消息内容,叫一段时间。使用邮箱而不是信号量是因为使用邮箱不仅可以控制蜂鸣器叫,还能控制蜂鸣器每一次叫多久。
7)send任务
该任务是无线串口发送任务,优先级为 21,当小车跑起来的时候,我们可以通过让无线串口发送一些信息给上位机的串口助手,从而更好的分析小车运行情况。该任务是一直执行的,但因为优先级不高,无需担心影响主要任务。
9)display任务
该任务是屏幕显示任务,优先级为 30.该任务优先级最低,因为屏幕显示要消耗大量时间,将其设置为最低优先级,可使其不会对其它任务造成影响,即使后
期小车需要处理的东西越来越多,但也无需担心刷新屏幕的占用问题。 10)用判读按键是否按下的“ button” rt-tread软件定时器
该定时器 20ms进入一次,用于判断按键是否按下,不同按键按下发送不同的信号量,在 display任务里接收,从而实现设置一些参数和切换屏幕显示内容 11)“timer1” rt-tread软件定时器
该定时器 1ms进入一次,在任务里可以进行其它时间的定时。
每 10ms采集编码器信息并根据期望速度对电机控制
每 5ms采集电磁信息并处理以及根据所有任务处理的结果对小车前轮舵机
进行判断。
使用定时器是因为控制电机、控制舵机、获取电磁信号都需要周期执行,产生 pwm波和读取编码器数值等都需要占用一个 RT1064的硬件定时器。而硬件定时器的数量是固定的, RT-tread的软件定时器拓展了定时器的个数,可以很好的解决我们定时器不够用的苦恼。
§09 RT-Thread创新点
9.1利用 RT-Thread提高图像处理程序的稳定性
一开始裸机进行小车软件的开发时,发现实时性差,编写程序代码冗长,在赛道要素逐渐增多,最后编写完成时,甚至 RT1064600Mhz主频都会有一些延迟,这里的延迟体现在摄像头拍摄的处理帧明显下降了,我们通过研究代码发现,裸机运行时,为了调试方便,将图像打印呈现在 lcd屏幕上,这一过程耗费了许多的时间去运行代码,然而这一过程对于小车任务的完成并没有任何帮助,于是在研究讨论之后,决定使用 RT-THread操作系统来解决问题。
在智能车控制系统开发过程中引入实时嵌入式操作系统,不仅可以充分发挥不同芯片的性能,让智能车跑的更加顺畅;而且在一定程度上屏蔽了不同单片机底层硬件细节,提高控制软件开发效率。
首先,我们将摄像头拍摄作为一个任务,每当摄像头拍摄完成就会发送一个信号量给赛道图像处理任务,然后执行一次赛道处理任务,来保证拍摄一张图片,执行一次图像处理,解决了初期使用操作系统时的资源浪费。
其次,我们将 lcd屏幕显示图像单独作为了一个任务,将其优先级改为最低,可以避免 lcd屏幕任务的执行导致其他关键任务的运行被堵塞,同时我们可以观察 lcd屏幕的刷新速度来判断赛车在各个赛道元素上的程序运行帧率来改良代码,当我们观察到 lcd屏幕发生卡顿时,就说明此时操作系统的运行任务跳转变慢了很多,我们通过研究可以改进此时的代码,来提高代码运行效率。
▲ 图9.1 定时器中断
图 9.1定时器中断如图 9.1所示,我们建立了一个定时器中断,在进入任务前开启定时器中断,在任务完成后结束定时器中断,同时在定时器中断中进行计时,单位为 us,便可以轻松得到这一任务的执行时间,随后通过串口发送到上位机,通过分析得到哪一段代码运行速度可以得到提高。
这样做一个方面可以提高代码运行的稳定性和实时性,另一个方面可以对代码的开发提供一定的直观帮助。
9.2利用 RT-Thread减少开发代码的难度
关于 Apriltag码任务和识别车库斑马线任务,在裸机运行时,遍历图片,识别赛道上的黑块要花费比较多的时间,花费了 2ms左右的时间,而我们摄像头拍摄的间隔为 10ms一次,也就是说,留给其他赛道要素和任务的极限时间只有 8ms不到,导致对我们开发代码的运行速度要求较高,开发后期令人头疼。
我们决定使用 RT-Thread操作系统,很好的避免了识别斑马线和 Apriltag码可能造成其他如赛道边界识别等关键任务堵塞的烦恼,解决了我们代码开发的后顾之忧。
这两个任务里面都各有一个获取信号量
rt_sem_take(camera_semMain2,RT_WAITING_FOREVER);
rt_sem_take(camera_semMain,RT_WAITING_FOREVER);
当一次拍照完成, main接收到摄像头任务中 csi_isr()里发送的信号量, main函数发送上面两个信号量,使用信号量是因为每一张图片只需识别一次,多次识别是没有意义的。
识别赛道上的 Apriltag码任务放在了 “recognitionTag”任务里,识别车库斑马线放在了“ recognitionGarage”任务里,他们的任务优先级为 17、18,相对靠后,优先级低一点就不会影响其它任务的执行。
9.3利用 RT-Thread提高车模软件开发速度
我们在初期车模软件开发过程中发现,当两个组员同时敲写代码时,往往组合到一起之后会发生很多的问题,例如运算顺序导致程序运行不下来,变量重复定义导致错误,必须要反复调整很多代码参数,会耗费大量的开发时间,而往往难以发现代码的错误,因为对某一赛道元素识别的处理顺序会直接影响到其他赛道元素。
后期我们使用了 RT-Thread来开发时,将每一个赛道元素都作为任务来开发,不用考虑某一元素对另一元素的影响,所有元素任务都对原始图像进行判断,避免了参数的调整,规避了很多细节问题,降低了两人同时开发软件代码并进行组合的难度,极大的提高了软件开发的速度。
9.4 RT-Thread社区贡献
在移植 RT-Thread到 RT1064过程中遇到了两个变量未定义的错误,在工程里搜索了,别的地方没有这个变量。在 RT-Thread社区中进行搜索遇到了有同样问题的人,如图 9-1所示
在翻看讨论后,发现其说明了是工程的分散加载文件,但没有详细提出解答方案,于是在我翻阅资料查阅解决问题后,将自己的详细解决方案发布在下面,希望让下次遇到此问题的人能及时解决。
只需点开这个 Edit,如图 10-2所示
进入编辑界面在图示位置加入下面两段代码即可,如图 10-3所示
#define RTT_HEAP_SIZE(m_data_size-ImageLength(RW_m_data)-ImageLength(ARM_LI B_HEAP)-ImageLength(ARM_LIB_STACK))
;ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack regiongrowing down
;} ARM_LIB_STACK+0 EMPTYStack_Size{};Stackregion growingdown RTT_HEAP+0EMPTYRTT_HEAP_SIZE{}
9.5 RT-Thread对相关辅助开发功能
在调试小车的过程中,我们都会用到屏幕显示和无线串口发送这两个功能。以往裸机开发中由于任务是顺序执行,这两个任务不可避免的会占用其它任务处理赛道的时间,而这两个任务对于赛道处理毫无用处,只是给开发人员看。但加上了 RT-Thread操作系统,只要调整好任务优先级的高低就可以完美避免这个问题,只有当单片机处理完其它任务处于空闲的时候才会被这两个任务占用,我们全程都可以放心的添加各种辅助程序。
9.6 RT-Thread对竞赛任务完成的帮助
今年我们智能视觉组加入了许多新元素,例如识别赛道上的 Apritag码。根据比赛要求这是一个放置位置随机,可能放在在直道或者弯道上,但又极其重要的元素。因为一旦识别不到就不能进行后续对水果打靶或者在动物区停留 3秒的任务。而一旦错过就是加时 15s。但在实际开发中,我们采用用总钻风摄像头识别赛道上的黑块个数来判别是否有 Apritag码,经过测试后,发现这个任务会占用较多的时间,大概有 1-2毫秒。同时为了识别车库我们采用识别斑马线判定为车库的方法。而这两次识别会占用较多时间,虽然它们在赛道上占比不多,但识别它们又非常有必要,所以得全程识别。在裸机开发上,就很难做到既能一直识别这两个元素,又不会对其它主要任务造成影响。而引入 RT-Thread就完全无需担心这种问题,可以全程识别,很好的帮助我们完成了比赛所有内容。
§10 总结
10.1 RT-Thread主要功能应用要点
我们主要使用了 RT-Thread的多任务、信号量、邮箱和定时器。多任务对我们视觉组组别来说真的是非常便捷,我们只要把主要的任务安排在高优先级,而把一些次要任务依次往下排列,就可以兼顾所有任务的识别,并且无需担心因为一些任务耗费大量时间耽误主要赛道
以上是关于基于RT-Thread开发智能视觉组智能车 - 温州大学 - 春华秋实的主要内容,如果未能解决你的问题,请参考以下文章
基于 RT-Thread智能车控制算法开发-河南科技大学ROCKET
基于RT-Thread操作系统的 基础四轮组智能车设计与实践