1. 学习目标
-
什么是 Topic
-
什么是 Publisher 和 Subscriber
-
Topic 通信流程是什么
-
ROS2 Topic 为什么是异步通信
-
如何编写一个发布者和订阅者
2. 什么是 Topic
Topic 是 ROS2 中 最常用的通信方式。
通信模型:
Publisher → Topic → Subscriber
结构图:
Publisher
|
| publish
v
Topic
|
| subscribe
v
Subscriber
简单理解:
Publisher 发布数据,Subscriber 订阅数据
3. Topic 通信示例
例如:
机器人激光雷达:
lidar_node
|
| 发布数据
v
/scan
|
| 订阅数据
v
slam_node
系统结构:
+-------------+
| lidar_node |
+-------------+
|
| publish
v
/scan topic
|
| subscribe
v
+-------------+
| slam_node |
+-------------+
4. Topic 的核心特点
Topic 有三个重要特点:
- 异步通信
发布者和订阅者 互不阻塞。
Publisher 只负责发送
Subscriber 只负责接收
二者互不影响。
- 一对多通信
一个 Publisher 可以有多个 Subscriber。
lidar_node
|
v
/scan
/ | \
v v v
slam_node map_node viewer_node
- 解耦设计
Publisher 不需要知道谁在订阅。
Subscriber 也不知道谁在发布。
只要 Topic 名字相同即可通信。
5. Topic 的组成
一个 Topic 通信需要三个要素:
Topic Name
Message Type
Publisher / Subscriber
例如:
Topic Name: /scan
Message Type: sensor_msgs/LaserScan
Publisher: lidar_node
Subscriber: slam_node
6. ROS2 Topic 通信流程
Topic 通信流程:
Publisher 创建 Topic
|
v
发布 Message
|
v
ROS2 中间件
|
v
Subscriber 接收 Message
系统结构:
Publisher
|
| message
v
DDS Middleware
|
v
Subscriber
ROS2 实际使用 DDS 中间件 实现通信。
7. ROS2 Topic 示例(C++)
下面写一个最简单的:
发布者 + 订阅者
7.1 Publisher(发布节点)
创建文件:
publisher_node.cpp
代码:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class PublisherNode : public rclcpp::Node
{
public:
PublisherNode() : Node("publisher_node")
{
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
timer_ = this->create_wall_timer(
std::chrono::seconds(1),
std::bind(&PublisherNode::publish_message, this));
}
private:
void publish_message()
{
auto message = std_msgs::msg::String();
message.data = "Hello ROS2";
publisher_->publish(message);
RCLCPP_INFO(this->get_logger(), "Publish: %s", message.data.c_str());
}
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<PublisherNode>();
rclcpp::spin(node);
rclcpp::shutdown();
}
7.2 Subscriber(订阅节点)
创建:
subscriber_node.cpp
代码:
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode() : Node("subscriber_node")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"chatter",
10,
std::bind(&SubscriberNode::topic_callback, this, std::placeholders::_1));
}
private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg)
{
RCLCPP_INFO(this->get_logger(), "Receive: %s", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<SubscriberNode>();
rclcpp::spin(node);
rclcpp::shutdown();
}
8. 修改 CMakeLists.txt
添加:
add_executable(publisher_node src/publisher_node.cpp)
add_executable(subscriber_node src/subscriber_node.cpp)
ament_target_dependencies(publisher_node rclcpp std_msgs)
ament_target_dependencies(subscriber_node rclcpp std_msgs)
install(TARGETS
publisher_node
subscriber_node
DESTINATION lib/${PROJECT_NAME})
9. 编译运行
编译:
colcon build
加载环境:
source install/setup.bash
运行发布节点:
ros2 run my_package publisher_node
运行订阅节点:
ros2 run my_package subscriber_node
终端输出:
Publish: Hello ROS2
Receive: Hello ROS2
说明通信成功。
10. ROS2 Topic 调试工具
ROS2 提供很多调试命令。
查看 Topic:
ros2 topic list
查看 Topic 信息:
ros2 topic info /chatter
查看消息内容:
ros2 topic echo /chatter
查看消息类型:
ros2 topic type /chatter
查看发送频率:
ros2 topic hz /chatter
11. 总结
ROS2 Topic 本质:
Publisher → Topic → Subscriber
特点:
异步通信
一对多
解耦设计
系统结构:
Publisher
|
v
Topic
|
v
Subscriber
12. Q&A
-
Topic 是什么
-
Publisher 和 Subscriber 的关系是什么
-
为什么 Topic 是异步通信
-
Topic 为什么支持一对多
-
Topic 和 Service 的区别是什么