Informed RRT*算法,并包含圆形障碍物环境

我将为你实现一个基于Matplotlib可视化的Informed RRT*算法,并包含圆形障碍物环境。这个算法在找到初始路径后会持续优化,使用椭圆采样区域来加速收敛。

python 复制代码
import numpy as np
import math
import random
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Ellipse
import matplotlib.animation as animation

class Node:
    """RRT树节点类"""
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.parent = None
        self.cost = 0.0

class InformedRRTStar:
    """Informed RRT*算法实现"""
    
    def __init__(self, start, goal, bounds, obstacles, 
                 max_iter=2000, step_size=20, neighbor_radius=30):
        """
        初始化算法
        
        参数:
            start: 起点坐标 (x, y)
            goal: 终点坐标 (x, y)
            bounds: 地图边界 [x_min, x_max, y_min, y_max]
            obstacles: 障碍物列表,每个障碍物为 (x, y, radius)
            max_iter: 最大迭代次数
            step_size: 步长
            neighbor_radius: 邻居搜索半径
        """
        self.start = Node(start[0], start[1])
        self.goal = Node(goal[0], goal[1])
        self.bounds = bounds
        self.obstacles = obstacles
        self.max_iter = max_iter
        self.step_size = step_size
        self.neighbor_radius = neighbor_radius
        
        # RRT树
        self.tree = [self.start]
        
        # 路径和成本
        self.best_path = []
        self.best_cost = float('inf')
        self.found_initial = False
        
        # 可视化数据
        self.visualization_data = {
            'nodes': [],
            'edges': [],
            'best_paths': []
        }
    
    def is_collision_free(self, node1, node2):
        """检查两点之间的线段是否与障碍物碰撞"""
        for (ox, oy, r) in self.obstacles:
            # 计算点到线段的最短距离
            distance = self.point_to_line_distance(ox, oy, 
                                                  node1.x, node1.y,
                                                  node2.x, node2.y)
            if distance <= r:
                return False
        return True
    
    def point_to_line_distance(self, px, py, x1, y1, x2, y2):
        """计算点(px,py)到线段(x1,y1)-(x2,y2)的距离"""
        # 线段长度
        line_len = math.hypot(x2 - x1, y2 - y1)
        
        if line_len == 0:
            return math.hypot(px - x1, py - y1)
        
        # 投影比例
        t = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / (line_len ** 2)
        t = max(0, min(1, t))
        
        # 最近点坐标
        closest_x = x1 + t * (x2 - x1)
        closest_y = y1 + t * (y2 - y1)
        
        return math.hypot(px - closest_x, py - closest_y)
    
    def random_point(self):
        """随机采样点"""
        if not self.found_initial:
            # 初始阶段在整个空间均匀采样
            return (random.uniform(self.bounds[0], self.bounds[1]),
                    random.uniform(self.bounds[2], self.bounds[3]))
        else:
            # Informed阶段:在椭圆内采样
            return self.sample_in_ellipse()
    
    def sample_in_ellipse(self):
        """在椭圆采样区域内生成随机点"""
        # 椭圆参数
        c_min = self.best_cost
        if c_min == float('inf'):
            return self.random_point()
        
        # 起点和终点中心
        x_center = [(self.start.x + self.goal.x) / 2,
                    (self.start.y + self.goal.y) / 2]
        
        # 旋转角度
        angle = math.atan2(self.goal.y - self.start.y,
                          self.goal.x - self.start.x)
        
        # 椭圆半长轴和半短轴
        a = c_min / 2
        c = math.sqrt((self.goal.x - self.start.x) ** 2 + 
                      (self.goal.y - self.start.y) ** 2) / 2
        b = math.sqrt(a ** 2 - c ** 2)
        
        # 在单位圆内采样
        while True:
            r = math.sqrt(random.random())
            theta = random.uniform(0, 2 * math.pi)
            
            # 单位圆内的点
            x = r * math.cos(theta)
            y = r * math.sin(theta)
            
            # 变换到椭圆
            x_rot = x * math.cos(angle) - y * math.sin(angle)
            y_rot = x * math.sin(angle) + y * math.cos(angle)
            
            # 缩放和平移
            x_sample = x_center[0] + a * x_rot
            y_sample = x_center[1] + b * y_rot
            
            # 检查是否在边界内
            if (self.bounds[0] <= x_sample <= self.bounds[1] and
                self.bounds[2] <= y_sample <= self.bounds[3]):
                return (x_sample, y_sample)
    
    def nearest_node(self, point):
        """找到最近的节点"""
        min_dist = float('inf')
        nearest = None
        
        for node in self.tree:
            dist = math.hypot(node.x - point[0], node.y - point[1])
            if dist < min_dist:
                min_dist = dist
                nearest = node
        
        return nearest
    
    def steer(self, from_node, to_point):
        """从from_node向to_point方向生长一步"""
        # 计算方向
        dx = to_point[0] - from_node.x
        dy = to_point[1] - from_node.y
        dist = math.hypot(dx, dy)
        
        if dist < self.step_size:
            return Node(to_point[0], to_point[1])
        
        # 按步长生长
        ratio = self.step_size / dist
        new_x = from_node.x + dx * ratio
        new_y = from_node.y + dy * ratio
        
        return Node(new_x, new_y)
    
    def find_near_nodes(self, new_node):
        """找到新节点附近的邻居节点"""
        near_nodes = []
        for node in self.tree:
            dist = math.hypot(node.x - new_node.x, node.y - new_node.y)
            if dist <= self.neighbor_radius:
                near_nodes.append(node)
        return near_nodes
    
    def choose_parent(self, new_node, near_nodes):
        """为new_node选择最优父节点"""
        min_cost = float('inf')
        best_parent = None
        
        for node in near_nodes:
            # 检查是否无碰撞
            if self.is_collision_free(node, new_node):
                # 计算成本
                cost = node.cost + math.hypot(node.x - new_node.x,
                                            node.y - new_node.y)
                if cost < min_cost:
                    min_cost = cost
                    best_parent = node
        
        return best_parent, min_cost
    
    def rewire(self, new_node, near_nodes):
        """重新连接附近的节点"""
        for node in near_nodes:
            # 检查通过new_node是否能为node提供更短路径
            if node == new_node.parent:
                continue
            
            new_cost = new_node.cost + math.hypot(new_node.x - node.x,
                                                new_node.y - node.y)
            
            if new_cost < node.cost:
                # 检查新路径是否无碰撞
                if self.is_collision_free(new_node, node):
                    # 重新连接
                    node.parent = new_node
                    node.cost = new_cost
    
    def check_goal_connection(self, node):
        """检查是否可以直接连接到目标点"""
        if math.hypot(node.x - self.goal.x, node.y - self.goal.y) <= self.step_size:
            if self.is_collision_free(node, self.goal):
                # 设置目标点的父节点和成本
                self.goal.parent = node
                self.goal.cost = node.cost + math.hypot(node.x - self.goal.x,
                                                      node.y - self.goal.y)
                
                # 更新最佳路径
                self.update_best_path()
                return True
        return False
    
    def update_best_path(self):
        """更新最佳路径"""
        if self.goal.parent is None:
            return
        
        # 回溯得到路径
        path = []
        node = self.goal
        while node is not None:
            path.append((node.x, node.y))
            node = node.parent
        path.reverse()
        
        path_cost = self.goal.cost
        
        if path_cost < self.best_cost:
            self.best_cost = path_cost
            self.best_path = path
            self.found_initial = True
    
    def plan(self):
        """执行路径规划主循环"""
        for i in range(self.max_iter):
            # 1. 随机采样
            rand_point = self.random_point()
            
            # 2. 找到最近节点
            nearest = self.nearest_node(rand_point)
            
            # 3. 向采样点生长
            new_node = self.steer(nearest, rand_point)
            
            # 4. 检查碰撞
            if not self.is_collision_free(nearest, new_node):
                continue
            
            # 5. 找到附近节点
            near_nodes = self.find_near_nodes(new_node)
            
            # 6. 选择最优父节点
            parent, cost = self.choose_parent(new_node, near_nodes)
            
            if parent is None:
                continue
            
            # 设置新节点的父节点和成本
            new_node.parent = parent
            new_node.cost = cost
            
            # 7. 添加到树中
            self.tree.append(new_node)
            
            # 8. 重新连接
            self.rewire(new_node, near_nodes)
            
            # 9. 检查是否到达目标
            self.check_goal_connection(new_node)
            
            # 10. 记录可视化数据(每100次迭代记录一次)
            if i % 100 == 0 or i == self.max_iter - 1:
                self.record_visualization_data(i)
            
            # 显示进度
            if i % 500 == 0:
                print(f"Iteration: {i}, Best Cost: {self.best_cost:.2f}")
        
        return self.best_path, self.best_cost
    
    def record_visualization_data(self, iteration):
        """记录可视化数据"""
        # 记录节点
        nodes = [(node.x, node.y) for node in self.tree]
        
        # 记录边
        edges = []
        for node in self.tree:
            if node.parent:
                edges.append([(node.parent.x, node.parent.y),
                            (node.x, node.y)])
        
        # 记录最佳路径
        best_path = self.best_path
        
        self.visualization_data['nodes'].append(nodes.copy())
        self.visualization_data['edges'].append(edges.copy())
        self.visualization_data['best_paths'].append(best_path.copy())

