<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>xinyang</title>
    <description></description>
    <link>https://xinyang-go.github.io/</link>
    <atom:link href="https://xinyang-go.github.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 19 May 2020 09:35:10 +0000</pubDate>
    <lastBuildDate>Tue, 19 May 2020 09:35:10 +0000</lastBuildDate>
    <generator>Jekyll v3.8.5</generator>
    
      <item>
        <title>血泪的教训，Cache与DMA的一致性</title>
        <description>&lt;p&gt;最近开了一个四轴飞行器的坑，把一直在吃灰的STM32H7的板子拿出来用了用。正好机架啥的都还没有，就打算从小模块开始，于是拿出了一块SPI显示屏。在玩H7的时候，知道它M7的内核有Cache，果断开起来，白赚的速度不要白不要。。。本应该是这样的，直到我给SPI打开了DMA。&lt;/p&gt;

&lt;p&gt;对于SPI的控制，我开辟了一块空间做“显存”，显存数据定时通过SPI传给显示屏，而绘图都在显存内进行绘图。这个SPI屏是240*240的分辨率，SPI波特率30M，算下来RGB565的彩点，刷屏速率只有30Hz左右，还会把CPU资源给占完。这哪行啊，CPU占满了拿什么去飞控←_←。于是我打开了DMA。。。这下惨了，原本正常的显示屏上千疮百孔，总有些区域显示不全。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/spi-lcd.jpg&quot; alt=&quot;spi-lcd&quot; /&gt;&lt;/p&gt;

&lt;p&gt;而且奇怪的是，只要打开DMA，必出现上述现象；而只要关闭DMA，上述现象就消失了。玩个单片机还能闹鬼了不成？作为一名程序员，发现问题首先问自己又错在哪里了。因为计算机总是忠实的按照自己的编程运行，闹鬼是不可能的。就这样折腾了老半天，在一次调试的过程中，突然发现，如果在启动DMA写数据前，先遍历访问一次“显存”区域，会使现象减弱或者消失。这时，即便对Cache了解并不深入的我，也终于意识到了问题所在，果断关闭Cache，果不其然，BUG消失了。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Cache的DMA之间会有数据一致性的问题。DMA全名Direct Memory Access，即直接内存访问，顾名思义，DMA访问总线前是不会经过Cache的。而Cache又名高速缓存，位于CPU和总线之间。Cache包括读缓冲和写缓冲，读缓冲指CPU想访问的总线地址如果已经在Cache中，则直接从Cache中读；写缓冲指CPU写总线会先写到Cache中，直到满足某些条件，Cache的数据才会写入内存。&lt;/p&gt;

&lt;p&gt;所以问题很明显了，程序写了“显存”区域之后，有的数据还在Cache中，未写入内存；而这是DMA开始工作，直接从内存中读取数据，读到的就不是完整的数据。同理，如果DMA从外设将数据送回内存，CPU在读的时候也可能因为Cache读缓冲导致没有读到最新的数据。&lt;/p&gt;

&lt;p&gt;解决方法也很简单：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;关Cache。但这不是个好办法，既然有Cache，那自然是想用的，把它关了不就亏了。&lt;/li&gt;
  &lt;li&gt;设置Cache熟悉。Cache可以设置直接读写某块地址空间，这应该是比较好的办法。&lt;/li&gt;
  &lt;li&gt;手动刷新Cache。即在DMA发送前，手动刷新Cache，保证数据已经回写到内存中。&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;问题根本原因还是对硬件不够熟悉，想做嵌入式开发，对硬件的熟悉性真的很重要！&lt;/p&gt;
</description>
        <pubDate>Tue, 19 May 2020 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/quadrotor/Cache-DMA/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/quadrotor/Cache-DMA/</guid>
        
        <category>cache</category>
        
        
        <category>quadrotor</category>
        
      </item>
    
      <item>
        <title>对目标检测网络的理解</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#一图像分类与目标检测&quot; id=&quot;markdown-toc-一图像分类与目标检测&quot;&gt;一、图像分类与目标检测&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#二图像金字塔与滑动窗口&quot; id=&quot;markdown-toc-二图像金字塔与滑动窗口&quot;&gt;二、图像金字塔与滑动窗口&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#三改进图像金字塔&quot; id=&quot;markdown-toc-三改进图像金字塔&quot;&gt;三、改进图像金字塔&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#四改进滑动窗口&quot; id=&quot;markdown-toc-四改进滑动窗口&quot;&gt;四、改进滑动窗口&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;最近对目标检测网络的结构设计有了一些新的认识，想记录一下。我将分几个阶段来分析目标检测的发展过程，同时会写到我自己对目标检测的理解。&lt;/p&gt;

&lt;h1 id=&quot;一图像分类与目标检测&quot;&gt;一、图像分类与目标检测&lt;/h1&gt;

&lt;p&gt;为了讲目标检测算法的发展过程，必须需要先说一说目标检测是一个怎样的任务，与常规的图像分类任务有怎样的不同。&lt;/p&gt;

&lt;p&gt;可以总结出一下几点图像分类和目标检测的区别：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;二者的目标不同，这一点是显然的。
    &lt;ul&gt;
      &lt;li&gt;图像分类的输出数量也称作输出维度是确定的，即想把图像分为几类就有几个输出，每个维度代表图像属于这个类别的概率。&lt;/li&gt;
      &lt;li&gt;目标检测的输出数量是不确定的，我们希望目标的输出应该是当前图像有几个对象，它就产生几个输出。同时对于每个对象，不仅要输出它的类别，还要输出它的位置。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;二者所处理的图像上有很大的不同，这一点容易被忽略。
    &lt;ul&gt;
      &lt;li&gt;图像分类的目标图像往往只有一个对象且占据较大的图像尺寸&lt;/li&gt;
      &lt;li&gt;目标检测的目标图像可以有很多个不同的对象，并且每个对象既可以占据较大画幅也可以占据较小的画幅。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在理解了二者的不同后，我们可以逐渐从图像分类过渡到目标检测上。&lt;/p&gt;

&lt;h1 id=&quot;二图像金字塔与滑动窗口&quot;&gt;二、图像金字塔与滑动窗口&lt;/h1&gt;

&lt;p&gt;基于上面对目标检测任务的介绍，我们可以很容易的想到目标检测的一个解决方案：将需要进行目标检测的图像切片成多个小区域（不同区域之间可以有重叠部分），然后使用一个图像分类器，对每个区域进行分类，如果某个区域被图像分类器识别，则说明这个区域有一个目标。&lt;/p&gt;

&lt;p&gt;为什么可以这样操作的呢？回顾上面提到的图像分类器所处理的图像的特点——通常只有一个对象，且该对象占据图像较大的画幅。对图像进行切片操作后所得到的区域，我们&lt;strong&gt;期待&lt;/strong&gt;它只包含一个对象，且占据较大画幅。注意，这里是期待，即我们不能保证上述条件一定满足。但&lt;strong&gt;如果被检测的目标的大小差异不大，且是预先已知的&lt;/strong&gt;，假设我们的待检目标基本上都是60个像素左右，那么我们对图像切片的时候只需要保证切出来的图像都是60个像素左右，就可以满足上述条件。比如，在下面这张图中，我们将图像切片成红，黄，蓝，三个部分，随后使用图像分类器，可以发现猫在蓝色框中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/cat.png&quot; alt=&quot;cat&quot; /&gt;&lt;/p&gt;

