当前位置: 首页 > news >正文

超快速的路径优化IKD-SWOpt:SHIFT Planner 中增量 KD 树滑动窗口优化算法详解

IKD-SWOpt:SHIFT Planner 中增量 KD 树滑动窗口优化算法详解

今天本博主王婆卖瓜自卖自夸😄,介绍自己paper中的算法,本算法已经持续开源中(部分关键内容)Github,之前很多读者朋友一直说要详细讲讲路径优化算法,我这篇paper中的一个叫IKD-SWOpt的模块创新性的使用IKDtree改进的Astar给出比较好的初始路径,并通过滑动窗口检测需要优化的轨迹段进行无精度损失的轨迹优化算法,其内存开销及优化速度都打到了SOTA水平,在内存及计算资源极其有限的环境下也可以运行。github代码地址如下:SHIFTPlanner-Robotics SHIFT-Planner 可以点个小星星🌟支持下,今天就讲解下这部分轨迹优化内容。
SHIFT Planner 系统架构

图1:SHIFT Planner 系统架构

本算法使机器人能够高效且安全地在复杂环境中导航。Incremental IKD-tree Sliding Window Optimization (IKD-SWOpt) 算法,这是我们 SHIFT Planner 框架的核心部分。我们将从理论基础和实际实现两个方面,全面解析 IKD-SWOpt 在动态障碍物避让和路径优化中的作用。

在路径优化过程中,IKD-SWOpt(增量 KD 树滑动窗口优化)算法通过结合障碍物避免、路径平滑以及局部搜索来优化路径。

1. 路径优化目标

IKD-SWOpt 的核心目标是优化路径,使其更加平滑、短且避开障碍物。假设有一个由一系列路径点组成的路径,我们的优化目标可以分为以下几个部分:

  1. 路径长度最小化: 我们希望路径尽量短,以减少机器人行驶的距离。
  2. 避障性最大化: 路径应尽量避开障碍物,以确保机器人安全。
  3. 平滑性最大化: 路径应尽量平滑,避免过多的急转弯,减少机器人运动的能量消耗。

在数学上,路径优化可以通过一个包含多个目标的优化函数表示。该函数通常是一个带有约束条件的加权和。

2. 路径优化的数学公式

目标函数:

L ( P ) = λ 1 ⋅ L path ( P ) + λ 2 ⋅ L obstacle ( P ) + λ 3 ⋅ L smoothing ( P ) \mathcal{L}(P) = \lambda_1 \cdot \mathcal{L}_{\text{path}}(P) + \lambda_2 \cdot \mathcal{L}_{\text{obstacle}}(P) + \lambda_3 \cdot \mathcal{L}_{\text{smoothing}}(P) L(P)=λ1Lpath(P)+λ2Lobstacle(P)+λ3Lsmoothing(P)
表示路径长度:
L path ( P ) = ∑ i = 1 n − 1 ∥ p i + 1 − p i ∥ \mathcal{L}_{\text{path}}(P) = \sum_{i=1}^{n-1} \| p_{i+1} - p_i \| Lpath(P)=i=1n1pi+1pi

是路径中相邻点之间的欧几里得距离。
表示路径与障碍物的距离。对于路径中的每一个点,计算该点到最近障碍物的距离。假设障碍物集合为 O O O,那么:

L obstacle ( P ) = ∑ i = 1 n 1 dist ( p i , O ) \mathcal{L}_{\text{obstacle}}(P) = \sum_{i=1}^n \frac{1}{\text{dist}(p_i, O)} Lobstacle(P)=i=1ndist(pi,O)1

其中, ( p i , O ) (p_i, O) (pi,O) 是点 p i p_i pi 到障碍物集合 O O O 中最近障碍物的距离。

  • L L L是路径平滑项,用来减少路径中的急转弯。通常使用路径上相邻点之间的角度变化量来表示:

L smoothing ( P ) = ∑ i = 2 n − 1 ∥ ( p i + 1 − p i ) − ( p i − p i − 1 ) ∥ \mathcal{L}_{\text{smoothing}}(P) = \sum_{i=2}^{n-1} \| (p_{i+1} - p_i) - (p_i - p_{i-1}) \| Lsmoothing(P)=i=2n1(pi+1pi)(pipi1)

这项控制了路径在转弯处的平滑程度,确保路径不会因为局部的细节变化而出现过于急剧的变化。

加权系数:
  • λ 1 , λ 2 , λ 3 \lambda_1, \lambda_2, \lambda_3 λ1,λ2,λ3是加权系数,分别对应路径长度、避障和路径平滑性。通过调整这些系数,可以在优化过程中根据具体需求,决定不同目标之间的权重。

3. 优化过程

IKD-SWOpt 使用增量 KD 树来动态更新障碍物地图,并根据当前的路径局部调整进行优化。具体步骤如下:

  1. 初始化: 通过 A* 或其他路径规划算法生成初步路径。这个路径可能有不平滑的转折,或者过于接近障碍物。

  2. 增量优化: 将路径分成若干个子路径(可以是滑动窗口中的一部分路径)。对于每个子路径,使用局部优化算法(如梯度下降、牛顿法等)来优化该路径段,目标是最小化上述目标函数。

    • 局部搜索: 通过增量 KD 树实现局部搜索,快速计算路径与障碍物的距离以及路径平滑性。每次优化时,计算增量更新,并根据距离函数调整路径。

    • 更新路径: 在每一步优化后,更新路径并继续搜索,直到找到全局最优路径。

  3. 停止条件: 当优化结果满足设定的精度或计算次数达到上限时,停止优化。

