0. 简介

在深入剖析了CeresEigenSophusG2O后,以V-SLAM为代表的计算方式基本已经全部讲完。就L-SLAM而言,本系列也讲述了PCLOpen3D、与GTSAM点云计算部分。最近在了解boost,个人觉得这些仍然在SLAM以及自动驾驶中是必须的,打算仍然延续本质剖析的结构来续写这个系列,方便自己回顾以及后面的人一起学习。

1. C++总体介绍

通用库

Boost:Boost是功能相当丰富的C++基础库,boost里有很多好用的组件,都值得我们学习,造轮子时也有个方向。

Folly:Facebook的开源库,和Boost类似,该框架基于C++14的库,目的不是为了替代标准库,而是对标准库的一种补充,尤其是大规模下的性能。

Abseil:Google的STL,我个人更喜欢的一个开源库,主要文档比较全,官方文档关于Abseil介绍的也很详细。这个库的特点是用 C++ 11 的代码实现了许多 C++ 14 和 C++ 17 的特性.

通讯

zeromq:zmq是一个为可伸缩的分布式或并发应用程序设计的高性能异步消息库。它提供一个高性能消息队列,该库设计成常见的套接字风格的API。ZeroMQ以嵌入式网络编程库的形式实现了一个并行开发框架(concurrency framework),能够提供进程内(inproc)、进程间(IPC)、网络(TCP)和广播方式的消息信道,并支持扇出(fan-out)、发布-订阅(pub-sub)、任务分发(task distribution)、请求/响应(request-reply)等通信模式。

asio:asio 是一个跨平台的C++网络编程框架,通过先进的C++方法为开发人员提供连续异步模型。(boost中存在该模型).

muduo:一个基于 Reactor 模式的现代 C++ 网络库,它采用非阻塞 IO 模型,基于事件驱动和回调,支持多核多线程,适合编写 Linux 服务端多线程网络应用程序。

protobuf:Google出品,protobuf一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。

poco:一个开源的C++类库的集合,它主要提供简单的、快速的网络和可移植应用程序的C++开发,这个类库和C++标准库可以很好的集成并填补C++标准库的功能空缺。POCO库的模块化、高效的设计及实现使得特别适合嵌入式开发。

libevent:一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动,高性能,轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台;支持多种 I/O 多路复用技术:epoll、poll、select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级等。

音视频

FFmpeg:ffmpeg的功能相当强大,音视频采集、音视频格式转换、滤镜、水印、音视频编解码、推送到流媒体服务器等等,太多了,对视频的绝大多数处理都可以使用ffmpeg,可以说无所不能。

图像处理

OpenCV:OpenCV用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS多种平台, 如今也提供对于C#、Ch、Ruby,GO的支持。

图形

OpenGL:用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API),图型开发者貌似都离不开OpenGL,与纹理有关的操作基本都会用到OpenGL,常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。

Vulkan:下一代的OpenGL,可解决OpenGL很多框架层面的缺陷,更高性能,可能我们以后都会从OpenGL迁移到Vulkan吧,但升级永远没有那么容易,成本太高,至少它现在还没那么普及。

Ogre:一个三维(3D)图形渲染引擎。它是面向对象的,并且高效,抽象化了不同的API和平台,这样可以以场景为对象来使用物体,支持多种场景。它已经成功地被应用于诸多三维仿真领域,包括网络游戏和三维仿真项目。

线性代数

Eigen:一个线性算术的C++模板库,功能强大、快速、优雅以及支持多平台。

GLM:由于 C/C++标准库中没有几何数学库,这样造成在开发一个三维系统之初往往都需要自行实现一个实用的几何数学库,这样太费时费力了。GLM的出现可以很好的解决这个问题,而且GLM不需要编译成任何库,只需要依赖头文件即可使用(OpenGL搭配GLM使用,很爽)。

JSON

C++语言就在rapidjson和nlohmann/json中选一个,C语言就用cJSON

LOG

spdlog:目前最火的C++log库,只需要头文件即可,兼容C++11,相当方便

glog:Google Glog 是一个C++语言的应用级日志记录框架,提供了 C++ 风格的流操作和各种辅助宏。

界面

Qt:工具丰富、功能丰富、开源并跨平台、架构的优势等,导致原来越多的程序员投向了QT的怀抱。

参数解析

