核心课程 / ROS2 应用与开发 / EXP-01
EXP-01 · 第 1 章 · 验证性 · 2 学时

ROS2 简介:机器人软件的操作系统

ROS(Robot Operating System,机器人操作系统)是什么?为什么机器人需要这个 ROS"中间件"?本实验从机器人软件的协作之痛出发,讲清 ROS2 的三大设计支柱,拆解 ROS1 中心化 Master 与 ROS2 去中心化 DDS 的架构发展变化,再通过 talker / listener 跑通第一个 ROS2 的 HelloWorld 程序。

难度 ⭐ · 2 学时 ROS2 HumbleDDS 中间件 turtlesimtalker · listener

目标本课学完,你应该能做到

① 说清本质
ROS2 到底解决了什么

能讲出 ROS 不是操作系统,而是"通信框架 + 工具集 + 包生态";能用自己的话说清"为什么机器人开发需要这层中间件"。

② 对比架构
ROS1 vs ROS2

能说出 ROS2 相对 ROS1 在三个维度的核心改进:通信架构(DDS vs TCPROS)、编译系统(colcon vs catkin)、节点生命周期管理,并解释各自的工程意义。

③ 跑通第一个例子
talker / listener

能正确执行 ros2 --help 看可用命令组,用 ros2 run 起 talker 与 listener,亲眼看到"零配置互联"。

④ 独立观察
用命令看系统

能用 ros2 node list / ros2 topic list / ros2 topic echo 观察运行中的节点拓扑与消息内容。

本课的重点与难点 重点:讲清"没有 Master 也能通信"这件事——它是后面所有实验的地基;以及 source setup.bash 为什么是每个终端的必做动作。
难点:理解"去中心化"不是"没有中心",而是"中心被拆散到每一端"。
和前后的关系 本课是整门课的地基:后面所有实验都建立在"节点 + DDS 发现"这套模型上。建议把 source 养成本能。

01问题引入:机器人软件为什么需要这个 ROS"中间件"

把机器人比作一个人:摄像头是眼睛、麦克风是耳朵、决策程序是大脑、机械臂和底盘是手脚——那"中间件"就是这套神经系统,把不同器官的信号翻译成统一语言。

1人体自带的"中间件" —— 神经系统把器官连成整体
人体自带的“中间件”:神经系统把器官连成整体 眼睛 · 耳朵 感知外界 大脑 思考与决策 双手 · 双腿 执行动作 传入 指令 = = = 摄像头·雷达·麦克风 传感器 决策程序(ROS2 节点) 订阅输入 · 发布指令 机械臂 · 底盘 执行器 神经系统 ↔ ROS2 中间件:把不同器官“翻译”成统一信号,大脑只管决策

神经系统把眼、耳、手、脚连成一个整体——传感器提供输入、决策做判断、执行器负责动作,ROS2 就是给机器人铺的这套"神经系统"。

2没有 ROS2 —— 每个零件一种"方言",大脑听不懂
没有 ROS2:每个零件一种“方言”,大脑?! 相机:USB · 私有协议 雷达:串口 · 0x55 包 电机:CAN 总线报文 大脑:?!全都听不懂 ✕✕✕✕ ?! 5 个零件 = 10 套转接程序,每换一个型号全部重写 —— 机器人软件的原罪

相机说 USB 方言、雷达说串口方言、电机说 CAN 方言,决策程序听谁的都费劲——直接的后果就是下面这行红字:转接程序随零件数爆炸,换型号全部重写。

3装上 ROS2 —— 都说"普通话",大脑只管听与发
装上 ROS2:都说“普通话”,大脑只管听与发 ROS2 消息总线(话题 = 微信群) 统一数据格式 · 自动发现 · 一个挂了不牵连别人 相机节点 发布 /image 雷达节点 发布 /scan 决策节点 订 /image · 发 /cmd_vel 订阅 / 发布 换零件 = 换一个会说“普通话”的节点,别人的代码一行不改

相机发 /image、雷达发 /scan,决策节点听普通话收、发普通话指令——换零件 = 换一个会说"普通话"的节点,别人的代码一行不改。中间件干的就是"统一说话"这件事。

四步把"为什么需要中间件"说透:

