ROS2(Robot Operating System 2)作为新一代机器人操作系统,相比ROS1重构了通信架构,具备跨平台兼容(Linux/Windows/macOS)、实时性更强、分布式通信更灵活等核心优势。Humble Hawksbill是ROS2的长期支持(LTS)版本(支持至2027年),稳定性和生态完善度最优,是新手入门ROS2的首选版本。

本文将从零基础视角出发,先拆解ROS2核心概念(用生活案例类比,拒绝晦涩),再手把手实现Python和C++版本的HelloWorld节点,让你不仅能跑通代码,更能理解每一步的底层逻辑。

一、ROS2核心概念速览(新手必懂,类比讲解)

在写代码前,先理清ROS2的核心术语——这些是理解ROS2通信模型的基础,避免「写代码但不懂原理」:

概念通俗解释生活类比核心特点
节点(Node)ROS2中最小的可执行单元,一个节点只负责一个具体功能(如发布消息、控制电机、读取传感器)手机里的单个APP(微信负责社交、抖音负责短视频)节点名全网唯一,可独立启动/关闭,降低耦合
话题(Topic)节点间异步通信的「数据通道」,采用「发布/订阅」模式微信群(一人发消息,多人可看)单向通信、异步解耦,支持多发布者/多订阅者
消息(Message)话题中传输的数据格式(预定义结构)微信群里发的「文字/图片/视频」(有固定格式)ROS2内置多种标准消息(如String、Twist),也可自定义
发布者(Publisher)向指定话题主动发送消息的节点微信群里发消息的人只负责「发」,不关心谁接收
订阅者(Subscriber)监听指定话题、接收消息的节点微信群里看消息的人只负责「收」,不关心谁发送
定时器(Timer)周期性触发回调函数的工具(如每秒执行一次发布操作)手机闹钟(每小时响一次,触发「起床」动作)基于时间触发,无需外部输入
工作空间(Workspace)存放ROS2项目所有代码、编译产物的目录电脑里的「项目文件夹」包含src(源码)、build(编译中间文件)、install(可执行文件)
功能包(Package)ROS2的基本代码组织单元,包含代码、配置、依赖等项目文件夹里的「子模块」(如用户模块、支付模块)一个包对应一个功能,可复用、可依赖其他包

补充:ROS2 vs ROS1 核心差异(新手快速认知)

特性ROS1ROS2
通信架构基于Master中心节点无中心节点(DDS分布式)
实时性弱(无原生实时支持)强(支持RTI Connext等实时DDS)
跨平台仅LinuxLinux/Windows/macOS/嵌入式
依赖管理rosbuild/catkinament(支持Python/C++混合开发)

二、环境准备(ROS2 Humble 安装与验证)

2.1 安装方式(新手推荐一键安装)

ROS2官方安装流程较繁琐,推荐使用fishros一键安装(适配Ubuntu 22.04):

# 下载并执行一键安装脚本
wget http://fishros.com/install -O fishros && sudo bash fishros

按脚本提示操作:

  1. 选择「1.一键安装ROS」→ 选择「1.humble(ROS2)」→ 选择「1.humble(ROS2)基础版(小)」;
  2. 等待安装完成(约10-20分钟,取决于网络);
  3. 安装完成后重启终端。

2.2 验证安装是否成功

# 查看ROS2版本(输出humble即正常)
echo $ROS_DISTRO

# 测试基础命令(输出ROS2版本信息)
ros2 --version

# 加载ROS2核心环境(每次新开终端必执行,或写入~/.bashrc)
source /opt/ros/humble/setup.bash

2.3 永久配置环境变量(避免重复source)

每次新开终端都手动source环境变量太麻烦,可将命令写入~/.bashrc

# 将ROS2核心环境写入bashrc
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc

# 立即生效
source ~/.bashrc

三、创建ROS2工作空间与功能包

3.1 工作空间:ROS2项目的「根目录」

工作空间是存放所有ROS2代码的统一目录,命名通常为ros2_ws(ws=workspace):

# 创建工作空间目录结构(-p:递归创建父目录)
mkdir -p ~/ros2_ws/src

# 进入工作空间根目录
cd ~/ros2_ws

# 编译空工作空间(生成build/install/log目录)
colcon build
工作空间目录说明:
  • src:存放所有功能包源码(核心目录,手动创建);
  • build:编译过程中生成的中间文件(自动生成,无需修改);
  • install:编译后的可执行文件、库、环境脚本(自动生成,运行节点需依赖);
  • log:编译日志(自动生成,排错时可查看)。

3.2 功能包:ROS2代码的「最小单元」

功能包是ROS2组织代码的基本形式,需区分Python和C++包(构建类型不同)。

(1)创建Python功能包

Python包适合快速开发、脚本化场景(如逻辑验证、数据处理):

