Robot Operating System——单线程中启动多个Node

news2024/9/30 0:28:53

在《Robot Operating System——Service的同步/异步通信》一文中,我们介绍了如何实现Service的客户端和服务端。在例子中,它们分别被编译成libadd_two_ints_client_async_library.so和libadd_two_ints_server_library.so,然后分别被可执行程序add_two_ints_client_async和add_two_ints_server加载。这样它们就以两个独立的进程运行起来。
在这里插入图片描述
在这里插入图片描述
本文我们将介绍,如果在一个进程中,使用单线程模式运行两个Node代码。本文选用的例子是composition/src/manual_composition.cpp、composition/src/client_component.cpp和composition/src/server_component.cpp。

服务端

和之前我们介绍的例子一样,Server需要继承于Node类,然后通过Node::create_service创建服务端,并设置一个处理请求的回调。

// composition/include/composition/server_component.hpp
class Server : public rclcpp::Node
{
public:
  COMPOSITION_PUBLIC
  explicit Server(const rclcpp::NodeOptions & options);

private:
  rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr srv_;
};
// composition/src/server_component.cpp
Server::Server(const rclcpp::NodeOptions & options)
: Node("Server", options)
{
  auto handle_add_two_ints =
    [this](
    const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request,
    std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response
    ) -> void
    {
      RCLCPP_INFO(
        this->get_logger(), "Incoming request: [a: %" PRId64 ", b: %" PRId64 "]",
        request->a, request->b);
      std::flush(std::cout);
      response->sum = request->a + request->b;
    };

  srv_ = create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", handle_add_two_ints);
}

客户端

客户端同样继承于Node。它会在定时器timer_中,使用rclcpp::Client与上述Service进行通信。

// composition/include/composition/client_component.hpp
class Client : public rclcpp::Node
{
public:
  COMPOSITION_PUBLIC
  explicit Client(const rclcpp::NodeOptions & options);

protected:
  void on_timer();

private:
  rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client_;
  rclcpp::TimerBase::SharedPtr timer_;
};

由于使用的是异步通信async_send_request方法,所以我们要使用Future在服务端响应后,将结果打印出来。

// composition/src/client_component.cpp
Client::Client(const rclcpp::NodeOptions & options)
: Node("Client", options)
{
  client_ = create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
  // Note(dhood): The timer period must be greater than the duration of the timer callback.
  // Otherwise, the timer can starve a single-threaded executor.
  // See https://github.com/ros2/rclcpp/issues/392 for updates.
  timer_ = create_wall_timer(2s, [this]() {return this->on_timer();});
}

void Client::on_timer()
{
  if (!client_->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(
        this->get_logger(),
        "Interrupted while waiting for the service. Exiting.");
      return;
    }
    RCLCPP_INFO(this->get_logger(), "Service not available after waiting");
    return;
  }

  auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
  request->a = 2;
  request->b = 3;

  // In order to wait for a response to arrive, we need to spin().
  // However, this function is already being called from within another spin().
  // Unfortunately, the current version of spin() is not recursive and so we
  // cannot call spin() from within another spin().
  // Therefore, we cannot wait for a response to the request we just made here
  // within this callback, because it was executed by some other spin function.
  // The workaround for this is to give the async_send_request() method another
  // argument which is a callback that gets executed once the future is ready.
  // We then return from this callback so that the existing spin() function can
  // continue and our callback will get called once the response is received.
  using ServiceResponseFuture =
    rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture;
  auto response_received_callback = [this](ServiceResponseFuture future) {
      RCLCPP_INFO(this->get_logger(), "Got result: [%" PRId64 "]", future.get()->sum);
    };
  auto future_result = client_->async_send_request(request, response_received_callback);
}

主程序

在主程序中,我们首先需要创建一个SingleThreadedExecutor对象exec。然后将上述两个Node的对象添加到exec。

int main(int argc, char * argv[])
{
  // Force flush of the stdout buffer.
  setvbuf(stdout, NULL, _IONBF, BUFSIZ);

  // Initialize any global resources needed by the middleware and the client library.
  // This will also parse command line arguments one day (as of Beta 1 they are not used).
  // You must call this before using any other part of the ROS system.
  // This should be called once per process.
  rclcpp::init(argc, argv);

  // Create an executor that will be responsible for execution of callbacks for a set of nodes.
  // With this version, all callbacks will be called from within this thread (the main one).
  rclcpp::executors::SingleThreadedExecutor exec;
  rclcpp::NodeOptions options;

  // Add some nodes to the executor which provide work for the executor during its "spin" function.
  // An example of available work is executing a subscription callback, or a timer callback.
  auto server = std::make_shared<composition::Server>(options);
  exec.add_node(server);
  auto client = std::make_shared<composition::Client>(options);
  exec.add_node(client);

  // spin will block until work comes in, execute work as it becomes available, and keep blocking.
  // It will only be interrupted by Ctrl-C.
  exec.spin();

  rclcpp::shutdown();

  return 0;
}

