目录

一、算法原理

二、核心思想

三、算法步骤

四、MATLAB实现


一、算法原理

RRT(Rapidly-exploring Random Tree,快速随机扩展树)是一种用于路径规划的采样型算法,特别适用于高维空间和非完整约束系统的路径规划。

二、核心思想

  1. 随机采样:在配置空间中随机采样点

  2. 最近邻搜索:在现有树中找到距离采样点最近的节点

  3. 扩展树:从最近节点向采样点方向扩展一小步

  4. 碰撞检测:确保新节点是可行的

  5. 连接起点和终点:直到找到连接起点和终点的路径

三、算法步骤

1. 初始化树T,只包含起始点q_start
2. for k = 1 to K do
3.   在配置空间中随机采样点q_rand
4.   在T中找到距离q_rand最近的节点q_near
5.   从q_near向q_rand方向扩展步长ε,得到新节点q_new
6.   如果q_new到q_near的路径无碰撞
7.     将q_new添加到T中,记录父节点为q_near
8.     如果q_new接近目标点q_goal
9.       找到路径,返回成功
10. 返回失败

四、MATLAB实现

function [path, smoothed_path, tree] = RRT_algorithm(varargin)
% RRT_ALGORITHM 快速探索随机树路径规划算法
% 用法:
%   [path, smoothed_path, tree] = RRT_algorithm()
%   [path, smoothed_path, tree] = RRT_algorithm('Param1', Value1, ...)
%
% 可选参数:
%   'start' - 起点坐标 [x, y] (默认: [10, 10])
%   'goal'  - 终点坐标 [x, y] (默认: [90, 90])
%   'map_width' - 地图宽度 (默认: 100)
%   'map_height' - 地图高度 (默认: 100)
%   'max_iter' - 最大迭代次数 (默认: 5000)
%   'step_size' - 步长 (默认: 5)
%   'goal_bias' - 目标偏置概率 (默认: 0.1)
%   'goal_radius' - 目标区域半径 (默认: 5)
%   'obstacles' - 障碍物矩阵 [x, y, width, height; ...] (默认: 4个矩形障碍物)
%   'visualize' - 是否可视化 (默认: true)

% 参数解析
params = parse_params(varargin{:});

%% 初始化RRT树
tree.vertices = params.start;           % 顶点列表
tree.edges = [];                        % 边列表
tree.parents = 0;                       % 父节点索引
found_path = false;                     % 路径找到标志
path = [];                              % 最终路径
smoothed_path = [];                     % 平滑路径

%% 可视化初始化
if params.visualize
    figure('Position', [100, 100, 800, 600]);
    axis([0 params.map_width 0 params.map_height]);
    axis equal;
    grid on;
    hold on;
    
    % 绘制起点和终点
    plot(params.start(1), params.start(2), 'go', 'MarkerSize', 10, 'MarkerFaceColor', 'g');
    plot(params.goal(1), params.goal(2), 'ro', 'MarkerSize', 10, 'MarkerFaceColor', 'r');
    rectangle('Position', [params.goal(1)-params.goal_radius, params.goal(2)-params.goal_radius, ...
               2*params.goal_radius, 2*params.goal_radius], 'Curvature', [1,1], ...
               'EdgeColor', 'r', 'LineWidth', 2);
    
    % 绘制障碍物
    for i = 1:size(params.obstacles, 1)
        rectangle('Position', params.obstacles(i, :), 'FaceColor', [0.5, 0.5, 0.5]);
    end
    
    title('RRT路径规划算法');
    xlabel('X坐标');
    ylabel('Y坐标');
end

