ROS2 话题实例解析
一、ROS2 话题基础
在 ROS2 中,话题是一种发布/订阅(Pub/Sub)模式的消息传递机制。发布者(Publisher)节点向某个话题发送消息,而订阅者(Subscriber)节点则接收该话题上的消息。这种机制允许节点间在不直接连接的情况下进行通信,极大地提高了系统的灵活性和可扩展性。
话题具有以下特点:
异步性:
发布者发送消息与订阅者接收消息是异步进行的,互不干扰。
多对多关系:
一个发布者可以向多个订阅者发送消息,同时一个订阅者也可以接收多个发布者的消息。
消息类型:
每个话题都有明确的消息类型,发布者发送的消息类型必须与订阅者期望的消息类型一致。
二、ROS2 话题实例
为了更直观地理解 ROS2 话题,下面通过一个简单的实例进行说明。假设我们有一个机器人系统,其中一个节点负责检测环境中的障碍物(发布者),另一个节点则根据障碍物信息规划路径(订阅者)。
1. 消息类型定义
首先,我们需要定义一个用于传递障碍物信息的消息类型。在 ROS2 中,消息类型通常使用 IDL(接口描述语言)进行定义,并存储在 .msg
文件中。
# ObstacleMessage.msg
string id
float32 position_x
float32 position_y
float32 size
上述消息类型包含障碍物的 ID、位置(x、y 坐标)和大小。
2. 发布者节点实现
接下来,我们实现一个发布者节点,该节点定期发布障碍物信息。
#include <chrono>
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include "obstacle_msgs/msg/obstacle_message.hpp"
class ObstaclePublisherNode : public rclcpp::Node
{
public:
ObstaclePublisherNode()
: Node("obstacle_publisher"), count_(0)
{
publisher_ = this->create_publisher<obstacle_msgs::msg::ObstacleMessage>(
"obstacle_topic", 10);
timer_ = this->create_wall_timer(
std::chrono::seconds(1), std::bind(&ObstaclePublisherNode::timer_callback, this));
}
private:
void timer_callback()
{
auto message = obstacle_msgs::msg::ObstacleMessage();
message.id = "obstacle_" + std::to_string(count_);
message.position_x = 1.0f * count_;
message.position_y = 2.0f * count_;
message.size = 0.5f;
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.id.c_str());
publisher_->publish(message);
count_++;
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<obstacle_msgs::msg::ObstacleMessage>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ObstaclePublisherNode>());
rclcpp::shutdown();
return 0;
}
在上述代码中,我们创建了一个名为 obstacle_publisher
的节点,并定义了一个定时器,每秒发布一次障碍物信息。
3. 订阅者节点实现
最后,我们实现一个订阅者节点,该节点接收障碍物信息并进行处理。
#include <memory>
#include <rclcpp/rclcpp.hpp>
#include "obstacle_msgs/msg/obstacle_message.hpp"
class ObstacleSubscriberNode : public rclcpp::Node
{
public:
ObstacleSubscriberNode()
: Node("obstacle_subscriber")
{
subscription_ = this->create_subscription<obstacle_msgs::msg::ObstacleMessage>(
"obstacle_topic", 10,
std::bind(&ObstacleSubscriberNode::topic_callback, this, std::placeholders::_1));
}
private:
void topic_callback(const obstacle_msgs::msg::ObstacleMessage::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s' at (%f, %f) with size %f",
msg->id.c_str(), msg->position_x, msg->position_y, msg->size);
}
rclcpp::Subscription<obstacle_msgs::msg::ObstacleMessage>::SharedPtr subscription_;
};
int main(int argc, char *argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<ObstacleSubscriberNode>());
rclcpp::shutdown();
return 0;
}
在上述代码中,我们创建了一个名为 obstacle_subscriber
的节点,并订阅了 obstacle_topic
话题。每当收到消息时,该节点会打印出障碍物的信息。