【ROS2】初级:客户端-编写一个简单的发布者和订阅者(C++)

目标:使用 C++创建并运行一个发布者和订阅者节点。

 教程级别:初学者

 时间:20 分钟

 目录

  •  背景

  •  先决条件

  •  任务

    • 1. 创建一个包

    • 2. 编写发布者节点

    • 3. 编写订阅者节点

    • 4. 构建并运行

  •  摘要

  •  下一步

  •  相关内容

 背景

节点是在 ROS 图上进行通信的可执行过程。在本教程中,节点将通过主题相互传递字符串消息形式的信息。这里使用的例子是一个简单的“说话者”和“监听者”系统;一个节点发布数据,另一个节点订阅主题以接收该数据。

这些示例中使用的代码可以在这里找到。

 先决条件

在之前的教程中,您学习了如何创建工作区和创建包。

任务

1. 创建一个包

打开一个新的终端并且初始化您的 ROS 2 安装,这样 ros2 命令就会生效。

导航到在之前教程中创建的 ros2_ws 目录。

请记住,包应该在 src 目录中创建,而不是在工作区的根目录中。因此,请导航到 ros2_ws/src ,然后运行包创建命令:

ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_pubsub
cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_pubsub
going to create a new package
package name: cpp_pubsub
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_cmake
dependencies: []
creating folder ./cpp_pubsub
creating ./cpp_pubsub/package.xml
creating source and include folder
creating folder ./cpp_pubsub/src
creating folder ./cpp_pubsub/include/cpp_pubsub
creating ./cpp_pubsub/CMakeLists.txt

您的终端将返回一条消息,确认您的包 cpp_pubsub 及其所有必要的文件和文件夹已创建。

导航到 ros2_ws/src/cpp_pubsub/src 。请记住,这是任何 CMake 包中包含可执行文件的源文件所在的目录。

2. 编写发布者节点

下载示例发布者代码,请输入以下命令:

cxy@ubuntu2404-cxy:~/ros2_ws/src$ cd cpp_pubsub/src
cxy@ubuntu2404-cxy:~/ros2_ws/src/cpp_pubsub/src$ wget -O publisher_lambda_function.cpp https://raw.githubusercontent.com/ros2/examples/jazzy/rclcpp/topics/minimal_publisher/lambda.cpp
--2024-07-05 21:02:40--  https://raw.githubusercontent.com/ros2/examples/jazzy/rclcpp/topics/minimal_publisher/lambda.cpp
正在连接 127.0.0.1:2334... 已连接。
已发出 Proxy 请求,正在等待回应... 200 OK
长度:1849 (1.8K) [text/plain]
正在保存至: ‘publisher_lambda_function.cpp’


publisher_lambda_fu 100%[===================>]   1.81K  --.-KB/s    用时 0s    


2024-07-05 21:02:42 (6.81 MB/s) - 已保存 ‘publisher_lambda_function.cpp’ [1849/1849])

现在将有一个名为 publisher_lambda_function.cpp 的新文件。使用您喜欢的文本编辑器打开该文件。

// 版权 2016 开源机器人基金会,公司。
//
// 根据 Apache 许可证,版本 2.0(“许可证”)许可;
// 除非符合许可证,否则不得使用此文件。
// 您可以在以下位置获取许可证副本:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// 除非适用法律要求或书面同意,否则在许可证下分发的软件
// 将按“原样”基础分发,无任何形式的明示或暗示保证或条件。
// 有关在许可证下管理权限和限制的特定语言,请参阅许可证。


#include <chrono>  // 包含 C++ 标准库中的时间函数
#include <memory>  // 包含 C++ 标准库中的内存管理函数
#include <string>  // 包含 C++ 标准库中的字符串函数


#include "rclcpp/rclcpp.hpp"  // 包含 ROS 2 的 rclcpp 库
#include "std_msgs/msg/string.hpp"  // 包含 ROS 2 的标准消息类型


