【智能车】从零写一份自己的完全模型智能车寻路算法(有手就行) --- 01
有手就行的智能车视觉寻路算法
前言
被同学拉进了智能车完全模型组做智能车的上层视觉软件算法,在交流群里吹水很多人起哄说让写一篇博客来教他们怎么写寻路。众望所托,今天就开始了这一系列的更新,保证有手就行,从最基础的开始。
其它的话:距离上次更新数据结构博客也很久了,那个博客并没有鸽,在复习二叉树的时候感觉这东西没啥好写的,和链表一样,而后面一直在看算法方面的东西,所以没有更新,后面的树状数组等等都会有更新的。
一、提出一份完整的需求
一份需求一定要层层细分:
最笼统的需求
- 能够识别道路线
- 能够识别道路标识
- 能够给出运动方向
- 特殊任务状态下的寻路
- 使用json控制一些变量的输入,方便后期调车
- 不重要:图形化界面,修改json文件并查看小车实时状态;使用socket等远程实时调试小车
这些就是最笼统的概念,接下来要细分到每一个部分上去:
识别道路线
识别道路线会遇到多种情况,包括直路,左右转弯,十字路口,环岛。
- 精确的识别出直线
- 判断出左右转弯状态
- 十字路口优化,单独处理十字路口
- 环岛入环,出环等状态的判断
识别道路标识
道路标识识别方法比较单一,只能是检测到目标然后进行图像分类确定标识种类。这就是需求了。
方案直接在这里给出:
- 直接使用目标检测算法,如yolo。但是效率会低,选择哪种算法请自行考虑。
- 传统图像算法优化目标检测,通过图像处理去到标识物的最小正接矩形范围,提取出图片,通过图像分类算法直接得到标识类别。
方便起见,直接选择方案一绝对是最好的,如果想要提升帧率,可以考虑借鉴方案二,或者自己找到其它更优解也是可以的。
给出运动方向
这里需要通过数学方法计算出小车后面需要行进的方向,具体如下
- 获得小车行进路线状态
- 数学方法处理路线,如拟合成直线等
- 通过斜率等判断转向度数
- pwm
- 串口传输pwm舵机打角信息
特殊任务
根据比赛规则自行调试,这里暂时不写需求
json文件
- 编写json文件读取协议
- 通过读取json文件初始化程序配置
本教程只针对基础寻路部分进行教学
二、板子的算力限制
这块板子真的是算力有限,只能说比树莓派强。在这样算力有限的情况下要是想跑yolov5或者一些图像分割算法的话那肯定是很难的,帧数很低,做人脸识别还可以,但是做这种实时高速识别的东西确实不太好。
因此,对于道路的识别我们要采用传统视觉算法。
三、从零开始
从零开始我们要先知道需要的一些基础技术,好进行学习。
1. 语言基础
推荐学习C/C++,python可针对深度学习单独学习。
推荐教程:
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
从0基础系统化学习C++,不可能学不会
MOOC浙大翁恺C语言
菜鸟教程python
2. OpenCV部分
用啥建议直接百度,最好谷歌,有一些问题上stackoverflow也有解,注意搜索的姿势就行(
如果是想了解一下,这里推荐看贾志刚老师的教程(网上很多,就不放链接了),但是很容易困,稍微了解下,剩下的慢慢学,用到的算法我都会写是怎么回事。
3. 开发环境搭建
对于大型项目,尤其是需要放到linux上的,我更倾向于使用Clion来编写,当然vs也是不错的,vscode可以用,就是有点费劲。
因此我选取Clion作为我开发这个寻路程序的IDE。
有了IDE,接下来就该了解一下需要配置什么环境:
首先是C++编译环境,Clion上建议使用CMake,它也会帮你生成CMakeLists.txt这个文件,同时我的CMake用的是clang++编译器,还可以选择g++等,可以自己选择编译器。
有了C++的编译环境,需要配置OpenCV库,EB板子自带一个低版本OpenCV,如果直接在上面开发可以直接看一下怎么用就行了。如果想在自己电脑上调试的话,可以百度,有很多教程,每个人遇到的问题都不一样,但是网上基本都有解了。
串口通讯库,可能会需要,也可能自己写。
额外的,为了后面读取json文件,需要选择一个json解释器的库,这个暂时不谈。
有可能自己需要一个日志系统,选择自己喜欢的日志库就行。
图形库可以暂时不做,因此不装了先。
总结下来就是下面这些:
- IDE
- C++编译器
- CMake帮助编译大型项目
- OpenCV环境
- 串口库(可能需要)
- json解释器(暂时不用管)
- 日志库(暂时不用管)
- 图形库(暂时不用管)
4. 创建项目
选择好自己想要的路行,c++标准建议选择c++14、c++17或者c++20,接下来点击创建。
这是创建后自动生成的主函数文件,我们来看一下文件树。
红色框出的部分是cmake编译生成的东西,不用管,我们来看另外两个文件。
这个是main.cpp,生成了一个hello world测试程序,我们可以点一下右上角的运行按钮测试一下能否使用:
这里可以看到程序运行正常,证明Clion成功找到CMake程序,CMake成功找到了c++编译器并进行编译,同时运行。
这两个按钮用的比较多,第一个是上面用到的那个功能,后面这个是调试按钮,也就是debug。
我们来看看剩下的CMakeLists.txt这个文件:
这就是用来编译c++项目的cmake的配置文件,具体使用方法百度找个文档就可以很快入门了。
接下来就该将OpenCV引入项目,直接在这个文件中加入下面语句:
#find OpenCV
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
和:
target_link_libraries(zhiNengChe ${OpenCV_LIBS})
然后将main.cpp改成下面这段代码,运行测试能否编译成功,如果不可以,请检查自己的opencv是否安装正确。(请将图片路径,红色圈出部分,修改为自己的图片路径,随便找一张图片测试就行)
#include <iostream>
#include <opencv2/opencv.hpp>int main() {cv::Mat frame = cv::imread("../114.png");cv::imshow("test", frame);cv::waitKey(0);return 0;
}
好了,到这里,所有暂时需要的环境已经配置完成,可以开始项目的内容了。
附测试用图:
四、图像预处理
1. 创建文件
右键文件夹,新建图像预处理类。
会在侧面显示出新生成的类文件。我们把他们重构到两个文件夹中,一个取名为include,另一个取名为src,方便前期管理文件,后期要更加细分。
直接将文件拖动到文件夹中,点击重构,即可自动重构CMakeLists.txt。
2. 编写类文件
文件会自动生成一些信息,学过c++应该能看懂了。
我们直接处理图像:
在这个类中我需要一份原图像数组,一份处理后的图像数组。一个处理图像函数,一个获取处理后图像的接口函数:
Pretreatment.hpp
//
// Created by JYSimilar on 2023/2/2.
//#ifndef ZHI_NENG_CHE_PRETREATMENT_HPP
#define ZHI_NENG_CHE_PRETREATMENT_HPP#include <iostream>
#include <opencv2/opencv.hpp>/** @brief: 图像预处理类* @brief: 实例化后通过run函数传入原图,通过getBinaryImage获取二值化结果图**/
class Pretreatment {private:cv::Mat m_frame; // 原图cv::Mat m_binary; // 二值化图像private: // 临时参数cv::Mat m_element1 = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::Mat m_element2 = getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));std::vector<cv::Point2f> m_srcPoint;std::vector<cv::Point2f> m_dstPoint;cv::Mat m_perspectiveMat;int m_cols, m_rows;private:/** @brief: 处理图像函数*/bool processImage();public:// 初始化图像大小Pretreatment(const int cols, const int rows):m_cols(cols),m_rows(rows){m_srcPoint.resize(4);m_dstPoint.resize(4);}/** @brief: 处理图像函数接口*/void run(const cv::Mat frame);/** @brief: 获取二值化图像接口*/cv::Mat getBinaryImage();
};#endif //ZHI_NENG_CHE_PRETREATMENT_HPP
Pretreatment.cpp
//
// Created by JYSimilar on 2023/2/2.
//#include "../include/Pretreatment.hpp"/**************************** 类内程序 ****************************/bool Pretreatment :: processImage() {// 可以直接做灰度二值化,很方便,也好用好调// 这里处理比较复杂了if (m_frame.empty()) return false;cv::Mat gray;std::vector<cv::Mat> bgr;cvtColor(m_frame, gray, cv::COLOR_BGR2GRAY);split(m_frame, bgr);cv::Mat gray_bin, color_bin;// 二值化等cv::threshold(gray, gray_bin, 140, 255, cv::THRESH_BINARY);cv::subtract(bgr[0], bgr[2], color_bin);cv::threshold(color_bin, color_bin, 60, 255, cv::THRESH_BINARY);m_binary = gray_bin - color_bin;cv::morphologyEx(m_binary, m_binary, cv::MORPH_CLOSE, m_element1);// 腐蚀膨胀cv::erode(m_binary, m_binary, m_element2);cv::erode(m_binary, m_binary, m_element2);cv::dilate(m_binary, m_binary, m_element2);// 后面这一部分整体提到初始化中(到透视变换矩阵位置),减少运算时间m_rows = m_binary.rows, m_cols = m_binary.cols;m_srcPoint[0] = cv::Point2f(0, m_rows / 4);m_srcPoint[1] = cv::Point2f(m_cols, m_rows / 4);m_srcPoint[2] = cv::Point2f(m_cols, m_rows);m_srcPoint[3] = cv::Point2f(0, m_rows);m_dstPoint[0] = cv::Point2f(-230, 0);m_dstPoint[1] = cv::Point2f(m_cols + 230, 0);m_dstPoint[2] = cv::Point2f(m_cols - 230, m_rows);m_dstPoint[3] = cv::Point2f(230, m_rows);// 应用透视变换,矫正成规则矩形cv::Mat transform = getPerspectiveTransform(m_srcPoint, m_dstPoint);warpPerspective(m_binary, m_perspectiveMat, transform, m_binary.size());return true;
}/**************************** 接口程序 ****************************/void Pretreatment :: run(const cv::Mat frame){// 复制图片,使用=会修改原图frame.copyTo(m_frame);if (!processImage()) {std:: cout << "Image is empty...\n";}
}cv::Mat Pretreatment :: getBinaryImage(){return m_perspectiveMat;
}
具体操作在代码中有写注释,这里解释一下灰度二值化。
我的方法不适合新上手的新人,放在这里做参考,仅供学习。
灰度二值化
灰度二值化就是将图片先改成灰度图,然后通过设定阈值,阈值内设为某种亮度,阈值外设为某种亮度,来实现二值化,如亮度为100以内的都修改为0,高于100修改为255,这就是一种二值化操作了,在opencv中以API threshold
封装,具体使用方法百度。
3. 处理结果
测试主函数:
#include "../include/Pretreatment.hpp"int main() {Pretreatment pretreatment(640, 480);cv::Mat frame = cv::imread("../114.png");pretreatment.run(frame);cv::Mat binary = pretreatment.getBinaryImage();cv::imshow("test", binary);cv::waitKey(0);return 0;
}
五、结语
目前先写这么多了,如果反响不错,会继续写下一集的扫线算法。
【智能车】从零写一份自己的完全模型智能车寻路算法(有手就行) --- 01相关推荐
- 手写实现李航《统计学习方法》书中全部算法
点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来源:专知 [导读]Dod-o的手写实现李航<统计学习方法>书中全部算法, ...
- 《预训练周刊》第35期:零样本规划器的语言模型:为智能体提取可操作的知识、LaMDA:对话应用的语言模型...
No.35 智源社区 预训练组 预 训 练 研究 观点 资源 活动 关于周刊 本期周刊,我们选择了13篇预训练相关的论文,涉及动作规划.大模型改进.网络结构.零样本学习.对话模型.视频理解.机器翻译. ...
- mxm智能教育机器人无法智能对话_零代码使用腾讯TBP打造智能对话机器人
点击观看大咖分享 心疼你独自一人承担生活的苦难,寂寞夜里陪伴你的只剩无人倾诉的压抑和无处安放的焦虑.养个宠物,它却不能get到你的"宠言宠语".找个伴侣,还要浪费吵架的时间和精力. ...
- LaTeX写一份完整的物理实验报告
直流非平衡电桥 用LaTeX写一份完整的物理实验报告.主要复习一下LaTeX的文本编辑,和学习LaTeX的最新编译环境和系统. 本物理实验报告的编辑软件和环境分别是:Texlive2022 + Tex ...
- 程序员如何写一份更好的简历
01 引言 面试前要准备的第一件事就是写简历,从零开始写简历是一件痛苦的事,因为它既重要又耗时.现在就有很多求职者在电脑前急躁不安地搜索简历模版和参考例句,却不知道如何下笔.写得太短的话HR一定觉得没 ...
- 领英个人简介如何支持html,普通外贸业务员怎么在领英上写一份完美的自我简介( Summary)?...
原标题:普通外贸业务员怎么在领英上写一份完美的自我简介( Summary)? 在领英上撰写优质的.完美的自我简介( Summary)是非常重要的,因为我是一名外贸业务员,所以站在我的角度来说,领英是我 ...
- 干货收藏 | 如何写一份专业的技术简历
作者:Windson Yang 本文转自:www.enginego.org 本文章由 ResumeJob 贡献,ResumeJob 能帮助你审视简历,模拟面试,重新规划你的职业生涯. 之前我写了一篇如 ...
- sis防屏蔽程序_程序员如何写一份更好的简历
01 引言 面试前要准备的第一件事就是写简历,从零开始写简历是一件痛苦的事,因为它既重要又耗时.现在就有很多求职者在电脑前急躁不安地搜索简历模版和参考例句,却不知道如何下笔.写得太短的话HR一定觉得没 ...
- 程序员如何写一份更好的简历!!
概述 面试前要准备的第一件事就是写简历,从零开始写简历是一件痛苦的事,因为它既重要又耗时.现在就有求职者在电脑前急躁不安地搜索简历模版和参考例句,却不知道如何下笔.写得太短的话觉得没诚意,写得太长的话 ...
最新文章
- html 标签 中 的Lang 有什么用
- python输入隔行的数组_python-使用间隔掩码numpy数组
- html5里可移动线性进度条的类型怎么表示,HTML5触摸事件实现移动端简易进度条的实现方法...
- 云炬60s看世界20211117
- Kubernetes APIServer机制概述
- 解析Json需要设置Mime
- python 文件指针在文件末尾_python文件操作及seek偏移详解
- 中科院自动化所目标跟踪论文整理!三篇综述、两篇ICCV 2019!
- 递增的整数序列链表的插入_LeetCode基础算法题第178篇:和为零的N个唯一整数
- 判断numpy的array中是否包含nan,NaN
- 用mysql web建立论坛_在web1上搭建Discuz论坛
- 纯css3代码写九宫格效果
- [16]manjaro安装nvidia驱动
- 面试官:前端布局了解嘛?我一下说了接近五十种布局方案,给面试官整不会了。
- 如何制作扫描连接WIFI二维码,手机扫码即可一键连接无线WIFI网络
- java 查看堆内存_查看java内存情况的几个常用命令
- SpringBoot发生404跳转404页面
- mysql误删库恢复操作
- Proxy与Object.defineProperty的优劣对比
- 2019NOIP普及组题解