欢迎光临散文网 会员登陆 & 注册

智能车竞赛 摄像头 简易摄像头搜线算法开源

2022-08-16 20:00 作者:奇异の芒果  | 我要投稿


各位参加智能车竞赛的小伙伴们好呀!


我是一名参加过16届智能车竞赛的过时选手,目前已经结束智能车竞赛生涯了,不过还是没有降低对智能车的关注,去年卓大大发布17届智能车竞赛规则与16届相比可谓是变化颇大,在规则刚发布的那晚上我都快压不住自己躁动的心,恨不得再选个组别冲一回,可惜没法继续参赛,不过看到学校的师弟师妹们的调车成果,一股属于我们实验室的自豪就油然而生了,哈哈。


最近挺多学弟学妹们在问一些关于摄像头巡线的问题,而我早在去年其实就已经做过了一个摄像头简易巡线思路和程序,却一直没来得及去整理,那就正好趁这次机会,把自己之前写的简易搜线程序梳理一下,然后开源出来让大佬们指点一下,同时也希望能让更多刚接触摄像头的车友有一个关于搜线的大体思路,而不是盲人摸象般的去对抗强校传承。


分享搜线思路之前,先给各位车友说一下我的硬件配置,以及为什么要分享简易搜线的方法。


摄像头方面使用的是逐飞的总钻风摄像头(灰度),配合逐飞的摄像头底层驱动函数,就俩字儿——好使;主控用的是CH32V103R8T6,之前参赛允许使用的单片机就这一款,但是搜线的算法是可以在所有单片机上面通用的;主板是使用的逐飞出的CH32V103R8T6双核学习板。以上就是主要的硬件,基本上算是全套逐飞了,不过用起来也确实舒服。


软件部分主要是两个点,第一个是用的MounRivers这个IDE编写的程序,这个软件用起来还是挺容易上手的,就是BUG有点多,正常使用还是影响不大;第二个就是CH32V103的超小RAM和72M主频了,估计不少16届全向组的车友都被这个芯片折磨过,RAM动不动就写爆了,主频还低。而这个也是我分享简易搜线方法的主要原因,103实在是跑不动稍微复杂一点的算法。

OK,前面的废话说完了,下面进入主题。


简易搜线的方法主要使用通过对比度判断边界的方式,之前我也使用过二值化的方式去处理图像,但是因为103本身主频就比较低,二值化之后再显示到屏幕直接就是看PPT,更别提后续的处理了(我这里说的二值化是直接固定阈值,没有计算阈值这一步),因此用了对比度的方式来进行边界搜索。


所谓的对比度算法就是通过判断两个点之间的差比和之后得到的对比度是否超过设置的对比度阈值。当对比度阈值合适的时候,如果两点的对比度超过了阈值则说明此处是明显的黑白交界点,而我们的赛道和边界在灰度图像上正好是黑白交界的地方,因此即可直接得到赛道边界。


下面我举个例子,简单的说一下:


1:点1的值为80,点2的值为70。通过差比和计算:(点1-点2)/(点1+点2),得到的值约为0.067,得到的是一个浮点数,而浮点数需要使用float类型存储,但是CH32V103R8T6只有20K的RAM,所以这里我们直接将其乘以200,转为uint8类型,从而将占用的空间降低四倍,最后得到的对比度就是13。


2:点1的值为80,点2的值为50。通过差比和计算:(点1-点2)*200/(点1+点2),得到的对比度为46。


可以很明显的看出:当两点的值差距逐渐变大时,对比度也会有非常大的变化。我们基于这个即可写出一个简单的对比度搜线程序。


这个时候可能有些同学比较疑惑,上面的例子我直接把两点做差也能得到不一样的差值从而判断是不是黑白交接点啊,确实,这样也能判断出来,但是这样的局限性非常大,大家可以看看下面的例子:


1:点1的值为80,点2的值为70。通过差比和计算:(点1-点2)*200/(点1+点2),得到对比度13。

2:点1的值为800,点2的值为700。通过差比和计算:(点1-点2)*200/(点1+点2),得到对比度还是13。

3:点1的值为80,点2的值为70。通过直接做差,得到差值为10。

4:点1的值为800,点2的值为700。通过直接做差,得到差值为100。


看完了这个简单的举例,相信各位也都知道为什么要采用差比和计算的方式了吧,不过摄像头采集的像素点最大只有256,上面的例子也只是想突出一下差比和计算的优点。