using namespace std::chrono_literals;  // 使用 C++ 标准库中的时间字面量


/* 这个例子创建了一个 Node 的子类,并使用了一个精巧的 C++11 lambda
 * 函数来简化回调语法,但这使得代码在初次查看时更难理解。*/


class MinimalPublisher : public rclcpp::Node  // 定义一个 MinimalPublisher 类,它是 rclcpp::Node 的子类
{
public:
  MinimalPublisher()  // MinimalPublisher 类的构造函数
  : Node("minimal_publisher"), count_(0)  // 初始化节点名为 "minimal_publisher",计数器 count_ 为 0
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);  // 创建一个发布者,发布到 "topic" 主题,队列大小为 10
    auto timer_callback =  // 定义一个定时器回调函数
      [this]() -> void {  // 这是一个 lambda 函数,捕获 this 指针
        auto message = std_msgs::msg::String();  // 创建一个 std_msgs::msg::String 类型的消息
        message.data = "Hello, world! " + std::to_string(this->count_++);  // 设置消息的内容,包括一个字符串和计数器的值
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());  // 在控制台上打印一条信息,显示正在发布的消息
        this->publisher_->publish(message);  // 发布消息
      };
    timer_ = this->create_wall_timer(500ms, timer_callback);  // 创建一个定时器,每 500 毫秒调用一次定时器回调函数
  }


private:
  rclcpp::TimerBase::SharedPtr timer_;  // 定义一个定时器
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;  // 定义一个发布者
  size_t count_;  // 定义一个计数器
};


int main(int argc, char * argv[])  // 主函数
{
  rclcpp::init(argc, argv);  // 初始化 ROS 2
  rclcpp::spin(std::make_shared<MinimalPublisher>());  // 创建一个 MinimalPublisher 类的实例,并让它保持运行
  rclcpp::shutdown();  // 关闭 ROS 2
  return 0;  // 返回 0,表示程序正常结束
}
检查代码 2.1

代码的顶部包括您将使用的标准 C++头文件。在标准 C++头文件之后是 rclcpp/rclcpp.hpp 包含,它允许您使用 ROS 2 系统中最常见的部分。最后是 std_msgs/msg/string.hpp ,它包括了您将用来发布数据的内置消息类型。

#include <chrono>
#include <memory>
#include <string>


#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"


using namespace std::chrono_literals;

这些行代表节点的依赖项。请记住,依赖项必须添加到 package.xml 和 CMakeLists.txt 中,您将在下一部分中执行此操作。

下一行通过继承 rclcpp::Node 来创建节点类 MinimalPublisher 。代码中的每个 this 都是指节点。

class MinimalPublisher : public rclcpp::Node

公共构造函数将节点命名为 minimal_publisher 并将 count_ 初始化为 0。在构造函数内部,发布者使用 String 消息类型、主题名称 topic 以及所需的队列大小来初始化,以限制备份事件中的消息。接下来,声明了一个名为 timer_callback 的 lambda 函数。它通过引用捕获当前对象 this ,不接受输入参数并返回 void。 timer_callback 函数创建一个类型为 String 的新消息,设置其数据为所需的字符串并发布它。 RCLCPP_INFO 宏确保每个发布的消息都打印到控制台。最后,初始化 timer_ ,这导致 timer_callback 函数每秒执行两次。

public:
  MinimalPublisher()
  : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
    auto timer_callback =
      [this]() -> void {
        auto message = std_msgs::msg::String();
        message.data = "Hello, world! " + std::to_string(this->count_++);
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
        this->publisher_->publish(message);
      };
    timer_ = this->create_wall_timer(500ms, timer_callback);
  }

在类的底部是计时器、发布者和计数器字段的声明。

private:
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  size_t count_;

继 MinimalPublisher 类之后是 main ,节点在此实际执行。 rclcpp::init 初始化 ROS 2, rclcpp::spin 开始处理来自节点的数据,包括来自计时器的回调。

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
 2.2 添加依赖项 