# 进入src目录(功能包必须放在src下)
cd ~/ros2_ws/src

# 创建Python功能包
ros2 pkg create my_python_pkg \
  --build-type ament_python \  # 构建类型:Python包专用
  --dependencies rclpy std_msgs \  # 依赖:rclpy(ROS2 Python核心库)、std_msgs(标准消息库)
  --node-name my_first_node  # 自动生成的节点文件名
命令参数详解:
  • --build-type ament_python:指定包的构建系统为ament_python(Python包专属);
  • --dependencies:声明当前包依赖的其他ROS2包(rclpy是Python开发必备,std_msgs提供基础消息类型);
  • --node-name:自动生成一个空的节点文件,避免手动创建。
(2)创建C++功能包

C++包适合高性能、实时性要求高的场景(如机器人运动控制、硬件驱动):

# 仍在src目录下
cd ~/ros2_ws/src

# 创建C++功能包
ros2 pkg create my_cpp_pkg \
  --build-type ament_cmake \  # 构建类型:C++包专用
  --dependencies rclcpp std_msgs  # 依赖:rclcpp(ROS2 C++核心库)、std_msgs

四、实战:Python版本HelloWorld节点(新手友好)

功能需求

创建一个节点,实现两个核心功能:

  1. 每秒向hello_topic话题发布一条「Hello, ROS2 Humble!」字符串消息;
  2. 订阅hello_topic话题,打印收到的所有消息(包括自己发布的)。

4.1 编写Python节点代码

编辑自动生成的节点文件~/ros2_ws/src/my_python_pkg/my_python_pkg/my_first_node.py

#!/usr/bin/env python3
# 声明Python解释器,确保系统用Python3执行

# 导入ROS2 Python核心库
import rclpy
from rclpy.node import Node  # 所有ROS2节点的基类
from std_msgs.msg import String  # 标准字符串消息类型

class HelloWorldNode(Node):
    """
    自定义ROS2节点类:继承自rclpy.node.Node
    功能:发布+订阅hello_topic话题
    """
    def __init__(self):
        # 初始化父类(Node),指定节点名(全网唯一,不能重复)
        super().__init__('hello_world_python')
        
        # 1. 创建发布者(Publisher)
        # 参数1:消息类型(String);参数2:话题名(hello_topic);参数3:队列大小(缓存10条消息)
        self.publisher_ = self.create_publisher(String, 'hello_topic', 10)
        
        # 2. 创建订阅者(Subscriber)
        # 参数1:消息类型;参数2:话题名;参数3:回调函数(收到消息时执行);参数4:队列大小
        self.subscription = self.create_subscription(
            String,
            'hello_topic',
            self.sub_callback,  # 订阅回调函数(处理收到的消息)
            10
        )
        # 防止Python垃圾回收机制回收订阅者(可选,但建议加)
        self.subscription
        
        # 3. 创建定时器(Timer)
        # 参数1:定时周期(1.0秒);参数2:定时器回调函数(周期性执行)
        self.timer = self.create_timer(1.0, self.timer_callback)
        
        # 计数变量:记录发布消息的次数
        self.count = 0
        
        # 打印启动日志(ROS2标准日志,比print更规范)
        self.get_logger().info('✅ Python HelloWorld节点已启动!')

    def timer_callback(self):
        """定时器回调函数:每秒执行一次,发布消息"""
        # 创建String类型的消息对象
        msg = String()
        # 填充消息内容(f-string格式化,拼接计数)
        msg.data = f'Hello, ROS2 Humble! (第{self.count}次)'
        
        # 发布消息到hello_topic话题
        self.publisher_.publish(msg)
        # 打印发布日志
        self.get_logger().info(f'📤 发布消息: "{msg.data}"')
        
        # 计数自增
        self.count += 1

    def sub_callback(self, msg):
        """订阅回调函数:收到消息时执行,打印消息内容"""
        # msg是接收到的String类型消息,通过msg.data获取内容
        self.get_logger().info(f'📥 收到消息: "{msg.data}"')

def main(args=None):
    """主函数:ROS2节点的入口"""
    # 1. 初始化ROS2上下文(建立与ROS2系统的连接)
    rclpy.init(args=args)
    
    # 2. 创建自定义节点实例
    hello_node = HelloWorldNode()
    
    # 3. 保持节点运行(阻塞当前线程,直到节点被关闭)
    # spin()会持续检查是否有新消息/定时器触发,是ROS2节点的核心循环
    rclpy.spin(hello_node)
    
    # 4. 销毁节点(释放资源)
    hello_node.destroy_node()
    # 5. 关闭ROS2上下文
    rclpy.shutdown()

# 程序入口(Python标准写法)
if __name__ == '__main__':
    main()

4.2 配置setup.py(节点运行入口)

