【ROS服务通信】服务端和客户端

本文记录下ROS服务通信的实现,首先明确,ROS中的服务通信主要适用于偶然的,有实时要求的场景。服务通信基于客户-服务的架构,在主节点下,由服务端和客户端组成,服务端负责对请求做出响应,客户端发出请求和得到响应。

1 定义服务信息,该部分和话题通信中自定义消息的过程非常类似

1.1 新建服务消息类型 srv

【ROS服务通信】服务端和客户端_第1张图片
Addints.srv代码内容:
"—"上方为请求信息,下方为响应信息

int32 num1
int32 num2
---
int32 sum
1.2 功能包配置

1 打开 packages.xml ,添加相应的依赖内容即可

  <build_depend>message_generation</build_depend>
   <exec_depend>message_runtime</exec_depend>

【ROS服务通信】服务端和客户端_第2张图片
2 打开 CMakeLists.txt,配置相关内容
【ROS服务通信】服务端和客户端_第3张图片
对应自定义的服务信息格式文件
【ROS服务通信】服务端和客户端_第4张图片
【ROS服务通信】服务端和客户端_第5张图片
【ROS服务通信】服务端和客户端_第6张图片
配置好以上信息后,就开始编译项目,然后会发现项目根目录下的 devel 文件夹下,出现了服务信息的编译内容
【ROS服务通信】服务端和客户端_第7张图片【ROS服务通信】服务端和客户端_第8张图片
然后将生成的编译文件,配置到 c_cpp_properties.json,这个主要是为了能够在C++或Python代码中,能够引到编写的内容。
【ROS服务通信】服务端和客户端_第9张图片
【ROS服务通信】服务端和客户端_第10张图片
【ROS服务通信】服务端和客户端_第11张图片

源代码工程目录

【ROS服务通信】服务端和客户端_第12张图片

C++实现

1 服务端

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/**
 * @brief 
 * 
 * @param argc 
 * @param argv 
 * @return int 
 * 服务端解析服务信息,且作出反应
 * 1 初始化ros节点
 * 2 创建句柄
 * 3 声明一个服务对象
 * 4 处理请求并作出响应
 * 5 spin 循环的话 spin_once()
 */
bool doNums(plumbing_server_client::Addints::Request &request,
plumbing_server_client::Addints::Response &response){
// 1  处理请求
int num1 = request.num1;
int num2 = request.num2;
ROS_INFO("receive the num : %d,%d",num1,num2);
//2 作出响应
int sum = num1 + num2;
response.sum = sum;
ROS_INFO("process result: %d",sum);
return true;
}
int main(int argc,char *argv[]){
    ros::init(argc,argv,"server_01");
    ros::NodeHandle nh;
    ros::ServiceServer server = nh.advertiseService("addints",doNums);
    ROS_INFO("server start...");
    ros::spin();
    return 0;
}

2 客户端代码

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
* 服务端解析服务信息,且作出反应
 * 1 初始化ros节点
 * 2 创建句柄
 * 3 声明一个客户端对象
 * 4 处理请求并作出响应
 *优化:动态增加两个参数
 rosrun 包名 节点名 para1 para2

如果客户端先启动,然后服务端再启动,会抛出异常
则如果客户端启动,可先挂起,等服务端启动后再执行
*/
int main(int argc,char *argv[]){
    setlocale(LC_ALL,"");
    //判断是不是3个参数
    if(argc !=3){
        ROS_INFO("paramer num is wrong!");
        return 1;
    }
    ros::init(argc,argv,"client_01");
    ros::NodeHandle nh;
    ros::ServiceClient client =  nh.serviceClient<plumbing_server_client::Addints>("addints");
    //提交请求获取响应
    plumbing_server_client::Addints ai;
    //atoi:char to int 
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);
    //wait  the service start...
    client.waitForExistence();
    //这两个都可
    //ros::service::waitForService("addints");
    //处理响应
    bool flag = client.call(ai);
    if(flag){
        ROS_INFO("process success... %d",ai.response.sum);
    }else{
        ROS_INFO("process false!");
    }
    return 0;
}

3 配置 CMakeLists.txt
在这里插入图片描述
增加节点编译依赖

add_dependencies(server_01_node ${PROJECT_NAME}_gencpp)
add_dependencies(client_01_node ${PROJECT_NAME}_gencpp)

【ROS服务通信】服务端和客户端_第13张图片
【ROS服务通信】服务端和客户端_第14张图片
4 配置好后,开始运行

roscore#启动主节点

启动服务端

source ./devel/setup.bash
rosrun plumbing_server_client server_01_node

启动客户端

source ./devel/setup.bash
#后面两个数值为传入的num1 和 num2
rosrun plumbing_server_client client_01_node 10 20

Python实现

1 服务端代码

#! /usr/bin/env python
# -*- coding:utf-8 -*-
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
#from plumbing_server_client.srv import *

# 0 导包
# 1 初始化 ros节点
# 2 新建服务端对象
# 3 处理逻辑(回调函数)
# 4 spin()

#封装request对象
def doNum(request):
    num1 = request.num1
    num2 = request.num2

    sum = num1 + num2

    response = AddintsResponse()
    response.sum = sum

    rospy.loginfo("num1 = %d , num2 = %d, response cal : %d", num1, num2, sum)
    return response
if __name__=="__main__":
    rospy.init_node("server_py")
    server = rospy.Service("addints_py",Addints,doNum)
    rospy.loginfo("server have started...")
    rospy.spin()

2 客户端代码

#! /usr/bin/env python
# -*- coding:utf-8 -*-
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
import sys
#对于偶然 实时性要求的场景
#客户端:组织并提交请求,处理服务端响应
# 1 初始化ros节点
# 2 创建客户端对象
# 3 组织请求数据 并发送请求
# 4 处理响应

if __name__=="__main__":
    #sys.argv[0]为文件名称
    if len(sys.argv) != 3:
        rospy.logerr("the paramers number is wrong!")
        sys.exit(1)
    rospy.init_node("client_py")
    client = rospy.ServiceProxy("addints_py",Addints)
    #解析传入的参数
    num1=int(sys.argv[1])
    num2=int(sys.argv[2])
    #client.wait_for_service()
    rospy.wait_for_service("addints_py")
    response = client.call(num1,num2)
    rospy.loginfo("response data:%d",response.sum)

CMakeLists.txt配置
【ROS服务通信】服务端和客户端_第15张图片
准备运行:

需要切换到python的脚本文件夹中,更改Python脚本的权限
chmod +x *.py
roscore#启动主节点

启动服务端

source ./devel/setup.bash
rosrun plumbing_server_client server.py

启动客户端

source ./devel/setup.bash
rosrun plumbing_server_client client.py 10 20 

你可能感兴趣的:(C++,ROS,Python,自动驾驶,人工智能)