2 ROS1通讯编程基础
- 2.3 配置文件的解读
- 2.3.1 CMakeList.txt解读
- 2.3.1.1 find_package的配置
- 2.3.1.2 messages, services and actions的配置
- 2.3.1.3 动态重配参数
- 2.3.1.4 catkin_package的配置
- 2.3.1.5 include_package配置
- 2.3.1.6 C++编译配置
- 2.3.1.7 Python配置
- 2.3.1.8 参考文献
- 2.3.2 package.xml文件解读
- 2.4 服务通讯编程
- 2.4.1 服务通讯理论模型
- 2.4.2 服务通讯的基本编程
- 2.4.2.1 编写srv服务文件
- 2.4.2.2 配置自定义服务文件
- 2.4.2.3 编译自定义服务文件并查看中间文件
- 2.4.2.4 编译C++代码调用自定义服务
- 2.4.2.5 编译Python代码调用自定义服务
- 2.4.3 服务通讯编程总结
- 2.4.3.1 服务通讯流程
- 2.4.3.2 服务编程代码逻辑
- 2.4.3.3 参考文献
- 其他ROS1学习笔记: ROS1学习笔记
- 代码仓库:Github连接地址
- 欢迎各位互相学习交流
2.3 配置文件的解读
2.3.1 CMakeList.txt解读
CMakeList.txt
作为ROS编程里,最需要配置的内容,对其默认文档进行解读,主要包括以下几个部分:其中重点在于find_package
、一些自定义内容的配置、catkin_package
和C++的配置,而且根据之前配置C++时候发现,配置的顺序对编译是有影响的,一般按照下图从左到右的顺序进行配置声明。
2.3.1.1 find_package的配置
- 新建功能包就包括的依赖项;可以说是功能包编译时候需要的依赖项;
- 例如
find_package(catkin REQUIRED COMPONENTS roscpp ...
其他依赖项名)等
2.3.1.2 messages, services and actions的配置
- 在msg文件夹下创建自定义消息,然后配置
add_message_files(FILES 自定义消息.msg ...其他自定义消息.msg)
; - 在srv文件夹下创建自定义服务,然后配置
add_service_files(FILES 自定义.srv ...其他自定义服务)
; - 在action文件夹下创建动作内容,然后配置
add_action_files(FILES 自定义.action ...其他自定义服务)
; - 构建自定义messages和services需要的依赖项,
generate_messages(DEPENDENCIES std_msgs ...其他依赖项名字)
2.3.1.3 动态重配参数
官方声明为Declare ROS dynamic reconfigure parameters:其中内容包括generate_dynamic_reconfigure_options(cfg/DynReconf1.cfg ...其他cfg)
2.3.1.4 catkin_package的配置
- 官方声明为catkin specific configuration;即可以理解为find_package在执行时候需要的依赖项文件;
- 其中内容按照关键字包括
①INCLUDE_DIRS include 其他文件夹位置
;②LIBRARIES 功能包名
;③ CATKIN_DEPENDS 执行依赖项例如message_runtime
;④DEPENDS system_lib 其他系统库文件夹
。
2.3.1.5 include_package配置
- 官方声明为指定头文件的附加位置,功能包的位置应该在其他位置之前就应该列出;
- 例如
include_directories(${catkin_INCLUDE_DIRS})
其实也等价于当前功能包下的include文件夹即include_directories(include)
。
2.3.1.6 C++编译配置
- 声明一个 C++ 库:
add_library(${PROJECT_NAME} src/${PROJECT_NAME}/功能包名.cpp)
; - 添加库的cmake目标依赖,主要正对于自定义消息或者是ROS动态重配置参数,解决本功能包编译时候,如果需要其他依赖的功能包但是没有编译的一个顺序依赖问题:
add_dependencies(${PROJECT_NAME}
${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
; - 声明一个 C++ 可执行文件:
add_executable(${PROJECT_NAME}_node src/目标代码文件.cpp)
; - 指定库以链接库或可执行目标:
target_link_libraries(${PROJECT_NAME}_node ${catkin_LIBRARIES})
。
2.3.1.7 Python配置
例如:catkin_install_python(PROGRAMS scripts/目标代码.py DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
2.3.1.8 参考文献
ROS-WIKI:catkinCMakeLists.txt
2.3.2 package.xml文件解读
解读package.xml主要对其构建、运行和测试依赖项(Build, Run, and Test Dependencies)进行分析,这些依赖项即最小的、不依赖其他功能包的包,例如std_msgs、roscpp和rospy这样的,这些包可以有以下几种类型:
-
- Build Dependencies(<build_depend>) :指定构建这个包需要哪些包,其包括这些包的头文件,链接来自这些包的库或在构建时需要其他资源(尤其是当这些包在CMake中使用find_package()时)。
-
- Build Export Dependencies(<build_export_depend>) :指定需要哪些包来对这个功能包构建库。例如,当将它们的头文件传递包含在公共头文件中时,尤其是当这些包在CMake的catkin_package()中声明为 (CATKIN_)DEPENDS时。
-
- Execution Dependencies(<exec_depend>) :指定运行此包中的代码需要哪些包。例如,赖此包中的共享库时,尤其当这些包在CMake中的catkin_package()中声明为
(CATKIN_)DEPENDS
时。
-
- Test Dependencies(<test_depend>) :仅指定单元测试的附加依赖项。这一项不应该使用任何已经作为构建或运行依赖项的依赖项。
-
- Build Tool Dependencies(<buildtool_depend>) :指定此包需要自行构建的构建系统工具。在ROS中,一般而言catkin是唯一的构建工具。
-
- Documentation Tool Dependencies(<doc_depend>) :指定此包生成文档所需的文档工具。
-
- Run Dependencies(<run_depend>) :指定运行此包中的代码需要哪些包,或针对此包构建库。类似于Execution Dependencies(<exec_depend>),当依赖共享库(shared libraries)或将它们的头文件传递地包含在此包的公共头文件中时,尤其是当这些包在CMake中的catkin_package()中声明为 (CATKIN_)DEPEND。
参考文献:ROSWIKI-package.xml
2.4 服务通讯编程
2.4.1 服务通讯理论模型
服务通讯的理论模型如图所示,主要由以下五步骤组成。
-
①Server注册:Server 启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含提供的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
-
②Client注册:Client 启动后,也会通过RPC在ROS Master 中注册自身信息,包含需要请求的服务的名称。ROS Master 会将节点的注册信息加入到注册表中。
-
③ROS Master实现信息匹配:ROS Master会根据注册表中的信息匹配Server和Client,并通过 RPC向Client发送 Server的TCP地址。
-
④Client发送请求:Client 根据步骤2 响应的信息,使用 TCP 与 Server 建立网络连接,并发送请求数据。
-
⑤Server发送响应:Server 接收、解析请求的数据,并产生响应结果返回给 Client。
注意:与话题通讯的具有很大的区别,Server必须在Client前面启动!
2.4.2 服务通讯的基本编程
服务通讯编程,其实是为自定义服务消息进行编程使用,其步骤和自定义消息类型的话题编程类似。
2.4.2.1 编写srv服务文件
-
- 新建功能包;在工作空间下新建功能包service_communication,依赖项包括std_msgs roscpp rospy等基础项目。
-
- 创建文件夹目录,在service_communication功能包下面,创建文件夹srv和文件夹scripts分别用于存放服务文件和Python文件。
ubuntu@ubuntu:~/catkin_ws/src/service_communication$ tree
.
├── CMakeLists.txt
├── include
│ └── service_communication
├── package.xml
├── scripts
├── src
└── srv
5 directories, 2 files
-
- ** 编写服务文件**。在srv文件夹下新建自定义服务文件
AddTwoInt.srv
用于实现服务程序两数求和,前俩行的a和b表示服务端的请求数据,---
号表示分割,c表示服务端的应答数据,此时的a,b和c只是指代请求端(request)和响应端的数据类型(respond),没有任何操作功能。
catkin_ws/src/service_communication/srv/AddTwoInt.srv
int32 a
int32 b
---
int32 c
2.4.2.2 配置自定义服务文件
同理,配置srv文件包括在package.xml
和CMakeList.txt
配置两部分。
-
- 在功能包的
package.xml
里添加话题的依赖,这一步操作和自定义消息配置是一样的。即在service_communication
文件夹的package.xml
里添加:
<exec_depend>message_runtime</exec_depend>
<build_depend>message_generation</build_depend>
-
- 在功能包的CmakeLists.txt添加编译选项,此时和自定义消息也是一样的,包括四部分。
-
- 5.1. 添加编译依赖项功能包:
find_package(catkin REQUIRED COMPONENTS ..+ 依赖项功能包)
,大约在第10行,此时依赖项功能包名字为message_generation(build_depend的内容)
,而且必须有std_msgs
作为基础在里面,配置的内容如下所示:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
-
- 5.2 . 添加自定义srv文件:
add_service_files(FILES 文件名)
,大约在第58行,如此时的文件名为AddTwoInt.srv:
add_service_files(
FILES
AddTwoInt.srv
)
-
- 5.3. 添加srv的编译依赖项:
generate_messages(DEPENDENCIES std_msgs)
,大约在第71行,即表示在编译msg时候得依赖于std_msgs:
generate_messages(
DEPENDENCIES
std_msgs
)
-
- 5.4. 添加执行时的依赖:catkin_package的关键字
CATKIN_DEPENDS后+包
,大约在106行,在官网上,并没有要求执行这一步,但是为了规范加上这一步,即:
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)
2.4.2.3 编译自定义服务文件并查看中间文件
-
- 编译功能包,在工作空间catkin_ws下执行catkin_make编译功能包,编译成功后,查看中间文件。同样的C++的中间文件在
/home/ubuntu/catkin_ws/devel/include/功能包名/文件夹下
,Python的中间文件在/home/ubuntu/catkin_ws/devel/lib/python2.7/dist-packages/功能包名/srv文件夹
下
2.4.2.4 编译C++代码调用自定义服务
-
- 创建C++服务端程序,并在CMakeList.txt配置C++文件。
-
- 7.1. 创建客户端程序。创建客户端程序
clientc_cpp.cpp
。此时额外注意如果客户端比服务器提前启动的解决办法!!client.waitForExistence()
!
catkin_ws/src/service_communication/src/client_cpp.cpp
#include <cstdlib>
#include "ros/ros.h"
#include "service_communication/AddTwoInt.h"
int main(int argc, char **argv)
{
setlocale(LC_ALL,"");
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3)
{
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<service_communication::AddTwoInt>("add_two_ints");
service_communication::AddTwoInt srv;
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
client.waitForExistence();
if (client.call(srv))
{
ROS_INFO("c: %ld", (long int)srv.response.c);
}
else
{
ROS_ERROR("客户端没有启动");
return 1;
}
return 0;
}
-
- 7.2. 在功能包的src目录下,创建服务程序server_cpp.cpp。
catkin_ws/src/service_communication/src/server_cpp.cpp
#include "ros/ros.h"
#include "service_communication/AddTwoInt.h"
bool add(service_communication::AddTwoInt::Request &req,
service_communication::AddTwoInt::Response &res)
{
res.c = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.c);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
-
- 7.3. 配置功能包下的CMakeLists.txt文件,和自定义消息话题通讯一样,也包括
add_executable
、add_dependencies
和target_link_libraries
三项。注意与自定义消息话题通讯区别在于add_dependencies的参数。
catkin_ws/src/service_communication/CMakeLists.txt
add_executable(server src/server_cpp.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_executable(client src/client_cpp.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp
-
-
- 7.5. 运行服务编程。首先打开一个终端输入roscore,打开ROS,再新建一个终端输入先
rosrun service_communication server
运行服务程序。最后新建终端输入rosrun service_communication client 1 2
即运行客户端并执行1和2的加法操作。
2.4.2.5 编译Python代码调用自定义服务
-
- 在scripts文件夹编写Python代码,同时赋予可执行权限。
-
- 8.1. 编写Python的客户端代码client_py.py代码,同样注意这个服务器是否没有启动的优化。
catkin_ws/src/service_communication/scripts/client_py.py
import rospy
from service_communication.srv import *
import sys
if __name__ == "__main__":
if len(sys.argv) != 3:
rospy.logerr("请正确提交参数")
sys.exit(1)
rospy.init_node("AddTwoInt_Client_p")
client = rospy.ServiceProxy("AddTwoInt",AddTwoInt)
client.wait_for_service()
req = AddTwoIntRequest()
req.a = int(sys.argv[1])
req.b = int(sys.argv[2])
resp = client.call(req)
rospy.loginfo("响应结果:%d",resp.c)
-
- 8.2. 编写Python的服务器server_py.py代码:
catkin_ws/src/service_communication/scripts/server_py.py
import rospy
from service_communication.srv import *
def doReq(req):
c = req.a + req.b
rospy.loginfo("提交的数据:a = %d, b = %d, c = %d",req.a, req.b, c)
resp = AddTwoIntResponse(c)
return resp
if __name__ == "__main__":
rospy.init_node("AddTwoInt_server_p")
server = rospy.Service("AddTwoInt",AddTwoInt,doReq)
rospy.spin()
-
- 8.3. 给Python文件赋予可执行权限,在script文件夹下输入命令
sudo chmod +x *.py
即可赋予所有Python可执行权限。
-
- 8.4. 先roscore打开ROS,再输
入rosrun service_communication server_py.py
即可运行py文件,同理运行client_py.py文件,其中未按照要求输入会提示No handlers could be found for logger "rosout"
2.4.3 服务通讯编程总结
服务通讯的关键点主要在于理解其作用:请求响应模式实现不同节点之间数据交互,用于偶然的、对时时性有要求、有一定逻辑处理需求的数据传输场景。
同时理解服务通讯理论模型的五个步骤,即服务端的启动是需要在客户端之前的,以及在编写代码时候对启动先后的处理。
2.4.3.1 服务通讯流程
服务通讯的操作流程和自定义消息话题通讯是类似的,主要包括:
- 在srv文件夹编写自定义srv文件,配置package.xml文件包括
①exec_depend>message_runtime</exec_depend>
;②<build_depend>message_generation</build_depend>
; - 配置CMakeList.txt文件包括①添加编译依赖项功能包:find_package;②添加自定义msg文件:add_message_files;③添加msg的编译依赖项:generate_messages;④添加执行时的依赖:catkin_package;
- C++代码:①添加头文件(头文件为功能包名字/自定义服务名.h);②正常编写代码;③CMakeList.txt进行配置(不仅有add_executable和target_link_libraries还包括add_dependencies);④编译运行;
- Python代码:①添加包,
from 功能包名.srv import *
;②正常代码编写;②CMakeList.txt进行配置(目前配置不配置影响不大);③赋予py文件可执行权限;④编译运行。
2.4.3.2 服务编程代码逻辑
2.4.3.3 参考文献
B站视频:【奥特学园】ROS机器人入门课程《ROS理论与实践》零基础教程
ROS-WIKI:编写简单的服务和客户端(C++)
ROS-WIKI:编写简单的服务和客户端(Python)
ROS-WIKI:检验简单的服务和客户端
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)