ROS之CMakeLists

本文详细解析了ROS环境下Catkin包的构建流程及CMakeLists.txt配置要点,包括CMake版本要求、工程命名、依赖查找、消息与服务生成等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://wiki.ros.org/catkin/CMakeLists.txt#msgs_srvs_actions

 

提要:文章顺序基于网址内容进行解读

 

另外:Catkin包就是我们平常所说的功能包,或者说ROS包,package等,再次强调这里大部分函数都是属于make工具,所以具体查书吧。

 

推荐资料Cmake实践》

 

概述

 

1.CMakeLists.txt

该文件描述如何构建代码以及安装在哪里。

 

总结:

CMakeLists.txt 它的作用本质上就像出版社的排版工作,将我们准备要编译的内容整理好(也就是生成所谓的Makefile文件,不要搞错哦,cmake,不是编译,make 才是编译)

cmake+ CMakeLists.txt=Makefile  排版】

make+Makefile =目标文件       编译】

 

VS对应就是所谓的解决方案,CMakeLists.txt对应的文件就是VS .sln后缀文件

 

Catkin是集成了CMake这个跨平台编译工具的工具,所以具有很多cmake特性。

 

想具体知道CMake是什么东东的话   CMake实践》 

 

强调:CMakeLists.txtpackage.xml有很强的对应关系,CMakeList有的在package.xml都有而且能对应上,哪一个缺了都会报错。

 

2.CMakeLists.txt文件必须遵循此格式,否则您的包将无法正确构建。

Cmake最低版本                              cmake_minimum_required()

工程名称                               project()

查找构建所需的库文件或者catkin       find_package()

启用Python模块支持                    catkin_python_setup()

消息/服务/动作生成器                   add_message_files()

add_service_files()

add_action_files()

调用消息/服务/动作生成                 generate_messages()

Catkin包的导出、依赖信息                      catkin_package()

建立库和可执行文件                                        add_library()     

                                                  add_executable()

                                                          target_link_libraries()

编译测试                               catkin_add_gtest()

安装规则                               install()   安装和测试等要用的时候自己看

 

总结:

         如果足够熟悉catkinCMakeLists 的话可以不需要依赖命令生成,直接按照以下顺序填写     catkin说白了就是基于ROSCMakecatkinROS定制版的CMake

 

2.1.CMake版本         cmake_minimum_required()

函数意思是这个要编译我的cmake工具如果低于2.8.3,我就不让你编译。

 

例子:        

cmake_minimum_required(VERSION2.8.3)

如果编译当前CMakeLists的Cmake它的版本低于2.8.3,则不编译。

 

 

2.2.工程名称             project()

就是平常用soildworks啊,或者用VS时我们说的新建的项目,工程名字,或者解决方案的名字。

 

例子:

project(home)

我当前的工程名字叫home,

 

注意:它与我们的节点名字,功能包名字没有一点关系。

 

还有,它对应package.xml<name>…..<name><name>标签,最重要的是它代表的功能包的名字,如果它叫LL那么这个功能包名字叫LL

 

 

 

2.3.查找编译时所需的库文件或者catkin  find_package()

                                                                                                                                                

首先,find_package()这个函数,或者叫宏。是cmake原有,它是用来寻找库文件,这没什么好说的,比如找Eigen库,Boost库,都要用,看书

这里主要注意几点的是

 

1.      对于ROS的功能包,肯定依赖于catkin,换句话说find_package(catkin REQUIRED)一定要有。

 

REQUIRED的作用表示我要寻找的依赖包对我来说很重要,如果找不到就不编译了,上面的例子就是说我找不到catkin就不玩了。

 

2.      当我们用它找某功能包(catkin包)时,它是可以直接用 find_package()例如:我现在在某个工作空间里有一个功能包——nodelet,我现在在编写一个新的功能包,它的CMakelists.txt,就需要这个nodelet包,(是指功能上需要这个包)

 

 

可以这样写

find_package(nodelet REQUIRED)

find_package(catkin REQUIRED)

 

3.      也可以将这个nodelet功能包当成要依赖的catkin的一个部件,元件,组件,反正就是一部分。

 

所以还可以这样写

 find_package(catkin REQUIRED COMPONENTSnodelet)

 

 

所以有两种找catkin包方式:

第一种:             find_package(nodeletREQUIRED)

                             find_package(catkin REQUIRED)

第二种:             find_package(catkinREQUIRED COMPONENTSnodelet)  nodelet这个功能包当成catkin的一部分

 

注意Catkin包不是catkin这个依赖的真正组成部分。对于catkin包,如果find_package作为catkin的组件,这是有利的,因为使用catkin_前缀创建了一组环境变量。就是说,环境变量设置上简化了,不用我们想太多。如果找catkin包单独用findpackage的话,就会有属于这个包它自己环境变量,具体还是看书。

 