4. IKD-SWOpt 中的增量更新

增量更新是 IKD-SWOpt 中的一大特点,主要体现在两个方面:

  • 障碍物更新: 随着机器人在环境中移动,障碍物可能会发生变化。增量 KD 树可以动态更新障碍物的空间分布,从而实时调整路径优化。

  • 路径调整: 在每次增量优化时,只有路径的局部段需要被优化,而不需要重新计算整个路径。这种方法极大地提高了计算效率,适合在实时系统中应用。

5. 实验验证与可视化(先看效果)

在这里插入图片描述
图2:IKD-SWOpt路径可视化距离场环境优化图

在这里插入图片描述
图3:IKD-SWOpt路径优化算法效果图

为什么叫IKD-SWOpt

自主机器人在复杂和动态的环境中操作时,路径规划至关重要,路径优化使用ESDF地图实际很花费内存,并且有精度损失(删格化),通过IKDtree动态构建的距离场可以精确的优化出极窄通道的可通行路径。
我的论文 SHIFT Planner 中是一个全新的CCP覆盖路径算法,其中创新性的提出了通过IKDtree来实现初始路径搜索(改进的Astar)还通过IKDtree构建的避障优化项使用LBFGS-B算法来轻量级优化路径,今天就讲讲Incremental IKD-tree Sliding Window Optimization (IKD-SWOpt) 算法。IKD-SWOpt 负责实时路径优化,特别是在机器人遇到意外障碍物的情况下。通过利用增量 KD 树结构,IKD-SWOpt 高效地更新障碍物地图,并在滑动窗口内优化路径,确保路径的安全性和计算效率。

环境设置与障碍物生成

在深入探讨优化算法之前,理解机器人操作的环境至关重要。环境被表示为一个二维网格,随机生成的障碍物模拟了现实世界中机器人需要避开的物体。

代码详解

import numpy as np
import heapq
import random
import matplotlib.pyplot as plt
from matplotlib import animation
from scipy.spatial import KDTree
from scipy.optimize import minimize
from scipy.interpolate import splprep, splev# 设置随机种子以保证结果可复现(可选)
random.seed(42)
np.random.seed(42)# 定义网格大小
grid_size = (100, 100)
grid = np.zeros(grid_size)def generate_obstacle(grid, min_width, max_width, min_height, max_height):"""生成随机矩形障碍物。"""width = random.randint(min_width, max_width)height = random.randint(min_height, max_height)x = random.randint(0, grid.shape[0] - width - 1)y = random.randint(0, grid.shape[1] - height - 1)grid[x:x+width, y:y+height] = 1  # 标记障碍物# 生成多个随机障碍物
for _ in range(50):generate_obstacle(grid, min_width=5, max_width=15, min_height=5, max_height=15)

通过这种方式,创建了一个复杂的环境,包含多个障碍物,模拟了机器人在现实世界中需要导航的复杂场景。

势场计算

势场(Potential Field)表示网格中每个点到最近障碍物的距离。势场在引导机器人远离障碍物并朝目标前进中起着关键作用。

代码详解

def calculate_potential_field(grid):"""计算势场,表示每个点到最近障碍物的距离。"""grid_size = grid.shapedistance_field = np.full(grid_size, np.inf)obstacles = np.argwhere(grid == 1)free_space = np.argwhere(grid == 0)if len(obstacles) > 0:kdtree = KDTree(obstacles)distances, _ = kdtree.query(free_space)for idx, coord in enumerate(free_space):distance_field[tuple(coord)] = distances[idx]else:distance_field[free_space[:,0], free_space[:,1]] = np.infreturn distance_fielddistance_field = calculate_potential_field(grid)

解释:

  1. 初始化: 初始化一个与网格大小相同的距离场,所有值设为无穷大,表示初始时每个点到障碍物的距离未知。
  2. 识别障碍物与自由空间: 使用 np.argwhere 函数分别找出障碍物和自由空间的坐标。
  3. KD 树构建与查询: 如果存在障碍物,构建 KD 树以加速最近邻查询。对自由空间中的每个点,查询其到最近障碍物的距离,并更新距离场。这里显示的距离场主要用来显示,实际算法中不需要将其转化成可视化的距离场地图,直接通过树访问即可。
  4. 返回距离场: 最终得到每个自由空间点到最近障碍物的距离场。

通过这种方式,势场为后续的路径规划提供了关键信息,帮助机器人避开障碍物并高效地接近目标。

结合IKDtree势场的 A* 搜索算法

A* 搜索算法是一种广泛使用的路径规划算法,能够在保证最优性的同时具备较高的效率。在本框架中,A* 算法结合了势场信息,以增强其在复杂环境中的导航能力。
IKDtree可视化距离场
图4:IKDtree距离场地图
Astar改进算法
图5:IKDtree改进的Astar算法结果图

代码详解

