04 ROS Client-Service-Server实例

零、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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值