COMPONENTS它表示我们要找的包需要后面的这些库或者包,比如要找的catkin需要nodelet这个包。当然像前面说的,并非真的如此。

 

 

4.     所谓编译时所需的依赖,其实就是要进行编译时所需要的依赖包,findpackage()其实是对应了package.xml标签中的<build_depend>xxx</build_depend>特别在生成消息/服务时,运行和编译时的依赖都不一样,可以说findpackage()与catkin_packageCATKIN_DEPEND xxx)所跟的依赖是略有不同的,findpackage要的是message_generationcatkin_package要的是message_runtime

 

 

总结:

编译时,所依赖的是一般的库文件(注意我的意思是,要找的东西不属于ROScatkin包)的话,正常用 find_package(),比如:Boost

                  

find_packageBoost REQUIRED COMPONENTS thread

 

编译时如果用该工程(该功能包)需要别的功能包的话,那么寻找的时候推荐将这个catkin包作为catkin这个依赖项的一个组件。即

find_package(catkin REQUIRED COMPONENTS 功能包名字)

 

findpackage找的其实是编译时的依赖,不是运行时的依赖,它对应package.xml<build_depend>xxx</build_depend>标签。一个找编译时依赖,一个找运行时依赖。这一点在生成消息服务是尤其明显。

 

 

 

 

2.7.指定包的编译导出信息            catkin_package()

 

catkin_package

  INCLUDE_DIRS include

  LIBRARIES ${PROJECT_NAME}

  CATKIN_DEPENDS roscpp nodelet

  DEPENDS eigen opencv

 

catkin_package()这条指令有四条

1.      表示这个catkin包的头文件是导出到include这个文件夹,include位置是位于该功能包目录下(需要手动创建)就是当前编写的CMakeList.txt所在位置,与msgsrvpackage.xmlCMakeList.txt共存的位置。与工作空间下(有src、build、devel的位置)devel文件下的include不同,那个有包含由msg文件生成的消息、服务的头文件。catkin_package的这个include我估计是存放以及读取这个功能包的头文件的地方。

 

2.      是将库导出来,就是做成一个库给别的功能包用。所以LIARARIES 和后面接的是工程名,或者是代表这个工程名的变量${PROJECT_NAME} 这个变量会替换成工程名

 

3.      是简单来说要填的内容就是该功能包运行时所需要的、所依赖的catkin包。它与package.xml中标签<run_depend>xxxx</run_depend>对应。运行和编译时的依赖都不一样,可以说findpackage()catkin_packageCATKIN_DEPEND xxx)所跟的依赖是略有不同的,findpackage要的是message_generationcatkin_package要的是message_runtime

4.      这个包运行时所依赖的包如果不属于catkin的话就要写在这里。简单来说,一样也是通过package.xml的run_depend标签所决定所确定,比如下面的opencv库 Eigen库

 

总结:

         Catkin_package中的CATKIN_DEPEND后面填的是运行时的依赖,于package.xmlrun_build标签对应,于findpackage刚好相反,一个找编译时依赖,一个找运行时依赖。这一点在生成消息是尤为明显。

 

 

 

例子:

catkin_package

  INCLUDE_DIRS include

  LIBRARIES ${PROJECT_NAME}

  CATKIN_DEPENDS roscpp nodelet      

  DEPENDS eigen opencv         该文章最后结束前给出的例子可以验证这一点

 

 

7.编译指定目标

中间有部分省略。。。  

关于7.1-7.2set_target_properties()这条指令属于cmake,用法翻书看看

 

7.3include路径和库文件路径

include_directories<dir1><dir2>...<dirN>

 

link_directories<dir1><dir2>...<dirN>

 

include_directories()(添加头文件的搜索路径,扩大搜索范围)

include_directories的参数应该是find_package调用生成的*_INCLUDE_DIRS变量。如果用findpackage()寻找catkin和Boost,然后用include_directories()找它俩的头文件,可以这样表达:

 

include_directoriesinclude  ${Boost_INCLUDE_DIRS}  ${catkin_INCLUDE_DIRS}

 

注意:这里的include是文件夹 也是路径的一部分,你可以理解成它用了相对路径。

 

这段话的理解:

首先,include_directories()它是用来指定头文件的搜索路径。路径之间用空格分开,所以这里有三个路径,文件夹include的路径(第一个参数“include”它代表哪个include,目前还不清楚)变量Boost_INCLUDE_DIRS所代表的路径,和变量catkin_INCLUDE_DIRS所代表的路径。

 

寻找catkin boost findpackage(),

findpackagecatkin REQUIRED

findpackageBoost REQUIRED会生成关于两个依赖项一堆变量,Boost_INCLUDE_DIRScatkin_INCLUDE_DIRS 就是其中之二。

 