setup.py是Python包的安装配置文件,需指定节点的运行入口(让ros2 run能找到节点):
编辑~/ros2_ws/src/my_python_pkg/setup.py,找到entry_points部分,修改为:

entry_points={
    'console_scripts': [
        # 格式:"运行命令 = 包名.文件名:主函数名"
        "hello_node = my_python_pkg.my_first_node:main",
    ],
},
配置说明:
  • hello_node:自定义的节点运行命令(后续用ros2 run my_python_pkg hello_node启动);
  • my_python_pkg.my_first_node:节点文件的模块路径(包名.文件名,无需.py后缀);
  • main:节点的入口函数名。

4.3 编译+运行Python节点

# 回到工作空间根目录
cd ~/ros2_ws

# 编译Python包(--symlink-install:修改代码后无需重新编译,直接生效)
colcon build --packages-select my_python_pkg --symlink-install

# 加载编译后的环境(必须执行,否则系统找不到新编译的节点)
source install/setup.bash

# 运行Python节点
ros2 run my_python_pkg hello_node
预期运行结果:
[INFO] [1772860000.000000000] [hello_world_python]: ✅ Python HelloWorld节点已启动!
[INFO] [1772860001.000000000] [hello_world_python]: 📤 发布消息: "Hello, ROS2 Humble! (第0次)"
[INFO] [1772860001.000000000] [hello_world_python]: 📥 收到消息: "Hello, ROS2 Humble! (第0次)"
[INFO] [1772860002.000000000] [hello_world_python]: 📤 发布消息: "Hello, ROS2 Humble! (第1次)"
[INFO] [1772860002.000000000] [hello_world_python]: 📥 收到消息: "Hello, ROS2 Humble! (第1次)"

Ctrl+C可退出节点运行。

五、实战:C++版本HelloWorld节点(高性能场景)

C++版本逻辑与Python完全一致,但语法更严谨,内存管理更精细,适合机器人运动控制等高性能场景。

5.1 编写C++节点代码

创建并编辑~/ros2_ws/src/my_cpp_pkg/src/my_first_node.cpp

// 导入必要的头文件
#include <memory>   // 智能指针头文件(ROS2 C++核心)
#include <string>   // 字符串处理
#include "rclcpp/rclcpp.hpp"  // ROS2 C++核心库
#include "std_msgs/msg/string.hpp"  // 标准字符串消息类型

// 使用命名空间(简化代码,避免重复写std::chrono)
using namespace std::chrono_literals;

// 自定义节点类:继承自rclcpp::Node(所有ROS2 C++节点的基类)
class HelloWorldNode : public rclcpp::Node
{
public:
    // 构造函数:初始化节点名、发布者、订阅者、定时器
    HelloWorldNode() : Node("hello_world_cpp"), count_(0)
    {
        // 1. 创建发布者
        // 模板参数:消息类型;参数1:话题名;参数2:队列大小
        publisher_ = this->create_publisher<std_msgs::msg::String>("hello_topic", 10);

        // 2. 创建订阅者
        // 模板参数:消息类型;参数1:话题名;参数2:队列大小;参数3:回调函数(用std::bind绑定类成员函数)
        subscription_ = this->create_subscription<std_msgs::msg::String>(
            "hello_topic", 
            10, 
            // std::bind:将类成员函数绑定为回调,this指向当前对象,_1预留消息参数位置
            std::bind(&HelloWorldNode::sub_callback, this, std::placeholders::_1));

        // 3. 创建定时器
        // 参数1:定时周期(1s);参数2:绑定的回调函数
        timer_ = this->create_wall_timer(
            1s, 
            std::bind(&HelloWorldNode::timer_callback, this));
        
        // 打印启动日志(RCLCPP_INFO是ROS2 C++标准日志宏)
        RCLCPP_INFO(this->get_logger(), "✅ C++ HelloWorld节点已启动!");
    }

private:
    // 定时器回调函数:发布消息
    void timer_callback()
    {
        // 创建String消息对象(auto自动推导类型)
        auto msg = std_msgs::msg::String();
        // 拼接消息内容(std::to_string将整数转为字符串)
        msg.data = "Hello, ROS2 Humble! (第" + std::to_string(count_++) + "次)";
        
        // 发布消息
        publisher_->publish(msg);
        // 打印日志(c_str()将C++ string转为C风格字符串)
        RCLCPP_INFO(this->get_logger(), "📤 发布消息: '%s'", msg.data.c_str());
    }

    // 订阅回调函数:接收消息(msg是智能指针,通过->访问成员)
    void sub_callback(const std_msgs::msg::String::SharedPtr msg)
    {
        RCLCPP_INFO(this->get_logger(), "📥 收到消息: '%s'", msg->data.c_str());
    }