gflags:google开源的gflags是一套命令行参数解析工具,功能强大,使用起来非常方便,gflags还支持从环境变量、配置文件读取参数

rpc

brpc:brpc又称为baidu-rpc,百度出品。brpc目前被应用于百度公司内部和其他外部好多公司的各种核心业务上。

grpc:gRPC 是可以在任何环境中运行的现代开源高性能 RPC 框架。google出品,必属经典。

2. boost介绍

Boost库是为C++语言标准库提供扩展的一些C++程序库的总称。当中有很多扩展库已经在C++的标准库中应用。按照实现的功能,Boost可为大致归入以下20个分类,在下面的分类中,有些库同时归入几种类别。详细的内容可以参考百度百科。目前在SLAM当中最常用的就是Geometry和Graph库。 Geometry库是用于解决几何问题的概念、原语和算法。而Graph 库则是处理图结构的库。

3. boost之Geometry

以下是Geometry常用的头文件以及typedef方式。

#include <boost/assign.hpp>
#include <boost/geometry/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/ring.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/geometry/algorithms/transform.hpp>
#include <boost/geometry/strategies/transform/inverse_transformer.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>

namespace bg = boost::geometry;
typedef bg::model::d2::point_xy<double> DPoint;
typedef bg::model::segment<DPoint> DSegment;
typedef bg::model::linestring<DPoint> DLineString;
typedef bg::model::box<DPoint> DBox;
//这里的ring就是我们通常说的多边形闭合区域(内部不存在缕空),模板参数为true,表示顺时针存储点,为false,表示逆时针存储点,由于MM_TEXT坐标系与传统上的坐标系的Y轴方向是相反的,所以最后为false,将TopLeft、TopRight、BottomRight、BottomLeft、TopLeft以此存储到ring中,以便能正确计算
typedef bg::model::ring<DPoint, false> DRing;
//polygon模板参数false,也是由上面相同的原因得出来的
typedef bg::model::polygon<DPoint, false> DPolygon;

以下是Geometry常用的实现方法

DPoint pt0(100, 100);
DPoint pt1(200, 200);
DSegment sg0(pt0, pt1);

double dDistance = 0;

//1、点到点的距离
dDistance = bg::distance(pt0, pt1);
//2、点到线段的距离,如果点到直线的垂足不在线段上,所计算的距离为(点到直线的距离、线段到垂足延长的距离之和)
dDistance = bg::distance(DPoint(200, 100), sg0);
dDistance = bg::distance(DPoint(100, 0), sg0);

//3、判断线段是否相交
DSegment sg1(DPoint(0, 100), DPoint(100, 0));
DSegment sg2(DPoint(100, 200), DPoint(200, 100));
bool bIntersect = false;
bIntersect = bg::intersects(sg0, sg1);
bIntersect = bg::intersects(sg0, sg2);

//4、求线段与线段的交点
std::list<DPoint> lstPoints;
bg::intersection(sg0, sg1, lstPoints);
lstPoints.clear();
bg::intersection(sg0, sg2, lstPoints);

DBox rc2(DPoint(0, 0), DPoint(0, 0));

//5、判断box是否相交
DBox rc(DPoint(0, 0), DPoint(200, 200));
DBox rc0(DPoint(250, 250), DPoint(450, 450));
DBox rc1(DPoint(100, 100), DPoint(300, 300));

bIntersect = bg::intersects(rc, rc0);
bIntersect = bg::intersects(rc, rc1);
//bg::intersection(rc, rc0, container);//error

//6、判断box是否与LineString相交
DLineString line0;

line0.push_back(DPoint(10, 250));
line0.push_back(DPoint(100, 100));
line0.push_back(DPoint(120, -10));
line0.push_back(DPoint(210, 200));
bIntersect = bg::intersects(rc, line0);
bIntersect = bg::intersects(rc0, line0);

//7、求box与linestring的交点
std::list<DLineString> lstLines;
bg::intersection(rc, line0, lstLines);

//8、点是否在box内
DBox rc7(DPoint(0, 0), DPoint(100, 100));
bool bInside = false;
bInside = bg::within(DPoint(50, 50), rc7);
bInside = bg::within(DPoint(0, 0), rc7);

//9、判断LineString与LineString是否相交
DLineString line1, line2, line3;

