[ROS学习笔记1]ROS的通讯:订阅、服务与动作
本文基于Ubuntu18.04
+ ROS1:melodic
ROS各节点间通讯主要有三个机制:订阅、服务、动作。本文将逐一介绍其特点和使用案例。
订阅【Topic】
订阅模式就类似于杂志的订阅。发布者ROS::Publisher
(出版商)向节点网络内发布advertise
一个话题名(期刊名)为"exp_topic"的消息(论文),订阅者ROS::Subscriber
(用户)订阅subscribe
该话题(期刊)即可获取该消息(论文)。
因此,订阅模式需要实现三/两个部分,即发布者ROS::Publisher
、订阅者ROS::Subscriber
和消息类型msg
(论文格式),其中也可以使用已有的消息类型
- [msg]:
$packege_name/msg/Paper.msg
string title string author
- [ROS::Publisher]:
$packege_name/src/IEEE.cpp
/** *发布IEEE_Access话题,消息类型为Paper */ #include <sstream> #include "ros/ros.h" // 这个头文件在catkin编译自定义msg的时候产生,将产生同名对象 #include "learning_communication/IEEE_Access.h" int main(int argc,char **argv) { //ros节点初始化,固定格式,最后一个参数"IEEE"为节点名称 ros::init(argc,argv,"IEEE"); // 声明一个节点n ros::NodeHandle n; //构造发布者IEEE_pub,话题名为IEEE_Access ros::Publisher IEEE_pub=n.advertise<learning_communication::IEEE_Access>("IEEE_Access",1000); // 设置循环发布时间 ros::Rate loop_rate(10); int count = 0; while (ros::ok()) { learning_communication::IEEE_Access paper; std::stringstream ss; ss<<"A Reserch on ROS Commuinication"<<count; paper.title="A Reserch on ROS Commuinication"; ss<<"A Reserch on ROS Commuinication"<<count; paper.author="yexiaowo"; // 这里%s对应的参数应该为char*,显然paper.title、paper.author均不是。c_str()方法返回一个指向以空字符结尾的char* ROS_INFO("title:%s;author:%s;index:%d",paper.title.c_str(),paper.author.c_str(),count); IEEE_pub.publish(paper); // 等待回调函数 ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; }
- [ROS::Subscriber]:
$packege_name/src/student.cpp
/** * 该例程将订阅IEEE_Access话题,消息类型paper */ #include "ros/ros.h" #include "learning_communication/IEEE_Access.h" // 接收到订阅的消息后,会进入消息回调函数 void chatterCallback(const learning_communication::IEEE_Access::ConstPtr& paper) { // 将接收到的论文打印出来 ROS_INFO("I received: [%s]", paper->title.c_str()); } int main(int argc, char **argv) { ros::init(argc, argv, "student"); ros::NodeHandle n; ros::Subscriber sub = n.subscribe("IEEE_Access", 1000, chatterCallback); ros::spin(); return 0; }
- 配置功能包
$packege_name/packege.xml
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
- 添加编译选项
$packege_name/CMakeLists.txt
#编译自定义消息msg find_package( ...... message_generation) catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime) add_message_files(FILES IEEE_Access.msg) generate_messages(DEPENDENCIES std_msgs) #编译cpp add_executable(IEEE src/IEEE.cpp) target_link_libraries(IEEE ${catkin_LIBRARIES}) add_executable(student src/student.cpp) target_link_libraries(student ${catkin_LIBRARIES})
- 运行
- terminal-1
roscore
- terminal-2
source devel/setup.bash rosrun learning_communication IEEE
- terminal-3
source devel/setup.bash rosrun learning_communication student
- terminal-1
服务【Service】
服务模式的通讯方式为由客户端ros::ServiceClient
发起请求request
,服务器ros::ServiceServer
收到请求后响应,再由客户端接收应答response
。
因此,需要定义请求应答消息格式srv
、服务器和客户端。
- [msg]:
$packege_name/srv/AddTwoInts.srv
int64 a int64 b --- int64 sum
- [ros::ServiceServer]:
$packege_name/src/server.cpp
#include "ros/ros.h" #include"learning_communication/AddTwoInts.h" bool add(learning_communication::AddTwoInts::Request &req,learning_communication::AddTwoInts::Response &res) { res.sum=req.a+req.b; ROS_INFO("request:x=%ld,y=%ld",(long int)req.a,(long int)req.b); ROS_INFO("sending back response:[%ld]",(long int)res.sum); return true; } int main(int argc,char **argv) { ros:: init(argc,argv,"add_two_ints_server"); ros::NodeHandle n; //注册服务,绑定add方法 ros::ServiceServer service = n.advertiseService("add_two_ints",add); ROS_INFO("ready to add two ints"); ros::spin(); return 0; }
- [ros::ServiceClient]:
$packege_name/src/client.cpp
#include<cstdlib> #include"ros/ros.h" #include"learning_communication/AddTwoInts.h" int main(int argc,char **argv) { ros::init(argc,argv,"add_two_ints_client"); if(argc!=3) { ROS_INFO("usage:add_two_ints_client X Y"); return 1; } ros::NodeHandle n; ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints"); learning_communication::AddTwoInts srv; //atoll即为array[] to long long,例如atoll("sgk14 679")返回(long long)14 srv.request.a=atoll(argv[1]); srv.request.b=atoll(argv[2]); if (client.call(srv)) { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; } return 0; }
- 配置功能包
$packege_name/packege.xml
<build_depend>message_generation</build_depend> <exec_depend>message_runtime</exec_depend>
- 添加编译选项
$packege_name/CMakeLists.txt
#编译自定义消息srv find_package( ...... message_generation) catkin_package(CATKIN_DEPENDS roscpp rospy std_msgs message_runtime) add_service_files(FILES AddTwoInts.srv) #编译cpp add_executable(server src/server.cpp) target_link_libraries(server ${catkin_LIBRARIES}) add_dependencies(server ${PROJECT_NAME}_gencpp) add_executable(client src/client.cpp) target_link_libraries(client ${catkin_LIBRARIES}) add_dependencies(client ${PROJECT_NAME}_gencpp)
- 运行
- terminal-1
roscore
- terminal-2
source devel/setup.bash rosrun learning_communication server
- terminal-3
source devel/setup.bash rosrun learning_communication client 123 222
- terminal-1
动作【Action】
动作模式的通讯方式为动作客户端actionlib::SimpleActionClient<$action>
发步任务目标goal
,动作服务器actionlib::SimpleActionServer<$action>
向动作客户端通知当前状态status
、周期反馈任务运行监控状态feedback
、并在最后发布任务结果result
。其中,动作客户端还可以提前发布取消任务的请求。cancel
。
因此,需要定义动作消息格式action
、服务器actionlib::SimpleActionServer<$action>
和客户端actionlib::SimpleActionClient<$action>
。
- [msg]:
$packege_name/srv/AddTwoInts.srv
#goal uint32 singer_id --- #result uint32 songs_singed --- #feedback float32 complete_percent
- [actionlib::SimpleActionServer<$action>]:
$packege_name/src/DoSing_server.cpp
#include <ros/ros.h> #include <actionlib/server/simple_action_server.h> #include "learning_communication/DoSingAction.h" typedef actionlib::SimpleActionServer<learning_communication::DoSingAction> Server; void execute(const learning_communication::DoSingGoalConstPtr& goal, Server* as) { // 假设唱歌的进度,并且按照1hz的频率发布进度feedback ros::Rate r(1); learning_communication::DoSingFeedback feedback; ROS_INFO("Singer %d is working.", goal->singer_id); for(int i=1; i<=10; i++) { feedback.complete_percent= i * 10; as->publishFeedback(feedback); r.sleep(); } // 当action完成后,向客户端返回结果 ROS_INFO("Singer %d finish working.", goal->singer_id); as->setSucceeded(); } int main(int argc, char** argv) { ros::init(argc, argv, "do_sing_server"); ros::NodeHandle n; // 定义一个服务器 //boost::bind(&execute, _1, &server)(x)等同于执行execute(x,&server) Server server(n, "do_sing", boost::bind(&execute, _1, &server), false); // 服务器开始运行 server.start(); ros::spin(); return 0; }
- [actionlib::SimpleActionClient<$action>]:
$packege_name/src/DoSing_client.cpp
#include <ros/ros.h> #include <actionlib/server/simple_action_server.h> #include "learning_communication/DoSingAction.h" typedef actionlib::SimpleActionServer<learning_communication::DoSingAction> Server; void execute(const learning_communication::DoSingGoalConstPtr& goal, Server* as) { // 假设唱歌的进度,并且按照1hz的频率发布进度feedback ros::Rate r(1); learning_communication::DoSingFeedback feedback; ROS_INFO("Singer %d is working.", goal->singer_id); for(int i=1; i<=10; i++) { feedback.complete_percent= i * 10; as->publishFeedback(feedback); r.sleep(); } // 当action完成后,向客户端返回结果 ROS_INFO("Singer %d finish working.", goal->singer_id); as->setSucceeded(); } int main(int argc, char** argv) { ros::init(argc, argv, "do_sing_server"); ros::NodeHandle n; // 定义一个服务器 //boost::bind(&execute, _1, &server)(x)等同于执行execute(x,&server) Server server(n, "do_sing", boost::bind(&execute, _1, &server), false); // 服务器开始运行 server.start(); ros::spin(); return 0; }
- 配置功能包
$packege_name/packege.xml
<build_depend>actionlib</build_depend> <build_depend>actionlib_msgs</build_depend> <exec_depend>actionlib</exec_depend> <exec_depend>actionlib_msgs</exec_depend>
- 添加编译选项
$packege_name/CMakeLists.txt
#编译自定义动作消息 find_package(catkin REQUIRED actionlib_msgs actionlib) add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES actionlib_msgs) #编译cpp add_executable(DoSing_client src/DoSing_client.cpp) target_link_libraries(DoSing_client ${catkin_LIBRARIES}) add_dependencies(DoSing_client ${${PROJECT_NAME}_EXPORTED_TARGETS}) add_executable(DoSing_server src/DoSing_server.cpp) target_link_libraries(DoSing_server ${catkin_LIBRARIES}) add_dependencies(DoSing_server ${${PROJECT_NAME}_EXPORTED_TARGETS})
- 运行
- terminal-1
roscore
- terminal-2
source devel/setup.bash rosrun learning_communication DoSing_client
- terminal-3
source devel/setup.bash rosrun learning_communication DoSing_server
- terminal-1
本文来自博客园,作者:叶小蜗,转载请注明原文链接:https://www.cnblogs.com/yexiaowo/p/ROS-communication.html