ROS多节点LIOSAM改造:解决命名冲突与资源竞争

ROS多节点LIOSAM改造:解决命名冲突与资源竞争

1. 项目背景与需求分析

在机器人SLAM(同步定位与建图)开发中,LIOSAM作为基于激光雷达的惯性紧耦合SLAM系统,常被用于复杂环境下的高精度建图。但在实际部署时,我们经常遇到一个典型问题:当需要在同一台主控设备(master)上运行多个LIOSAM节点时,系统会出现命名冲突和资源竞争。

这种情况在以下场景尤为常见:

  • 多机器人协同作业时,需要在一台中央控制电脑上监控所有机器人的建图状态
  • 开发阶段需要同时测试不同参数配置的建图效果
  • 大规模场景建图时,需要分区域运行多个建图节点

2. 核心问题诊断

2.1 默认实现的局限性

原始LIOSAM代码设计时主要考虑单节点运行场景,存在以下关键限制:

  1. 话题命名冲突

    • 所有节点默认发布相同话题(如/laser_odom/map
    • 导致消息路由混乱,无法区分不同节点的数据流
  2. TF帧命名冲突

    • 固定使用base_linkodom等帧名称
    • 多节点运行时会导致TF树结构破坏
  3. 参数服务器冲突

    • 所有节点读取相同的参数命名空间
    • 无法实现差异化配置

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中需要调整以下关键部分:

  1. 话题发布重命名
// 原始代码 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);
  1. 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.0

4. 完整部署流程

4.1 多节点启动命令

# 终端1 - 启动第一个节点 roslaunch liosam run.launch robot_ns:=robot1 # 终端2 - 启动第二个节点 roslaunch liosam run.launch robot_ns:=robot2

4.2 验证方法

  1. 查看节点列表
rosnode list # 应显示/robot1/liosam_node和/robot2/liosam_node
  1. 检查话题命名
rostopic list # 应包含/robot1/laser_odom和/robot2/laser_odom等
  1. TF树检查
rosrun tf view_frames # 生成的frames.pdf中应能看到robot1/odom->robot1/base_link等独立结构

5. 常见问题与解决方案

5.1 TF帧异常问题

现象:RVIZ中显示坐标帧错乱

解决方案

  1. 确保所有TF广播都添加了命名空间前缀
  2. 在RVIZ中设置Global Options->Fixed Frame为特定机器人的odom帧(如robot1/odom

5.2 话题订阅失败

现象:节点无法接收到传感器数据

排查步骤

  1. 使用rostopic hz检查原始话题数据频率
  2. 确认launch文件中所有<remap>标签配置正确
  3. 检查节点代码中的订阅话题名称是否使用相对命名

5.3 参数读取错误

现象:节点读取到错误的参数值

解决方法

  1. 确保参数服务器加载了正确的yaml文件
  2. 使用rosparam list检查参数命名空间
  3. 在代码中使用private_nh.param()读取私有参数

6. 性能优化建议

  1. 共享静态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"/>
  1. 资源隔离配置
# 使用taskset绑定CPU核心 taskset -c 0 roslaunch liosam run.launch robot_ns:=robot1 taskset -c 1 roslaunch liosam run.launch robot_ns:=robot2
  1. 内存管理技巧
  • 为每个节点设置独立的/tmp目录
  • 使用ulimit限制单个节点的内存使用量

7. 扩展应用场景

这种改造方案还可应用于:

  1. 多传感器融合测试:在同一设备上运行不同传感器配置的节点进行对比
  2. 算法AB测试:同时运行不同参数或算法的节点评估效果
  3. 仿真环境部署:在Gazebo中模拟多机器人时保持隔离

实际部署中发现,经过这种改造后,单台i7-11800H设备可以稳定运行4个LIOSAM节点(每个节点分配2个CPU核心),平均CPU占用率控制在65%以下,内存占用约1.2GB/节点。