双向快速扩展随机树(Bi-RRT)算法是机器人路径规划领域的经典方法,特别适合解决复杂环境下的高维空间搜索问题。我在机器人导航项目实践中发现,相比传统的单向RRT算法,Bi-RRT通过从起点和终点同时构建搜索树的方式,能够显著提高路径搜索效率。本文将以MATLAB实现为例,详细解析2D环境下基于Bi-RRT的路径规划实现过程。
这个算法实现主要解决三个核心问题:首先是如何在包含障碍物的环境中进行有效搜索;其次是如何确保生成的路径无碰撞;最后是如何优化算法参数以获得更好的性能表现。对于机器人开发者、自动化专业学生以及算法研究者来说,理解Bi-RRT的实现细节对开发实际的导航系统很有帮助。
快速扩展随机树(RRT)算法的核心思想是通过随机采样和树形扩展来探索构型空间。其基本流程包括:
在MATLAB实现中,我们使用Node类来表示树节点,每个节点保存其坐标和父节点索引,便于最终路径回溯。
Bi-RRT算法通过同时从起点和终点构建两棵搜索树,显著提高了搜索效率。其优势主要体现在:
在代码实现中,我们设置了mi参数(通常取值0.3-0.5)来控制目标偏向概率,即有一定概率直接向另一棵树的方向扩展,而不是完全随机采样。这种启发式策略能有效加速两棵树的连接。
提示:实际项目中,mi值需要根据环境复杂度调整。简单环境可取较小值(0.1-0.3),复杂环境建议取0.4-0.6。
代码中使用make2Dobstacles()函数创建2D障碍物环境,其核心设计要点包括:
matlab复制function obstacles = make2Dobstacles()
% 矩形障碍物表示为[x,y,width,height]的矩阵
obstacles = [
20, 30, 40, 10; % 横置长条形障碍物
60, 20, 10, 40; % 竖置长条形障碍物
35, 60, 30, 15 % 斜向障碍物
];
end
碰撞检测采用线段与矩形相交判断,核心代码如下:
matlab复制function collision = checkCollision(p1, p2, obstacles)
collision = false;
for i = 1:size(obstacles,1)
obs = obstacles(i,:);
% 将矩形分解为四条边进行线段相交检测
if lineRectIntersect(p1, p2, obs)
collision = true;
return;
end
end
end
双向RRT的核心在于两棵树的交替生长和连接判断:
matlab复制while iter < maxNodeNum
% 交替生长两棵树
if mod(iter,2) == 0
[G1, newNode] = extendTree(G1, G2, mi, deltaQ, obstacles);
if isConnected(newNode, G2.nodes, deltaQ)
% 找到路径,构建完整路径
path = extractPath(G1, G2, newNode);
break;
end
else
[G2, newNode] = extendTree(G2, G1, mi, deltaQ, obstacles);
if isConnected(newNode, G1.nodes, deltaQ)
% 找到路径,构建完整路径
path = extractPath(G1, G2, newNode);
break;
end
end
iter = iter + 1;
end
extendTree函数实现了带目标偏向的树扩展:
matlab复制function [tree, newNode] = extendTree(tree, otherTree, mi, deltaQ, obstacles)
if rand() < mi
% 目标偏向:向另一棵树的随机节点生长
randNode = otherTree.nodes(randi(length(otherTree.nodes)));
q_rand = randNode.pos;
else
% 随机采样
q_rand = rand(1,2) * 100; % 假设环境大小为100x100
end
% 寻找最近邻并扩展
nearestNode = findNearest(tree.nodes, q_rand);
q_new = steer(nearestNode.pos, q_rand, deltaQ);
% 碰撞检测
if ~checkCollision(nearestNode.pos, q_new, obstacles)
newNode = Node(q_new, length(tree.nodes)+1);
newNode.parent = nearestNode.id;
tree.nodes = [tree.nodes, newNode];
else
newNode = [];
end
end
当两棵树连接成功后,需要从连接点分别回溯到起点和终点,拼接成完整路径:
matlab复制function path = extractPath(G1, G2, connectionNode)
% 从连接节点回溯到G1的根节点
path1 = [];
node = connectionNode;
while ~isempty(node)
path1 = [node.pos; path1];
if node.parent ~= 0
node = G1.nodes(node.parent);
else
node = [];
end
end
% 从连接节点回溯到G2的根节点
path2 = [];
node = findNodeInTree(G2, connectionNode.pos);
while ~isempty(node)
if node.parent ~= 0
path2 = [path2; node.pos];
node = G2.nodes(node.parent);
else
node = [];
end
end
path = [path1; path2];
end
deltaQ(步长):
maxNodeNum(最大节点数):
mi(目标偏向概率):
问题1:算法无法找到路径
问题2:路径不够平滑
问题3:运行时间过长
注意:在MATLAB中实现时,向量化操作能显著提高性能。避免在循环中进行大量单个节点的处理。
RRT*在基础RRT上引入了重布线和最优父节点选择机制,能渐进获得最优路径:
matlab复制function tree = rewire(tree, newNode, radius, obstacles)
nearNodes = findNearNodes(tree, newNode.pos, radius);
for i = 1:length(nearNodes)
node = nearNodes(i);
% 检查是否可以通过newNode获得更短路径
newCost = node.cost + norm(node.pos - newNode.pos);
if newCost < node.cost && ~checkCollision(node.pos, newNode.pos, obstacles)
% 重布线
node.parent = newNode.id;
node.cost = newCost;
end
end
end
对于动态障碍物环境,可以引入以下改进:
将算法扩展到三维空间需要考虑:
在实际项目中实现Bi-RRT算法时,建议采用以下模块化结构:
matlab复制% 初始化环境
obstacles = make2Dobstacles();
start = [0, 0]; goal = [80, 80];
% 算法参数
params.deltaQ = 5;
params.maxNodeNum = 2000;
params.mi = 0.3;
params.goalTolerance = 3;
% 运行Bi-RRT
[path, G1, G2] = biRRT(start, goal, obstacles, params);
% 可视化
plotEnvironment(obstacles);
plotTree(G1, 'b'); plotTree(G2, 'g');
plotPath(path, 'r', 2);
matlab复制classdef Node
properties
pos % 节点位置[x,y]
id % 节点ID
parent % 父节点ID
cost % 从根节点到该节点的代价(RRT*使用)
end
end
classdef Tree
properties
nodes % 节点集合
rootId % 根节点ID
end
end
在实际应用中,我发现以下几个经验点特别值得注意:
碰撞检测函数通常是性能瓶颈,应该优先优化。可以考虑使用空间划分数据结构(如四叉树)来加速。
对于重复规划场景(如机器人SLAM),可以缓存部分搜索树来加速后续规划。
MATLAB的面向对象编程虽然方便,但在处理大量节点时可能效率不高。对于性能关键部分,可以考虑使用结构体数组代替对象。
在复杂环境中,将Bi-RRT与局部规划器(如DWA)结合使用效果更好 - Bi-RRT提供全局路径,局部规划器处理动态障碍。