ROS节点通信之服务

本文介绍了ROS(RobotOperatingSystem)中计算图结构以及服务通信方式的应用,通过实例展示如何创建和测试基于服务的计算服务,包括设计服务器节点、客户端节点和服务类型定义。
摘要由CSDN通过智能技术生成

ROS计算图结构

在这里插入图片描述
ROS节点之间通过收发消息进行通信,消息收发机制分为话题(topic)、服务(service)和动作(action)三种。如上计算图中的节点2与节点3、节点2与节点5采用话题通信,节点2与节点4采用服务通信,节点1与节点2采用动作通信。计算图中的节点、话题、服务、动作都要有唯一名称作为标识。

ROS服务通信方式

服务通信方式是双向同步的,服务客户端向服务提供端发送请求,服务提供端在收到请求后立即进行处理并返回响应信息。服务通信一般用在实时性要求比较高且使用频次低的场景下,比如获取全局静态地图。

编写程序测试服务通信方式

创建一个功能包,在此功能包中设计一些节点来测试服务通信方式。

创建功能包

假设:工作空间为catkin_ws,功能包为pkg_service_example,依赖roscppstd_msgs功能包。

cd catkin_ws/src/
catkin_create_pkg pkg_service_example roscpp std_msgs

关于创建功能包的更多信息,请参考《创建/删除ROS功能包

在这里插入图片描述

示例:基于服务通信方式,提供和调用一个简易的计算服务

目标

  1. 服务器节点提供一个简易的计算服务,实现基本的“加减乘除”功能。
  2. 客户端节点调用服务器节点的计算服务。