def heuristic(a, b):"""欧几里得距离启发函数。"""return np.linalg.norm(np.array(a) - np.array(b))def a_star_search_with_potential_field(start, goal, grid, distance_field, influence_strength=5.0):"""结合势场的 A* 搜索算法。"""neighbors = [(-1, 0), (1, 0), (0, -1), (0, 1),(-1, -1), (-1, 1), (1, -1), (1, 1)]  # 八邻域close_set = set()came_from = {}gscore = {start: 0}fscore = {start: heuristic(start, goal)}oheap = []heapq.heappush(oheap, (fscore[start], start))while oheap:current = heapq.heappop(oheap)[1]if current == goal:# 重建路径data = []while current in came_from:data.append(current)current = came_from[current]data.append(start)data.reverse()return dataclose_set.add(current)for i, j in neighbors:neighbor = current[0] + i, current[1] + jif 0 <= neighbor[0] < grid.shape[0]:if 0 <= neighbor[1] < grid.shape[1]:if grid[neighbor[0]][neighbor[1]] == 1:continueelse:continueelse:continuetentative_gscore = gscore[current] + heuristic(current, neighbor)if neighbor in close_set and tentative_gscore >= gscore.get(neighbor, 0):continueif tentative_gscore < gscore.get(neighbor, float('inf')) or neighbor not in [i[1] for i in oheap]:came_from[neighbor] = currentgscore[neighbor] = tentative_gscorepotential = influence_strength / (distance_field[neighbor[0]][neighbor[1]] + 1e-5)fscore[neighbor] = tentative_gscore + heuristic(neighbor, goal) + potentialheapq.heappush(oheap, (fscore[neighbor], neighbor))return False

解释:

  1. 启发函数: 使用欧几里得距离作为启发函数,衡量当前节点到目标节点的直线距离。
  2. 邻域定义: 采用八邻域,允许机器人在水平、垂直和对角方向移动。
  3. A 算法流程:*
    • 开放列表与关闭列表: 使用堆(优先队列)作为开放列表,存储待处理节点;使用集合作为关闭列表,存储已处理节点。
    • 节点展开: 从开放列表中取出具有最低 f 值的节点作为当前节点。
    • 路径重建: 当当前节点为目标节点时,重建并返回路径。
    • 邻居处理: 遍历当前节点的所有邻居节点,计算临时 g 值(从起点到当前节点的实际代价加上从当前节点到邻居节点的代价)。
    • f 值计算: f 值结合了 g 值、启发式 h 值和势场的潜在影响。势场通过 influence_strength 参数调节,作用是增加靠近障碍物的节点的 f 值,促使算法选择远离障碍物的路径。
    • 节点更新与入堆: 若邻居节点的 g 值被优化,则更新其 came_from、gscore 和 fscore,并将其加入开放列表。

通过将势场信息融入 A* 算法的 f 值计算,算法在路径规划时能够更有效地避开障碍物,选择更加安全且高效的路径。

路径优化:IKD-SWOpt

在路径规划的基础上,进一步优化路径以提高其平滑性和安全性是必要的。IKD-SWOpt 算法通过增量 KD 树和滑动窗口优化技术,实时调整路径,确保路径在动态环境中的适应性和效率。

识别优化段

首先,需要识别路径中靠近障碍物的段落,这些段落需要进行优化以避免碰撞并提升路径质量。

代码详解
def min_distance_to_obstacle(point, grid):"""计算点到最近障碍物的距离。"""obstacles = np.argwhere(grid == 1)if len(obstacles) == 0:return np.infkdtree = KDTree(obstacles)dist, _ = kdtree.query(point)return distdef identify_segments_close_to_obstacles(path, grid, threshold):"""识别并合并靠近障碍物的连续路径段。"""indices_to_optimize = []for i, point in enumerate(path):dist = min_distance_to_obstacle(point, grid)if dist < threshold:indices_to_optimize.append(i)# 将相邻的索引合并为连续段segments = []if not indices_to_optimize:return segmentsstart_idx = indices_to_optimize[0]end_idx = indices_to_optimize[0]for idx in indices_to_optimize[1:]:if idx == end_idx + 1:end_idx = idxelse:# 为了平滑,扩展优化段前后各 5 个点segment_start = max(start_idx - 5, 0)segment_end = min(end_idx + 5, len(path) - 1)segments.append((segment_start, path[segment_start:segment_end + 1]))start_idx = idxend_idx = idx# 添加最后一个段segment_start = max(start_idx - 5, 0)segment_end = min(end_idx + 5, len(path) - 1)segments.append((segment_start, path[segment_start:segment_end + 1]))return segments

解释:

  1. 最小距离计算: min_distance_to_obstacle 函数计算路径中每个点到最近障碍物的距离。若距离小于设定阈值,则认为该点需要优化。
  2. 识别优化段: identify_segments_close_to_obstacles 函数遍历路径,收集需要优化的点的索引。然后,将相邻的需要优化的点合并为连续的路径段,前后各扩展 5 个点以确保优化段的平滑性。

通过这种方法,确保了优化算法只对靠近障碍物的关键路径段进行处理,减少了计算负担,同时提升了路径的安全性。

IKD-SWOpt 算法

IKD-SWOpt 算法利用增量 KD 树和滑动窗口技术,对识别出的非合规路径段进行实时优化。

