6:ROS2 Python 发布者 Publisher 从零实现
前言
很多刚学ROS2的同学都会遇到一个问题:跟着步骤能跑通程序,但完全不懂原理。
比如:为什么创建包要加 --dependencies?为什么写了代码必须编译?为什么新开终端找不到功能包?文件夹这么多分别是干嘛的?ROS1和ROS2发布者到底有什么区别?
本文基于Ubuntu + ROS2 + Python,结合亲手实操的小乌龟发布者案例,从零、无死角、逐行拆解ROS2发布者开发全流程。
包含:目录结构详解、每个文件的作用、代码逐行解析、编译运行原理、报错原因、固定开发套路,看完彻底告别“稀里糊涂跑通代码”。
本文适配纯小白,无任何ROS基础也能看懂,所有命令、配置、代码均为实操验证过的可落地版本。
一、核心认知:ROS2话题通信与发布者核心概念
1.1 什么是节点、话题、发布者?
-
节点(Node):ROS2中最小的执行单元,就是我们写的Python程序(publisher节点),负责实现具体功能。
-
话题(Topic):节点之间数据传输的“通道”,是单向通信管道。比如小乌龟的速度控制话题
/turtle1/cmd_vel。 -
发布者(Publisher):负责主动往话题通道发送数据的节点。简单说:发布者=发数据的一方。
-
订阅者(Subscriber):对应接收话题数据的一方(本文暂不展开,后续适配)。
本次实操核心逻辑:我们自己写的Python发布者节点 → 持续向 /turtle1/cmd_vel 话题发布速度数据 → 小乌龟仿真节点接收数据 → 小乌龟转圈运动。
1.2 ROS2 与 ROS1 最大区别(新手必看)
ROS1与ROS2,语法、命令、机制完全不通用,核心差异:
-
ROS1 需要
roscore启动核心,ROS2 无需核心节点,分布式运行; -
ROS1 创建包直接后缀写依赖,ROS2 必须加
--dependencies声明依赖; -
ROS1 无需注册节点,ROS2 必须在
setup.py注册可执行节点,否则ros2 run找不到; -
ROS1 编译
catkin_make,ROS2 编译colcon build; -
ROS1 环境
devel,ROS2 环境install。
二、ROS2工作空间与功能包完整结构详解
2.1 工作空间路径
你本次实操的完整工作目录:~/Test/colcon_ws
ROS2 标准工作空间固定结构:
-
src:唯一的源码存放目录,所有自定义功能包必须放在这里,绝对不能乱放;
-
build:编译缓存目录,自动生成,无需手动修改;
-
install:编译输出目录,ROS2 真正运行的代码在这里(核心重点);
-
log:日志目录,存放运行报错日志。
2.2 功能包完整目录逐文件解析(重点)
执行创建包命令后,src 下生成的 learning_topic 功能包,每一个文件、文件夹作用全覆盖解析
ros2 pkg create --build-type ament_python learning_topic --dependencies rclpy std_msgs geometry_msgs turtlesim
learning_topic
├── learning_topic # 【核心代码目录】写 Python节点的地方
│ └── __init__.py # Python包标识文件,空文件即可,无需修改
├── package.xml # 【ROS身份配置】声明包名、依赖、版本
├── resource
│ └── learning_topic # ROS内部资源索引文件,无需修改
├── setup.cfg # Python打包辅助配置,无需修改
├── setup.py # 【必改核心】编译配置、注册可执行节点
└── test # 自动化测试目录,新手完全不用管
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py
2.3 关键文件核心作用
1. 内层 learning_topic 文件夹
唯一的代码编写目录,所有的publisher.py、subscriber.py 放在这里,和__init__.py 同级。
__init__.py:标记当前文件夹为Python模块,让ROS2可以识别导入内部的py文件,默认空文件,无需编辑。
2. package.xml(依赖配置文件)
相当于功能包的“身份证”,创建包时填写的依赖 rclpy std_msgs geometry_msgs turtlesim 会自动写入此文件。
作用:告诉ROS2,当前这个包运行需要依赖哪些官方库,缺少依赖会编译报错、运行报错。
新手场景:不需要手动修改,只有新增额外依赖时才需要编辑。
3. setup.py(最最重要!90%新手报错都在这里)
ROS2 Python包的编译+运行核心配置文件,必须手动修改。
核心作用:注册可执行节点。如果不在这里注册,哪怕代码写的再对,ros2 run 也找不到节点,报 Package not found 或节点不存在错误。
核心配置字段 entry_points 格式固定:
entry_points={
'console_scripts': [
'终端运行的节点名 = 包名.文件名:主函数名',
],
},
对应实操配置:
entry_points={
'console_scripts': [
'velocity_publisher = learning_topic.publisher:main',
],
},
- velocity_publisher:ros2 run 后面的节点名称(自定义)
- learning_topic.publisher:包名.你的py文件名(无需加后缀)
- main:代码入口函数名,固定对应代码里的main函数
三、从零创建ROS2发布者功能包(标准正确命令)
3.1 ROS2高频报错点
ROS2 不支持 ROS1 直接后缀写依赖的语法,所有依赖必须跟在 --dependencies 后面。
3.2 正确创建包完整命令
# 进入工作空间源码目录
cd ~/Test/colcon_ws/src
# ROS2 Python功能包创建命令(带完整依赖)
ros2 pkg create --build-type ament_python learning_topic --dependencies rclpy std_msgs geometry_msgs turtlesim
3.3 命令逐段解析
ros2 pkg create:ROS2 创建功能包主命令;--build-type ament_python:指定为Python类型功能包(C++用ament_cmake);learning_topic:自定义功能包名;--dependencies:固定关键字,声明后续为依赖库;rclpy:ROS2 Python核心库(必须,所有Python节点都需要);std_msgs:标准消息库;geometry_msgs:几何消息库(本次小乌龟Twist消息依赖);turtlesim:小乌龟仿真依赖库。
四、ROS2 Publisher发布者代码逐行超详细解析
路径:~/Test/colcon_ws/src/learning_topic/learning_topic/publisher.py
完整可运行代码(适配小乌龟转圈案例):
# 1.导入ROS2核心库
import rclpy
from rclpy.node import Node
# 导入小乌龟速度消息类型
from geometry_msgs.msg import Twist
# 2.自定义发布者节点类,继承ROS2基础Node类
class VelocityPublisher(Node):
# 节点初始化函数
def __init__(self):
# 初始化父类,注册节点名称:velocity_publisher
super().__init__("velocity_publisher")
# 3.创建发布者对象(核心)
# 参数:消息类型、话题名称、队列长度
self.pub = self.create_publisher(Twist, "/turtle1/cmd_vel", 10)
# 4.设置定时器,控制发布频率(0.1秒发布一次=10Hz)
self.timer_period = 0.1
self.timer = self.create_timer(self.timer_period, self.timer_callback)
# 定时器回调函数:定时执行,持续发布消息
def timer_callback(self):
# 实例化速度消息对象
vel_msg = Twist()
# 设置线速度:x方向前进
vel_msg.linear.x = 0.5
# 设置角速度:z方向旋转(实现转圈)
vel_msg.angular.z = 0.2
# 5.发布消息(核心动作)
self.pub.publish(vel_msg)
# 终端打印发布日志
self.get_logger().info(f"发布速度:线速度={vel_msg.linear.x}, 角速度={vel_msg.angular.z}")
# 程序入口函数
def main():
# 初始化ROS2环境
rclpy.init()
# 创建自定义发布者节点对象
node = VelocityPublisher()
# 循环阻塞,保持节点运行,持续执行定时器回调
rclpy.spin(node)
# 释放节点资源
node.destroy_node()
# 关闭ROS2环境
rclpy.shutdown()
# 程序入口触发
if __name__ == "__main__":
main()
4.1 代码四大核心步骤
- 初始化ROS节点:rclpy.init() + super().__init__()
- 注册发布者:self.create_publisher() 绑定话题和消息类型
- 封装消息数据:实例化 Twist 对象,赋值线速度、角速度
- 循环发布消息:定时器定时调用 publish() 发送数据
4.2 关键函数深度解释
- create_publisher(消息类型, 话题名, 队列长度):创建发布者,告诉ROS2当前节点要往哪个话题发什么数据,队列长度10为通用默认值,缓存未处理的消息;
- create_timer(周期, 回调函数):实现循环发布,替代ROS1的 ros::Rate,精准控制发布频率;
- rclpy.spin(node):节点“保活”核心,阻塞程序,持续监听、执行定时器回调,没有这行代码,节点运行后会立刻退出;
- get_logger().info():ROS2标准打印日志,替代print,可分级输出日志信息。
五、ROS2编译核心原理
这是90%新手最疑惑的点:我只是改了py代码,为什么必须重新编译?
5.1 ROS2编译的本质(ament_python机制)
你的源码在:colcon_ws/src/功能包/xxx.py
ROS2 绝对不直接运行src里的源码!
执行 colcon build 的核心作用:把src中的Python代码、配置文件,完整复制到 install 目录下。
ROS2 运行节点时,读取的是install/ 目录下的副本,不是 src 源码!
5.2 关键结论
-
修改
src中的代码 → install 里还是旧代码; -
不编译直接运行:程序无变化,依旧跑旧代码;
-
执行编译:新代码覆盖install旧副本,修改生效;
-
增量编译:只编译修改的包,不会覆盖其他包的文件,速度极快。
5.3 标准编译命令(推荐,最快)
# 回到工作空间根目录
cd ~/Test/colcon_ws
# 只编译当前自定义包(避免全空间编译浪费时间)
colcon build --packages-select learning_topic
# 刷新环境变量
source install/setup.bash
六、环境变量核心原理
wzl@ubuntu-wzl:~/Test$ ros2 run learning_topic velocity_publisher
Package 'learning_topic' not found
6.1 报错根本原因
新开终端、切换目录后,终端没有加载ROS2工作空间环境变量
终端不知道你的 learning_topic 功能包存在。
source install/setup.bash 命令的作用:
告诉当前终端,注册这个工作空间的所有功能包、节点、话题,让终端可以识别 ros2 run 命令。
6.2 核心规则
-
每次新开终端,必须执行一次 source;
-
每次重新编译后,必须重新 source;
-
source 只对当前终端生效,其他终端不受影响。
6.3 永久解决不用每次source(可选)
echo "source ~/Test/colcon_ws/install/setup.bash" >> ~/.bashrc
source ~/.bashrc
七、完整可落地运行流程
全程无错、已验证成功的最终流程,以后所有ROS2 Python节点都按这个来:
第一步:进入工作空间 src 源码目录 + 创建包
cd ~/Test/colcon_ws/src
ros2 pkg create --build-type ament_python learning_topic --dependencies rclpy std_msgs geometry_msgs turtlesim
第二步:在init同级文件下创建并写入 publisher.py 完整发布者代码
cd ~/Test/colcon_ws/src/learning_topic/learning_topic
touch publisher.py
# 导入ROS2核心依赖
import rclpy
from rclpy.node import Node
# 导入小乌龟速度消息类型
from geometry_msgs.msg import Twist
# 自定义发布者节点类
class VelocityPublisher(Node):
def __init__(self):
# 初始化节点,节点名:velocity_publisher
super().__init__("velocity_publisher")
# 创建发布者:消息类型、话题名、队列长度
self.pub = self.create_publisher(Twist, "/turtle1/cmd_vel", 10)
# 定时器:0.1s执行一次,10Hz频率发布
self.timer_period = 0.1
self.timer = self.create_timer(self.timer_period, self.timer_callback)
# 定时发布回调函数
def timer_callback(self):
# 实例化消息对象
vel_msg = Twist()
vel_msg.linear.x = 0.5
vel_msg.angular.z = 0.2
# 发布话题数据
self.pub.publish(vel_msg)
# 终端日志输出
self.get_logger().info(f"发布速度:线速度={vel_msg.linear.x}, 角速度={vel_msg.angular.z}")
# 程序入口
def main():
rclpy.init()
node = VelocityPublisher()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == "__main__":
main()
第三步:修改 setup.py 完成节点注册(ROS2 最关键一步)
只有注册节点后,ros2 run 才能识别你的程序,否则报 Package not found / 无节点 错误。
cd ~/Test/colcon_ws/src/learning_topic
打开 setup.py 文件,找到 entry_points 字段,替换为下面内容:
entry_points={
'console_scripts': [
'velocity_publisher = learning_topic.publisher:main',
],
},
第三步:回到工作空间根目录编译 + 刷新环境
Python 代码必须编译复制到 install 目录,新代码才会生效
cd ~/Test/colcon_ws
colcon build --packages-select learning_topic
source install/setup.bash
第四步:双终端运行测试(最终验证)
终端1:启动小乌龟仿真
ros2 run turtlesim turtlesim_node
终端2:加载环境并运行自定义发布者
source ~/Test/colcon_ws/install/setup.bash
ros2 run learning_topic velocity_publisher
八、配套调试命令(新手必备)
运行成功后,可通过以下命令验证话题通信:
1. 查看所有活跃话题
ros2 topic list
可以看到你的核心话题:/turtle1/cmd_vel
2. 实时打印话题发布的数据
ros2 topic echo /turtle1/cmd_vel
实时查看你代码发布的线速度、角速度,验证发布功能正常。
3. 检查功能包是否被终端识别
ros2 pkg list | grep learning_topic
有输出说明包加载成功,无输出说明环境未加载或编译失败。
九、新手高频报错汇总+终极解决方案
报错1:创建包 unrecognized arguments
原因:ROS2 依赖未加 --dependencies,沿用ROS1语法
解决:使用本文标准创建包命令
报错2:Package not found
原因:未source环境、编译失败、setup.py未注册节点
解决:回到工作空间source环境,重新编译,核对setup.py配置
报错3:运行无效果,代码修改不生效
原因:修改代码后未重新编译,运行的是install旧代码
解决:改完代码必执行编译+source
更多推荐


所有评论(0)