5.1.2 ROS2 Installation
如果使用的是 Bianbu ROS 可以跳过该步骤
设置软件源
终端执行下面命令启用进迭时空 ROS2 软件源:
grep -q '^Suites:.*\bnoble-ros\b' /etc/apt/sources.list.d/bianbu.sources || sudo sed -i '0,/^Suites:/s//& noble-ros/' /etc/apt/sources.list.d/bianbu.sources
sudo apt update
安装工具
安装开发工具(推荐)
如需编译 ROS2 功能包或进行二次开发,建议安装开发工具集:
sudo apt update && sudo apt install ros-dev-tools
安装 ROS-Base
基础版包含 ROS 通信库、消息接口、命令行工具等核心组件,不包含 GUI 工具:
sudo apt install ros-humble-ros-base
构建自定义 ROS2 功能包
创建工作空间
mkdir -p ~/ros2_demo_ws/src
加载 ROS2 环境变量
source /opt/ros/humble/setup.zsh
若使用 Bash 终端,请将 setup.zsh
替换为 setup.bash
。
创建 Python 发布/订阅示例包
创建名为 py_pubsub
的功能包:
cd ~/ros2_demo_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
创建成功后,ROS2 将自动生成所需的功能包结构。
编写发布节点
cd ~/ros2_demo_ws/src/py_pubsub/py_pubsub
vim publisher_member_function.py
粘贴如下代码:
import rclpy # 导入 ROS2 Python 客户端库
from rclpy.node import Node # 从 rclpy 中导入 Node 类,用于创建节点
from std_msgs.msg import String # 导入标准的字符串消息类型(std_msgs/String)
# 定义一个继承自 Node 的类,用于创建发布者节点
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher') # 初始化父类,并设置节点名称为 "minimal_publisher"
self.publisher_ = self.create_publisher( # 创建一个发布者,发布消息类型为 String
String, # 消息类型为 std_msgs/String
'topic', # 主题名称为 "topic"
10 # 队列大小为10,缓冲待发送的消息
)
timer_period = 0.5 # 定时器周期设置为0.5秒
self.timer = self.create_timer( # 创建定时器,每0.5秒调用一次回调函数
timer_period,
self.timer_callback
)
self.i = 0 # 定义一个计数器,用于在消息中加入序号
def timer_callback(self):
msg = String() # 创建一个 String 类型的消息对象
msg.data = 'Hello World: %d' % self.i # 设置消息内容,包含当前计数值
self.publisher_.publish(msg) # 发布消息到 "topic"
self.get_logger().info('Publishing: "%s"' % msg.data) # 在终端输出当前发布的消息内容
self.i += 1 # 计数器递增,用于下一条消息
# main 函数是程序入口点
def main(args=None):
rclpy.init(args=args) # 初始化 rclpy
minimal_publisher = MinimalPublisher() # 创建 MinimalPublisher 节点对象
rclpy.spin(minimal_publisher) # 保持节点运行,监听回调(比如定时器回调)
minimal_publisher.destroy_node() # 节点关闭前,销毁节点资源
rclpy.shutdown() # 关闭 rclpy 系统
# 如果作为主程序运行,则调用 main() 函数
if __name__ == '__main__':
main()
添加依赖项
cd ~/ros2_demo_ws/src/py_pubsub
vim package.xml
在 <license>Apache-2.0</license>
后添加:
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
配置入口点
编辑 setup.py
文件,确认 maintainer
、license
等字段与 package.xml
保持一致:
maintainer='YourName',
maintainer_email='you@email.com',
description='Examples of minimal publisher/subscriber using rclpy',
license='Apache-2.0',
并在 entry_points
中添加:
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
],
},
验证 setup.cfg 配置
确保 setup.cfg
内容如下所示(默认已正确生成):
[develop]
script_dir=$base/lib/py_pubsub
[install]
install_scripts=$base/lib/py_pubsub
编写订阅节点
cd ~/ros2_demo_ws/src/py_pubsub/py_pubsub
vim subscriber_member_function.py
粘贴以下代码:
import rclpy # 导入 ROS2 的 Python 客户端库
from rclpy.node import Node # 从 rclpy.node 中导入 Node 类,用于定义节点
from std_msgs.msg import String # 导入标准消息类型 String(std_msgs/String)
# 定义一个继承自 Node 的类:MinimalSubscriber(最小订阅者示例)
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber') # 初始化父类,并设置节点名称为 "minimal_subscriber"
# 创建一个订阅者,订阅名为 'topic' 的主题,消息类型为 String,队列大小为 10
self.subscription = self.create_subscription(
String, # 消息类型为 std_msgs/String
'topic', # 订阅的主题名称为 'topic'
self.listener_callback, # 接收到消息时回调函数
10 # 消息队列大小
)
self.subscription # 防止变量未使用的警告(可有可无,起提示作用)
# 回调函数:每当收到一条消息,就会调用这个函数
def listener_callback(self, msg):
# 打印收到的消息内容到终端
self.get_logger().info('I heard: "%s"' % msg.data)
# main() 函数是程序的入口
def main(args=None):
rclpy.init(args=args) # 初始化 ROS2 客户端功能
minimal_subscriber = MinimalSubscriber() # 创建 MinimalSubscriber 节点实例
rclpy.spin(minimal_subscriber) # 保持节点运行,监听消息并调用回调函数
# 显式销毁节点资源(可选,一般在程序退出前调用)
minimal_subscriber.destroy_node()
rclpy.shutdown() # 关闭 ROS2 客户端功能
# 如果是主程序执行,则调用 main 函数
if __name__ == '__main__':
main()
订阅者节点的代码几乎与发布者相同。构造函数使用与发布者相同的参数创建一个订阅者。发布者和订阅者使用的主题名称和消息类型必须匹配,以便它们能够进行通信。
订阅者的构造函数和回调函数不包含任何计时器定义,因为它不需要。它的回调函数在接收到消息后立即被调用。
由于此节点与发布者具有相同的依赖项,因此无需在 package.xml 中添加任何新内容。 setup.cfg 文件也可以保持不变。
添加订阅节点入口
编辑 setup.py
,在发布节点下方添加订阅节点入口:
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},