ROS2 节点和话题
引言
本篇是ROS2最核心的通信模式:发布-订阅(Pub-Sub)
1. 发布-订阅模型的数学模型
1.1 基本概念
发布-订阅模型可以用集合论来描述:
Topic(t) = {m_1, m_2, ..., m_n} at time t
其中:
- 发布者节点 P_i 向话题 T_j 发送消息 m_i
- 订阅话题 T_j 的节点 S_k 接收消息
1.2 通信时序图

1.3 通信模式对比
- 1对多:一个发布节点广播消息到订阅节点
- 多对1:多个发布节点发送消息到一个订阅节点
- 多对多:多个发布节点发送消息到话题总线,多个接收节点从总线上接受消息
2. 话题通信的QoS配置
2.1 基本介绍
QoS(Quality of Service)定义了消息传递的可靠性和持久性,话题端点(Endpoint)的属性,发布者端和订阅者端各自独立配置,两者会进行"协商。配置时注意兼容问题,不兼容时会拒绝连接。
2.2 配置项
核心策略:
- history: 历史策略。决定是只保留最新的消息(KEEP_LAST)还是保留所有消息(KEEP_ALL)。默认值:KEEP_LAST
- depth: 队列深度。配合 history 使用,如果是 KEEP_LAST,这个值决定了队列里存几条消息。
- reliability: 可靠性策略。这是最重要的之一,决定是“必须收到”(RELIABLE)还是“丢了就丢了”(BEST_EFFORT)。默认值:RELIABLE
- durability: 持久性策略。决定“晚加入的订阅者能不能收到之前的消息”(TRANSIENT_LOCAL vs VOLATILE)。默认值:VOLATILE
高级/时间策略:
- deadline: 截止期。期望收到消息的最大时间间隔。如果超时没收到,会触发回调报错。 默认(无限制)
- lifespan: 消息寿命。消息在队列里能活多久。如果订阅者处理太慢,消息过期了就会被丢弃,不再发送。默认(无限制)
- liveliness: 存活策略。用来检测对方是不是还“活着”(比如节点挂了或者网线拔了)。 默认(AUTOMATIC)
- liveliness_lease_duration: 存活租约时间。配合上面的 liveliness 使用,定义“多久没动静就算挂了”。默认(无限制)
2.3 兼容性
QoS可靠性策略的兼容性:

QoS持久性策略的兼容性:

QoS时间期限策略的兼容性(假设x和y是任意有效的持续时间值):

QoS活力策略的兼容性:

QoS租期策略的兼容性(假设x和y是任意有效的持续时间值):

