一、客户端 Client
- 初始化ROS节点
- 创建句柄
- 检测所需的服务是否存在
- 创建Client实例
- 封装要请求的数据内容
- 发布数据请求服务
- 等待Service反馈结果
1.创建源码
#include<ros/ros.h>
#include<turtlesim/Spawn.h>
int main(int argc,char **argv)
{
ros::init(argc,argv,"turtle_spawn");
ros::NodeHandle node;
ros::service::waitForService("/spawn");
ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");
turtlesim::Spawn srv;
srv.request.x = 2.0;
srv.request.y = 2.0;
srv.request.name = "turtle2";
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);
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.编写源码
#include<ros/ros.h>
#include<geometry_msgs/Twist.h>
#include<std_srvs/Trigger.h>
ros::Publisher turtle_vel_pub;
bool pubCommand = false;
bool CommandCallBack(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &res)
{
pubCommand = !pubCommand;
ROS_INFO("Publish turtle velocity command [%s]",pubCommand==true?"YES":"NO");
res.success = true;
res.message = "Change turtle command state";
}
int main(int argc,char **argv)
{
ros::init(argc,argv,"turtle_command_server");
ros::NodeHandle n;
ros::ServiceServer command_service = n.advertiseService("/turtle_command",CommandCallBack);
turtle_vel_pub=n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);
ROS_INFO("Ready to receive turtle command.");
ros::Rate loop_rate(10);
while(ros::ok())
{
ros::spinOnce();
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源码
#include<ros/ros.h>
#include "learning_service/Person.h"
int main(int argc,char **argv)
{
ros::init(argc,argv,"person_client");
ros::NodeHandle n;
ros::service::waitForService("/show_person");
ros::ServiceClient person_client = n.serviceClient<learning_service::Person>("/show_person");
learning_service::Person srv;
srv.request.name="Herman123";
srv.request.age=20;
srv.request.sex=learning_service::Person::Request::male;
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);
ROS_INFO("Result:%s",srv.response.result.c_str());
return 0;
}
5.编写Server源码
#include<ros/ros.h>
#include"learning_service/Person.h"
bool PersonCallBackFunction(learning_service::Person::Request &req,learning_service::Person::Response &res)
{
ROS_INFO("Person name:%s age:%d sex:%d",req.name.c_str(),req.age,req.sex);
res.result="response:OK";
return true;
}
int main(int argc,char** argv)
{
ros::init(argc,argv,"person_server");
ros::NodeHandle n;
ros::ServiceServer person_service= n.advertiseService("/show_person",PersonCallBackFunction);
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
|