line1.push_back(DPoint(50, 50));
line1.push_back(DPoint(150, 50));
line1.push_back(DPoint(50, 200));
line1.push_back(DPoint(150, 200));
line2.push_back(DPoint(100, 0));
line2.push_back(DPoint(70, 100));
line2.push_back(DPoint(150, 210));
line3.push_back(DPoint(200, 0));
line3.push_back(DPoint(200, 200));

bIntersect = bg::intersects(line1, line2);
bIntersect = bg::intersects(line1, line3);

//10、求LineString与LineString的交点
lstPoints.clear();
bg::intersection(line1, line2, lstPoints);
lstPoints.clear();
bg::intersection(line1, line3, lstPoints);


//11、判断ring与ring是否相交
DPoint arDPoint0[6] = {DPoint(0, 0), DPoint(100, 0), DPoint(200, 100), DPoint(100, 200), DPoint(0, 200), DPoint(0, 0)};
DPoint arDPoint1[6] = {DPoint(100, 100), DPoint(200, 0), DPoint(300, 0), DPoint(300, 200), DPoint(200, 200), DPoint(100, 100)};
DRing r0(arDPoint0, arDPoint0 + 6);
DRing r1(arDPoint1, arDPoint1 + 6);
bIntersect = bg::intersects(r0, r1);

//12、求ring与ring的交点
lstPoints.clear();
bg::intersection(r0, r1, lstPoints);

DPolygon poly1;
DPolygon poly2;
DPolygon poly3;

auto lstOf = boost::assign::list_of(DPoint(0, 0))(DPoint(200, 0))(DPoint(200, 200))(DPoint(0, 200))(DPoint(0, 0));
poly1.outer().assign(lstOf.begin(), lstOf.end());
lstOf = boost::assign::list_of(DPoint(50, 50))(DPoint(150, 50))(DPoint(150, 150))(DPoint(50, 150))(DPoint(50, 50));
poly1.inners().push_back(lstOf);
lstOf = boost::assign::list_of(DPoint(100, 0))(DPoint(120, 0))(DPoint(120, 200))(DPoint(100, 200))(DPoint(100, 0));
poly2.outer().assign(lstOf.begin(), lstOf.end());
lstOf = boost::assign::list_of(DPoint(100, 60))(DPoint(120, 60))(DPoint(120, 140))(DPoint(100, 140))(DPoint(100, 60));
poly3.outer().assign(lstOf.begin(), lstOf.end());

//13、判断polygon与polygon是否相交
bIntersect = bg::intersects(poly1, poly2);
bIntersect = bg::intersects(poly1, poly3);

//14、求polygon与polygon相交的区域
std::list<DPolygon> lstPolygon;

bg::intersection(poly1, poly2, lstPolygon);
lstPolygon.clear();
bg::intersection(poly1, poly3, lstPolygon);

//15、判断点是否在polygon内
bInside = bg::within(DPoint(100, 100), poly1);
bInside = bg::within(DPoint(25, 25), poly1);

//16、平移变换
template<typename Geometry, typename CalculationType>
struct translate_trais
{
    static void apply(Geometry& geometry, const CalculationType& x, const CalculationType& y);
};

template<typename Geometry, typename CalculationType>
void translate(Geometry& geometry, const CalculationType& x, const CalculationType& y)
{
    translate_trais<Geometry, CalculationType>::apply(geometry, x, y);
}
bg::assign_point(pt4, pt1);
translate(pt4, 10, 20);

//    17.旋转变换
template<typename Geometry, typename DegreeOrRadian, typename CalculationType>
struct rotate_trais
{
    static void apply(Geometry& geometry, const CalculationType& angle);
};

template<typename Geometry, typename DegreeOrRadian, typename CalculationType>
void rotate(Geometry& geometry, const DegreeOrRadian&, const CalculationType& angle)
{
    rotate_trais<Geometry, DegreeOrRadian, CalculationType>::apply(geometry, angle);
}
bg::assign(pt4, pt1);
Grotate(pt4, bg::degree(), 45.0);

//17、比例变形
template<typename Geometry, typename CalculationType>
struct scale_trais
{
    static void apply(Geometry& geometry, const CalculationType& scale_x, const CalculationType& scale_y);
};
template<typename Geometry, typename CalculationType>
void scale(Geometry& geometry, const CalculationType& scale_x, const CalculationType& scale_y)
{
    scale_trais<Geometry, CalculationType>::apply(geometry, scale_x, scale_y);
}
bg::assign(pt4, pt1);
scale(pt4, 0.5, 0.2);

