智能车镜头组入门(四)元素识别

news2024/9/21 1:38:00

元素识别是摄像头部分中难度最大的一部分,也是我花时间最长的一部分,前前后后画了很长时间,最后还是勉勉强强完成了。

基础的元素识别主要有两个:十字,圆环,和斑马线。十字要求直行,圆环需要进圆环。我们的车在完全不写的情况下,十字可以猛冲过去,只是会非常抖。圆环不能进。

如果十字不能走直线的话,是直接判失败的,因为你抄了近路。圆环不能进入的话,是罚时30s的,我们这一年华南赛区一等奖是要在10s之内的,区2也要15s左右。所以如果你吃了这个罚时,基本上与区二无缘了。

前面我的推文提到过,我们在搜线搜完之后会额外出现两个数组,一个是左右边线的丢线数组,另一个是赛道宽度的数组。我们先对左右边线丢线数组进行处理:

    loss_pointer_L = -1;
    loss_pointer_R = -1;
    if (loss_array[IMG_H - 1][0] == 1) {
        loss_array_L[++loss_pointer_L] = IMG_H - 1;
    }
    if (loss_array[IMG_H - 1][1] == 1) {
        loss_array_R[++loss_pointer_R] = IMG_H - 1;
    }
    for (int j = IMG_H - 1; j > IMG_H - max_length; j--) {
        if (loss_array[j - 1][0] == 1 && loss_array[j][0] == 0) { // head
            loss_array_L[++loss_pointer_L] = j;
        }
        if (loss_array[j - 1][0] == 0 && loss_array[j][0] == 1) { // end
            loss_array_L[++loss_pointer_L] = j - 1;
            loss_counter_L[loss_pointer_L / 2] = loss_array_L[loss_pointer_L - 1] - loss_array_L[loss_pointer_L];
        }
        if (loss_array[j - 1][1] == 1 && loss_array[j][1] == 0) { // head
            loss_array_R[++loss_pointer_R] = j;
        }
        if (loss_array[j - 1][1] == 0 && loss_array[j][1] == 1) { // end
            loss_array_R[++loss_pointer_R] = j - 1;
            loss_counter_R[loss_pointer_R / 2] = loss_array_R[loss_pointer_R - 1] - loss_array_R[loss_pointer_R];
        }
    }

这其中,loss_array[IMG_H][2]中存的是丢线的数据,比如左边线的第100行丢线了,则loss_array[100][0] = 1。

这串代码的意图是把这个loss_array分成四个一维数组,分别为loss_array_L,loss_array_R,loss_counter_L,loss_counter_R。loss_array_L[]这个数组偶数存放的是左边线丢线的开始,奇数存放的是 左边线丢线的结束,loss_counter_L[]这个数组存放的是左边线丢线的个数。便于之后辨别元素。例如loss_array_L[0] = 75, loss_array_L[1] = 55,那么说明左边线的55行-75行存在丢线的情况,那么 loss_counter_L[0]的值则为20。

所需要的另一个数组则为宽度的计算

我们首先需要计算在大直道的情况下,如果把车至于赛道中央,顶端和最低端赛道的像素数量

​
    for (i = IMG_H - 1; i >= 0; i--) {
        compared_array[i] = (HEAD_LENGTH + (END_LENGTH - HEAD_LENGTH) * (float)i / IMG_H) * ELEMENT_RATE;
        if (boundary_length[i] > compared_array[i]) {
            elem_array[i] = 1;
        }
        else {
            elem_array[i] = 0;
        }
    }

​

这样我们可以计算出大直道上理论上的赛道宽度,如果大于这个宽度*ELEMENT_RATE,则有进元素的嫌疑。