程序设计

  • nodeServerA.cpp实现名为“NodeServerA”的节点,提供基本的“加减乘除”运算服务"daniel_service_calculation"。
  • nodeClientA.cpp实现名为“NodeClientA”的节点,调用“NodeServerA”提供的运算服务"daniel_service_calculation"。
  • 自定义服务类型“calculator.srv”。请求成员包含运算元素elementA、运算元素elementB、计算规则algorithm。回复成员包含元素运算结果result
  1. 新建服务类型calculator.srv

    mkdir -p catkin_ws/src/pkg_service_example/srv
    touch calculator.srv
    

    calculator.srv

    int64 elementA
    int64 elementB
    int64 algorithm
    ---
    int64 result
    
  2. 将服务类型calculator.srv添加至编译环境

    • 打开pkg_service_example/CMakeLists.txt,添加下面的修改:
      在这里插入图片描述
      在这里插入图片描述

      在这里插入图片描述

    • 打开pkg_service_example/package.xml,添加下面的修改:
      在这里插入图片描述
      添加成功后,可以通过rossrv show pkg_service_example/calculator查看calculator服务类型:
      在这里插入图片描述

  3. 新建源文件 nodeServerA.cpp nodeClientA.cpp

    cd catkin_ws/src/pkg_service_example/src/
    touch nodeServerA.cpp nodeClientA.cpp
    

    在这里插入图片描述

    nodeServerA.cpp

    #include "ros/ros.h"
    #include "pkg_service_example/calculator.h"
    
    #define NODE_NAME		"NodeServerA"
    #define SERVICE_NAME	"daniel_service_calculation"
    
    enum Algorithm{
    	ALG_PLUS = 0,
    	ALG_MINUS = 1,
    	ALG_TIMES = 2,
    	ALG_DIVIDE = 3,
    };
    
    bool onRecvRequest(pkg_service_example::calculator::Request& request, 
    				   pkg_service_example::calculator::Response& response)
    {
    	switch (request.algorithm) {
    		case ALG_PLUS:
    			response.result = request.elementA + request.elementB;
    		break;
    		case ALG_MINUS:
    			response.result = request.elementA - request.elementB;
    		break;
    		case ALG_TIMES:
    			response.result = request.elementA * request.elementB;
    		break;
    		case ALG_DIVIDE:
    			response.result = request.elementA / request.elementB;
    		break;
    		default:
    			return false;
    	} 
    	
    	return true;
    }
    
    int main(int argc, char* argv[])
    {
    	ros::init(argc, argv, NODE_NAME);
    	ros::NodeHandle nodeHandle;
    	
    	ros::ServiceServer server = nodeHandle.advertiseService(SERVICE_NAME, onRecvRequest);
    	
    	ROS_INFO("%s is ready\n", SERVICE_NAME);
    	
    	ros::spin();
    	
    	return 0;
    }
    

    nodeClientA.cpp

    #include "ros/ros.h"
    #include "pkg_service_example/calculator.h"
    
    #include <iostream>
    
    #define NODE_NAME		"NodeClientA"
    #define SERVICE_NAME	"daniel_service_calculation"
    
    enum Algorithm{
    	ALG_PLUS = 0,
    	ALG_MINUS = 1,
    	ALG_TIMES = 2,
    	ALG_DIVIDE = 3,
    };
    
    int main(int argc, char* argv[])
    {
    	ros::init(argc, argv, NODE_NAME);
    	ros::NodeHandle nodeHandle;
    	
    	ros::ServiceClient client = nodeHandle.serviceClient<pkg_service_example::calculator>(SERVICE_NAME);
    	
    	while (ros::ok()) {
    		std::cout << "Please input two elements to calculate" << std::endl;
    		long elementA, elementB;
    		std::cin >> elementA >> elementB;
    		
    		std::cout << "Please select calculation algorithm:\n 0:plus\n 1:minus\n 2:times\n 3:divide" << std::endl;
    		long algorithm;
    		std::cin >> algorithm;
    		
    		pkg_service_example::calculator service;
    		service.request.elementA = elementA;
    		service.request.elementB = elementB;
    		service.request.algorithm = algorithm;
    		
    		if (client.call(service)) {
    			switch (algorithm) {
    				case ALG_PLUS:
    					ROS_INFO("%ld plus %ld equals %ld\n", elementA, elementB, (long)service.response.result);
    				break;
    				case ALG_MINUS:
    					ROS_INFO("%ld minus %ld equals %ld\n", elementA, elementB, (long)service.response.result);
    				break;
    				case ALG_TIMES:
    					ROS_INFO("%ld times %ld equals %ld\n", elementA, elementB, (long)service.response.result);
    				break;
    				case ALG_DIVIDE:
    					ROS_INFO("%ld divide %ld equals %ld\n", elementA, elementB, (long)service.response.result);
    				break;
    			}
    		}
    	}
    	return 0;
    }
    

编译

  1. pkg_topic_example/CMakeLists.txt的末尾加上下面的内容:

    #node serverA
    add_executable(NodeServerA ./src/nodeServerA.cpp)
    target_link_libraries(NodeServerA ${catkin_LIBRARIES})
    
    #node clientA
    add_executable(NodeClientA ./src/nodeClientA.cpp)
    target_link_libraries(NodeClientA ${catkin_LIBRARIES})
    

    在这里插入图片描述

  2. 编译功能包
    在工作空间根目录中使用catkin_make,编译该工作空间中的所有功能包。
    如果需要编译指定的某个功能包(譬如:target_pkg),
    使用catkin_make -DCATKIN_WHITELIST_PACKAGES=“target_pkg”。

    cd catkin_ws
    catkin_make 或者 catkin_make -DCATKIN_WHITELIST_PACKAGES="pkg_service_example"
    

    在这里插入图片描述

测试

  1. 新开终端,运行主节点(如果主节点已运行,则跳过此步骤)

    roscore
    

    使用roscore运行主节点。
    在这里插入图片描述

  2. 运行服务器节点
    执行rosrun pkg_service_example NodeServerA,运行服务器节点NodeServerA
    在这里插入图片描述

  3. 运行客户端节点
    执行rosrun pkg_service_example NodeClientA,运行服务器节点NodeClientA
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值