04 ROS Client-Service-Server实例

本文涉及的产品
资源编排,不限时长
简介: 本文通过实例讲解了ROS(机器人操作系统)中服务(Service)机制的工作原理,包括客户端请求服务的步骤、服务器提供服务的步骤,以及如何编写、编译和测试服务的客户端和服务器代码。

零、Service机制

  1. Client与Server向ROS MASTER注册
  2. MASTER根据Client订阅的信息,查阅注册表中是否有提供对应Service的Server
  3. Client收到Server的TCP通信地址
  4. Client用TCP通信尝试与Server连接,并发送Service的请求数据
  5. Server接收Service请求与参数,开始执行Service
  6. Server执行Service完毕后,向Client发送应答数据

一、客户端 Client

  1. 初始化ROS节点
  2. 创建句柄
  3. 检测所需的服务是否存在
  4. 创建Client实例
  5. 封装要请求的数据内容
  6. 发布数据请求服务
  7. 等待Service反馈结果

1.创建源码

/*
ask the service /spawn
data type of the service is turtlesim::Spawn
*/

#include<ros/ros.h>
#include<turtlesim/Spawn.h>

int main(int argc,char **argv)
{
   
    //ROS INIT
    ros::init(argc,argv,"turtle_spawn");

    //create nodeHandle
    ros::NodeHandle node;

    //when /spawn service is detected
    //create a client and connect to /spawn service
    ros::service::waitForService("/spawn");
    ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");

    //initialize the ask data for turtlesim::Spawn
    turtlesim::Spawn srv;
    srv.request.x = 2.0;
    srv.request.y = 2.0;
    srv.request.name = "turtle2";

    //ask for the service
    ROS_INFO("Call service to spawn turtle[x:0.3%f y:0.3%f name:%s]",
    srv.request.x,srv.request.y,srv.request.name.c_str());
    add_turtle.call(srv);

    //show the result
    ROS_INFO("Spawn turtle successfully![name:%s]",srv.response.name.c_str());

    return 0;
}

2.修改CMakeLists.txt

为成功编译
在build部分下添加

add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${
   catkin_LIBRARIES})

二、服务器 Server

1.编写源码

/*
This node will execuate /turtle_command
data type of the service is std_srvs/Trigger
*/

#include<ros/ros.h>
#include<geometry_msgs/Twist.h>
#include<std_srvs/Trigger.h>

//a global publisher
ros::Publisher turtle_vel_pub;

//figure out the sport statement of turtle 
//false is stop, and true is moving
bool pubCommand = false;

//Function:
//CallBack Function for service, the input parameter is req, and the output parameter is res
//when the request is called,system will execute this function
//回调函数,输入参数为std_srvs::trigger::Request类型的req,输出参数为std_srvs::Trigger::Response类型的res
bool CommandCallBack(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
{
   
    //not
    pubCommand = !pubCommand;

    //print the ask data
    ROS_INFO("Publish turtle velocity command [%s]",pubCommand==true?"YES":"NO");

    //set feedback data to the Client
    //执行Service成功,返回res给Client
    res.success = true;
    res.message = "Turtle command state Changed";
}

int main(int argc,char **argv)
{
   
    //ROS INIT
    ros::init(argc,argv,"turtle_command_server");

    //Create NodeHandle
    ros::NodeHandle n;

    //Create server /turtle_command
    //register the CallBack Function
    //注册Server名为command_service,接收名为/turtle_command的Service请求,触发回调函数CommandCallBack
    ros::ServiceServer command_service = n.advertiseService("/turtle_command",CommandCallBack);

    //Create a Publisher and pub the topic /turtle1_cmd_vel
    //message type is geometry_msgs::Twist
    //length of line is 10
    turtle_vel_pub=n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);

    //waiting for the ask
    ROS_INFO("Ready to receive turtle command.");

    //set the rate of rotation
    ros::Rate loop_rate(10);

    while(ros::ok())
    {
   
        //check the CallBackFunction line
        ros::spinOnce();//循环等待回调函数

        //pub the velocity command if the flag is ture
        //the main process was set in the main function
        //so the CallBackFunction will be easy
        if(pubCommand)
        {
   
            geometry_msgs::Twist vel_msg;
            vel_msg.linear.x=0.5;
            vel_msg.angular.z=0.2;
            turtle_vel_pub.publish(vel_msg);
        }

    }
}

2.修改CMakeLists.txt

为成功编译
在build部分下添加

add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server ${
   catkin_LIBRARIES})

3.测试

roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_command_server

执行结果:服务器准备接受service请求

[ INFO] [1647765013.874899023]: Ready to receive turtle command.

通过rosservice的call指令向服务器发起请求,内容为空(该service数据格式允许request部分为空)

rosservice call /turtle_command "{}"

该命令的反馈结果:

success: True
message: “Change turtle command state”

在服务器端结果:

[ INFO] [1647765059.771672656]: Publish turtle velocity command [YES]

海龟状态:开始画圆圈

再次执行

rosservice call /turtle_command "{}"

该命令的反馈结果:

success: True
message: “Change turtle command state”

在服务器端结果:

[ INFO] [1647765367.266284668]: Publish turtle velocity command [NO]

海龟状态:停止画圆圈

三、Service数据的定义与使用

在目录下建立srv文件夹用于存储.srv文件

1.修改Person.srv内容

string name
uint8 age
uint8 sex

uint8 unknown = 0
uint8 male= 1
uint8 female= 2
---
string result

三条横线用于划分request部分和response部分
在编译生成头文件时,两个部分将分开保存为三个头文件
Person.h PersonRequest.h PersonResponse.h

2.修改package.xml

添加消息生成、运行的功能包依赖

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

3.修改CMakeLists.txt

在find_package中添加message_generation包依赖

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  turtlesim
  message_generation
)

