【ROS2】中级-编写可组合节点 (C++)

目录

  •  起点

  •  代码更新

    • 添加包依赖

    •  类定义

    • 没有主方法

    •  CMake 变更

  •  运行您的节点

 起点

我们假设你有一个常规的 rclcpp::Node 可执行文件,你希望在与其他节点相同的进程中运行它,以实现更高效的通信。

我们将从一个直接继承自 Node 的类开始,并且定义了一个主方法。

namespace palomino
{
    class VincentDriver : public rclcpp::Node
    {
        // ...
    };
}


int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<palomino::VincentDriver>());
    rclcpp::shutdown();
    return 0;
}

这通常会在您的 Cmake 中编译为可执行文件。

# ...
add_executable(vincent_driver src/vincent_driver.cpp)
# ...
install(TARGETS vincent_driver
    DESTINATION lib/${PROJECT_NAME}
)

 代码更新 

添加包依赖项

您的 package.xml 应该依赖于 rclcpp_components ,类似于

<depend>rclcpp_components</depend>

或者,您可以独立添加一个 build_depend/exec_depend 。

<?xml version="1.0"?>
<!-- XML 版本声明 -->


<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!-- XML 模型声明,定义了 ROS 包的格式 -->


<package format="3">
<!-- 开始定义一个 ROS 包,格式为 3 -->


  <name>composition</name>
  <!-- 包的名称为 composition -->


  <version>0.33.4</version>
  <!-- 包的版本号为 0.33.4 -->


  <description>Examples for composing multiple nodes in a single process.</description>
  <!-- 包的描述 -->


  <maintainer email="aditya.pande@openrobotics.org">Aditya Pande</maintainer>
  <maintainer email="audrow@openrobotics.org">Audrow Nash</maintainer>
  <!-- 维护者的信息 -->


  <license>Apache License 2.0</license>
  <!-- 许可证信息 -->


  <author email="dthomas@osrfoundation.org">Dirk Thomas</author>
  <author email="mabel@openrobotics.org">Mabel Zhang</author>
  <!-- 作者的信息 -->


  <buildtool_depend>ament_cmake</buildtool_depend>
  <!-- 构建工具依赖项 -->


  <build_depend>example_interfaces</build_depend>
  <build_depend>rclcpp</build_depend>
  <build_depend>rclcpp_components</build_depend>
  <build_depend>rcutils</build_depend>
  <build_depend>std_msgs</build_depend>
  <!-- 构建依赖项 -->


  <exec_depend>example_interfaces</exec_depend>
  <exec_depend>launch_ros</exec_depend>
  <exec_depend>rclcpp</exec_depend>
  <exec_depend>rclcpp_components</exec_depend>
  <exec_depend>rcutils</exec_depend>
  <exec_depend>std_msgs</exec_depend>
  <!-- 执行依赖项 -->


  <test_depend>ament_cmake_pytest</test_depend>
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
  <test_depend>launch</test_depend>
  <test_depend>launch_testing</test_depend>
  <test_depend>launch_testing_ros</test_depend>
  <test_depend>launch_testing_ament_cmake</test_depend>
  <test_depend>rmw_implementation_cmake</test_depend>
  <!-- 测试依赖项 -->


  <export>
    <build_type>ament_cmake</build_type>
  </export>
  <!-- 导出构建类型 -->


</package>
<!-- 结束定义 ROS 包 -->

 类定义

您的类定义唯一可能需要更改的是确保类的构造函数https://github.com/ros2/demos/blob/jazzy/composition/src/talker_component.cpp 接受一个 NodeOptions 参数。

VincentDriver(const rclcpp::NodeOptions & options) : Node("vincent_driver", options)
{
  // ...
}
// 版权声明
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// 根据 Apache License 2.0(“许可证”)许可;
// 除非遵守许可证,否则不得使用此文件。
// 您可以在以下位置获取许可证副本:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// 除非适用法律要求或书面同意,否则在许可证下分发的软件
// 将按“原样”基础分发,无任何形式的明示或暗示保证。
// 请参阅许可证以了解管理权限和
// 许可证下的限制。


// 导入 Talker 组件头文件
#include "composition/talker_component.hpp"


// 导入所需的标准库
#include <chrono>
#include <iostream>
#include <memory>
#include <utility>


// 导入 ROS 客户端库和标准消息
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"


// 使用 std::chrono_literals 命名空间
using namespace std::chrono_literals;


