Robot Operating System——Action通信机制的服务端

news2024/9/22 15:44:04

大纲

  • 回调
    • 接受或者拒绝请求
    • 执行任务的回调
    • 终止任务回调
  • 创建服务
  • 完整代码
  • 总结

在《Robot Operating System——Action通信机制概念及Client端》一文中,我们介绍了Action客户端的主要流程。本文我们将介绍Action服务端的编写。

回顾下Action的构成:

  1. 目标(Goal):这是由Action客户端(Client)发送给Action服务器(Server)的请求,指示服务器需要执行的具体任务。例如,在控制一个机器人旋转的任务中,目标可能是指定机器人需要旋转的角度。
  2. 反馈(Feedback):在执行目标的过程中,Action服务器会定期向客户端发送反馈,以报告当前的执行状态和进度。这种反馈机制使得客户端能够实时了解任务的执行情况,并在必要时进行干预或调整。
  3. 结果(Result):当目标执行完成后,Action服务器会向客户端发送一个结果,表明任务的最终执行状态。这个结果可以是成功完成、失败或者部分完成等状态信息。

我们看到Action客户端通过设置与上述组成对应的回调函数来实现整体功能。

但是Action的服务端则不是如此。它也由三部分组成,分别是:

  1. 处理Client发送请求的回调。用于表示是“接受”还是“拒绝”该请求。
  2. 处理Client发送“终止”请求任务的回调。Action服务端在处理任务时,是可以接收客户端发送的“终止”指令的。这个回调可以“接受”还是“拒绝”该请求。
  3. 处理执行任务的回调。这个就是主要的处理逻辑。

我们通过action_tutorials/action_tutorials_cpp/src/fibonacci_action_server.cpp看下其具体实现。

回调

接受或者拒绝请求

class FibonacciActionServer : public rclcpp::Node
{
public:
  using Fibonacci = action_tutorials_interfaces::action::Fibonacci;
  using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;

  ACTION_TUTORIALS_CPP_PUBLIC
  explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
  : Node("fibonacci_action_server", options)
  {
    using namespace std::placeholders;

    auto handle_goal = [this](
      const rclcpp_action::GoalUUID & uuid,
      std::shared_ptr<const Fibonacci::Goal> goal)
      {
        (void)uuid;
        RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order);
        // The Fibonacci action uses int32 for the return of sequences, which means it can only hold
        // 2^31-1 (2147483647) before wrapping negative in two's complement. Based on empirical
        // tests, that means that an order of > 46 will cause wrapping, so we don't allow that here.
        if (goal->order > 46) {
          return rclcpp_action::GoalResponse::REJECT;
        }
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
      };

本例中,handle_goal会根据客户端传递过来的order值来判断是否“接受”或“拒绝”处理该请求。
如果拒绝就返回rclcpp_action::GoalResponse::REJECT;如果接受,可以返回rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE或者rclcpp_action::GoalResponse::ACCEPT_AND_DEFER。
ACCEPT_AND_EXECUTE表示该回调后会立即调用执行任务的回调;ACCEPT_AND_DEFER表示过会儿再调用。

执行任务的回调

    auto handle_accepted = [this](
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        // this needs to return quickly to avoid blocking the executor,
        // so we declare a lambda function to be called inside a new thread
        auto execute_in_thread = [this, goal_handle]() {return this->execute(goal_handle);};
        std::thread{execute_in_thread}.detach();
      };

由于我们处理任务的回调执行时间很长(它内部要Sleep一段时间),所以为了让该回调可以立即返回,不阻碍后续流程,于是主流程会放在线程中执行。

  ACTION_TUTORIALS_CPP_LOCAL
  void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
  {
    RCLCPP_INFO(this->get_logger(), "Executing goal");
    rclcpp::Rate loop_rate(1);
    const auto goal = goal_handle->get_goal();
    auto feedback = std::make_shared<Fibonacci::Feedback>();
    auto & sequence = feedback->partial_sequence;
    sequence.push_back(0);
    sequence.push_back(1);
    auto result = std::make_shared<Fibonacci::Result>();

我们先构建Action客户端2个回调函数需要的参数:

  • feedback用于告知客户端FeedbackCallback回调结果。
  • result用于告知客户端ResultCallback回调结果。
    for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
      // Check if there is a cancel request
      if (goal_handle->is_canceling()) {
        result->sequence = sequence;
        goal_handle->canceled(result);
        RCLCPP_INFO(this->get_logger(), "Goal canceled");
        return;
      }
      // Update sequence
      sequence.push_back(sequence[i] + sequence[i - 1]);
      // Publish feedback
      goal_handle->publish_feedback(feedback);
      RCLCPP_INFO(this->get_logger(), "Publish feedback");

      loop_rate.sleep();
    }

然后For循环中会不停计算斐波那契数列。在计算过程中,通过GoalHandleFibonacci::is_canceling检查是否服务端接受了客户端“终止”任务的请求。如果终止,则会通过GoalHandleFibonacci::canceled返回Result。这样客户端的ResultCallback回调会收到结果,且状态是rclcpp_action::ResultCode::CANCELED。
在这里插入图片描述
如果没有终止,就会通过GoalHandleFibonacci::publish_feedback来将中间过程返回给客户端。这样客户端的FeedbackCallback回调会收到响应。
在这里插入图片描述
如果任务执行结束,就调用GoalHandleFibonacci::succeed来返回最终结果。

