目标:本教程将展示如何在 ROS 2 中使用 Fast DDS 的扩展配置功能。
教程级别:高级
时间:20 分钟
目录
背景
先决条件
在同一个节点中混合同步和异步发布
创建具有发布者的节点
创建包含配置文件的 XML 文件
执行发布者节点
创建一个包含订阅者的节点
执行订阅者节点
示例分析
使用其他 FastDDS 功能与 XML
限制匹配订阅者的数量
在主题内使用分区
配置服务和客户端
使用服务和客户端创建节点
为服务和客户端创建 XML 配置文件
执行节点
背景
ROS 2 堆栈和 Fast DDS 之间的接口由 ROS 2 中间件实现 rmw_fastrtps 提供。此实现可在所有 ROS 2 发行版中使用,无论是从二进制文件还是从源代码。
ROS 2 RMW 仅允许配置某些中间件 QoS(参见 ROS 2 QoS 策略 https://docs.ros.org/en/jazzy/Concepts/Intermediate/About-Quality-of-Service-Settings.html )。然而, rmw_fastrtps
提供了扩展的配置功能,以充分利用 Fast DDS 中的功能。本教程将通过一系列示例指导您如何使用 XML 文件解锁此扩展配置。
为了获得有关在 ROS 2 上使用 Fast DDS 的更多信息,请查看以下文档。https://fast-dds.docs.eprosima.com/en/latest/fastdds/ros2/ros2.html
sudo apt install ros-jazzy-rmw-fastrtps-cpp
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export RMW_IMPLEMENTATION=rmw_fastrtps_dynamic_cpp
RMW_IMPLEMENTATION=rmw_fastrtps_cpp ros2 run <package> <application>
RMW_IMPLEMENTATION=rmw_fastrtps_dynamic_cpp ros2 run <package> <application>
先决条件
本教程假设您知道如何创建一个包。它还假设您知道如何编写一个简单的发布者和订阅者以及一个简单的服务和客户端。尽管示例是用 C++实现的,但相同的概念也适用于 Python 包。
在同一个节点中混合同步和异步发布
在这个第一个例子中,将创建一个具有两个发布者的节点,其中一个是同步发布模式,另一个是异步发布模式。
rmw_fastrtps
默认使用同步发布模式。
在同步发布模式下,数据直接在用户线程的上下文中发送。这意味着在写操作期间发生的任何阻塞调用都会阻塞用户线程,从而阻止应用程序继续运行。然而,由于线程之间没有通知或上下文切换,这种模式通常在较低的延迟下产生更高的吞吐量。
另一方面,在异步发布模式下,每次发布者调用写操作时,数据会被复制到队列中,后台线程(异步线程)会收到有关队列中新增数据的通知,并在数据实际发送之前将线程的控制权返回给user。后台线程负责消费队列并将数据发送给每个匹配的reader。
创建带有发布者的节点
首先,在新的工作区上创建一个名为 sync_async_node_example_cpp
的新包:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 --dependencies rclcpp std_msgs -- sync_async_node_example_cpp
然后,向包中添加一个名为 src/sync_async_writer.cpp
的文件,内容如下。请注意,同步发布者将发布在主题 sync_topic
上,而异步发布者将发布在主题 async_topic
上。
#include <chrono> // 包含用于时间操作的头文件
#include <functional> // 包含用于函数对象和绑定的头文件
#include <memory> // 包含用于智能指针的头文件
#include <string> // 包含用于字符串操作的头文件
#include "rclcpp/rclcpp.hpp" // 包含ROS 2的C++客户端库
#include "std_msgs/msg/string.hpp" // 包含标准消息类型String
using namespace std::chrono_literals; // 使用chrono命名空间中的字面量
class SyncAsyncPublisher : public rclcpp::Node // 定义一个名为SyncAsyncPublisher的类,继承自rclcpp::Node
{
public:
SyncAsyncPublisher() // 构造函数
: Node("sync_async_publisher"), count_(0) // 初始化节点名称为sync_async_publisher,计数器count_初始化为0
{
// 创建一个同步发布者,发布到主题'sync_topic'
sync_publisher_ = this->create_publisher<std_msgs::msg::String>("sync_topic", 10);
// 创建一个异步发布者,发布到主题'async_topic'
async_publisher_ = this->create_publisher<std_msgs::msg::String>("async_topic", 10);
// 定义一个定时器回调函数,每次定时器触发时执行的操作
auto timer_callback = this{
// 创建一个新的消息
auto sync_message = std_msgs::msg::String();
sync_message.data = "SYNC: Hello, world! " + std::to_string(count_);
// 将消息记录到控制台以显示进度
RCLCPP_INFO(this->get_logger(), "Synchronously publishing: '%s'", sync_message.data.c_str());
// 使用同步发布者发布消息
sync_publisher_->publish(sync_message);
// 创建一个新的消息
auto async_message = std_msgs::msg::String();
async_message.data = "ASYNC: Hello, world! " + std::to_string(count_);
// 将消息记录到控制台以显示进度
RCLCPP_INFO(this->get_logger(), "Asynchronously publishing: '%s'", async_message.data.c_str());
// 使用异步发布者发布消息
async_publisher_->publish(async_message);
// 准备下一条消息的计数
count_++;
};
// 创建一个定时器,每隔半秒触发一次,执行定时器回调函数
timer_ = this->create_wall_timer(500ms, timer_callback);
}
private:
// 定时器,每隔半秒触发一次,发布新的数据
rclcpp::TimerBase::SharedPtr timer_;
// 异步发布者
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr async_publisher_;
// 同步发布者
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr sync_publisher_;
// 已发送的消息数量
size_t count_;
};
int main(int argc, char * argv[]) // 主函数
{
rclcpp::init(argc, argv); // 初始化ROS 2
rclcpp::spin(std::make_shared<SyncAsyncPublisher>()); // 创建SyncAsyncPublisher节点并运行
rclcpp::shutdown(); // 关闭ROS 2
return 0; // 返回0表示程序正常结束
}
现在打开 CMakeLists.txt
文件,添加一个新的可执行文件并将其命名为 SyncAsyncWriter
,以便您可以使用 ros2 run
运行您的节点:
add_executable(SyncAsyncWriter src/sync_async_writer.cpp)
ament_tar