3. ROS2工作空间与功能包创建:工作空间结构、使用colcon编译、创建第一个C++/Python功能包
好,咱们直接进入正题。这一章要聊的,是ROS2开发最基础、也最绕不开的东西——工作空间和功能包。说白了,这就是你写代码的“工地”和“工具箱”。
我记得刚接触ROS1那会儿,对工作空间的理解就是“把代码扔进去,然后source一下”。结果经常出现编译冲突、找不到包、路径混乱的问题。后来转到ROS2,发现这套体系其实更清晰了,但前提是——你得理解它的设计逻辑。
3.1 工作空间长什么样?
ROS2的工作空间,本质上就是一个文件夹。但它的内部结构是约定好的。我个人习惯把它叫做ros2_ws,当然你叫什么都行。
一个标准的工作空间包含四个子目录:
| 目录名 | 作用 |
|---|---|
src |
放你的功能包源码 |
build |
编译过程中的中间文件 |
install |
编译产物的安装目录 |
log |
编译日志,排查错误时很有用 |
你想想看,这其实跟C++项目的构建流程很像。源码在src里,编译产物在install里。但ROS2用colcon这个工具来管理这一切。
核心要点:你只需要手动创建src目录。剩下的build、install、log,colcon会自动生成。
3.2 使用colcon编译——没那么玄乎
colcon是ROS2的编译工具。它替代了ROS1时代的catkin_make。说实话,我第一次用colcon的时候,觉得它比catkin慢。但后来发现,它更智能——它只编译你修改过的包,而不是全部重新编译。
基本操作就这几步:
# 创建并进入工作空间
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
# 编译整个工作空间
colcon build
# 只编译某个功能包
colcon build --packages-select my_package
# 编译并忽略测试
colcon build --cmake-args -DBUILD_TESTING=OFF
嗯,这里要注意。colcon默认是并行编译的,如果你的电脑配置不高,可以加个参数限制一下:
colcon build --parallel-workers 2
我曾经在一个树莓派上编译整个ROS2,没加这个参数,结果直接卡死。后来老老实实改成单线程,虽然慢点,但至少能跑完。
小技巧:每次编译完,别忘了source一下:source install/setup.bash。我建议把它写到~/.bashrc里,省得每次手动敲。
3.3 创建第一个C++功能包
好,咱们来动手。创建一个C++功能包,名字就叫my_cpp_pkg。
cd ~/ros2_ws/src
ros2 pkg create my_cpp_pkg --build-type ament_cmake --dependencies rclcpp std_msgs
这条命令会生成什么?我带你看看:
CMakeLists.txt—— 编译规则文件package.xml—— 包的元信息,比如依赖、版本号src/—— 放C++源文件的地方include/—— 放头文件的地方
然后我们写一个最简单的节点。在src/下创建my_node.cpp:
#include "rclcpp/rclcpp.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("my_cpp_node");
RCLCPP_INFO(node->get_logger(), "Hello from C++ node!");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
别忘了修改CMakeLists.txt,添加可执行文件:
add_executable(my_node src/my_node.cpp)
ament_target_dependencies(my_node rclcpp std_msgs)
install(TARGETS
my_node
DESTINATION lib/${PROJECT_NAME}
)
然后编译、运行:
cd ~/ros2_ws
colcon build --packages-select my_cpp_pkg
source install/setup.bash
ros2 run my_cpp_pkg my_node
看到终端打印出“Hello from C++ node!”,就说明成了。
3.4 创建第一个Python功能包
Python包更简单。我个人其实更喜欢用Python做原型验证,因为改代码不用重新编译。
cd ~/ros2_ws/src
ros2 pkg create my_py_pkg --build-type ament_python --dependencies rclpy std_msgs
生成的目录结构是这样的:
package.xmlsetup.py—— Python包的安装配置setup.cfgmy_py_pkg/—— 放Python模块的目录
在my_py_pkg/下创建my_node.py:
import rclpy
from rclpy.node import Node
def main(args=None):
rclpy.init(args=args)
node = Node("my_py_node")
node.get_logger().info("Hello from Python node!")
rclpy.spin(node)
rclpy.shutdown()
if __name__ == "__main__":
main()
然后修改setup.py,在entry_points里添加:
entry_points={
'console_scripts': [
'my_node = my_py_pkg.my_node:main',
],
},
编译和运行:
colcon build --packages-select my_py_pkg
source install/setup.bash
ros2 run my_py_pkg my_node
注意:Python包虽然不用编译C++代码,但colcon仍然会执行一些安装步骤。所以每次修改了setup.py或新增了文件,还是需要重新colcon build一下。
3.5 工作空间的核心逻辑
为了让你更直观地理解整个流程,我画了一张图:
这张图把整个流程串起来了。你从src开始,写代码,然后用colcon编译,产物放到install里,最后source一下就能运行。说白了,就是这么回事。
3.6 避坑指南
最后,分享几个我踩过的坑:
- 路径问题:一定要在
ros2_ws根目录执行colcon build,不要在src里面执行。我曾经犯过这个错,结果编译产物全乱套了。 - 依赖缺失:创建包时忘了加
--dependencies,编译时各种报错。后来我习惯先列清楚依赖再创建。 - Python包入口:记得在
setup.py的entry_points里注册你的函数,否则ros2 run找不到节点。
我的习惯:每次新建工作空间,我会先创建一个README.md,把编译命令、包名、依赖都记下来。项目大了之后,这能省不少时间。
好了,这一章的内容就到这儿。工作空间和功能包是ROS2开发的基石,你花点时间把这块搞透,后面写节点、通信、集成都会顺手很多。