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

ROS1 Nodelets 与 ROS2 rclcpp_components 多节点运行以及功能插件

0. 概述

在ROS1中,Nodelets和Nodes是两种不同的实现方法。Nodelets允许在同一进程中运行多个节点,从而减少了跨进程通信的开销,提高了性能。而在ROS2中,推荐使用组件(Components)来实现类似的功能,组件通过统一的API实现了更好的模块化与可扩展性。之前我们在ROS到ROS2的多节点组合运行对这两个内容进行了整理归纳。这里我们更进一步系统性的介绍Nodelets 与 组件的比较。并用最简单的实例来阐述这两个是如何使用的。

特性Nodelets (ROS1)组件 (ROS2)
运行方式通过Nodelet Manager在同一进程中运行通过component_manager动态加载和卸载
API 一致性不一致,Node与Nodelet使用不同的API统一API,简化了编程模型
生命周期管理需要手动管理,复杂通过rclcpp提供的生命周期管理
动态加载卸载通过Nodelet Manager实现通过component_manager实现
性能降低了跨进程通信开销进一步优化了通信效率

1. ROS1 Nodelets

Nodelets是ROS1中的一种特殊机制,允许在同一进程中运行多个节点,以减少跨进程通信的开销。这种设计使得Nodelets能够共享内存,从而提高性能,特别是在需要频繁通信的情况下,如图像处理和传感器数据处理。Nodelet通过Nodelet Manager进行管理,后者负责加载、卸载和管理Nodelet的生命周期。

1.1 Nodelet 概述

Nodelet的主要优势包括:

  • 高效的通信:由于所有Nodelet都运行在同一个进程内,因此它们之间的通信开销显著降低。
  • 共享内存:Nodelet可以直接访问彼此的内存空间,支持更快速的数据交换。
  • 动态加载:Nodelet可以在运行时动态加载和卸载,增强系统的灵活性。
1.2 创建 Nodelet
1.2.1 定义 Nodelet 类

下面是一个简单的Nodelet示例,它每秒发布一条消息到特定的主题。

#include <nodelet/nodelet.h>
#include <ros/ros.h>
#include <std_msgs/String.h>namespace my_namespace {
class MyNodelet : public nodelet::Nodelet {
public:virtual void onInit() override {ros::NodeHandle& nh = getNodeHandle(); // 获取NodeHandlepub_ = nh.advertise<std_msgs::String>("topic_name", 1); // 创建发布者timer_ = nh.createTimer(ros::Duration(1.0), &MyNodelet::timerCallback, this); // 创建定时器}private:void timerCallback(const ros::TimerEvent&) {std_msgs::String msg;msg.data = "Hello from Nodelet!"; // 发布的消息内容pub_.publish(msg); // 发布消息}ros::Publisher pub_;  // 发布者对象ros::Timer timer_;    // 定时器对象
};} // namespace my_namespace#include <pluginlib/class_list_macros.h>
PLUGINLIB_EXPORT_CLASS(my_namespace::MyNodelet, nodelet::Nodelet) // 注册Nodelet
1.2.2 CMakeLists.txt 配置

在CMakeLists.txt中配置Nodelet的构建设置:

cmake_minimum_required(VERSION 3.0.2)
project(my_nodelet)find_package(catkin REQUIRED COMPONENTSnodeletroscppstd_msgs
)include_directories(${catkin_INCLUDE_DIRS}
)add_library(my_nodelet src/my_nodelet.cpp) // 添加Nodelet库
target_link_libraries(my_nodelet ${catkin_LIBRARIES}) // 链接依赖库add_dependencies(my_nodelet ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) // 依赖配置install(TARGETS my_nodeletLIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} // 安装库
)
1.2.3 package.xml 配置

在package.xml文件中声明Nodelet的依赖关系:

<package format="2"><name>my_nodelet</name><version>0.0.0</version><description>The my_nodelet package</description><buildtool_depend>catkin</buildtool_depend><depend>nodelet</depend><depend>roscpp</depend><depend>std_msgs</depend><export></export>
</package>
1.2.4 启动 Nodelet

在launch文件中启动Nodelet及其管理器:

<launch><node pkg="nodelet" type="nodelet" name="my_nodelet_manager" args="manager" /> <!-- 启动Nodelet管理器 --><node pkg="nodelet" type="nodelet" name="my_nodelet" args="load my_namespace/MyNodelet my_nodelet_manager" /> <!-- 加载Nodelet -->
</launch>
1.3 调用 Nodelet

