ROS2学习(四)进程,线程与节点的关系

6 篇文章 2 订阅

节点与节点执行器

节点,英文是node,在ROS2中,节点是一个抽象的实体,它可以代表某种或某类特定功能的抽象集合体,它可以存在于进程中,也可以存在于线程中。所有ROS2的基础功能最基础的载体是节点,所有的通信也都需要通过节点来实现和运作。
在ROS中,节点是作为最小的进程单元存在的,它作为一个独立的可执行程序,承载着与其他节点通信的重要使命。在ROS2中,节点和进程的概念完全分开的,节点是独立于操作系统进程或者线程的概念的抽象定义,它虽然依旧承载着通信的功能,但是并不作为独立的进程运行,而是嵌入进程中,作为一个抽象的实体进行运作。
在一个项目中,可能存在若干个进程,每个进程中有一个或者若干个节点执行器,而每个节点执行器中又有一个或者若干个节点。节点运行在节点执行器中,借助节点执行器协调到资源和调度方式运作,如在哪一时刻处理订阅的消息,在哪个时刻处理服务消息等。所有在服务中和订阅中有关线程的设定,也需要节点执行器满足条件才能成功运作。

进程,节点执行器与节点
节点执行器作为进程中维护节点的载体, 在rclpy和rclcpp中均有单线程节点执行器(SingleThread Executor)和多线程节点执行器(Multi Threaded Executor)。单线程节点执行器表示其负责管理的回调函数只会占用一个线程资源,并且会根据其指定的规则对回调顺序和优先级进行设置。多线程节点执行器表示其负责管理的回调函数可以占用多个线程,线程数量可以在节点执行器初始化时设置。

进程,线程与节点

一个进程可以维护多个节点执行器,一个节点执行器可以维护多个节点。按照线程数量区分,可以分为单线程节点执行器和多线程节点执行器。单线程节点执行器会将所有已添加到维护队列的节点限制在一个线程内处理所有回调,而多线程节点执行器会按照设备的性能,动态分配线程为队列内的节点处理回调。从最表象上看,在单线程节点执行器中,所有节点的进程ID相同,线程ID也相同;而在多线程节点执行器中,所有节点的进程ID相同,线程ID会不同。

下面代码在节点定时器的回调函数中打印进程ID和线程ID可以验证这一点。

//thread_sample.h
#pragma once
#include <string>

#include "rclcpp/rclcpp.hpp"

class ThreadSample: public rclcpp::Node
{
        public:
                explicit ThreadSample(const std::string& node_name);
                ~ThreadSample(){};

        private:
                rclcpp::TimerBase::SharedPtr print_timer_;
};
//thread_sample.cpp
#include "sample3/thread_sample.h"

#include <chrono>
#include <string>
#include <thread>
#include <unistd.h>
#include "sys/types.h"
#include "rclcpp/rclcpp.hpp"

ThreadSample::ThreadSample(const std::string& node_name): rclcpp::Node(node_name)
{
        auto printtimer_callback =
        [&]()->void {
                pid_t pid = getpid();
                std::cout<< this->get_name() << ": pid is " << pid << ", thread id is " << std::this_thread::get_id() << std::endl;
        };

        print_timer_ = this->create_wall_timer(std::chrono::milliseconds(500), printtimer_callback);
}

#include "sample3/thread_sample.h"
#include <memory>
#include <vector>

#include <string>
#include "rclcpp/rclcpp.hpp"

