[ROS学习笔记1]ROS的通讯:订阅、服务与动作

本文基于Ubuntu18.04 + ROS1:melodic

ROS各节点间通讯主要有三个机制:订阅、服务、动作。本文将逐一介绍其特点和使用案例。

参考自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 
      

服务【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
      

动作【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
      
posted @ 2021-07-25 17:31  叶小蜗  阅读(568)  评论(0编辑  收藏  举报