ROS Action

在 ROS 中,Action 是一种支持长时间异步任务的通信机制。与 Service 不同,Action 允许客户端发起一个请求,并在任务执行的过程中不断接收反馈,直到任务完成。这种机制非常适用于可能需要较长时间来完成的任务,比如机器人移动、导航或复杂的传感器数据采集。

ROS Action 的工作原理

Action 的通信流程由三个主要组成部分:Goal(目标)Feedback(反馈)Result(结果)。在 ROS 中,Action 通常包含以下几个节点角色:

  • Action Server(服务器):负责执行具体的任务,并将执行过程中的反馈和最终结果发送给客户端。
  • Action Client(客户端):发起任务请求,发送目标并接收服务器的反馈和结果。

1. ROS Action 的基本组成

每个 ROS Action 包括以下几个部分:

  • Goal(目标):客户端发送的目标数据,定义了任务的具体内容。
  • Feedback(反馈):服务器在执行过程中可以发送实时反馈,告知任务进展。
  • Result(结果):服务器在任务完成后返回的最终结果。

2. 定义 ROS Action

在 ROS 中,我们使用 .action 文件定义 Action 的数据格式,它类似于 .srv 文件,包含三部分:GoalFeedbackResult,各部分使用 --- 分隔。

示例:定义一个计数器的 Action

假设我们想定义一个计数器的 Action,它接收一个目标数字并从 0 计数到该数字,期间不断提供进度反馈,并在完成后返回最终结果。

首先我们先创建一个my_action的功能包,进入到自己的ros工作空间下面,执行:

catkin_create_pkg my_action roscpp actionlib actionlib_msgs std_msgs

在建立的功能包下再创建一个action文件夹,用来存放“.action”文件

定义 Countdown.action 文件:

# CountToNumber.action# Goal: 目标数字
int32 target_number---
# Result: 操作结果(是否成功)
bool success---
# Feedback: 当前计数
int32 current_count

这意味着我们将向 Action 服务器发送一个目标数字,服务器会从 0 开始计数并每次提供当前的计数进度,最后返回一个表示成功的布尔值。

3. 配置 CMakeLists.txt 和 package.xml

然后,像之前的例子一样,我们需要更新 CMakeLists.txtpackage.xml 文件,确保 Action 文件被编译并生成相应的消息类型。

  • CMakeLists.txt
find_package(catkin REQUIRED COMPONENTSactionlibactionlib_msgsroscpp std_msgs
)add_action_files(FILESCountToNumber.action
)generate_messages(DEPENDENCIESactionlib_msgsstd_msgs
)catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES my_actionCATKIN_DEPENDS roscpp rospy std_msgs actionlib actionlib_msgs
#  DEPENDS system_lib
)
  • package.xml
<buildtool_depend>catkin</buildtool_depend>
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>actionlib</build_export_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>std_msgs</exec_depend>

确保上述内容无误后,回到工作空间编译我们的my_action功能包

cd ~/catkin_ws/
catkin_make -DCATKIN_WHITELIST_PACKAGES=my_action

(编译你可以直接使用catkin_make,这里我加 -DCATKIN_WHITELIST_PACKAGES

=my_action表示只编译my_action功能包)

编译通过后你就可以在工作空间目录下的devel/include/my_action路径中找到自定义的action的头文件

4. 实现 Action 服务器