4. boost之Graph

#include <boost/graph/undirected_graph.hpp>
#include <boost/graph/adjacency_list.hpp>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    // 创建简单无向图
    //typedef boost::adjacency_list<boost::lists boost::undirecteds="" boost::vecs=""> Graph;
    /*
    Graph g;
    // 添加边
         boost::add_edge(0, 1, g);
    boost::add_edge(0, 3, g);
    boost::add_edge(1, 2, g);
    boost::add_edge(2, 3, g);

    cout << "Number of edges: " << boost::num_edges(g) << endl;// 边的数量
    cout << "Number of vertices: " << boost::num_vertices(g) << endl;// 顶点的数量


    Graph::vertex_iterator vertexIt,vertexEnd;// 顶点
    Graph::adjacency_iterator neighbourIt, neighbourEnd;// 邻接边

    boost::tie(vertexIt, vertexEnd) = boost::vertices(g);
    for (; vertexIt != vertexEnd; ++vertexIt) {
        std::cout << "edge vertex " << *vertexIt << std::endl;
        // 输出顶点的入边
        Graph::in_edge_iterator inedgeIt, inedgeEnd;

        boost::tie(inedgeIt, inedgeEnd) = boost::in_edges(*vertexIt, g);
        std::cout << "in edge: ";
        for (; inedgeIt != inedgeEnd; ++inedgeIt) {
            std::cout << *inedgeIt << " ";
        }
        std::cout << std::endl;


        // 输出顶点的出边
        Graph::out_edge_iterator outedgeIt, outedgeEnd;

        boost::tie(outedgeIt, outedgeEnd) = boost::out_edges(*vertexIt, g);
        std::cout << "out edge: ";
        for (; outedgeIt != outedgeEnd; ++outedgeIt) {
            std::cout << *outedgeIt << " ";
        }
        std::cout << std::endl;
    }
    */


    // 创建无向图,并使用顶点和边的属性
    // 顶点属性
    typedef boost::property<boost::vertex_name_t std::string=""> VertexNameProp;
    // 边属性
    typedef boost::property<boost::edge_weight_t int=""> EdgeWeightProp;
    // 图
    typedef boost::adjacency_list<boost::lists boost::vecs="" boost::undirecteds="" vertexnameprop="" edgeweightprop=""> Graph;

    Graph g;
    std::string citys[4] = {"北京", "上海", "武汉", "西安"};
    Graph::vertex_descriptor v[4];
    // 添加顶点
    v[0] = boost::add_vertex(citys[0], g);
    v[1] = boost::add_vertex(citys[1], g);
    v[2] = boost::add_vertex(citys[2], g);
    v[3] = boost::add_vertex(citys[3], g);
    // 添加边
    boost::add_edge(v[0], v[1], 10, g);
    boost::add_edge(v[0], v[2], 40, g);
    boost::add_edge(v[0], v[3], 50, g);

    Graph::vertex_iterator vertexIt, vertexEnd;
    // 顶点的属性
    boost::property_map<graph boost::vertex_name_t="">::type vertexprop = boost::get(boost::vertex_name, g);
    // 边的属性
    boost::property_map<graph boost::edge_weight_t="">::type edgeprop = boost::get(boost::edge_weight, g);

    boost::tie(vertexIt, vertexEnd) = boost::vertices(g);
    for (; vertexIt != vertexEnd; ++vertexIt) {
        // 获取顶点属性
        std::string vprop = vertexprop[*vertexIt];
        // 设置顶点的属性 
        //vertexprop[*vertexIt] = "";

        Graph::out_edge_iterator outedgeIt, outedgeEnd;
        // 设置边属性
        boost::tie(outedgeIt, outedgeEnd) = boost::out_edges(*vertexIt, g);
        for (; outedgeIt != outedgeEnd; ++outedgeIt) {
            edgeprop[*outedgeIt] = 100;
        }
    }
}

5. 参考链接

https://baike.baidu.com/item/Boost%E5%BA%93/10671694?fr=aladdin

https://zhuanlan.zhihu.com/p/404489941

https://www.cnblogs.com/tiandsp/p/7762138.html

https://blog.csdn.net/it_xiangqiang/category_11077598.html

https://blog.csdn.net/dc2010_/article/details/24054767

https://blog.csdn.net/dc2010_/article/details/39758641

https://www.boost.org/doc/libs/1_78_0/