def create_environment():
    """创建测试环境"""
    # 地图边界 [x_min, x_max, y_min, y_max]
    bounds = [0, 100, 0, 100]
    
    # 起点和终点
    start = (10, 10)
    goal = (90, 90)
    
    # 圆形障碍物 (x, y, radius)
    obstacles = [
        (30, 30, 8),
        (50, 20, 6),
        (60, 60, 10),
        (20, 70, 7),
        (70, 40, 8),
        (40, 80, 9),
        (80, 20, 7),
        (35, 50, 5)
    ]
    
    return start, goal, bounds, obstacles

def visualize_animation(rrt_star):
    """创建可视化动画"""
    fig, ax = plt.subplots(figsize=(10, 10))
    
    # 设置边界
    ax.set_xlim(rrt_star.bounds[0], rrt_star.bounds[1])
    ax.set_ylim(rrt_star.bounds[2], rrt_star.bounds[3])
    ax.set_aspect('equal')
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_title('Informed RRT* Path Planning with Circular Obstacles')
    
    # 绘制障碍物
    for (ox, oy, r) in rrt_star.obstacles:
        circle = Circle((ox, oy), r, color='red', alpha=0.6)
        ax.add_patch(circle)
    
    # 绘制起点和终点
    ax.plot(rrt_star.start.x, rrt_star.start.y, 'go', markersize=12, 
            label='Start', markeredgecolor='black', markeredgewidth=2)
    ax.plot(rrt_star.goal.x, rrt_star.goal.y, 'ro', markersize=12, 
            label='Goal', markeredgecolor='black', markeredgewidth=2)
    
    # 可视化元素
    tree_scatter = ax.scatter([], [], s=10, c='blue', alpha=0.6, 
                             label='RRT Tree Nodes')
    tree_lines = [ax.plot([], [], 'gray', linewidth=0.5, alpha=0.3)[0] 
                 for _ in range(len(rrt_star.visualization_data['edges'][0]))]
    path_line, = ax.plot([], [], 'yellow', linewidth=3, label='Best Path')
    ellipse_patch = None
    
    # 添加图例
    ax.legend(loc='upper right')
    
    def init():
        """初始化动画"""
        tree_scatter.set_offsets(np.empty((0, 2)))
        for line in tree_lines:
            line.set_data([], [])
        path_line.set_data([], [])
        return [tree_scatter, *tree_lines, path_line]
    
    def update(frame):
        """更新动画帧"""
        # 更新树节点
        nodes = np.array(rrt_star.visualization_data['nodes'][frame])
        if len(nodes) > 0:
            tree_scatter.set_offsets(nodes)
        
        # 更新树边
        edges = rrt_star.visualization_data['edges'][frame]
        for i, line in enumerate(tree_lines):
            if i < len(edges):
                edge = edges[i]
                line.set_data([edge[0][0], edge[1][0]], 
                            [edge[0][1], edge[1][1]])
            else:
                line.set_data([], [])
        
        # 更新最佳路径
        best_path = rrt_star.visualization_data['best_paths'][frame]
        if len(best_path) > 1:
            path_x, path_y = zip(*best_path)
            path_line.set_data(path_x, path_y)
        else:
            path_line.set_data([], [])
        
        # 移除旧的椭圆
        nonlocal ellipse_patch
        if ellipse_patch is not None:
            ellipse_patch.remove()
        
        # 绘制椭圆采样区域(如果已找到初始路径)
        if rrt_star.found_initial and frame > 0:
            c_min = rrt_star.best_cost
            if c_min < float('inf'):
                # 椭圆参数
                x_center = [(rrt_star.start.x + rrt_star.goal.x) / 2,
                           (rrt_star.start.y + rrt_star.goal.y) / 2]
                angle = math.atan2(rrt_star.goal.y - rrt_star.start.y,
                                  rrt_star.goal.x - rrt_star.start.x)
                a = c_min / 2
                c = math.sqrt((rrt_star.goal.x - rrt_star.start.x) ** 2 + 
                             (rrt_star.goal.y - rrt_star.start.y) ** 2) / 2
                b = math.sqrt(max(0.1, a ** 2 - c ** 2))  # 避免b为负数
                
                # 创建椭圆
                ellipse_patch = Ellipse(xy=x_center, width=2*a, height=2*b,
                                       angle=math.degrees(angle),
                                       color='orange', alpha=0.2,
                                       label='Sampling Ellipse')
                ax.add_patch(ellipse_patch)
        
        # 更新标题
        iteration = frame * 100
        ax.set_title(f'Informed RRT* - Iteration: {iteration}, '
                    f'Path Cost: {rrt_star.best_cost:.2f}')
        
        return [tree_scatter, *tree_lines, path_line]
    
    # 创建动画
    frames = len(rrt_star.visualization_data['nodes'])
    anim = animation.FuncAnimation(fig, update, frames=frames,
                                 init_func=init, blit=False,
                                 interval=100, repeat=False)
    
    plt.tight_layout()
    plt.show()
    
    return anim

