路径规划是机器人导航、自动驾驶和工业自动化领域的核心技术之一。想象一下,当你需要让一台扫地机器人从客厅的角落移动到卧室时,它必须能够自主找到一条避开所有障碍物的最优路径。这正是RRT(快速扩展随机树)算法大显身手的地方。
与传统A*或Dijkstra算法不同,RRT特别适合解决高维空间中的复杂路径规划问题。它通过随机采样和树形扩展的方式,逐步探索环境空间,最终找到一条可行路径。这种方法的优势在于:
注意:RRT算法生成的路径通常不是最优路径,但一定能快速找到可行解。如果需要最优路径,可以考虑RRT*等改进算法。
RRT算法的核心思想可以类比为"盲人摸象"的过程:
matlab复制% 伪代码示例
function path = RRT(start, goal, map)
tree = initializeTree(start);
while notReachedGoal(tree, goal)
q_rand = randomSample();
q_near = findNearestNode(tree, q_rand);
q_new = extend(q_near, q_rand);
if noCollision(q_near, q_new, map)
addNode(tree, q_new);
if distance(q_new, goal) < threshold
path = extractPath(tree);
return;
end
end
end
end
在实际应用中,以下参数会显著影响算法性能:
| 参数 | 典型值 | 影响 | 调优建议 |
|---|---|---|---|
| 步长(Δq) | 地图尺寸的5-10% | 步长越大收敛越快,但容易碰撞 | 从较小值开始逐步增加 |
| 目标偏置 | 5-20% | 提高收敛速度 | 在复杂环境中适当增加 |
| 最大迭代次数 | 1000-5000 | 防止无限循环 | 根据地图复杂度调整 |
我在实际项目中发现的几个经验:
在Matlab中,我们通常用二维矩阵表示地图:
matlab复制% 创建100x100的空地图(0=自由空间,1=障碍物)
map = zeros(100,100);
% 添加矩形障碍物
map(30:70, 40:60) = 1;
% 可视化
imshow(~map, 'InitialMagnification', 'fit');
hold on;
plot(start(1), start(2), 'go'); % 起点绿色
plot(goal(1), goal(2), 'ro'); % 终点红色
以下是RRT核心函数的实现要点:
matlab复制function [tree, path] = buildRRT(start, goal, map, params)
% 初始化树结构
tree.nodes = start;
tree.edges = [];
tree.costs = 0;
for i = 1:params.maxIter
% 随机采样(带目标偏置)
if rand > params.goalBias
q_rand = [rand*size(map,2), rand*size(map,1)];
else
q_rand = goal;
end
% 寻找最近节点
[q_near, idx] = findNearestNode(tree, q_rand);
% 向随机点方向扩展
q_new = extend(q_near, q_rand, params.stepSize);
% 碰撞检测
if ~checkCollision(q_near, q_new, map)
% 添加新节点
tree.nodes = [tree.nodes; q_new];
tree.edges = [tree.edges; idx size(tree.nodes,1)];
tree.costs = [tree.costs; tree.costs(idx) + norm(q_new-q_near)];
% 检查是否到达目标
if norm(q_new - goal) < params.threshold
path = extractPath(tree, size(tree.nodes,1));
return;
end
end
end
path = []; % 未找到路径
end
matlab复制function [q_near, idx] = findNearestNode(tree, q_rand)
distances = sqrt(sum((tree.nodes - q_rand).^2, 2));
[~, idx] = min(distances);
q_near = tree.nodes(idx,:);
end
matlab复制function collision = checkCollision(q1, q2, map)
points = bresenham(q1, q2); % 获取两点间所有像素
for i = 1:size(points,1)
if map(points(i,2), points(i,1)) == 1
collision = true;
return;
end
end
collision = false;
end
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 算法无法找到路径 | 步长太大 | 减小stepSize参数 |
| 运行时间过长 | 地图太复杂 | 增加goalBias或maxIter |
| 路径过于曲折 | RRT固有特性 | 改用RRT*或进行路径平滑 |
| 穿过障碍物 | 碰撞检测不准确 | 检查bresenham实现 |
原始RRT生成的路径通常不够平滑,可以通过以下方法优化:
matlab复制function smoothPath = simplifyPath(path, map)
smoothPath = path(1,:);
current = 1;
for i = size(path,1):-1:1
if ~checkCollision(path(current,:), path(i,:), map)
smoothPath = [smoothPath; path(i,:)];
current = i;
end
end
end
matlab复制function smoothed = bsplineSmooth(path, degree)
t = linspace(0,1,size(path,1));
tt = linspace(0,1,10*size(path,1));
smoothed = [spline(t, path(:,1), tt)' spline(t, path(:,2), tt)'];
end
code复制rrt_planner/
├── main.m % 主脚本
├── buildRRT.m % RRT核心算法
├── checkCollision.m % 碰撞检测
├── findNearestNode.m % 最近节点查找
├── simplifyPath.m % 路径简化
├── bsplineSmooth.m % 路径平滑
└── utils/
├── bresenham.m % 直线绘制算法
└── visualize.m % 可视化工具
matlab复制% 1. 创建地图
map = zeros(100,100);
map(30:70, 40:60) = 1; % 中央障碍物
% 2. 设置起点和终点
start = [10,10];
goal = [90,90];
% 3. 设置算法参数
params.stepSize = 5;
params.maxIter = 2000;
params.goalBias = 0.1;
params.threshold = 10;
% 4. 运行RRT算法
[tree, path] = buildRRT(start, goal, map, params);
% 5. 路径后处理
if ~isempty(path)
simplePath = simplifyPath(path, map);
smoothPath = bsplineSmooth(simplePath, 3);
% 可视化
visualize(map, start, goal, tree, path, simplePath, smoothPath);
else
disp('未找到可行路径!');
end
为了更直观地展示算法运行过程,可以添加动态可视化:
matlab复制function visualize(map, start, goal, tree, varargin)
figure;
imshow(~map, 'InitialMagnification', 'fit');
hold on;
plot(start(1), start(2), 'go', 'MarkerSize',10, 'LineWidth',2);
plot(goal(1), goal(2), 'ro', 'MarkerSize',10, 'LineWidth',2);
% 绘制树结构
for i = 1:size(tree.edges,1)
plot([tree.nodes(tree.edges(i,1),1), tree.nodes(tree.edges(i,2),1)],...
[tree.nodes(tree.edges(i,1),2), tree.nodes(tree.edges(i,2),2)],...
'b', 'LineWidth',0.5);
end
% 绘制不同路径
colors = {'g', 'm', 'c'};
for i = 1:length(varargin)
path = varargin{i};
plot(path(:,1), path(:,2), colors{i}, 'LineWidth',2);
end
legend('起点','终点','树结构','原始路径','简化路径','平滑路径');
end
RRT*在RRT基础上增加了重布线优化,能渐进趋近最优路径。关键改进点:
matlab复制% 在添加新节点后,增加以下步骤:
nearNodes = findNearNodes(tree, q_new, params.searchRadius);
minCost = tree.costs(end);
minParent = size(tree.nodes,1)-1;
% 寻找最优父节点
for i = 1:length(nearNodes)
cost = tree.costs(nearNodes(i)) + norm(q_new - tree.nodes(nearNodes(i),:));
if cost < minCost && ~checkCollision(tree.nodes(nearNodes(i),:), q_new, map)
minCost = cost;
minParent = nearNodes(i);
end
end
% 更新父节点
if minParent ~= size(tree.nodes,1)-1
tree.edges(end,:) = [minParent, size(tree.nodes,1)];
tree.costs(end) = minCost;
end
% 重布线附近节点
for i = 1:length(nearNodes)
cost = tree.costs(end) + norm(tree.nodes(nearNodes(i),:) - q_new);
if cost < tree.costs(nearNodes(i)) && ~checkCollision(q_new, tree.nodes(nearNodes(i),:), map)
% 更新父节点和代价
tree.edges(tree.edges(:,2)==nearNodes(i),1) = size(tree.nodes,1);
updateNodeCost(tree, nearNodes(i), cost);
end
end
对于动态环境,可以采用以下策略:
matlab复制function [tree, path] = dynamicRRT(tree, goal, map, params)
% 检查当前路径是否仍然有效
if ~isPathValid(path, map)
% 保留有效部分树结构
validNodes = findValidNodes(tree, map);
% 从最后一个有效节点重新扩展
[tree, path] = regrowRRT(tree, validNodes(end), goal, map, params);
end
% ...其余处理逻辑不变
end
当多个机器人共享同一环境时,可以:
matlab复制function paths = multiRobotRRT(starts, goals, map, params)
paths = cell(length(starts),1);
obstaclePaths = cell(length(starts),1);
for i = 1:length(starts)
% 将其他机器人的路径作为障碍物
tempMap = addDynamicObstacles(map, obstaclePaths);
% 为当前机器人规划路径
[tree, path] = buildRRT(starts(i), goals(i), tempMap, params);
paths{i} = path;
obstaclePaths{i} = path;
end
end
在实现RRT算法时,最耗时的部分往往是碰撞检测。在我的一个实际项目中,通过以下优化将运行时间缩短了60%:
matlab复制% KD-tree加速示例
function [q_near, idx] = findNearestNode(tree, q_rand)
persistent kdtree;
if isempty(kdtree) || size(kdtree.X,1) ~= size(tree.nodes,1)
kdtree = KDTreeSearcher(tree.nodes);
end
[idx, ~] = knnsearch(kdtree, q_rand);
q_near = tree.nodes(idx,:);
end