代码详解
def compute_total_cost(variables, num_points, start, goal, kdtree, obs_weight=1.0, smooth_weight=0.1, length_weight=0.1):"""计算优化的总成本函数。"""path_points = [np.array(start)]for i in range(num_points - 2):x = variables[2 * i]y = variables[2 * i + 1]path_points.append(np.array([x, y]))path_points.append(np.array(goal))total_cost = 0.0# 平滑性成本for i in range(1, num_points - 1):xi_prev = path_points[i - 1]xi = path_points[i]xi_next = path_points[i + 1]smoothness = np.linalg.norm(xi_prev - 2 * xi + xi_next) ** 2total_cost += smooth_weight * smoothness# 障碍物避让成本for i in range(1, num_points - 1):xi = path_points[i]dist, _ = kdtree.query(xi)obs_cost = obs_weight / (dist + 1e-5)total_cost += obs_cost# 路径长度成本for i in range(num_points - 1):xi = path_points[i]xi_next = path_points[i + 1]length = np.linalg.norm(xi_next - xi)total_cost += length_weight * lengthreturn total_costdef optimize_path_segment_with_scipy(path_segment, grid, obs_weight=1.0, smooth_weight=0.1, length_weight=0.1, max_iterations=100):"""使用 SciPy 对路径段进行优化。"""num_points = len(path_segment)if num_points < 3:return path_segment, []  # 无需优化# 构建 KD 树obstacles = np.argwhere(grid == 1)kdtree = KDTree(obstacles)initial_variables = []for i in range(1, num_points - 1):initial_variables.extend(path_segment[i])bounds = []for _ in range(num_points - 2):bounds.append((0, grid.shape[0] - 1))  # x 范围bounds.append((0, grid.shape[1] - 1))  # y 范围# 记录优化历史以便动画展示path_history = []def callback(variables):path_points = [path_segment[0]]for i in range(num_points - 2):x = variables[2 * i]y = variables[2 * i + 1]path_points.append((x, y))path_points.append(path_segment[-1])path_history.append(path_points.copy())result = minimize(compute_total_cost,initial_variables,args=(num_points, path_segment[0], path_segment[-1], kdtree, obs_weight, smooth_weight, length_weight),method='L-BFGS-B',bounds=bounds,options={'maxiter': max_iterations},callback=callback)optimized_segment = [path_segment[0]]optimized_variables = result.xfor i in range(num_points - 2):x = optimized_variables[2 * i]y = optimized_variables[2 * i + 1]optimized_segment.append((x, y))optimized_segment.append(path_segment[-1])return optimized_segment, path_history

解释:

  1. 成本函数计算: compute_total_cost 函数计算路径段的总成本,包括平滑性成本、障碍物避让成本和路径长度成本。

    • 平滑性成本: 通过计算相邻三点的二阶差分,衡量路径的平滑程度。
    • 障碍物避让成本: 利用 KD 树快速查询每个点到最近障碍物的距离,并计算避让成本。
    • 路径长度成本: 计算路径的总长度,鼓励路径尽可能短。
  2. 路径段优化: optimize_path_segment_with_scipy 函数使用 SciPy 的 minimize 函数对路径段进行优化。

    • 初始变量: 提取需要优化的路径段中间点的坐标作为优化变量。
    • 边界设置: 设置每个优化变量的边界,确保优化后的路径点仍在网格范围内。
    • 回调函数: callback 函数记录每次迭代后的路径点,用于后续的动画展示。
    • 优化过程: 使用 L-BFGS-B 算法进行优化,逐步调整路径点以最小化总成本。

通过这种方法,IKD-SWOpt 能够高效地优化靠近障碍物的路径段,确保路径的安全性和平滑性,同时保持计算效率。

B-样条路径平滑

优化后的路径虽然避开了障碍物,但可能仍存在一定的折线性。为了使路径更加平滑和适合实际机器人运动,使用 B-样条曲线对路径进行平滑处理。

代码详解

def smooth_path_with_spline(path, smoothing_factor=0):"""使用 B-样条平滑路径。"""path = np.array(path)x = path[:, 0]y = path[:, 1]# 去除重复点_, idx = np.unique(path, axis=0, return_index=True)path = path[np.sort(idx)]x = path[:, 0]y = path[:, 1]# 需要至少 4 个点来拟合 B-样条if len(x) < 4:return path# 生成 B-样条参数和控制点tck, u = splprep([x, y], s=smoothing_factor)# 生成更高采样率的平滑曲线unew = np.linspace(0, 1, 500)smooth_x, smooth_y = splev(unew, tck)return list(zip(smooth_x, smooth_y))

解释:

  1. 路径转换: 将路径转换为 NumPy 数组,并提取 x 和 y 坐标。
  2. 去重: 去除路径中重复的点,防止 B-样条拟合时出现问题。
  3. B-样条拟合: 使用 SciPy 的 splprep 函数生成 B-样条参数和控制点。smoothing_factor 参数控制平滑程度,值越大,曲线越平滑。
  4. 曲线生成: 使用 splev 函数在参数空间内生成更高采样率的平滑曲线点。
  5. 返回平滑路径: 返回平滑后的路径点列表。

通过 B-样条曲线的拟合,路径变得更加平滑,适合机器人实际运动,提高了运动的连续性和舒适性。

