目前博主还在总结并完善智能车的博客,这篇文章也会随时间更新。这里的小技巧其实并不是什么很高级的算法和技巧,全是博主在编写智能车程序的过程中总结归纳的一些经验(可能仅仅只是对我而言的技巧吧,实在是太菜了QAQ,大佬轻喷)。
启用编译器优化
在逐飞官方给出的示例程序、库模板工程中,默认的优化等级都是-O0
,也就是不开启编译优化。这样生成的代码不仅长度大,而且执行速度也很慢。这时,我们就可以去修改MRS的工程设置,将编译器优化打开:
打开“项目-属性”菜单,在设置窗口中如下设置:
其中由上到下直到-Ofast
为止优化等级逐渐提高,但是优化等级也不是越高越好,过高的优化等级可能导致代码执行异常,需要根据实际情况斟酌考虑。我这里开到-Ofast
没有问题,所以就使用了-Ofast
。
修改优化等级后,实测图像处理速度提升明显(大津法35.3fps->50fps,索贝尔43.3fps->50fps,直接跑满了总钻风在188*120分辨率下的最高输出帧率),应该是大量优化了逐飞库中实际上没有执行但是仍然占用资源的程序。需要注意的是,优化等级提高后,编译器的检查也会变严格,Warning会变多,有些在低优化等级下不会报Warning的也会报Warning了(说的就是你逐飞库)。在后续进行Debug时,过高的优化等级可能导致变量无法正常在MRS侧边栏中显示,这时候把优化等级开到-Og
就OK,这一优化等级是专门为了Debug而设计的,编译器会保留大量的Debug信息。
元素识别的跳变沿检测与多帧连续判定
做图像处理需要有的意识是,在智能车图像处理中,时间刻度是以“帧”作为基本单位进行衡量的。
元素识别结果反映在标志位的“0、1”变化上,由于每帧图像进来是有一定间隔的,这个信号也是一个离散信号,是一系列的散点,但是仍然存在“跳变沿”。在元素识别的时候,可以设置一个上一帧识别某一元素结果的标志位element_backup
,然后在这一帧识别这个元素的时候重新备份一下这个标志位(element
= element_backup
),这样就可以根据上一次判断的结果来进行“上升沿”和“下降沿”的判别,从而可以定位一些需要精确定位到“没识别到->识别到”或者“识别到->未识别到”的过程。
识别元素的时候,其实是有很多帧的时间,该元素是在视野内的。如果不引入多帧判定的机制,只要有一帧误判了某元素,那标志位就整个乱套了。所以,可以引入一个多帧判定机制,根据设定每个元素每个状态的判定帧数阈值,然后引入一个判定到特征帧计数器(我称判断到某元素的一幅图像为该元素的一个特征帧),每次只有判定到足够多的帧数之后才置位元素标志位,同时清除特征帧计数器;同理,没有识别到某元素的时候也可以引入一个未识别到特征帧计数器和未识别到特征帧计数阈值。引入这个机制之后,通过调整特征帧的阈值,就可以调节识别元素的前瞻量,并降低单帧可能带来的误判概率了。
元素锁
元素锁是个非常有用的东西,它允许在识别到一个元素后,“锁住”该元素的状态,在该状态下只允许调用识别该元素的函数,直到识别函数清除掉了该元素标志后才释放元素锁,这样就可以避免在元素内对其他元素的误判发生。这个机制非常有用而且有效,同时这个机制如果稍加改造,甚至还可以利用这一机制“背赛道”(虽然在线下赛肯定是没用了)。我写的元素锁架构是在所有元素识别函数外平行创建一个新的函数,由这个函数读取元素状态并且根据元素状态来调用相应的识别函数,从而实现元素锁。这就要求函数的封装性非常好,且每个元素识别函数内都必须有一套对标志位完整的置位、状态转换和清除的机制。这个锁机制由两个函数实现,一个函数负责检测这一帧的元素状态,另一个统一的元素识别函数中进行调用和实现。具体的代码就不放了,注释啊什么的都夹在里面比较乱,我这里用伪代码简单说明一下吧:
- 元素状态检测函数
u8 元素状态检测函数()
{
if(!环岛 && !三岔 && !十字 && !坡道 && !其他未列出的元素)//未识别到
元素状态标志 = 0;
else if(环岛 && !三岔 && !十字 && !坡道 && !其他未列出的元素)
元素标志位 = 1;
else if(!环岛 && 三岔 && !十字 && !坡道 && !其他未列出的元素)
元素标志位 = 2;
else if(!环岛 && !三岔 && 十字 && !坡道 && !其他未列出的元素)
元素标志位 = 3;
else if(!环岛 && !三岔 && !十字 && 坡道 && !其他未列出的元素)
元素标志位 = 4;
else if(!环岛 && !三岔 && !十字 && 坡道 && 其他未列出的元素)
元素状态位 = 5;
……
}
- 元素识别函数
void 元素识别函数()
{
switch(元素状态位)
{
case 未识别到:
调用一遍各个元素的识别函数,每个函数后面都加一个元素状态检测函数,注意元素识别函数的调用顺序
break;
case 1:
环岛识别函数
元素状态检测函数
break;
case 2:
三岔识别函数
元素状态检测函数
break;
case 3:
十字识别函数
元素状态检测函数
break;
case 4:
坡道识别函数
元素状态检测函数
break;
case 5:
其他元素识别函数
元素状态检测函数
break;
……
}
}