// 定义 composition 命名空间
namespace composition
{


// 创建一个 Talker “组件”,该组件继承自通用的 rclcpp::Node 基类。
// 组件被构建成共享库,因此不编写自己的主函数。
// 使用组件的共享库的进程将实例化该类作为 ROS 节点。
Talker::Talker(const rclcpp::NodeOptions & options)
: Node("talker", options), count_(0)
{
  // 在 "chatter" 主题上创建一个 "std_mgs/String" 消息的发布者。
  pub_ = create_publisher<std_msgs::msg::String>("chatter", 10);


  // 使用定时器来定期发布消息。
  timer_ = create_wall_timer(1s, this {return this->on_timer();});
}


// 定时器回调函数
void Talker::on_timer()
{
  // 创建一个独特的消息
  auto msg = std::make_unique<std_msgs::msg::String>();
  // 设置消息数据
  msg->data = "Hello World: " + std::to_string(++count_);
  // 记录发布信息
  RCLCPP_INFO(this->get_logger(), "发布: '%s'", msg->data.c_str());
  // 刷新标准输出
  std::flush(std::cout);


  // 将消息放入队列中,由中间件处理。
  // 此调用是非阻塞的。
  pub_->publish(std::move(msg));
}


}  // namespace composition 结束


// 导入节点注册宏
#include "rclcpp_components/register_node_macro.hpp"


// 使用 class_loader 注册组件。
// 这相当于一个入口点,允许在其库被加载到正在运行的进程时发现组件。
RCLCPP_COMPONENTS_REGISTER_NODE(composition::Talker)

没有主方法了

将您的主方法替换为 pluginlib 风格的宏调用。

#include <rclcpp_components/register_node_macro.hpp>
RCLCPP_COMPONENTS_REGISTER_NODE(palomino::VincentDriver)

 小心

如果您要替换的主方法包含 MultiThreadedExecutor ,请务必记下并确保您的容器节点是多线程的。请参阅下面的部分。

 CMake 变更 

首先,在 CMakeLists.txt 中添加 rclcpp_components 作为依赖项:

find_package(rclcpp_components REQUIRED)

其次,我们将用一个新的目标名称替换我们的 add_executable 为 add_library 。

add_library(vincent_driver_component src/vincent_driver.cpp)

第三,将使用旧目标的其他构建命令替换为作用于新目标的命令。即 ament_target_dependencies(vincent_driver ...) 变为 ament_target_dependencies(vincent_driver_component ...)

第四,添加一个新命令来声明您的组件。

rclcpp_components_register_node(
    vincent_driver_component
    PLUGIN "palomino::VincentDriver"
    EXECUTABLE vincent_driver
)

第五,最后,将 CMake 中的任何安装命令从操作旧目标更改为安装库版本。例如,不要将任何目标安装到 lib/${PROJECT_NAME} 中。请用库安装替换。

ament_export_targets(export_vincent_driver_component)
install(TARGETS vincent_driver_component
        EXPORT export_vincent_driver_component
        ARCHIVE DESTINATION lib
        LIBRARY DESTINATION lib
        RUNTIME DESTINATION bin
)

运行您的节点

查看组合教程https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Composition.html ,以深入了解节点的组合。快速而简洁的版本是,如果您在 Python 启动文件中有以下内容,

from launch_ros.actions import Node


# ..


ld.add_action(Node(
    package='palomino',
    executable='vincent_driver',
    # ..
))

你可以将它替换掉

from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode


# ..
ld.add_action(ComposableNodeContainer(
    name='a_buncha_nodes',
    namespace='',
    package='rclcpp_components',
    executable='component_container',
    composable_node_descriptions=[
        ComposableNode(
            package='palomino',
            plugin='palomino::VincentDriver',
            name='vincent_driver',
            # ..
            extra_arguments=[{'use_intra_process_comms': True}],
        ),
    ]
))

 小心

如果您需要多线程,请不要将可执行文件设置为 component_container ,而应将其设置为 component_container_mt

相关推荐

  1. ROS2中级-编写组合节点 (C++)

    2024-07-11 04:04:01       22 阅读
  2. ROS2中级-编写动作服务器和客户端(Python)

    2024-07-11 04:04:01       21 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 04:04:01       51 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 04:04:01       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 04:04:01       44 阅读
  4. Python语言-面向对象

    2024-07-11 04:04:01       55 阅读

热门阅读

  1. Appium+Python自动化脚本的APK打包探索

    2024-07-11 04:04:01       20 阅读
  2. C++学习笔记(一):入门概念的疑问与解答

    2024-07-11 04:04:01       20 阅读
  3. 容器之docker compose

    2024-07-11 04:04:01       19 阅读
  4. 55、定义浅层神经网络架构和算法(matlab)

    2024-07-11 04:04:01       17 阅读
  5. FastApi+WebSocket 解析

    2024-07-11 04:04:01       16 阅读