ros通信机制学习——latched持久化机制
点云的地图的发送逻辑中,我发现每次使用rostopic echo 时只会打印一次,然后就不会再打印了。并且rviz中也是始终都会显示的,这里面其实就是用到了latched持久话机制,可以接受这最后一次发布的消息。
我们通过一个具体的项目来学习和认识这个过程
latched话题:
你创建的发布者使用了 latching(持久化)机制 (advertise<sensor_msgs::PointCloud2>(map_topic, 10, true) 中的 true 参数)。Latching 意味着最后一次发布的信息会被保存下来,新的订阅者连接时会立即接收到这条消息。因此,每当一个新的订阅者(如 rostopic echo)连接到这个话题时,它都会立即接收到那条被 latched 的消息。
源码信息:
头文件
#ifndef _MAP_LOADER_HPP_
#define _MAP_LOADER_HPP_
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl_conversions/pcl_conversions.h>
#include <ros/ros.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/PointCloud2.h>
#include <vector>
#include <pcl_ros/transforms.h>
using namespace std;
class MapLoader {
public:
ros::Publisher pc_map_pub_;
vector<string> file_list_;
MapLoader(ros::NodeHandle &nh);
private:
sensor_msgs::PointCloud2 CreatePcd();
};
#endif
map_load.cpp
#include <map_load/map_load.h>
using namespace std;
MapLoader::MapLoader(ros::NodeHandle &nh){
std::string pcd_file, map_topic;
nh.param<string>("pcd_path", pcd_file_path, "");
nh.param<string>("map_topic", map_topic, "point_map");
pc_map_pub = nh.advertise<sensor_msgs::PointCloud2>(map_topic, 10, true);
//latched话题:创建的发布者使用了 latching(持久化)机制
// (advertise<sensor_msgs::PointCloud2>(map_topic, 10, true) 中的 true 参数)
// Latching 意味着最后一次发布的信息会被保存下来,新的订阅者连接时会立即接收到这条消息。
// 每当一个新的订阅者(如 rostopic echo)连接到这个话题时,它都会立即接收到那条被 latched 的消息
file_list_.push_back(pcd_file_path);
auto pc_msg = CreatePcd();
cout << "pc_msg.width" << "\n" << pc_msg.width << endl;
if (pc_msg.width != 0) {
pc_msg.header.frame_id = "map";
pc_map_pub_.publish(pc_msg);
}
}
sensor_msgs::PointCloud2 MapLoader::CreatePcd() {
sensor_msgs::PointCloud2 pc_msg;
pcl::PointCloud<pcl::PointXYZ> pcd, part;
int pcd_width = 0;
for (const string& path : file_list_) {
if (pcd_width == 0) {
if (pcl::io::loadPCDFile(path.c_str(), pcd) == -1) {
cerr << "load failed " << path << endl;
}
}
else {
if (pcl::io::loadPCDFile(path.c_str(), part) == -1) {
cerr << "load failed " << path << endl;
}
pcd.width += part.width;
pcd.row_step += part.row_step;
pcd.data.insert(pcd.data.end(), part.data.begin(), part.data.end());
}
cerr << " load " << path << endl;
if (!ros::ok()) break;
}
return pcd;
}
int main(int argc, char** argv) {
ros::init(argc, argv, "map_loader");
ROS_INFO("\033[32m ---> \033[0m Map Loader Started.");
ros::NodeHandle nh;
ros::spin();
return 0;
}
然后我们看launch文件中的传参
<launch>
<!--- Sim Time -->
<param name="/use_sim_time" value="false" />
<!--- Run Rviz-->
<!-- <node pkg="rviz" type="rviz" name="rviz" args="-d $(find map_load)/rviz/map_show.rviz" /> -->
<!--- MapLoader -->
<arg name="pcd_path" default="$(find map_load)/map/map_Statistical_filter.pcd"/>
<arg name="map_topic" default="point_map"/>
<node pkg="map_load" type="mapload" name="mapLoader" output="screen">
<param name="pcd_path" value="$(arg pcd_path)"/>
<param name="map_topic" value="$(arg map_topic)"/>
</node>
</launch>
主要就是两个参数,一个是pcd的路径,一个是map发布的话题运行后:
此时可以查看一下话题的发布情况,可以看到
rostopic list
rostopic hz /mapLoader/point_map
所以就是没有数据在这里发布,也就不会占用带宽.我们卻是可以打印出来的:
rviz中也是可以显示出对应的点云地图的,这个方式非常适合那些不会变化的数据的内容.
我们从原理上深入分析一下:
ROS Publisher Latch 机制详解
1. 核心含义
- 功能定义:
latch=true
启用持久化消息机制,发布者会缓存最后一次发送的消息。 - 核心作用:
新订阅者连接到话题时,立即收到缓存的最后一条消息,无需等待下一次发布。
2. 底层机制
机制 | 说明 |
---|---|
消息存储 | ROS内部维护一个长度为1的队列,仅保留最新消息 |
触发条件 | 新订阅者通过 ros::Subscriber 连接时触发 |
生命周期 | 缓存消息保留至新消息发布或节点关闭 |
3. 适用场景
场景1:静态数据发布(如地图)
// 只需加载一次,新节点需立即获取
pc_map_pub_ = nh.advertise<sensor_msgs::PointCloud2>("map", 10, true);
场景2:低频更新数据(如初始位姿)
// 新的GUI模块启动后需立刻显示当前位置
pose_pub_ = nh.advertise<geometry_msgs::PoseStamped>("initial_pose", 1, true);
场景3:服务类数据(如全局路径)
// 控制模块可能稍后启动,需确保其能获取路径
path_pub_ = nh.advertise<nav_msgs::Path>("global_plan", 1, true);
4. 禁用 Latch 的对比
// 默认行为:latch=false
pc_map_pub_ = nh.advertise<sensor_msgs::PointCloud2>("map", 10);
新订阅者体验 立即收到最后消息 必须等待下次 publish() 调用
数据连续性 适合单次发布场景 需要持续发布维持数据流
5. 实际效果验证
# 终端1:发布地图(仅一次)
rosrun map_loader map_loader _pcd_path:=/home/user/map.pcd
# 终端2:查看话题
rostopic echo /map # 立即显示数据
# 禁用 Latch 时的表现
rostopic echo /map # 无输出,直到再次调用 publish()
6注意事项
- List item
内存管理
大消息(如10万+点云)会持续占用内存,需评估消息大小
- ROS 2 对应机制
通过QoS策略实现:
// ROS 2 等效实现
auto qos = rclcpp::QoS(10).durability(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL);
pc_map_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>("map", qos);
- 最佳实践指南
ROS Publisher Latch 参数对比表
对比维度 | latch=true (持久化) | latch=false (非持久化,默认) |
---|---|---|
新订阅者连接行为 | 立即收到最后一次发布的消息 | 必须等待下一次 publish() 调用 |
消息存储机制 | ROS内部维护长度为1的队列,保留最新消息 | 不存储任何历史消息 |
适用数据类型 | 静态数据(地图/参数)、低频更新数据(路径/位姿) | 高频动态数据(激光/图像/IMU) |
内存占用 | 存储完整消息副本(需注意大消息内存消耗) | 无额外占用 |
典型场景 | 初始化配置、服务类结果、需即时同步的全局数据 | 实时数据流、连续状态更新 |
调试验证方法 | rostopic info /topic 显示 LATCH: true | rostopic echo /topic 只在发布后显示新数据 |
队列机制 | 固定队列长度=1(强制保留最新消息) | 队列长度由 advertise() 的第二个参数定义 |
ROS 2 等效实现 | DurabilityPolicy::TRANSIENT_LOCAL | DurabilityPolicy::VOLATILE |
典型代码示例 | pub = nh.advertise<MapMsg>("map", 10, true); | pub = nh.advertise<LaserScan>("scan", 10); |
数据更新影响 | 新消息会覆盖缓存,后续订阅者收到最新版本 | 旧订阅者按队列长度接收历史消息,新订阅者只收新消息 |