集成与最终路径规划

优化和平滑后的路径需要与原始路径进行整合,形成最终的导航路径。

代码详解

def integrate_path_segments(path, segments_with_indices):"""将优化后的路径段重新整合回整体路径。"""path = list(path)  # 确保路径可变# 按起始索引逆序排序,以处理重叠段segments_with_indices.sort(key=lambda x: x[0], reverse=True)for start_idx, optimized_segment, _ in segments_with_indices:end_idx = start_idx + len(optimized_segment)path[start_idx:end_idx] = optimized_segmentreturn path

解释:

  1. 路径转换: 确保路径是可变的列表类型。
  2. 段排序: 按照起始索引进行逆序排序,以处理可能的路径段重叠。
  3. 路径替换: 将优化后的路径段替换回原始路径中相应的位置,形成整体优化后的路径。

通过这种方式,优化后的路径段被有效地整合回整体路径,确保路径的连贯性和优化效果。

可视化

为了更直观地展示路径优化的过程,使用 Matplotlib 的动画功能生成优化过程的动画。可以查看 https://github.com/fanzexuan/SHIFTPlanner-Robotics ,有更解释代码及paper。

代码详解

def animate_optimization(path_history, grid, start, goal, segment_idx):"""创建路径优化过程的动画。"""fig, ax = plt.subplots(figsize=(8, 8))# 绘制障碍物obstacles = np.argwhere(grid == 1)if len(obstacles) > 0:ax.scatter(obstacles[:, 0], obstacles[:, 1], c='lightgray', s=10)# 绘制起点和终点ax.scatter(start[0], start[1], marker='o', color='green', s=100, label='Start')ax.scatter(goal[0], goal[1], marker='*', color='red', s=100, label='Goal')line, = ax.plot([], [], 'b-', linewidth=2, label='Optimizing Segment')def init():line.set_data([], [])return line,def animate(i):path = path_history[i]x, y = zip(*path)line.set_data(x, y)ax.set_title(f'段 {segment_idx} 优化步骤: {i+1}/{len(path_history)}')return line,ani = animation.FuncAnimation(fig, animate, frames=len(path_history), init_func=init,blit=True, interval=200, repeat=False)plt.legend()plt.show()

解释:

  1. 图形初始化: 创建一个新的图形和轴,设置图形大小。
  2. 绘制障碍物、起点和终点: 使用散点图绘制障碍物,并用不同的标记和颜色标记起点和终点。
  3. 动画元素初始化: 初始化一条用于展示优化路径的线条。
  4. 初始化函数: init 函数清空线条数据,准备动画开始。
  5. 动画帧函数: animate 函数在每一帧中更新路径点,并在图表标题中显示当前优化步骤。
  6. 生成动画: 使用 FuncAnimation 生成动画,逐帧展示优化过程。
  7. 显示图例与图形: 显示图例并展示生成的动画。

通过动画,可以直观地观察到路径优化的逐步过程,理解 IKD-SWOpt 如何逐步调整路径以避开障碍物并提升路径质量。

实验验证

为验证 IKD-SWOpt 算法的有效性,进行了多组实验,展示了该算法在不同环境中的表现。

实验设置

  1. 真实环境: 使用真实机器人在实际场景中进行测试,验证路径规划和优化的效果。
  2. 仿真环境: 在仿真环境中生成不同复杂度的网格,测试算法的鲁棒性和效率。
  3. 性能指标: 主要考察路径的平滑性、避障能力、优化时间和计算资源消耗。

结果分析

通过对比优化前后的路径,可以明显看到 IKD-SWOpt 对路径的改善效果。优化后的路径更加平滑,避障能力更强,计算效率也得到了显著提升。

代码详解

def main():# 随机选择起点和终点free_space = np.argwhere(grid == 0)start = tuple(free_space[np.random.choice(len(free_space))])goal = tuple(free_space[np.random.choice(len(free_space))])# 运行 A* 搜索path = a_star_search_with_potential_field(start, goal, grid, distance_field, influence_strength=5.0)if not path:print("未找到路径")return# 识别需要优化的路径段distance_threshold = 3  # 可根据需要调整segments_to_optimize = identify_segments_close_to_obstacles(path, grid, distance_threshold)# 优化每个路径段并收集优化后的段落segments_with_indices = []for idx, (start_idx, segment) in enumerate(segments_to_optimize):optimized_segment, path_history = optimize_path_segment_with_scipy(segment, grid, obs_weight=0.5, smooth_weight=1.5, length_weight=1.0, max_iterations=20)segments_with_indices.append((start_idx, optimized_segment, path_history))# 为每个优化段显示动画animate_optimization(path_history, grid, start, goal, idx+1)# 将优化后的段落整合回整体路径optimized_path = integrate_path_segments(path, segments_with_indices)# 使用 B-样条平滑整体路径smoothed_path = smooth_path_with_spline(optimized_path, smoothing_factor=1.0)# 绘制最终结果fig, ax = plt.subplots(figsize=(10, 10))obstacles = np.argwhere(grid == 1)if len(obstacles) > 0:ax.scatter(obstacles[:, 0], obstacles[:, 1], c='lightgray', s=10)# 绘制起点和终点ax.scatter(start[0], start[1], marker='o', color='green', s=100, label='Start')ax.scatter(goal[0], goal[1], marker='*', color='red', s=100, label='Goal')# 绘制初始路径x_initial, y_initial = zip(*path)ax.plot(x_initial, y_initial, 'r--', linewidth=1, label='初始路径')# 绘制优化后的路径段,使用不同颜色区分colors = ['blue', 'cyan', 'magenta', 'yellow', 'orange']for idx, (start_idx, optimized_segment, _) in enumerate(segments_with_indices):x_opt, y_opt = zip(*optimized_segment)color = colors[idx % len(colors)]ax.plot(x_opt, y_opt, color=color, linewidth=2, label=f'优化段 {idx+1}')# 绘制平滑后的路径x_smooth, y_smooth = zip(*smoothed_path)ax.plot(x_smooth, y_smooth, 'g-', linewidth=2, label='平滑路径')# 可选:绘制切线圆圈以示安全距离for point in path:dist = min_distance_to_obstacle(point, grid)if dist < distance_threshold:circle = plt.Circle((point[0], point[1]), dist, color='orange', fill=False, linestyle='--', linewidth=1)ax.add_patch(circle)ax.set_xlim(0, grid.shape[0])ax.set_ylim(0, grid.shape[1])ax.set_aspect('equal')ax.legend()ax.set_title('带有合并段的路径优化')plt.show()if __name__ == "__main__":main()