函数里的include我暂时理解有两个,一个是存放由.msg文件生成的头文件后所存放的那个include(即devel目录下的include)另一个是有catkin_package()中的INCLUDE_DIRS include所生成的,位置在功能包目录下的include(需要手动创建)

 

link_directories()              (添加库的搜索路径,扩大搜索范围)

link_directories()函数用来添加非标准共享库的路径。(扩大库的搜索路径,搜索范围)

但是不推荐这样做(简单说不用考虑这个函数)。因为catkin和CMake软件包在find_packaged时都会自动添加链接信息。只需链接到target_link_libraries()中的库

 

 

 

add_executable()  (将该功能包的cpp生成可执行文件)

 

例子:

add_executablemyProgram   src/main.cpp   src/some_file.cpp   src/another_file.cpp

该功能包将生成一个名为myProgram的可执行文件,它由3个源文件构建:src /main.cppsrc / some_file.cppsrc /another_file.cpp

 

add_library()       (将功能包做成一个库给别人用)

是用来将目标文件生成一个库文件(造一个库给别人用)。默认情况下,生成共享库(即所谓的动态库,我们平常见到的share文件夹装的就是这些动态库,后缀名是xxx.so的库文件)

注意:静态库生成的是xxx.a文件,一般放在lib文件夹。

 

例子:

add_libraryhaha src/some_file.cpp

该功能包将生成一个名为haha的动态库即 haha.so

add_libraryhaha STATICsrc/some_file.cpp在中间加入STATIC就可以构建静态库

该功能包将生成一个名为haha的静态库即 haha.a

 

 

target_link_libraries()  是为目标添加链接库(调用别的库给自己用)

注意前面link_directoies()这个函数作用是添加库文件的搜索路径,不一样,还有这通常放在add_executable()函数之后的,对于ROS包来讲,参数填$ {catkin_LIBRARIES}就足够了。

即:target_link_librariesexecu-name  ${catkin_LIBRARIES }

 

句法:

target_link_libraries(<executable_Name>,<lib1>,<lib2>,... )

 

例:

add_executablefoo src/foo.cpp foo.cpp生成一个foo.o的目标文件

add_librarymoo src/moo.cpp  moo.cpp生成一个libmoo.so的动态库

target_link_librariesfoo moo  foo代表foo.o moo代表libmoo.so moo库加入到foo这个目标文件中。

请注意,在大多数用例中不需要使用link_directories(),因为信息通过find_package()自动导入。

还有target_link_libraries()是放在add_executable()和add_library()之后,如上例子所示。其实从参数构成就可以猜到为什么,我要先执行target_link_libraries的话哪来的目标文件,动态库。

 

 

 

消息,服务和行动目标

msg.msg),srv.srv)和act.action)文件在ROS包构建和使用之前需要一个特殊的预处理器构建步骤。这些函数用于是生成特定语言格式文件。

提供了三个函数来分别处理消息,服务和动作:

 

add_message_files

           FILES

           PP.msg

 msg文件夹下的pp.msg文件处理生成pp.h的头文件

 

add_service_files()同理可得

 

add_action_files()同理可得

 

注意:这些函数后面一定要有generate_messages(),还有只是使用别人包中的消息是不用上面几条函数。(最后的例子有说明这一点)

 

 

 

generate_messages()消息生成器

 

例子:

generate_messages

         DEPENDENCIES

         std_msg

 

如果这个消息或服务的生成需要依赖某个包中的某个消息类/服务类。就需要将需要的那个类对应的功能包填上。例子中就是需要std_msg这个包的某个消息类(即某个xxx.msg文件)所以才填上这个包。

 

对于消息和服务怎样理解它们对包的依赖呢?其实就是看我在这个包上的xxx.msg文件它的内容上,它所定义的类是不是用了某一个功能包的生成的消息类。具体看消息这一章。

 

注意:测试证明如果你在你这个功能包下只是使用别的catkin包的消息类,例如geometry包下Twist.msg是完全可以把generate_messages()注释掉,它是用来生成消息用的,对于不生成和只是使用别人包中现有的消息是不需要用的。

注意,一个catkin包是可以有多个消息/服务文件。

 

 

 

关于catkin_package()函数注意几点:

以下函数一定要放在它的前面。

 find_packagecatkin  REQUIRED  COMPONENTS ...

 add_message_files...

 add_service_files...

 add_action_files...

 generate_messages...

 catkin_package...

 ...

 

catkin_package()必须对message_runtime具有CATKIN_DEPENDS依赖关系。

 

像下面这样

catkin_package

 ...

 CATKIN_DEPENDSmessage_runtime ...

 ...

