目录
起点
代码更新
添加包依赖
类定义
没有主方法
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