【ROS入门】使用 ROS 动作(Action)机制实现目标请求、进度与完成结果的反馈

任务要求

使用 ROS 动作(Action)机制实现目标请求、进度与完成结果的反馈:

  • 创建服务端,注册 Action
  • 客户端发送action 请求检测 40个零件
  • 服务端接收后,每隔 1s 检测一个零件 (每检测一个打印1次),并实时给客户端返回检测进度(客户端打印进度百分比),并在检测完毕时告知客户端目标完成。

话题模型

action 是一种类似于服务通信的实现,其实现模型也包含请求和响应,但是不同的是,在请求和响应的过程中,服务端还可以连续的反馈当前任务进度,客户端可以接收连续反馈并且还可以取消任务。

action结构图解:

请添加图片描述

action结构图解:
请添加图片描述

实现步骤

定义action文件

action、srv、msg 文件内的可用数据类型一致,且三者实现流程类似

按照固定格式创建action文件

首先新建功能包,并导入依赖: roscpp rospy std_msgs actionlib actionlib_msgs

然后功能包下新建 action 目录,新增 check.action文件。

action 文件内容组成分为三部分:请求目标值、最终响应结果、连续反馈,三者之间使用—分割

内容如下:
在这里插入图片描述

编辑配置文件

CMakeLists.txt

find_package
(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  actionlib
  actionlib_msgs
)
add_action_files(
  FILES
  check.action
)
generate_messages(
  DEPENDENCIES
  std_msgs
  actionlib_msgs
)
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo04_action
 CATKIN_DEPENDS roscpp rospy std_msgs actionlib actionlib_msgs
#  DEPENDS system_lib
)

编译生成中间文件

编译后会生成一些中间文件。

msg文件(…/工作空间/devel/share/包名/msg/xxx.msg):

在这里插入图片描述

C++ 调用的文件(…/工作空间/devel/include/包名/xxx.h):

在这里插入图片描述

Python 调用的文件(…/工作空间/devel/lib/python3/dist-packages/包名/msg/xxx.py):

在这里插入图片描述

编写服务端和客户端

vscode配置

{
  "configurations": [
    {
      "browse": {
        "databaseFilename": "${default}",
        "limitSymbolsToIncludedHeaders": false
      },
      "includePath": [
        "/opt/ros/noetic/include/**",
        "/home/chenyikeng/demo01_ws/src/helloworld/include/**",
        "/home/chenyikeng/ROS_Topic_Demo/src/topic_demo/include/**",
        "/usr/include/**",
        "/home/chenyikeng/ROS_Topic_Demo/devel/include"
      ],
      "name": "ROS",
      "intelliSenseMode": "gcc-x64",
      "compilerPath": "/usr/bin/gcc",
      "cStandard": "gnu11",
      "cppStandard": "c++14"
    }
  ],
  "version": 4
}

服务端

/*
流程:
    1.包含头文件;
    2.初始化ROS节点;
    3.创建NodeHandle;
    4.创建action服务对象;
    5.处理请求,产生反馈与响应;
      a.获取并解析提交的目标值
      b.产生连续反馈
      c.最终结果响应
    6.spin().
*/


#include "ros/ros.h"
#include "actionlib/server/simple_action_server.h"  //actionlib里头服务端的库
#include "action_demo/checkAction.h"    //自定义action文件时编译生成的库

// 使用模板创建action服务对象并定义
typedef actionlib::SimpleActionServer<action_demo::checkAction> Server;


//请求处理(a.解析提交的目标值;b.产生连续反馈;c.最终结果响应) --- 回调函数
void callBack(const action_demo::checkGoalConstPtr &goalPtr, Server*  server)
{
    // a.获取并解析提交的目标值
    int goal_num = goalPtr -> num;
    ROS_INFO("客户端提交的目标值是: %d",goal_num);

    // b.产生连续反馈
    ros::Rate rate(1); //1Hz
    int result = 0;
    for(int i = 1; i<= goal_num; i++)
    {
        // 累加
        result ++;
        // 休眠
        rate.sleep();
        //产生(封装)连续反馈
        //创建feedback对象
        action_demo::checkFeedback fb;
        fb.progress_bar = i / (double)goal_num;
        //发送
        server->publishFeedback(fb);
        //打印
        ROS_INFO("检测%d个零件", result);
    }

    // c.最终结果响应
    // 创建result对象
    ROS_INFO("检测完成");
    action_demo::checkResult r;
    r.result = result;
    server -> setSucceeded(r);
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");

    ros::init(argc,argv,"check_server");

    ros::NodeHandle n;

    // 创建action服务对象
    /*SimpleActionServer(ros::NodeHandle n,     #句柄
                        std::string name,       #话题名称
                        boost::function<void (const action_demo::checkGoalConstPtr &)> execute_callback,    
                        #回调函数,可以解析传入的目标值,产生连续反馈,以及响应最终结果
                        bool auto_start)        #布尔值,是否自动启动
    */
    Server server(n,"check",boost::bind(&callBack,_1,&server),false);

    //如果auto_start为false,那么需要手动调用该函数启动服务
    server.start();     
    ROS_INFO("服务启动……");

    //spin()回旋
    ros::spin();
    return 0;
}