SingleThreadedExecutor::spin()方法在底层会不停检测两个Node的回调是否需要被调用(get_next_executable)。如果有,则调用execute_any_executable方法让这个回调执行。

void
SingleThreadedExecutor::spin()
{
  if (spinning.exchange(true)) {
    throw std::runtime_error("spin() called while already spinning");
  }
  RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););

  // Clear any previous result and rebuild the waitset
  this->wait_result_.reset();
  this->entities_need_rebuild_ = true;

  while (rclcpp::ok(this->context_) && spinning.load()) {
    rclcpp::AnyExecutable any_executable;
    if (get_next_executable(any_executable)) {
      execute_any_executable(any_executable);
    }
  }
}

通过这段代码,我们就能理解为什么ROS2的设计中有大量大回调函数。以及上例中客户端的代码在构造函数中,创建了一个Timer来定时执行任务,而不是通过死循环来不停发送任务——因为构造函数需要快速返回,不能堵塞住。
在这里插入图片描述

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

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

相关文章

C:将代码拆分放在多个文件的操作

目录 前言&#xff1a; 1、多个文件 2、将一个程序分为多个文件的好处 3、一定程度上对代码进行隐藏 结语&#xff1a; 前言&#xff1a; 在我们刚开始学习C语言时&#xff0c;编写的代码通常比较简短&#xff0c;因此将其放在一个文件中并不会带来不便。然而&#xff0c;…

17965 幸运之星(优先做)

这个问题可以通过使用递归或者迭代的方法来解决。我们可以使用一个一维数组dp来存储中间结果&#xff0c;dp[i]表示i个人时的“幸运之星”的初始编号。 以下是使用C的代码实现&#xff1a; #include <iostream> using namespace std;const int MAXN 1000000; int dp[M…

力扣:100379. 新增道路查询后的最短距离 I(Java,BFS)

目录 题目描述&#xff1a;示例 &#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你一个整数 n 和一个二维整数数组 queries。 有 n 个城市&#xff0c;编号从 0 到 n - 1。初始时&#xff0c;每个城市 i 都有一条单向道路通往城市 i 1&#xff08; 0 < i < …

web高可用群集架构部署----超详细

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【笔记1-6】Qt bug记录:Qt6 无法使用qsort函数排序

在进行Qt5向Qt6升级的过程中&#xff0c;发现Qt6会编译时会出现以下错误&#xff0c;找不到qsort的定义 一开始以为应该是需要头文件或者.pro文件追加一些配置的问题&#xff0c;但是按照下面的提示追加了两个头文件后也没有效果 再进一步调查&#xff0c;找到了下面的文章&a…

python3 pyside6图形库学习笔记及实践(三)

目录 前言菜单栏相关控件使用QtDesigner快速构建菜单栏结构语法 上下文菜单概念为窗体添加上下文菜单为控件添加上下文菜单 折叠菜单资源的加载内置图标Rcc的使用创建资源文件加载资源文件 前言 本系列文章为b站PySide6教程以及官方文档的学习笔记 原视频传送门&#xff1a;【…

【Linux 17】进程信号

文章目录 &#x1f308; 一、信号的概念⭐ 1. 什么是信号⭐ 2. 常见的信号⭐ 3. 信号的管理 &#x1f308; 二、进程的运行⭐ 1. 进程运行模式⭐ 2. 查看后台进程⭐ 3. 运行后台进程⭐ 4. 终止后台进程 &#x1f308; 三、信号的产生⭐ 1. 通过键盘产生信号⭐ 2. 调用系统函数向…

YiYi-Web项目技术栈介绍

项目地址&#xff1a;https://gitee.com/jack0240/yiyi-web YiYi后台管理系统&#xff08;不分离版&#xff09;&#xff0c;SpringBoot Thymeleaf Layui 后台管理系统框架。 前端技术栈 HTML JavaScript JQuery Layui、Bootstrap Echarts图表、大屏展示、富文本 进阶&#…

书生大模型实战营第三期——入门岛