def visualize_final_result(rrt_star, best_path, best_cost):
    """可视化最终结果"""
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
    
    # 子图1:最终规划结果
    ax1.set_xlim(rrt_star.bounds[0], rrt_star.bounds[1])
    ax1.set_ylim(rrt_star.bounds[2], rrt_star.bounds[3])
    ax1.set_aspect('equal')
    ax1.set_xlabel('X')
    ax1.set_ylabel('Y')
    ax1.set_title('Final Planned Path')
    
    # 绘制障碍物
    for (ox, oy, r) in rrt_star.obstacles:
        circle = Circle((ox, oy), r, color='red', alpha=0.6)
        ax1.add_patch(circle)
    
    # 绘制RRT树
    for node in rrt_star.tree:
        if node.parent:
            ax1.plot([node.parent.x, node.x], [node.parent.y, node.y], 
                    'gray', linewidth=0.5, alpha=0.3)
    
    # 绘制树节点
    nodes_x = [node.x for node in rrt_star.tree]
    nodes_y = [node.y for node in rrt_star.tree]
    ax1.scatter(nodes_x, nodes_y, s=10, c='blue', alpha=0.6, 
               label='RRT Tree Nodes')
    
    # 绘制最佳路径
    if best_path:
        path_x, path_y = zip(*best_path)
        ax1.plot(path_x, path_y, 'yellow', linewidth=3, label='Best Path')
    
    # 绘制起点和终点
    ax1.plot(rrt_star.start.x, rrt_star.start.y, 'go', markersize=12, 
            label='Start', markeredgecolor='black', markeredgewidth=2)
    ax1.plot(rrt_star.goal.x, rrt_star.goal.y, 'ro', markersize=12, 
            label='Goal', markeredgecolor='black', markeredgewidth=2)
    
    # 绘制椭圆采样区域
    if rrt_star.found_initial and best_cost < float('inf'):
        # 椭圆参数
        x_center = [(rrt_star.start.x + rrt_star.goal.x) / 2,
                   (rrt_star.start.y + rrt_star.goal.y) / 2]
        angle = math.atan2(rrt_star.goal.y - rrt_star.start.y,
                          rrt_star.goal.x - rrt_star.start.x)
        a = best_cost / 2
        c = math.sqrt((rrt_star.goal.x - rrt_star.start.x) ** 2 + 
                     (rrt_star.goal.y - rrt_star.start.y) ** 2) / 2
        b = math.sqrt(max(0.1, a ** 2 - c ** 2))
        
        ellipse = Ellipse(xy=x_center, width=2*a, height=2*b,
                         angle=math.degrees(angle),
                         color='orange', alpha=0.2, label='Sampling Ellipse')
        ax1.add_patch(ellipse)
    
    ax1.legend(loc='upper right')
    ax1.grid(True, alpha=0.3)
    
    # 子图2:收敛曲线
    ax2.set_xlabel('Iteration')
    ax2.set_ylabel('Best Path Cost')
    ax2.set_title('Convergence Curve')
    ax2.grid(True, alpha=0.3)
    
    # 提取收敛数据
    iterations = list(range(0, len(rrt_star.visualization_data['best_paths']) * 100, 100))
    costs = []
    for path in rrt_star.visualization_data['best_paths']:
        if len(path) > 1:
            # 计算路径成本
            cost = 0
            for i in range(len(path) - 1):
                cost += math.hypot(path[i+1][0] - path[i][0],
                                 path[i+1][1] - path[i][1])
            costs.append(cost)
        else:
            costs.append(float('inf'))
    
    # 绘制收敛曲线
    valid_indices = [i for i, cost in enumerate(costs) if cost < float('inf')]
    if valid_indices:
        valid_iterations = [iterations[i] for i in valid_indices]
        valid_costs = [costs[i] for i in valid_indices]
        ax2.plot(valid_iterations, valid_costs, 'b-', linewidth=2, 
                label='Path Cost')
        ax2.fill_between(valid_iterations, 0, valid_costs, alpha=0.2)
    
    ax2.legend(loc='upper right')
    
    plt.tight_layout()
    plt.show()

