ROS学习笔记(11)——服务通信自定义srv

ROS学习笔记(11)——服务通信自定义srv在运行指令 rosserviceca addints num1 0num2 0 时 num1 和 num2 后的数字可以更改 更改后 sum 输出的结果也会随之改变 其中 huati addints 为在 C 文件中自定义的话题名称

一、创建srv文件

(1)在ROS工作空间下新建一个功能包plumbing_server_client,创建srv文件夹用以存放srv文件,新建一个srv文件命名为Addints.srv:

(2)编写srv文件:

(3)修改配置文件。首先打开package.xml文件,添加依赖:

再打开CMakeLists.txt文件,修改配置:

去掉注释,将文件名改为自定义srv文件的文件名:

去掉注释:

去掉注释,添加工作空间依赖:

编译工作空间:

二、使用C++实现服务通信

(1)服务端实现

在src文件夹下新建一个.cpp文件,命名为demo01_server.cpp,编写程序:

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
    服务端实现:解析客户端提交的数据,并运算再产生响应
        1.包含头文件;
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建一个服务对象;
        5.处理请求并产生响应
        6.spin()
*/

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("收到的请求数据:num1 = %d, num2 = %d",num1,num2);
    //2.组织响应
    int sum = num1 + num2;
    response.sum = sum;
    ROS_INFO("求和结果为:sum = %d",sum);
    return true;
}
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点;
    ros::init(argc,argv,"Server_01");   //节点名称要保证是唯一的
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建一个服务对象;
    ros::ServiceServer server = nh.advertiseService("huati_addints",doNums);    //括号第一项为话题名称,第二项为回调函数
    ROS_INFO("服务器端已启动!!!");
    // 5.处理请求并产生响应
    // 6.spin()
    ros::spin();
    return 0;
}

打开CMakeLists.txt文件,修改文件名称:

编译:

打开终端,运行roscore,配置环境后运行程序:

在运行指令rosservice call huati_addints "num1: 0 num2: 0"时,num1 和 num2 后的数字可以更改,更改后sum输出的结果也会随之改变,其中huati_addints为在C++文件中自定义的话题名称。

(2)客户端实现

在src文件夹下新建一个.cpp文件,命名为demo02_client.cpp,编写客户端文件代码:

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
    客户端:提交两个整数,并处理响应的结果
        1.包含头文件;
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建一个客户端对象;
        5.提交请求并处理响应;
*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化ROS节点;
    ros::init(argc,argv,"Client_01");   //节点名称要保证是唯一的
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建一个客户端对象;
    ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
    //5.提交请求并处理响应;
    plumbing_server_client::Addints ai;
    //5.1组织请求
    ai.request.num1 = 100;
    ai.request.num2 = 200;
    //5.2处理响应
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功!!!");
        //获取响应结果
        ROS_INFO("响应结果为:%d",ai.response.sum);
    } else {
        ROS_INFO("处理失败......");
    }
    return 0;
}

打开CMakeLists.txt文件,配置依赖:

编译:

打开终端,运行程序:

其中,num1和num2的数值在程序中已经给定,这样太过不方便,现在修改程序,使得能够在终端动态地修改num1和num2的数值:

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
    客户端:提交两个整数,并处理响应的结果
        1.包含头文件;
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建一个客户端对象;
        5.提交请求并处理响应;

    实现参数的动态提交:
        1.终端命令行格式: rosrun XXXX XXXX num1 num2
        2.节点执行时,需要获取命令中的参数,并组织进 request
*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //优化实现,获取命令中的参数
    if(argc != 3)
    {
        ROS_INFO("提交的参数个数不对!!!");
        return 1;
    }
    // 2.初始化ROS节点;
    ros::init(argc,argv,"Client_01");   //节点名称要保证是唯一的
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建一个客户端对象;
    ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
    //5.提交请求并处理响应;
    plumbing_server_client::Addints ai;
    //5.1组织请求
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);
    //5.2处理响应
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功!!!");
        //获取响应结果
        ROS_INFO("响应结果为:%d",ai.response.sum);
    } else {
        ROS_INFO("处理失败......");
    }
    return 0;
}

打开终端,运行程序:

但程序还存在问题,如果先启动客户端,那么会出现请求异常,现在我们要做到如果先启动客户端,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求。解决方案是在ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动,修改程序:

