ROS2动作通信的实现

文章目录

  • 1.动作通信的概念及应用场景
    • 1.1 概念
    • 1.2 应用场景
  • 2.准备工作
  • 3.动作通信的实现
    • 3.1 动作通信接口消息
    • 3.2 服务端实现
    • 3.3 客户端实现
    • 3.4 编译及运行


1.动作通信的概念及应用场景

1.1 概念

动作通信适用于长时间运行的任务。就结构而言动作通信由目标、反馈和结果三部分组成;就功能而言动作通信类似于服务通信,动作客户端可以发送请求到动作服务端,并接收动作服务端响应的最终结果,不过动作通信可以在请求响应过程中获取连续反馈,并且也可以向动作服务端发送任务取消请求;就底层实现而言动作通信是建立在话题通信和服务通信之上的,目标发送实现是对服务通信的封装,结果的获取也是对服务通信的封装,而连续反馈则是对话题通信的封装。

在这里插入图片描述

1.2 应用场景

机器人导航到某个目标点,此过程需要一个节点A发布目标信息,然后一个节点B接收到请求并控制移动,最终响应目标达成状态信息。

其感觉很像是服务通信的过程
因为需求中要A发送目标,B执行并返回结果,这是一个典型的基于请求响应的应答模式,不过,如果只是使用基本的服务通信实现,存在一个问题:
导航是一个过程,是耗时操作,如果使用服务通信,那么只有在导航结束时,才会产生响应结果,而在导航过程中,节点A是不会获取到任何反馈的,从而可能出现程序"假死"的现象,过程的不可控意味着不良的用户体验,以及逻辑处理的缺陷(比如:导航中止的需求无法实现)。
更合理的方案应该是:导航过程中,可以连续反馈当前机器人状态信息,当导航终止时,再返回最终的执行结果。

一般适用于耗时的请求响应场景,用以获取连续的状态反馈。
提示:这里对文章进行总结:

2.准备工作

老样子:
在这里插入图片描述

3.动作通信的实现

3.1 动作通信接口消息

在这里插入图片描述

3.2 服务端实现

需求:编写动作通信,动作客户端提交一个整型数据N,动作服务端接收请求数据并累加1-N之间的所有整数,将最终结果返回给动作客户端,且每累加一次都需要计算当前运算进度并反馈给动作客户端。

// 1.包含头文件;
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "base_interfaces_demo/action/progress.hpp"
using namespace std::placeholders;
using base_interfaces_demo::action::Progress;
using GoalHandleProgress = rclcpp_action::ServerGoalHandle<Progress>;// 3.定义节点类;
class MinimalActionServer : public rclcpp::Node
{
public:
explicit MinimalActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())  
: Node("minimal_action_server", options)
{    
// 3-1.创建动作服务端;  
this->action_server_ = rclcpp_action::create_server<Progress>(      this,      "get_sum",      std::bind(&MinimalActionServer::handle_goal, this, _1, _2),      std::bind(&MinimalActionServer::handle_cancel, this, _1),      std::bind(&MinimalActionServer::handle_accepted, this, _1));    
RCLCPP_INFO(this->get_logger(),"动作服务端创建,等待请求...");  
}private:  
rclcpp_action::Server<Progress>::SharedPtr action_server_;  
// 3-2.处理请求数据;  
rclcpp_action::GoalResponse handle_goal(const rclcpp_action::GoalUUID & uuid,std::shared_ptr<const Progress::Goal> goal)  
{    
(void)uuid;    
RCLCPP_INFO(this->get_logger(), "接收到动作客户端请求,请求数字为 %ld", goal->num);    
if (goal->num < 1) {      return rclcpp_action::GoalResponse::REJECT;    
}   
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;  
}  // 3-3.处理取消任务请求;  
rclcpp_action::CancelResponse handle_cancel(    const std::shared_ptr<GoalHandleProgress> goal_handle)  
{    (void)goal_handle;    RCLCPP_INFO(this->get_logger(), "接收到任务取消请求");    return rclcpp_action::CancelResponse::ACCEPT;  
}  void execute(const std::shared_ptr<GoalHandleProgress> goal_handle)  
{    
RCLCPP_INFO(this->get_logger(), "开始执行任务");    
rclcpp::Rate loop_rate(10.0);    
const auto goal = goal_handle->get_goal();    
auto feedback = std::make_shared<Progress::Feedback>();    
auto result = std::make_shared<Progress::Result>();    
int64_t sum= 0;    for (int i = 1; (i <= goal->num) && rclcpp::ok(); i++) {      sum += i;      // Check if there is a cancel request      if (goal_handle->is_canceling()) {        result->sum = sum;        goal_handle->canceled(result);        RCLCPP_INFO(this->get_logger(), "任务取消");        return;      }      feedback->progress = (double_t)i / goal->num;      goal_handle->publish_feedback(feedback);      RCLCPP_INFO(this->get_logger(), "连续反馈中,进度:%.2f", feedback->progress);      loop_rate.sleep();    } if (rclcpp::ok()) { 
result->sum = sum; 
goal_handle->succeed(result);
RCLCPP_INFO(this->get_logger(), "任务完成!");
}
}// 3-4.生成连续反馈。  void handle_accepted(const std::shared_ptr<GoalHandleProgress> goal_handle)
{
std::thread{std::bind(&MinimalActionServer::execute, this, _1), goal_handle}.detach();
}
};int main(int argc, char ** argv)
{
// 2.初始化 ROS2 客户端;  
rclcpp::init(argc, argv);
// 4.调用spin函数,并传入节点对象指针;  
auto action_server = std::make_shared<MinimalActionServer>();
rclcpp::spin(action_server);
// 5.释放资源。  
rclcpp::shutdown();
return 0;
} 