为了建立连接,所有影响兼容性的策略都必须兼容。例如,即使请求和提供的QoS配置文件在QoS可靠性策略方面兼容,但它们在QoS持久性策略方面不兼容,仍然不会建立连接。
如果未建立连接,则发布者和订阅者之间将不会传递任何消息。
3. 演示项目
3.1 构建功能包
API相关详解可查看官方文档https://docs.ros2.org/latest/api/rclcpp/
依次输入下面的命令,创建chapt3_ws工作空间、example_topic_rclcpp功能包和topic_publisher_01.cpp和topic_subscribe_01.cpp
mkdir -p chapt3/chapt3_ws/src
cd chapt3/chapt3_ws/src
ros2 pkg create example_topic_rclcpp --build-type ament_cmake --dependencies rclcpp
touch example_topic_rclcpp/src/topic_publisher_01.cpp
touch example_topic_rclcpp/src/topic_subscribe_01.cpp
workspace目录结构如下
chapt3_ws
└── src
└── example_topic_rclcpp
├── CMakeLists.txt
├── include
│ └── example_topic_rclcpp
├── package.xml
└── src
└── topic_publisher_01.cpp
└── topic_subscribe_01.cpp
修改CMakeLists.txt
add_executable(topic_subscribe_01 src/topic_subscribe_01.cpp)
ament_target_dependencies(topic_subscribe_01 rclcpp)
install(TARGETS
topic_subscribe_01
DESTINATION lib/${PROJECT_NAME}
)
3.2 发布者实现
定义了一个周期500ms发布字符类型消息到command话题的节点,qos配置depth=10
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class TopicPublisher01 : public rclcpp::Node
{
public:
// 构造函数,有一个参数为节点名称
TopicPublisher01(std::string name) : Node(name)
{
RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
// 创建发布者
command_publisher_ = this->create_publisher<std_msgs::msg::String>("command", 10);
// 创建定时器,500ms为周期,定时发布
timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&TopicPublisher01::timer_callback, this));
}
private:
void timer_callback()
{
// 创建消息
std_msgs::msg::String message;
message.data = "forward";
// 日志打印
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
// 发布消息
command_publisher_->publish(message);
}
// 声名定时器指针
rclcpp::TimerBase::SharedPtr timer_;
// 声明话题发布者指针
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr command_publisher_;
};
3.3 订阅者实现
定义了接收节点,根据接收到的消息,执行相应动作。qos配置depth=10.
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class TopicSubscribe01 : public rclcpp::Node
{
public:
TopicSubscribe01(std::string name) : Node(name)
{
RCLCPP_INFO(this->get_logger(), "大家好,我是%s.", name.c_str());
// 创建一个订阅者订阅话题
command_subscribe_ = this->create_subscription<std_msgs::msg::String>("command", 10, std::bind(&TopicSubscribe01::command_callback, this, std::placeholders::_1));
}
private:
// 声明一个订阅者
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr command_subscribe_;
// 收到话题数据的回调函数
void command_callback(const std_msgs::msg::String::SharedPtr msg)
{
double speed = 0.0f;
if(msg->data == "forward")
{
speed = 0.2f;
}
RCLCPP_INFO(this->get_logger(), "收到[%s]指令,发送速度 %f", msg->data.c_str(),speed);
}
};
3.4 导入消息接口
消息接口是ROS2通信时必须的一部分,通过消息接口ROS2才能完成消息的序列化和反序列化。ROS2为我们定义好了常用的消息接口,并生成了C++和Python的依赖文件,我们可以直接在程序中进行导入。
ament_cmake类型功能包导入消息接口分为三步:
在CMakeLists.txt中导入,具体是先find_packages再ament_target_dependencies。
在packages.xml中导入,具体是添加depend标签并将消息接口写入。
在代码中导入,C++中是#include"消息功能包/xxx/xxx.hpp"。
我们依次做完这三步后文件内容如下:
CMakeLists.txt
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
...
add_executable(topic_publisher_01 src/topic_publisher_01.cpp)
ament_target_dependencies(topic_publisher_01 rclcpp std_msgs)
install(TARGETS
topic_publisher_01
DESTINATION lib/${PROJECT_NAME}
)
add_executable(topic_publisher_01 src/topic_subscribe_01.cpp)
ament_target_dependencies(topic_subscribe_01 rclcpp std_msgs)
install(TARGETS
topic_subscribe_01
DESTINATION lib/${PROJECT_NAME}
)
packages.xml
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
代码文件topic_publisher_01.cpp、topic_subscribe_01.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
3.5 运行及监控工具命令
编译,source,运行
cd chapt3_ws/
colcon build --packages-select example_topic_rclcpp
source install/setup.bash
ros2 run example_topic_rclcpp topic_publisher_01
ros2 run example_topic_rclcpp topic_subscribe_01
监控工具
- 查看话题列表
# 列出所有活跃的话题
ros2 topic list
# 输出示例:
# /command
# /parameter_events
# /rosout
- 监听话题消息
# 实时查看话题上的消息
ros2 topic echo /command
# 输出示例:
# data: forward
# ---
# data: forward
- 查看话题信息
# 显示话题的详细信息,节点数量
ros2 topic info /command
# 输出:
# Type: std_msgs/msg/String
# Publisher count: 1
# Subscription count: 1
- 查看消息统计信息
# 显示话题的消息速率等信息
ros2 topic hz /command
# 输出示例:
#average rate: 1.944
# min: 0.431s max: 0.650s std dev: 0.08708s window: 4
#average rate: 2.053
# min: 0.399s max: 0.650s std dev: 0.07737s window: 7
浙公网安备 33010602011771号