&lt;p&gt;然而，往往目标检测的对象都不会是固定大小的。大的对象如果进行小切片会导致切片的结果都不能包含完整的目标，从而图像分类全部失败；而小的对象如果进行大切片会导致定位不精确，或者切片图像中包含多个目标。更进一步，一般的图像的分类器都只能接受固定分辨率的图像输入，也只能对固定大小（或者说大小变化不大）的图像进行分类，这更加与目标检测中的不同大小的对象这一条件所不符合。&lt;/p&gt;

&lt;p&gt;所以，为了解决对象大小不确定所带来的问题，有一个自然而然的想法就是，我们将输入图像进行缩放，分别缩放2倍，4倍，8倍……&lt;strong&gt;这样将图像进行不同倍数的缩放的方法被称为图像金字塔&lt;/strong&gt;。虽然对象大小不一，但将图像缩放不同倍数，总有一张图像上该对象的大小是正好合适的。在不同缩放大小的图像上都使用固定大小的滑动窗口，就可以解决对象大小不统一的问题。&lt;/p&gt;

&lt;p&gt;更进一步，滑动窗口方法的定位往往不够精准，我们只能知道目标在某个窗口中，但窗口的位置只有一些固定的位置，这导致了定位较为粗略。由于应用了图像金字塔，总有某个缩放下的滑动窗口中只包含一个目标。既然只包含一个目标，我们可以将我们的分类器进行一下“升级”，即现在图像分类器不仅仅输出图像的类别，同时输出该图像中对象的位置，由于前面以及保证了滑动窗口中只有一个对象，所以也不用担心窗口中多个目标从而不知道该输出哪个目标的位置。&lt;/p&gt;

&lt;p&gt;这样处理之后，目标检测的问题可以说就已经被解决了。但图像金字塔加滑动窗口的策略计算量实在是太大，所以目标检测有后续的发展。&lt;/p&gt;

&lt;h1 id=&quot;三改进图像金字塔&quot;&gt;三、改进图像金字塔&lt;/h1&gt;

&lt;p&gt;上面说到，目标检测就是由图像金字塔+滑动窗口的方式解决的，那么想改良目标检测方法，减少计算量就得从这两点出发。&lt;/p&gt;

&lt;p&gt;那么我们首先来改进目标检测两个步骤中的图像金字塔。&lt;/p&gt;

&lt;p&gt;我们知道，图像金字塔设计之初是用来解决目标大小不确定这个问题的。由于应用于滑动窗口的分类器（注意这里分类器指还可以同时对单一目标进行定位的分类器）只能接受固定分辨率大小的图像输入，且只能对固定大小（或者大小不怎么变化）的目标进行分类。使用图像金字塔其实是人为构造了不同大小的对象，从而总有一个对象可以被分类器正确分类。这是一个改变输入（图像），去适应系统（分类器）的过程。&lt;/p&gt;

&lt;h1 id=&quot;四改进滑动窗口&quot;&gt;四、改进滑动窗口&lt;/h1&gt;

</description>
        <pubDate>Fri, 15 May 2020 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/object-detection/object-detection-introduction/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/object-detection/object-detection-introduction/</guid>
        
        <category>object-detection</category>
        
        
        <category>object-detection</category>
        
      </item>
    
      <item>
        <title>视觉识别入门方法</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#一了解摄像头的基本情况与选型方式&quot; id=&quot;markdown-toc-一了解摄像头的基本情况与选型方式&quot;&gt;一、了解摄像头的基本情况与选型方式&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#二从开源代码中学习&quot; id=&quot;markdown-toc-二从开源代码中学习&quot;&gt;二、从开源代码中学习&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#三对比不同的开源方案&quot; id=&quot;markdown-toc-三对比不同的开源方案&quot;&gt;三、对比不同的开源方案&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#四从csdn等渠道了解更优秀的算法&quot; id=&quot;markdown-toc-四从csdn等渠道了解更优秀的算法&quot;&gt;四、从CSDN等渠道了解更优秀的算法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;给新RM队伍以及新入门视觉目标检测的新手的一份指引。&lt;/p&gt;

&lt;h1 id=&quot;一了解摄像头的基本情况与选型方式&quot;&gt;一、了解摄像头的基本情况与选型方式&lt;/h1&gt;

&lt;p&gt;因为摄像头是做视觉开发的第一步，所以需要首先对摄像头有一定的了解，包括相机和镜头的基本参数，常用的相机模型等。在了解清楚之后，购置一款摄像头也十分重要，这可以帮助后续的学习。&lt;/p&gt;

&lt;h1 id=&quot;二从开源代码中学习&quot;&gt;二、从开源代码中学习&lt;/h1&gt;

&lt;p&gt;开源代码可以在&lt;a href=&quot;http://bbs.robomaster.com/&quot;&gt;RoboMaster官方论坛&lt;/a&gt;上找到。各个学校开源的代码都有差异，可以先选择其中一个学校的开源代码，熟读并充分理解思路。之后便是使用之前购置好的摄像头实际运行代码。在代码运行的过程中，将识别过程的各个阶段显示出来，这样可以充分了解代码的优缺点。&lt;/p&gt;

&lt;h1 id=&quot;三对比不同的开源方案&quot;&gt;三、对比不同的开源方案&lt;/h1&gt;

&lt;p&gt;由于开源方案多，而每个开源方案可能都有各自的优缺点，所以对于一个新队伍来说，整合不同开源方案的优点，舍弃其缺点可能是最快也是最有效的方法去实现一个自己视觉识别系统。&lt;/p&gt;

&lt;h1 id=&quot;四从csdn等渠道了解更优秀的算法&quot;&gt;四、从CSDN等渠道了解更优秀的算法&lt;/h1&gt;

&lt;p&gt;在通过开源代码搭建了自己的BaseLine之后，未必能够达到自己预期的效果，这时可以寻求更优秀的算法。但是CSDN等网站上找到的一般都不是直接用于RoboMaster装甲检测的，所以需要现有一个自己的BaseLine，再在其上运用优秀的算法进行改进。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;在做完上述步骤后，未必能有一个强健的视觉系统，但可以有一个较为广泛的对于视觉的认识。&lt;/p&gt;

</description>
        <pubDate>Sun, 17 Nov 2019 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/way-of-introduction/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/way-of-introduction/</guid>
        
        <category>robomaster</category>
        
        
        <category>Jiaolong-cv-novice-tutorial</category>
        
      </item>
    
      <item>
        <title>交龙视觉部新手任务</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#task1-配置环境&quot; id=&quot;markdown-toc-task1-配置环境&quot;&gt;Task1-配置环境&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#task2-代码重构&quot; id=&quot;markdown-toc-task2-代码重构&quot;&gt;Task2-代码重构&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#task3-实操视觉寻迹&quot; id=&quot;markdown-toc-task3-实操视觉寻迹&quot;&gt;Task3-实操视觉寻迹&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本篇将会安排几个新手任务，既用于帮助新手更快熟悉&lt;strong&gt;开发环境&lt;/strong&gt;、&lt;strong&gt;代码&lt;/strong&gt;以及&lt;strong&gt;开发流程&lt;/strong&gt;，同时也可能用于队内考核。&lt;br /&gt;
完成之后可以在下方评论，或者直接与我微信联系。有问题同理。&lt;/p&gt;

&lt;h1 id=&quot;task1-配置环境&quot;&gt;Task1-配置环境&lt;/h1&gt;

