在开发ros的server时候,参考ros教程http://wiki.ros.org/ROS/Tutorials/WritingServiceClient%28c%2B%2B%29
客户端的源码文件中,存在:
#include "beginner_tutorials/AddTwoInts.h"
刚开始觉得很奇怪,实际上include头文件不存在,为什么可以放在这里,而且可以编译通过呢?
其实官方教程中以及说明了一些端倪:
beginner_tutorials/AddTwoInts.h is the header file generated from the srv file that we created earlier.
就是说这个头文件是由srv文件生成的!而srv文件是什么?在这个例子中,AddTwoInts.srv的文件内容不过4行:
int64 a
int64 b
---
int64 sum
其实这个要从服务文件定义说起,服务文件的描述分成2个部分:请求和响应,被"—"符号分隔开。
真实原因是.msg/.srv/.action文件在使用之前都需要一个特殊的预处理编译步骤。下面说明的就是这些宏的概念,以及如何让它正常工作的步骤。
这些宏的关键是生成特定于编程语言的文件,以便可以使用所选编程语言中的消息,服务和操作。构建系统将使用所有可用的生成器(例如gencpp,genpy,genlisp等)生成绑定。
在CMakeList中,这三个宏分别提供了3个消息句柄:
add_message_files
add_service_files
add_action_files
然后,必须在调用生成的宏之后调用这些宏:
generate_messages()
注意,这里需要区别顺序!!
这些宏必须在catkin_package()宏之前,以便生成正常工作。
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
...
而catkin_package()宏必须对message_runtime具有CATKIN_DEPENDS依赖项。
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
而且必须将find_package()用于包messagesegeneration,可以单独使用,也可以作为catkin的一个组件使用:
find_package(catkin REQUIRED COMPONENTS message_generation)
而这个时候,也需要修改package.xml,因为需要添加运行时的依赖message_runtime。如果这个依赖是从其他包传递过来的就可以不用改,但大多数时候保险起见还是改一下吧。
最后一步分3种情况:
- 如果只有一个目标(甚至可以传递)依赖于需要构建msg/srv/action的其他目标,则需要在目标catkin_EXPORTED_TARGETS上添加显式依赖项,以便它们按照正确的顺序构建。除非你自己的package不包含任何ros部分,否则就必须添加该显式依赖项。当然,如果不添加的话,程序也不会自动添加。
add_dependencies(some_target ${catkin_EXPORTED_TARGETS})
- 如果你有一个构建msg和/或srv的package,以及使用它们的可执行文件,必须在自动生成的消息目标上创建一个显式的依赖,以便它们能以正确的顺序编译。
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS})
- 如果你的package满足以上2个条件,你必须同时添加两个依赖。
add_dependencies(some_target ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
综上所述,一个完整的CMakeList如下:
# 获取pkg编译依赖的信息
find_package(catkin REQUIRED
COMPONENTS message_generation std_msgs sensor_msgs)
# 声明需要编译的msg文件
add_message_files(FILES
MyMessage1.msg
MyMessage2.msg
)
# 声明需要编译的srv文件
add_service_files(FILES
MyService.srv
)
# 实际上生成特定语言的消息和服务文件
generate_messages(DEPENDENCIES std_msgs sensor_msgs)
# 声明catkin包运行时依赖
catkin_package(
CATKIN_DEPENDS message_runtime std_msgs sensor_msgs
)
# 定义可执行文件的源代码
add_executable(message_program src/main.cpp)
add_dependencies(message_program ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
# 或者
add_dependencies(message_program {PROJECT_NAME}_generate_messages_cpp)
# 定义不使用这个package中任何消息/服务的其他任意可执行文件
add_executable(does_not_use_local_messages_program src/main.cpp)
add_dependencies(does_not_use_local_messages_program ${catkin_EXPORTED_TARGETS})
# 或者
add_dependencies(message_program {PROJECT_NAME}_generate_messages_cpp)
其中,要说明的是catkin_EXPORTED_TARGETS和之间的主要区别在于${PROJECT_NAME}_EXPORTED_TARGETS前者包含所有构建依赖项的导出目标(即:消息,服务,操作生成目标dynamic_reconfig等),而后者包含仅当前包的目标。
最后,在实际开发过程中,假如遇到srv include头文件不存在无法编译的情况,找到你当前工作空间中的devel文件夹下的setup.bash:
source <you workspace path>/devel/setup.bash
然后在该终端里面运行clion就可以啦
. <clion installed path>/clion.sh
当然,因为是动态生成的,如果照搬代码肯定会提示beginner_tutorial错误,这是因为这个名称是由CMakeList里面的工程名字生成的,所以在开发过程中,需要把头文件的路径换成#include “(project_name/include_name)”,以及相关类名都要换成project_name。
如果有其他问题欢迎小伙伴们一起探讨。