使用以下命令启动Nodelet:

roslaunch my_nodelet my_nodelet.launch

ROS 2 rclcpp_components

2.1 组件概述

在ROS 2中,rclcpp_components提供了一种高效的机制,类似于ROS 1中的Nodelet,允许开发者将节点以组件的形式组织。这种方式支持动态加载和卸载组件,从而实现高效的资源利用和模块化设计。组件可以在同一进程内运行,从而减少开销并提高通信效率。

2.2 创建组件

2.2.1 定义组件类

组件类通常继承自rclcpp::Node,并在构造函数中初始化所需的资源。

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"class MyComponentNode : public rclcpp::Node {
public:// 构造函数,接受NodeOptions作为参数MyComponentNode(const rclcpp::NodeOptions & options): Node("my_component_node", options) {// 创建发布者,发布在"topic_name"主题上publisher_ = this->create_publisher<std_msgs::msg::String>("topic_name", 10);// 创建定时器,每秒调用一次timer_callbacktimer_ = this->create_wall_timer(std::chrono::seconds(1),[this]() { this->timer_callback(); });}private:// 定时器回调函数,用于发布消息void timer_callback() {auto message = std::make_shared<std_msgs::msg::String>();message->data = "Hello from Component!";publisher_->publish(*message); // 发布消息}// 成员变量:发布者和定时器的智能指针rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;rclcpp::TimerBase::SharedPtr timer_;
};// 使用宏注册组件,使其可被动态加载
#include "rclcpp_components/register_node_macro.hpp"
RCLCPP_COMPONENTS_REGISTER_NODE(MyComponentNode)

2.2.2 CMakeLists.txt 配置

为了正确编译和链接组件,需要在CMakeLists.txt文件中进行适当配置。

cmake_minimum_required(VERSION 3.5)
project(my_component)find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(std_msgs REQUIRED)# 添加共享库
add_library(my_component SHAREDsrc/my_component.cpp
)# 设置依赖
ament_target_dependencies(my_component"rclcpp""rclcpp_components""std_msgs"
)# 注册组件
rclcpp_components_register_node(my_componentPLUGIN "MyComponentNode"EXECUTABLE my_component_node
)# 安装目标
install(TARGETS my_componentEXPORT export_my_componentLIBRARY DESTINATION lib
)

2.2.3 package.xml 配置

package.xml中声明依赖关系。

<package format="2"><name>my_component</name><version>0.0.0</version><description>The my_component package</description><buildtool_depend>ament_cmake</buildtool_depend><depend>rclcpp</depend><depend>rclcpp_components</depend><depend>std_msgs</depend><export></export>
</package>

2.2.4 创建 Launch 文件

Launch文件用于启动组件并配置其参数。

from launch import LaunchDescription
from launch_ros.actions import ComposableNodeContainer, ComposableNodedef generate_launch_description():container = ComposableNodeContainer(name='my_container',namespace='',package='rclcpp_components',executable='component_container',composable_node_descriptions=[ComposableNode(package='my_component',plugin='MyComponentNode',name='my_component',parameters=[{'param_name': 'param_value'}])],output='both',)return LaunchDescription([container])

2.3 调用组件

在终端中运行以下命令启动组件:

ros2 launch my_component my_component_launch.py

2.4 动态加载和卸载组件

ros2 component命令允许在运行时动态加载和卸载组件。

2.4.1 加载组件

ros2 component load /ComponentManager my_component MyComponentNode

2.4.2 卸载组件

ros2 component unload /ComponentManager <component_name>

在ROS(Robot Operating System)中,插件是一种可扩展的机制,允许开发者以动态的方式加载和使用软件组件。ROS1和ROS2都支持插件,但它们的实现方式有所不同。下面将分别介绍ROS1和ROS2插件的操作及示例代码。

好的!下面是一个更为详细的ROS1和ROS2插件实例,包括CMakeLists.txt文件的配置,确保代码完整且可运行。

3. ROS1 插件示例

3.1 创建插件接口

首先,定义一个插件接口 MyPluginInterface,这个接口包含一个虚函数 printMessage,用于输出消息。

// my_plugin_interface.h
#ifndef MY_PLUGIN_INTERFACE_H
#define MY_PLUGIN_INTERFACE_H#include <string>// 定义插件接口
class MyPluginInterface
{
public:virtual ~MyPluginInterface() {} // 虚析构函数virtual void printMessage(const std::string &message) = 0; // 纯虚函数
};#endif // MY_PLUGIN_INTERFACE_H