&lt;p&gt;关于开发环境的配置，之前以及写过一个&lt;a href=&quot;../environment-setup&quot;&gt;粗略的教程&lt;/a&gt;，请按照该教程（或者网上的其他教程），完成开发环境的配置。包括Ubuntu系统，OpenCV库，IDE。&lt;br /&gt;
一个完整的开发环境是后续工作的基础，同时在尝试进行环境配置的过程中，也会熟悉到Ubuntu系统的基本操作方式。&lt;/p&gt;

&lt;h1 id=&quot;task2-代码重构&quot;&gt;Task2-代码重构&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/xinyang-go/SJTU-RM-CV-2019&quot;&gt;RoboMaster2019赛季代码&lt;/a&gt;是基于OpenCV3.4.5进行编写的，而OpenCV现在已经发布了4+的版本，其中包括很多新内容（包括更完整的DNN模块）。紧跟OpenCV的最新发展也可以帮助我们更好的完成视觉识别的功能。但OpenCV4+的版本相较于OpenCV3+的版本有不少改动，包括&lt;strong&gt;常量命名&lt;/strong&gt;，&lt;strong&gt;函数命名&lt;/strong&gt;等方面，所以自己的代码也需要相应的变动。&lt;br /&gt;
总之，&lt;strong&gt;第二个Task就是将RoboMaster2019赛季的代码重构，使其可以运行在OpenCV4+的环境之下&lt;/strong&gt;。&lt;br /&gt;
这个过程不仅可以帮助熟悉老代码，也可以对OpenCV库有一个更为全面的了解。&lt;/p&gt;

&lt;h1 id=&quot;task3-实操视觉寻迹&quot;&gt;Task3-实操视觉寻迹&lt;/h1&gt;

&lt;p&gt;在经过上面两个过程之后，需要一个实际的项目，这样才能帮助大家了解，当遇到新的机器视觉的任务时，应该怎样从头构思整个识别逻辑。这里我选用了一个十分经典的任务，视觉寻迹。即通过视觉的方式，让车辆跟着地面上划的线走。&lt;br /&gt;
考虑到有人可能不知怎么入手，后面我会再补充一份视觉寻迹的大致思路教程。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;新手任务暂时更新这些，有其他想法的队员可以让我知道（使用下方评论或直接微信联系）。&lt;/p&gt;
</description>
        <pubDate>Mon, 09 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/novice-task/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/novice-task/</guid>
        
        <category>opencv</category>
        
        <category>寻迹</category>
        
        
        <category>Jiaolong-cv-novice-tutorial</category>
        
      </item>
    
      <item>
        <title>交龙2019赛季步兵视觉代码详解</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#一maincpp&quot; id=&quot;markdown-toc-一maincpp&quot;&gt;&lt;span id=&quot;main&quot;&gt;一、main.cpp&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#二摄像头部分&quot; id=&quot;markdown-toc-二摄像头部分&quot;&gt;&lt;span id=&quot;camera&quot;&gt;二、摄像头部分&lt;/span&gt;&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#bool-init函数&quot; id=&quot;markdown-toc-bool-init函数&quot;&gt;&lt;span id=&quot;camera-part-one&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool init()&lt;/code&gt;函数&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#bool-readcvmat-src函数&quot; id=&quot;markdown-toc-bool-readcvmat-src函数&quot;&gt;&lt;span id=&quot;camera-part-two&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool read(cv::Mat &amp;amp;src)&lt;/code&gt;函数&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#三armorfinder类&quot; id=&quot;markdown-toc-三armorfinder类&quot;&gt;&lt;span id=&quot;armorfinder&quot;&gt;三、ArmorFinder类&lt;/span&gt;&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#1主要成员变量解析&quot; id=&quot;markdown-toc-1主要成员变量解析&quot;&gt;&lt;span id=&quot;armorfinder-variable&quot;&gt;1.主要成员变量解析&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#2void-runcvmat-src函数&quot; id=&quot;markdown-toc-2void-runcvmat-src函数&quot;&gt;&lt;span id=&quot;armordfinder-run&quot;&gt;2.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void run(cv::Mat &amp;amp;src);&lt;/code&gt;函数&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#3搜寻模式&quot; id=&quot;markdown-toc-3搜寻模式&quot;&gt;&lt;span id=&quot;armorfinder-search&quot;&gt;3.搜寻模式&lt;/span&gt;&lt;/a&gt;        &lt;ul&gt;
          &lt;li&gt;&lt;a href=&quot;#31灯条搜寻&quot; id=&quot;markdown-toc-31灯条搜寻&quot;&gt;&lt;span id=&quot;armorfinder-search-blobsearch&quot;&gt;3.1灯条搜寻&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#32灯条匹配&quot; id=&quot;markdown-toc-32灯条匹配&quot;&gt;&lt;span id=&quot;armorfinder-search-blobmatch&quot;&gt;3.2灯条匹配&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#33候选区二次筛选&quot; id=&quot;markdown-toc-33候选区二次筛选&quot;&gt;&lt;span id=&quot;armorfinder-search-classify&quot;&gt;3.3候选区二次筛选&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;&lt;a href=&quot;#34搜寻模式总结&quot; id=&quot;markdown-toc-34搜寻模式总结&quot;&gt;&lt;span id=&quot;armorfinder-search-summary&quot;&gt;3.4搜寻模式总结&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#4追踪模式&quot; id=&quot;markdown-toc-4追踪模式&quot;&gt;&lt;span id=&quot;armorfinder-track&quot;&gt;4.追踪模式&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#5目标位置解算及数据发送&quot; id=&quot;markdown-toc-5目标位置解算及数据发送&quot;&gt;&lt;span id=&quot;armorfinder-solve&amp;amp;send&quot;&gt;5.目标位置解算及数据发送&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#6反陀螺&quot; id=&quot;markdown-toc-6反陀螺&quot;&gt;&lt;span id=&quot;armorfinder-antitop&quot;&gt;6.反陀螺&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#四energy类&quot; id=&quot;markdown-toc-四energy类&quot;&gt;&lt;span id=&quot;energy&quot;&gt;四、Energy类&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#五辅助代码介绍&quot; id=&quot;markdown-toc-五辅助代码介绍&quot;&gt;&lt;span id=&quot;support-code&quot;&gt;五、辅助代码介绍&lt;/span&gt;&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#1-logh&quot; id=&quot;markdown-toc-1-logh&quot;&gt;&lt;span id=&quot;support-code-log&quot;&gt;1. log.h&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#2-systimeh&quot; id=&quot;markdown-toc-2-systimeh&quot;&gt;&lt;span id=&quot;support-code-systime&quot;&gt;2. systime.h&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;关于该代码的大致介绍及其环境配置在&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019&quot;&gt;代码github仓库&lt;/a&gt;已经进行过了，但那是针对有一定视觉经验的人写的代码大致实现思路。 &lt;br /&gt;
本教程主要针对与缺少视觉经验的新手，在了解代码的同时从中学习到视觉的基本思路和实现方法。&lt;strong&gt;虽说是详解，但实际并不可能将代码每个变量的含义都讲解清楚，尤其是OpenCV库内的变量，这部分请自行百度。该详解注重算法实现思路。&lt;/strong&gt;&lt;br /&gt;
前置知识基础：C++&lt;br /&gt;
我将从&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/main.cpp&quot;&gt;main.cpp&lt;/a&gt;开始，按照深度优先顺序对程序进行讲解。同时，我&lt;strong&gt;主要讲解代码中的算法部分&lt;/strong&gt;，其余还有调试代码，辅助代码等，可以自行查看。&lt;br /&gt;
本教程较长，建议配合源码食用，注意运行环境的配置，详见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019&quot;&gt;代码github仓库&lt;/a&gt;。&lt;/p&gt;

