ROS:添加第三方库
参考资料:
编写C++程序时,为了方便,常常会使用一些已经编好的开源库。那么如何使用这些开源库就成了一个问题。之前我一直对这方面不太清楚,这次需要在ROS中使用第三方库,正好查查资料了解一下这方面的知识。
1.C++库
库是指已经写好的现有的,成熟的,可以复用的代码。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。一般会存在两种类型的库,它们的命名规则如下:
- 静态库:
- Linux:
libxxx.a
; - Windows:
xxx.lib
;
- Linux:
- 动态库:
- Linux:
libxxx.so
; - Windows:
xxx.dll
。
- Linux:
所谓静态、动态是指链接的方式不同。 C++编写好的源文件经过预编译、编译、汇编流程后,转化为目标文件.o
,然后这个目标文件会在链接阶段与一些使用到的静态库或动态库进行链接,最终形成一个可执行文件,如下图所示。

上文说到动态和静态的区别在于链接的方式不同,那么它们究竟哪里不同呢?有什么优缺点呢?
1.1.静态库
首先,我们来谈谈静态库。所谓静态链接,是指在链接阶段,会将汇编生成的目标文件.o
与引用到的库一起链接打包到可执行文件中。这也说明了静态库文件也应该是类似于目标文件.o
的形式,或者说是很多目标文件的集合。 本质上,静态链接,就是目标文件与静态库的目标文件集合之间的链接,然后产生一个可执行文件。下图展示了一个静态库的创建过程。

那么该如何使用静态库呢?下面简要介绍一下在Linux系统下使用方法。使用方法很简单,只需要在使用g++
编译时,添加一些链接参数即可,用于说明在链接阶段静态库的储存位置和名字。
# -L: 添加所需要库的目录
# -l: 添加所需要库的名称(不需要前缀lib和后缀名.a)
g++ TestStaticLibrary.cpp -L ../StaticLibrary -l staticmath # 静态库名:libstaticmath.a
需要注意的是,编译器查找链接库时有隐含的命名规则,即会在给出的名字的前面自动加上lib,后面自动加上.a或.so来确定库的名称。 因此,-l
指定链接库的名字时不需要添加前缀和后缀。
1.2.动态库
虽然,静态库可以让我们移植程序更加方法,但它存在一些缺点。
- 静态库的链接是放在编译时期(源文件到可执行文件)完成的。 当静态库修改之后,那么必然需要对程序进行重新编译,会比较麻烦;
- 静态库比较浪费资源和空间。 因为在链接时,它会把所有用到的目标文件都链接为一个可执行文件,会导致一些重复。如下图所示。

由于静态库的上述缺点,动态库应运而生。动态库在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入。 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可。

动态库的使用方式与静态库相同,在使用g++
编译时,添加一些链接参数即可,用于说明在链接阶段静态库的储存位置和名字。
# -L: 添加所需要库的目录
# -l: 添加所需要库的名称(不需要前缀lib和后缀名.so)
g++ TestDynamicLibrary.cpp -L ../DynamicLibrary -l dynmath # 动态库名:libdynmath.so
但与静态库的使用不同的是,使用动态库时还需要保证使用的库文件可以被系统访问。这是由于动态链接的特点,程序在每一次运行时,都需要访问这个动态库。而静态链接会在编译过程中就包含了静态库,因此生成可执行文件后就不需要再访问静态库了。
为了让系统能够找到,需要做以下操作:
-
如果安装在
/lib
或者/usr/lib
下,那么动态加载器ld默认能够找到,无需其他操作; -
如果安装在其他目录,需要将其添加到
/etc/ld.so.cache
文件中,步骤如下:- 编辑
/etc/ld.so.conf
文件,加入库文件所在目录的路径; - 运行
ldconfig
,该命令会重建/etc/ld.so.cache
文件。
- 编辑
-
如果安装在其他目录,但是又不想修改
/etc/ld.so.conf
文件。那可以修改全局变量LD_LIBRARY_PATH
,在该全局变量中添加新的路径,然后运行程序的时候就会去这个目录中找动态库。
最后,需要注意的是,无论是使用静态库还是动态库,在源文件中都要用include
包含。
2.在ROS中添加第三方库
接下来,就是本篇的正题了。在ROS中添加第三方库,也就是添加静态库或者动态库,步骤其实与上文提到的相同,都需要指定链接时使用到的库文件的储存位置和库名。只不过,在ROS功能包中进行这些操作,需要修改功能包下的CMakeLists.txt
。
- 在
link_directories()
里添加所需要的库的存储路径,相当于编译过程中的-L
命令; - 在
target_link_libraries()
里面添加需要的库的名称,相当于编译过程中的-l
命令。
下面简要描述一下我在ROS中添加C++第三方库fuzzylite
的过程:
-
使用
git clone
命令从github上下载相关源码; -
根据使用文档,通过源码中的脚本
build.sh
把相关源码编译成静态库或动态库文件; -
修改功能包下的
CMakeLists.txt
。为了以后便于移植,我把动态库文件libfuzzylite.so
放在了功能包下的lib
文件夹内。然后,把相关头文件放在了include
文件夹内。CMakeLists.txt
修改如下:include_directories(${catkin_INCLUDE_DIRS} include) # include: 头文件相对路径 link_directories(${catkin_LIB_DIRS} lib) # lib: 库文件的相对路径 add_executable(fuzzy_logic_control src/fuzzy_logic_control.cpp) # 编译成可执行文件 target_link_libraries(fuzzy_logic_control ${catkin_LIBRARIES} fuzzylite) # 与使用到的库进行链接
-
最后,使用ROS命令
catkin_make
进行编译即可。