3.3 客户端实现

需求:编写动作客户端实现,可以提交一个整型数据到服务端,并处理服务端的连续反馈以及最终返回结果。

#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "base_interfaces_demo/action/progress.hpp"
using base_interfaces_demo::action::Progress;
using GoalHandleProgress = rclcpp_action::ClientGoalHandle<Progress>;
using namespace std::placeholders;// 3.定义节点类;
class MinimalActionClient : public rclcpp::Node
{
public:  
explicit MinimalActionClient(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())  
: Node("minimal_action_client", node_options)  
{    // 3-1.创建动作客户端;    this->client_ptr_ = rclcpp_action::create_client<Progress>(this,"get_sum");    }  // 3-2.发送请求;  void send_goal(int64_t num)  {      if (!this->client_ptr_) {    RCLCPP_ERROR(this->get_logger(), "动作客户端未被初始化。");    }    if (!this->client_ptr_->wait_for_action_server(std::chrono::seconds(10))) {      RCLCPP_ERROR(this->get_logger(), "服务连接失败!");      return;    }    auto goal_msg = Progress::Goal();    goal_msg.num = num;    RCLCPP_INFO(this->get_logger(), "发送请求数据!");    auto send_goal_options = rclcpp_action::Client<Progress>::SendGoalOptions();        send_goal_options.goal_response_callback =std::bind(&MinimalActionClient::goal_response_callback, this, _1);    send_goal_options.feedback_callback =std::bind(&MinimalActionClient::feedback_callback, this, _1, _2);    send_goal_options.result_callback =std::bind(&MinimalActionClient::result_callback, this, _1);    auto goal_handle_future = this->client_ptr_->async_send_goal(goal_msg, send_goal_options);  
}private:
rclcpp_action::Client<Progress>::SharedPtr client_ptr_;// 3-3.处理目标发送后的反馈;  
void goal_response_callback(GoalHandleProgress::SharedPtr goal_handle)  
{    
if (!goal_handle) 
{    RCLCPP_ERROR(this->get_logger(), "目标请求被服务器拒绝!");    } else {     RCLCPP_INFO(this->get_logger(), "目标被接收,等待结果中");    }  
}  // 3-4.处理连续反馈;  
void feedback_callback(GoalHandleProgress::SharedPtr,const std::shared_ptr<const Progress::Feedback> feedback)  
{    int32_t progress = (int32_t)(feedback->progress * 100);      RCLCPP_INFO(this->get_logger(), "当前进度: %d%%", progress);  }// 3-5.处理最终响应。  
void result_callback(const GoalHandleProgress::WrappedResult & result)  
{    switch (result.code) {  case rclcpp_action::ResultCode::SUCCEEDED:        break;      case rclcpp_action::ResultCode::ABORTED:      RCLCPP_ERROR(this->get_logger(), "任务被中止");        return;      case rclcpp_action::ResultCode::CANCELED:        RCLCPP_ERROR(this->get_logger(), "任务被取消");        return;      default:        RCLCPP_ERROR(this->get_logger(), "未知异常");        return;  }    RCLCPP_INFO(this->get_logger(), "任务执行完毕,最终结果: %d", result.result->sum);  }
};int main(int argc, char ** argv)
{  
// 2.初始化 ROS2 客户端;  
rclcpp::init(argc, argv);  
// 4.调用spin函数,并传入节点对象指针;  
auto action_client = std::make_shared<MinimalActionClient>();  
action_client->send_goal(10);  
rclcpp::spin(action_client);
// 5.释放资源。  
rclcpp::shutdown();
return 0;
} 

3.4 编译及运行

CMakeLists怎么该就不说了,前面两个已经说得很清楚了!

colcon build --packages-select cpp03_action  编译