第一关&#xff1a;Linux基础知识 任务如下&#xff1a; 任务描述闯关任务完成SSH连接与端口映射并运行hello_world.py可选任务 1将Linux基础命令在开发机上完成一遍可选任务 2使用 VSCODE 远程连接开发机并创建一个conda环境可选任务 3创建并运行test.sh文件 1. 使用密码进行…

数据结构——单向链表

目录 前言 一、单向链表 二、单向链表基本操作 1、链表单创建 2.节点插入 &#xff08;1&#xff09;尾部插入 &#xff08;2&#xff09;任意位置插入 3、单向链表节点删除 4、链表打印 5、释放链表 6、链表逆序 ...... 三、链表测试 总结 前言 链表&#xff08;Linked List&a…

单细胞Seurat的umi矩阵-与feature、counts(用于质控)

目录 关于umi矩阵学习 用umi计算feature、counts值 ①meta数据查看 ②Count和Feature计算(生成Seurat时自动计算) 1)提取UMI矩阵 2)计算 其他指标 评估质量指标(重点) 1)UMI计数 2)基因计数 3)UMIs vs. genes detected 4)线粒体计数比率 5)综合过滤 过…

【C语言篇】文件操作(下篇)

文章目录 前言文件的顺序读写fscanf和fprintffread和fwrite 文件的随机读写fseekftellrewind 文件读取结束的判定容易被错误使用的feof 文件缓冲区 前言 本篇接上一篇文件操作&#xff08;上篇&#xff09;的内容 文件的顺序读写 在上一篇已经介绍了前面四个了&#xff0c;接…

【人工智能基础四】循环神经网络(RNN)与长短时记忆网络(LSTM)

文章目录 一. RNN1. 循环神经网络结构2. 循环神经网络计算2.1. 机器翻译2.2. 循环体 二. 长短时记忆网路&#xff08;LSTM&#xff09;1. 产生背景2. LSTM的设计思想与LSTM的链式结构2.1. LSTM的设计思想2.2. LSTM链式结构图与遗忘门 3. 长短时记忆网络结构 一. RNN RNN出现的…

五种多目标算法(MOGOA、MOMA、MODA、MOPSO、NSGA2)性能对比(MATLAB代码)

一、算法介绍 MOGOA&#xff1a;多目标蝗虫优化算法 MOMA&#xff1a;多目标蜉蝣算法 MODA&#xff1a;多目标蜻蜓算法 MOPSO&#xff1a;多目标粒子群优化算法 NSGA2&#xff1a;非支配排序遗传算法II 这些算法都是针对多目标优化问题设计的元启发式算法&#xff0c;每种…

Java | Leetcode Java题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; class Solution {public int[] maxNumber(int[] nums1, int[] nums2, int k) {int m nums1.length, n nums2.length;int[] maxSubsequence new int[k];int start Math.max(0, k - n), end Math.min(k, m);for (int i start; i < e…

C语言 | Leetcode C语言题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; int compare(int* subseq1, int subseq1Size, int index1, int* subseq2, int subseq2Size, int index2) {while (index1 < subseq1Size && index2 < subseq2Size) {int difference subseq1[index1] - subseq2[index2];if (…

Codeforces Round 963 (Div. 2) A~C

封面原图 画师やんよ 掉大分的一场 连夜补题 真的不会写啊真的红温了 A - Question Marks 题意 选择题中答案为ABCD的题目各有n道&#xff0c;小明的答案给你&#xff0c;其中&#xff1f;表示这道题空着没写&#xff0c;问他的最高得分 思路 空着的题目肯定没分 超出选项…

【连续数组】python刷题记录

R3-前缀和专题 绝对要用字典记录 ben神&#xff0c;前缀和字典 class Solution:def findMaxLength(self, nums: List[int]) -> int:#前缀和字典,key为差值&#xff0c;value为坐标dict{0:-1}#当前1和0的差值counter0ret0for i,num in enumerate(nums):#多1&#xff0b;1if…

服务器自动部署网络安装环境

实验环境 rhel7&#xff1a;IP地址为172.25.254.200、主机名为node1.rhel7.org 实验配置 一.kickstart自动安装脚本制作 1.安装图形化生成kickstart自动安装脚本的工具 [rootnode1 ~]# yum install system-config-kickstart 2. 启动图形制作工具 [rootnode1 ~]# system-…

[Meachines] [Easy] shocker CGI-BIN Shell Shock + Perl权限提升

信息收集 IP AddressOpening Ports10.10.10.56TCP:80,2222 $ nmap -p- 10.10.10.56 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) |_http-title: Site doesnt have a title (text/html). |_http-server-…