1每个模块一种私有协议:3 个部件要写 3 组转接,5 个部件要写 10 组——部件越多,转接代码越爆炸
2进程发现、断线重连、丢包重传全部手写,一改全崩
3ROS 用中间件统一只说一种语言:节点 / 话题 / 服务 / 动作
4上层算法变成可插拔积木:换雷达 = 换一个发布同一话题的节点
💡 一句话定义ROS(Robot Operating System)不是操作系统:不管理硬件,它是一个通信框架 + 工具集 + 包生态,运行在 Linux 之上,为机器人软件提供"进程之间如何说话"的标准答案。
简单来说,ROS2 = 机器人的"普通话 + 微信群 + 一键工具箱"——谁写的应用都能插上就跑,多台电脑自动成网。本课所有内容,都是在拆解这句话。

02核心原理:ROS2 的三大设计支柱

支柱一
去中心化的分布式图

没有 Master 单点。每个节点自带 DDS 参与者身份,通过组播自动发现彼此,组成一张"节点-话题"关系图。就像一个微信群:谁上线大家自动看见,谁退群也不影响别人聊天。

👉 本支柱专属展开:见下方"总机 vs 群聊"对比

支柱二
语言与框架无关

Python(rclpy)、C++(rclcpp)乃至 Swift/Java 都能发声——接口由 IDL 消息定义统一约束,与实现语言解耦。

👉 本支柱专属展开:见下方"同一份消息合同"图解

支柱三
工具生态开箱即用

ros2 CLI、rqt、rviz2、rosbag2、launch——同一套图信息喂给全部工具,"看不见的系统"随时可自省。

👉 本支柱专属展开:下方三张真实运行截图 · 完整工具链见 EXP-02

先看一个熟悉的场景再看机器:老式总机转接电话,总机一下班,全网失联;微信群里谁上线自动进群直接聊,谁退群都不影响别人。ROS1 选了总机(Master),ROS2 选了群聊(DDS):节点自己组播发现,任意一个挂掉,其余照常。下面左半边是总机模式的下场,右半边是群聊模式的底气:

ROS 1 · 中心化 Master ROS 2 · DDS 去中心化 Master 激光节点视觉节点 底盘节点规划节点 Master 宕机 → 全网停止发现 注册 / 寻址全部经总机 · 单点故障 激光节点视觉节点 底盘节点规划节点 SPDP 组播发现 → RTPS 点对点传输 节点直接互连 · 无单点故障
动画 1-1|左:Master 一倒全网失联;右:消息在网状直连中持续流动(6 秒循环)

Python 写的节点和 C++ 写的节点,语言不通凭什么直接对话?——因为它们事先签好了同一份"消息合同"(IDL):

支柱二 · 语言无关:同一份"合同",各自说母语 IDL 消息合同 · std_msgs/String "字符串 data" —— 双方共同签字画押 节点 A · Python rclpy · 把数据装进"合同信封" 节点 B · C++ rclcpp · 拆"合同信封"读数据 话题 /chatter 换语言 = 换个"写信人",收发双方都不用动 —— 开源社区的包拿来就能接入
动画 1-2|支柱二:Python 按同一份 IDL 合同发出(绿点)→ 话题中转 → C++ 按合同收下(蓝点)—— 母语不同,字节相同
对比维度ROS1(Melodic 及以前)ROS2(Foxy / Humble / …)
通信架构中心化:Master(rosmaster) 统一注册去中心化:DDS 点对点,无中心进程
底层协议TCPROS(自研 TCP 长连接)RTPS(DDS 标准,UDP + 组播发现)
节点发现启动必须先起 Master,向它注册组播自动发现,谁先谁后无所谓
编译系统catkin_make 单一 CMake 大工程colcon 每包独立构建,支持混合语言
生命周期节点崩了只能重启整个进程Managed Node 四状态,可"待机-激活"恢复
服务质量无 QoS,可靠性不可配QoS 三要素(可靠性/持久性/历史)可按话题配置

支柱三不是口号——下面是 ROS2 Humble 实机运行的真实截图(三张均取自本课程配套实验现场,点击图片可放大):

ros2 topic list 真实输出:相机话题清单
① ros2 CLI · topic list 一眼清点全系统话题
ros2 topic echo 真实消息字段输出
② topic echo · 实时抽看消息内容与字段结构
RViz2 图形界面:按话题添加点云显示
③ RViz2 · 把激光点云画成三维画面

