首页
关于
Search
1
Golang闭包
10 阅读
2
ROS2(二):第一个自定义 Python 节点从编写到运行
9 阅读
3
Golang并发
7 阅读
4
Golang匿名函数
3 阅读
5
Golang Time包入门
2 阅读
默认分类
编程
ROS
登录
Search
tqtqtq
累计撰写
11
篇文章
累计收到
1
条评论
首页
栏目
默认分类
编程
ROS
页面
关于
搜索到
11
篇与
的结果
2026-04-16
Leetcode 239. 滑动窗口最大值
239. 滑动窗口最大值题目描述给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。 你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回 滑动窗口中的最大值。示例 1输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7示例 2输入:nums = [1], k = 1 输出:[1]提示1 <= nums.length <= 10^5-10^4 <= nums[i] <= 10^41 <= k <= nums.length题解笔记:暴力、大顶堆、单调队列一、先理解题目本质这道题的核心是:窗口大小固定为 k窗口不断向右滑动每次都要快速得到当前窗口的最大值最直接的想法是: 每形成一个窗口,就把窗口里的元素都扫一遍,找最大值。这就是暴力解法。但如果数组很长,每次都重新扫,会有很多重复计算,所以就会继续优化到:大顶堆单调队列解法一:暴力解法思路每次窗口形成后,直接遍历这个窗口里的所有元素,找到最大值。例如窗口是:[3, -1, -3]那就把这 3 个数重新比较一遍,得到最大值 3。代码from typing import List class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: ans = [] for left in range(len(nums) - k + 1): max_num = nums[left] for right in range(left, left + k): max_num = max(max_num, nums[right]) ans.append(max_num) return ans复杂度时间复杂度:O(n * k)空间复杂度:O(1)(不算返回结果)优缺点优点最容易理解非常适合刚开始学这题时打基础缺点每个窗口都重新扫描一遍重复计算很多数据大时容易超时解法二:大顶堆先说结论大顶堆可以做,而且比暴力解法更高效。 不过它不是最优解,通常复杂度是:时间复杂度:O(n log n)(严格说更稳妥)空间复杂度:O(n)1. 为什么会想到堆因为这题每次都在问:当前窗口里的最大值是谁?而堆最擅长做的事就是:插入一个数快速拿到最大值(或最小值)所以一个很自然的想法就是:把窗口相关元素放进堆里每次从堆顶拿最大值2. 但为什么不能直接只存值问题在于: 窗口会向右移动,旧元素会离开窗口。例如:[3, -1, -3]下一步变成:[-1, -3, 5]这里的 3 已经离开窗口了。 但如果它还在堆里,并且还在堆顶,那就会影响答案。所以只存值不够,还必须存:值下标3. 为什么 Python 里要存 (-值, 下标)因为 Python 的 heapq 默认是小顶堆,没有原生大顶堆。所以我们常用一个技巧:把值取负原本最大的值,负数后反而最小这样小顶堆就能模拟大顶堆例如数字 5,下标为 4,会存成:(-5, 4)其中:-5:用来模拟大顶堆4:表示它原来的下标4. 什么叫“过期元素”假设当前窗口范围是:[left, right]如果某个元素的下标:idx < left就说明它已经滑出窗口,不能再参与当前窗口最大值比较了。这类元素就叫:过期元素5. 什么叫“懒删除”堆不擅长删除中间任意位置的元素。所以我们不主动去堆里搜索并删除所有过期元素,而是:只要堆顶过期,就把堆顶弹出一直弹到堆顶合法为止这就叫:懒删除代码里通常是:while heap[0][1] < left: heapq.heappop(heap)代码from typing import List import heapq class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: heap = [] ans = [] for i, num in enumerate(nums): # 入堆:(-值, 下标) heapq.heappush(heap, (-num, i)) # 窗口形成后再开始记录答案 if i >= k - 1: left = i - k + 1 # 懒删除:弹掉所有过期堆顶 while heap[0][1] < left: heapq.heappop(heap) # 堆顶就是当前窗口最大值 ans.append(-heap[0][0]) return ans堆解法动画复杂度时间复杂度:O(n log n)空间复杂度:O(n)优缺点优点思路比单调队列更直观很适合从暴力解法继续进阶也能解决这道题缺点不是最优解代码里要理解“过期元素”和“懒删除”堆中可能保留一些已经过期但暂时不在堆顶的元素解法三:单调队列先说结论这是这道题的最优解。时间复杂度:O(n)空间复杂度:O(k)1. 核心思想我们希望队列里维护的是:可能成为当前窗口最大值的元素下标并且让这些下标对应的值,保持单调递减。也就是说:队首最大队尾最小这样每次窗口形成后:队首就是当前窗口最大值的下标直接取 nums[deque[0]] 就行2. 为什么要存下标,不直接存值因为窗口会移动,我们需要判断队首元素是否已经离开窗口。如果只存值,就不知道它对应的是哪个位置。所以单调队列里存的是:下标3. 队列维护规则遍历到 i 时,做三件事。第一步:移除队首过期元素如果队首下标已经不在窗口内:deque[0] < i - k + 1就把它弹掉。第二步:保持单调递减如果当前元素 nums[i] 比队尾对应的值还大,那么队尾那个元素以后就不可能成为最大值了。因为:它比当前元素小它还比当前元素更早过期所以直接弹出队尾,直到队列重新单调递减。第三步:把当前下标加入队尾这样队列中保留的,都是未来仍有机会成为最大值的元素。代码from typing import List from collections import deque class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: q = deque() ans = [] for i, num in enumerate(nums): # 1. 删除窗口左边已经过期的下标 while q and q[0] < i - k + 1: q.popleft() # 2. 维护单调递减队列 while q and nums[q[-1]] < num: q.pop() # 3. 当前下标加入队尾 q.append(i) # 4. 窗口形成后,队首就是最大值 if i >= k - 1: ans.append(nums[q[0]]) return ans复杂度时间复杂度:O(n)空间复杂度:O(k)为什么是 O(n)因为每个元素最多只会:进队一次出队一次不会反复进出,所以总操作次数和 n 成正比。优缺点优点最优解性能最好是这道题的经典标准答案缺点对初学者来说抽象一点第一次接触“单调队列”时不如堆直观
2026年04月16日
2 阅读
0 评论
0 点赞
2026-03-18
ROS2(二):第一个自定义 Python 节点从编写到运行
ROS2(二):第一个自定义 Python 节点从编写到运行1. 本节目标完成一个“最小可运行”的 ROS2 Python 节点,并理解完整开发流程:在包内编写节点代码在 setup.py 注册可执行入口使用 colcon build 构建source install/setup.bash 加载工作区使用 ros2 run 启动节点2. 为什么要这样做在 ROS2 中,Python 文件“能运行”不代表“能被 ROS2 识别运行”。要让命令 ros2 run <包名> <可执行名> 成立,必须满足:代码文件存在于 Python 包目录setup.py 中 console_scripts 注册了入口映射包已构建并安装到 install/当前终端已加载工作区环境这就是 ROS2 工程化流程和普通 Python 脚本最大的区别。3. 代码放在哪里本节包名:py_pubsub_demo 节点文件位置:src/py_pubsub_demo/py_pubsub_demo/hello_node.py说明:第一个 py_pubsub_demo 是 ROS2 包目录第二个 py_pubsub_demo 是 Python 模块目录4. 最小节点代码结构解析本节节点包含 4 个关键部分:4.1 继承 Nodeclass HelloNode(Node):只有继承 Node,这个类才具有 ROS2 节点能力(日志、定时器、参数等)4.2 给节点命名super().__init__('hello_node')节点名是 ROS 图中的身份标识运行后可通过 ros2 node list 看到 /hello_node4.3 定时器 + 回调self.timer = self.create_timer(1.0, self.timer_callback)每 1 秒调用一次回调函数回调中用日志打印“第几次问候”4.4 标准启动入口 main()rclpy.init() node = HelloNode() rclpy.spin(node) node.destroy_node() rclpy.shutdown()init:初始化 ROS2spin:让节点持续处理回调destroy_node + shutdown:优雅退出5. setup.py 入口注册是关键步骤在 setup.py 里注册:'hello_node = py_pubsub_demo.hello_node:main'含义拆解:左边 hello_node:ros2 run 时使用的可执行名右边 py_pubsub_demo.hello_node:main:模块路径 py_pubsub_demo/hello_node.py调用函数 main没有这一步,ros2 run 找不到自定义节点。6. 标准开发流程(建议记成固定模板)每次写完节点,按以下顺序执行:cd ~/ros2_wssource /opt/ros/jazzy/setup.bashcolcon buildsource install/setup.bashros2 run py_pubsub_demo hello_node这是初学阶段最稳定的流程。7. 本节实际验证结果构建成功后,节点按 1Hz 输出日志:你好 ROS2,第 0 次问候你好 ROS2,第 1 次问候你好 ROS2,第 2 次问候说明以下链路全部成功:代码可执行入口可解析ROS2 节点运行正常定时器回调生效8. 常见错误与定位错误 1:No executable found原因:setup.py 没注册或注册名写错。 检查:console_scripts 是否包含正确映射。错误 2:改了代码但运行还是旧行为原因:改完没重新 build / 没 source install。 修复:重新执行“构建 + source install”。错误 3:colcon: command not found原因:构建工具未安装。 修复:安装 python3-colcon-common-extensions。9. 本节知识点小结本节完成后应能明确:ROS2 Python 节点是一个继承 Node 的类节点常见执行模型是“定时器 + 回调”setup.py 的 console_scripts 决定 ros2 run 能否找到入口ROS2 开发是“写代码 → 注册入口 → 构建 → source → 运行”的工程闭环
2026年03月18日
9 阅读
0 评论
0 点赞
2026-03-18
ROS2(一) :从 0 到 1 理解 Node、Topic、Package 与工作区
ROS2(一) :从 0 到 1 理解 Node、Topic、Package 与工作区1. 适用环境系统:Ubuntu 24.04(WSL2)ROS2:Jazzy Jalisco语言:Python 3.12场景:无实体机器人,先在仿真/命令行环境建立基础2. ROS2 到底是什么ROS2 不是一门编程语言,也不是传统意义上的操作系统,而是机器人软件开发框架。可以把它理解为:通信框架 + 工程组织方式 + 机器人生态工具集初学阶段最关键的是先掌握通信模型。3. 三个最核心概念(入门必会)3.1 Node(节点)节点是最小功能单元。一个节点通常只做一件事。例如:发布传感器数据、接收控制指令、打印日志类比:一个团队成员负责一个岗位职责3.2 Topic(话题)话题是节点之间传输数据的“频道”。发布者向话题发送消息订阅者从话题接收消息类比:群聊频道3.3 Message(消息)消息是话题里传输的数据格式。例如:std_msgs/msg/String含义:发送的是字符串类型数据4. 首次验证通信链路:talker 与 /chatter典型验证路径:启动发布者(talker)查看节点列表查看话题列表查看话题详情(类型、发布者数量、订阅者数量)观察重点:/talker 出现在节点列表,说明节点在运行/chatter 出现在话题列表,说明通信通道已建立Publisher count: 1,说明有发布者在发数据5. 为什么经常要执行 source setup.bashsource /opt/ros/jazzy/setup.bash 的作用是给当前终端加载 ROS2 环境变量。若系统已在 ~/.bashrc 自动配置,可能新终端可直接使用 ros2 命令;但在团队项目、容器环境、CI 环境中,手动 source 仍是高频操作。建议形成习惯:进入新 ROS2 会话先确认环境需要时明确执行 source6. 工作区(Workspace)结构标准 ROS2 工作区通常包含四个目录:src/:源码目录(只在这里写代码)build/:构建中间产物install/:安装结果(运行依赖这里)log/:构建日志核心原则:日常开发主要修改 src/,其他目录由工具自动维护。7. 第一个 Python 包的创建与注册在 src/ 下创建包:包名示例:py_pubsub_demo构建类型:ament_python创建后会自动生成:package.xml:包元信息(名称、版本、依赖)setup.py:Python 安装与入口配置resource/:包索引资源<包名>/__init__.py:Python 包目录test/:模板测试文件构建成功后,可通过包列表命令检索到该包,说明工作区链路已打通。8. 常见问题:colcon: command not found原因:系统未安装构建工具链。 解决:安装 python3-colcon-common-extensions,再执行构建。
2026年03月18日
1 阅读
0 评论
0 点赞
2026-03-17
ROS 2 初识学习笔记
ROS 2 初识学习笔记1. 什么是 ROS 2?ROS (Robot Operating System) 并不是真正意义上的操作系统(如 Linux 或 Windows),而是一个运行在操作系统之上的中间件(Middleware)和软件框架。从后端开发的视角来看,ROS 2 本质上是一个面向机器人的分布式微服务架构。它提供了一套标准化的通信机制、硬件抽象层和一系列工具,让你能把复杂的机器人系统(比如感知、决策、控制)拆解解耦成独立运行的小模块。与 ROS 1 相比,ROS 2 最大的改进是引入了 DDS (Data Distribution Service) 协议,实现了真正的去中心化(移除了单点故障的 Master 节点),并提升了实时性和安全性。2. 核心通信机制 (重点)ROS 2 的核心在于各个模块之间如何交换数据。理解以下四个概念是掌握 ROS 2 的关键:2.1 节点 (Node)定义:ROS 2 中最小的执行单元。一个节点负责执行一个具体的任务(例如:读取激光雷达数据、控制电机转动)。特性:节点之间是互相独立的进程,可以运行在同一台机器上,也可以跨网络运行在不同的机器上。类比:一个 Node 就相当于一个独立的 Go 微服务实例或一个 Java Spring Boot 应用。2.2 话题 (Topic)通信模式:发布/订阅 (Publish / Subscribe)。特点:单向、异步、多对多。数据流是连续的,发布者只管发,不关心谁在听;订阅者只管收,不关心谁在发。适用场景:高频、连续的数据流(如摄像头画面、传感器实时数据)。类比:非常类似后端的 Kafka 或 RabbitMQ 的 Pub/Sub 模式。2.3 服务 (Service)通信模式:请求/响应 (Request / Response)。特点:双向、同步(或基于回调的异步)、一对一。客户端发送请求,服务端处理后返回结果。适用场景:快速执行的、离散的、需要确认结果的操作(如“查询当前电池电量”、“打开特定传感器”)。类比:标准的 HTTP RESTful API 或 gRPC 调用。2.4 动作 (Action)通信模式:目标 (Goal) + 反馈 (Feedback) + 结果 (Result)。特点:双向、异步、可抢占/可取消。它是基于 Topic 和 Service 组合出来的高级封装。适用场景:耗时较长、且执行期间需要实时了解进度的复杂任务(如“导航到指定坐标点”、“机械臂抓取物体”)。类比:提交一个耗时的异步任务(Job),客户端可以通过长连接或轮询不断获取当前执行进度,并在最终完成时拿到结果。同时,客户端随时有权取消这个 Job。3. 工程与构建系统在实际开发中,ROS 2 有自己的一套代码组织和编译规范。3.1 工作空间 (Workspace)定义:一个包含 ROS 2 代码的特定目录结构。通常包含 src(源码目录)、build(中间编译文件)、install(最终的可执行文件和库)、log(日志)四个子目录。3.2 功能包 (Package)定义:ROS 2 代码组织的基本单元。一个 Package 可以包含多个相关的 Node。语言支持:原生支持 C++ (rclcpp) 和 Python (rclpy)。你可以用 Python 写高层逻辑 Node,用 C++ 写底层性能敏感的 Node,它们可以无缝通信。类比:类似于 Go 的 module 或者是 Java 的 Maven/Gradle 模块。3.3 编译工具 (Colcon)定义:ROS 2 的官方构建工具。用于遍历工作空间下的所有 Package 并解决依赖进行编译。常用命令:colcon build类比:相当于后端的 go build 或 mvn clean install。4. 运行与调试概念Setup/Source 环境:在运行任何 ROS 2 程序前,必须在终端执行 source /opt/ros/<版本>/setup.bash 以及自己工作空间的 setup.bash。这类似于配置后端的环境变量或 $GOPATH,用来告诉系统去哪里找包和依赖。CLI 工具:ROS 2 提供了强大的命令行工具(如 ros2 node list, ros2 topic echo),方便开发者不写代码就能监听网络中的数据走向,极大地降低了调试分布式系统的难度。
2026年03月17日
1 阅读
0 评论
0 点赞
2026-03-15
Python核心数据结构
Python核心数据结构1. List(列表):灵活的多面手列表是 Python 中最常用的数据结构,就像一个可以随时增删改查的动态数组。特性:有序(按添加顺序排列)、可变(随时可以修改元素)、允许重复。适用场景:需要按顺序存储同类或不同类数据,且后续需要频繁修改数据的场景(如:待办事项列表、爬虫抓取的数据集)。创建与核心操作# 创建列表 my_list = [1, 2, "hello", 3.14, 1] # 增加元素 my_list.append([3, 4]) # 在末尾追加,将列表 [3, 4] 作为单个元素添加,结果为:[1, 2, "hello", 3.14, 1, [3, 4]] my_list.insert(1, "插入") # 在指定索引位置插入 my_list.extend([4, 5]) # 将另一个列表的元素拆散合并进来 # 删除元素 my_list.remove("hello") # 删除第一个匹配的元素 popped_item = my_list.pop() # 移除并返回最后一个元素 # 修改与查询 my_list[0] = 100 # 修改指定索引的值 print(my_list[1]) # 通过索引访问2. Tuple(元组):带锁的列表元组和列表非常相似,但它一旦创建,里面的元素就不能被修改(Immutable)。特性:有序、不可变、允许重复。适用场景:存储不需要更改的数据(如:地理坐标经纬度、数据库查询返回的单条记录)。因为不可变,元组的内存占用比列表小,遍历速度稍快,且可以作为字典的 Key(键)。创建与核心操作# 创建元组 my_tuple = (1, 2, "hello", 1) single_tuple = (1,) # 注意:只有一个元素的元组必须加逗号 # 访问元素 (与列表相同) print(my_tuple[0]) # 输出: 1 # 核心方法 (因为不可变,所以只有查询方法) print(my_tuple.count(1)) # 统计元素 1 出现的次数 (输出: 2) print(my_tuple.index("hello")) # 查找元素的索引位置 """ 如果尝试修改,会抛出TypeError: 't = (1, 2, 3) result = t[0] = 100 # 尝试修改 tuple' object does not support item assignment """ 注意:虽然元组本身不可变,但如果元组里面包含可变对象(如列表),该列表内部的元素是可以修改的。3. Dict(字典):高效的键值对字典就像一本真正的字典,通过唯一的词条(Key)去快速查找对应的释义(Value)。特性:键值对映射、可变、Key 必须唯一且不可变(如字符串、数字、元组)、Value 可以是任意类型。(注:Python 3.7+ 版本后,字典默认保留插入顺序)。适用场景:需要通过特定标识符快速检索数据的场景(如:用户个人信息、配置参数存储、JSON数据处理)。创建与核心操作# 创建字典 my_dict = {"name": "Alice", "age": 25, "city": "New York"} # 增加/修改元素 my_dict["job"] = "Engineer" # 增加新键值对 my_dict["age"] = 26 # 修改已有键的值 # 删除元素 del my_dict["city"] # 删除键值对 age = my_dict.pop("age") # 移除并返回对应的值 # 查询元素 print(my_dict["name"]) # 可能会触发 KeyError 如果键不存在 print(my_dict.get("phone", "Not Found")) # 安全获取,不存在则返回默认值 # 遍历字典 for key, value in my_dict.items(): print(f"{key}: {value}")4. Set(集合):去重与数学运算利器集合就像是一个没有 Value 的字典,它里面只存放独一无二的元素。特性:无序(不支持索引)、可变、元素必须唯一且不可变。适用场景:数据快速去重、测试成员是否属于某个群体、进行数学集合运算(交集、并集、差集)。创建与核心操作# 创建集合 my_set = {1, 2, 3, 3, 4} # 重复的 3 会被自动过滤,结果为 {1, 2, 3, 4} empty_set = set() # 注意:空集合必须用 set(),不能用 {} (那是空字典) # 增删元素 my_set.add(5) # 增加元素 my_set.remove(2) # 移除元素 (如果不存在会报错) my_set.discard(10) # 移除元素 (不存在不会报错) # 集合数学运算 set_a = {1, 2, 3} set_b = {3, 4, 5} print(set_a | set_b) # 并集: {1, 2, 3, 4, 5} (也可用 set_a.union(set_b)) print(set_a & set_b) # 交集: {3} (也可用 set_a.intersection(set_b)) print(set_a - set_b) # 差集: {1, 2} (也可用 set_a.difference(set_b))综合对比总结数据结构符号有序性可变性是否允许重复核心优势 / 用途List (列表)[]有序可变允许灵活存储和操作一连串数据Tuple (元组)()有序不可变允许保护数据不被修改,比列表更省内存Dict (字典){}保持插入顺序可变Key 唯一,Value 允许极其快速的键值查找 (O(1) 复杂度)Set (集合){}无序可变不允许快速去重,强大的集合数学运算
2026年03月15日
1 阅读
0 评论
0 点赞
1
2
3