视觉SLAM十四讲随笔

☆樱花仙子☆ 提交于 2019-11-26 19:19:04

CH1:

clion的使用

断点设置之后,逐行执行 (Step Into) ( F7 ),逐函数执行 (Step Over) ( F8 ),注意0和O在clion中的显示区别,0是中间有个点的,O看起来像0,中间没有原点

Run->Edit Configurations-> Program arugements可直接加入程序所需的文件等等,便于debug的使用 如ch5的图片

Run->Edit Configurations-> Working Directory 可直接加入fstream所需的工作目录,便于debug或者run的使用 如ch4的轨迹文件

TODO 类似与带书签属性的注释

 

CH2:

文件夹的命名和作用:

library是库文件 include是头文件 src是源文件(该源文件不一定是可执行程序的所需的源文件,也可以是生成所需的内部库的源文件)

CMakeLists.txt:

library是库的意思 include是包含 directory是目录  

include_directories() 添加目录

一般find_package()下紧挨着include_directories() 找到外部库之后添加目录到工程中

find_package(OpenCV 3 REQUIRED)    include_directories(${OpenCV_INCLUDE_DIRS})

注意:OpenCV大小写和${NAME_INCLUDE_DIRS}  在target_link_libraries( ... ${NAME_LIBS}) DIRS和LIBS

在ORB_SLAM的CMakeLists中 使用${PROJECT_NAME} 变量作为中间量,把ORB_SLAM中所需的内部库和外部库一切连接起来

set(CMAKE_CXX_STANDARD 11)是高版本 

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")是低版本 

在set(CMAKE_BUILD_TYPE "Release")如果要是用断点 修改"Release"为"Debug"。

同时,如果有set(CMAKE_CXX_FLAGS "-O3")也要去掉"-O3"或者改成"-O0" "-O3"表示优化程度最高 "-O0"表示不需要优化

find_package具体用法https://www.jianshu.com/p/39fc5e548310


CH3:

sudo apt-get install libeigen3-dev :libeigen3-dev lib表示库 eigen3就是软件名称和版本 dev表示开发包 安装到/usr/include/目录中

eigen求矩阵的特征值和特征向量的时候,得是实对称矩阵,才能保证对角化的成功。

.cast<double> 是显式转换的意思

对于eigen的出错,因为是模板类,所以错误不是很好读,但是可以在错误中找到 required from here 有对应的错误代码行提示的。

CH3/examples/plotTrajectory.cpp:

按照git下clone的文件 由于在ch3/下有CMakeLists.txt 直接在ch3/目录下编译,并且不切换到其他目录,直接在该目录下执行: cmake-build-debug/examples/plotTrajectory 就会成功编译。是因为trajectory_file的路径显示examples/trajectory.txt,而在编译的时候(无论是build还是cmake-build-debug)都是默认在当前目录下寻找./examples/trajectory.txt。所以无论是在example/目录下还是正确编译之后(cmake-build-debug/examples/plotTrajectory),只要运行可执行程序时,对应不到./examples/trajectory.txt都会报错。当然,也可以更改trajectory_file的路径。

如果STL容器中的元素是Eigen库数据结构,例如这里定义一个vector容器,元素是Matrix4d 则vector<Eigen::Matrix4d>这个错误也是和上述一样的提示,编译不会出错,只有在运行的时候出错。解决的方法很简单,定义改成下面的方式:vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>;其实上述的这段代码才是标准的定义容器方法,只是我们一般情况下定义容器的元素都是C++中的类型,所以可以省略,这是因为在C++11标准中,aligned_allocator管理C++中的各种数据类型的内存方法是一样的,可以不需要着重写出来。但是在Eigen管理内存和C++11中的方法是不一样的,所以需要单独强调元素的内存分配和管理。

pangolin::CreatWindowAndBind创建3D窗口,()内含windows_title,width,height

glEnable:启用或禁用服务器端GL功能 GL_DEPTH_TEST: GL_BLEND颜色的blend 

glBlendFunc:定义启用所有绘制缓冲区的混合操作。总之就是为了画图的

 

CH4:

SLAM十四讲第一版用的sophus是非模板,第二版用的是模板类。模板类是基于eigen3.2.92版本,非模板是基于eigen3.3.7。虽然书上说Sophus不用安装,但是有时程序会找不到头文件,还是需要sudo make install

sophus是一个很小的库,不用安装,完全可以集成在工程中,find_package 是寻找SophusConfig.cmake文件,这里面说明了你的Sophus库安装在哪里,如果只是make了,没有make install,那把Sophus的目录就放在当前项目根目录中,这个工程中的find_package会在当前目录中找这个配置文件