之后我们还是像刚刚处理丢线的方式,来把丢线的长度入栈,

    for (int j = ELEM_SEARCH_BEGIN; j > IMG_H - max_length; j--) {
        if (elem_array[j - 1] == 1 && elem_array[j] == 0) { // 开始
            elem[++elem_pointer] = j;
            line_L[elem_pointer] = abs(boundary[j][LEFT] - boundary[j - 1][LEFT]);
            line_R[elem_pointer] = abs(boundary[j][RIGHT] - boundary[j - 1][RIGHT]);

        }
        if (elem_array[j - 1] == 0 && elem_array[j] == 1) { // 结束
            elem[++elem_pointer] = j - 1;//结尾入栈
            elem_counter[elem_pointer / 2] = elem[elem_pointer - 1] - elem[elem_pointer];
            line_L[elem_pointer] = abs(boundary[j - 1][LEFT] - boundary[j][LEFT]);
            line_R[elem_pointer] = abs(boundary[j - 1][RIGHT] - boundary[j][RIGHT]);
        }
    }
    if (elem_array[1] == 1) {                                         //如果图象底部缺线 则将底部入栈
        elem[++elem_pointer] = 1;
        elem_counter[elem_pointer / 2] = elem[elem_pointer - 1] - elem[elem_pointer];
        line_L[elem_pointer] = abs(boundary[1][LEFT] - boundary[0][LEFT]);
        line_R[elem_pointer] = abs(boundary[1][RIGHT] - boundary[0][RIGHT]);
    }

这样,我们就完成了数据的预处理。

我们来具体分析下十字和圆环的特征。

图片是选用逐飞对ccd组进行讲解的图,对上面的标号对镜头组其实不适用,可以忽略

我们可以看出,临近十字有一条很长的直线,圆环的一侧是一条很长的直线,我们可以用这个来辅助判断圆环和十字的特征

然后圆环的入口和出口会有一个很大的丢线区域,十字中也会有一个很大的丢线区域

所以,我们需要一个函数来判断是否为直线 

unsigned char is_straight(unsigned char begin, unsigned char end, line_type line);//直线判断函数

       我们的函数接受三个数据,读取boundary数组的line行,也就是确定左边线还是右边线。其他的有begin end  返回值是1或者0。