接下来就是程序源码部分了。


先说一下其中宏定义及变量的含义以及作用。


宏定义部分:

Search_Image_W:图像宽度,一般直接定义为总钻风底层的图像宽度。

Search_Image_H:图像高度,一般直接定义为总钻风底层的图像高度。

BLACKPOINT:黑点值,低于此值的直接认为是黑点。

WHITEMAXMUL:白点最大值,通过动态参考点放大得到。

WHITEMAXMUL:白点最小值,通过动态参考点缩小得到。

REFRENCEROW:计算参考点时统计的行数。

SEARCHRANGE:边缘搜线的半径,通过规划搜线范围的方式增加搜线速度。

STOPROW:搜线停止行,也可理解为搜线距离。

CONTRASTOFFSET:对比点的偏移量(用于求对比度的两个点如果用相邻的两个点显然变化程度不够,所以两个点之间一般会间隔一小段距离,我将其称为对比点的偏移量)。


变量部分:

Reference_Point:动态参考点,通过求图像最近几行的像素点的均值得到,因此每幅图像都有自己的动态参考点,然后将其放大或者缩小之后得到白点的最大值和最小值,通过这个来对抗图像中来自光线不均匀的干扰。

White_Max_Point:动态白点最大值,超过白点最大值的点不就行边界判断。

White_Min_Point:动态白点最小值,搜索边界时,低于白点最小值的直接认为是黑点。

Reference_Col:动态搜线参考列,保存图像能看到的最远的一列来作为搜线参考列,左右边界线的搜索则是从此列开始。

Reference_Contrast_Ratio:参考对比度,大于参考对比度则认为是边界。

Left_Edge_Line[Search_Image_H]:保存左边界。

Left_Edge_Line[Search_Image_H]:保存右边界。

Remote_Distance[Search_Image_W]:保存白点远端距离(参考列通过求白点远端距离的最小值得到最远列)。


乍一看这宏定义和变量还挺多,不是说好的简易搜线吗?怎么上来就是一大堆?哈哈,这个确实是简易搜线,使用103都能稳定处理达到50帧,但虽是简易搜线,我们也不能只让它简单搜个线呀,去年参加过西部赛的车友们应该对光线印象比较深刻,侧边的赛道明显要比靠中间的赛道更暗一点,当然我这里没有吐槽举办方的意思,只是举个例,同时这也是我后面重新写了这个抗光线干扰较强的简易搜线算法的主要原因。


下面看看具体的代码:

参考点获取

这部分主要是为了先获取到本幅图像的一些基本参数,其中包括了一个近端的参考点以及白点(赛道)的最大最小值,类似于把二值化的阈值换成了一个区间,小于区间范围的点通通认为是黑点,大于区间范围的点通通认为是白点。

搜索图像参考列

我个人认为搜索图像参考列才是本次分享的灵魂所在。

当小车摆在直道上时,观察原始灰度图像,按照对比度搜线的方法,大家可能都可以想到从图像中间往两边搜线,但是如果是弯道或者各种特殊元素,直接从中间搜线就有些不太够用了。因此,我们在搜索边界之前,先一步求得每一列的白点距离,也可以理解为图像上每列赛道的长度,然后冒泡求的最远的一列作为参考列。

通过参考列往两边搜索边界则可以获得最完整的赛道边界。

当然,如果是特殊元素,那么还可以主动更改参考列来让小车往需要的地方搜线。

搜索赛道边界

搜索赛道边界可以说是最冗杂的部分了,因为103的主频实在太低,所以增加了较多的判断来提高搜索边界的效率,各位可以就着注释来看看各个判断的主要作用,核心理念就是根据参考列往两边搜索边界,搜到边界之后则就行小范围搜索(提高搜索效率,不用遍历整行),如果搜索到边界则保存,如果没搜索到则从参考列重新搜索。

最后就是上面用到的两个子函数,用的最频繁的一个就是限幅函数,防止数据溢出导致单片机死机,另外一个则是冒泡求值的函数,用来求数组的最大最小值。

双边限幅函数
求最大最小值函数

代码的源文件我放在了这个链接:https://gitee.com/seekfree_pudding/pudding_code,欢迎各位有兴趣的大佬研究讨论,也希望大家多多分享一些有意思的算法。


智能车竞赛 摄像头 简易摄像头搜线算法开源的评论 (共 条)

分享到微博请遵守国家法律