在这里插入图片描述

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

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

相关文章

基于springboot+vue实现高校学生党员发展管理系统项目【项目源码+论文说明】

基于springboot实现高校学生党员发展管理系统演示 摘要 随着高校学生规模的不断扩大&#xff0c;高校内的党员统计及发展管理工作面临较大的压力&#xff0c;高校信息化建设的不断优化发展也进一步促进了系统平台的应用&#xff0c;借助系统平台可以实现更加高效便捷的党员信息…

给定时间求这是本年的第几天

之前我在编写个代码的时候是把它用大量的if逻辑判断语句&#xff0c;我并没有把是闰年这个条件选择出来&#xff0c;只是依据一般的想法—— #include <stdio.h> #define M 13 int main() {/********** Begin **********/int days[M] {0,31,28,31,30,31,30,31,31,30,3…

transformer--使用transformer构建语言模型

什么是语言模型? 以一个符合语言规律的序列为输入&#xff0c;模型将利用序列间关系等特征&#xff0c;输出一个在所有词汇上的概率分布.这样的模型称为语言模型. # 语言模型的训练语料一般来自于文章&#xff0c;对应的源文本和目标文本形如: src1"Ican do",tgt1…

抖音短视频素材哪里找,推荐五个好用的抖音素材网站

不知道你有没有想过一个问题&#xff0c;为什么别人都能找到那种高质量的视频素材&#xff0c;画质特别高清&#xff0c;甚至是4K的内容&#xff0c;而你需要视频素材却不知道去哪里找&#xff1f;网上有各种参差不齐的网站&#xff0c;变着法的想掏空你那本不富裕的腰包。今天…

消息队MQ

文章描述 &#xff1a;&#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a; 消息队列MQ_rabbitMQ搭建 ⏱️ 创作时间&#xff1a;2024年03月9日 ———————————————…

GIS之深度学习08:安装GPU环境下的pytorch

环境&#xff1a; cuda&#xff1a;12.1.1 cudnn&#xff1a;12.x pytorch&#xff1a;2.2.0 torchvision&#xff1a;0.17.0 Python&#xff1a;3.8 操作系统&#xff1a;win &#xff08;本文安装一半才发现pytorch与cuda未对应&#xff0c;重新安装了cuda后才开始的&a…

中探:事件循环相关内容(因为不仅仅是初步认识,但也不至于是深入探讨,所以命名为“中探”)

下面内容写于 2022 年&#xff0c;文本描述过多&#xff0c;可能不适合有经验的人看。新的文章在 个人网站 中。 对了&#xff0c;说到事件循环&#xff0c;怎么可以离开这个最知名的视频呢&#xff01;视频是英文的&#xff0c;但即使你听不懂&#xff0c;单纯看他的操作&…

微信小程序如何实现下拉刷新

1.首先在你需要实现下拉刷新页面的json文件中写入"enablePullDownRefresh": true。 2.在js文件的onPullDownRefresh() 事件中实现下拉刷新。 实现代码 onPullDownRefresh() {console.log(开始下拉刷新)wx.showNavigationBarLoading()//在标题栏中显示加载图标this.d…

【网络原理】使用Java基于UDP实现简单客户端与服务器通信

目录 &#x1f384;API介绍&#x1f338;DatagramSocket&#x1f338;DatagramPacket&#x1f338;InetSocketAddress &#x1f333;回显客户端与服务器&#x1f338;建立回显服务器&#x1f338;回显客户端 ⭕总结 我们用Java实现UDP数据报套接字编程&#xff0c;需要借用以下…

[LeetCode][102]二叉树的层序遍历——遍历结果中每一层明显区分

题目 102. 二叉树的层序遍历 给定二叉树的根节点 root&#xff0c;返回节点值的层序遍历结果。即逐层地&#xff0c;从左到右访问所有节点。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 示例 2&#xff1a; 输入…

MongoDB在Linux环境下的安装与配置

目录 1. 准备工作 2. 安装MongoDB 2.1 传输MongoDB安装包 2.2 解压安装包 2.3 创建MongoDB安装目录 2.4 创建数据目录和日志目录 3. 启动MongoDB服务 3.1 启动MongoDB 3.2 连接MongoDB 3.3 退出MongoDB 1. 准备工作 在安装MongoDB之前&#xff0c;请确保您已具备以下…

10、MongoDB -- MongoDB 的 MongoTemplate 的功能和用法介绍

目录 MongoTemplate 的功能和用法演示前提&#xff1a;登录单机模式的 mongodb 服务器命令登录【test】数据库的 mongodb 客户端命令登录【admin】数据库的 mongodb 客户端命令 为 MongoDB 提供的两个 Starterspring-boot-starter-data-mongodb&#xff08;为以同步方式操作 Mo…