    // Check if goal is done
    if (rclcpp::ok()) {
      result->sequence = sequence;
      goal_handle->succeed(result);
      RCLCPP_INFO(this->get_logger(), "Goal succeeded");
    }
  }

终止任务回调

    auto handle_cancel = [this](
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
      };

本例中,我们直接接受了客户端的“终止”请求,返回了rclcpp_action::CancelResponse::ACCEPT。现实中,我们还可以通过rclcpp_action::CancelResponse::REJECT来拒绝“终止”请求,让任务继续进行。

如果这个回调我们返回了rclcpp_action::CancelResponse::ACCEPT,则GoalHandleFibonacci::is_canceling会返回true。这样主处理流程会做相应的动作来“终止”任务。

创建服务

最后我们只要将上述3个回调传递给rclcpp_action::create_server来构造对象即可。

    this->action_server_ = rclcpp_action::create_server<Fibonacci>(
      this,
      "fibonacci",
      handle_goal,
      handle_cancel,
      handle_accepted);
  }

private:
  rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;

完整代码

// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <memory>

#include "action_tutorials_interfaces/action/fibonacci.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "rclcpp_components/register_node_macro.hpp"

#include "action_tutorials_cpp/visibility_control.h"

namespace action_tutorials_cpp
{
class FibonacciActionServer : public rclcpp::Node
{
public:
  using Fibonacci = action_tutorials_interfaces::action::Fibonacci;
  using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;

  ACTION_TUTORIALS_CPP_PUBLIC
  explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
  : Node("fibonacci_action_server", options)
  {
    using namespace std::placeholders;

    auto handle_goal = [this](
      const rclcpp_action::GoalUUID & uuid,
      std::shared_ptr<const Fibonacci::Goal> goal)
      {
        (void)uuid;
        RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order);
        // The Fibonacci action uses int32 for the return of sequences, which means it can only hold
        // 2^31-1 (2147483647) before wrapping negative in two's complement. Based on empirical
        // tests, that means that an order of > 46 will cause wrapping, so we don't allow that here.
        if (goal->order > 46) {
          return rclcpp_action::GoalResponse::REJECT;
        }
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
      };

    auto handle_cancel = [this](
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
      };

    auto handle_accepted = [this](
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        // this needs to return quickly to avoid blocking the executor,
        // so we declare a lambda function to be called inside a new thread
        auto execute_in_thread = [this, goal_handle]() {return this->execute(goal_handle);};
        std::thread{execute_in_thread}.detach();
      };

    this->action_server_ = rclcpp_action::create_server<Fibonacci>(
      this,
      "fibonacci",
      handle_goal,
      handle_cancel,
      handle_accepted);
  }

private:
  rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;

  ACTION_TUTORIALS_CPP_LOCAL
  void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
  {
    RCLCPP_INFO(this->get_logger(), "Executing goal");
    rclcpp::Rate loop_rate(1);
    const auto goal = goal_handle->get_goal();
    auto feedback = std::make_shared<Fibonacci::Feedback>();
    auto & sequence = feedback->partial_sequence;
    sequence.push_back(0);
    sequence.push_back(1);
    auto result = std::make_shared<Fibonacci::Result>();

    for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
      // Check if there is a cancel request
      if (goal_handle->is_canceling()) {
        result->sequence = sequence;
        goal_handle->canceled(result);
        RCLCPP_INFO(this->get_logger(), "Goal canceled");
        return;
      }
      // Update sequence
      sequence.push_back(sequence[i] + sequence[i - 1]);
      // Publish feedback
      goal_handle->publish_feedback(feedback);
      RCLCPP_INFO(this->get_logger(), "Publish feedback");

      loop_rate.sleep();
    }

    // Check if goal is done
    if (rclcpp::ok()) {
      result->sequence = sequence;
      goal_handle->succeed(result);
      RCLCPP_INFO(this->get_logger(), "Goal succeeded");
    }
  }
};  // class FibonacciActionServer

}  // namespace action_tutorials_cpp