还有更多"开箱即用"在等你:rqt 图形化大全家、turtlesim 教学小海龟、rosbag2 录放时间机器、launch 一键拉起全系统——同一张计算图喂给所有工具,EXP-02 起逐个上手。

🔍 关键跃迁ROS1 的 Master 是"电话总机",总机不在,全网瘫痪;ROS2 的 DDS 是"群聊":任何两个人先加上好友(发现),之后直接私聊(P2P 传输)。这决定了 ROS2 天生适合多机、车载与机器人集群场景。

拼图收尾:三根柱子拼回整张图 —— ROS2 整体框架(中文版)

三根柱子讲完了,现在拼回整机。穿针引线的"针"就是你两个小节后要亲手跑的那条 Hello World: 1:楼越往下越靠近硬件,右栏的工具站在图外读图。任意点一层看它此刻在干什么,点 ▶ 播放 看这条消息怎么"下楼 → 过河 → 上楼"。

🏗 点击任意一层 · 由上到下 = 由"人话"到"网线"

🧰 支柱③ · 工具生态(站在图外看图)ros2 CLI · rqt · rviz2 · rosbag2 · launch——它们读的都是同一张"计算图",不侵入你的节点,开箱即用(EXP-02 起逐个上手)。
👆 点左侧任意一层

这里会告诉你:这层是干什么的、此刻 Hello World 走到这层发生了什么、以及它对应哪根支柱。

📣 等待播放——或先点击任意一层单独查看。

图 1-3|ROS2 整体框架(全中文):①–③ 承载支柱②"语言无关",⑤ 承载支柱①"去中心化",工具栏承载支柱③——一条 Hello World 下六层、上六层,两边代码零连接

03ROS2 的架构与设计特点

第 02 节末尾那张可播放的框架图,是跟着一条 Hello World 把楼走了个来回。这节换成工程师查房的视角:官方把 ROS2 从上到下切成五层。先看整栋楼的楼层表,再把第 02 节的三根设计支柱拿过来对照,看它们各站在哪一层。

ROS2 五层架构 · 越往下越靠近硬件 调用往下走 · 数据回程往上走 1 应用层 你写的节点:talker · listener · turtlesim · 规划与控制程序 2 客户端库层 rclpy(Python)· rclcpp(C++)· 共用 rcl 这个语言无关内核 3 DDS 接口层(RMW) 标准插座:换插头不换电器,上层只认统一的发布 / 订阅 / 服务接口 4 DDS 实现层 Fast DDS(默认)· Cyclone DDS · Connext DDS,组播发现 + 点对点传输 5 操作系统层 Ubuntu 22.04(本课程)· 内核 / 网络栈 / 网卡驱动 ROS2 客户端侧 你和"母语窗口"打交道 DDS 中间件世界 标准插座 + 运输队 操作系统与硬件 网卡 · 网络 · 操作系统 换语言只动第 2 层,换 DDS 实现只动第 4 层,上面的代码一个字不用改

图 1-4|ROS2 五层架构:左侧箭头是调用方向,右侧虚线把五层归成三块,第 3 层(标准插座)特意高亮

逐层用一句话说清各自干什么,以及你在课程的哪一节会碰到它:

层这层干什么你会在哪里碰到它
1 应用层写节点、定话题、发消息收消息,完全不碰网络细节talker / listener(第 04 节),EXP-05 起的所有节点代码
2 客户端库层每种语言一个母语窗口(rclpy / rclcpp),底下共用 rcl 这个语言无关内核"同一份消息合同"动画(动画 1-2)讲的就是这层怎么把语言差异吃掉
3 DDS 接口层(RMW)画一条标准插座线:上层只认统一的发布 / 订阅 / 服务接口,不认插座下面插的是谁RMW_IMPLEMENTATION 一个环境变量换 DDS,见下方卡片
4 DDS 实现层真正干活的运输队:组播把节点互相喊出来,RTPS 点对点送消息支柱①的落点,动画 1-1 右半边那张网;EXP-12 把整层拆开讲
5 操作系统层真实操作系统与网络设施,ROS2 只是跑在它上面的普通程序。本课程统一用 Ubuntu 22.04;官方同时支持 Windows 10+ 与 macOS,但装法、路径、终端习惯都不一样本节下半的 source 三板斧,验证的就是这层环境通没通
💻 我电脑是 Windows / macOS,能跟着做吗?能,但不建议在本课程的实验里直接用它们。ROS2 的三个平台不是"三套等价安装":Linux 是官方一等公民(二进制包齐全、教程最全、机器人硬件驱动原生支持);Windows 需要额外的 Visual Studio 与包管理环境,很多示例(turtlesim、rqt、部分驱动)跑不顺;macOS 支持面更窄,社区基本靠源码编译。本课程所有命令、路径(~/ros2_ws)、终端习惯都是按 Ubuntu 写的,所以三条路任选:① 装 Ubuntu 22.04 双系统或虚拟机(最省事,推荐);② Windows 用 WSL2 + Ubuntu 22.04(命令完全一致,图形界面需额外配置);③ 直接用课程提供的实验箱。macOS 用户建议直接上虚拟机跑 Ubuntu。
💡 一句话复述五层从上到下:写代码只碰前两层,第 3 层是标准插座,第 4 层是运输队,最底下是操作系统。换语言动第 2 层,换 DDS 实现动第 4 层,其余层原封不动。说得出这句话,这张图就算看懂了。