接下来,我们实现 Action 服务器,接收目标数字并从 0 开始计数,定期提供反馈,直到计数完成或被取消。我们在my_action功能包的src下创建一个countdown_server.cpp

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include <my_action/CountToNumberAction.h>class CountToNumberAction
{
protected:ros::NodeHandle nh_;   // ROS节点句柄,用于与ROS系统交互actionlib::SimpleActionServer<my_action::CountToNumberAction> as_;  // Action 服务器对象,类型是自定义的 `CountToNumberAction`std::string action_name_;   // Action的名称my_action::CountToNumberFeedback feedback_;   // 存储反馈的对象my_action::CountToNumberResult result_;   // 存储结果的对象public:CountToNumberAction(std::string name) :as_(nh_, name, boost::bind(&CountToNumberAction::executeCB, this, _1), false),action_name_(name){as_.start();}void executeCB(const my_action::CountToNumberGoalConstPtr &goal){int target = goal->target_number;   // 获取目标数字ROS_INFO("Counting to %d", target);  // 打印目标数字// 从 0 开始计数并提供反馈for (int i = 0; i <= target; ++i) {if (as_.isPreemptRequested()) {ROS_INFO("%s: Preempted", action_name_.c_str());as_.setPreempted();   // 如果请求了中断,则设置为已中断return;}feedback_.current_count = i;  // 更新当前计数as_.publishFeedback(feedback_);   // 发布反馈ros::Duration(1.0).sleep();  // 每秒更新一次反馈}// 返回结果result_.success = true;  // 设置操作成功as_.setSucceeded(result_);  // 设置 Action 成功并返回结果}
};int main(int argc, char** argv)
{ros::init(argc, argv, "count_to_number_action_server");   // 初始化ROS节点CountToNumberAction count_to_number_action("count_to_number");  // 创建 Action 服务器实例ros::spin();  // 进入 ROS 事件循环,等待请求return 0;
}

代码我已经注释好,就不用过多篇幅重复介绍了,只想插一嘴构造函数中表现的回调函数机制,因为最近面试被问到了

这段内容可以通过目录选择性阅读哈

扩展:C++的回调函数机制

CountToNumberAction(std::string name) :as_(nh_, name, boost::bind(&CountToNumberAction::executeCB, this, _1), false),action_name_(name)
{as_.start();
}

解释:

  • 构造函数接收一个字符串 name 作为参数,指定 Action 的名称。
  • as_(nh_, name, boost::bind(&CountToNumberAction::executeCB, this, _1), false)
    • actionlib::SimpleActionServer 需要三个参数:
      1. nh_:ROS节点句柄,负责通信。
      2. name:Action 名称。
      3. 回调函数:使用 boost::bind 绑定一个成员函数 executeCB,这是处理客户端请求的回调函数。_1 表示传递给回调函数的第一个参数(即 Goal 对象)。
    • false:这是一个布尔参数,表示是否立即启动服务器。这里设置为 false,表示服务器启动后不会自动开始等待请求,而是通过调用 as_.start() 来启动。
  • as_.start():启动 Action 服务器,开始监听客户端请求。

在 ROS 的 Action 服务器中,boost::bind 是一种通过将成员函数或普通函数转换为可调用对象(仿函数)的方式。这样做的目的是为了实现回调机制,尤其是在 actionlib::SimpleActionServer 中。

这段代码通过 boost::bindCountToNumberAction::executeCB 成员函数转换成一个可以作为回调的函数对象(仿函数)。让我们逐步分析:

  • boost::bind 的作用

    • boost::bind 用来将一个成员函数绑定到特定的对象上,并且返回一个可调用对象(仿函数)。这个对象可以像普通函数一样被调用。

    • &CountToNumberAction::executeCB:这是 CountToNumberAction 类的成员函数 executeCB 的指针。

    • this:它是指向当前对象的指针,告诉 boost::bind 成员函数是作用于哪个对象。

    • _1:这是一个占位符,表示传递给回调函数的第一个参数(即 goal)。在实际调用时,_1 会被替换为客户端发送的目标(Goal)。

  • actionlib::SimpleActionServer

    • actionlib::SimpleActionServer 构造函数接受一个回调函数作为参数,这个回调函数会在 Action 服务器收到目标请求时被调用。
    • 在这个例子中,回调函数是 CountToNumberAction::executeCB,通过 boost::bind 转换成了一个仿函数,它接收 goal 作为输入并处理相关逻辑。
  • 仿函数的调用

    • 当 Action 服务器接收到客户端请求时,它会调用绑定的回调函数(仿函数),从而触发计数逻辑的执行。这个回调会处理客户端的目标请求并定期向客户端发送反馈。

5. 实现 Action 客户端

