ROS多节点LIOSAM改造:解决命名冲突与资源竞争
1. 项目背景与需求分析
在机器人SLAM(同步定位与建图)开发中,LIOSAM作为基于激光雷达的惯性紧耦合SLAM系统,常被用于复杂环境下的高精度建图。但在实际部署时,我们经常遇到一个典型问题:当需要在同一台主控设备(master)上运行多个LIOSAM节点时,系统会出现命名冲突和资源竞争。
这种情况在以下场景尤为常见:
- 多机器人协同作业时,需要在一台中央控制电脑上监控所有机器人的建图状态
- 开发阶段需要同时测试不同参数配置的建图效果
- 大规模场景建图时,需要分区域运行多个建图节点
2. 核心问题诊断
2.1 默认实现的局限性
原始LIOSAM代码设计时主要考虑单节点运行场景,存在以下关键限制:
话题命名冲突:
- 所有节点默认发布相同话题(如
/laser_odom、/map) - 导致消息路由混乱,无法区分不同节点的数据流
- 所有节点默认发布相同话题(如
TF帧命名冲突:
- 固定使用
base_link、odom等帧名称 - 多节点运行时会导致TF树结构破坏
- 固定使用
参数服务器冲突:
- 所有节点读取相同的参数命名空间
- 无法实现差异化配置
2.2 ROS多节点运行原理
要实现安全的多节点并行,需要理解ROS的命名空间机制:
- 节点名称:每个节点必须有唯一标识(如
/lio_sam_1) - 话题命名:支持相对命名(
odom)和全局命名(/robot1/odom) - 参数命名空间:通过
~指定私有命名空间(如~/scan_period)
3. 具体改造方案
3.1 节点启动文件改造
修改launch/run.launch文件,增加命名空间支持:
<launch> <!-- 添加namespace参数 --> <arg name="robot_ns" default="robot1"/> <group ns="$(arg robot_ns)"> <node pkg="liosam" type="liosam_node" name="liosam_node" output="screen"> <!-- 重映射所有话题 --> <remap from="/scan" to="$(arg robot_ns)/scan"/> <remap from="/odometry/gps" to="$(arg robot_ns)/odometry/gps"/> <!-- 其他必要话题重映射... --> </node> </group> </launch>3.2 核心代码修改点
在src/liosam_node.cpp中需要调整以下关键部分:
- 话题发布重命名:
// 原始代码 pubLaserOdometry = nh.advertise<nav_msgs::Odometry>("/laser_odom", 5); // 修改为 ros::NodeHandle private_nh("~"); std::string laser_odom_topic; private_nh.param<std::string>("laser_odom_topic", laser_odom_topic, "laser_odom"); pubLaserOdometry = private_nh.advertise<nav_msgs::Odometry>(laser_odom_topic, 5);- TF帧命名处理:
// 在类成员变量中添加 std::string tf_prefix; // 在初始化函数中获取命名空间 tf_prefix = ros::this_node::getNamespace(); if (tf_prefix != "/") { tf_prefix = tf_prefix.substr(1); // 去除开头的/ } // 发布TF时添加前缀 tf::StampedTransform transform; transform.frame_id_ = tf_prefix + "/" + "odom"; transform.child_frame_id_ = tf_prefix + "/" + "base_link";3.3 参数配置文件调整
修改config/params.yaml支持多实例:
%YAML 1.1 --- # 公共参数 common: scan_period: 0.1 max_range: 100.0 # 各机器人专用参数 robot1: ~scan_period: 0.1 ~max_range: 50.0 robot2: ~scan_period: 0.2 ~max_range: 75.04. 完整部署流程
4.1 多节点启动命令
# 终端1 - 启动第一个节点 roslaunch liosam run.launch robot_ns:=robot1 # 终端2 - 启动第二个节点 roslaunch liosam run.launch robot_ns:=robot24.2 验证方法
- 查看节点列表:
rosnode list # 应显示/robot1/liosam_node和/robot2/liosam_node- 检查话题命名:
rostopic list # 应包含/robot1/laser_odom和/robot2/laser_odom等- TF树检查:
rosrun tf view_frames # 生成的frames.pdf中应能看到robot1/odom->robot1/base_link等独立结构5. 常见问题与解决方案
5.1 TF帧异常问题
现象:RVIZ中显示坐标帧错乱
解决方案:
- 确保所有TF广播都添加了命名空间前缀
- 在RVIZ中设置
Global Options->Fixed Frame为特定机器人的odom帧(如robot1/odom)
5.2 话题订阅失败
现象:节点无法接收到传感器数据
排查步骤:
- 使用
rostopic hz检查原始话题数据频率 - 确认launch文件中所有
<remap>标签配置正确 - 检查节点代码中的订阅话题名称是否使用相对命名
5.3 参数读取错误
现象:节点读取到错误的参数值
解决方法:
- 确保参数服务器加载了正确的yaml文件
- 使用
rosparam list检查参数命名空间 - 在代码中使用
private_nh.param()读取私有参数
6. 性能优化建议
- 共享静态TF广播:
<!-- 在launch文件中添加 --> <node pkg="tf" type="static_transform_publisher" name="static_tf" args="0 0 0 0 0 0 map $(arg robot_ns)/odom 100"/>- 资源隔离配置:
# 使用taskset绑定CPU核心 taskset -c 0 roslaunch liosam run.launch robot_ns:=robot1 taskset -c 1 roslaunch liosam run.launch robot_ns:=robot2- 内存管理技巧:
- 为每个节点设置独立的
/tmp目录 - 使用
ulimit限制单个节点的内存使用量
7. 扩展应用场景
这种改造方案还可应用于:
- 多传感器融合测试:在同一设备上运行不同传感器配置的节点进行对比
- 算法AB测试:同时运行不同参数或算法的节点评估效果
- 仿真环境部署:在Gazebo中模拟多机器人时保持隔离
实际部署中发现,经过这种改造后,单台i7-11800H设备可以稳定运行4个LIOSAM节点(每个节点分配2个CPU核心),平均CPU占用率控制在65%以下,内存占用约1.2GB/节点。