RCLCPP_COMPONENTS_REGISTER_NODE(action_tutorials_cpp::FibonacciActionServer)

总结

  • Action服务端主要由3个回调构成:
    • 是否接受执行任务请求的handle_goal;
    • 是否接受终止任务请求的handle_cancel;
    • handle_goal中返回rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE或者rclcpp_action::GoalResponse::ACCEPT_AND_DEFER后执行的handle_accepted。
  • Action客户端主要由3个回调构成:
    • 请求Action服务端handle_goal回调返回结果的goal_response_callback。用于知晓服务端是接受了还是拒绝了该任务请求。
    • 用于接收Action服务端在handle_accepted中通过调用GoalHandleFibonacci::publish_feedback回传过程的 feedback_callback。
    • 用于接收接收Action服务端在handle_accepted中通过调用GoalHandleFibonacci::canceled或者GoalHandleFibonacci::succeed的result_callback。
  • Action服务端的handle_goal返回结果会决定了handle_accepted是否执行。
  • Action服务端的handle_cancel返回结果会决定GoalHandleFibonacci::is_canceling的返回结果,进而可以干涉主任务流程的处理逻辑。

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

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

相关文章

cesium canvas广告牌

在有些业务中&#xff0c;对场景中的广告牌样式要求比较高&#xff0c;需要动态显示一些数据&#xff0c;这个时候&#xff0c;我们可以通过将复杂背景样式制作成图片&#xff0c;通过canvas绘制图片和动态数据&#xff0c;从而达到比较好的显示效果。 1 CanvasMarker 类封装 …

ICM-20948芯片详解(2)

接前一篇文章&#xff1a;ICM-20948芯片详解&#xff08;1&#xff09; 二、详述 ICM-20948是一款9轴运动跟踪设备&#xff0c;全部采用3x3x1mm QFN封装。ICM-20948是一个多芯片模块&#xff08;MCM&#xff09;&#xff0c;由集成在单个QFN封装中的两个管芯组成。一个芯片内装…

2024年【制冷与空调设备运行操作】考试技巧及制冷与空调设备运行操作考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作考试技巧考前必练&#xff01;安全生产模拟考试一点通每个月更新制冷与空调设备运行操作考试试题题目及答案&#xff01;多做几遍&#xff0c;其实通过制冷与空调设备运行操作作业模拟考试很简…

leetcode173. 二叉搜索树迭代器,注意vector中的size()的无符号整数类型,无符号整数和有符号整数的加减比大小有着种种大坑

leetcode173. 二叉搜索树迭代器 实现一个二叉搜索树迭代器类BSTIterator &#xff0c;表示一个按中序遍历二叉搜索树&#xff08;BST&#xff09;的迭代器&#xff1a; BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分…

【MySQL】全面剖析索引失效、回表查询与索引下推

1.索引失效的情况 以tb_user表举例&#xff0c;id为主键索引、name和phone字段上建立了一个普通索引&#xff0c;name和phone均为varchar类型。 索引列运算 当在 WHERE 子句或 JOIN 子句中对列使用函数或表达式时&#xff0c;索引会失效。 执行以下语句&#xff0c;可以发现执…

分布式事务原理

目录 第一节&#xff1a;分布式事务基础详细总结 1.1 事务的核心特性&#xff08;ACID&#xff09; 1.2 分布式事务的挑战 1.3 分布式事务的实现难点 1.4 分布式事务解决方案概览 图解&#xff1a;分布式事务的ACID特性 第二节&#xff1a;事务消息方案详细总结 2.1 事务…

AUTOSAR实战教程-使用DET来发现开发错误

2年之前因为在调试AUTOSAR存储协议栈的时候使用DET并没发现有用的信息,所以就武断下结论--这玩意没有用。活到老学到老吧,bug经历的多了,发现这玩意还挺有用的。说一下这个bug的背景。 在将时间同步报文改道CanTsync之后,由于这个AUTOSAR工具本身的问题以及配置工程师本身的…

SpringBoot校园社团场地租借平台开发-计算机毕业设计源码00746

摘 要 这个项目旨在开发一个基于SpringBoot 的校园社团场地租借平台&#xff0c;帮助学校社团更方便地租借校园内的场地。用户可以在平台上浏览不同场地的信息、预订场地、查看租借历史记录等。管理员可以管理场地信息、审批租借申请和生成报表统计等功能。通过该平台&#xff…

【大模型实战篇】搭建本地的隐私计算知识问答系统“密答”

