从 ROS2 基本概念一路讲到 Topic、Service、Action、Parameter、Launch、rosbag2、TF、RViz2 和 URDF,使用的是 Ubuntu 22.04 + ROS2 Jazzy,按下面这条线走:
| 阶段 | 学什么 | 最终你能干什么 |
|---|---|---|
| 1 | ROS2、Node、命令行 | 看懂 ROS2 系统 |
| 2 | Topic | 写发布者/订阅者 |
| 3 | Service / Action / Parameter | 搞懂各种通信方式 |
| 4 | Workspace、Package、colcon | 自己建 C++ ROS2 工程 |
| 5 | Launch、rosbag2 | 管理和调试大型项目 |
| 6 | TF2、URDF、RViz2 | 建机器人模型和坐标系 |
| 7 | 项目化 | 把你的动捕/GMR链路 ROS2 化 |
现在先只记住这个:
ROS2
┌──── Node ────┐
│ │
│ Publisher │
│ │ │
└──────│───────┘
│
Topic
│
┌──────↓───────┐
│ Subscriber │
│ │
└──── Node ────┘
翻译成人话:
Node
程序
Publisher
发数据的人
Subscriber
收数据的人
Topic
传输数据的频道
比如:
IMU节点
│
│ 发布姿态
↓
/imu/data
│
↓
控制器节点
这里:
IMU节点 Node
/imu/data Topic
IMU Publisher
控制器 Subscriber
这就是 ROS2 最核心的东西之一。
官方文档也把 ROS2 Graph 描述为多个 ROS2 元素共同运行并相互连接形成的网络;通常一个 Node 负责一个相对独立的功能。(Ros Documentation)
我们暂时不碰图形界面、不碰 turtlesim。
先验证你的 ROS2 Jazzy 核心环境,这样也不用管之前 VcXsrv/VMware GUI 那些问题。
打开终端:
source /opt/ros/jazzy/setup.bash
作用是把 ROS 2 Jazzy 版本的环境变量加载到当前终端:/opt/ros/jazzy/ 是 ROS 2 Jazzy 的安装目录。setup.bash 是 ROS 2 安装时自带的环境配置脚本,source 让这个脚本在当前 Bash 里执行,而不是开子 shell,这样设置的环境变量才会保留下来。
执行后,当前终端才能找到 ros2 命令,找到 ROS 2 的核心库和可执行文件,找到已安装的 ROS 2 包,比如 demo_nodes_cpp,如果不执行这一行,直接输入 ros2,通常会提示:ros2: command not found,或者找不到包。
然后:
ros2 --help
ROS2 官方说明,ros2 就是 ROS2 命令行工具的主要入口,下面还有 node、topic、service、action、param、bag、launch 等子命令。(Ros Documentation)
接下来执行:
ros2 node list
终端 1:
source /opt/ros/jazzy/setup.bash
ros2 run demo_nodes_cpp talker
-
ros2:ROS 2 的命令行工具。 -
run:ros2的子命令,意思是"运行一个节点"。 -
demo_nodes_cpp:一个 ROS 2 示例包,里面包含用 C++ 写的演示节点。 -
talker:这个包里的一个可执行文件,通常是一个发布者节点。
启动 demo_nodes_cpp 包里的 talker 节点。
这个节点会:
-
创建一个发布者
-
往话题
chatter上周期性地发布字符串消息 -
默认大约每秒发布一次,内容类似:
Publishing: 'Hello World: 1'
Publishing: 'Hello World: 2'
Publishing: 'Hello World: 3'
不要关。
再打开 终端 2:
source /opt/ros/jazzy/setup.bash
ros2 node list
看看现在出现了什么。
再运行:
ros2 topic list
然后:
ros2 topic echo /chatter
你应该开始看到:
data: Hello World: 1
data: Hello World: 2
data: Hello World: 3
这里已经出现了 ROS2 最核心的通信:
talker Node
│
│ Publisher
↓
/chatter
│
│ Topic
↓
你执行的 ros2 topic echo
官方 Jazzy 文档也使用 /chatter 演示 Topic 发布与接收。(Ros Documentation)
你暂时不用记几十个命令,只要真正理解这句话:
ROS2 的核心思想,就是把一个大型机器人软件拆成很多 Node,然后让 Node 之间通过标准方式通信。
这句话以后会贯穿你的整个项目。
1. Package 是什么?
可以先把:
Package
理解成:
一个功能模块的"文件夹 + 配置 + 代码"。
比如将来你的工程可能有:
my_robot_ws/
└── src/
├── mocap_driver/
├── gmr_retarget/
├── robot_output/
└── humanoid_bringup/
这里:
mocap_driver
gmr_retarget
robot_output
每一个都可以是一个 ROS2 Package。
例如:
mocap_driver
│
├── Axis驱动
├── Xsens驱动
├── PNLink驱动
├── 参数文件
├── C++源码
└── ROS2配置文件
所以 Package 比 Node 更大。
关系可以理解成:
Package
│
├── Node A
├── Node B
├── 配置文件
├── Launch
├── 头文件
└── 其他代码
2. Node 又是什么?
Node 是:
正在运行的 ROS2 功能单元。
例如:
mocap_driver package
│
└── mocap_driver_node
运行:
ros2 run mocap_driver mocap_driver_node
之后系统里才真的出现:
/mocap_driver_node
所以一定要区分:
Package
↓
里面装程序
Executable
↓
真正可以运行的程序
Node
↓
程序运行起来后,参与 ROS2 通信的节点
这一点非常重要。
初学阶段,你可以近似认为:
1 个程序 ≈ 1 个 Node
比如:
ros2 run demo_nodes_cpp talker
产生:
/talker
但严格来说,一个进程里面可以创建多个 Node。
目前记成:
Node 就是 ROS2 里面负责一项工作的"小程序"。
完全够用了。
3. Message 又是什么?
这里出现:
std_msgs/msg/String
什么意思?
拆开:
std_msgs / msg / String
│ │ │
│ │ └── 消息名称
│ │
│ └── message
│
└── ROS2标准消息包
就是:
/chatter里面传输的数据格式是 String。
类似 C++:
std::string
但 ROS2 使用自己的 Message 定义通信格式。
4. ROS2 五个东西先建立框架
现在你的脑子里应该开始形成:
ROS2
│
├── Package
│ ↓
│ 存代码
│
├── Node
│ ↓
│ 干活
│
├── Topic
│ ↓
│ 数据频道
│
├── Publisher
│ ↓
│ 发数据
│
└── Subscriber
↓
收数据
暂时理解这五个就行。
先记住一张图:
ROS2 工程
│
└── ros2_ws Workspace 工作空间
│
├── src 放源码
│ │
│ └── my_first_pkg Package
│
├── build 编译中间文件
├── install 编译后的可运行环境
└── log 编译日志
其中最重要的是:
Workspace
↓
Package
↓
C++源码
↓
colcon build
↓
Executable
↓
ros2 run
这就是以后你开发 ROS2 项目的基本流程。
5. Workspace 是什么?
你可以把 Workspace 理解成:
整个 ROS2 项目的"大工程目录"。
以后你的机器人项目可能是:
humanoid_ws/
└── src/
├── mocap_driver
├── gmr_retarget
├── robot_output
├── humanoid_description
└── humanoid_bringup
也就是说:
一个 Workspace
可以包含
很多 Package
ROS2 Jazzy 的常见工作流也是在 workspace 的 src 目录中放 Package,然后使用 colcon 构建整个工作空间。(GitHub)
打开终端,先加载 ROS2:
source /opt/ros/jazzy/setup.bash
然后:
cd ~
mkdir -p ~/ros2_ws/src
再进去看看:
cd ~/ros2_ws
ls
现在应该只有:
src
所以结构是:
/home/你的用户名/
└── ros2_ws/
└── src/
这里的:
ros2_ws
就是我们的 Workspace。
src = source
也就是:
放源码的地方。
以后:
ros2_ws/src/
里面可能有:
mocap_driver/
gmr_retarget/
robot_output/
所以可以理解成:
ros2_ws
│
└── src
│
├── 包1
├── 包2
├── 包3
└── 包4
6. 创建第一个 C++ Package
进入:
cd ~/ros2_ws/src
执行:
ros2 pkg create \
--build-type ament_cmake \
--license Apache-2.0 \
--dependencies rclcpp \
--node-name my_first_node \
my_first_pkg
拆开看:
ros2 pkg create
意思:
创建 ROS2 Package。
然后:
--build-type ament_cmake
意思:
这是一个 C++ Package。
目前你可以近似记:
C++ → ament_cmake
Python → ament_python
我们以后主要走 C++。
接下来:
--dependencies rclcpp
表示这个 Package 要用:
rclcpp
rclcpp 就是:
ROS2 的 C++ 客户端库。
以后你写:
rclcpp::Node
rclcpp::Publisher
rclcpp::Subscription
都来自这里。
最后:
my_first_pkg
就是你的 Package 名字。
而:
my_first_node
是生成的可执行程序名字。
ROS2 官方教程也支持使用 ros2 pkg create --build-type ament_cmake ... --node-name ... 创建 C++ Package 和示例源文件。(GitHub)
7. package.xml 是干什么的?
暂时大白话理解:
package.xml
就是:
这个 Package 的身份证。
里面告诉 ROS2:
我叫什么
我是什么版本
谁维护
什么许可证
我依赖什么包
可以看看:
cat package.xml
里面你应该能找到类似:
<name>my_first_pkg</name>
以及:
<depend>rclcpp</depend>
说明:
my_first_pkg
依赖
rclcpp
8. CMakeLists.txt 又是什么?
这个非常重要。
你已经有一些 C/C++ 基础,所以可以这样理解:
CMakeLists.txt
就是告诉编译系统:
我的 C++ 源文件在哪里、需要什么库、最终要生成什么程序。
也就是说:
my_first_node.cpp
↓
CMakeLists.txt
↓
CMake
↓
编译
↓
my_first_node
ROS2 的 C++ Package 使用的是:
ament_cmake
但底下依然是你熟悉的:
CMake
所以以后你的 EtherCAT、GMR、SDK 代码往 ROS2 搬时,这部分其实非常重要。
9. 开始编译
现在一定回到:
ros2_ws
而不是在:
ros2_ws/src
执行:
cd ~/ros2_ws
确认:
pwd
应该类似:
/home/skr/ros2_ws
然后:
source /opt/ros/jazzy/setup.bash
开始编译:
colcon build --packages-select my_first_pkg
这里:
colcon build
意思:
编译 ROS2 Workspace。
而:
--packages-select my_first_pkg
意思:
我这次只编译
my_first_pkg。
以后你的 Workspace 可能有几十个 Package。
修改了:
gmr_retarget
就没必要所有东西全部重新编译,可以:
colcon build --packages-select gmr_retarget
这是以后非常常用的命令。ROS2 文档与 Jazzy 工程都普遍使用这种单包构建方式。(GitHub)
10. 编译成功后发生了什么?
运行:
ls
现在应该出现:
build
install
log
src
也就是:
ros2_ws/
│
├── build/
├── install/
├── log/
└── src/
这三个新目录是:
build
编译过程中的中间文件。
install
真正安装好的 ROS2 Package 和程序。
log
编译日志。
以后遇到:
编译失败
经常就会去看:
build/
log/
11. source install/setup.bash
编译完以后执行:
source ~/ros2_ws/install/setup.bash
然后:
ros2 pkg list | grep my_first_pkg
应该出现:
my_first_pkg
这说明:
ROS2 终于认识你的 Package 了。
你现在已经看到两个非常像的命令。
第一个:
source /opt/ros/jazzy/setup.bash
第二个:
source ~/ros2_ws/install/setup.bash
这是非常关键的区别。
可以这么理解:
/opt/ros/jazzy
↓
ROS2 官方环境
~/ros2_ws
↓
你自己开发的环境
所以:
source /opt/ros/jazzy/setup.bash
相当于告诉终端:
我现在要使用 ROS2 Jazzy。
而:
source ~/ros2_ws/install/setup.bash
相当于:
再把我自己开发的 Package 加进 ROS2 环境。
这就是:
Underlay
ROS2 Jazzy
↓
Overlay
你自己的 Workspace
Jazzy 开发流程中,在构建 Workspace 后 source install/setup.bash 来加载 overlay 是标准做法。(GitHub)
ros2 run demo_nodes_cpp talker
意思是:
运行
demo_nodes_cpp这个包里的talker可执行文件。
拆开看:
-
ros2:ROS 2 命令行工具 -
run:运行一个节点 -
demo_nodes_cpp:包名,ROS 2 官方 C++ 示例包 -
talker:可执行文件名
运行后会启动一个节点,节点名默认也叫 /talker,它会不断往 /chatter 话题发布字符串消息
ros2 node info /talker
意思是:
查看名为
/talker的节点的信息。
前提是 talker 节点正在运行。
它会列出这个节点的:
-
发布者(Publishers)
-
订阅者(Subscribers)
-
服务端(Service Servers)
-
客户端(Service Clients)
-
Action 服务端和客户端
-
参数相关服务
注意:
-
/talker是节点名,不是可执行文件名。 -
节点名默认和可执行文件名一样,但可以通过参数改。
-
如果节点没运行,会提示找不到节点。
可以先用:
ros2 node list
查看当前有哪些节点在运行。
| 命令 | 作用 |
|---|---|
ros2 run demo_nodes_cpp talker |
运行 demo_nodes_cpp 包里的 talker 可执行文件 |
ros2 node info /talker |
查看 /talker 节点的发布、订阅、服务等信息 |
Executable |
可执行文件,ros2 run 的第二个参数 |
pwd |
打印当前工作目录 |
简单说:
ros2 run负责运行节点;ros2 node info负责查看节点;Executable是可执行文件;pwd是看当前目录。
nano my_first_node.cpp 的意思是:
用 nano 这个终端文本编辑器,打开当前目录下名为
my_first_node.cpp的文件。如果这个文件不存在,nano 会新建它。
它只是编辑文件,不是编译,也不是运行。
12. 运行自己的程序
现在:
ros2 run my_first_pkg my_first_node
应该能看到生成程序的输出。
到这里其实已经非常重要了。
因为这是第一次:
你自己的源码
↓
ros2 pkg create
↓
my_first_pkg
↓
colcon build
↓
install
↓
source install/setup.bash
↓
ros2 run
↓
程序运行
你已经走完 ROS2 C++ 开发最基本的一整圈。