#

Declare ROS messages, services and actions

#

部分
添加

add_service_files(FILES Person.srv)
generate_messages(DEPENDENCIES std_msgs)

无须添加Person.srv的路径,编译时会自动查找功能包下的srv文件
添加生成信息的依赖包std_msgs

4.编写Client源码

/*
This node will request service /show_person
ServiceDataType is learning_service::Person
*/

#include<ros/ros.h>
#include "learning_service/Person.h"

int main(int argc,char **argv)
{
   
    //ROSINIT
    ros::init(argc,argv,"person_client");

    //Create NodeHandle
    ros::NodeHandle n;

    //Searching for service /show_person 
    //Create a Client and connect it with service /show_person
    ros::service::waitForService("/show_person");
    ros::ServiceClient person_client = n.serviceClient<learning_service::Person>("/show_person");

    //Initialize the date which would be send to service /show_person
    learning_service::Person srv;
    srv.request.name="Herman123";
    srv.request.age=20;
    srv.request.sex=learning_service::Person::Request::male;

    //send request to Service /show_person
    ROS_INFO("Call service to show person[name:%s age:%d sex:%d]",srv.request.name.c_str(),srv.request.age,srv.request.sex);
    person_client.call(srv);

    //show the response
    ROS_INFO("Result:%s",srv.response.result.c_str());
    return 0;

}

5.编写Server源码

/*
 /show_person   server
 ServiceDataType is learning_service::Person
*/
#include<ros/ros.h>
#include"learning_service/Person.h"

//Service CALLBACKFUNCTION 
bool PersonCallBackFunction(learning_service::Person::Request &req,learning_service::Person::Response &res)
{
   
    //show request data
    ROS_INFO("Person name:%s age:%d sex:%d",req.name.c_str(),req.age,req.sex);

    //set response data
    res.result="response:OK";

    return true;
}

int main(int argc,char** argv)
{
   
    //ROSINIT
    ros::init(argc,argv,"person_server");

    //Create NodeHandle
    ros::NodeHandle n;

    //Create a server /show_person
    //register the CALLBACKFUNCTION
    //Server provide the Service /show_person
    //when server receive the request from Client
    //Server will call the CBF
    ros::ServiceServer person_service= n.advertiseService("/show_person",PersonCallBackFunction);

    //waiting for request and call the CBF
    ROS_INFO("READY TO SHOW PERSON INFORMATION");
    ros::spin();
    return 0;
}

6.修改CMakeLists.txt

添加依赖

add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${
   catkin_LIBRARIES})
add_dependencies(person_server ${
   PROJECT_NAME}_gencpp)
add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${
   catkin_LIBRARIES})
add_dependencies(person_client ${
   PROJECT_NAME}_gencpp)

7.测试

roscore

尝试调换服务器和客户端的启动顺序
当客户端先启动时,客户端无法找到服务,就会等待服务

rosrun learning_service person_client

客户端Client反馈信息:

[ INFO] [1647790441.760583113]: waitForService: Service [/show_person]
has not been advertised, waiting…

rosrun learning_service person_server

服务器Server提示消息:

[ INFO] [1647790528.243310236]: READY TO SHOW PERSON INFORMATION

此时客户端Client提示与反馈消息:

[ INFO] [1647790528.250326397]: waitForService: Service [/show_person]
is now available. [ INFO] [1647790528.250425869]: Call service to show
person[name:Herman123 age:20 sex:1] [ INFO] [1647790528.258398762]:
Result:response:OK

服务器Server消息:

[ INFO] [1647790528.257503152]: Person name:Herman123 age:20 sex:1

相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
目录
相关文章
|
3月前
|
机器人
05 ROS Parameter服务器实例
本文介绍了如何在ROS(机器人操作系统)中使用参数服务器,包括获取和设置参数,并通过示例代码展示了如何读取和修改海龟仿真(turtlesim)的背景颜色参数。
28 0
|
3月前
|
传感器 网络协议 机器人
03 ROS Publisher-Topic-Subscriber实例
关于ROS(机器人操作系统)中发布者-订阅者模式的教程,详细讲解了如何创建功能包、编写发布者和订阅者的源码、定义和使用自定义消息类型,以及编译和运行发布者和订阅者节点的过程。
47 0
|
弹性计算 固态存储 Linux
使用ROS创建ECS实例
本场景利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云云服务器实例(ECS)。
|
传感器 C++
ROS TF2 添加一个 坐标系 附实例
ROS TF2 添加一个 坐标系 附实例
ROS TF2 添加一个 坐标系 附实例
|
弹性计算 运维 Windows
资源编排支持云助手,增强实例运维能力
目前,阿里云资源编排服务(ROS)开始支持ECS实例云助手功能,增加了2个新的云资源类型。 - [ALIYUN::ECS::Command](https://ros.console.aliyun.com/?#/resourceType/detail/ALIYUN::ECS::Command/metadata) 创建命令 - [ALIYUN::ECS::Invocation](https:/
2003 0
|
弹性计算 数据安全/隐私保护
|
关系型数据库 数据库 对象存储
资源编排配合实例自定义数据,实现RDS自动创建与恢复
背景不少客户需要批量部署系统他们希望ROS不仅能帮助他们自动部署底层PAAS与IAAS资源还能够省去安装软件链接数据库导入数据库文件的动作。而ROS能完美结合ECS的自定义数据自定义镜像RDS Web API最大化减少人肉工作。
2189 0
|
弹性计算
用资源编排模板创建0M带宽的ECS实例
阿里云资源编排服务,创建和管理云资源。
4316 0
|
安全 关系型数据库 MySQL
利用ROS 创建RDS实例
介绍用阿里云资源编排服务创建rds实例
4909 0

推荐镜像

更多