def main():
    """主函数"""
    print("=== Informed RRT* Path Planning Demo ===")
    print("Creating environment...")
    
    # 创建环境
    start, goal, bounds, obstacles = create_environment()
    
    # 初始化算法
    print("Initializing Informed RRT* algorithm...")
    rrt_star = InformedRRTStar(start, goal, bounds, obstacles,
                              max_iter=3000, step_size=15, neighbor_radius=25)
    
    # 执行路径规划
    print("Starting path planning...")
    best_path, best_cost = rrt_star.plan()
    
    # 显示结果
    print("\n=== Planning Results ===")
    if best_path:
        print(f"Path found! Cost: {best_cost:.2f}")
        print(f"Path length: {len(best_path)} nodes")
    else:
        print("No path found within iteration limit.")
    
    # 可视化最终结果
    print("\nGenerating visualizations...")
    visualize_final_result(rrt_star, best_path, best_cost)
    
    # 询问是否查看动画
    response = input("\nDo you want to see the animation? (y/n): ")
    if response.lower() == 'y':
        print("Generating animation (this may take a moment)...")
        anim = visualize_animation(rrt_star)
    
    print("\nDemo completed!")

if __name__ == "__main__":
    main()

算法特点与使用说明

🎯 算法核心特点

  1. Informed RRT*:找到初始路径后,将采样区域限制在椭圆内,大大提高了收敛速度
  2. 圆形障碍物:使用精确的几何碰撞检测,支持多个圆形障碍物
  3. 双向优化:找到路径后继续优化,直到达到最佳路径
  4. 完整可视化:
    · 实时显示RRT树的生长过程
    · 动态显示椭圆采样区域
    · 路径收敛曲线分析