设计特点:三大设计支柱各站在哪一层

第 02 节的三根柱子落到这张五层图上,每根都有自己的楼层,彼此不重叠也不缺位:

支柱一 · 落在第 4 层
去中心化分布式图

发现与传输都在 DDS 实现层完成:组播喊一嗓子互相找到,之后点对点直送,没有 Master 这个总机。
回看"总机 vs 群聊"对比

支柱二 · 贯穿第 2 ~ 3 层
语言与框架无关

rclpy / rclcpp 在第 2 层各说母语,到进第 3 层之前已被 rcl 收敛成一套动作,消息格式由 IDL 合同统一约束。
回看"同一份消息合同"图解

支柱三 · 站在楼外
工具生态开箱即用

ros2 CLI、rviz2、rosbag2 读的都是同一张计算图,不进你任何一层的代码。
回看三张实机运行截图

发行版、开关与节点生命周期

楼层和支柱对上之后,还差三件落地的事:本课程用哪个发行版、换 DDS 怎么换、节点怎么"待机而不退出"。

发行版字母表
Humble Hawksbill(本课程)

2022.05 发布的 LTS 版本,支持至 2027.05;此前 Foxy(2020)、Galactic(2021),此后 Iron、Jazzy。配套 Ubuntu 22.04。

切换 DDS 实现
一个环境变量完成

RMW_IMPLEMENTATION=rmw_cyclonedds_cpp——排障时用来隔离"是不是 DDS 实现的锅",无需改一行代码。

生命周期管理
Managed Node 四状态

Unconfigured → Inactive → Active → Finalized:节点可"待机而不退出",故障后原地恢复。

环境验证三板斧

# ① 确认发行版(预期输出 humble)
echo $ROS_DISTRO
humble

# ② 加载 ROS2 环境(每个新开终端都要执行!)
source /opt/ros/humble/setup.bash

# ③ 看 ros2 命令认识自己的能力清单
ros2 --help
# ... verbs: action bag interface launch node param pkg run service topic ...
#            ↑ 八大动词组,EXP-02 逐一拆解
turtlesim 冒烟测试(可选 · 60 秒)需要图形界面:设备本地桌面直接跑;远程用 VNC 或 MobaXterm(X11 转发),先敲 echo $DISPLAY 确认非空。下面三条命令按序复制执行:
# ① 装包(只需一次)
sudo apt install ros-humble-turtlesim

# ② 启动海龟窗口,保持开着(拆解见下面的讲解卡)
ros2 run turtlesim turtlesim_node

# ③ 再开一个终端,按 ↑↓←→ 方向键遥控海龟
ros2 run turtlesim turtle_teleop_key
# 报 No executable found?说明命令被拼接过,先跑 ros2 pkg executables turtlesim 核对
📖 这行命令是什么意思ros2 run turtlesim turtlesim_node 从左到右读:用 ros2 run 这个"启动命令",去启动包名为 turtlesim 、可执行文件名为 turtlesim_node 的应用——相当于"从 turtlesim 这个工具箱里,取出 turtlesim_node 这件工具运行起来",回车后进程里就多了一个节点(名为 /turtlesim)。通用格式是 ros2 run <包名> <可执行文件名>,本页下文的 turtle_teleop_key、demo_nodes_cpp talker 全按这个格式读。
实机桌面弹出 TurtleSim 窗口截图
实测效果:第 ② 条回车后,桌面弹出 TurtleSim 蓝色窗口(设备实拍,点击可放大)