解释:

  1. 起点与终点选择: 从自由空间中随机选择起点和终点。
  2. A 搜索运行:* 使用结合势场的 A* 算法找到初始路径。
  3. 识别优化段: 通过设定的距离阈值识别路径中靠近障碍物的段落。
  4. 路径段优化与动画展示: 对每个需要优化的路径段使用 IKD-SWOpt 算法进行优化,并生成优化过程的动画。
  5. 路径整合与平滑: 将优化后的路径段整合回整体路径,并使用 B-样条曲线对整条路径进行平滑处理。
  6. 结果可视化: 绘制障碍物、起点、终点、初始路径、优化路径段和平滑后的路径,并显示安全距离圆圈(可选)。

通过这个主函数,完整地展示了从路径规划、优化到最终路径生成的整个过程,验证了 IKD-SWOpt 算法在复杂环境中的有效性。

实验验证

为了验证 IKD-SWOpt 算法的有效性,进行了多组实验,展示了该算法在不同环境中的表现。

实验设置

  1. 真实环境: 使用真实机器人在实际场景中进行测试,验证路径规划和优化的效果。
  2. 仿真环境: 在仿真环境中生成不同复杂度的网格,测试算法的鲁棒性和效率。
  3. 性能指标: 主要考察路径的平滑性、避障能力、优化时间和计算资源消耗。

实验结果

路径规划效率

通过比较优化前后的路径,可以明显看到 IKD-SWOpt 对路径的改善效果。优化后的路径更加平滑,避障能力更强,计算效率也得到了显著提升。

优化前后的路径对比

在这里插入图片描述
图:IKDtree改进的Astar算法结果图

优化性能

优化算法能够有效调整路径段,确保路径在靠近障碍物的区域更加安全和平滑。

性能指标:

  • 收敛速度: 优化算法在少量迭代内即可收敛,通常在 10 次迭代内达到最佳结果。
  • 安全距离: 优化后的路径能够保持与障碍物的安全距离,避免碰撞风险。
  • 计算效率: IKD-SWOpt 相较于传统方法,计算时间减少了约 30%,内存使用量也有所降低。

结论

本文详细介绍了 IKD-SWOpt 算法在 SHIFT Planner 框架中的应用。通过结合增量 KD 树和滑动窗口优化技术,IKD-SWOpt 能够实时优化机器人路径,确保其在复杂和动态环境中的安全性和平滑性。

主要贡献包括:

  1. 识别优化段: 通过设定距离阈值,精准识别路径中需要优化的段落。
  2. 增量 KD 树: 利用 KD 树结构加速最近邻查询,提高障碍物避让的效率。
  3. 滑动窗口优化: 仅对路径中的关键段落进行优化,降低计算负担。
  4. 路径平滑: 使用 B-样条曲线进一步提升路径的连续性和可行性。
  5. 实验验证: 多组实验结果展示了 IKD-SWOpt 在不同环境中的优越性能。

后续会继续讲解下其中基于语义地图进行速度分配的RFICP算法内容,感兴趣的可以先去看看paper:SHIFTPlanner。

相关文章:

超快速的路径优化IKD-SWOpt:SHIFT Planner 中增量 KD 树滑动窗口优化算法详解

IKD-SWOpt&#xff1a;SHIFT Planner 中增量 KD 树滑动窗口优化算法详解 今天本博主王婆卖瓜自卖自夸&#x1f604;&#xff0c;介绍自己paper中的算法&#xff0c;本算法已经持续开源中(部分关键内容)Github&#xff0c;之前很多读者朋友一直说要详细讲讲路径优化算法&#x…...

精读DeepSeek v3技术文档的心得感悟

最近宋大宝同学读完了DeepSeekv3的文档&#xff0c;心中颇多感慨&#xff0c;忍不住想在这里记录一下对这款“业界有望启示未来低精度训练走向”的开源大模型的观察与思考。DeepSeek v3的亮点绝不仅仅是“Float8”或“超长上下文”这么简单&#xff0c;而是贯穿了从数值精度、注…...