%% RRT主循环
fprintf('开始RRT路径规划...\n');
for iter = 1:params.max_iter
    % 生成随机点(带目标偏置)
    if rand() < params.goal_bias
        rand_point = params.goal;  % 直接以目标点作为随机点
    else
        rand_point = [rand() * params.map_width, rand() * params.map_height];
    end
    
    % 寻找最近的节点
    distances = sum((tree.vertices - rand_point).^2, 2);
    [~, nearest_idx] = min(distances);
    nearest_point = tree.vertices(nearest_idx, :);
    
    % 向随机点方向扩展
    direction = rand_point - nearest_point;
    distance = norm(direction);
    
    if distance > 0
        new_point = nearest_point + (direction / distance) * min(params.step_size, distance);
        
        % 检查路径是否与障碍物碰撞
        if ~check_collision(nearest_point, new_point, params.obstacles)
            % 添加到树中
            tree.vertices = [tree.vertices; new_point];
            tree.edges = [tree.edges; nearest_idx, size(tree.vertices, 1)];
            tree.parents = [tree.parents; nearest_idx];
            
            % 可视化
            if params.visualize
                plot([nearest_point(1), new_point(1)], ...
                     [nearest_point(2), new_point(2)], 'b-', 'LineWidth', 1.5);
                plot(new_point(1), new_point(2), 'b.', 'MarkerSize', 8);
            end
            
            % 检查是否到达目标区域
            if norm(new_point - params.goal) <= params.goal_radius
                fprintf('在第%d次迭代找到路径!\n', iter);
                found_path = true;
                break;
            end
        end
    end
    
    % 动态显示
    if params.visualize && mod(iter, 100) == 0
        drawnow;
        title(sprintf('RRT路径规划 (迭代次数: %d/%d)', iter, params.max_iter));
    end
end

%% 提取并显示路径
if found_path
    % 回溯路径
    current_idx = size(tree.vertices, 1);
    
    while current_idx > 0
        path = [tree.vertices(current_idx, :); path];
        current_idx = tree.parents(current_idx);
    end
    
    if params.visualize
        % 绘制最终路径
        plot(path(:, 1), path(:, 2), 'r-', 'LineWidth', 3);
        plot(path(:, 1), path(:, 2), 'yo', 'MarkerSize', 6, 'MarkerFaceColor', 'y');
        title(sprintf('RRT路径规划完成!迭代次数: %d, 路径长度: %.2f', iter, calculate_path_length(path)));
    end
    
    fprintf('路径节点数: %d\n', size(path, 1));
    fprintf('路径长度: %.2f\n', calculate_path_length(path));
    
    % 平滑路径
    if size(path, 1) > 2
        smoothed_path = smooth_path(path, params.obstacles, params.visualize);
        fprintf('平滑后路径节点数: %d\n', size(smoothed_path, 1));
        fprintf('平滑后路径长度: %.2f\n', calculate_path_length(smoothed_path));
    end
    
else
    fprintf('未找到路径!已达到最大迭代次数%d\n', params.max_iter);
    if params.visualize
        title('RRT路径规划失败');
    end
end

if params.visualize
    drawnow;
end
end

%% 辅助函数
function params = parse_params(varargin)
% 解析输入参数
p = inputParser;
addParameter(p, 'start', [10, 10]);
addParameter(p, 'goal', [90, 90]);
addParameter(p, 'map_width', 100);
addParameter(p, 'map_height', 100);
addParameter(p, 'max_iter', 5000);
addParameter(p, 'step_size', 5);
addParameter(p, 'goal_bias', 0.1);
addParameter(p, 'goal_radius', 5);
addParameter(p, 'visualize', true);
addParameter(p, 'obstacles', [30, 20, 20, 40; 60, 60, 15, 25; 20, 70, 25, 15; 70, 20, 15, 30]);

parse(p, varargin{:});
params = p.Results;
end

function collision = check_collision(p1, p2, obstacles)
% 检查线段与障碍物是否碰撞
collision = false;
num_points = 10;
for t = linspace(0, 1, num_points)
    point = p1 + t * (p2 - p1);
    
    for i = 1:size(obstacles, 1)
        obs = obstacles(i, :);
        if point(1) >= obs(1) && point(1) <= obs(1) + obs(3) && ...
           point(2) >= obs(2) && point(2) <= obs(2) + obs(4)
            collision = true;
            return;
        end
    end
end
end

