多机的话题名如何定义

在多机器人开发中,订阅和发布的黄金法则 是:代码中永远只写"相对名称"(不带开头的斜杠 /),让 Launch 文件去决定它属于哪个机器人。

只要你遵守这个规则,你的代码写一遍,就可以部署到 robot01、robot02、robot99 等无数个机器人上,而不需要修改一行 C++ 代码。

以下是具体的写法规范:


1. 核心原则:三种写法的区别

假设你在 Launch 文件里已经把节点放入了 ns="robot01"

写法 (Topic Name) 代码示例 实际效果 (Result) 适用场景
相对名称 (推荐) "scan" /robot01/scan 绝大多数情况。机器人内部通信(雷达发给同机器人的定位节点)。
全局名称 (带斜杠) "/scan" /scan 千万别用。这会无视 namespace,导致所有机器人的数据混在一起。
私有名称 (带波浪号) "~scan" /robot01/node_name/scan 仅用于节点内部调试或参数读取,不常用于跨节点通信。

2. 标准代码模板 (C++)

这是最标准的多机适配写法。注意看话题名都没有 / 开头。

cpp 复制代码
#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <nav_msgs/Odometry.h>

int main(int argc, char** argv) {
    ros::init(argc, argv, "my_node");

    // 1. 获取句柄(自动继承 launch 的 ns="robot01")
    ros::NodeHandle nh;           // 公共句柄:用于 Pub/Sub
    ros::NodeHandle nh_private("~"); // 私有句柄:用于读取 param

    // ---------------------------------------------------------
    // 【重点 1】发布 (Publish)
    // ---------------------------------------------------------
    // 错误写法:nh.advertise<...>(robot_id + "/odom", 10); // 不要手动拼ID!
    // 错误写法:nh.advertise<...>("/odom", 10);           // 不要加斜杠!
    
    // 正确写法:直接写话题名
    // 实际发布话题:/robot01/odom
    ros::Publisher pub_odom = nh.advertise<nav_msgs::Odometry>("odom", 10);

    // ---------------------------------------------------------
    // 【重点 2】订阅 (Subscribe)
    // ---------------------------------------------------------
    // 假设我们要订阅同机器人的雷达数据
    // 实际订阅话题:/robot01/scan
    ros::Subscriber sub_scan = nh.subscribe("scan", 10, [&](const sensor_msgs::PointCloud2::ConstPtr& msg) {
        
        // -----------------------------------------------------
        // 【重点 3】处理 TF 坐标系 (Frame ID) ------ 这是唯一的例外
        // -----------------------------------------------------
        // 话题名 ROS 会自动加前缀,但消息内部的 header.frame_id 是字符串,ROS 改不了!
        // 如果你不处理,所有机器人的雷达数据都会说自己在 "base_link",Rviz 就乱了。
        
        nav_msgs::Odometry odom_msg;
        
        // 必须给 frame_id 加上前缀 (这个前缀通常通过 param 传入)
        std::string tf_prefix; 
        nh_private.param<std::string>("tf_prefix", tf_prefix, "robot01"); 
        
        // 赋值:robot01/odom
        odom_msg.header.frame_id = tf_prefix + "/odom"; 
        // 赋值:robot01/base_link
        odom_msg.child_frame_id  = tf_prefix + "/base_link"; 
        
        pub_odom.publish(odom_msg);
    });

    ros::spin();
    return 0;
}

3. 配套的 Launch 文件

为了配合上面的代码,Launch 文件这样写:

xml 复制代码
<launch>
    <arg name="robot_id" default="robot01" />
    <group ns="$(arg robot_id)">
        <node pkg="my_pkg" type="my_node" name="controller" output="screen">
            <param name="tf_prefix" value="$(arg robot_id)" />
            <remap from="scan" to="velodyne_points" />  
        </node>
    </group>
</launch>

4. 特殊情况:中央控制节点 (跨机器人通信)

如果你写在一个 中央调度节点 (Fleet Manager),它需要同时订阅 robot01 和 robot02 的位置,这时候该怎么办?

这种情况下,你的节点通常运行在全局命名空间(根目录 /),你需要显式指定绝对路径

cpp 复制代码
// 这种节点通常在 launch 里不放在 <group> 中
ros::NodeHandle nh;

// 必须写全路径,因为你要跨越命名空间去抓数据
ros::Subscriber sub1 = nh.subscribe("/robot01/odom", 10, cb1);
ros::Subscriber sub2 = nh.subscribe("/robot02/odom", 10, cb2);

总结

  1. 绝大多数业务节点 (SLAM、规划、控制):**只写 topic**,不要写 /topic,也不要拼字符串。
  2. Launch 文件 :用 group ns 包裹。
  3. TF 坐标系 :这是唯一需要在代码里手动拼前缀的地方(frame_id = prefix + "/base_link")。
相关推荐
阿Q论文2 小时前
机械设计制造及其自动化/ 机械电子工程 / 机电一体化技术(四自由度的工业机器人)
机器人·自动化·制造
zhangrelay4 小时前
ROS 2 Lyrical 第4章 URDF机器人建模与Gazebo Garden仿真
linux·笔记·学习·ubuntu·机器人·ros2
吴少风5 小时前
机器人仿真软件比较
机器人·仿真软件
QQ5416451216 小时前
【驿帮查件助手python开源】开源聚合查件机器人,实现 24 类驿站批量查件取代短信通知。技术方案分享
python·机器人·开源·驿站查件机器人
XMAIPC_Robot9 小时前
RK3588+STM32:高性能机器人运动控制解决方案,兼顾实时性与AI算力
人工智能·stm32·嵌入式硬件·算法·fpga开发·机器人·arm+fpga
M-Robots echo12 小时前
王成录:黑客松不只是赛事,是开源机器人生态快速试错、挖掘创新的试验场
机器人·ros·鸿蒙·具身智能·开源社区·m-robots
xiangzhihong813 小时前
零基础学AI之机器学习原理
人工智能·机器学习·机器人
沐梯子1 天前
具身智能爆发前夜的“数据饥渴”:2026机器人数据采集产业链选型解析
大数据·人工智能·机器人
消失的旧时光-19431 天前
第 5 篇:Android 与机器人主控之间,指令、回执、超时和重试怎么设计?
机器人·tcp通信·ack·sequence
YFJ_mily1 天前
【紧急截稿提醒】MRCS 2026第三届机电一体化、机器人与控制系统国际会议|EI/Scopus双检索|9月11日截止投稿
机器人·工业自动化·机器人控制·机电一体化·控制系统·rdlink研发家·驻马店会议