ROBOMASTER TT巡线.2
我们上篇文章完成了对TT下视摄像头的测试,以及相应的使用了内置的RC指令,完成了对飞行器的实时控制。
具体的RC参数含义在这里
这篇文章我们来分析完成无人机的巡线操作需要的一个大致的流程。
我们的TT是一个高度精密的电子设备,所以控制它自己动起来需要很多不同领域的知识才可以,以下的思维导图呈现了我们TT在巡线任务中涉及到的各种知识。
我们之后的文章就是围绕着这些子主题展开,并提供相应的拓展链接
我们先来介绍硬件情况,因为我们巡线只用到下视,所以我们只针对这个摄像头说。
这是一款黑白摄像头分辨率为320x240
帧率为25
色彩编码为YUV
编码方式是H.264
因为大疆将视频接口封装了,所以可能限制了一些大家的想象力,所以我按照下面这份使用说明重新封装了一次接口,对于直接获取码流的小伙伴可能有益
这个是我们的参考依据
使用了CV2中的VideoCapture接口,从UDP端口直接获得视频帧
写法上面可能比SDK稍微精简一些,因为就是简单的发送和回收+日志
下面我们使用这串代码打开下视视频流
如果要是执行,请到空旷地区。因为有起飞指令,或者注释掉
原地起飞,环境可见光太强,而且没有丰富得纹理
使用ffplay捕获视频
如果你是使用了扩展件,连接得是RMTT这个热点
可以在属性里面获得更多得信息,比如是5G得频段
很多人得机器收不到这个RMTT得名字,那就是你得网卡的毛病,建议更换支持5G频段的卡,另外注意IP地址
ffplay -f h264 udp://0.0.0.0:11111
在CMD内输入如上指令
稍等片刻,会丢弃一些帧。然后稳定输出
会捕获到一些视频流的信息,做参考用。
预览画面
如果不想频繁的将PC的wifi断开去连TT的话,推荐使用路由器来进行连接。注意在有扩展器的情况下,一定使用有5G信道的路由器。在编写代码的时候,注意要指定IP
接着就是一台处理算法的主机,目前是在电脑上面做处理。代码可以无需修改就迁移到别的平台,譬如树莓派,Jeston,DJI妙算等~算法还有很大的改进空间,不用太考虑性能问题。
接着是软件的准备,我们主要是用Python语言来编写,完成图像处理+飞行控制。
我们需要准备这些
numpy库,用来对图形进行像素级别的运算
cv2是opencv的Python实现(只是接口是Python,底层还是C++)
time ,测量代码中部分语句的耗时情况,用来优化算法
RobomasterSDK是官方的包,里面拥有更加丰富以及完善的功能,在稳定性,扩展性,以及性能上面有着更加良好的体验,建议安装Robomaster SDK安装(Win10+Py3.8)可以参考这篇文章安装
easytello是封装的简易版TT支持包,优点在于写法简洁,视频可以用cv2的标准接口读取
一款合适的编辑器,可以完成对项目的管理,代码的自动补全,debug等,使用自己喜欢的就好。
算法是我们任务的灵魂
先说图像处理,首先我们了解一下什么是视频:视频(Video)泛指将一系列静态影像以电信号的方式加以捕捉、记录、处理、储存、传送与重现的各种技术。连续的图像变化每秒超过24帧(frame)画面以上时,根据视觉暂留原理,人眼无法辨别单幅的静态画面;看上去是平滑连续的视觉效果,这样连续的画面叫做视频。
也就是说,其实视频是一帧一帧的画面,当按照一定的要求(主要是满足1s超24f)就在感官上动了起来。
所以对于我们的巡线来讲,获取地表的数据,从图中解算出相应的线信息,靠这个信息来指导无人机飞行、
所以我们的处理也是一帧一帧的来处理,不停的更新地表信息,下图是处理流程。
这个线性的流程图,是我们对每一帧都要这样处理
这里注意处理的写法,我当时写的话写错了循环。导致图像处理严重迟钝,达到5s以上的延时,对于一个实时度这么高的项目是不可忍受的。
错误代码,看出来毛病了吗?
每一次循环都是要执行灰度,二值化,计数,中心标定等。而且是按照24f/1
来循环,所以根本处理不过来。这里注意!
要改成,最后的循环只是在处理二值化的图像
当然了更多细节我们之后会谈~
下面我会逐条来解释这些操作的含义以及具体在代码中的实现
灰度化,在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。
可以得到BGR空间到灰度空间的转换公式:
注意这里的2.2次方和2.2次方根,RGB颜色值不能简单直接相加,而是必须用2.2次方换算成物理光功率。因为RGB值与功率并非简单的线性关系,而是幂函数关系,这个函数的指数称为Gamma值,一般为2.2,而这个换算过程,称为Gamma校正。
如果觉得太学术:
灰度化后的R = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
灰度化后的G = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
灰度化后的B = 处理前的R * 0.3+ 处理前的G * 0.59 +处理前的B * 0.11
这个是比较容易接受的一种算法,下面还有一种:
灰度化后的R=(处理前的R + 处理前的G +处理前的B)/ 3
灰度化后的G=(处理前的R + 处理前的G +处理前的B)/ 3
灰度化后的B=(处理前的R + 处理前的G +处理前的B)/ 3
个人推荐用第一个算法,第二个处理出来的图像有些模糊。
对于更深的理解你需要知道什么是像素点,以及什么是灰度化:
像素点是最小的图像单元,一张图片由好多的像素点构成。就像我们的下视摄像头是320x240的。也就是宽度是320像素,高度是240像素。也就是说这张图片是由一个320 * 240的像素点矩阵构成的(可以把矩阵理解为C语言中的二维数组),这个矩阵是320行,240列,像素是图像的最小单元,这张图片的宽度是320个像素点的长度,高度是240个像素点的长度,共有320 * 240 = 76800个像素点,7.68w。因为一个像素点的颜色是由RGB三个值来表现的,所以一个像素点矩阵对应三个颜色向量矩阵,分别是R矩阵,G矩阵,B矩阵。在理解了一张图片是由一个像素点矩阵构成之后,我们就知道我们对图像的处理就是对这个像素点矩阵的操作,想要改变某个像素点的颜色,我们只要在这个像素点矩阵中找到这个像素点的位置,比如第x行,第y列,所以这个像素点在这个像素点矩阵中的位置就可以表示成(x,y),因为一个像素点的颜色由红、绿、蓝三个颜色变量表示,所以我们通过给这三个变量赋值,来改变这个像素点的颜色,比如改成红色(255,0,0),可以表示为(x,y,(R=255,G=0,B=0))。那么什么叫图片的灰度化呢?其实很简单,就是让像素点矩阵中的每一个像素点都满足下面的关系:R=G=B(就是红色变量的值,绿色变量的值,和蓝色变量的值,这三个值相等,“=”的意思不是程序语言中的赋值,是数学中的相等),此时的这个值叫做灰度值。
在我们程序里面只要一句就完成了
接下来是二值化处理,是在整个计算过程中算性能瓶颈的地方
定义:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
灰度值0:黑,灰度值255:白
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。
作用是去掉噪,例如过滤很小或很大像素值的图像点。这里也就是为什么他要保持赛道的颜色有较大的区分度的原因
我们这里用大津算法对图像进行二值化处理,这样处理的图像二值化使图像中数据量大为减少,从而能凸显出目标的轮廓
其次将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。
这里有4个参数,图片的源文件,图片的起始阈值,图片的最大值划分的时候使用什么算子。
(x,y)是对应的像素点
这里是选择算子一。小于规定的像素值全为0,就是白色。大于的话就是填充我们上面的值,(0~255)
其实还有很重要的cv2.THRESH_OTSU 作为图像自适应二值化的一个很优的算法Otsu大津算法的参数:
使用为cv2.threshold(img, 0, 255, cv2.THRESH_OTSU )
这些是各种算子处理过的图像,可以对比的看
接下来是图像的腐蚀操作:
这个函数的第一个参数表示内核的形状,有三种形状可以选择。
矩形:MORPH_RECT;
交叉形:MORPH_CROSS;
椭圆形:MORPH_ELLIPSE;
对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心点。
element形状唯一依赖锚点位置,其他情况下,锚点只是影响了形态学运算结果的偏移。
腐蚀操作是对图像进行形态学处理
原理是卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是1,那么中心元素就保持。根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为0),所以前景物体会变小。 这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。
函数的第一个参数是待处理的图像,第二个是要使用的内核,默认是3x3的矩阵。就是现在指定None
iterations指的是腐蚀次数,省略是默认为1