💡 三步跑通即说明图形环境、话题发布、订阅执行全部正常——方向键海龟之所以听话,是 teleop_key 在往 /turtle1/cmd_vel 话题发"速度"消息,海龟订阅到就动(EXP-06 细讲)。

⚠️ 为什么 source 是"三板斧"之一ROS2 的一切命令都靠 setup.bash 注入的环境变量找到:不 source,ros2 直接 command not found;换了工作空间不 source install/setup.bash,你编译的包就"不存在"。这是新手第一大坑,贯穿全课程。

04实操 1 · ROS2 的 Hello World:talker

1首次先装官方示例包:sudo apt install ros-humble-demo-nodes-cpp(只需一次,装过就跳过)
2再开终端 A:source 环境后运行 ros2 run demo_nodes_cpp talker
3观察输出:[INFO] ... Publishing: 'Hello World: N' 每秒刷新
4后台发生的事:DDS 发现 → 节点创建 → 在 /chatter 注册 Publisher → 定时器驱动发消息
# ① 首次先装官方示例包(已装跳过;缺它必报 package not found)
sudo apt install ros-humble-demo-nodes-cpp
# 核对: ros2 pkg executables demo_nodes_cpp  →  应列出 talker / listener

# ② 终端 A:加载环境(新开终端必做)
source /opt/ros/humble/setup.bash

# ③ 启动 talker,保持运行
ros2 run demo_nodes_cpp talker
# [INFO] [1712...]: Publishing: 'Hello World: 1'
# [INFO] [1712...]: Publishing: 'Hello World: 2'
🔍 run 命令解剖ros2 run <包名> <可执行文件>——它只做一件事:去 ament 索引里按包名找到 install 目录,启动其中的可执行文件。找不到包?按序排查:①没装(本节第一步)→ ②没 source → ③自己的包才轮到没编译。

05实操 2 · 零配置互联:listener 与系统监控

1另开终端 B:ros2 run demo_nodes_cpp listener——无需任何"连接配置"
2DDS 组播自动发现 /chatter 的发布端,开始打印 [INFO]: I heard: [Hello World: N]
3终端 C 四连自省:node list → topic list → topic info → topic echo / hz
# 终端 C:以第三方身份俯瞰整个系统
ros2 node list
# /listener  /talker
ros2 topic list
# /chatter  /parameter_events  /rosout
ros2 topic info /chatter
# Type: std_msgs/msg/String   Publisher count: 1   Subscription count: 1
ros2 topic echo /chatter        # 第三方"匿名"订阅,实时看到 data
ros2 topic hz /chatter          # average rate: 1.000
💡 弹性验证(试一下)① 先启动 listener 再启动 talker,照样通(发现无顺序);② Ctrl+C 杀掉 talker 重启,listener 自动恢复接收。把 Master 概念从脑子里删掉,这就是 DDS。

06源码一撇:发布订阅关键代码逻辑

刚才跑通的那两个节点,代码其实只有几十行。下面是你刚执行的 demo_nodes_cpp 官方示例的真实源码(已精简掉版权头和组件注册宏),重点看发布与订阅这两条主线各自靠哪几行动作完成。

共 4 件事
任何 ROS2 节点都一样

① 继承 Node → ② 起个名字 → ③ 建发布/订阅 → ④ 有消息就回调。

关键差异
只有 3 行不同

talker 是 create_publisher + 定时器发;listener 是 create_subscription + 收到就打印。

📤 talker.cpp —— 每秒说一句话

#include "rclcpp/rclcpp.hpp"          // ROS2 C++ 客户端库
#include "std_msgs/msg/string.hpp"      // 消息类型:字符串

class Talker : public rclcpp::Node   // ① 任何节点都继承 Node
{
public:
  Talker() : Node("talker")              // ② 节点名就叫 talker
  {
    // ③ 建一个发布者:往 "chatter" 这个话题上发 String
    pub_ = create_publisher<std_msgs::msg::String>("chatter", 10);

    // ④ 每秒触发一次,发一条编号递增的消息
    timer_ = create_wall_timer(1s, [this]() {
      auto msg = std_msgs::msg::String();
      msg.data = "Hello World: " + std::to_string(count_++);
      RCLCPP_INFO(get_logger(), "Publishing: '%s'", msg.data.c_str());
      pub_->publish(msg);            // ← 发布动作:把消息推上 chatter
    });
  }
private:
  size_t count_ = 1;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr pub_;
  rclcpp::TimerBase::SharedPtr timer_;
};

