• ROS action客户端和服务端通信(Ubuntu )


    ROS action客户端和服务端通信

    @gcusms

    ROS 一般都是用 servicetopic 进行数据之间的交互传输,因为这种通信方式无法满数据实时反馈的要求,所以采用 action 动作消息反馈通信机制(实时反馈的任务进度,并且可以随时终止运行)可以更加有效的控制数据流的输入输出和机器人运动控制,并且在导航中消息传输有更加灵活的数据传输
    在这里插入图片描述

    工作模式图

    工作模式入如下面的图所示 (client 和 server)
    加粗样式
    client 和 server 之间通过 actionlib 定义的 “action protocol” 进行通讯。这种通讯协议是基于 ROS 的消息机制实现的,提供了 client 和 server 的接口

    1. goal:任务目标
    2. cancel:请求取消任务
    3. status:通知 client (客户端)当前的状态
    4. feedback:周期反馈任务运行的监控数据
    5. result:向 client(客户端) 发送任务的执行结果,最终任务完成的结果只会发送一次

    数据文件定义

    action 通信机制的消息格式是以 .action 文件名为后缀的格式定义的,例如 XXX.action

    # 定义需要发送到目标值
    uint32 id 
    ---
    # 结果值
    uint32 total_date
    ---
    # feedback 过程进度反馈值 
    float32 percent_complete
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    我的 action 文件名字定义为 test.action

    CMakeLists.txt 配置文件的修改

    完成 action 文件的定义之后就可以开始创建功能包
    使用命令:✋:

    catkin_create_pkg action_test roscpp rospy actionlib actionlib_msgs std_msgs
    
    • 1

    创建一个名为 action_test 的功能包,然后修改 CMakelists.txt 里面的配置

    在这里插入图片描述

    find_package(catkin REQUIRED COMPONENTS
      actionlib
      actionlib_msgs
      rospy
      std_msgs
      roscpp
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    ## Generate actions in the 'action' folder
    add_action_files(
      FILES
      test.action
    )
    
    ## Generate added messages and services with any dependencies listed here
    generate_messages(
      DEPENDENCIES
      std_msgs
      actionlib_msgs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    其他的相关配置如同创建话题发布消息即可


    客户端 Client

    代码编写,编写一个循环反馈的程序
    创建客户端的步骤如下:

    1. 包含相关的头文件
    2. 初始化 ROS 节点
    3. 创建 NodeHandle
    4. 创建 action 的关联对象
    5. 编写处理的请求,也是代码数据处理的主要部分
    6. 循环

    对于客户端,主要是编写是三个函数:

    1. 任务开始的回调函数
    2. 任务进度过程中接受到的进度反馈函数
    主要的(需要注意)代码块
    • Client 客户端声明
    typedef actionlib::SimpleActionClient<action_test::testAction> Client;
    
    • 1
    • 任务开始调用的函数
    // 任务开始后调用的函数
    void activeCb()
    {
        ROS_INFO("开始任务");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 进度反馈函数
      注意 feddback 申明指针的定义 testFeedbackConstPtr =【action文件名字】& FeedbackConstPtr
    // 收到 feedback 后调用的回调函数
    void feedbackCb(const action_test::testFeedbackConstPtr& feedback)
    {
        ROS_INFO(" 完成的进度 : %f ", feedback->percent_complete);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    具体的代码如下
    #include 
    #include "action_test/testAction.h"
    
    typedef actionlib::SimpleActionClient<action_test::testAction> Client;
    
    // 当action完成后会调用次回调函数一次
    void doneCb(const actionlib::SimpleClientGoalState& state,
            const action_test::testResultConstPtr& result)
    {
        ROS_INFO("任务完成");
        ros::shutdown();
    }
    
    // 任务开始后调用的函数
    void activeCb()
    {
        ROS_INFO("开始任务");
    }
    
    // 收到 feedback 后调用的回调函数
    void feedbackCb(const action_test::testFeedbackConstPtr& feedback)
    {
        ROS_INFO(" 完成的进度 : %f ", feedback->percent_complete);
    }
    
    int main(int argc, char** argv)
    {
        setlocale(LC_ALL,"");
        ros::init(argc, argv, "do_dishes_client");
    
        // 定义一个客户端
        Client client("do_dishes", true);
    
        // 等待服务器端
        ROS_INFO("等待服务器开始运行");
        client.waitForServer();
        ROS_INFO("服务器开始运行");
    
        // 创建一个action的goal
        action_test::testGoal goal;
        goal.send_id = 1;
    
        // 发送action的goal给服务器端,并且设置回调函数
        client.sendGoal(goal,  &doneCb, &activeCb, &feedbackCb);
    
        ros::spin();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    服务端 Server

    创建服务端的步骤如下:

    1. 包含相关的头文件
    2. 初始化 ROS 节点
    3. 创建 NodeHandle
    4. 创建 action 的客户端对象
    5. 编写处理的请求,也是代码数据处理的主要部分
    6. 循环

    对于客户端,主要是编写是三个函数:

    1. 任务开始的回调函数
    2. 任务进度过程中的进度反馈函数
    3. 任务完成结束反馈函数
    主要的(需要注意)代码块
    • 接收到 goal 后调用的函数
    void MissionOne(const action_test::testGoalConstPtr& goal, Server* as)
    
    • 1
    • 消息反馈对象声明和 publishFeedback 函数
    action_test::testFeedback feedback;
    as->publishFeedback(feedback);
    
    • 1
    • 2
    • 任务结束反馈的函数
    as->setSucceeded();
    
    • 1
    具体的代码如下
    #include 
    #include 
    #include "action_test/testAction.h"
    
    typedef actionlib::SimpleActionServer<action_test::testAction> Server;
    
    // 收到action的 goal 后调用的回调函数
    void MissionOne(const action_test::testGoalConstPtr& goal, Server* as)
    {
        ros::Rate r(1);
        action_test::testFeedback feedback;
    
        ROS_INFO("任务 %d 正在工作中.", goal->send_id);
    
        // 假设洗盘子的进度,并且按照1hz的频率发布进度feedback
        for(int i=1; i<=10; i++)
        {
            feedback.percent_complete = i * 10;
            as->publishFeedback(feedback);
            r.sleep();
        }
    
        // 当action完成后,向客户端返回结果
        ROS_INFO("任务 %d 完成.", goal->send_id);
        as->setSucceeded();
    }
    
    int main(int argc, char** argv)
    {
        setlocale(LC_ALL,"");
        ros::init(argc, argv, "do_dishes_server");
        ros::NodeHandle n;
    
        // 定义一个服务器
        Server server(n, "do_dishes", boost::bind(&MissionOne, _1, &server), false);
    
        // 服务器开始运行
        server.start();
    
        ros::spin();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    上述代码块中的客户端和服务端编写的过程中,函数传递的对象一般使用引用活着指针来对数据进行操作,获取到 action 文件里面的数据需要以 GalConsPtr 来声明常数指针进行后序的操作


    测试

    打开一个新的终端,如果还没有加载到环境变量当中,重新添加临时的环境声明,不过下次需要重新声明加载一次

    source devel/setup.bash
    
    • 1
    1. 启动 ros 中的 master 节点
    roscore
    
    • 1

    2.启动客户端

    rosrun action_test action02_client 
    
    • 1

    3.启动服务端

    rosrun action_test action01_server 
    
    • 1

    在这里插入图片描述

    至此,完成了 ROS 中的 action 简易的通信机制,这种通信机制尝尝会应用于很多的场景,例如路径规划,机器人关节控制


    🌸🌸🌸完结撒花🌸🌸🌸


    🌈🌈Redamancy🌈🌈


  • 相关阅读:
    eCapture环境搭建编译|Android https明文抓包
    ElasticStack日志分析平台-ES 集群、Kibana与Kafka
    [持续更新...] 牛客Python专项题
    第3章“程序的机器级表示”:异类的数据结构
    Mybatis Plus 详解 IService、BaseMapper、自动填充、分页查询功能
    Python多线程技巧心得详解
    linux 查看系统版本
    Apache Flink 1.12.0 on Yarn(3.1.1) 所遇到的問題
    为什么拼多多总能给市场带来惊喜?
    HTTPS对HTTP的加密过程
  • 原文地址:https://blog.csdn.net/Msyusheng/article/details/128169720