深入解析ROS 2的通用发布者创建:create_generic_publisher
函数逐行解析
概述
在 ROS 2 中,发布者是节点用于发布消息的重要组件。通常情况下,发布者的类型在编译时就已经确定。然而,有时我们需要在运行时动态指定发布者的消息类型。这种需求可以通过通用发布者 (GenericPublisher
) 来满足。本文将详细解析 create_generic_publisher
函数的实现,逐行解释其工作原理,并展示如何在 ROS 2 中创建一个通用发布者。
源码
首先,让我们看一下 create_generic_publisher
函数的完整源代码:
// Copyright 2020, Apex.AI Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
#define RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/generic_publisher.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/typesupport_helpers.hpp"
namespace rclcpp
{
/// Create and return a GenericPublisher.
/**
* The returned pointer will never be empty, but this function can throw various exceptions, for
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
*
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup
* \param topic_name Topic name
* \param topic_type Topic type
* \param qos %QoS settings
* \param options %Publisher options.
* Not all publisher options are currently respected, the only relevant options for this
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
*/
template<typename AllocatorT = std::allocator<void>>
std::shared_ptr<GenericPublisher> create_generic_publisher(
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
)
)
{
auto ts_lib = rclcpp::get_typesupport_library(topic_type, "rosidl_typesupport_cpp");
auto pub = std::make_shared<GenericPublisher>(
topics_interface->get_node_base_interface(),
std::move(ts_lib),
topic_name,
topic_type,
qos,
options);
topics_interface->add_publisher(pub, options.callback_group);
return pub;
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
逐行解析
文件头部
// Copyright 2020, Apex.AI Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
与大多数开源项目一样,这里首先是版权声明和许可证信息。它明确了该代码的使用受 Apache License 2.0 约束,允许用户在遵循许可证的前提下使用、修改和分发代码。
头文件保护
#ifndef RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
#define RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
这一部分是头文件保护宏,用于防止头文件被多次包含。#ifndef
检查 RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
是否已经定义,如果没有定义,则继续定义它并包含文件内容。这是 C++ 中的一种常见实践,确保同一头文件不会被多次包含,从而避免重复定义的问题。
包含的头文件
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/generic_publisher.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/typesupport_helpers.hpp"
这些头文件提供了创建通用发布者所需的基础设施:
<memory>
:用于智能指针管理。<string>
:用于处理字符串。<utility>
:提供了一些常用的实用函数,如std::move
。rclcpp
相关头文件:这些头文件为 ROS 2 的发布者、节点接口、QoS 设置以及类型支持库提供了定义和功能支持。
命名空间声明
namespace rclcpp
{
这一行代码声明了 rclcpp
命名空间。rclcpp
是 ROS 2 的核心命名空间之一,包含了与客户端、服务、节点、发布者、订阅者等相关的功能。
create_generic_publisher
函数
/// Create and return a GenericPublisher.
/**
* The returned pointer will never be empty, but this function can throw various exceptions, for
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
*
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup
* \param topic_name Topic name
* \param topic_type Topic type
* \param qos %QoS settings
* \param options %Publisher options.
* Not all publisher options are currently respected, the only relevant options for this
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
*/
template<typename AllocatorT = std::allocator<void>>
std::shared_ptr<GenericPublisher> create_generic_publisher(
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
)
)
函数头解析
-
函数声明:
- 该函数是一个模板函数,默认使用
std::allocator<void>
作为分配器类型AllocatorT
。 - 返回类型为
std::shared_ptr<GenericPublisher>
,即一个指向GenericPublisher
对象的智能指针。
- 该函数是一个模板函数,默认使用
-
参数解析:
topics_interface
: 指向NodeTopicsInterface
的共享指针,用于节点与话题相关的接口操作。topic_name
: 要发布消息的话题名称。topic_type
: 要发布的消息类型(以字符串形式表示,例如"std_msgs/msg/String"
)。qos
: 质量服务设置(QoS),控制消息的传递行为,如可靠性、历史记录等。options
: 发布者选项,可以指定回调组、事件回调等配置。
-
函数文档注释:
- 文档注释说明了函数的行为:返回的指针不会为空,但在某些情况下可能会抛出异常,例如无法找到消息的包时。
函数实现
{
auto ts_lib = rclcpp::get_typesupport_library(topic_type, "rosidl_typesupport_cpp");
- 解释:
- 这行代码调用
rclcpp::get_typesupport_library
函数,根据指定的topic_type
和类型支持标识符"rosidl_typesupport_cpp"
来获取消息类型的类型支持库(Type Support Library)。 rosidl_typesupport_cpp
是 ROS 2 中用于 C++ 的类型支持库的标识符。
- 这行代码调用
auto pub = std::make_shared<GenericPublisher>(
topics_interface->get_node_base_interface(),
std::move(ts_lib),
topic_name,
topic_type,
qos,
options);
- 解释:
- 使用
std::make_shared
创建一个GenericPublisher
对象的共享指针pub
。 - 构造
GenericPublisher
对象时,传入了节点的基础接口(通过topics_interface->get_node_base_interface()
获取)、类型支持库、话题名称、话题类型、QoS 设置以及发布者选项。 - 这里使用了
std::move(ts_lib)
将类型支持库的所有权转移给GenericPublisher
,以避免不必要的拷贝。
- 使用
继续解析 create_generic_publisher
函数的实现部分:
topics_interface->add_publisher(pub, options.callback_group);
- 解释:
- 这行代码将刚创建的
GenericPublisher
对象添加到topics_interface
中进行管理。topics_interface->add_publisher
方法将发布者对象与特定的回调组关联,以便在消息发布过程中,回调函数能够被正确地调度和执行。 options.callback_group
是一个回调组,可以为空(nullptr
),如果为空,则使用节点的默认回调组。
- 这行代码将刚创建的
return pub;
}
- 解释:
- 该函数返回创建的
GenericPublisher
对象的共享指针pub
。这个指针由调用方持有和管理,确保在其生命周期内发布者对象不会被销毁。 - 返回的
GenericPublisher
对象允许在运行时动态地发布任意类型的消息,灵活性非常高。
- 该函数返回创建的
结束 rclcpp
命名空间与头文件保护
} // namespace rclcpp
#endif // RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
- 解释:
- 这部分代码关闭了
rclcpp
命名空间,并结束了头文件的防重复包含保护。
- 这部分代码关闭了
总结
create_generic_publisher
函数为 ROS 2 提供了一种在运行时动态创建发布者的机制。与特定消息类型的发布者不同,通用发布者 (GenericPublisher
) 允许开发者在运行时指定消息类型和话题名称,从而提高了系统的灵活性和可扩展性。函数通过使用 rclcpp::get_typesupport_library
来获取类型支持库,并结合 GenericPublisher
对象,创建一个强大的工具,满足动态消息发布的需求。
在 ROS 2 的开发中,理解和使用 create_generic_publisher
函数可以大大简化处理不同消息类型的工作,尤其是在需要处理动态消息类型的复杂应用中。这种通用化的设计使得代码更易于维护和扩展,对于开发者来说是一个非常有用的工具。