返回到 ros2_ws/src/cpp_pubsub 目录, CMakeLists.txt 和 package.xml 文件已为您创建。

使用您的文本编辑器打开 package.xml 。

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>cpp_pubsub</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="cxy@todo.todo">cxy</maintainer>
  <license>Apache-2.0</license>


  <buildtool_depend>ament_cmake</buildtool_depend>


  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>


  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

如前所述教程中提到的,确保填写 <description> 、 <maintainer> 和 <license> 标签:

<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>

在 ament_cmake 构建工具依赖项之后添加新行,并粘贴以下与您节点的 include 语句相对应的依赖项:

<depend>rclcpp</depend>
<depend>std_msgs</depend>

这声明了当代码构建和执行时,包需要 rclcpp 和 std_msgs 。

确保保存文件。

2.3 CMakeLists.txt

现在打开 CMakeLists.txt 文件。

cmake_minimum_required(VERSION 3.8)  // 设置 CMake 的最低版本要求为 3.8
project(cpp_pubsub)  // 定义一个名为 cpp_pubsub 的项目


if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")  // 如果编译器是 GNU C++ 或 Clang
  add_compile_options(-Wall -Wextra -Wpedantic)  // 添加编译选项,开启所有警告、额外警告和严格模式
endif()


// 寻找依赖项
find_package(ament_cmake REQUIRED)  // 寻找 ament_cmake 包,这是必需的
// 取消下面的注释以手动填写更多的依赖项
// find_package(<dependency> REQUIRED)


if(BUILD_TESTING)  // 如果开启了测试
  find_package(ament_lint_auto REQUIRED)  // 寻找 ament_lint_auto 包,这是必需的
  // 下面这行跳过了检查版权的 linter
  // 当所有源文件都添加了版权和许可证时,注释掉这行
  set(ament_cmake_copyright_FOUND TRUE)
  // 下面这行跳过了 cpplint(只在 git 仓库中有效)
  // 当这个包在一个 git 仓库中,并且所有源文件都添加了版权和许可证时,注释掉这行
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()  // 自动寻找测试依赖项
endif()


ament_package()  // 创建 ament 包

在现有的依赖项 find_package(ament_cmake REQUIRED) 下面,添加以下几行:

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

之后,添加可执行文件并将其命名为 talker ,这样您就可以使用 ros2 run 运行您的节点:

add_executable(talker src/publisher_lambda_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)

最后,添加 install(TARGETS...) 部分,以便 ros2 run 可以找到你的可执行文件:

install(TARGETS
  talker
  DESTINATION lib/${PROJECT_NAME})

您可以通过删除一些不必要的部分和评论来清理您的 CMakeLists.txt ,使其看起来像这样:

cmake_minimum_required(VERSION 3.5)  // 设置 CMake 的最低版本要求为 3.5
project(cpp_pubsub)  // 定义一个名为 cpp_pubsub 的项目


// 默认使用 C++14
if(NOT CMAKE_CXX_STANDARD)  // 如果没有指定 C++ 标准
  set(CMAKE_CXX_STANDARD 14)  // 则设置 C++ 标准为 14
endif()


if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")  // 如果编译器是 GNU C++ 或 Clang
  add_compile_options(-Wall -Wextra -Wpedantic)  // 添加编译选项,开启所有警告、额外警告和严格模式
endif()


find_package(ament_cmake REQUIRED)  // 寻找 ament_cmake 包,这是必需的
find_package(rclcpp REQUIRED)  // 寻找 rclcpp 包,这是必需的
find_package(std_msgs REQUIRED)  // 寻找 std_msgs 包,这是必需的


add_executable(talker src/publisher_lambda_function.cpp)  // 添加一个名为 talker 的可执行文件,源文件为 src/publisher_lambda_function.cpp
ament_target_dependencies(talker rclcpp std_msgs)  // 设置 talker 的依赖项为 rclcpp 和 std_msgs