function length = calculate_path_length(path)
% 计算路径长度
length = 0;
for i = 1:size(path, 1)-1
    length = length + norm(path(i+1, :) - path(i, :));
end
end

function smoothed_path = smooth_path(path, obstacles, visualize)
% 平滑路径
smoothed_path = path(1, :);
current_idx = 1;

while current_idx < size(path, 1)
    next_idx = size(path, 1);
    
    for test_idx = size(path, 1):-1:current_idx+1
        if ~check_collision(path(current_idx, :), path(test_idx, :), obstacles)
            next_idx = test_idx;
            break;
        end
    end
    
    smoothed_path = [smoothed_path; path(next_idx, :)];
    
    if visualize
        plot([path(current_idx, 1), path(next_idx, 1)], ...
             [path(current_idx, 2), path(next_idx, 2)], ...
             'm--', 'LineWidth', 2);
    end
    
    current_idx = next_idx;
end
end

这个RRT算法实现包含以下功能:

主要特性:

  1. 基本RRT算法:标准的快速探索随机树实现

  2. 目标偏置:通过goal_bias参数提高收敛速度

  3. 障碍物避碰:检查路径是否与矩形障碍物碰撞

  4. 可视化:实时显示树的生长过程

  5. 路径提取:找到目标后回溯生成完整路径

  6. 路径平滑:可选的后处理步骤,缩短路径

使用说明:

  1. 修改startgoal设置起点和终点

  2. 调整obstacles数组添加或修改障碍物

  3. 通过max_iter控制最大迭代次数

  4. step_size控制树的生长步长

  5. goal_bias控制直接向目标扩展的概率

运行结果:

  • 蓝色线条:RRT树的生长过程

  • 红色线条:找到的最终路径

  • 黄色点:路径节点

  • 紫色虚线:平滑后的路径(如果启用)

开始RRT路径规划...
在第144次迭代找到路径!
路径节点数: 28
路径长度: 135.00
平滑后路径节点数: 3
平滑后路径长度: 122.01

ans =

   10.0000   10.0000
   12.5998   14.2710
   17.4190   12.9387
   22.0416   14.8444
   26.2167   12.0933
   31.2150   11.9631
   36.1765   11.3439
   41.1575   10.9083
   44.9475   14.1695
   49.3802   16.4829
   51.7982   20.8593
   53.0981   25.6874
   54.6910   30.4269
   57.1897   34.7577
   57.6446   39.7370
   60.7246   43.6758
   63.3957   47.9025
   67.5643   50.6634
   71.0269   54.2704
   73.3719   58.6864
   77.4086   61.6368
   79.4374   66.2067
   78.5828   71.1331
   76.2139   75.5363
   80.4189   78.2415
   83.5773   82.1176
   86.7356   85.9938
   89.8940   89.8699

代码主要组成部分

1. 参数初始化部分

%% 1. 参数设置
map.width = 100;      % 地图尺寸
start = [10, 10];     % 起点
goal = [90, 90];      % 终点
goal_radius = 5;      % 目标容差半径
max_iter = 5000;      % 最大迭代次数
step_size = 5;        % 扩展步长
goal_bias = 0.1;      % 目标偏置概率
  • goal_bias:每次有10%的概率直接向目标点扩展,加速收敛

  • step_size:控制树的生长速度,步长过小效率低,过大可能错过狭窄通道

2. 数据结构初始化

%% 2. 初始化RRT树
tree.vertices = start;  % 存储所有节点坐标
tree.edges = [];        % 存储边(节点间连接)
tree.parents = 0;       % 记录每个节点的父节点索引

树结构包含:

  • vertices:所有节点的位置坐标

  • edges:节点之间的连接关系

  • parents:每个节点的父节点编号(用于回溯路径)

3. 主循环逻辑(核心算法)

步骤1:采样随机点

if rand() < goal_bias
    rand_point = goal;  % 有goal_bias概率直接采样目标点
else
    rand_point = [rand() * map.width, rand() * map.height];  % 随机采样
end
  • 目标偏置机制提高算法收敛速度