【Java数据结构】LinkedList与链表

认识LinkedList LinkedList就是一个链表&#xff0c;它也是实现List接口的一个类。LinkedList就是通过next引用将所有的结点链接起来&#xff0c;所以不需要数组。LinkedList也是以泛型的方法实现的&#xff0c;所以使用这个类都需要实例化对象。 链表分为很多种&#xff0c;比…...

uniapp——微信小程序,从客户端会话选择文件

微信小程序选择文件 文章目录 微信小程序选择文件效果图选择文件返回数据格式 API文档&#xff1a; chooseMessageFile 微信小程序读取文件&#xff0c;请查看 效果图 选择文件 /*** description 从客户端会话选择文件* returns {String} 文件路径*/ const chooseFile () &g…...

【CSS in Depth 2 精译_098】17.3:CSS 动画延迟技术与填充模式设置 + 17.4:通过 CSS 动画传递意图的秘诀

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 17 章 动画】 ✔️ 17.1 关键帧17.2 3D 变换下的动画设置 17.2.1 添加动画前页面布局的构建17.2.2 为布局添加动画 17.3 动画延迟与填充模式 ✔️17.4 通过动画传递意图…...

Oracle考试多少分算通过?

OCP和OCM认证的考试及格分数并不是固定的&#xff0c;而是根据考试的难度和考生的整体表现来确定。对于OCP认证&#xff0c;考生需要全面掌握考试要求的知识和技能&#xff0c;并在考试中表现出色才有可能通过。而对于OCM认证&#xff0c;考生则需要在每个模块中都达到一定的水…...

在云服务器中编译IDF(ESP32库)

登录云服务器 使用gitee从github上导入仓库 地址GitHub - espressif/esp-idf: Espressif IoT Development Framework. Official development framework for Espressif SoCs. 然后在云服务器中创建目录~/esp 进入路径后使用git clone 下载项目 进入编程指南ESP-IDF 编程指南…...

Oracle 日常巡检

1. 检查服务器状态 1.1. CPU使用情况 1.1.1. top top 命令是 Linux 和 Unix 系统中用于显示实时系统状态的工具&#xff0c;特别是对于监控 CPU 和内存的使用非常有用。 在命令行中输入 top&#xff0c;top 会显示一个实时更新的界面&#xff0c;其中包含系统的关键指标&am…...

机器学习常用术语

目录 概要 机器学习常用术语 1、模型 2、数据集 3、样本与特征 4、向量 5、矩阵 6、假设函数与损失函数 7、拟合、过拟合与欠拟合 8、激活函数(Activation Function) 9、反向传播(Backpropagation) 10、基线(Baseline) 11、批量(Batch) 12、批量大小(Batch Size)…...