📥 listener.cpp —— 收到什么就打印什么

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class Listener : public rclcpp::Node   // ① 同样继承 Node
{
public:
  Listener() : Node("listener")            // ② 节点名叫 listener
  {
    // ③ 建一个订阅者:盯住同一个 "chatter" 话题
    sub_ = create_subscription<std_msgs::msg::String>(
      "chatter", 10,
      [this](std_msgs::msg::String::ConstSharedPtr msg) {   // ④ 回调
        RCLCPP_INFO(get_logger(), "I heard: [%s]", msg->data.c_str());
      });                          // ← 回调:每收到一条就执行一次
  }
private:
  rclcpp::Subscription<std_msgs::msg::String>::SharedPtr sub_;
};
🔍 把两份代码叠在一起看,会发现什么两个类结构完全一样:继承 Node、给节点起名、连到 chatter。唯一的差别是:talker 用 create_publisher 主动往外发,listener 用 create_subscription 被动等消息。它们从头到尾没有互相"认识"过——没有任何一行代码写着"我要连 listener"。这就是这一节最想让你看到的事:连接是 DDS 在背后自动配对的,不是代码写的。
对比项talkerlistener
节点名"talker""listener"
关键 APIcreate_publishercreate_subscription
话题名"chatter" ← 两边必须写成一样,否则接不上
消息类型std_msgs::msg::String ← 类型也必须一致
何时动作定时器每秒触发一次收到消息时被回调
主动/被动主动发送被动等待
💡 不用会写,先看得懂本实验不要求你会写 C++。你只需要能从代码里认出四个东西:节点名、话题名、消息类型、是发还是收。后面学 Python 写节点时,会发现换成 rclpy 之后结构一模一样,只是语法不同——话题、类型、发布、订阅这四个概念是跨语言的。

07注意事项与验收清单

  • 每个新终端都执行过 source /opt/ros/humble/setup.bash,echo $ROS_DISTRO 输出 humble
  • talker 输出 Publishing: 'Hello World: N',序列号持续递增
  • listener 输出 I heard: [Hello World: N],与 talker 同步
  • ros2 node list 同时看到 /talker /listener;ros2 topic hz /chatter ≈ 1.000 Hz
  • 能口头解释:ROS1 Master 和 ROS2 DDS 在"节点发现"上的本质区别
⚠️ 常见坑现象 → 解法
未 source 环境ros2: command not found → source /opt/ros/humble/setup.bash
包未安装Package 'demo_nodes_cpp' not found → sudo apt install ros-humble-demo-nodes-cpp
组播被阻断虚拟机/容器内两终端互看不见 → 桥接网络放开组播,或临时用 ROS_DOMAIN_ID=0 统一域
域 ID 不一致一边 ROS_DOMAIN_ID=42 一边默认 0 → 互相"隐身",对齐环境变量
GUI 无显示turtlesim 黑屏 → 确认 X11 转发 / echo $DISPLAY 非空

08实验总结与思考题

✅ 完成成果
跑通 talker / listener 双节点

用 node / topic 四件套俯瞰系统,完成第一次 ROS2 Hello World。

🧠 原理收获
三大支柱 + 架构发展变化

去中心化、语言无关、工具生态;六维理解 ROS1 → ROS2 的必然性。

🛠 技能收获
环境验证与自省命令

source 三板斧、ros2 run、node/topic list · info · echo · hz。

❓ 排障思路
command not found 家族

先查 source → 再查 apt 安装 → 再查 DOMAIN_ID 与组播。

📝 思考题 1能否同时支持多个 listener?如果可以,两个 listener 接收到的数据会不会互相影响?如果同时开多个 talker,又是否会产生冲突呢?请动手分别在两个终端各起一个 listener、再起第二个 talker 验证你的判断。
📝 思考题 2在同一个局域网下,一台设备运行 ros2 run turtlesim turtlesim_node,另一台设备执行 ros2 run turtlesim turtle_teleop_key,能控制这只海龟吗?如果不希望对方能够控制,应该怎么解决这个问题?