【ROS2学习笔记】节点篇:ROS 2编程基础
前言
本系列博文是本人的学习笔记,自用为主,不是教程,学习请移步其他大佬的相关教程。主要学习途径为@鱼香ROS大佬的教程,欢迎各位大佬交流学习,若有错误,轻喷。
一、面向对象编程(OOP)基础
面向对象编程是现代语言的核心特性,与 C 语言 “面向过程” 不同,它通过 ** 类(Class)** 对 “事物” 进行封装,类包含:
- 属性:描述事物的特征(如 “人” 的年龄、“手机” 的品牌)。
- 方法:描述事物的行为(如 “人” 吃饭、“手机” 开机)。
1. Python 类的基本创建与使用
(1)定义空类
用 class
关键字定义类,__init__
是构造方法(创建对象时自动调用),self
代表类的实例本身。
class PersonNode:def __init__(self) -> None:pass
(2)添加属性与方法
在 __init__
中定义属性,用函数定义 “行为方法”。
class PersonNode:def __init__(self, name: str, age: int) -> None:print('PersonNode 的 __init__ 方法被调用了')self.age = age # 定义“年龄”属性self.name = name # 定义“姓名”属性def eat(self, food_name: str):print(f'我叫{self.name},今年{self.age}岁,我现在正在吃{food_name}')
(3)实例化与调用
创建类的 “对象”,并调用方法。
def main():# 实例化 PersonNode(传入姓名、年龄)node = PersonNode('法外狂徒张三', 18)# 调用 eat 方法(传入食物名)node.eat('鱼香肉丝')
2. 类的继承(代码复用)
子类可继承父类的属性和方法,减少重复代码。注意:若子类定义了 __init__
,需用 super().__init__()
显式调用父类构造方法,否则父类属性不会被初始化。
示例:WriterNode
继承 PersonNode
,并新增 “书籍” 属性:
# 从父类模块导入 PersonNode
from demo_python_pkg.person_node import PersonNodeclass WriterNode(PersonNode):def __init__(self, name: str, age: int, book: str) -> None:# 调用父类 PersonNode 的 __init__,初始化 name、agesuper().__init__(name, age)self.book = book # 新增“书籍”属性def main():# 实例化 WriterNode(传入姓名、年龄、书籍)node = WriterNode('法外狂徒张三', 18, '张三自传')# 调用从父类继承的 eat 方法node.eat('鱼香肉丝')
二、让类成为 ROS 2 节点:继承 Node
类
ROS 2 的 Node
类提供了节点的核心能力(如日志输出、话题通信等)。让自定义类继承 Node
,就能拥有这些能力,成为真正的 ROS 2 节点。
示例:改造 PersonNode
继承 Node
,使用 ROS 2 日志系统:
import rclpy
from rclpy.node import Node # 导入 Node 类class PersonNode(Node):def __init__(self, node_name: str, name: str, age: int) -> None:# 调用父类 Node 的 __init__,传入“节点名”super().__init__(node_name)self.age = ageself.name = namedef eat(self, food_name: str):# 使用 ROS 2 日志(替代 print)self.get_logger().info(f'我叫{self.name},今年{self.age}岁,我现在正在吃{food_name}')def main():rclpy.init() # 初始化 rclpy# 实例化 PersonNode(传入节点名、姓名、年龄)node = PersonNode('person_node', '法外狂徒张三', 18)node.eat('鱼香肉丝')rclpy.spin(node) # 让节点保持运行(若有回调逻辑需持续执行)rclpy.shutdown() # 关闭 rclpy
三、多线程与回调函数
- 多线程:让程序并行执行(如同时下载多个文件,无需 “等一个完了再下下一个”)。
- 回调函数:“事件触发后自动执行的函数”(如下载完成后,自动调用函数处理结果)。
Python 多线程 + 回调示例(下载小说)
利用 threading
库创建线程,requests
库发 HTTP 请求,结合回调函数处理下载结果。
import threading # 导入线程模块,用于实现多线程功能
import requests # 导入请求模块,用于发送HTTP请求下载网络内容class Download: # 定义Download类,封装下载相关的方法def download(self, url, callback): # 定义下载方法,参数为目标URL和回调函数print(f'线程:{threading.get_ident()} 开始下载: {url}') # 打印当前线程ID和开始下载的URLresponse = requests.get(url) # 发送GET请求获取URL对应的内容response.encoding = 'utf-8' # 设置响应内容的编码为UTF-8,保证中文等字符正常显示callback(url, response.text) # 下载完成后调用回调函数,传递URL和下载的文本内容def start_download(self, url, callback): # 定义启动下载的方法,用于创建新线程执行下载# 创建新线程,执行 download 方法thread = threading.Thread(target=self.download, args=(url, callback)) # 创建线程,指定目标方法和参数thread.start() # 启动线程,开始执行下载任务# 定义“下载完成”的回调函数:处理结果
def download_finish_callback(url, result): # 定义下载完成后的回调函数,参数为URL和下载结果print(f'{url}下载完成,共: {len(result)}字,内容为: {result[:5]}...') # 打印下载完成信息(URL、内容长度、前5个字符预览)def main(): # 定义主函数,程序执行的入口d = Download() # 创建Download类的实例d# 同时下载 3 个文件,都用 download_finish_callback 处理结果d.start_download('http://localhost:8000/novel1.txt', download_finish_callback) # 启动线程下载第一个小说文件d.start_download('http://localhost:8000/novel2.txt', download_finish_callback) # 启动线程下载第二个小说文件d.start_download('http://localhost:8000/novel3.txt', download_finish_callback) # 启动线程下载第三个小说文件
测试准备(本地模拟小说与 HTTP 服务)
在终端执行以下命令,创建小说文件并启动 HTTP 服务:
# 1. 创建 3 个小说文件
echo "第一章 少年踏上修仙路,因诛仙力量被驱逐。" > novel1.txt
echo "第二章 学习修仙,结交朋友,明白责任。" > novel2.txt
echo "第三章 张家杰回村,抵抗邪恶,成为守护者。" > novel3.txt# 2. 启动 HTTP 服务(端口 8000)
python3 -m http.server
运行代码后,3 个下载任务会并行执行,下载完成后自动调用 download_finish_callback
处理结果。
四、小结与点评
本章核心知识点:
- 面向对象编程:掌握 “类、属性、方法、继承”,是 ROS 2 复杂逻辑封装的基础。
- 继承
Node
类:让自定义类成为真正的 ROS 2 节点,获得日志、通信等核心能力。 - 多线程与回调:实现 “并行执行” 与 “事件驱动”,为后续复杂任务(如多传感器数据处理)打基础。
通过本章,你已入门 ROS 2 开发~后续将学习更深入的 “通信、功能包组织” 等内容。