install(TARGETS  // 安装目标
  talker  // 安装 talker
  DESTINATION lib/${PROJECT_NAME})  // 安装位置为 lib/${PROJECT_NAME}


ament_package()  // 创建 ament 包

您现在可以构建您的包,源本地设置文件,并运行它,但让我们先创建订阅者节点,这样您就可以看到完整系统的运作。

3. 编写订阅者节点

返回 ros2_ws/src/cpp_pubsub/src 以创建下一个节点。在您的终端输入以下代码:

cxy@ubuntu2404-cxy:~/ros2_ws/src/cpp_pubsub/src$ wget -O subscriber_lambda_function.cpp https://raw.githubusercontent.com/ros2/examples/jazzy/rclcpp/topics/minimal_subscriber/lambda.cpp
--2024-07-05 21:35:49--  https://raw.githubusercontent.com/ros2/examples/jazzy/rclcpp/topics/minimal_subscriber/lambda.cpp
正在连接 127.0.0.1:2334... 已连接。
已发出 Proxy 请求,正在等待回应... 200 OK
长度: 1335 (1.3K) [text/plain]
正在保存至: ‘subscriber_lambda_function.cpp’


subscriber_lambda_f 100%[===================>]   1.30K  --.-KB/s    用时 0s    


2024-07-05 21:35:50 (26.2 MB/s) - 已保存 ‘subscriber_lambda_function.cpp’ [1335/1335])

检查以确保这些文件存在:

publisher_lambda_function.cpp  subscriber_lambda_function.cpp

打开 subscriber_lambda_function.cpp 使用你的文本编辑器。

// 版权 2016 Open Source Robotics Foundation, Inc.
//
// 根据 Apache 许可证版本 2.0(“许可证”)许可;
// 除非符合许可证,否则不得使用此文件。
// 您可以在以下位置获取许可证副本:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// 除非适用法律要求或书面同意,否则在许可证下分发的软件
// 将按“原样”基础分发,
// 无任何明示或暗示的保证或条件。
// 有关在许可证下的权限和
// 限制的特定语言,请参阅许可证。


#include <memory>  // 包含内存库


#include "rclcpp/rclcpp.hpp"  // 包含 ROS 的 rclcpp 库
#include "std_msgs/msg/string.hpp"  // 包含 std_msgs 的 string 消息类型


class MinimalSubscriber : public rclcpp::Node  // 定义一个名为 MinimalSubscriber 的类,它继承自 rclcpp::Node
{
public:
  MinimalSubscriber()  // MinimalSubscriber 类的构造函数
  : Node("minimal_subscriber")  // 初始化 Node,节点名为 "minimal_subscriber"
  {
    auto topic_callback =  // 定义一个回调函数
      this -> void {  // 当收到消息时,执行此函数
        RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());  // 在控制台打印收到的消息
      };
    subscription_ =  // 创建一个订阅者
      this->create_subscription<std_msgs::msg::String>("topic", 10, topic_callback);  // 订阅名为 "topic" 的主题,队列大小为 10,回调函数为 topic_callback
  }


private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;  // 定义一个订阅者的共享指针
};


int main(int argc, char * argv[])  // 主函数
{
  rclcpp::init(argc, argv);  // 初始化 ROS
  rclcpp::spin(std::make_shared<MinimalSubscriber>());  // 创建 MinimalSubscriber 的实例并开始循环
  rclcpp::shutdown();  // 关闭 ROS
  return 0;  // 返回 0,表示程序正常结束
}
3.1 检查代码

订阅者节点的代码与发布者的几乎相同。现在节点被命名为 minimal_subscriber ,构造函数使用节点的 create_subscription 函数来执行回调。

没有计时器,因为订阅者只是在数据发布到 topic 主题时才做出响应。

该 topic_callback 函数接收在主题上发布的字符串消息数据,并使用 RCLCPP_INFO 宏简单地将其写入控制台。

回想一下主题教程中的内容,发布者和订阅者使用的主题名称和消息类型必须匹配,以便它们能够通信。