接下来,我们实现客户端来发送目标数字并接收进度反馈和最终结果。

我们在my_action功能包的src下创建一个countdown_client.cpp

#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <my_action/CountToNumberAction.h>  //包含自定义 Action 类型的头文件,CountToNumberAction 这个类型定义了目标(Goal)、反馈(Feedback)和结果(Result)结构。typedef actionlib::SimpleActionClient<my_action::CountToNumberAction> Client;// doneCb - 任务完成后的回调
void doneCb(const actionlib::SimpleClientGoalState& state,const my_action::CountToNumberResultConstPtr& result)
{ROS_INFO("Finished in state: %s", state.toString().c_str());if (result->success)ROS_INFO("Counting completed successfully!");elseROS_WARN("Counting failed.");
}/*** activeCb:当目标开始被服务器处理时触发。此回调表示目标已经被接受并正在处理。它简单地打印一条信息,表示目标正在被处理。*/
void activeCb()
{ROS_INFO("Goal is being processed...");
}// feedbackCb - 反馈更新的回调
void feedbackCb(const my_action::CountToNumberFeedbackConstPtr& feedback)
{ROS_INFO("Current count: %d", feedback->current_count);
}int main(int argc, char** argv)
{ros::init(argc, argv, "count_to_number_action_client");// 创建一个客户端,连接到服务器Client ac("count_to_number", true);ROS_INFO("Waiting for action server to start...");ac.waitForServer();  // 等待服务器启动my_action::CountToNumberGoal goal;goal.target_number = 10;  // 设置目标数字// 发送目标并设置回调函数ac.sendGoal(goal, &doneCb, &activeCb, &feedbackCb);ros::spin();return 0;
}
  • 这段代码实现了一个 ROS Action 客户端,连接到名为 "count_to_number" 的服务器并发送目标(goal.target_number = 10)。
  • 客户端使用三个回调函数:
    1. doneCb:处理任务完成后的状态和结果。
    2. activeCb:处理目标开始被服务器处理时的状态。
    3. feedbackCb:处理服务器发送的反馈信息(当前计数值)。
  • 客户端通过调用 ac.sendGoal 发送目标,并且使用 ros::spin() 保持客户端运行,接收和处理服务器的反馈和状态。

(关于C++回调函数机制的设计,我会单独再写一篇)

6. 再次配置CMakeList.txt

接下来,在CMakeList.txt的末尾添加以下内容

