零基础入门ROS2 Humble:从概念到实战(Python+CPP双版本HelloWorld)
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 核心差异(新手快速认知)
| 特性 | ROS1 | ROS2 |
|---|---|---|
| 通信架构 | 基于Master中心节点 | 无中心节点(DDS分布式) |
| 实时性 | 弱(无原生实时支持) | 强(支持RTI Connext等实时DDS) |
| 跨平台 | 仅Linux | Linux/Windows/macOS/嵌入式 |
| 依赖管理 | rosbuild/catkin | ament(支持Python/C++混合开发) |
二、环境准备(ROS2 Humble 安装与验证)
2.1 安装方式(新手推荐一键安装)
ROS2官方安装流程较繁琐,推荐使用fishros一键安装(适配Ubuntu 22.04):
# 下载并执行一键安装脚本
wget http://fishros.com/install -O fishros && sudo bash fishros
按脚本提示操作:
- 选择「1.一键安装ROS」→ 选择「1.humble(ROS2)」→ 选择「1.humble(ROS2)基础版(小)」;
- 等待安装完成(约10-20分钟,取决于网络);
- 安装完成后重启终端。
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节点(新手友好)
功能需求
创建一个节点,实现两个核心功能:
- 每秒向
hello_topic话题发布一条「Hello, ROS2 Humble!」字符串消息; - 订阅
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参数 |
八、核心总结
- ROS2核心通信模型:以「节点-话题-消息」为核心,通过发布/订阅模式实现异步通信,节点是最小执行单元;
- 开发流程固定:创建工作空间→创建功能包→编写节点代码→配置编译文件→编译→加载环境→运行;
- 语言选型:Python适合快速验证逻辑,C++适合高性能/实时场景(如机器人运动控制);
- 关键操作:编译后必须
source install/setup.bash,否则系统无法识别新编译的节点; - 调试技巧:善用
ros2 node list/ros2 topic echo等命令,快速定位通信问题。
更多推荐



所有评论(0)