当前位置: 首页 > news >正文

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  编译

在这里插入图片描述

相关文章:

  • 电子数字灯LED管变化数量
  • 计算机找不到api-ms-win-core-path-l1-1-0的5种解决方法
  • 解决ts报错:类型“entry”上不存在属性“$AppTools”
  • 【REST2SQL】12 REST2SQL增加Token生成和验证
  • YOLOv8.1.0安装
  • java实现文件下载(前端传文件后端以流的方式进行输出)
  • 专业140+总分430+西南交通大学924信号与系统考研经验电子信息与通信工程,真题,大纲,参考书
  • 【面试题】Rocketmq面试题总结
  • 图形库实战丨C语言扫雷小游戏(超2w字,附图片素材)
  • Golang pprof 分析程序的使用内存和执行时间
  • Hack The Box-Crafty
  • Linux——文件重定向
  • 数据结构从入门到精通——队列
  • 基于springboot实现数据资产管理系统 项目【项目源码+论文说明】
  • java 9及更高版操作和查询本地进程中信息:如关闭window或Linux中的进程
  • 时间复杂度分析经典问题——最大子序列和
  • 【EOS】Cleos基础
  • 77. Combinations
  • Brief introduction of how to 'Call, Apply and Bind'
  • Electron入门介绍
  • Git 使用集
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • Iterator 和 for...of 循环
  • miaov-React 最佳入门
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Spark学习笔记之相关记录
  • storm drpc实例
  • TypeScript实现数据结构(一)栈,队列,链表
  • 从零开始在ubuntu上搭建node开发环境
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 经典排序算法及其 Java 实现
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​linux启动进程的方式
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • !$boo在php中什么意思,php前戏
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #QT(TCP网络编程-服务端)
  • (+4)2.2UML建模图
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (接口自动化)Python3操作MySQL数据库
  • (六)c52学习之旅-独立按键
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (强烈推荐)移动端音视频从零到上手(下)
  • (三) diretfbrc详解
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一)kafka实战——kafka源码编译启动
  • (原創) 物件導向與老子思想 (OO)
  • (转)视频码率,帧率和分辨率的联系与区别
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .bat文件调用java类的main方法
  • .jks文件(JAVA KeyStore)
  • .Net MVC4 上传大文件,并保存表单