🚀 运行方式

  1. 确保已安装必要的库:
bash 复制代码
pip install numpy matplotlib
  1. 直接运行脚本,程序将:
    · 创建包含8个圆形障碍物的测试环境
    · 执行Informed RRT*路径规划(3000次迭代)
    · 显示最终规划结果和收敛曲线
    · 可选择观看完整的动画过程

⚙️ 参数调整建议

· 增加max_iter:如果需要更优路径,可增加到5000-10000

· 调整step_size:较小的步长(10-15)适合复杂环境,较大的步长(20-30)适合开阔环境

· 修改neighbor_radius:通常设置为step_size的1.5-2倍

· 自定义障碍物:在create_environment()函数中添加/修改圆形障碍物

📊 输出说明

· 左侧图:显示最终路径、RRT树、椭圆采样区域

· 右侧图:显示路径成本的收敛过程

· 动画:展示算法完整的运行过程(可选)

这个实现包含了Informed RRT*的所有关键特性,并提供了丰富的可视化功能。你可以根据需要修改环境配置或算法参数来适应不同的场景。

我将重写 sample_in_ellipse 函数,使用矩阵变换的方法来实现更简洁高效的椭圆采样:

python 复制代码
import numpy as np

def sample_in_ellipse(self):
    """在椭圆采样区域内生成随机点(使用矩阵变换)"""
    # 如果没有找到初始路径,使用均匀采样
    if not self.found_initial or self.best_cost == float('inf'):
        return self.random_point()
    
    # 计算椭圆参数
    start = np.array([self.start.x, self.start.y])
    goal = np.array([self.goal.x, self.goal.y])
    
    # 椭圆中心
    center = (start + goal) / 2.0
    
    # 椭圆半长轴(沿起点到终点方向)
    c_min = self.best_cost
    a = c_min / 2.0  # 半长轴
    
    # 起点到终点的距离
    c = np.linalg.norm(goal - start) / 2.0  # 焦距
    
    # 计算半短轴(确保不为负)
    if a ** 2 - c ** 2 < 0:
        b = 0.1  # 最小正值,避免数值问题
    else:
        b = np.sqrt(a ** 2 - c ** 2)  # 半短轴
    
    # 计算旋转角度
    direction = goal - start
    angle = np.arctan2(direction[1], direction[0])
    
    # 构建变换矩阵
    # 1. 旋转矩阵(将椭圆转正)
    cos_theta = np.cos(-angle)
    sin_theta = np.sin(-angle)
    R = np.array([[cos_theta, -sin_theta],
                  [sin_theta, cos_theta]])
    
    # 2. 缩放矩阵(将椭圆变为单位圆)
    S = np.array([[1.0/a, 0.0],
                  [0.0, 1.0/b]])
    
    # 3. 组合变换:先旋转再缩放
    T = S @ R
    
    # 在单位圆内采样
    max_attempts = 100  # 防止无限循环
    for _ in range(max_attempts):
        # 方法1:使用均匀圆内采样(推荐)
        # 在单位圆内均匀采样
        r = np.sqrt(np.random.random())
        theta = 2 * np.pi * np.random.random()
        
        # 单位圆内的点
        point_in_circle = np.array([r * np.cos(theta), 
                                   r * np.sin(theta)])
        
        # 方法2:拒绝采样法(备用)
        # point_in_circle = np.random.uniform(-1, 1, 2)
        # if np.linalg.norm(point_in_circle) > 1:
        #     continue  # 不在单位圆内,重新采样
        
        # 逆变换:将单位圆内的点变换到椭圆
        # 先进行缩放逆变换,再进行旋转逆变换
        R_inv = np.array([[np.cos(angle), -np.sin(angle)],
                         [np.sin(angle), np.cos(angle)]])
        S_inv = np.array([[a, 0.0],
                         [0.0, b]])
        
        # 组合逆变换:先缩放再旋转
        T_inv = R_inv @ S_inv
        
        # 应用到采样点并平移
        point_in_ellipse = T_inv @ point_in_circle + center
        
        # 检查是否在边界内
        if (self.bounds[0] <= point_in_ellipse[0] <= self.bounds[1] and
            self.bounds[2] <= point_in_ellipse[1] <= self.bounds[3]):
            return (float(point_in_ellipse[0]), float(point_in_ellipse[1]))
    
    # 如果多次尝试都失败,返回一个默认采样点
    print("Warning: Failed to sample within ellipse after multiple attempts")
    return self.random_point()

