2、工作空间与功能包:创建colcon工作空间,编写第一个C++/Python节点

好,咱们正式开始动手了。这一章,我带你从零搭建一个ROS2工作空间,再写两个最基础的节点——一个用C++,一个用Python。说白了,这就是你ROS2生涯的“Hello World”。

我个人习惯把工作空间比作一个“项目文件夹”。你所有的代码、依赖、配置文件,都整整齐齐地放在里面。colcon就是那个帮你把代码编译成可执行文件的工具。嗯,这里要注意,ROS2默认用的就是colcon,不是catkin了,别搞混。

2.1 创建colcon工作空间

打开终端,我们一步步来。先创建一个目录,然后初始化它。

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build

就这么简单?对,就这么简单。我第一次用colcon的时候,也觉得太轻量了。build完之后,你会看到三个新目录:build、install、log。它们各自干什么用?

目录 作用
build 编译中间文件,一般不用管
install 编译产物,包括可执行文件、库、配置文件
log 编译日志,出错了来这里查
小技巧:每次修改代码后,记得在~/ros2_ws目录下重新colcon build。我刚开始经常忘了这步,结果运行的一直是老版本,排查了半天……你想想看,多冤。

2.2 创建功能包

工作空间有了,接下来创建功能包。功能包是ROS2里最小的组织单元。一个包可以包含节点、消息定义、配置文件等等。

我们创建一个C++包和一个Python包,分别演示。

2.2.1 创建C++功能包

cd ~/ros2_ws/src
ros2 pkg create my_cpp_pkg --build-type ament_cmake --dependencies rclcpp std_msgs

这条命令干了三件事:

  • 创建了my_cpp_pkg目录
  • 指定了编译类型为ament_cmake(C++专用)
  • 添加了依赖rclcpp(ROS2客户端库)和std_msgs(标准消息类型)

打开看看,里面有个src目录,还有个CMakeLists.txt和package.xml。这两个文件是功能包的“身份证”。

重点:package.xml里声明了包的元信息和依赖,CMakeLists.txt里写了编译规则。两个文件都要改,缺一不可。

2.2.2 创建Python功能包

ros2 pkg create my_py_pkg --build-type ament_python --dependencies rclpy std_msgs

Python包的结构略有不同。它没有src目录,而是直接有一个和包名同名的Python模块目录。里面放着__init__.py和你的节点代码。

我记得刚开始用Python包时,总找不到入口点。后来才明白,需要在setup.py里配置entry_points。这个后面会讲到。

2.3 编写第一个C++节点

在my_cpp_pkg/src下新建一个文件,叫cpp_node.cpp。写一个最简单的发布者节点。

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  auto node = std::make_shared<rclcpp::Node>("cpp_node");
  auto publisher = node->create_publisher<std_msgs::msg::String>("topic", 10);
  rclcpp::WallRate rate(1);

  while (rclcpp::ok())
  {
    auto msg = std_msgs::msg::String();
    msg.data = "Hello from C++ node!";
    RCLCPP_INFO(node->get_logger(), "Publishing: '%s'", msg.data.c_str());
    publisher->publish(msg);
    rclcpp::spin_some(node);
    rate.sleep();
  }

  rclcpp::shutdown();
  return 0;
}

这段代码干了什么?

  • 初始化ROS2
  • 创建一个节点,名字叫cpp_node
  • 创建一个发布者,往topic话题上发字符串消息
  • 每秒发一次,循环不断

然后,修改CMakeLists.txt,加上编译规则:

add_executable(cpp_node src/cpp_node.cpp)
ament_target_dependencies(cpp_node rclcpp std_msgs)

install(TARGETS
  cpp_node
  DESTINATION lib/${PROJECT_NAME}
)
注意:我曾经漏写了install那一段,结果编译成功但运行时找不到可执行文件。折腾了半小时才发现……所以,install这一步千万别省。

2.4 编写第一个Python节点

在my_py_pkg/my_py_pkg目录下新建py_node.py:

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class PyNode(Node):
    def __init__(self):
        super().__init__('py_node')
        self.publisher_ = self.create_publisher(String, 'topic', 10)
        self.timer = self.create_timer(1.0, self.timer_callback)

    def timer_callback(self):
        msg = String()
        msg.data = 'Hello from Python node!'
        self.get_logger().info(f'Publishing: "{msg.data}"')
        self.publisher_.publish(msg)

def main(args=None):
    rclpy.init(args=args)
    node = PyNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Python版本用了类的方式,更符合面向对象习惯。定时器每1秒触发一次回调,发布消息。

然后修改setup.py,添加入口点:

entry_points={
    'console_scripts': [
        'py_node = my_py_pkg.py_node:main',
    ],
},

这样,ros2 run my_py_pkg py_node就能找到你的节点了。

2.5 编译与运行

回到工作空间根目录,编译:

cd ~/ros2_ws
colcon build

如果一切顺利,你会看到绿色的Finished字样。然后source环境:

source install/setup.bash

运行C++节点:

ros2 run my_cpp_pkg cpp_node

运行Python节点:

ros2 run my_py_pkg py_node

你会看到终端里每秒打印一条消息。恭喜,你的第一个ROS2节点跑起来了!

避坑指南:我曾经在多个终端窗口间切换,忘了source环境,结果一直报package not found。所以,每次打开新终端,记得先source install/setup.bash。或者干脆把它写到~/.bashrc里,一劳永逸。

2.6 知识体系总览

下面这张图,帮你理清这一章的核心逻辑:

colcon工作空间 功能包 C++功能包 ament_cmake Python功能包 ament_python cpp_node(发布者) py_node(发布者) 话题 图:工作空间 → 功能包 → 节点 → 话题 的层级关系

说白了,这一章就干了三件事:搭环境、建包、写节点。你想想看,是不是很简单?但就是这三步,构成了你后续所有ROS2开发的基石。

嗯,到这里,你已经有了自己的第一个C++和Python节点。接下来,你可以试着改改话题名称、消息内容,或者同时运行两个节点看看它们怎么通信。动手试试,比看十遍都管用。

可可精品资料库