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,一经查实,立即删除!

相关文章

C++设计模式和编程框架两种设计元素的比较与相互关系

C设计模式和编程框架都是软件设计中的重要元素&#xff0c;它们各自具有独特的特点和应用场景。以下是对它们的比较&#xff0c;并通过举例说明它们的关系。 C设计模式 定义 设计模式是在软件设计中反复出现的特定问题的解决方案。它们是经过验证的、可复用的设计方案&#…

约束(MYSQL)

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

MySQL 权限困境:从权限丢失到权限重生的完整解决方案20241108

&#x1f6e0;️ MySQL 权限困境&#xff1a;从权限丢失到权限重生的完整解决方案 引言 在使用 MySQL 的过程中&#xff0c;我们常常会遇到权限设置不当或丢失的问题&#xff0c;特别是在初次安装或配置更改后。这种权限困境的修复不仅复杂&#xff0c;而且往往是环环相扣&…

笔记 | 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 三、右值引用和移…

【WebRTC】视频发送链路中类的简单分析(下)

目录 1.任务队列节流发送器&#xff08;TaskQueuePacedSender&#xff09;1.1 节流控制器添加RTP数据包&#xff08;PacingController::EnqueuePacket()&#xff09;1.2 监测是否要处理Packet&#xff08;PacingController::MaybeProcessPackets()&#xff09; 2.数据包路由&am…

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 代码框架提供了最常用和最基础的功能实现。 全局函数主要包含了对时间…

Spark 中的 RDD 分区的设定规则与高阶函数、Lambda 表达式详解

Spark 的介绍与搭建&#xff1a;从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 Spark on YARN&#xff1a;Spark集群模式…

Spring学习笔记_30——事务接口PlatformTransactionManager

PlatformTransactionManager是Spring框架中事务管理的核心接口&#xff0c;它负责管理事务的创建、提交和回滚等操作。 源码 /** Copyright 2002-2020 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you m…

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

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

Python小游戏25——黄金矿工

首先&#xff0c;你需要安装Pygame库。 如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 【python】代码展示 import pygame import random # 初始化Pygame pygame.init() # 设置屏幕尺寸 screen_width 800 screen_height 60…

鸿蒙5.0版开发:订阅卡死事件(ArkTS)

在HarmonyOS 5.0中&#xff0c;开发者可以通过ArkTS订阅应用的卡死事件&#xff0c;以便在应用出现卡死时进行相应的处理。这不仅可以帮助开发者监控应用的稳定性&#xff0c;还可以在问题发生时快速定位问题原因。本文将详细介绍如何在ArkTS中订阅卡死事件&#xff0c;并提供示…

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免密登录 三…

衡石科技BI如何助力企业实现数字化转型

在当今数字化浪潮席卷全球的背景下&#xff0c;企业纷纷寻求通过数字化转型来降低成本、提升效率、优化决策&#xff0c;从而在激烈的市场竞争中脱颖而出。衡石科技&#xff0c;作为一家专注于商业智能&#xff08;BI&#xff09;与数据分析的创新型企业&#xff0c;通过其先进…