add_executable(countdown_server src/countdown_server.cpp)
add_dependencies(countdown_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(countdown_server ${catkin_LIBRARIES})add_executable(countdown_client src/countdown_client.cpp)
add_dependencies(countdown_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(countdown_client ${catkin_LIBRARIES})

此时功能包的目录结构大家可以对照看一下有没有问题

├── action
│   └── CountToNumber.action
├── CMakeLists.txt
├── include
│   └── my_action
├── package.xml
└── src├── countdown_client.cpp└── countdown_server.cpp4 directories, 5 files

7. 启动 ROS Action 节点

编译
cd ~/catkin_ws/
catkin_make -DCATKIN_WHITELIST_PACKAGES=my_action

编译通过后起一个终端,输入roscore回车,启动ros master

roscore

再起一个终端来启动服务端

rosrun my_action countdown_server

再起一个终端来启动客户端

rosrun my_action countdown_client 

客户端输出如下图

服务端打印信息如下图:

8. Action 的应用场景

  • 机器人运动控制:如导航到指定点,机器人可以通过 Action 接收目标位置,在运动过程中反馈进度。
  • 图像处理任务:复杂的图像处理任务(如识别、追踪)可能需要较长时间,可以通过 Action 提供进度反馈。
  • 传感器数据采集:采集数据并持续返回采集进度。

Action 与 Service 的区别

特性ServiceAction
通信方式请求-响应,同步通信目标-反馈-结果,异步通信
使用场景短时间任务长时间任务,提供进度反馈
回调机制单次回调多次反馈回调和结果回调
数据类型请求和响应目标、反馈和结果
中断任务不支持支持预取消和中断

总结

ROS Action 是 ROS 中一种异步的请求-反馈机制,适用于长时间运行任务。通过 Goal、Feedback 和 Result 的组合,Action 提供了一种更加灵活的任务管理方式,支持任务进度反馈和任务中断功能,使其在复杂的机器人任务中非常有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/14811.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

江苏省考公务员报名照片要求及处理方法

随着江苏省公务员考试的临近&#xff0c;许多考生已经开始准备报名所需的各项材料&#xff0c;其中照片的准备尤为重要。本文将详细介绍江苏省考公务员报名照片的具体要求以及如何使用手机拍照并处理照片&#xff0c;确保您的报名过程顺利进行。 一、江苏省公务员招录考试报名照…

计算机网络学习笔记-3.2介质访问控制

文章目录 介质访问控制静态划分信道 动态分配信道轮询访问介质访问控制随机访问介质访问控制ALOHA协议简介ALOHA协议的工作原理 介质访问控制 介质访问控制&#xff08;MAC&#xff0c;Medium Access Control&#xff09;&#xff0c;质访问控制的目的是确保多个设备能够高效、…

软件测试-巨量测试开发

软件测试-巨量测试 编辑时间&#xff1a;2024/11/13 软件测试基础知识 软件测试定义和测试分类 软件是计算机程序、程序所用的数据以及有关文档资料的集合。 软件测试分类 按测试执行阶段划分 单元测试、集成测试、系统测试、验收测试 是否运行程序划分 动态测试、静态测试…

pycharm中from[本地包]import文件/模块出现问题(最最最全方法!)

1.通过PYTHONPATH的方法在此处将路径添加上&#xff0c;能够让IDE访问得到。 2.通过选中目标文件所在的文件的文件夹单击右键&#xff0c;如下图所示可以看到下方的mark directory as选项中存在 存在excluded&#xff0c;选择此项可解决问题&#xff0c;如果仍有问题可以尝试其…

【日志】Unity——Roll-A-Ball(二)

2024.11.13 【Unity】 3.搭建游戏场景 4.设置可拾取物品 4.1设置可拾取方块 给予一定的变化和颜色 编写方块旋转脚本Rotator.cs using System.Collections; using System.Collections.Generic; using UnityEngine;public class Rotator : MonoBehaviour {// Update is cal…

机器学习(1)线性回归

前言   线性回归算法是机器学习深度学习入门的必学的算法&#xff0c;其算法原理虽然简单&#xff0c;但是却蕴含着机器学习中的一些重要的基本思想。许多功能更为强大的非线性模型可在线性模型的基础上通过引入层级结构或高维映射而得。同时机器学习深度学习的核心思想就是优…

CSS:导航栏三角箭头

用CSS实现导航流程图的样式。可根据自己的需求进行修改&#xff0c;代码精略的写了一下。 注&#xff1a;场景一和场景二在分辨率比较低的情况下会有一个1px的缝隙不太优雅&#xff0c;自行处理。有个方法是直接在每个外面包一个DIV&#xff0c;用动态样式设置底色。 场景一、…

Redis设计与实现 学习笔记 第十七章 集群

Redis集群是Redis提供的分布式数据库方案&#xff0c;集群通过分片&#xff08;sharding&#xff0c;水平切分&#xff09;来进行数据共享&#xff0c;并提供复制和故障转移功能。 17.1 节点 一个Redis集群通常由多个节点&#xff08;node&#xff09;组成&#xff0c;在刚开…

(11)(2.1.7) FETtec OneWire ESCs(二)

文章目录 前言 3 组态 4 可选功能 5 SITL模拟 6 故障排除 前言 &#xff01;Note 此功能在固件版本4.1.1及更高版本上可用。 3 组态 FTW掩码 SERVO_FTW_MASK 参数选择将哪些伺服输出&#xff08;如果有的话&#xff09;路由到 FETtec ESC。更改此参数后需要重新启动。…

Python Bokeh 数据可视化教程

Python Bokeh 数据可视化教程 引言 在数据科学和分析的过程中&#xff0c;数据可视化是一个至关重要的环节。它不仅能帮助我们更好地理解数据&#xff0c;还能在报告和展示中提升数据的可读性和吸引力。Python 作为数据科学的主要工具之一&#xff0c;提供了多种数据可视化库…

(免费领源码)java#SSM#mysql高校就业数据可视化管理系统的设计与实现81461-计算机毕设 原创

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对高校就业管理等问题&#xff0c;对高校就业…

wordcloud库基本介绍

文章目录 wordcloud库概述wordcloud库的安装 wordcloud库使用说明配置对象参数 wordcloud应用实例实例: 政府工作报告词云 wordcloud库概述 wordcloud是优秀的词云展示第三方库 词云以词语为基本单位,更加直观和艺术地展示文本 wordcloud库的安装 (cmd命令行) pip install …

替换OpenTSDB和HBase,宝武集团使用IoTDB助力钢铁设备智能运维

时序数据库 IoTDB 应用于宝武集团全基地钢铁时序数据管理&#xff0c;激活数据资产&#xff0c;赋能大型设备智能运维。 1. 背景概述 宝武装备智能科技有限公司&#xff08;以下简称&#xff1a;宝武智维&#xff09;是中国宝武设备智能运维专业化平台公司&#xff0c;30 余年始…

面试_ABtest原理简介

01 什么是ABtest ABtest来源于假设检验&#xff0c;现有两个随机均匀的有样本组A、B&#xff0c;对其中一个组A做出某种改动&#xff0c;实验结束后分析两组用户行为数据&#xff0c;通过显著性检验&#xff0c;判断这个改动对于我们所关注的核心指标是否有显著的影响&#xf…

Anolis8.2系统中搭建python环境

文章目录 安装依赖项依赖项介绍 下载python源码包安装python源码包 安装依赖项 [rootPython ~]# dnf install -y gcc make zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel xz-devel libffi-devel uuid-devel libnsl2-d…

Gurobi学术版+Anaconda安装步骤

注意&#xff1a;在anaconda虚拟环境中安装gurobi库是不需要在本地下载gurobi这个软件的&#xff0c;只需要conda install gurobi即可&#xff0c;或者指定版本的安装conda install -c gurobi gurobi11.0.3。 step0&#xff1a;安装ananconda step1&#xff1a;获得学术许可&a…

OBOO鸥柏:旗下户外景区自助触摸查询一体机已布局智慧城市便民

OBOO鸥柏作为户外自助查询一体机制造商品牌源头工厂一体化&#xff0c;鸥柏室外液晶广告屏凭借其独特的展览展示交互式查询互动优势&#xff0c;OBOO鸥柏正逐渐成为城市公共服务与商业信息查询的商用及工业液晶显示终端机新标杆。基于智慧城市便民化布局主要体现于以下几点&…

Mysql每日一题(分组+select嵌套查询)

本题我通过自己的努力&#xff0c;利用多个知识点&#xff0c;完成了本题&#xff0c;目前还没有查看题解&#xff0c;一会会给出别人题解的方法&#xff0c;自己写的代码就很很繁琐很麻烦&#xff0c;如果是大佬&#xff0c;可能知道这一题就直接这个窗口函数加这个窗口函数就…

Java并发篇--线程池

线程池 为什么要创建线程池 因为CPU核心数量有限,如果每来一个任务就创建一个线程,就会使线程数远远多于CPU核心数,使线程上下文切换过于频繁,会导致系统性能降低。而且每创建一个线程都会占用一定的内存,如果每来一个任务就创建一个线程,内存消耗太大了。 ThreadPoolExecuto…

ubontu--cuDNN安装

1. 下载 cuDNN https://developer.nvidia.com/cudnn 2. 拷贝到服务器/home/<username>文件夹下 解压缩到当前文件夹&#xff1a; tar -xvf cudnn-linux-x86_64-9.5.1.17_cuda11-archive.tar.xz复制头文件和库文件到cuda安装目录/usr/local/cuda/ sudo cp /home/usern…