# 为了保持代码兼容性,更新类的初始化方法以使用NumPy
def __init__(self, start, goal, bounds, obstacles, 
             max_iter=2000, step_size=20, neighbor_radius=30):
    """
    初始化算法
    
    参数:
        start: 起点坐标 (x, y)
        goal: 终点坐标 (x, y)
        bounds: 地图边界 [x_min, x_max, y_min, y_max]
        obstacles: 障碍物列表,每个障碍物为 (x, y, radius)
        max_iter: 最大迭代次数
        step_size: 步长
        neighbor_radius: 邻居搜索半径
    """
    self.start = Node(start[0], start[1])
    self.goal = Node(goal[0], goal[1])
    self.bounds = bounds
    self.obstacles = obstacles
    self.max_iter = max_iter
    self.step_size = step_size
    self.neighbor_radius = neighbor_radius
    
    # RRT树
    self.tree = [self.start]
    
    # 路径和成本
    self.best_path = []
    self.best_cost = float('inf')
    self.found_initial = False
    
    # 可视化数据
    self.visualization_data = {
        'nodes': [],
        'edges': [],
        'best_paths': []
    }
    
    # 预计算一些常用值以提高性能
    self.start_np = np.array([start[0], start[1]])
    self.goal_np = np.array([goal[0], goal[1]])
    self.center_np = (self.start_np + self.goal_np) / 2.0
    self.base_distance = np.linalg.norm(self.goal_np - self.start_np)

矩阵变换方法的优势

🔧 变换矩阵分解

整个变换过程可以分解为以下几个步骤:

python 复制代码
# 从单位圆到椭圆的完整变换过程:
point_in_ellipse = R_inv @ S_inv @ point_in_circle + center