现在,我们来开始元素判断

    if (mode == NORMAL && max_length > 95) {

        if (elem_pointer > 0) {
            i = 0;
            while (i <= elem_pointer && i < 10) {
                int lc = 0, rc = 0, llc = 0, rlc = 0, lcbegin = 0, rcbegin = 0;//l counter(long), l loss counter
                if(elem[i] - elem[i + 1] < 20) return;
                for(int a = 0; a < loss_pointer_L; a += 2){
                    llc += loss_counter_L[a / 2];
                    if(loss_counter_L[a / 2] > 25){
                        lc++;
                        lcbegin = loss_array_L[a];
                    }

                }
                for(int b = 0; b < loss_pointer_R; b += 2){
                    rlc += loss_counter_R[b / 2];
                    if(loss_counter_R[b / 2] > 25){
                        rc++;
                        rcbegin = loss_array_R[b];
                    }

                }

这部分是基础的判断,把丢线再次分为了长丢线和短丢线,这是一个很方便的分辨圆环和十字的方式,即圆环为一边的长丢线,十字为两边都有长丢线。

先来进行圆环的判断,前面有提到,圆环的一个很显著的特征是一侧边线为直线,但是实际你的图象不可能特别完美,比如可能会出现这样的情况:

显然 左边线的处理会比较困难,另外,可能由于光照之类的因素,左边线并不是非常的完整的直线,所以需要将左边线简单的做一下分割,分段判断是否为直线,方式和前文提到的数据预处理相差不大

if(loss_array_R[0] == IMG_H - 1){//在底部丢线的情况下 排除底部
    for(int j = 1; j <= loss_pointer_R; j += 2){
        straight_judge_array[++straight_pointer] = loss_array_R[j];
        straight_judge_array[++straight_pointer] = loss_array_R[j + 1];
        if(loss_array_R[j + 1] < 20){
            straight_judge_array[straight_pointer] = 20;
            break;
        }
    }
}
else
{
    straight_judge_array[++straight_pointer] = IMG_H - 1;
    straight_judge_array[++straight_pointer] = loss_array_R[0];
    for(int j = 1; j <= loss_pointer_R; j += 2){
        straight_judge_array[++straight_pointer] = loss_array_R[j];
        straight_judge_array[++straight_pointer] = loss_array_R[j + 1];
        if(loss_array_R[j + 1] < 20){
            straight_judge_array[++straight_pointer] = 20;
            break;
        }
     }
}

然后,进行数据的筛除,把那些特别小的段进行剔除,之后可以进行直线判断

for(int j = 1; j <= straight_pointer; j++){
    if(straight_pointer <= 2) break;
    if(straight_judge_array[j + 1] - straight_judge_array[j] > 5) flag = 0;
    if(abs(boundary[straight_judge_array[j + 1]][RIGHT] - boundary[straight_judge_array[j]][RIGHT])> 5) flag = 0;
    }
for(int j = 0; j <= straight_pointer; j += 2){
    if(!is_straight(straight_judge_array[j], straight_judge_array[j + 1], RIGHT)){
        flag = 0;
        break;
    }
}

这样判断后,圆环基本上可以实现识别到圆环了

if(flag == 1){
    if(system_time - last_elem_finish_flag < 200)	return;
    if(circle_num_L == 0){
        return;
    }
    else{
        mode = FAR_CIRCLE_L;
    }
}

一种比较鸡贼的避免误判的方式 就是把根据赛道把圆环数量写死,也就是代码中的circle_num_L,需要注意每一次发车的时候都需要重置这个值

当然还有一些比较特殊的情况,需要另外加判断去屏蔽,这些需要大家跑车的时候多去试试。

圆环是误判最麻烦的一部分,因为圆环分成了好几个状态,每个状态都需要不同的补线方式,我们采用的出圆环的方式是陀螺仪的积分,这样就要求小车必须转一圈才会出圆环,所以一旦误判圆环就会造成直接的错误。

接下去是十字识别

if(lc && rc){
    if(abs(lcbegin - rcbegin) > 20) return;
    l_down = elem[i]; r_down = elem[i];
    l_up = elem[i + 1]; r_up = elem[i + 1];
    for(int k = 10; k >= 0; k = k - 2){
        if(k > loss_pointer_L) continue;
        if(elem[i] - 3 < loss_array_L[k] && loss_counter_L[k / 2] > 5 && loss_array_L[k] != IMG_H - 1){
            l_down = loss_array_L[k] + 3;
            l_up = loss_array_L[k + 1] - 7;
            break;
        }
    }
    for(int k = 10; k >= 0; k = k - 2){
        if(k > loss_pointer_R) continue;
        if(elem[i] - 3 < loss_array_R[k] && loss_counter_R[k / 2] > 5 && loss_array_R[k] != IMG_H - 1){
            int a;
            r_down = loss_array_R[k] + 3;
            for(int b = 7; b < 13; b++){
                if(boundary[loss_array_R[k + 1] - b][RIGHT] < IMG_W - 5){
                    r_up = loss_array_R[k + 1] - b;
                    break;
                }
            }
            break;
        }
    }
    if(is_straight( l_down, l_down + 15, LEFT) && is_straight( r_down, r_down + 15, RIGHT)){
        mode = FAR_CROSS;
        return;
    }

}

十字识别相对来说简单些,两边都有长丢线:两边丢线到底端都是直线,而且直线误判也没关系,状态机会很快的恢复到正常模式,也不用单独调整权重。所以我们并没有在直线判断上下很多功夫

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2146752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技修复记忆:轻松几步,旧照变清晰

在时间的长河中&#xff0c;旧照片承载着无数珍贵的记忆与故事。然而&#xff0c;随着岁月的流逝&#xff0c;这些照片往往变得模糊不清&#xff0c;色彩黯淡&#xff0c;令人惋惜。 幸运的是&#xff0c;随着科技的发展&#xff0c;我们有了多种方法来修复这些旧照片的画质&a…

【Python基础】Python模块(提高代码可维护性与重用性的关键)

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、什么是Python模块&#xff1f;三、创建模块四、导入模块五、使用if __name__ "__main__&quo…

(黑马点评) 五、探店达人系列功能实现

5.1 发布和查看探店笔记 5.1.1 发布探店笔记 这块代码黑马已经完成了&#xff0c;在发布探店笔记界面&#xff0c;有两块内容是需要上传的。一是笔记内容&#xff0c;二是笔记配图。其中笔记配图部分黑马使用的是上传到本地前端服务器上面的。我我觉得可以将图片文件发布在阿里…

开始你的博客之旅:从零到一的详细指南

创建博客不仅是表达自我的方式&#xff0c;更是与世界分享知识、塑造个人品牌、甚至实现商业变现的强大工具。本文将详细介绍从确定主题到实际运营的每个步骤&#xff0c;帮助你顺利开启个人博客的旅程。 确定博客的主题和目标 在开始博客之前&#xff0c;首先要明确博客的主…

windows环境下安装python第三方包

python环境下&#xff0c;通常通过Anaconda来管理多个python环境&#xff1b; 即通过Anaconda创建python不用的虚拟环境&#xff1b; 1. 安装更新python第三方包&#xff0c;打开Anaconda&#xff0c;在右侧的搜索需要的python包并进行安装&#xff1b; 2.如果没有搜索到&…

【线性规划求解系列】MATLAB中使用linprog解决线性规划问题

linprog - 求解线性规划问题 - MATLAB - MathWorks 中国https://ww2.mathworks.cn/help/optim/ug/linprog_zh_CN.html 本文详细介绍了如何在MATLAB中使用linprog函数来解决各种类型的线性规划问题。首先概述了linprog的基本语法&#xff0c;随后通过五个具体实例演示了如何处理…

《中国数据库前世今生》纪录片观感:从古至今数据库的演变与未来

我的数据库之路&#xff1a;从新手到稳步前行 三年数据库开发的经历&#xff0c;让我从一名菜鸟程序员逐步成长为能够独立解决问题的开发者。这段时间里&#xff0c;我经历过迷茫、困惑&#xff0c;也感受过技术攻关后的成就感。最近看了腾讯云推出的《中国数据库前世今生》纪…

基于机器学习的注意力缺陷/多动障碍 (ADHD)(python论文+代码)HYPERAKTIV

简述 医疗保健领域的机器学习研究往往缺乏完全可重复性和可比性所需的公共数据。由于患者相关数据附带的隐私问题和法律要求&#xff0c;数据集往往受到限制。因此&#xff0c;许多算法和模型发表在同一主题上&#xff0c;没有一个标准的基准。因此&#xff0c;本文提出了一个公…

盘点BDC/ZCU方案常用的芯片

文章目录 1.前言2.方案概述3.主控芯片3.1 RH850/U2A3.2 TC39x3.3 E34303.4 CCFC3007、CCFC3012 4.电源芯片4.1 混合方案4.2 分立方案 5.电机驱动芯片5.1 多路半桥驱动5.2 多路预驱5.3 步进电机驱动5.4 H桥驱动5.4.1 TI的H桥驱动5.4.2 ST的H桥驱动 6.高边驱动芯片/低边驱动芯片6…

自定义项目授权文件生成与认证

基于 TrueLicense 生成的授权文件证书存在很多局限性。所用这里通过自定义的方式来实现一个License授权文件的生成&#xff01; 这里通过非对称加密RSA 的方式来创建 项目授权文件内容&#xff01; 需要注意项目打包后最好将class文件进行防反编译的操作&#xff01; 否则通过暴…

LVGL 控件之滑动条(lv_slider)

目录 一、概述二、滑块1、设置滑块当前值和范围值2、设置滑块部件的模式3、禁用单击4、事件5、API 函数 一、概述 滑动条对象看起来像是在 进度条 增加了一个可以调节的旋钮&#xff0c;使用时可以通过拖动旋钮来设置一个值。 就像进度条&#xff08;bar&#xff09;一样&…

828华为云征文|采用华为云Flexus云服务器X实例部署MQTT服务器完成设备上云

文章目录 一、前言1.1 开发需求1.2 Flexus云服务器介绍1.3 EMQX服务器 二、服务器选购2.1 登录官网2.2 选购服务器2.3 选择服务器区域2.4 选择服务器规格2.5 选择系统镜像2.6 选择存储盘2.7 配置密码2.8 配置云备份2.9 确认配置2.10 立即购买2.10 后台控制台 三、服务器登录3.1…

最佳软件测试基础入门教程4静态测试

静态测试 对工作产品&#xff08;文档和代码&#xff09;进行静态测试和分析&#xff0c;对提高产品质量有很大的帮助。本章介绍了静态测试的一般情况&#xff0c;以及所涉及的具体过程&#xff0c;包括其活动和必须填补的角色。我们描述了四种经过验证的技术和它们的具体优势…

【HarmonyOS 】编译报错:Install Failed: error: failed to install bundle

此问题是由于支付宝sdk兼容性造成的&#xff0c;目前只能删除支付宝sdk依赖&#xff0c;如下图所示操作&#xff0c;删除后需要点右上角的 Sync Now&#xff0c;并等待 Sync 结束 删除后还需要点右上角的 Sync Now&#xff0c;并等待 Sync 结束 uniapp解决方案&#xff1a; htt…

50个必须知道的VS代码扩展

我们即将浏览50个必须知道的VS Code扩展&#xff0c;这些扩展将大幅提高您的生产力&#xff0c;并帮助您像专业人士一样编码&#xff01; 1. TabNine TabNine 是一个基于AI的自动完成工具&#xff0c;它可以根据您的代码上下文和模式建议完成&#xff0c;通过智能自动完成提高…

6000 字掌握 Java IO 知识体系

“子谦&#xff0c;Java IO 也太上头了吧&#xff1f;”新兵蛋子小二向头顶很凉快的老韩抱怨道&#xff0c;“你瞧&#xff0c;我就按照传输方式对 IO 进行了一个简单的分类&#xff0c;就能搞出来这么多的玩意&#xff01;” 好久没搞过 IO 了&#xff0c;老王看到这幅思维导图…

【HarmonyOS NEXT】DevEco快速实现真机截屏,并保存到电脑

点日志点照机图标选一个路径保存图片在ide中右键图片&#xff0c;点复制电脑随便找个位置保存图片https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-screenshot-V5

SpringBoot(40) — SpringBoot整合MyBatis-plus

前言 在上节中我们对MyBatis-plus特性有了一个整体的认识&#xff0c;然后也大致讲了些MyBatis与MyBatis-plus的不同之处。大家感兴趣的话&#xff0c;可参考以下文章 SpringBoot(39) — MyBatis-plus简介 这节我们来讲讲SpringBoot项目如何快速接入MyBatis-plus框架。 今天涉及…

Redis技术解析(基础篇)

1.初识Redis Redis是一种键值型的NoSql数据库&#xff0c;这里有两个关键字&#xff1a; 键值型 Redis-server NoSql 其中键值型&#xff0c;是指Redis中存储的数据都是以key、value对的形式存储&#xff0c;而value的形式多种多样&#xff0c;可以是字符串、数值、甚至jso…

2024-09-13 冯诺依曼体系结构 OS管理 进程

一、冯诺依曼体系结构 1. 外部设备&#xff08;外设&#xff09;&#xff1a; 分为输入设备和输出设备 输入设备&#xff1a;键盘、网卡、鼠标、网卡、磁盘&#xff08;外存&#xff09;、摄像头等 输出设备&#xff1a;显示器、磁盘、网卡、打印机等 2. 存储器 内存 3. 中…