【ROS2学习笔记】节点篇:用python编写一个节点
前言
本系列博文是本人的学习笔记,自用为主,不是教程,学习请移步其他大佬的相关教程。主要学习途径为@鱼香ROS大佬的教程,欢迎各位大佬交流学习,若有错误,轻喷。
典型示例
import rclpy
from rclpy.node import Nodedef main():rclpy.init() node = Node('python_node')node.get_logger().info("hello python node")rclpy.spin(node)rclpy.shutdown()if __name__ == '__main__':main()
代码整体功能
这段代码是一个最简单的 ROS2 节点,它会:
- 初始化 ROS2 环境
- 创建一个名为
python_node
的节点 - 在终端打印一条日志信息
hello python node
- 保持节点运行(等待接收任务)
- 最后在退出时清理资源
逐行详细解释
# 导入 rclpy 库,这是 ROS2 Python 客户端库的核心,提供了创建和管理节点的功能
import rclpy# 从 rclpy.node 模块中导入 Node 类,Node 类是所有 ROS2 节点的基类
from rclpy.node import Node
# 定义 main 函数,作为节点的入口
def main():
# 初始化 ROS2 通信环境,让程序与 ROS2 系统建立连接# 必须在创建节点之前调用rclpy.init()
# 创建一个节点实例,节点名为 "python_node"# 节点名在 ROS2 网络中必须唯一node = Node('python_node')
# 通过节点的日志工具输出一条信息# get_logger() 会返回一个日志器对象,可以输出不同级别的日志:debug, info, warn, error, fatal# info 表示一般提示信息,会显示在终端node.get_logger().info("hello python node")
# 让节点进入“循环等待”状态(阻塞当前线程)# 节点会一直运行,等待处理回调(话题、服务、动作等)# 只有当节点被关闭或收到退出信号时,spin 才会结束rclpy.spin(node)
# 关闭 ROS2 通信环境,释放资源# 必须在程序退出前调用,否则可能会导致资源泄露rclpy.shutdown()
# 检查当前模块是否是主模块(防止被其他模块导入时执行 main())
if __name__ == '__main__':# 如果是主模块,就执行 main() 函数main()
运行方式(复习)
假设这个文件放在一个 ROS2 包(package)中,编译后可以用:
ros2 run <package_name> <executable_name>
比如:
ros2 run my_py_pkg my_node
运行后终端会输出:
[INFO] [1695800000.123456789] [python_node]: hello python node
小提示
- rclpy.init() 和 rclpy.shutdown() 必须成对出现
- rclpy.spin(node) 会让节点保持运行,直到你按下
Ctrl + C
- 日志系统比
print()
更好,因为它会自动加上时间戳和节点名,方便调试 - 如果想让节点做更多事情,可以在
spin()
之前注册话题、服务、定时器等回调函数
丰富 ROS2 Python 节点的日志输出
1. 基础日志输出效果
运行简单的 ROS2 Python 节点(如前文中的python_node
),日志默认包含日志级别(如INFO
)、时间戳、节点名和自定义信息。示例输出:
[INFO] [1699126991.00948500] [python_node]: hello python node
2. 用环境变量自定义日志格式
通过环境变量 RCUTILS_CONSOLE_OUTPUT_FORMAT
,可自定义日志输出的内容(如添加代码函数名、行号等调试信息)。
操作示例:
- 设置环境变量(终端中执行):
export RCUTILS_CONSOLE_OUTPUT_FORMAT="{function_name}:{line_number}: {message}"
- 再次运行 Python 节点,日志输出会变为类似:
main:7: hello python node
3. 日志格式的可替换字段
RCUTILS_CONSOLE_OUTPUT_FORMAT
支持多种字段,用于精细化控制日志内容,常用字段包括:
{function_name}
:输出日志的函数名{line_number}
:输出日志的代码行号{message}
:自定义的日志信息{severity}
:日志级别(INFO
/WARN
/ERROR
等){name}
:日志记录器的名称{file_name}
:代码文件名{time}
:时间戳(秒级){time_as_nanoseconds}
:时间戳(纳秒级)