由于系统既安装了模板类,也安装了非模板类的sophus 因此在使用模板类时,正常使用 而使用非模板类时,需将target_link_libraries中的${Sophus_LIBRARIES}改为具体路径,如/usr/local/lib/libSophus.so 等(在sophus非模板的源文件中,找到SophusConfig.cmake中,对应的${Sophus_LIBRARIES}变量即可)

因为sophus是基于eigen的,所以R或者T也可以用SO3和SE3表示更优。

ch4/trajectoryError.cpp: typedef:

1)定义一种类型的别名:char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针和一个字符变量;

 typedef char* P; P pa,pb;//即可

2)struct tag{int x;int y;}; struct tag p1;可以改为 typedef struct tag{int x; int y;}P; P p1;

详情见https://blog.csdn.net/andrewniu/article/details/80566324#commentBox

assert:预处理宏(所谓预处理宏就是一个预处理变量,行为有点类似与内联函数)用法assert(expr); 如果expr的值为0/假,assert输出信息并终止程序的执行。如果expr的值为1/真,assert什么也不做。

 

CH5:
CH5/imageBasics.cpp:

int main(int argc,char ** argv) //函数体内使用或不使用argc和argv都可

带形参的main函数,如 main( int argc, char* argv[], char **env ) ,是UNIX、Linux以及Mac OS操作系统中C/C++的main函数标准写法,并且是血统最纯正的main函数写法。 

第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。        

第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:        

argv[0]指向程序运行的全路径名        

argv[1]指向在DOS命令行中执行程序名后的第一个字符串        

argv[2]指向执行程序名后的第二个字符串        

argv[3]指向执行程序名后的第三个字符串        

argv[argc]为NULL        