&lt;h1 id=&quot;一maincpp&quot;&gt;&lt;span id=&quot;main&quot;&gt;一、main.cpp&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;作为主函数，一上来的任务就是进行程序的初始化操作&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;processOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;// 处理命令行参数&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uartReceive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// 开启串口接收线程&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from_camera&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;                    &lt;span class=&quot;c1&quot;&gt;// 根据条件选择视频源&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run_with_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Input 1 for camera, 0 for video files&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cin&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;关于初始化操作，注释写的很清楚，其中命令行参数的处理主要是为了调试，具体实现不做赘述，有兴趣可以查看&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/include/options.h&quot;&gt;options.h&lt;/a&gt;和&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/src/options.cpp&quot;&gt;options.cpp&lt;/a&gt;。串口接收的实现同样不做赘述，其源码位于&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/src/additions.cpp&quot;&gt;additions.cpp&lt;/a&gt;。&lt;br /&gt;
接下来，便是根据给定的视频源，打开对应的视频源，并进行初始化。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 打开视频源&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CameraWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ARMOR_CAMERA_EXPOSURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ARMOR_CAMERA_GAIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VideoWrapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PROJECT_DIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/video/blue_big.avi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOGM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;video_source initialization successfully.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOGW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;video_source unavailable!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CameraWrapper&lt;/code&gt;类，请参见&lt;a href=&quot;#camera&quot;&gt;摄像头部分&lt;/a&gt;。&lt;br /&gt;
接下来跳过10帧图片，随后进入主循环。在主循环中，由于步兵需要击打能量机关，其实就是一个判断状态，然后根据状态选择运行识别算法。但在代码中可以看见，实际还有很多其他的代码，这些代码主要用于调试，错误处理等功能，包括摄像头断线重连，录制视频，图像显示，状态切换初始化等。同样不做赘述。实际调用算法的函数为&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;energy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 能量机关主函数&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;armor_finder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;　&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;// 自瞄主函数&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中energy为&lt;a href=&quot;#energy&quot;&gt;Energy类&lt;/a&gt;的对象，armor_finder为&lt;a href=&quot;#armorfinder&quot;&gt;ArmorFinder类&lt;/a&gt;的对象。&lt;/p&gt;

&lt;h1 id=&quot;二摄像头部分&quot;&gt;&lt;span id=&quot;camera&quot;&gt;二、摄像头部分&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;摄像头是做视觉的硬件基础，同时&lt;strong&gt;有一个好的摄像头和镜头可以让我们获取一张干净的图片，从而降低图像处理过程中的难度&lt;/strong&gt;。&lt;br /&gt;
&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/include/camera/camera_wrapper.h&quot;&gt;camera_wrapper.h&lt;/a&gt;和&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/src/camera/camera_wrapper.cpp&quot;&gt;camera_wrapper.cpp&lt;/a&gt;两个文件分别定义了摄像头类及其成员函数实现。
我们首先看&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/include/camera/camera_wrapper.h&quot;&gt;camera_wrapper.h&lt;/a&gt;中的一部分&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CameraWrapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WrapperHead&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;CameraWrapper类继承了WrapperHead类，其中WrapperHead类是一个接口类，目的是为了在主函数中方便的切换视频源（摄像头和视频）。这个类实现了作为一个视频源的基础功能&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面两个函数声明是CameraWrapper类的两个public类型的成员函数声明，分别对应了视频源的初始化（初始化成功则返回true否则返回false）和读取一帧图片（读取成功则返回true否则返回false）。&lt;/p&gt;

&lt;h2 id=&quot;bool-init函数&quot;&gt;&lt;span id=&quot;camera-part-one&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool init()&lt;/code&gt;函数&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init()&lt;/code&gt;函数中，主要是使用相机SDK对相机进行初始化，这里不做赘述，主要讲一下几个可能需要根据环境调节的相机参数。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CameraReadParameterFromFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PROJECT_DIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/others/MV-UB31-Group0.config&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CameraLoadParameter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PARAMETER_TEAM_A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CameraSetExposureTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exposure&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CameraSetAnalogGain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h_camera&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前两行从相机配置文件中加载了相机参数，该配置文件是由该相机生产公司提供的GUI调参工具自动生成的（主要参数包括，白平衡，伽马值，LUT表等），而后两行分别重新设置了相机的曝光时间和模拟增益，其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exposure&lt;/code&gt;和&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gain&lt;/code&gt;是类构造函数时传入的曝光时间和模拟增益。&lt;br /&gt;
关于曝光时间，曝光时间越长，图片亮度越高，但更容易出现由于摄像头和目标发生相对移动而产生的模糊现象。&lt;br /&gt;
关于模拟增益，模拟增益越高，图片亮度越高，但噪声也越强。&lt;br /&gt;
在2019赛季中，由于一直面临亮度不足的问题，所以这两个参数都调整的比较大。&lt;/p&gt;

