收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-
因为每个学校的视觉组都有着自己独有的故事,而本文所描述的观点综合了笔者以及组内其他靓仔🤵一年下来的感受和体会(文中附有其他靓仔们的微信)。当然大家有什么想法也欢迎在评论区留言交流🤔。
-
如果你好奇RM视觉组的小伙伴平时都在熬夜干什么🧙♂️,可以把本文当成小说📑来看,希望看完后你能有一个大致的概念。
-
如果你是刚进视觉组的朋友,建议前期可以多看一些别的学校的开源的代码,熟悉整个项目的流程,知道每一个模块是怎么设计的,干了什么,不同学校之间的代码差异在哪,各自的特点是什么,能从中学习到什么知识,有什么更好的思路,这都是阅读源代码的时候应当思考🤒的。
- 这里贴一下RM论坛上面各名校的开源代码,大家可以下载下来学习一下。——>🚋各名校开源代码方案🚋
- 顺便宣传一波深圳大学RoboPilots战队的开源方案,个人感觉非常好,作者写得细致入微😱
——> 🚀深圳大学2019步兵视觉开源方案🚀
-
如果你是视觉组的烙饼,也希望能够指导本文描述得不够严谨的地方🚝。
1. 视觉基本概念
由于笔者是参加的2020年的比赛,因此从2020年的赛制进行本文的展开介绍。
1.1 关于各兵种的视觉
-
今年一共有七个兵种:步兵,英雄,工程,哨兵,无人机,飞镖,雷达站。其中新增了飞镖和雷达站。关于每一个兵种,都有着各自的战术定位,这并不是本文的讨论主体,但他们涉及到的视觉功能主要是以下几类:
- 设计自瞄算法,以供机器人精准打击敌方装甲板🎯。
- 设计能量机关瞄准算法,以供机器人有效打击大小符能量机关🎆。
- 设计反导算法,以供拦截敌方飞镖(笔者知识有限,无法提供什么干货)。
- 设计雷达站相关算法(如目标检测),辅助团队做出战术调整。
- … … … … … … … … … … …
-
其中自瞄算法的应用范围最广,在步兵,英雄,工程,哨兵,无人机,飞镖上都有应用,因此也是笔者认为最重要的,它的目的就是告诉机器人往哪里打,且能够有效打击⛽****。攻击敌方基地🕍,攻击敌方机器人🤖,都离不开自瞄算法。
-
而能量机关瞄准算法也是非常重要的,毕竟BUFF的加成对于团队来说不言而喻。而且今年的小符为匀速旋转,大符为以sin函数😕的速度进行旋转,加大了打击的难度。
-
反导算法这方面笔者没有什么可以介绍的,毕竟涉及到的变量因素过多,暂时无好的算法推荐,大家可以多思考下这个方向。
-
雷达站相当于上帝视角,能够看到整个地图,一个思路就是通过目标检测检测敌方机器人的动向,辅助我方机器人做出有效的战术调整。主要涉及目标检测相关知识,这部分可参考RM论坛中大佬的博客——>✈✈✈RoboMaster 基于官方数据集目标检测训练代码开源
1.2 关于mini PC(小电脑)
- 小电脑其实就是每台机器人的”大脑“👻,指导机器人动起来和怎么动。我们的代码最终就是通过小电脑来指导机器人实现各种功能。
- 小电脑的特点就是体积小。不同学校使用的小电脑品种也不尽相同,比如我们使用的是NUC。
- 可参考这位大佬的博客,里面详细介绍了几款主流计算平台——>✈✈✈RoboMaster 计算平台的选型与感想
- 像笔者到了后期调车的时候,一般就是拿着显示屏🖥,鼠标🖱,键盘⌨这三件套连接机器人上的小电脑,然后坐在地上调车。
- 连显示屏的HDMI线真的是又爱又恨🤸♂️
- TIPS:调车过程要时刻注意电池电量🚦!!! 如果调得太入神然后发现突然黑屏,那代码可能就没保存了。
1.3 关于Linux的学习
- 如果代码是在Linux系统下面跑的,而且你没接触过Linux,那么前期可以学习一下Linux命令行的基本操作。
- 然后就是Shell脚本的知识,主要是为了编写程序自启动脚本,看门狗程序🐕,这样子机器人每次开机或者程序中途退出的情况就能自动运行程序,不然每次比赛都开机手动点程序运行也不太现实。
- 如果到了后期调车,经常会迭代代码,这时候我们就要对代码进行版本管理🐳,以便我们能够找到各个阶段写的代码。一般有两种方法保存:
- 创建若干个文件夹,标注上当前版本代码的关键信息,以便后续快速定位。(简单方便)
- 通过git进行版本控制,可学习廖雪峰的git教程。(规范整洁)
1.4 关于相机
- 前面说到相机是机器人的眼睛,而不同相机的功能大相径庭,因此了解一部相机如何使用,有什么功能是非常重要的。
- 比如画面太亮影响装甲板的识别,怎么通过调用函数和设置参数手动调低相机的曝光。当然还有调gamma,各通道的增益,白平衡等参数,一般相机的说明文档📒都有各函数的调用介绍的。
- 比如笔者在摸索的过程中发现,如果**想通过低曝光来排除掉外界光的干扰🌤,但是又想在低曝光的前提下看清楚装甲板的数字,然后摸索出可以通过调整gamma值或者提高绿通道的增益来达到该目的。**这就是利用相机自带功能来辅助我们达到某个效果的典型例子。
- Tips:如果使用了提高绿通道的增益的方法,原先识别 红色灯条🔥 跟 蓝色灯条🌌 的算法要反过来用,别问我怎么知道的🐙。
- 相机的帧率越高,则获取图的速度越快,得到的图的效果就越好(如果帧率很慢的相机,且敌方机器人在左右移动,拍出来的视频可能就有光轨延时摄影🌠的feel)。相机的帧率会跟调整的各参数有一定的关系,比如曝光调得太高,可能帧率就会下降,拍出来的视频看起来一卡一卡的。
- 焦距的调整:如果相机拍出来的画面像是近视眼看到的一样模糊(可能是机器人飞坡测试的振动加上相机上调焦距的螺丝没拧紧导致错位),那这时候就需要调整焦距了,原因有二:
- 由于模糊效果拍出来的装甲板灯条会很粗,会影响到后期自瞄算法的灯条识别效果🌵。
- 焦距的改变导致PNP算法(能够根据二维图像计算目标到相机之间的三维距离🚂)的计算结果不准确,需要重新标定。
(🏳🌈当然如果你是使用类似小孔成像的方法得到相机与目标的距离就另当别论了)
- 标定,获取PNP所需参数: 在PNP算法中,我们需要通过相机内部的参数带入算法进行计算,这时候就需要对当前焦距下的相机进行标定了。
1.5 关于串口通信
所谓串口通信,就是我们(视觉组)得知了要打哪里,然后接下来就要通过发送信号给机器人,让它动起来(电控组)。
这个过程联通了视觉组和电控组的信息隔阂,完成了机器人动起来和怎么动这两个功能的闭环💫。
1.5.1 关于信息传输
- 串口模块负责联通视觉组和电控组的信息🚧:
- 视觉组可以通过一个接收函数得到电控组发送过来的信息:
- 敌方为红色还是蓝色
- 当前模式是击打能量机关还是自瞄
- 是否启动吊射基地模式
- … … … … … …
- 也能通过另一个函数将敌方装甲板的信息发送给电控组:
- 比如计算得到敌方装甲板位于相机的右边5°,下面2°
- 自动识别到敌方为陀螺状态
- … … … … … …
- 视觉组可以通过一个接收函数得到电控组发送过来的信息:
1.5.2 关于信息传送失败
- 调车的时候经常遇到信息传不过去的问题,有可能是视觉组这边没发过去,也有可能是电控组那边没有接受到。
- 记得有天晚上和电控的靓仔各自找问题找到半夜,才发现是串口线路某个位置接反了所导致。因此这里 整理一下笔者一年来遇到的所有可能性,以便判断是否是视觉组这边没发送成功的问题 ,不然经常让电控小伙伴等你找bug你可能也会很尴尬:
- 开机未赋予串口权限: (加上自启动就不必处理)
手动赋予权限的方法(举个🌰):sudo chmod 777 /dev/ttyUSB0
- 通信协议出现问题:由于不同车的协议可能在调的过程中发生变化,所以可能导致传不过去,这时候可以找到以前版本的通信去测试,同时可以看传输函数里面的变量类型是否正常或者看看有没有加传输函数send。
- ttyUSB1的情况出现:一般情况下串口的名字为ttyUSB0,但因为某些原因如被撞了一下之后,串口名字可能会发生变化,所以这时候看看名字是否改变了即可。一般重启一下就好了。
- 电控那边的串口线的两个位置接反了:适用于导致偶尔能收到一次信息,其他时候都收不到的情况。
- 串口未初始化:(有一次是跑自己程序传输数据不成功,跑别的程序再跑自己的程序就ok,然后找到这个问题)
1.5.3 调通信的技巧
- 比如有7个标志位,调的时候比如将vdata={1,2,3,4,5,6,7},看看电控那边接收到的数据是不是正确的顺序,如果不是,很大可能是 这七个变量定义的时候没有按照顺序定义(虽然感觉跟定义顺序没区别,但实际有一次就是这么一回事),这时候将变量定义的顺序设置成发送的这七个变量的顺序即可。
1.5.4 自启动的设置
- 有时候电控组的小伙伴想要调车,但是这时候我们可能在上课或者在忙别的事情,这时候设置了自启动就能让电控小伙伴随时都能调车啦。😁
- 添加自启动脚本 :拿ubuntu系统举例,打开Ubuntu系统的 启动应用程序(start up application),将脚本添加进去即可,很方便。
Tips:如果重启之后自启动失败,可考虑是否是未赋予该脚本权限🔑所导致的。
2. 简述各视觉算法
- 由于笔者主要负责自瞄这一部分的算法,因此下面主要针对自瞄这一部分进行介绍,其他的算法邀请了组内其他靓仔负责介绍。
2.1 自瞄算法
顾名思义,自瞄算法的作用是自动瞄准敌人,精准有效地攻击敌方机器人。下面进行大致的介绍。
- 实际上自瞄算法的实现方法有很多,比如可以通过目标检测🧟♀️定位装甲板的位置,也可以通过Opencv库定位敌方装甲板,可以在Python中实现,也可在C++中实现。由于笔者的自瞄算法是基于C++中的Opencv实现的,因此主要介绍一下该框架下的自瞄算法。
- 实际上比赛进行到今年,随着各个学校的开源,自瞄算法已经算是比较成熟的了,因此笔者今年的主要研究方向为自瞄算法延伸出去的分支,比如如何自动识别敌方陀螺状态。
- 下面描述的重点不在于各算法中的细节,而是带大家大致过一遍整个流程。
2.1.1 需要掌握什么知识
-
如果你也是基于C++上使用Opencv来实现的自瞄算法,那么首先你得掌握C++的相关知识,主要是:
- 🔸类和对象的编写
- 🔸多文件之间的链接
- 🔸STL常见容器的使用(vector,map,list)
- 🔸多线程的用法(用于缩小执行时间)
- … … … … … … … …
-
然后就是学习OpenCv的知识,这是整个自瞄算法的核心。网上关于OpenCV的教程非常多也非常好,具体教程也不是本文的重点。这里我主要整理了一下自瞄算法中可能用到的Opencv的知识📀:
- 🔹视频的读取和保存(VideoCapture,便于后期一帧帧调试BUG)
- 🔹通过相机进行拍照(拍标定板用于PNP的参数标定)
- 🔹Mat的用法(非常重要!!包括对图像的各种处理)
- 🔹旋转矩形的深入理解(RotatedRect,掌握角度的范围和含义,四个点的编排顺序)
- 🔹ROI区域(用于减少每一帧图像的处理时间)
- 🔹图像阈值的概念(用于调参二值图的效果)
- 🔹中值滤波,均值滤波(去噪点)
- 🔹Canny,Sobel,Laplacian(边缘提取)
- 🔹形态学操作:如开运算,闭运算,腐蚀,膨胀(用于调整二值图的效果)
- 🔹PNP算法的使用(SolvePNP,用于根据相机获取的二维图像计算出三维世界中相机距离真实物体的距离)
- … … … … … … … … …
2.1.2 什么是自瞄算法
- 自瞄算法的核心在于判断是否存在敌方装甲板,如果存在,那么具体在哪里。
- 比如下面的思维导图展示了笔者今年的自瞄算法的整个流程:
2.1.3 自瞄算法的具体流程
-
通过相机,我们能够源源不断地获取到当前的画面,也就是一帧帧的图像。自瞄算法处理的对象,就是这每一张图像。
为了让便于分离灯条与其他光线,一般将曝光设置得很低,如下图为相机获取到的画面:
-
预处理🕛:因为我们要从图像中找到装甲板上的灯条,因此需要先对图像进行预处理,即排除掉画面中的其他多余的光线。下面是笔者今年的处理方法,具体细节就不展开说啦,毕竟本文只是让大家有一个宏观的认识。
下图为经过预处理之后的效果(二值化图像),可见除了灯条外的地方几乎都被排除掉了: -
找到符合灯条条件的轮廓🕒:我们通过预处理得到的图片是二值图,即我们把可能是灯条的位置处理变成白色,其他敌方都是黑的。此时可以通过Opencv的函数对轮廓用旋转矩形框起来,然后通过几何关系判断其是否符合灯条的条件(角度,长宽比等条件)
下图为框选出来的符合灯条条件的灯条: -
匹配装甲板🕔:如果上一步得到的灯条个数大于1(毕竟装甲板的灯条都是成对的),就进行装甲板的匹配,主要是通过两个灯条的几何信息进行匹配(高度差,宽度差,角度差,形成的装甲板的角度,长宽比…),最终得到若干个装甲板
下图为用黄色矩形框选出来的装甲板: -
选择最后的装甲板🕘:如果上一步得到的装甲板个数大于0,则进行装甲板的选择。装甲板的选择有很多种方法,比如下面介绍的两种:
- 选择距离相机中心点最近的装甲板,这样子相机偏移的位置就比较小。
- 通过对装甲板进行数字识别,按照一定的击打优先级进行选择,比如优先打英雄,再打步兵,再打工程。
-
PNP结算装甲板信息🕙:我们找到了装甲板的位置,还需要告诉相机应当如何移动才能让枪管指向敌方装甲板的中心。这里的如何移动,其实就是角度的偏移,左右偏多少度(yaw角),上下偏多少度(pitch角),相机距离装甲板的距离是多少(可用于后续介绍的抬头补偿)… …得到这些信息之后,发送给下位机,相机就知道怎么动啦。
- 看到这里,对一张图片进行自瞄算法就介绍完了,而我们相机每秒钟可以获取到很多张图片,因此上面所说的几个步骤一秒钟就要执行非常多次,因此算法的时间复杂度就显得非常重要了,一般是几毫秒处理一张图较好,否则就会因为延时而带来击打不准的负效果。 而多线程就是一个加速的好方法,因为相机获取图片和我们处理图片是两个相对独立的过程,因此可以创立双线程进行加速。
- 下面是视频呈现的自瞄算法的效果:(如果是比赛的话尽可能关掉这些展示图,不然很耗时)
2.1.4 自瞄算法的其他细节
2.1.4.1 掉帧处理
-
当我们打中敌方机器人时,其装甲板上的灯条实际上会闪一下,而这个闪一下的过程,相机识别到就是没有装甲板,因此这时候枪管🏹就不会移动。
- 对于不断左右移动的敌方机器人(如哨兵),如果我们因为闪一下的时间而不动,就会跟不上敌人,这很明显是我们想要避免的情况。
-
而掉帧处理就是专门用来处理上面的情况。掉帧处理的方法有很多种,下面简单介绍一种:
- 首先我们需要测试灯条闪一下的时间对应到相机这边最多是多少帧(不同相机的帧数不同,比如笔者的相机闪一下最多是六帧)。
- 然后要记录当前连续掉帧的次数,如果在阈值范围内(一般设置在上一步测试的最大帧数再大一点,比如笔者设置了8),我们就认为它是在掉帧,这时候我们发送掉帧前的信息即可(或者乘一个系数缓和一下),目的是不要让枪管骤然停下来。
2.1.4.2 抬头补偿
- 我们都知道子弹是具有初速度和重力加速度的。距离击打目标越远,初速度越小,则重力下坠越多,因此在较远距离,初速度较低的情况下,可以考虑做抬头补偿,即让枪管往上仰。
- 由于子弹初速度我们是可以通过串口通信从电控那边得到数据,因此这个好说。
- 但是距离存在一个很致命的问题,比如PNP计算出来的距离与真实的距离是有偏差的(尤其是侧对着我方相机的装甲板,通过PNP计算出来的距离偏差较大)。要解决这个问题,可以从两个角度考虑:
-
让计算出来的距离更准确🚆:
- 一个解决思路是改进PNP算法,比如我们取计算PNP的四个点,比如可以尝试外四个点或者内四个点或者对点进行处理,当然我试过许多方法都是治标不治本。大家也可以思考下如何进行改进。
- 采用别的计算距离的方法,比如线性拟合法,即根据灯条在二维图像中长度或者宽度的大小来决定真实的距离,比如距离得越远,在二维图像上看起来越小,该方法的关键在于求系数。
-
根据不同距离的范围设置不同的抬头补偿📏:比如2-3米设置一个补偿值,3-4米设置一个补偿值…
-
2.1.5 反陀螺算法
-
现在小陀螺基本成了强队的标配,然而采用普通的自瞄算法击打处于陀螺状态的敌方机器人,由于高速旋转下枪管识别的方向不断切换,造成枪管一直抖动,击打方向不稳,命中率较低🙃的现象。如下动图所示:
-
针对这个问题,我们可以思考两点:
- 我们如何判断敌方机器人是否处于陀螺状态🐣
- 得知敌方机器人处于陀螺状态后,如何设计反陀螺策略🏑
2.1.5.1 自动识别敌方陀螺状态算法
- 判断敌方是否位于陀螺状态,最直接的方法就是操作手按一个按钮🔨然后反馈给程序,再执行反陀螺策略。但是这个对操作手的要求比较高,本身比赛中就要考虑很多情况,还要按一个按钮,也太不友好了,果断pass掉这种方法❌。
- 那么我们能不能通过算法来自动判断敌方是否处于陀螺状态呢?我觉得肯定有,但是网上搜了个遍也没看到,开源代码中也没看到类似的思路,Opencv中也没有现成的库可以调用,因此需要从零开始设计算法🥌。
- 当我们一无所有的时候,总是会思考我们能够拥有什么。因此设计算法的第一步,就是罗列出我们能够根据相机得到的信息:每一帧的图像的装甲板的信息(坐标,长度,高度,角度)。除此之外,好像就没有有用的信息了。但是我认为大道至简,因此足够了。
- 然后我们来思考,对于旋转状态下的敌方机器人,我方相机识别到的画面有什么特点⭐。如下动图所示,我们识别到的装甲板不断发生切换,🍤敌方机器人是顺时针旋转,刚开始我们慢慢向左跟随敌方装甲板,然后在一瞬间就识别到右边的另一块装甲板,如此往复。
- 因此切换装甲板的前后两帧之间的信息🍥是该算法的关键。而该算法就是建立在这个信息上构建的。
- 下面展示该算法的大体框架(最终框架还有很多细节没写进来,有兴趣可自己探索),其具体实现细节就没必要展示了,简单来说就是通过🚠前后两帧识别到的装甲板的几何信息(高度差,宽度差等)判断敌方是否处于陀螺状态,进而自动决定是否使用反陀螺击打策略。
- 本来只是想着能够识别原地旋转的敌方机器人就很好了,没想到测试发现,敌方陀螺+移动的情况也能准确识别出来🤯,可见算法的鲁棒性非常重要。
- 需要注意的是,只有当敌方的旋转速度大于某个值的时候才会识别为陀螺🎡,如果转得很慢的情况下是不会判断为陀螺状态的(实际上敌方小陀螺速度慢的情况下,测试发现使用或者不使用反陀螺策略都差不多)
- 该算法能够判断敌方小陀螺是顺时针转🤾♀️还是逆时针转🤾♂️还是顺着顺着突然切换逆时针🤸♀️,也能够计算出小陀螺的旋转角速度💫等信息,也不打算在这里细说了。
2.1.5.2 反陀螺策略
- 上面我们能够判断出敌方是否为小陀螺状态了,下面就要针对前面的问题进行反陀螺策略的设计。反陀螺策略的方法可以随便想,我下面提供的策略只是基于测试过程中遇到的问题而设计的,仅供参考。
- 之前的问题是枪管不断摆动,导致击打方向不稳定,命中率低。那反陀螺的其中一个策略就是减少枪管的摆动。如何减少呢?我们拿敌方原地顺时针小陀螺⛄的情况举一个例子:
- 若识别到两帧之间的装甲板进行了切换(如下图中从A切换到B),根据A,B的坐标信息预测🎣新的击打点P,将枪管指向P点。
- 其他情况下枪管保持不动🚨,以免出现枪管不断抖动的现象,达到击打方向稳定的效果。
-
可能你会问,如果指向P点,那么当B点还没转过来的那个间隙,不就打不中了?要不要通过角速度判断B点到达P点之后再进行打击,或者通过最小二乘法拟合B点到达P点的时间呢?
- 刚开始我也想了很多这方面的问题,不过测试之后感觉可以先不搞那么复杂,因为我们的🥕大前提是敌方的旋转速度要大于某个值才使用反陀螺策略,因此只要我们使用反陀螺策略,就意味着每次切换装甲板的时间是非常短的🥕。
- 而且测试发现单纯用这种方法,在敌方高速旋转的情况下,命中率最高能达到70%~80%,相比于没使用反陀螺的情况已经有很大的提升,因此笔者就没有在这方面深究。当然我认为还有很大的优化空间。
- 如果敌方是边陀螺边移动的情况,开始想过通过预测其水平移动速度来为P点加上一个偏移量🍣,但是发现这个移动速度的计算很不准,还不如不加的效果好,而且效果也还不错,因此就没深入研究了。
-
下面的动图展示了使用反陀螺策略后的效果,可以看到枪管的摆动明显减小了:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
n/20200822164512477.gif#pic_center)
[外链图片转存中…(img-q71SqJFQ-1715901983976)]
[外链图片转存中…(img-4WG8n7CA-1715901983977)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)