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.mzph.cn/diannao/60480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

约束(MYSQL)

not null&#xff08;非空&#xff09; unique&#xff08;唯一&#xff09; default&#xff08;默认约束&#xff0c;规定值&#xff09; 主键约束primary key&#xff08;非空且唯一&#xff09; auto_increment&#xff08;自增类型&#xff09; 复合主键 check&#xff08…

笔记 | image may have poor performance,or fail,if run via emulation

在Docker Desktop中现象如图&#xff1a; 当你运行 AMD64 平台代码时&#xff08;Intel 和 AMD 芯&#xff09;&#xff0c;你的 Mac 必须模拟其CPU架构&#xff08;因为你自身是ARM&#xff09;。这通常会非常吃性能。 Docker Desktop 警告你在模拟 Intel/AMD x64 CPU 时性能可…

【C++】C++11特性(上)

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;C 个人主页&#xff1a;Celias blog~ 目录 一、列表初始化 二、std::initializer_list 三、右值引用和移…

24/11/7 算法笔记 PCA主成分分析

假如我们的数据集是n维的&#xff0c;共有m个数据(x,x,...,x)。我们希望将这m个数据的维度从n维降到k维&#xff0c;希望这m个k维的数据集尽可能的代表原始数据集。我们知道数据从n维降到k维肯定会有损失&#xff0c;但是我们希望损失尽可能的小。那么如何让这k维的数据尽可能表…

JS 实现SSE通讯和了解SSE通讯

SSE 介绍&#xff1a; Server-Sent Events&#xff08;SSE&#xff09;是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比&#xff0c;SSE提供了更高效和实时的数据推送机制。 SSE基于HTTP协议&#xff0c;允许服务器将数据以事件流&#xff08;…

C/C++每日一练:查找链表的中间节点

链表&#xff08;Linked List&#xff09; 链表是一种线性数据结构&#xff0c;由一系列节点&#xff08;Node&#xff09;通过指针链接在一起。与数组不同&#xff0c;链表中的元素在内存中不需要连续存储&#xff0c;每个节点包含两部分&#xff1a; 数据部分&#xff1a;存…

对称加密与非对称加密:密码学的基石及 RSA 算法详解

对称加密与非对称加密&#xff1a;密码学的基石及 RSA 算法详解 在当今数字化的时代&#xff0c;信息安全至关重要。对称加密和非对称加密作为密码学中的两种基本加密技术&#xff0c;为我们的数据安全提供了强大的保障。本文将深入探讨对称加密和非对称加密的特点、应用场景&…

PH47代码框架全局函数及功能类

PH47代码框架全局函数及功能类 概述 全局函数及功能类体系是PH47框架当中除了4个逻辑层之外最重要的组成部分之一&#xff0c;它们可以在 整个PH7 代码框架及用户代码中使用。常用全局函数及功能类为 PH7 代码框架提供了最常用和最基础的功能实现。 全局函数主要包含了对时间…

力扣 LeetCode 203. 移除链表元素(Day2:链表)

解题思路&#xff1a; 方法一&#xff1a;头节点和非头节点分开处理 方法二&#xff1a;定义一个dummy虚拟节点&#xff0c;后面的节点就可以采用相同的处理方式 注意&#xff1a; cur需要指向要删除的节点的上一个节点&#xff0c;因为要越过这一个被删除的节点 class Sol…

IEC60870-5-104 协议源码架构详细分析

IEC60870-5-104 协议源码架构 前言一、资源三、目录层级一二、目录层级二config/lib60870_config.hdependencies/READMEexamplesCMakeLists.txtcs101_master_balancedcs104_client_asyncmulti_client_servertls_clienttls_server说明 make这些文件的作用是否需要导入这些文件&a…

turbo c 2.0 画螺栓

代码; #include<graphics.h> void bolt(x0,y0,d,l) int x0,y0,d,l; {int x1,x2,x3,x4,x5,x6,x7,x8;int y1,y2,y3,y4,y5,r1,r2,b,c;if(l>2*d) b2*d;else b1;r11.5*d;r20.38*d;c0.1*d;x1x0-0.7*d;x2x0-0.61*d;x3x0-0.32*d;x4x00.8*d;x5x0l-b;x6x0l-c;x7x0l-0.05*d;x8x0…

网络服务综合项目-博客

一、运行环境&#xff1a; 主机主机名系统服务192.168.31.128Server-WebLinuxWeb192.168.31.129Server-NFS-DNSLinuxNFS 二、基础配置&#xff1a; 配置主机名开启防火墙并配置部分开启selinux并配置服务器之间使用ntp.aliyun.com进行时间同步服务器之间实现ssh免密登录 三…

leetcode86:分隔链表

给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x 3 输出&am…

Android Mobile Network Settings | APN 菜单加载异常

问题 从log看是有创建APN对应的Controller&#xff08;功能逻辑是ok的&#xff09;&#xff0c;但是Mobile Network Settings无法显示&#xff08;UI异常&#xff09;。 日志分析 看似APN 菜单已经创建了&#xff0c;实际上并没有显示。 11-12 07:01:28.150 8773 8773 D Pr…

上海市计算机学会竞赛平台2020年4月月赛丙组永恒的生命游戏

题目背景 2020年4月11日&#xff0c;英国数学家 约翰霍顿康威&#xff08;John Horton Conway&#xff09;因为新型冠状病毒肺炎不幸逝世。他在群论、数论、代数、几何拓扑、理论物理、组合博弈论和几何等领域&#xff0c;都做出了重大贡献。他的离去是人类文明的损失。他最著…

FS8x 功能安全

fail-safe是电独立的和物理隔离的。fail-safe由自己的参考电压和电流提供,有自己的振荡器,有重复的模拟路径以最小化常见的故障,并有LBIST/ABIST来覆盖潜在故障。fail-safe根据设备部件号提供ASIL B或ASIL D遵从性。除非另有规定,fail-safe定时来自故障安全振荡器,其精度为…

项目模块十七:HttpServer模块

一、项目模块设计思路 目的&#xff1a;实现HTTP服务器搭建 思想&#xff1a;设计请求路由表&#xff0c;记录请求方法与对应业务的处理函数映射关系。用户实现请求方法和处理函数添加到路由表&#xff0c;服务器只接受请求并调用用户的处理函数即可。 处理流程&#xff1a; …

内网域环境、工作组、局域网等探针方案

1. 信息收集 1.1 网络收集 了解当前服务器的计算机基本信息&#xff0c;为后续判断服务器角色&#xff0c;网络环境做准备 systeminfo 详细信息 net start 启动服务 tasklist 进程列表 schtasks 计划任务&#xff08;受权限影响&#xff09; 了解当前服务器的网络接口信息…

什么是量化交易

课程大纲 内容初级初识量化&#xff0c;理解量化 初识量化 传统量化和AI量化的区别 量化思想挖掘 量化思想的挖掘及积累技巧 量化代码基础&#xff1a; python、pandas、SQL基础语法 金融数据分析 常用金融分析方式 常用因子分析方式 数据分析实战练习 回测及交易引擎 交易引擎…

OpenHarmony-1.启动流程

OpenHarmony启动流程 1.kernel的启动 流程图如下所示&#xff1a;   OpenHarmony(简称OH)的标准系统的底层系统是linux&#xff0c;所以调用如下代码&#xff1a; linux-5.10/init/main.c: noinline void __ref rest_init(void) {struct task_struct *tsk;int pid;rcu_sch…