&lt;h2 id=&quot;bool-readcvmat-src函数&quot;&gt;&lt;span id=&quot;camera-part-two&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool read(cv::Mat &amp;amp;src)&lt;/code&gt;函数&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;我们首先看源码&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CameraWrapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init_done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readProcessed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readRaw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;只要已经完成初始化，read函数将根据模式选择一个读取方式。由于2019赛季实际采用的读取方式是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mode==2&lt;/code&gt;`对应的方法，这里主要对其进行讲解。其他读取方式可自行阅读代码。 &lt;br /&gt;
首先我们讲一下常规程序运行流程，顺序执行&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/normal-flow.png&quot; alt=&quot;pic1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;一帧图像的读取经过曝光及数据传输，在2019赛季的配置下需要大约10ms。一张图片的处理，平均需要6ms。这样算下来，整个一帧图像总共需要大约16ms的时间，但其中有10ms的时间cpu处于等待状态，这显然没有充分利用cpu资源。能想到的办法就是当上一帧图像开始处理时，就立马进行下一帧图像的读取，充分利用cpu资源。具体的实现方式略有区别但大致相同。&lt;br /&gt;
首先我使用了相机SDK的采集回调函数，并将相机设置为连续采集模式，及相机连续采集图像，每当一帧图像采集完毕时，调用用户注册的回调函数对用户进行通知。&lt;br /&gt;
在回调函数中，将采集到的图像保存在该相机对象的一个循环队列之中，关键代码：&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cvarrToMat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iplImage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c&lt;/code&gt;是相机对象的指针，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src_queue&lt;/code&gt;是对象中的循环队列。&lt;br /&gt;
而此时读取函数只需要判断队列中是否有未读取的图片，如果有就读取否则继续等待即可。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CameraWrapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;systime&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;te&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;getsystime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;getsystime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;te&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getTimeIntervalms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;te&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;采用计时的方式，如果超过500ms没有等到下一帧图片则报错。&lt;br /&gt;
此时程序以并行的方式运行，即：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/parallel-flow.png&quot; alt=&quot;pic2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时，在之前的条件下，一帧图像的总的时间只需要10ms。&lt;/p&gt;

&lt;h1 id=&quot;三armorfinder类&quot;&gt;&lt;span id=&quot;armorfinder&quot;&gt;三、ArmorFinder类&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;该类为&lt;strong&gt;自瞄算法的封装类&lt;/strong&gt;，实现了包括装甲版识别，装甲板追踪，陀螺击打，数据发送等功能。对外提供接口&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void run(cv::Mat &amp;amp;src);&lt;/code&gt;主函数只需要调用该成员函数即可完成一帧图像从处理到数据发送的全过程。&lt;/p&gt;

&lt;h2 id=&quot;1主要成员变量解析&quot;&gt;&lt;span id=&quot;armorfinder-variable&quot;&gt;1.主要成员变量解析&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;这部分在&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/include/armor_finder/armor_finder.h&quot;&gt;armor_finder.h&lt;/a&gt;中通过注释写的很清楚，每当在代码中看见记不清干什么的成员变量可以返回该文件查看。&lt;/p&gt;

&lt;h2 id=&quot;2void-runcvmat-src函数&quot;&gt;&lt;span id=&quot;armordfinder-run&quot;&gt;2.&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;void run(cv::Mat &amp;amp;src);&lt;/code&gt;函数&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;该函数维护了一个&lt;strong&gt;状态机&lt;/strong&gt;，这决定这一帧图像使用什么方法进行识别（搜寻或追踪）。&lt;br /&gt;
函数首先是一个switch-case结构，完成状态机的功能。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEARCHING_STATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 此处为调用搜寻算法&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stateSearchingTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 判断装甲板区域是否脱离图像区域&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;480&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;                                       
                    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
                    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cvtColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_RGB2GRAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;THRESH_BINARY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;contour_area&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;countNonZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackerToUse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;                       
                &lt;span class=&quot;c1&quot;&gt;// 成功搜寻到装甲板，创建tracker对象&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRACKING_STATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;tracking_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;LOGM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STR_CTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WORD_LIGHT_CYAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;into track&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRACKING_STATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 此处为调用追踪算法&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stateTrackingTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracking_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   
            &lt;span class=&quot;c1&quot;&gt;// 最多追踪100帧图像&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEARCHING_STATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LOGM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STR_CTR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WORD_LIGHT_YELLOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;into search!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;STANDBY_STATE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;default:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;stateStandBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// currently meaningless&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以看到这里状态机切换的逻辑为&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/armorfinder-run.png&quot; alt=&quot;pic3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在switch-case结构执行完毕后，成员变量target_box将存储这一帧图像的目标的结果。如果有目标则保存目标信息，否则为空。在这之后判断是否执行反陀螺代码，以及目标信息的发送。即&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_anti_top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 判断当前是否为反陀螺模式&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;antiTop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;anti_top_cnt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;angle_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sendBoxPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中如果为反陀螺，目标信息在反陀螺函数中进行发送，否则直接发送目标信息。&lt;/p&gt;

&lt;h2 id=&quot;3搜寻模式&quot;&gt;&lt;span id=&quot;armorfinder-search&quot;&gt;3.搜寻模式&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;搜寻模式主函数为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool stateSearchingTarget(cv::Mat &amp;amp;src);&lt;/code&gt;是ArmorFinder类的私有成员函数。但其工作则是调用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool findArmorBox(const cv::Mat &amp;amp;src, ArmorBox &amp;amp;box);&lt;/code&gt;进行目标搜寻，再进行反目标切换工作。这里&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findArmorBox&lt;/code&gt;才是实际的算法代码。&lt;br /&gt;
在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findArmorBox&lt;/code&gt;函数中，首先&lt;strong&gt;对灯条进行寻找&lt;/strong&gt;，如果找到了，则将所有可能是的灯条的信息保存在局部变量&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;light_blobs&lt;/code&gt;中，否则返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;表示搜寻失败。其中light_blobs为LightBlobs类的对象，其定义参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/include/armor_finder/armor_finder.h&quot;&gt;armor_finder.h&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findLightBlobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_blobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后&lt;strong&gt;对所有灯条进行两两匹配&lt;/strong&gt;。因为我们的目标——装甲板，有两个灯条，这两个灯条有许多位置和形状特征，我们可以由此匹配出一些可能是装甲板的候选区。如果匹配成功，则将所有装甲板候选区的信息保存在局部变量&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;armor_boxes&lt;/code&gt;中，否则返回&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;表示搜寻失败。其中armor_boxes为ArmorBoxes类的对象，其定义参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/include/armor_finder/armor_finder.h&quot;&gt;armor_finder.h&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matchArmorBoxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_blobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;armor_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在成功获取候选区之后，我们对候选区&lt;strong&gt;使用分类器二次筛选&lt;/strong&gt;，去掉其中不是装甲板的区域，并识别出是装甲板区域的数字编号。其中&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id==0&lt;/code&gt;表示不是装甲板。classifier为Classifier类的对象，是分类器的实体，其定义和实现参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/include/armor_finder/classifier/classifier.h&quot;&gt;classifier.h&lt;/a&gt;和&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/classifier/classifier.cpp&quot;&gt;classifier.cpp&lt;/a&gt;。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;armor_box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;armor_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;armor_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;armor_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于一帧图像可能有多个目标，所以需要有一个优先级策略来选择一个最优目标。即如果上一帧有目标，则选择离其最近的目标，否则按照装甲板id的优先级进行击打。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 按照优先级对装甲板进行排序&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;armor_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;armor_boxes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Rect2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getPointLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;getPointLength&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;31灯条搜寻&quot;&gt;&lt;span id=&quot;armorfinder-search-blobsearch&quot;&gt;3.1灯条搜寻&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;即&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool ArmorFinder::findLightBlobs(const cv::Mat &amp;amp;src, LightBlobs &amp;amp;light_blobs)&lt;/code&gt;函数。&lt;br /&gt;
首先介绍灯条搜寻的思路，利用灯条的亮度和其周围有明显区分度这一条件，我们可以对图像的亮度进行二值化&lt;strong&gt;（二值化：一种将图像每个像素点根据特定条件变成非0即1的操作）&lt;/strong&gt;。找出其中的轮廓&lt;strong&gt;（轮廓寻找：所有常规方法进行目标搜寻的关键操作，在二值化后的图像上找出所有联通区域）&lt;/strong&gt;。最后对所有轮廓利用灯条的形状特征进行筛选，得到可能是灯条的区域。&lt;br /&gt;
所以灯条搜寻的&lt;strong&gt;第一步是二值化&lt;/strong&gt;，由于这里利用亮度条件进行灯条的搜寻，所以二值化方法便是将二值化阈值(threshold)一下的设为0，其余设为1。一个好的二值化应该能够使得灯条区域不和其他区域发生联通。这里我采用在目标颜色通道上进行二值化，并且对不同颜色选择不同二值化阈值，同时使用一高一低两个阈值，得到两幅二值图。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;             &lt;span class=&quot;cm&quot;&gt;/************************/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy_color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENEMY_BLUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;      &lt;span class=&quot;cm&quot;&gt;/*                      */&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;color_channel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;      &lt;span class=&quot;cm&quot;&gt;/* 根据目标颜色进行通道提取 */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy_color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENEMY_RED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/*                      */&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;color_channel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;channels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;      &lt;span class=&quot;cm&quot;&gt;/************************/&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enemy_color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ENEMY_BLUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;light_threshold&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;225&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;light_threshold&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 二值化对应通道&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color_channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src_bin_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_THRESH_BINARY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imagePreProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// 开闭运算&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// 二值化对应通道&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color_channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src_bin_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_THRESH_BINARY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;imagePreProcess&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// 开闭运算&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;由于二值化极度依赖二值化参数（这里是阈值threshold），所以得到的二值图往往会有一些噪声，所以&lt;strong&gt;第二步是使用开闭运算进行处理&lt;/strong&gt;。开闭运算是什么及其作用请&lt;a href=&quot;https://www.baidu.com/s?ie=UTF-8&amp;amp;wd=开闭运算&quot;&gt;百度&lt;/a&gt;。&lt;br /&gt;
一二两步之后，我们期待得到了一幅（或两幅）比较完美的二值图，接下来&lt;strong&gt;第三步就是轮廓提取&lt;/strong&gt;。轮廓提取使用的是OpenCV自带的函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;findContours&lt;/code&gt;，其用法很多，详情请&lt;a href=&quot;https://www.baidu.com/s?ie=UTF-8&amp;amp;wd=findContours&quot;&gt;百度&lt;/a&gt;。&lt;br /&gt;
总之，在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;light_contours_light, light_contours_dim&lt;/code&gt;中保存了轮廓信息，在&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hierarchy_light, hierarchy_dim&lt;/code&gt;中保存了轮廓的层级信息。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Point&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_contours_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec4i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hierarchy_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hierarchy_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findContours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hierarchy_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_RETR_CCOMP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_CHAIN_APPROX_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findContours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src_bin_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_contours_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hierarchy_dim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_RETR_CCOMP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_CHAIN_APPROX_NONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;成功获取轮廓之后，&lt;strong&gt;第四步就是对轮廓进行筛选&lt;/strong&gt;，利用的是灯条的形状信息。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hierarchy_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RotatedRect&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minAreaRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isValidLightBlob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;light_blobs_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;emplace_back&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;areaRatio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_contours_light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
                &lt;span class=&quot;n&quot;&gt;get_blob_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//获取灯条颜色&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isValidLightBlob&lt;/code&gt;函数判断是否该区域可能是灯条，如果是则添加到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;light_blobs_light&lt;/code&gt;中（PS:这里只展示了两个二值图两个轮廓中的其中一个轮廓的筛选代码，另一个同理）。具体的筛选条件这里不做展示，可以参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/find/find_light_blobs.cpp&quot;&gt;find_light_blobs.cpp&lt;/a&gt;。可以添加更多已经更加严苛的条件，排除更多不是灯条的区域。但由于二值化得到的二值图往往并不理想，所以该代码中并没有使用较为严苛的筛选条件。&lt;br /&gt;
由于使用了一高一低两种二值化阈值进行了两次轮廓提取和筛选，同一个灯条可能同时存在于两个轮廓。这将极大增加后续处理的冗余计算，所以&lt;strong&gt;第五步是重复区域的剔除&lt;/strong&gt;。这部分代码我感觉写的不太好，还有优化空间，这里就不放出来了，可以自行查看。&lt;/p&gt;

&lt;h3 id=&quot;32灯条匹配&quot;&gt;&lt;span id=&quot;armorfinder-search-blobmatch&quot;&gt;3.2灯条匹配&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;灯条匹配同样利用装甲板的形状信息，包括但不限于两个灯条角度，两个灯条的距离，装甲板自身角度。使用二重循环O(n^2)的复杂度进行两两匹配，同样使用&lt;/p&gt;
&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isCoupleLight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LightBlob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_blob_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LightBlob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_blob_j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint8_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enemy_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;判断两个灯条是否可以匹配，如果可以则保存下来。具体实现不算复杂，可以参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/find/find_armor_box.cpp&quot;&gt;find_armor_box.cpp&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;33候选区二次筛选&quot;&gt;&lt;span id=&quot;armorfinder-search-classify&quot;&gt;3.3候选区二次筛选&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;使用分类器进行二次筛选，这里分类器是一个使用TensorFlow训练的小型神经网络。在c++中的前向计算为了方便，利用for循环和Eigen3矩阵库自行编写了一个，没有性能损失。详细可以参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/include/armor_finder/classifier/classifier.h&quot;&gt;classifier.h&lt;/a&gt;和&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/classifier/classifier.cpp&quot;&gt;classifier.cpp&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&quot;34搜寻模式总结&quot;&gt;&lt;span id=&quot;armorfinder-search-summary&quot;&gt;3.4搜寻模式总结&lt;/span&gt;&lt;/h3&gt;

&lt;p&gt;可以看到，目标搜寻就是可以产生候选区和筛选候选区的过程。而主要难点在于候选区的生成。以后其他类似的目标搜寻任务其实也是这样一个思路。常见的候选区生成方式：二值图上轮廓提取。&lt;/p&gt;

&lt;h2 id=&quot;4追踪模式&quot;&gt;&lt;span id=&quot;armorfinder-track&quot;&gt;4.追踪模式&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;追踪模式，顾名思义就是根据上一帧目标的位置得出这一帧的目标。其好处在于快速，简单。说简单是因为OpenCV有自带的追踪算法。我使用的是其中的KCF追踪算法，关于KCF的原理，比较难，有兴趣自行&lt;a href=&quot;https://www.baidu.com/s?ie=UTF-8&amp;amp;wd=KCF&quot;&gt;百度&lt;/a&gt;。在OpenCV由KCFTracker类提供目标追踪接口。该算法对象首先需要初始化，即第一次指定目标好开始后续追踪。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后在追踪模式主函数&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bool ArmorFinder::stateTrackingTarget(cv::Mat &amp;amp;src)&lt;/code&gt;（函数实现位置：&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/tracking_state/tracking_state.cpp&quot;&gt;tracking_state.cpp&lt;/a&gt;）更新即可。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)){&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 使用KCFTracker进行追踪&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;LOGW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Track fail!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;追踪失败，返回false。&lt;br /&gt;
既然KCFTracker这么好用，那么追踪模式只需要几行代码就可以完成。但实际并不如此，这其中有两点原因：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;由于KCF算法是根据上一帧更新下一帧，所以当追踪时间长了，可能会忘记一开始的目标追到其他地方去。&lt;/li&gt;
  &lt;li&gt;同时由于KCFTracker在追踪的时候，只改变追踪框的位置，而不改变长宽。但在后续数据发送时需要长宽信息进行距离的估算。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;针对这两个问题，在代码中这样解决：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;需要使用分类器判断当前是否追踪错误。&lt;/li&gt;
  &lt;li&gt;将追踪区域扩大一倍重新搜寻一次目标，由于搜寻区域小，速度较快。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;由于重新搜寻并不总是可以成功，而追踪却往往可以成功。所以，重新搜寻如果失败，还是使用分类器作为当前是否跟丢的判断依据，在后续数据发送时，舍弃小部分距离精准度，换取位置信息的连续性。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 在区域内重新搜索。&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findArmorBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 如果成功获取目标，则利用搜索区域重新更新追踪器&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bigger_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//　添加roi偏移量&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bigger_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_blobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bigger_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bigger_rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrackerToUse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tracker&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// 如果没有成功搜索目标，则使用判断是否跟丢。&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 分类器可用，使用分类器判断。&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;LOGW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Track classify fail range!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//　分类器不可用，使用常规方法判断（基本弃用）&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Mat&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cvtColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CV_RGB2GRAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;THRESH_BINARY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;contour_area&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;countNonZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;countNonZero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;roi_gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contour_area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contour_area&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ArmorBox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;light_blobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;5目标位置解算及数据发送&quot;&gt;&lt;span id=&quot;armorfinder-solve&amp;amp;send&quot;&gt;5.目标位置解算及数据发送&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;目标位置包括三个信息，相对摄像头光轴yaw轴方向的偏移角度，相对摄像头光轴pitch轴方向的偏移角度，以及离相机的距离。这里我们采用最简单的相似三角形相机模型进行目标位置的解算。事先标定好相机焦距（以像素为单位），就可以得出角度值。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define FOCUS_PIXAL_8MM  (1488)
#define FOCUS_PIXAL_5MM  (917)
#define FOCUS_PIXAL      FOCUS_PIXAL_5MM
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMAGE_CENTER_X&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMAGE_CENTER_Y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FOCUS_PIXAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pitch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;atan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FOCUS_PIXAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于距离的测量，说的简单点就是利用进大远小这样一个知识进行距离估算。网络上有很多使用PNP算法进行距离估算，但其实没必要。PNP应用范围比较广，对于目标的不同姿态都可以计算出距离及其姿态。但在当前的应用场景下，有一个先验条件就是敌方装甲板基本垂直于地面，同时所有装甲板高度相同。所以我们同样使用相似三角形模型，计算可以发现距离和装甲板在图像上所占高度的乘积为一个定值。所以我们事先测量好定值的大小，就可以估算距离。&lt;/p&gt;

&lt;div class=&quot;language-c++ highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define DISTANCE_HEIGHT_5MM (10700.0)     // 单位: cm*pixel
#define DISTANCE_HEIGHT     DISTANCE_HEIGHT_5MM
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DISTANCE_HEIGHT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;数据发送使用USB转TTL进行串口数据发送，位置解算和数据发送具体参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/send_target/send_target.cpp&quot;&gt;send_target.cpp&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;6反陀螺&quot;&gt;&lt;span id=&quot;armorfinder-antitop&quot;&gt;6.反陀螺&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;常规情况下，自瞄发送目标信息到单片机，单片机上会融合陀螺仪信息对敌方的速度和加速度进行估算，从而向预测点发射子弹进行击打。而当敌方为陀螺时，如果也使用这样的方法，云台会来回晃动，相应速度不够，而且由于预测，子弹会击打到车辆以外的位置，没办法造成有效伤害。所以针对陀螺，需要一个专门的击打方式。&lt;br /&gt;
&lt;strong&gt;基本思路在于估算敌方陀螺的转动角速度&lt;/strong&gt;，通过这个角速度，计算出预判击打点。&lt;br /&gt;
那么如何估算转动角速度呢？发现，想在任何情况下都对转动角速度进行估算是十分困难的，但当敌我双方没有发送相对移动的时候，则可以计算转动角速度。&lt;br /&gt;
&lt;img src=&quot;/static/img/anti-top1.png&quot; alt=&quot;pic4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;通过简单的几何计算可以发现β∝α，再假设敌方匀速运动，即α∝t，所以β∝t，而β和t都是可以采集到的数据，所以我们可以通过对正面这个装甲板从消失到出现的角度和时间进行线性拟合，得出β=0时对应的时间，这样当我方视野中心正对敌方旋转中心时，连续两次β=0时的时间差，敌方正好转过了90°。从而计算出了转速。&lt;br /&gt;
但是由于通常敌方车辆前后装甲板和左右装甲板距离旋转中心的半径不同，当我方视野中心不是正对敌方旋转中心时，连续两次β=0的时间差将不再是90°。如图&lt;br /&gt;
&lt;img src=&quot;/static/img/anti-top2.png&quot; alt=&quot;pic5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时我们使用前后装甲板，和左右装甲板分别计算β=0的时间差，这样时间差正好为敌方转过180°的时间。&lt;br /&gt;
具体实现参见&lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/armor/src/armor_finder/anti_top/anti_top.cpp&quot;&gt;anti_top.cpp&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;四energy类&quot;&gt;&lt;span id=&quot;energy&quot;&gt;四、Energy类&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;能量机关的识别同样采用二值化加轮廓提取获取候选区，利用能量机关的形状特征，对区域进行筛选，最终得到目标，并发送数据。&lt;/p&gt;

&lt;h1 id=&quot;五辅助代码介绍&quot;&gt;&lt;span id=&quot;support-code&quot;&gt;五、辅助代码介绍&lt;/span&gt;&lt;/h1&gt;

&lt;h2 id=&quot;1-logh&quot;&gt;&lt;span id=&quot;support-code-log&quot;&gt;1. &lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/include/log.h&quot;&gt;log.h&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;该头文件主要使用宏定义的方式实现了更方便的调试信息输出方式，以及程序运行时间统计。头文件内有较为详细的使用方法介绍。&lt;/p&gt;

&lt;h2 id=&quot;2-systimeh&quot;&gt;&lt;span id=&quot;support-code-systime&quot;&gt;2. &lt;a href=&quot;https://github.com/lloi7/SJTU-RM-CV-2019/blob/master/others/include/systime.h&quot;&gt;systime.h&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;该头文件统一了不同平台(Windows,Linux)的系统时间获取接口，是代码跨平台能力更强。&lt;/p&gt;

</description>
        <pubDate>Sat, 07 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/RM2019-code-annotate/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/RM2019-code-annotate/</guid>
        
        <category>RM2019</category>
        
        
        <category>Jiaolong-cv-novice-tutorial</category>
        
      </item>
    
      <item>
        <title>开发环境配置</title>
        <description>&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#-一ubuntu1804安装&quot; id=&quot;markdown-toc--一ubuntu1804安装&quot;&gt;&lt;span id=&quot;head2&quot;&gt; 一、Ubuntu18.04安装&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#-1虚拟机安装&quot; id=&quot;markdown-toc--1虚拟机安装&quot;&gt;&lt;span id=&quot;head3&quot;&gt; 1.虚拟机安装&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#-2双系统安装&quot; id=&quot;markdown-toc--2双系统安装&quot;&gt;&lt;span id=&quot;head4&quot;&gt; 2.双系统安装&lt;/span&gt;&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#-21磁盘分区&quot; id=&quot;markdown-toc--21磁盘分区&quot;&gt;&lt;span id=&quot;head5&quot;&gt; 2.1磁盘分区&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#-22启动盘制作&quot; id=&quot;markdown-toc--22启动盘制作&quot;&gt;&lt;span id=&quot;head6&quot;&gt; 2.2启动盘制作&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#-23从u盘启动&quot; id=&quot;markdown-toc--23从u盘启动&quot;&gt;&lt;span id=&quot;head7&quot;&gt; 2.3从U盘启动&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#-24开始安装&quot; id=&quot;markdown-toc--24开始安装&quot;&gt;&lt;span id=&quot;head8&quot;&gt; 2.4开始安装&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#-二opencv库安装&quot; id=&quot;markdown-toc--二opencv库安装&quot;&gt;&lt;span id=&quot;head9&quot;&gt; 二、OpenCV库安装&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#-三安装一款ide&quot; id=&quot;markdown-toc--三安装一款ide&quot;&gt;&lt;span id=&quot;head10&quot;&gt; 三、安装一款IDE&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#-四安装相机驱动&quot; id=&quot;markdown-toc--四安装相机驱动&quot;&gt;&lt;span id=&quot;head11&quot;&gt; 四、安装相机驱动&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;-一ubuntu1804安装&quot;&gt;&lt;span id=&quot;head2&quot;&gt; 一、Ubuntu18.04安装&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;ubuntu18.04是一个操作系统，在其上进行开发比windows更加方便。&lt;/p&gt;

&lt;p&gt;镜像下载地址&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ubuntu.com/download/desktop&quot;&gt;官网地址&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pan.baidu.com/s/1hJJBsDpbrWk7MiiB116mdg&quot;&gt;百度网盘链接&lt;/a&gt;  提取码: 6h2j&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;-1虚拟机安装&quot;&gt;&lt;span id=&quot;head3&quot;&gt; 1.虚拟机安装&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;首先安装vmware，交大有参加vmware学术计划，可以申请到正版下载。&lt;a href=&quot;http://vmap.sjtu.edu.cn/&quot;&gt;http://vmap.sjtu.edu.cn&lt;/a&gt;&lt;br /&gt;
之后安装，百度有许多教程。&lt;/p&gt;

&lt;h1 id=&quot;-2双系统安装&quot;&gt;&lt;span id=&quot;head4&quot;&gt; 2.双系统安装&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;双系统安装比较麻烦，但运行速度会稍快一些。同时虚拟机无法使用GPU资源，如果想要使用GPU进行机器学习计算等，务必使用双系统安装方式。&lt;/p&gt;

&lt;h2 id=&quot;-21磁盘分区&quot;&gt;&lt;span id=&quot;head5&quot;&gt; 2.1磁盘分区&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;为了安装ubuntu系统，我们需要在磁盘上开辟一块空间，&lt;strong&gt;不需要分区，只需要划出一片空间即可&lt;/strong&gt;，推荐空间大小&amp;gt;50G。&lt;br /&gt;
如果电脑空间足够的话，建议将分区分在固态硬盘上，如果安装在机械硬盘上，亲测会比较卡顿。&lt;br /&gt;
分区软件可以使用DiskGenius：&lt;a href=&quot;http://www.diskgenius.cn/&quot;&gt;官网下载地址&lt;/a&gt;&lt;br /&gt;
具体使用方法请自行探索。&lt;/p&gt;

&lt;h2 id=&quot;-22启动盘制作&quot;&gt;&lt;span id=&quot;head6&quot;&gt; 2.2启动盘制作&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;也就是把下载的镜像文件刻录到Ｕ盘中。&lt;br /&gt;
推荐的刻录软件UltraISO：&lt;a href=&quot;https://cn.ultraiso.net/&quot;&gt;官网下载地址&lt;/a&gt;&lt;br /&gt;
刻录方式自行百度&lt;/p&gt;

&lt;h2 id=&quot;-23从u盘启动&quot;&gt;&lt;span id=&quot;head7&quot;&gt; 2.3从U盘启动&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;这一步不同的电脑型号略有不同，请自行根据自己电脑型号百度方法。注意：&lt;strong&gt;务必使用UEFI方式启动&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;-24开始安装&quot;&gt;&lt;span id=&quot;head8&quot;&gt; 2.4开始安装&lt;/span&gt;&lt;/h2&gt;

&lt;p&gt;从U盘启动成功后，按照显示屏上的指导进行。&lt;br /&gt;
当出现下面这个界面时，选择Somethine else&lt;br /&gt;
&lt;img src=&quot;/static/img/type.png&quot; alt=&quot;type&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来这个界面上，在最下方的boot loader installation找到你的windows启动分区，即Windows Boot Manager&lt;br /&gt;
&lt;img src=&quot;/static/img/boot.png&quot; alt=&quot;boot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;接下来在这个界面上找到刚才划分出的磁盘空间，并双击，按照如下配置&lt;br /&gt;
&lt;img src=&quot;/static/img/partation.png&quot; alt=&quot;partation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最后点击Install Now即可开始安装，等待安装完毕即可。&lt;/p&gt;

&lt;h1 id=&quot;-二opencv库安装&quot;&gt;&lt;span id=&quot;head9&quot;&gt; 二、OpenCV库安装&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;首先安装依赖，在控制台执行以下命令&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt upgrade &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; htop cmake cmake-gui gcc g++ vim git wget net-tools zip unzip build-essential intltool curl libcanberra-gtk-module python python-dev python-pip python-tk python3 python3-dev python3-pip python3-tk libssl-dev libgtk-3-dev libeigen3-dev libavcodec-dev libavformat-dev libpng-dev libtiff5-dev libjpeg-dev libwebp-dev libavresample-dev libswscale-dev libavutil-dev libopenexr-dev
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;pip3 &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--upgrade&lt;/span&gt; pip setuptools
pip3 &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt; numpy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;随后下载&lt;a href=&quot;https://github.com/opencv/opencv/archive/3.4.5.zip&quot;&gt;opencv3.4.5&lt;/a&gt;和&lt;a href=&quot;https://github.com/opencv/opencv_contrib/archive/3.4.5.zip&quot;&gt;opencv_contrib3.4.5&lt;/a&gt;源码。如果下载速度过慢可以使用&lt;a href=&quot;https://pan.baidu.com/s/11T89p59ps4F4kBrCWiEOKw&quot;&gt;百度网盘&lt;/a&gt;下载，提取码: hxyt。&lt;br /&gt;
下载完成后解压，并新建文件夹build用于编译，建议文件夹组织格式&lt;br /&gt;
&lt;img src=&quot;/static/img/folder.png&quot; alt=&quot;folder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;进入build文件夹并打开终端执行&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cmake &lt;span class=&quot;nt&quot;&gt;-D&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;OPENCV_EXTRA_MODULES_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;../opencv_contrib-3.4.5/modules&quot;&lt;/span&gt; ../opencv-3.4.5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;过程中会下载一些东西，因为有某墙，下载可能失败，反复执行上述命令，直到成功为止。&lt;br /&gt;
确保上述命令执行成功之后，再执行下面的命令开始编译并安装，编译时间较长，耐心等待。&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-j8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;make &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不出意外，OpenCV环境配置成功&lt;/p&gt;

&lt;h1 id=&quot;-三安装一款ide&quot;&gt;&lt;span id=&quot;head10&quot;&gt; 三、安装一款IDE&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;推荐的IDE：clion &lt;a href=&quot;http://www.jetbrains.com/clion/&quot;&gt;官方下载地址&lt;/a&gt;&lt;br /&gt;
该软件收费，但交大可以申请教育版免费使用。申请方法自行百度。&lt;br /&gt;
如果有其他已经习惯的IDE也可以，但clion支持cmake生成项目，亲测十分好用。&lt;/p&gt;

&lt;h1 id=&quot;-四安装相机驱动&quot;&gt;&lt;span id=&quot;head11&quot;&gt; 四、安装相机驱动&lt;/span&gt;&lt;/h1&gt;

&lt;p&gt;由于2019赛季用的是MindVision公司的相机，需要专门的驱动程序。&lt;a href=&quot;http://www.mindvision.com.cn/rjxz/list_12.aspx?lcid=138&quot;&gt;官网下载地址&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 05 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/environment-setup/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/environment-setup/</guid>
        
        <category>ubuntu</category>
        
        <category>opencv</category>
        
        <category>ide</category>
        
        
        <category>Jiaolong-cv-novice-tutorial</category>
        
      </item>
    
      <item>
        <title>上海交通大学RoboMaster交龙战队视觉教程</title>
        <description>&lt;p&gt;本教程主要针对于2020赛季新加入交龙视觉部的成员&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;../environment-setup&quot;&gt;一、开发环境配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;../RM2019-code-annotate&quot;&gt;二、从代码入手，2019赛季代码详解&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;../novice-task&quot;&gt;三、新手任务&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;本内容不定期更新。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;如果内容中有错误希望可以积极反馈。&lt;br /&gt;
作者：唐欣阳 微信号：xinyang_tang&lt;/p&gt;

</description>
        <pubDate>Wed, 04 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/Jiaolong-cv-novice-tutorial/</link>
        <guid isPermaLink="true">https://xinyang-go.github.io/jiaolong-cv-novice-tutorial/Jiaolong-cv-novice-tutorial/</guid>
        
        <category>RM2019</category>
        
        <category>opencv</category>
        
        
        <category>Jiaolong-cv-novice-tutorial</category>
        
      </item>
    
  </channel>
</rss>