#include "ros/ros.h"
#include "plumbing_server_client/Addints.h"
/*
    客户端:提交两个整数,并处理响应的结果
        1.包含头文件;
        2.初始化ROS节点;
        3.创建节点句柄;
        4.创建一个客户端对象;
        5.提交请求并处理响应;

    实现参数的动态提交:
        1.终端命令行格式: rosrun XXXX XXXX num1 num2
        2.节点执行时,需要获取命令中的参数,并组织进 request

    问题:
        如果先启动客户端,那么会出现请求异常。
    需求:
        如果先启动客户端,不要直接抛出异常,而是挂起,等服务器启动后,再正常请求。
    解决:
        在ROS中内置了相关函数,这些函数可以让客户端启动后挂起,等待服务器启动。
*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //优化实现,获取命令中的参数
    if(argc != 3)
    {
        ROS_INFO("提交的参数个数不对!!!");
        return 1;
    }
    // 2.初始化ROS节点;
    ros::init(argc,argv,"Client_01");   //节点名称要保证是唯一的
    // 3.创建节点句柄;
    ros::NodeHandle nh;
    // 4.创建一个客户端对象;
    ros::ServiceClient client = nh.serviceClient<plumbing_server_client::Addints>("huati_addints"); //话题名称要与服务器端保持一致
    //5.提交请求并处理响应;
    plumbing_server_client::Addints ai;
    //5.1组织请求
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);
    //5.2处理响应
        //调用判断服务器状态的函数
        //函数1:
        client.waitForExistence();
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功!!!");
        //获取响应结果
        ROS_INFO("响应结果为:%d",ai.response.sum);
    } else {
        ROS_INFO("处理失败......");
    }
    return 0;
}

编译之后运行程序:

运行客户端后,程序挂起等待服务端,此时再运行服务端,程序正常执行:

三、使用Python实现服务通信

(1)创建服务端

在功能包目录下新建一个scirpts文件夹用来存放Python文件,在scripts文件夹下新建一个Python文件命名为demo01_server_p.py,编写服务端文件代码:

#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
    服务端:解析客户端请求,产生响应。
        1.导包;
        2.初始化ROS节点;
        3.创建服务端对象;
        4.处理逻辑(回调函数);
        5.spin()  
"""
#参数:封装了请求数据
#返回值:响应数据
def doNum(request):
    #1.解析提交的两个整数
    num1 = request.num1
    num2 = request.num2
    #2.求和
    sum = num1 + num2
    #3.将结果封装进响应
    response = AddintsResponse()
    response.sum = sum
    rospy.loginfo("服务器解析结果num1 = %d, num2 = %d, 响应的结果: sum = %d",num1,num2,sum)
    return response

if __name__=="__main__":
    # 2.初始化ROS节点;
    rospy.init_node("Server_02")
    # 3.创建服务端对象;
    server = rospy.Service("huati02_addints",Addints,doNum)
    rospy.loginfo("服务器已经启动!!!")
    # 4.处理逻辑(回调函数);
    # 5.spin() 
    rospy.spin()

在scripts文件夹下打开终端,赋予Python文件可执行权限:

打开CMakeList.txt文件,配置文件依赖:

保存后进行编译:

打开终端,运行roscore,运行程序,正常执行:

(2)创建客户端

在scripts文件夹下新建一个.py文件命名为demo02_client_p.py,编写客户端文件代码:

#! /usr/bin/env python
import rospy
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
    客户端:组织并提交请求,处理服务端响应。
        1.导包;
        2.初始化ROS节点;
        3.创建客户端对象;
        4.组织请求数据,并发送请求;
        5.处理响应;
"""
if __name__=="__main__":
    
    # 2.初始化ROS节点;
    rospy.init_node("Client_02")
    # 3.创建客户端对象;
    client = rospy.ServiceProxy("huati02_addints",Addints)
    # 4.组织请求数据,并发送请求;
    response = client.call(12,14)
    # 5.处理响应;
    rospy.loginfo("响应的数据:%d",response.sum)

添加可执行权限:

打开CMakeList.txt文件,配置文件依赖:

编译:

运行程序:

如上图所示,num1和num2是写在程序里面的,很不方便,现在优化实现动态传入参数,修改程序:

#! /usr/bin/env python
import rospy
import sys
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
    客户端:组织并提交请求,处理服务端响应。
        1.导包;
        2.初始化ROS节点;
        3.创建客户端对象;
        4.组织请求数据,并发送请求;
        5.处理响应;

    优化实现:
        可以在实现节点时,动态传入参数。
"""
if __name__=="__main__":
    #判断参数长度
    if len(sys.argv) != 3:
        rospy.logerr("传入的参数不对!!!")
        sys.exit(1)
    # 2.初始化ROS节点;
    rospy.init_node("Client_02")
    # 3.创建客户端对象;
    client = rospy.ServiceProxy("huati02_addints",Addints)
    # 4.组织请求数据,并发送请求;
    #解析传入的参数
    num1 = int(sys.argv[1])
    num2 = int(sys.argv[2])
    response = client.call(num1,num2)
    # 5.处理响应;
    rospy.loginfo("响应的数据:%d",response.sum)

编译:

打开终端,运行程序:

程序运行成功。

但是程序依然存在一个问题,客户端先于服务端启动,会抛出异常。

现在需要户端先于服务端启动时,不要抛出异常而是挂起,等待服务端启动,再执行程序。解决方法是:ROS中内置了相关的函数,这些函数可以判断服务器的状态,如果服务端没有启动,那么就让客户端挂起。修改程序:

#! /usr/bin/env python
import rospy
import sys
from plumbing_server_client.srv import Addints,AddintsRequest,AddintsResponse
"""
    客户端:组织并提交请求,处理服务端响应。
        1.导包;
        2.初始化ROS节点;
        3.创建客户端对象;
        4.组织请求数据,并发送请求;
        5.处理响应;

    优化实现:
        可以在实现节点时,动态传入参数。

    问题:
        客户端先于服务端启动,会抛出异常。
    需要:
        客户端先于服务端启动时,不要抛出异常而是挂起,等待服务端启动,再执行程序。
    实现:
        ROS中内置了相关的函数,这些函数可以判断服务器的状态,如果服务端没有启动,那么就让客户端挂起。
"""
if __name__=="__main__":
    #判断参数长度
    if len(sys.argv) != 3:
        rospy.logerr("传入的参数不对!!!")
        sys.exit(1)
    # 2.初始化ROS节点;
    rospy.init_node("Client_02")
    # 3.创建客户端对象;
    client = rospy.ServiceProxy("huati02_addints",Addints)
    # 4.组织请求数据,并发送请求;
    #解析传入的参数
    num1 = int(sys.argv[1])
    num2 = int(sys.argv[2])
    #等待服务启动
    client.wait_for_service()
    response = client.call(num1,num2)
    # 5.处理响应;
    rospy.loginfo("响应的数据:%d",response.sum)

当启动客户端时,程序挂起,不再抛出异常:

运行服务端,程序正常运行:

编程小号
上一篇 2025-01-09 08:17
下一篇 2025-01-09 08:06

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/106405.html