# 其中:
# R_inv: 旋转矩阵的逆(将点转回椭圆方向)
# S_inv: 缩放矩阵的逆(将单位圆拉伸为椭圆)
# center: 平移向量(将中心移回正确位置)

📊 性能优化版本

如果需要更高的性能,可以使用预计算变换矩阵:

python 复制代码
def sample_in_ellipse_optimized(self):
    """优化的椭圆采样(预计算变换矩阵)"""
    if not self.found_initial or self.best_cost == float('inf'):
        return self.random_point()
    
    # 预计算变换矩阵(如果参数发生变化时重新计算)
    if not hasattr(self, '_cached_transform') or self._cached_transform['cost'] != self.best_cost:
        self._update_transform_matrix()
    
    # 在单位圆内采样
    r = np.sqrt(np.random.random())
    theta = 2 * np.pi * np.random.random()
    point_in_circle = np.array([r * np.cos(theta), r * np.sin(theta)])
    
    # 应用预计算的变换
    point_in_ellipse = self._T_inv @ point_in_circle + self.center_np
    
    # 边界检查
    if (self.bounds[0] <= point_in_ellipse[0] <= self.bounds[1] and
        self.bounds[2] <= point_in_ellipse[1] <= self.bounds[3]):
        return (float(point_in_ellipse[0]), float(point_in_ellipse[1]))
    
    return self.random_point()

def _update_transform_matrix(self):
    """更新预计算的变换矩阵"""
    c_min = self.best_cost
    a = c_min / 2.0
    c = self.base_distance / 2.0
    
    if a ** 2 - c ** 2 < 0:
        b = 0.1
    else:
        b = np.sqrt(a ** 2 - c ** 2)
    
    # 计算旋转角度
    direction = self.goal_np - self.start_np
    angle = np.arctan2(direction[1], direction[0])
    
    # 构建逆变换矩阵(从单位圆到椭圆)
    cos_angle = np.cos(angle)
    sin_angle = np.sin(angle)
    
    R_inv = np.array([[cos_angle, -sin_angle],
                      [sin_angle, cos_angle]])
    S_inv = np.array([[a, 0.0],
                      [0.0, b]])
    
    self._T_inv = R_inv @ S_inv
    self._cached_transform = {'cost': c_min, 'a': a, 'b': b, 'angle': angle}

🎯 两种采样方法的对比

特性 原方法(极坐标) 新方法(矩阵变换)

数学清晰度 直观但公式分散 矩阵运算,结构清晰

代码可读性 中等 高(分离了变换步骤)

扩展性 有限 容易扩展到3D或更高维

性能 中等 优化后更高

数值稳定性 可能受角度计算影响 矩阵运算更稳定

💡 使用建议

  1. 基本场景:使用第一个版本(sample_in_ellipse)即可,代码清晰且性能足够
  2. 高性能需求:使用优化版本(sample_in_ellipse_optimized),特别适用于大量迭代
  3. 3D扩展:矩阵变换方法可以轻松扩展到3D空间,只需将2x2矩阵改为3x3矩阵

这个矩阵变换方法不仅代码更简洁,而且数学上更优雅,更容易理解和维护。变换矩阵的方法也更符合计算机图形学和机器人学中的标准实践。

相关推荐
Yupureki3 小时前
《算法竞赛从入门到国奖》算法基础:入门篇-双指针
c语言·开发语言·数据结构·c++·算法·visual studio
AshinGau4 小时前
权重衰减(Weight Decay)
神经网络·算法
Liangwei Lin4 小时前
洛谷 P3367 【模板】并查集
算法
数据的世界014 小时前
重构智慧书-第16条:学当广博,志当赤诚
人工智能
dyxal4 小时前
动态规划:给“最优解”一张记住过去的备忘录
算法·动态规划·代理模式
用户5191495848454 小时前
Android AI示例宝库:一站式探索Google AI模型的无限可能
人工智能·aigc
黑客思维者4 小时前
XGW-9000 网关 DDR4/LPDDR4 内存子系统信号完整性仿真细化设计
开发语言·python·嵌入式硬件·ddr4·信号仿真
炽烈小老头4 小时前
【每天学习一点算法2025/12/16】二叉树的最大深度
学习·算法
Felven4 小时前
华为昇腾310P模型转换失败问题解决
linux·python·模型训练·昇腾·310p