public:
  MinimalSubscriber()
  : Node("minimal_subscriber")
  {
    auto topic_callback =
      [this](std_msgs::msg::String::UniquePtr msg) -> void {
        RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
      };
    subscription_ =
      this->create_subscription<std_msgs::msg::String>("topic", 10, topic_callback);
  }

本类中唯一的字段声明是订阅。

private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;

main 函数的作用完全相同,只是现在它会旋转 MinimalSubscriber 节点。对于发布者节点,旋转意味着启动计时器,但对于订阅者来说,它仅仅意味着准备好随时接收消息。

由于此节点与发布者节点具有相同的依赖关系,因此没有新内容需要添加到 package.xml 。

3.2 CMakeLists.txt

重新打开 CMakeLists.txt ,并在发布者条目下添加订阅者节点的可执行文件和目标。

add_executable(listener src/subscriber_lambda_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)


install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})

请确保保存文件,然后您的发布/订阅系统应该就绪。

c4ad19e5c4ac8a47237b2945174d9fee.png

4. 构建并运行

您可能已经在 ROS 2 系统中安装了 rclcpp 和 std_msgs 包。在构建之前,在工作空间的根目录( ros2_ws )运行 rosdep 以检查缺失的依赖项是一个好习惯:

rosdep install -i --from-path src --rosdistro jazzy -y

仍在您的工作区的根目录中, ros2_ws ,构建您的新包:

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select cpp_pubsub
Starting >>> cpp_pubsub
Finished <<< cpp_pubsub [21.1s]                       
                      
Summary: 1 package finished [21.6s]

打开一个新的终端,导航到 ros2_ws ,并且导入设置文件:

. install/setup.bash

现在运行 talker 节点:

ros2 run cpp_pubsub talker

终端应该每 0.5 秒发布一次信息消息,如下:

cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run cpp_pubsub talker
[INFO] [1720187087.525329991] [minimal_publisher]: Publishing: 'Hello, world! 0'
[INFO] [1720187088.025417781] [minimal_publisher]: Publishing: 'Hello, world! 1'
[INFO] [1720187088.525298470] [minimal_publisher]: Publishing: 'Hello, world! 2'
[INFO] [1720187089.025531468] [minimal_publisher]: Publishing: 'Hello, world! 3'

3e3cf2f67ebbbae9dca770c9bdc1a5de.png

打开另一个终端,再次从 ros2_ws 内部加载设置文件,然后启动监听节点:

cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run cpp_pubsub listener
Package 'cpp_pubsub' not found
cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 run cpp_pubsub listener
[INFO] [1720187115.025931086] [minimal_subscriber]: I heard: 'Hello, world! 55'
[INFO] [1720187115.525992227] [minimal_subscriber]: I heard: 'Hello, world! 56'
[INFO] [1720187116.025935417] [minimal_subscriber]: I heard: 'Hello, world! 57'
[INFO] [1720187116.525967640] [minimal_subscriber]: I heard: 'Hello, world! 58'
[INFO] [1720187117.025949535] [minimal_subscriber]: I heard: 'Hello, world! 59'
[INFO] [1720187117.526001204] [minimal_subscriber]: I heard: 'Hello, world! 60'

在每个终端输入 Ctrl+C 以停止节点旋转。

 摘要

您创建了两个节点,用于通过主题发布和订阅数据。在编译和运行它们之前,您将它们的依赖项和可执行文件添加到包配置文件中。

 下一步

接下来,您将使用服务/客户端 service/client  模型创建另一个简单的 ROS 2 包。同样,您可以选择用 C++ https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Service-And-Client.html 或 Python https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Service-And-Client.html 编写。

 相关内容 