    // 成员变量声明(智能指针管理资源,自动释放)
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;  // 发布者指针
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;  // 订阅者指针
    rclcpp::TimerBase::SharedPtr timer_;  // 定时器指针
    size_t count_;  // 消息计数器(无符号整数,避免负数)
};

int main(int argc, char * argv[])
{
    // 1. 初始化ROS2上下文
    rclcpp::init(argc, argv);
    
    // 2. 创建节点实例(std::make_shared创建智能指针,自动管理内存)
    // spin()阻塞运行,直到节点退出
    rclcpp::spin(std::make_shared<HelloWorldNode>());
    
    // 3. 关闭ROS2上下文,释放资源
    rclcpp::shutdown();
    return 0;
}

5.2 配置CMakeLists.txt(C++编译核心)

CMakeLists.txt是C++包的编译配置文件,需指定编译规则、依赖和安装路径:
编辑~/ros2_ws/src/my_cpp_pkg/CMakeLists.txt,替换为以下内容:

# 指定CMake最低版本(ROS2 Humble要求3.8及以上)
cmake_minimum_required(VERSION 3.8)
# 项目名(与功能包名一致)
project(my_cpp_pkg)

# 编译选项:开启警告(提高代码质量)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# 查找依赖包(必须声明,否则编译时找不到头文件/库)
find_package(ament_cmake REQUIRED)        # ROS2构建系统核心
find_package(rclcpp REQUIRED)             # ROS2 C++核心库
find_package(std_msgs REQUIRED)           # 标准消息库

# 添加可执行文件(生成节点可执行程序)
# 格式:add_executable(可执行程序名 源码文件路径)
add_executable(hello_node src/my_first_node.cpp)

# 链接依赖库(将可执行程序与依赖库关联)
ament_target_dependencies(hello_node rclcpp std_msgs)

# 安装可执行文件(必须,否则ros2 run找不到节点)
# 将可执行程序安装到install/lib/${PROJECT_NAME}目录
install(TARGETS
  hello_node
  DESTINATION lib/${PROJECT_NAME}
)

# 测试相关(可选,新手可忽略)
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()

# 生成包配置文件(ROS2必备,声明包信息)
ament_package()

5.3 编译+运行C++节点

# 回到工作空间根目录
cd ~/ros2_ws

# 编译C++包(只编译指定包,速度更快)
colcon build --packages-select my_cpp_pkg

# 加载编译后的环境
source install/setup.bash

# 运行C++节点
ros2 run my_cpp_pkg hello_node
预期运行结果:

与Python版本一致,终端每秒输出发布和接收的消息,逻辑完全等价。

六、ROS2常用调试命令(新手必备)

运行节点后,可通过以下命令验证通信是否正常,排查问题:

# 1. 查看当前运行的所有节点
ros2 node list
# 预期输出:/hello_world_cpp 或 /hello_world_python

# 2. 查看所有话题
ros2 topic list
# 预期输出:/hello_topic /parameter_events /rosout

# 3. 实时监控话题消息(查看所有发布到hello_topic的消息)
ros2 topic echo /hello_topic

# 4. 查看节点详细信息(发布/订阅的话题、依赖等)
ros2 node info /hello_world_cpp

# 5. 查看话题类型(确认消息格式是否正确)
ros2 topic type /hello_topic
# 预期输出:std_msgs/msg/String

# 6. 手动发布消息到话题(测试订阅者是否正常)
ros2 topic pub /hello_topic std_msgs/msg/String "{data: '手动发送的测试消息'}" --once

七、常见问题排查(新手避坑)

问题现象原因解决方案
ros2: command not found未加载ROS2环境变量执行source /opt/ros/humble/setup.bash,或写入~/.bashrc
编译报错:找不到头文件CMakeLists.txt未声明依赖添加find_package(对应包 REQUIRED)
ros2 run提示找不到节点未编译/未安装可执行文件执行colcon build,并确保install目录下有节点程序
Python语法错误:form rclpy.node import Node拼写错误(form→from)修正拼写,Python对大小写/拼写敏感
C++编译报错:HelloWorldNode未定义类名拼写不一致(如HelloWordNode)统一类名拼写,C++区分大小写
C++回调函数参数不匹配定时器回调和订阅回调参数混淆定时器回调无参数,订阅回调需接收msg参数

八、核心总结

  1. ROS2核心通信模型:以「节点-话题-消息」为核心,通过发布/订阅模式实现异步通信,节点是最小执行单元;
  2. 开发流程固定:创建工作空间→创建功能包→编写节点代码→配置编译文件→编译→加载环境→运行;
  3. 语言选型:Python适合快速验证逻辑,C++适合高性能/实时场景(如机器人运动控制);
  4. 关键操作:编译后必须source install/setup.bash,否则系统无法识别新编译的节点;
  5. 调试技巧:善用ros2 node list/ros2 topic echo等命令,快速定位通信问题。
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