3.2 实现插件

接下来,实现该接口的插件,命名为 MyPlugin,并实现 printMessage 函数。

// my_plugin.cpp
#include "my_plugin_interface.h"
#include <pluginlib/class_list_macros.h>
#include <iostream>class MyPlugin : public MyPluginInterface
{
public:// 实现 printMessage 函数void printMessage(const std::string &message) override{std::cout << "Plugin message: " << message << std::endl; // 打印消息}
};// 使用 pluginlib 注册插件
PLUGINLIB_EXPORT_CLASS(MyPlugin, MyPluginInterface)

3.3 主程序

在主程序中加载和使用插件。

// main.cpp
#include <ros/ros.h>
#include <pluginlib/class_loader.h>
#include "my_plugin_interface.h"int main(int argc, char **argv)
{ros::init(argc, argv, "plugin_example"); // 初始化 ROS 节点ros::NodeHandle nh;// 创建插件加载器pluginlib::ClassLoader<MyPluginInterface> loader("my_plugin_package", "MyPluginInterface");try{// 创建插件实例boost::shared_ptr<MyPluginInterface> plugin_instance = loader.createInstance("MyPlugin");plugin_instance->printMessage("Hello from the plugin!"); // 调用插件方法}catch (const pluginlib::PluginlibException &ex){ROS_ERROR("Pluginlib exception: %s", ex.what()); // 捕捉异常并输出错误}ros::spin(); // 保持节点运行return 0;
}

3.4 CMakeLists.txt

构建项目的 CMake 配置文件。

cmake_minimum_required(VERSION 3.0.2)
project(my_plugin_package)find_package(catkin REQUIRED COMPONENTSroscpppluginlib
)include_directories(${catkin_INCLUDE_DIRS} // 包含目录
)add_library(my_plugin src/my_plugin.cpp) // 添加插件库
add_dependencies(my_plugin ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})install(TARGETS my_pluginEXPORT my_plugin_packageLIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} // 安装库
)install(FILES my_plugin_interface.hDESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} // 安装头文件
)// 安装资源文件(如有)
install(DIRECTORY resource/DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/resource
)// 导出插件描述文件
pluginlib_export_plugin_description_file(my_plugin_package my_plugin_plugins.xml)

3.5 插件描述文件

创建插件描述文件 my_plugin_plugins.xml,用于指定插件信息。

<?xml version="1.0"?>
<library path="my_plugin"><class name="MyPlugin" type="MyPlugin" base_class_type="MyPluginInterface"><description>A simple plugin that prints messages.</description></class>
</library>

4. ROS2 插件示例

…详情请参照古月居


http://www.mrgr.cn/news/69731.html

相关文章:

  • 23.网工入门篇--------介绍一下园区网典型组网架构及案例实践
  • CSS多列布局:打破传统布局的束缚
  • Apache服务安装
  • 外呼系统的功能都有哪些,怎么去选择?
  • EN 1335-2办公家具.办公椅.第2部分:安全要求
  • 创新体验触手可及 紫光展锐携手影目科技推出AI眼镜开放平台
  • 手把手教你写Unity3D飞机大战(6)玩家子弹射击之瞄准程序(射线检测)
  • 平衡二叉树
  • 【含文档】基于ssm+jsp的旅游网站(含源码+数据库+lw)
  • 【数据结构实战】从零开始打造你的专属链表
  • FPGA 第5讲 点亮你的LED灯
  • AI重塑软件开发流程
  • A025-基于SpringBoot的售楼管理系统的设计与实现
  • 【网络安全】Nginx功能快速入门
  • 05_docker 安装常用软件
  • 【GPTs】EmojiAI:轻松生成趣味表情翻译
  • Linux服务器进程的控制与进程之间的关系
  • ReentrantLock【复习】
  • 微服务(二)
  • AI背后的“思考者“:LLM大语言模型是什么?
  • 使用热冻结数据层生命周期优化在 Elastic Cloud 中存储日志的成本
  • 一定要chatgpt吗?
  • 十八:Spring Boot 依赖(3)-- spring-boot-starter-data-jpa 依赖详解
  • 对静态资源加载失败的场景做降级处理
  • 防倒灌电路【手电钻工作日志】
  • 素数筛选法