客户端

/*
流程:
    1.包含头文件;
    2.初始化ROS节点;
    3.创建NodeHandle;
    4.创建action客户端对象;
    5.发送目标,处理反馈以及最终结果;
      a.连接建立 --- 回调函数
      b.处理连续反馈 --- 回调函数
      c.处理最终响应 --- 回调函数
    6.spin().
*/
#include "ros/ros.h"
#include "actionlib/client/simple_action_client.h"  //actionlib里头服务端的库
#include "action_demo/checkAction.h"    //自定义action文件时编译生成的库


// 创建action客户端对象
typedef actionlib::SimpleActionClient<action_demo::checkAction> Client;

// 服务端返回最终响应结果时候触发的回调
void done_cb(const actionlib::SimpleClientGoalState &state, const action_demo::checkResultConstPtr &result)
{
    // 判断响应状态是否成功
    if (state.state_ == state.SUCCEEDED)
        ROS_INFO("检测完成");
    else 
        ROS_INFO("任务失败!");
}

// 连接被激活的时候触发的回调
void active_cb()
{
    ROS_INFO("服务已经被激活....");
}

// 连续反馈时的回调函数
void feedback_cb(const action_demo::checkFeedbackConstPtr &feedback)
{   
    float progress_bar_percentage = feedback->progress_bar*100;
    if(progress_bar_percentage-(int)progress_bar_percentage==0)
        ROS_INFO("当前进度:%d%%",(int)progress_bar_percentage);
    else
        ROS_INFO("当前进度:%.1f%%",progress_bar_percentage);
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");

    ros::init(argc,argv,"check_client");

    ros::NodeHandle n;

    // 创建action客户端对象;
    // SimpleActionClient(ros::NodeHandle & n, const std::string & name, bool spin_thread = true)
    // actionlib::SimpleActionClient<demo01_action::AddIntsAction> client(nh,"addInts");
    Client client(n,"check",true);

    //等待服务启动
    ROS_INFO("等待服务器启动……");
    client.waitForServer();

    // 发送目标,处理反馈以及最终结果;
    /*  
        void sendGoal(const demo01_action::AddIntsGoal &goal, 
            boost::function<void (const actionlib::SimpleClientGoalState &state, const demo01_action::AddIntsResultConstPtr &result)> done_cb, 
            用于处理最终响应
            boost::function<void ()> active_cb, 
            连接被激活的时候使用的回调
            boost::function<void (const demo01_action::AddIntsFeedbackConstPtr &feedback)> feedback_cb)
            处理连续反馈时相关的回调函数
    */

    // 设置目标值:40个零件
    // 声明对象
    action_demo::checkGoal goal;
    goal.num = 40;

    // 发送目标数据
    client.sendGoal(goal,&done_cb,&active_cb,&feedback_cb);


    // spin()回旋
    ros::spin();
    return 0;
}

编译配置文件

add_executable(check_server_c src/check_server_c.cpp)
add_executable(check_client_c src/check_client_c.cpp)
...

add_dependencies(check_server_c ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
add_dependencies(check_client_c ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
...

target_link_libraries(check_server_c
  ${catkin_LIBRARIES}
)
target_link_libraries(check_client_c
  ${catkin_LIBRARIES}
)

执行

首先启动 roscore,然后分别启动action服务端与action客户端,最终运行结果与案例类似。

结果如下:

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS通信机制包括话题(Topic)、服务(Service)和动作Action)三种方式。 1. 话题:话题是一种发布/订阅(Publish/Subscribe)机制,发布者(Publisher)将消息发布到话题中,订阅者(Subscriber)从话题中接收消息。话题的实现流程如下: - 发布者指定话题名、消息类型和队列长度。 - 发布者将消息发布到话题中。 - 订阅者从话题中接收消息。 2. 服务:服务是一种请求/响应(Request/Response)机制,客户端(Client)向服务器(Server)发送请求,服务器对请求进行处理并返回响应。服务的实现流程如下: - 定义服务类型和服务名。 - 服务器等待客户端请求。 - 客户端向服务器发送请求。 - 服务器对请求进行处理并返回响应。 3. 动作动作是一种异步请求/响应机制,客户端向服务器发送请求,服务器对请求进行处理并返回响应,但在响应返回之前,客户端可以取消请求或者向服务器发送取消请求动作的实现流程如下: - 定义动作类型和动作名。 - 服务器等待客户端请求。 - 客户端向服务器发送请求。 - 服务器对请求进行处理并返回响应。 - 在响应返回之前,客户端可以取消请求或者向服务器发送取消请求。 相同点:话题、服务和动作都是ROS通信机制的一部分,它们都实现了不同的通信机制,使得ROS系统中的组件能够进行数据交换和协作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值