springboot507基于Springboot教学管理系统(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装教学管理系统软件来发挥其高效地信息处理的作用&#xff0c…...

工具变量笔记

补充知识 简单介绍工具变量 假设 Y i α β D i ϵ i Y_i\alpha\beta D_i\epsilon_i Yi​αβDi​ϵi​, where E ( ϵ i ∣ D i ) 0 E(\epsilon_i\mid D_i)0 E(ϵi​∣Di​)0. 但是通常这个条件不满足。于是假如有这样一个工具变量 Z i Z_i Zi​存在的话&#xff0c;满…...

ElasticSearch 统计分析全攻略

在大数据时代&#xff0c;数据的价值不仅在于存储&#xff0c;更在于能够从中挖掘出有意义的信息。ElasticSearch 作为一款强大的分布式搜索引擎&#xff0c;除了具备出色的搜索功能外&#xff0c;其内置的统计分析能力也不容小觑&#xff0c;能够助力我们快速洞察数据背后的规…...

DataCap MongoDB Driver: 全面解析MongoDB在DataCap中的使用指南

在大数据时代&#xff0c;MongoDB作为一款广受欢迎的NoSQL数据库&#xff0c;其灵活的文档存储模型和强大的查询能力使其成为许多现代应用的首选数据存储方案。今天&#xff0c;我们将深入探讨DataCap MongoDB Driver&#xff0c;这是一个强大的工具&#xff0c;它让在DataCap环…...

DDSort-简单实用的jQuery拖拽排序插件

DDSort.js是一款简单实用的jQuery拖拽排序插件。通过该插件你可以任意拖动页面中元素&#xff0c;并放置到指定的地方。DDSort.js插件实用简单&#xff0c;兼容IE8浏览器。 在线预览 下载 使用方法 实用该拖拽排序插件需要在页面中引入jquery文件和ddsort.js文件。 <scri…...

「下载」智慧园区及重点区域安全防范解决方案:框架统一规划,建设集成管理平台

智慧园区在基础设施建设和管理上仍存在诸多挑战。园区内场景碎片化、系统独立化、数据无交互、应用无联动等问题普遍存在&#xff0c;导致管理效率低下&#xff0c;安全隐患频发。 各安保系统如视频监控系统、报警管理系统、门禁管理系统等独立运行&#xff0c;数据不共享&…...

华为 IPD,究竟有什么特点?(一)

关注作者 &#xff08;一&#xff09;华为版 IPD 特点一&#xff1a;一定要让研发转身为作战 部队 冲到前台的研发&#xff0c;应主动拉通公司上下游&#xff0c;向前抓需求&#xff0c;向后支撑可制造性、可 服务性&#xff0c;并推动制造、服务的改进。 1&#xff09;研发从…...

Llama 3 后训练(三)

目录 4. 后训练 4.1 建模 图表解读 4.1.1 聊天对话格式 4.1.2 奖励建模 4.1.3 监督微调&#xff08;Supervised Finetuning&#xff09; 4.1.4 直接偏好优化&#xff08;Direct Preference Optimization&#xff09; 4.1.5 模型平均&#xff08;Model Averaging&#x…...

Docker 安装全攻略:从入门到上手

Docker 安装全攻略&#xff1a;从入门到上手 在当今的软件开发与部署领域&#xff0c;Docker 已经成为了一项不可或缺的关键技术。它能够将应用程序及其依赖项打包成轻量级、可移植的容器&#xff0c;极大地简化了开发、测试和部署的流程。本文将详细讲解在不同操作系统下 Doc…...

螺杆支撑座在运用中会出现哪些问题?

螺杆支撑座是一种用于支撑滚珠螺杆的零件&#xff0c;通常用于机床、数控机床、自动化生产线等高精度机械设备中。在运用中可能会出现多种问题&#xff0c;这些问题源于多个方面&#xff0c;以下是对可能出现的问题简单了解下&#xff1a; 1、安装不当&#xff1a;安装过程中没…...

Java与SQL Server数据库连接的实践与要点

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;Java和SQL Server数据库交互是企业级应用开发中的重要环节。本文详细探讨了使用Java通过JDBC连接到SQL Server数据库的过程&#xff0c;包括加载驱动、建立连接、执行SQL语句、处理异常、资源管理、事务处理和连…...

前端杂货铺——TodoList

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…...

智能对联网页小程序的仓颉之旅

#传统楹联遇上AI智能体&#xff1a;我的Cangjie Magic开发纪实 引言&#xff1a;一场跨越千年的数字对话 "云对雨&#xff0c;雪对风&#xff0c;晚照对晴空"。昨天晚上星空璀璨&#xff0c;当我用仓颉语言写下第一个智能对联网页小程序的Agent DSL代码时&#xff0…...

RT-Thread内核组成——内核移植

内核移植就是指将 RT-Thread 内核在不同的芯片架构、不同的板卡上运行起来&#xff0c;能够具备线程管理和调度&#xff0c;内存管理&#xff0c;线程间同步和通信、定时器管理等功能。移植可分为 CPU 架构移植和 BSP&#xff08;Board support package&#xff0c;板级支持包&…...

Android USB 通信开发

Android USB 通信开发主要涉及两种模式&#xff1a;主机模式(Host Mode)和配件模式(Accessory Mode)。以下是开发USB通信应用的关键知识点和步骤。 1. 基本概念 主机模式(Host Mode) Android设备作为USB主机&#xff0c;控制连接的USB设备 需要设备支持USB主机功能(通常需要O…...

多模型协同:基于 SAM 分割 + YOLO 检测 + ResNet 分类的工业开关状态实时监控方案

一、技术优势与适配性分析 1. 任务分工的合理性 YOLO&#xff08;目标检测&#xff09; 核心价值&#xff1a;快速定位工业开关在图像中的位置&#xff08;边界框&#xff09;&#xff0c;为后续分割和分类提供ROI&#xff08;感兴趣区域&#xff09;。工业场景适配性&#xf…...

gitlab CI/CD本地部署配置

背景: 代码管理平台切换为公司本地服务器的gitlab server。为了保证commit的代码至少编译ok&#xff0c;也为了以后能拓展test cases&#xff0c;现在先搭建本地gitlab server的CI/CD基本的编译job pipeline。 配置步骤&#xff1a; 先安装gitlab-runner: curl -L "ht…...

html文字红色粗体,闪烁渐变动画效果,中英文切换版本

1. 代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>红色粗体闪烁文字表格 - 中英文切换</t…...

Qt 5.12 上读取 .xlsx 文件(Windows 平台)

推荐最优方案&#xff1a;使用 QXlsx 库 QXlsx 是一个基于 Qt 的开源库&#xff0c;专门用于读写 .xlsx 文件&#xff0c;适用于 Qt 5.12&#xff0c;且无需依赖 Microsoft Excel 或 COM 对象。以下是其优势与实现步骤&#xff1a; 优势 跨平台&#xff1a;QXlsx 不依赖 Mic…...

Python爬虫实战:研究urlunparse函数相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上的数据量呈现出指数级增长。如何从海量的网页数据中高效地获取有价值的信息,成为了学术界和工业界共同关注的问题。网络爬虫作为一种自动获取网页内容的技术,能够按照预定的规则遍历互联网上的网页,并提取出所需…...

【时时三省】(C语言基础)局部变量和全局变量

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 以前所见到的程序大多数是一个程序只包含一个main函数&#xff0c;变量是在函数的开头处定义的。这些变量在本函数范围内有效&#xff0c;即在本函数开头定义的变量&#xff0c;在本函数中可…...