就是说,在运行时,注意不是编译时,如果我要生成自己的消息/服务,那么就需要用这个message_runtime包,所以,可以猜到,跟findpackage()这个函数所填的依赖有点不一样,因为findpackage找的是编译时所需要的依赖即message_generation而不是运行时

例如这样

find_package(catkin  REQUIRED  COMPONENTS  message_generation)

 

当然如果你不要生成消息/服务就不必加入message_runtime

 

注意:测试证明如果你在你这个功能包下只是用已有catkin包的消息类,例如geometry包下Twist.msg,是完全不用加入message_runtime也就说明,它是用来生成消息用的,对于使用别人catkin包的消息是不影响。

其次package.xml文件也要包含对message_generation编译时的依赖关系以及message_runtime运行时的依赖关系

 

add_dependenciessome_target $ {catkin_EXPORTED_TARGETS}

对于该函数的一些使用,因为不怎么用,看书。

 

 

 

实例

CMakeLists.txt内容清单

工作空间:cat_ws

 

该功能包名字:rostext_alpha

 

工程名称:rostext.0.0.1

 

源文件:rostest.cpp

 

Cmake最低版本号:2.8.3

 

寻找所编译时需要的依赖项:

依赖项属于ros包:catkinroscpprospymessage_runtimestd_msgsensor_msgs (后面三个是与消息服务的依赖有关)

 

依赖项不属于ros包:BoostEigenopencv

 

msg目录:

两个消息文件——“ MyMessage1.msg ”“ MyMessage2.msg ”

这些消息依赖于std_msgssensor_msgs

 

srv目录:

一个服务文件——“ MyService.srv ”

这个服务不依赖于任何服务类型

增加头文件搜索路径

 

生成一个动态库文件和静态库文件:

libhaha.solibhello.a

 

生成可执行文件:

Kaka.exe

 

将一些库文件链接到生成的可执行文件

 

 

 

 

 

 

CMakeLists.txt的内容有以下:

 

cmake所需最低版本号

cmake_minimum_requiredVERSION 2.8.3

 

工程名称

projectrostext.0.01

 

寻找编译时的依赖catkinroscpprospymessage_generationstd_msgssensor_msgs,其中后三个作为catkin的组件,而不选择另起三个find_package()

find_packagecatkin REQUIRED COMPONENTS  roscpp rospy  message_generation  std_msgs sensor_msgs

 

寻找编译时的依赖BoostopencvEigen

find_packageBoost

find_packageEigen

find_packageopencv REQUIRED##如果你认为opencv很重要,没了它你这个包就不行,就加REQUIRED关键字

 

 

要生成消息类的xxx.msg消息文件

  add_message_files

         FILES

    MyMessage1.msg

    MyMessage2.msg

  ##将生成两个头文件,MyMessage1.hMyMessage2.h

 

 

 

 

要生成服务类的xxx.srv服务文件

  add_service_files

         FILES

    MyService.srv

  ##将生成一个头文件,MyService.h

 

 

 

如果要生成消息或者服务就必须使用该函数

生成这些消息和服务所依赖的功能包

generate_messagesDEPENDENCIES  std_msgs sensor_msgs

 

 

 

 

声明这个catkin包的运行时依赖关系

catkin_package

# LIBRARYIES hehe

CATKIN_DEPENDS  roscpp  rospy  message_runtime  std_msgs sensor_msgs

DEPENDS  Boost  opencv Eigen

 

添加头文件的搜索路径

为了能顺利找到Boostopencv还有egien

下面的变量是由findpackage()函数产生的。你也可以用绝对路径的方式寻找

下面一共添加了5个搜索路径,include文件夹 boost eigen opencv库还有catkin包(以及跟在它后面的作为组件的其他功能包)

Include_directoriesinclude  ${Boost_INCLUDE_DIRS} ${eigen_INCLUDE_DIRS}${opencv_INCLUDE_DIRS}  ${catkin_INCLUDE_DIRS}

 

生成静态库文件

hello将生成为libhello.a

add_libraryhello  /src/rostest.cpp

 

生成动态库文件

haha将生成为libhaha.so

add_libraryhaha  /src/rostest.cpp

 

生成可执行文件

add_executablekaka  src/main.cpp

 

将一些库文件链接到这个可执行文件

我将刚生成的两个库也加进去了,虽然没什么卵用,把boost opencv eigen库也加进去,你前面找了,不加进去那找来干嘛,也可以选择用绝对路径。Catkin包以及其他包肯定要有。强调这些包虽然是作为组件放到catkin包,但他们是同等的。如果独立分开用findpackage就要像opencv库那样加${roscpp_LIBRARIES}${rospy_LIBRARIES}等等

target_link_librarieskaka  ${catkin_LIBRARIES}  haha hello ${boost_LIBRARIES} ${opencv_LIBRARIES}${eigen_LIBRARIES}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值