1. 背景介绍 在之前分享的文章《基于开源大模型的问答系统本地部署实战教程》中&#xff0c;我们介绍了基于ollama实现本地问答系统的部署和使用。本文将基于ollama进一步实现本地垂直领域的问答系统搭建。ollama作为大模型的运行框架&#xff0c;可以提供大模型的使用接口…

Python 设计模式之建造者模式

文章目录 建造者模式中的简单版本逐渐复杂的问题建造者模式的实现 建造者模式中的经典版本 建造者&#xff08;builder&#xff09;模式属于创建型模式&#xff0c;建造者模式一般有两种类型的应用 建造者模式中的简单版本 逐渐复杂的问题 假设现在需要创建一个用户对象&…

如何提高小红书种草转化率

企业和品牌方想要在小红书上做种草推广一般分为图文种草和视频种草两种形式&#xff0c;而且小红书是一个完全可以依靠内容实现涨粉、变现、种草转化的平台&#xff01; 因为小红书是算法推荐制&#xff0c;将你的作品放在流量池中检测&#xff0c;满足要求就能进入下一个流量池…

Python酷库之旅-第三方库Pandas(061)

目录 一、用法精讲 236、pandas.Series.explode方法 236-1、语法 236-2、参数 236-3、功能 236-4、返回值 236-5、说明 236-6、用法 236-6-1、数据准备 236-6-2、代码示例 236-6-3、结果输出 237、pandas.Series.searchsorted方法 237-1、语法 237-2、参数 237-…

Linux 内核源码分析---插入和删除模块

模块是一种向 Linux 内核添加设备驱动程序、文件系统及其他组件的有效方法&#xff0c;不需编译新内核或重启系统。 模块具有如下优点&#xff1a; • 通过使用模块&#xff0c;内核发布者能够预先编译大量驱动程序&#xff0c;而不会致使内核映像的尺寸发生膨胀&#xff1b; …

PTA—基础编程题目集(7-18)

7-18 二分法求多项式单根 目录 题目描述 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 参考代码 总结 题目描述 输入在第1行中顺序给出多项式的4个系数a3​、a2​、a1​、a0​&#xff0c;在第2行中顺序给出区间端点a和b。题目保证多项式在给定区间…

60_1简单的学生管理系统【功能实现(查看所有学生(分页)、修改和删除学生信息)】

功能实现 老师角色查看所有学生 获取学生列表和分页 1.修改index.jsp 不能直接跳stuList.jsp&#xff0c;没数据 <%if("teacher".equals(role)){%><a href"TeaInitModifyServlet?username<%username%>">修改信息</a><a href…

设施农业“AutoML“时代:大模型自动调参,让农业算法模型更简单易用

&#xff08;于景鑫 北京市农林科学院智能装备技术研究中心&#xff09;设施农业是现代农业的重要发展方向,但在数字化、智能化的进程中仍面临诸多挑战。传统的农业算法模型虽然可以为设施农业提供一定的决策支持,但在实际应用中往往受限于参数调优复杂、模型泛化能力差等因素。…

<Rust><iced>基于rust使用iced构建GUI实例:一个CRC16校验码生成工具

前言 本专栏是Rust实例应用。 环境配置 平台:windows 软件:vscode 语言:rust 库:iced、iced_aw 概述 本文是专栏第五篇实例,是一个CRC16校验码转换程序。 本篇内容: 1、CRC16校验码生成 代码介绍 本文的crc16校验码生成工具,主要设计两个方面,一个是crc16 modbus…

PADS Router 扇出失败问题详细解决方法。

第一步&#xff1a;确定单位是一致的,我的单位是 “密尔”&#xff0c;不是“公制”。 第二步&#xff1a;进去pads router 右键选择特性&#xff0c;注意&#xff0c;是右键点击任意板框内空白位置的特性&#xff0c;这个是涵盖整体的设置&#xff0c;和单独点击一个元器件选…

react-native从入门到实战系列教程一Swiper组件的使用及bug修复

轮播图&#xff0c;在app中随处可见&#xff0c;这么重要的功能我们怎么可能不学习下在react-native中的实现方式。 依然是第三方组件react-native-swiper 官网地址 https://www.npmjs.com/package/react-native-swiper 组件使用的组件及事件参考官方即可。 实现效果 官网…

文件审查流程:使用指南

当您正在处理一个项目并且必须进行文档审查时&#xff0c;您可能会对这个过程到底涉及什么、谁是利益相关者以及审查过程的结果可能是什么感到困惑。在这篇博客文章中&#xff0c;让我们简单介绍一下文档审核过程及其对高质量内容的活力。 文件审查的定义 文件审查是文件经过…