如果你已经会 ROS 1 的节点与话题,学 ROS 2 不是「换一套命令」,而是换了一套发现机制与服务质量(QoS)哲学。新项目默认应选 ROS 2;旧栈则要评估包生态与实时/多机需求再迁。
ROS 1 → ROS 2:先改世界观
| 维度 | ROS 1 | ROS 2 |
|---|---|---|
| 发现 | 中心化 roscore / Master |
去中心化,靠 DDS 发现 |
| 通信后端 | 自研 TCPROS/UDPROS | DDS(多种 RMW 实现) |
| 服务质量 | 基本靠约定 | 显式 QoS(可靠性、历史、耐久等) |
| 启动 | roslaunch XML |
launch.py(Python)为主 |
| 构建 | catkin / catkin_tools | colcon + ament |
| 客户端库 | roscpp / rospy |
rclcpp / rclpy |
| 长任务 | actionlib |
rclcpp_action / 统一 Action 接口 |
| 节点生命周期 | 较弱 | Lifecycle Node(配置/激活/停用) |
没有 Master 的好处:少一个单点故障;代价是:你必须理解 DDS 域、网卡、组播/发现,否则「明明起了节点却互相看不见」。
发行版怎么选(实务)
常见稳定线(以社区惯用为准,部署前再核对 LTS):
- Humble(Ubuntu 22.04):很多产线与开源栈的「默认最大公约数」
- Jazzy 及更新版:新功能与依赖更新,适合能跟发行版的团队
选版原则:和系统 Ubuntu、驱动、仿真器(Gazebo / Isaac)对齐,比追最新代号更重要。
最小 pub/sub(rclpy)
source /opt/ros/$ROS_DISTRO/setup.bash
mkdir -p ~/ros2_ws/src && cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python demo_talker --dependencies rclpy std_msgs
发布者骨架:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__("talker")
self.pub = self.create_publisher(String, "chatter", 10)
self.timer = self.create_timer(0.1, self.tick)
self.i = 0
def tick(self):
msg = String()
msg.data = f"hello ROS2 {self.i}"
self.pub.publish(msg)
self.i += 1
def main():
rclpy.init()
node = Talker()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
构建与运行:
cd ~/ros2_ws && colcon build --packages-select demo_talker
source install/setup.bash
ros2 run demo_talker talker # 按你 package.xml / setup 里入口名为准
常用自检:
ros2 node list
ros2 topic list
ros2 topic echo /chatter
ros2 interface show std_msgs/msg/String
QoS:ROS 2 里最该认真看的开关
同一话题名、同一消息类型,QoS 不兼容就订不上阅——这是从 ROS 1 迁过来时最阴的坑。
常见旋钮:
- Reliability:
RELIABLEvsBEST_EFFORT(激光/图像常用 BEST_EFFORT) - History:
KEEP_LAST+ depth,或KEEP_ALL - Durability:
VOLATILEvsTRANSIENT_LOCAL(晚订阅还想拿到「最后一帧地图」时会用到)
经验法则:
- 传感器高速流:偏
BEST_EFFORT+ 小 depth - 控制指令、关键状态:偏
RELIABLE - 调试时用
ros2 topic info -v /xxx看双方 QoS 是否对得上
Lifecycle:把「启动成功」变成状态机
长期运行的驱动/规划节点,推荐 Lifecycle:
unconfigured → inactive → active → finalized
好处:配置失败可以停在 inactive;热切换参数或传感器时不必杀进程。代价:launch 与监控要会管状态转换,而不是 ros2 run 完事。
Launch(Python)直觉
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package="demo_talker",
executable="talker",
name="talker",
output="screen",
remappings=[("chatter", "robot/chatter")],
)
])
组合多机、多 namespace、参数文件时,Python launch 比 ROS 1 XML 更好组合——但也更容易写成「无法阅读的脚本」,记得拆文件、抽函数。
从 ROS 1 迁移:检查清单
- 消息/服务定义:
msg/srv语法细微差异;自定义接口进独立*_msgs包。 - 时间:统一用
rclcpp::Time/node.get_clock(),注意仿真钟use_sim_time。 - TF:
tf→tf2;补全frame_id/child_frame_id纪律。 - 参数:ROS 2 参数声明(declare)更严格,未声明就访问会失败。
- 桥接:短期可用
ros1_bridge双栈共存,长期别当架构。 - 包有没有 ROS 2 版:先查
ros2分支 / vendor;没有就评估自己 port 的成本。 - 网络:多机务必验证 DDS 发现(防火墙、VPN、Docker 网桥都会坑)。
和仿真、硬件的交界
- Gazebo / Ignition:话题名与 QoS 要对齐控制栈
- 真机驱动:优先找官方或厂商维护的 ROS 2 驱动,别长期跑「半移植」
- 录包:
ros2 bag record;分析时同样先确认时钟与 frame
小结
学 ROS 2 的优先级建议:
- 跑通 pub/sub +
colcon - 弄懂 QoS 不匹配 现象
- 会写最小
launch.py - 再上 Lifecycle、组件容器、安全与实时调参
ROS 1 教你「怎么拆节点」;ROS 2 逼你「怎么在真实网络与质量约束下拆节点」。下一篇把镜头拉远:Physical AI——模型如何长到机器人身上,以及 ROS 在这条链路上站哪一环。