有几种方法可以用 C++编写发布者和订阅者;请查看 ros2/examples 仓库中的 minimal_publisher 和 minimal_subscriber 包。https://github.com/ros2/examples/tree/jazzy/rclcpp/topics

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在同一个ROS2节点中包含多个订阅者和发布者的多线程功能,可以使用ROS2提供的多线程库来实现。 具体步骤如下: 1. 创建一个ROS2节点,并在其中创建多个订阅者和发布者。 2. 使用C语言的多线程库,如pthread,创建多个线程。 3. 在每个线程中,分别订阅发布相应的消息。 4. 在每个线程中,使用ROS2提供的rcl_wait函数来等待消息到达。 5. 在每个线程中,使用ROS2提供的rcl_take函数来获取消息并处理。 6. 在每个线程中,使用ROS2提供的rcl_publish函数来发布消息。 7. 确保在每个线程中使用适当的同步机制,以避免竞争条件和死锁等问题。 8. 在程序结束时,使用ROS2提供的rcl_shutdown函数来关闭节点并释放资源。 以下是一个简单的示例代码,演示了如何在同一个ROS2节点中使用多线程处理多个订阅者和发布者: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include "rcl/rcl.h" #include "std_msgs/msg/int32.h" rcl_node_t node; rcl_subscription_t sub1, sub2; rcl_publisher_t pub1, pub2; void* thread1(void* arg) { rcl_wait_set_t wait_set; rcl_ret_t ret; std_msgs__msg__Int32 msg; rcl_wait_set_init(&wait_set, 2, 0, 0, 0, 0, rcl_get_default_allocator()); while(1) { rcl_wait(&wait_set, RCL_MS_TO_NS(10)); if(rcl_wait_set_is_ready(&wait_set, &sub1)) { ret = rcl_take(&sub1, &msg, NULL, NULL); if(ret == RCL_RET_OK) { printf("Thread 1 received message: %d\n", msg.data); msg.data *= 2; rcl_publish(&pub1, &msg, NULL); } } if(rcl_wait_set_is_ready(&wait_set, &sub2)) { ret = rcl_take(&sub2, &msg, NULL, NULL); if(ret == RCL_RET_OK) { printf("Thread 1 received message: %d\n", msg.data); msg.data *= 2; rcl_publish(&pub2, &msg, NULL); } } } rcl_wait_set_fini(&wait_set); return NULL; } void* thread2(void* arg) { rcl_wait_set_t wait_set; rcl_ret_t ret; std_msgs__msg__Int32 msg; rcl_wait_set_init(&wait_set, 2, 0, 0, 0, 0, rcl_get_default_allocator()); while(1) { rcl_wait(&wait_set, RCL_MS_TO_NS(10)); if(rcl_wait_set_is_ready(&wait_set, &sub1)) { ret = rcl_take(&sub1, &msg, NULL, NULL); if(ret == RCL_RET_OK) { printf("Thread 2 received message: %d\n", msg.data); msg.data *= 3; rcl_publish(&pub1, &msg, NULL); } } if(rcl_wait_set_is_ready(&wait_set, &sub2)) { ret = rcl_take(&sub2, &msg, NULL, NULL); if(ret == RCL_RET_OK) { printf("Thread 2 received message: %d\n", msg.data); msg.data *= 3; rcl_publish(&pub2, &msg, NULL); } } } rcl_wait_set_fini(&wait_set); return NULL; } int main(int argc, char** argv) { rcl_init_options_t options = rcl_get_zero_initialized_init_options(); rcl_init_options_init(&options, rcl_get_default_allocator()); rcl_init(argc, argv, &options, &node); rcl_subscription_init(&sub1, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "topic1", NULL); rcl_subscription_init(&sub2, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "topic2", NULL); rcl_publisher_init(&pub1, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "output1", NULL); rcl_publisher_init(&pub2, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), "output2", NULL); pthread_t t1, t2; pthread_create(&t1, NULL, thread1, NULL); pthread_create(&t2, NULL, thread2, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); rcl_subscription_fini(&sub1, &node); rcl_subscription_fini(&sub2, &node); rcl_publisher_fini(&pub1, &node); rcl_publisher_fini(&pub2, &node); rcl_node_fini(&node); rcl_shutdown(); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值