int main(int argc, char ** argv)
{
        rclcpp::init(argc, argv);
        uint32_t node_count(0);
        bool is_multi(false);
        std::vector<std::shared_ptr<ThreadSample>> node_vector;

        rclcpp::executors::SingleThreadedExecutor executor_s;
        rclcpp::executors::MultiThreadedExecutor executor_m;

        if(argc >= 3)
        {
                int input_count = atoi(argv[1]);
                node_count = input_count > 0 ? input_count : 0;

                node_vector.reserve(node_count);
                std::string multi_flag = static_cast<std::string>(argv[2]);

                if(multi_flag == std::string("m")){
                        is_multi = true;
                }
                else if (multi_flag == std::string("s")){
                        is_multi = false;
                }
                else{
                        std::cout<<"Example ros2 run sample3 sample3 <node_count> s/m" << std::endl;
                        return 0;
                }
        }
        else
        {
                        std::cout<<"Example ros2 run sample3 sample3 <node_count> s/m" << std::endl;
                        return 0;
        }

        for (int i = node_count; i> 0; i--){
                node_vector.push_back(
                        std::make_shared<ThreadSample>("cpp_node_a_" + std::to_string(i)));

                if(is_multi)
                {
                        executor_m.add_node(node_vector.back()->get_node_base_interface());
                }
                else
                {
                        executor_s.add_node(node_vector.back()->get_node_base_interface());
                }
        }

        if(is_multi)
        {
                executor_m.spin();
        }
        else
        {
                executor_s.spin();
        }

        rclcpp::shutdown();
        return 0;
}

单线程节点执行器同时运行5个节点

crabe@crabe-virtual-machine:~/Desktop/ROS2_Sample/sample3$ ros2 run sample3 sample3 5 s
cpp_node_a_5: pid is 7044, thread id is 140166163743616
cpp_node_a_4: pid is 7044, thread id is 140166163743616
cpp_node_a_3: pid is 7044, thread id is 140166163743616
cpp_node_a_2: pid is 7044, thread id is 140166163743616
cpp_node_a_1: pid is 7044, thread id is 140166163743616
cpp_node_a_5: pid is 7044, thread id is 140166163743616
cpp_node_a_4: pid is 7044, thread id is 140166163743616
cpp_node_a_3: pid is 7044, thread id is 140166163743616
cpp_node_a_2: pid is 7044, thread id is 140166163743616
cpp_node_a_1: pid is 7044, thread id is 140166163743616

多线程节点执行器同时运行3个节点

cpp_node_a_2: pid is 7198, thread id is 140274060408576
cpp_node_a_1: pid is 7198, thread id is 140274060408576
cpp_node_a_2: pid is 7198, thread id is 140274060408576
cpp_node_a_1: pid is 7198, thread id is 140274159653760
cpp_node_a_2: pid is 7198, thread id is 140274159653760
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS2中,C++节点通信主要通过ROS2提供的rclcpp库实现。rclcpp库是一个ROS2的C++客户端库,它提供了创建ROS2节点、发布和订阅话题、服务调用等功能。 下面是一个简单的例子,演示如何使用rclcpp库在C++节点之间进行通信: 1. 首先,我们需要包含必要的头文件: ```cpp #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" ``` 2. 创建一个发布者: ```cpp auto publisher = node->create_publisher<std_msgs::msg::String>("my_topic", 10); ``` 其中,`node`是一个`rclcpp::Node`对象,`std_msgs::msg::String`是消息类型,`my_topic`是话题名称,`10`是话题队列长度。 3. 创建一个消息: ```cpp auto message = std_msgs::msg::String(); message.data = "Hello, world!"; ``` 4. 发布消息: ```cpp publisher->publish(message); ``` 5. 创建一个订阅者: ```cpp auto subscription = node->create_subscription<std_msgs::msg::String>("my_topic", 10, [](const std_msgs::msg::String::SharedPtr msg) { RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "I heard: '%s'", msg->data.c_str()); }); ``` 其中,`std_msgs::msg::String`是消息类型,`my_topic`是话题名称,`10`是话题队列长度,`[](const std_msgs::msg::String::SharedPtr msg) {...}`是消息回调函数,用于处理接收到的消息。 6. 运行节点: ```cpp rclcpp::spin(node); ``` 以上是一个简单的例子,演示了如何在C++节点之间使用ROS2进行通信。在实际应用中,我们可以根据需要创建多个发布者和订阅者,以实现节点之间的复杂通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值