第三个参数,char**型的env,为字符串数组。env[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。平时使用到的比较少。

详细解释内容见https://blog.csdn.net/dgreh/article/details/80985928


CV_8UC1:一般的图像文件格式使用的是 Unsigned 8bits吧,CvMat矩阵对应的参数类型就是CV_8UC1,CV_8UC2,CV_8UC3。(最后的1、2、3表示通道数,譬如RGB3通道就用CV_8UC3)

而float 是32位的,对应CvMat数据结构参数就是:CV_32FC1,CV_32FC2,CV_32FC3...double是64bits,对应CvMat数据结构参数:CV_64FC1,CV_64FC2,CV_64FC3等。

https://blog.csdn.net/qq_29540745/article/details/52487832


size_t:size_t在C语言中就有了。它是一种"整型"类型,里面保存的是一个整数,就像int, long那样。这种整数用来记录一个大小(size)。size_t的全称应该是size type,就是说"一种用来记录大小的

数据类型",便于程序的移植。 

遍历图像:有两层循环,第一层是循环图像的行row,第二层是图像的列column。该遍历使用的是指针循环首先定义指针 指针类型为unsigned char 是属于图像指针image.ptr的一种,image.ptr的类型也为unsigned char, (y)表示第y行,因为是要跟着循环走。第二层循环定义了数据指针data_ptr 要跟着第一层循环,所以是&row_ptr[x* image.channels()]可能图片是灰度图、深度图、或者彩色图。对于灰色图只有一个通道(其实通道这个概念是针对彩色图而提出的),而对于彩色图来说,有三或四个通道。进一步,还能输出每个像素对应的多个各个通道的值。


在cv::Mat拷贝中加入cv::imshow("image_another",image_another);与cv::imshow("image",image);

对比可得知:浅拷贝也就是这段code 在修改image_another时,原image也相应的改变。而下一段code,就只改变image_clone图片,原image不改变,就是所谓的深拷贝。


单目成像的过程:第一步,在世界坐标系下有一固定的点$P_W$
;第二步,由于相机在运动,$P_W$转换成$P_W^\prime=T_{CW}P_W$(因为相机的位置就是机器人的位置,而对于成的像来说,就相当于路标,所以要切换到相机坐标系下);第三步,将$P_C^\prime$投影到归一化平面上($Z=1$)变成 $P_C$, 

  PC=[X/Z,Y/Z];第四步,将PC 畸化 Pc_distorted,因为相机产生畸变(注意,这里所说的是畸变,看下角标distorted,去畸化是undistorted);第五步,$P_{Cdistorted}$左乘内参矩阵,得出像素矩阵。(内参矩阵包含坐标的放缩和像素原点的平移)。


ch5/undistort_image:要求去畸化。计算去畸变后的图像的内容:循环给出的最后一部分是将image(v_distorted,u_distorted)=image_undistorted(v,u),就是求得的distorted视为undistorted。像素坐标与归一化坐标没有问题。问题就是畸变和去畸变之间的转变问题。


通常来说,假设v是一个vector对象,则v.at(n)和v[n]是一样的,只不过前者会检查是否越界(因此花费的时间稍多),而后者不会(后者越界会导致未定义行为)。


最近邻插值越是简单的模型越适合用来举例子,我们就举个简单的图像:3X3 的256级灰度图,也就是高为3个象素,宽也是3个象素的图像,每个象素的取值可以是 0-255,代表该像素的亮
度,255代表最亮,也就是白色,0代表最暗,即黑色。假如图像的象素矩阵如下图所示(这个原始图把它叫做源图,Source):

234   38    22

67     44    12

89     65    63

这个矩阵中,元素坐标(x,y)是这样确定的,x从左到右,从0开始,y从上到下,也是从零开始,这是图象处理中最常用的坐标系,就是这样一个坐标: 

----------------------> X 

|

|

∨ Y

如果想把这副图放大为 4X4大小的图像,那么该怎么做呢?那么第一步肯定想到的是先把4X4的矩阵先画出来再说,好了矩阵画出来了,如下所示,当然,矩阵的每个像素都是未知数,等待着我们去填充(这个将要被填充的图的叫做目标图,Destination):

?        ?        ?       ?

?        ?        ?       ?

?        ?        ?       ?

?        ?        ?       ?                

然后要往这个空的矩阵里面填值了,要填的值从哪里来来呢?是从源图中来,先填写目标图最左上角的象素,坐标为(0,0),那么该坐标对应源图中的坐标可以由如下公式得出:    

$$X_{src}=X_{dst}*\left({Width}_{src}/{Width}_{dst}\right)\\Y_{src}=Y_{dst}*\left({Width}_{src}/{Width}_{dst}\right)$$

好了,套用公式,就可以找到对应的原图的坐标了(0*(3/4),0*(3/4))=>(0*0.75,0*0.75)=>(0,0),找到了源图的对应坐标,就可以把源图中坐标为(0,0)处的234象素值填进去目标图的(0,0)这个位置了。接下来,如法炮制,寻找目标图中坐标为(1,0)的象素对应源图中的坐标,套用公式:(1*0.75,0*0.75)=>(0.75,0)结果发现,得到的坐标里面竟然有小数,这可怎么办?计算机里的图像可是数字图像,象素就是最小单位了,象素的坐标都是整数,从来没有小数坐标。这时候采用的一种策略就是采用四舍五入的方法(也可以采用直接舍掉小数位的方法),把非整数坐标转换成整数,那么按照四舍五入的方法就得到坐标(1,0),完整的运算过程就是这样的:(1*0.75,0*0.75)=>(0.75,0)=>(1,0)那么就可以再填一个象素到目标矩阵中了,同样是把源图中坐标为(1,0)处的像素值38填入目标图中的坐标。  

依次填完每个象素,一幅放大后的图像就诞生了,像素矩阵如下所示:

234    38     22     22

67      44     12     12

89      65     63     63

89      65     63     63

这种放大图像的方法叫做最临近插值算法,这是一种最基本、最简单的图像缩放算法,效果也是最不好的,放大后的图像有很严重的马赛克,缩小后的图像有很严重的失真;效果不好的根源就是其简单的最临近插值方法引入了严重的图像失真,比如,当由目标图的坐标反推得到的源图的的坐标是一个浮点数的时候,采用了四舍五入的方法,直接采用了和这个浮点数最接近的象素的值,这种方法是很不科学的,当推得坐标值为 0.75的时候,不应该就简单的取为1,既然是0.75,比1要小0.25 ,比0要大0.75 ,那么目标象素值其实应该根据这个源图中虚拟的点四周的四个真实的点来按照一定的规律计算出来的,这样才能达到更好的缩放效果。双线型内插值算法就是一种比较好的图像缩放算法,它充分的利用了源图中虚拟点四周的四个真实存在的像素值来共同决定目标图中的一个像素值,因此缩放效果比简单的最邻近插值要好很多。原文链接:https://blog.csdn.net/caomin1hao/article/details/81092134

 

变元:在初等数学里,变量或变元、元是一个用来表示值的符号,该值可以是随意的,也可能是未指定或未定的。

定义1. 设矩阵$A=(a_{ij})\in R_{m\times n}$

把矩阵A的元素按行的顺序排列成一个列向量:$vecA=(a_{11},a_{12},\cdots,a_{1n},a_{21},a_{22},\cdots,a_{2n},\cdots,a_{m1},a_{m2},\cdots,a_{mn})^T$则称向量$vecA$为矩阵$A$按行展开的列向量。

定义2. 设矩阵$A=(a_{ij})\in R_{m\times n}$,

把矩阵A的元素按行的顺序排列成一个列向量:$vecA=(a_{11},a_{21},\cdots,a_{n1},a_{12},a_{22},\cdots,a_{n2},\cdots,a_{1n},a_{2n},\cdots,a_{mn})^T$则称向量$vecA$为矩阵$A$按列展开的列向量。

Jacobian为分子布局的类型(分子布局:vector y->mx1,vector x->nx1,求y对x的偏导)

 

 

 

 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!