步骤2:寻找最近节点

distances = sum((tree.vertices - rand_point).^2, 2);
[~, nearest_idx] = min(distances);
nearest_point = tree.vertices(nearest_idx, :);
  • 计算随机点到树中所有节点的欧氏距离

  • 选择距离最近的节点作为生长起点

步骤3:向随机点方向扩展

direction = rand_point - nearest_point;
distance = norm(direction);
new_point = nearest_point + (direction / distance) * min(step_size, distance);
  • 计算方向向量

  • 从最近节点沿该方向移动step_size距离(或直接到达随机点)

步骤4:碰撞检测

if ~check_collision(nearest_point, new_point, obstacles)
    % 无碰撞,添加新节点
  • 调用check_collision函数检查路径段是否穿过障碍物

  • 无碰撞才添加新节点

步骤5:更新树结构

tree.vertices = [tree.vertices; new_point];
tree.edges = [tree.edges; nearest_idx, size(tree.vertices, 1)];
tree.parents = [tree.parents; nearest_idx];
  • 添加新节点坐标

  • 记录边(从最近节点到新节点)

  • 记录父节点索引

步骤6:检查是否到达目标

if norm(new_point - goal) <= goal_radius
    found_path = true;  % 找到路径
    break;
end
  • 新节点进入目标区域即视为成功

4. 路径回溯与提取

if found_path
    current_idx = size(tree.vertices, 1);  % 从终点开始
    
    while current_idx > 0
        path = [tree.vertices(current_idx, :); path];  % 添加到路径
        current_idx = tree.parents(current_idx);  % 回溯到父节点
    end
end
  • 从终点节点开始,沿着parents链回溯到起点

  • 逆序存储形成完整路径

5. 路径平滑处理

while current_idx < size(path, 1)
    next_idx = size(path, 1);
    
    for test_idx = size(path, 1):-1:current_idx+1
        if ~check_collision(path(current_idx, :), path(test_idx, :), obstacles)
            next_idx = test_idx;  % 找到可直达的最远点
            break;
        end
    end
    smoothed_path = [smoothed_path; path(next_idx, :)];
    current_idx = next_idx;
end
  • 贪心算法:尝试连接尽可能远的节点

  • 用直线替换折线,缩短路径长度

6. 辅助函数说明

碰撞检测函数

function collision = check_collision(p1, p2, obstacles)
% 在线段上均匀采样10个点
% 检查每个点是否在任意障碍物内
  • 将线段离散化为10个点

  • 逐个检查点是否在障碍物矩形内

路径长度计算

function length = calculate_path_length(path)
% 计算路径上相邻点的欧氏距离之和

算法流程图

开始
  ↓
初始化RRT树(起点为根节点)
  ↓
进入主循环(最多max_iter次):
  1. 采样随机点(有goal_bias概率直接采样目标点)
  2. 找到树中距离随机点最近的节点
  3. 从最近节点向随机点方向扩展step_size
  4. 碰撞检测:检查新线段是否与障碍物相交
  5. 无碰撞:添加新节点到树中
  6. 检查新节点是否在目标区域内
      是 → 跳出循环,找到路径
      否 → 继续下一次迭代
  ↓
路径找到? → 是 → 路径回溯
  ↓         ↓
  否        路径平滑
  ↓         ↓
失败报告  成功输出

关键参数调优建议

  1. step_size(步长)

    • 值大:探索快,但可能错过狭窄通道

    • 值小:路径精细,但搜索慢

    • 建议:地图尺寸的1/20到1/10

  2. goal_bias(目标偏置)

    • 值大:收敛快,但可能陷入局部最优

    • 值小:探索充分,但可能收敛慢

    • 建议:0.05-0.2

  3. max_iter(最大迭代次数)

    • 复杂环境需要更多迭代

    • 建议:1000-10000

  4. goal_radius(目标半径)

    • 值大:容易成功,但路径终点不精确

    • 值小:更精确,但更难成功

    • 建议:step_size的1-2倍

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