自动驾驶路径规划算法学习-RRT算法及matlab实现

自动驾驶路径规划算法学习-RRT算法及matlab实现

参考 手把手教用matlab做无人驾驶(六)-路径规划RRT

        RRT路径规划算法在二维仿真环境中的应用 -- Python代码实现

        RRT路径规划算法在二维仿真环境中的应用 -- Python代码实现

        RRT 算法原理以及过程演示

RRT快速随机数算法 Rapid Random Tree

是基于采样的规划方法的一种。

快速搜索随机树,就是在环境中随机撒一些点,这些点经过算法运算,最终可以连接起来,变成车辆可以运行的轨迹。

1.算法原理

RRT 适用于涉及非完整约束场合下的路径规划问题。

过程中,算法不断在搜索空间中随机生成状态点,如果该点位于无碰撞位置,则寻找搜索树中离该节点最近的结点为基准结点,由基准结点出发以一定步长朝着该随机结点进行延伸,延伸线的终点所在的位置被当做有效结点加入搜索树中。这个搜索树的生长过程一直持续,直到目标结点与搜索树的距离在一定范围以内时终止。随后搜索算法在搜索树中寻找一条连接起点到终点的最短路径。
计算实例参考 RRT 算法原理以及过程演示,这篇博客讲的非常清楚,如下:

1.1计算实例

Step 1.初始化一个环境,包括地图,起点,终点。如下图所示,黑色物体为障碍物,蓝色飞机位于起点位置,红色五角星为目标终点位置

Step2:从环境中随机采样状态点,如下图所示,采样点为 Xrand

Step3: 从所构建的树中寻找距离采样点 Xrand最近的结点 Xnear。现在树中只有起点一个结点,所有最近的结点就是起点

Step4:开始树的生长过程。首先连接 Xnear 和 Xrand连接起来,这个连接线的方向就是树生长的方向。设置一个步长 Stepsize作为树一次生长的步长,在树生长的这个方向上生长一个步长,然后就会在生长的末端会产生一个新的结点 Xnew

Step5:判断从 Xnear 到 Xrand是否穿过障碍物,如果穿过,则放弃该新的结点,如果没有,则将 Xnew 结点加入到树中。

Step6:从步骤 2 开始再循环执行,从环境中随机采样状态点。

.........

重复上述树的生长过程,直到树新生成的结点到目标点的距离小于一个步长,则终止树的生长。直接将该新结点与目标点相连。

整个步骤动图如下:

1.2算法伪代码

概述之:

输入地图,起点,终点→起点先加入树节点nodes表→在地图随机采样一个点xrand(同时保证有一定的概率会选择到目标点,保证有节点会向着目标点的方向扩展)→找到树节点中离xrand最近的点xnearest→xnearest朝着xrand前进一个步长得到新的点xnew→判断xnearest到xnew连线进行碰撞检测,若有碰撞则放弃该节点重新选择,若无碰撞则将该节点加入树节点→重复xnew的扩展过程直到扩展的xnew在目标点附近。

2.算法Matlab实现

这里只介绍了关键代码的实现,非完整程序,完整代码链接附在文末。

2.1 二维环境的搭建  CreateMap.m

CreateMap.m的主要作用输入起点终点障碍物等信息,障碍物是一一个个实心圆表示。绘制起点终点障碍物信息,代码如下:

%CreateMap.m
start = [0,0];
goal = [10,14];
%障碍物(x,y,radiu)
obstacle_list=[3,3,1.5;
               12,2,3;
               3,9,2;
               9,11,2];
%画出地图框及障碍物           
axis([-2,18,-2,15])
hold on
for i=1:length(obstacle_list(:,1))
%调用画障碍物函数
plot_obstacle(obstacle_list(i,1),obstacle_list(i,2),obstacle_list(i,3))
end
plot(start(1),start(2),'og')
hold on
plot(goal(1),goal(2),'or')
hold on

2.2 随机采样

隶属于RRT算法程序RRT_planning.m的一部分。

在状态空间中随机采样p_rand(这里采样的是一个坐标点), 有一定的概率采样到目标点,确保路径能以一定的概率向着目标点前进。这里随机采样取得目标点的概率是0.3,这个参数越大,越快找到路径,但障碍物较多时可能反而要耗费更多时间绕开。

%随机采样取得目标点的概率是0.3,这个参数越大,越快找到路径,但障碍物较多时可能反而要耗费更多时间绕开
p=0.3
%在环境中采样p_rand,p_int是start
p_randx = randi(16)-1;  %x随机采样范围0-15
p_randy = randi(13)-1;  %x随机采样范围0-12
p_rand=[p_randx,p_randy];
%一定概率采样点取得目标点
if rand(1)<p
    p_rand=goal;
end

2.3 FindNearest.m

从节点树中找到距随机采样点p_rand最近的节点p_nearest的程序FindNearest.m程序如下:

这里有一个要注意的细节,运行时出错,因为nodes节点树很可能存在好几个点同时到p_rand随机采样点距离最小,这里设置的返回值必须只有一个,如果有多个最近点,只取第一个返回。

function minID=FindNearest(p_rand,nodes)
%dist矩阵存放p_rand到nodes节点每个节点的距离
%nodes的节点数
nodes_num = length(nodes(:,1));
prand_matx=ones(nodes_num,1)*p_rand(1);
prand_maty=ones(nodes_num,1)*p_rand(2);
nodes_matx=nodes(:,1);
nodes_maty=nodes(:,2);
dist=((prand_matx-nodes_matx).^2+(prand_maty-nodes_maty).^2).^0.5;
minID=find(dist==min(dist));
minID=minID(1);  %万一有多个同样小的,只取其中一个
end

2.4 扩展新节点

最近点朝着随机点走一个步长得到新节点。

先求出随机点p_rand和最近点p_nearest连线与x轴所成角theta,然后计算pnew新节点,代码如下:

%随机点和树中最近点连线与x轴夹角
theta = atan2(p_rand(2)-p_nearest(2),p_rand(1)-p_nearest(1));
%计算新节点
pnew(1)= p_nearest(1)+ RRT_stepsize*cos(theta);
pnew(2)= p_nearest(2)+ RRT_stepsize*sin(theta);
%看该随机点是否已在随机树nodes中,是的话重新选择,防止pnew在nodes里出现两次
father=FindFather(pnew, nodes);
if ~isempty(father)   %如果father非空说明能找到父节点说明在nodes里,重复了,避免出错
    continue
end

特别注意,我在跑程序时开始跑很多次总有一两次会陷入死循环,怎么都找不到错误所在,后来发现是在回溯路径时出现了两个节点互为父节点father的情况,原因是在扩展新节点pnew时,没有判断pnew是否已经在nodes树节点中了,如果已经在nodes树节点中,就不应再作为新的扩展点,本次随机采样放弃,进入下一次随机采样。下面用图说明:

假设

P2是由父节点P0扩展出;P1是由父节点P2扩展出;此时新一次的扩展P1扩展出的pnew正好是P2

我们程序里树节点存放在nodes中,是一层层往上堆的,后扩展的放在上面,但是在nodes中找父节点时又是从上往下,则会出现下图的情况

对扩展的新节点判断是否已经在nodes树节点中,若是则放弃本次采样,pnew也不加入树节点nodes中,就不会陷入死循环:

2.5 碰撞检测 collision_check.m

计算障碍物圆心o到线段p_nearest-pnew的最短距离是否小于半径,是则会发生碰撞。

障碍物的圆心o和线段p_nearest-pnew的距离计算三种情况(垂点在线段之间,垂点在线段下方,垂点在线段上方):

点到线段最短距离d的计算方法为点到直线的距离

点到线段最短距离d的计算方法为圆心o到p_nearest的距离

点到线段最短距离d的计算方法为圆心o到p_new的距离

点到线段最短距离的实现封装为distance_squared_point_to_segment.m,其返回值为最短距离的平方,其代码如下:

程序中  x1--p_nearest, x2--pnew, x3--圆心

向量(x3-x1)乘向量(x2-x1)求到O-pnearest在pnearest-pnew上的投影,投影/l2求到垂足在线段长度中的百分比,可能超过1或为负数。超过1时,最短距离取x3x2,小于0时距离取x3x1

distance_squared_point_to_segment.m

function dd=distance_squared_point_to_segment(x1,x2,x3)
%""" 计算线段 vw 和 点 p 之间的最短距离""",x1 near v; x2 new w; x3 obstacle_圆心 p
%点 v 和 点 w 重合的情况
if isequal(x1,x2)
    dd=(x3(1)-x1(1))^2+(x3(2)-x1(2))^2;
    return
end
%线段 vw 长度的平方
l2=(x2(1)-x1(1))^2+(x2(2)-x1(2))^2;
t = max(0, min(1, (x3 - x1)*(x2 - x1)' / l2));   %向量(x3-x1)乘向量(x2-x1)求到O-pnearest在pnearest-pnew上的投影,投影/l2求导垂足在线段长度中的百分比,可能超过1或为负数。超过1时,最短距离取x3x2,小于0时距离取x3x1
projection = x1 + t * (x2 - x1);
dd = (x3 - projection)*(x3 - projection)';
end

整个碰撞检测函数collision_check.m代码如下:

function collisionflag=collision_check(pnew,p_nearest,obstacle_list)
collisionflag=0;
for i=1:length(obstacle_list(:,1))
x0=p_nearest;
x1=pnew;
x2=[obstacle_list(i,1),obstacle_list(i,2)];
dd = distance_squared_point_to_segment(x0,x1,x2);
if dd<(obstacle_list(i,3))^2   %%距离小于障碍物半径
    collisionflag=1;
    return
end
end

如果发生碰撞,就放弃本次采样,直接进入下一次

如果没有发生碰撞,计算出的新节点pnew加入节点树nodes,并在nodes存入pnew父节点为p_nearest

2.6 判断是否到达目标点is_near_goal.m

判断的原理就是计算通过碰撞检测的pnew新扩展节点到目标点距离是否小于一个步长RRT_stepsize,是的话,

则说明达到,并将目标点加入节点树nodes,记录其父节点为此时的pnew,实现代码如下:

function goalflag=is_near_goal(pnew,goal,RRT_stepsize)
goalflag=0;
dist=pdist([pnew;goal],'euclidean');
if dist<RRT_stepsize
    goalflag=1;
    return
end
end

3. 运行结果

RRT算法的几个可调节参数   步长RRT_stepsize,随机采样取得目标点概率p

RRT_stepsize越大,计算路径的速度越快,当步长过大可能来回震荡往复无法达到目标点附近;

p越大,计算路径的速度越快,节点更快向目标点生长,但p过大时,遇到障碍物时要花费更多的时间才能绕开,反而使搜索速度下降。

从运行结果来看,搜索到的并非最优路径,可以了解RRT相关的改进算法如RRT*等,考虑到路径代价的优化。

运行视频:https://www.bilibili.com/video/BV1wK4y1R7H7/

运行代码:https://download.csdn.net/download/weixin_39199083/18932919?spm=1001.2014.3001.5501

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiangzhu_xjtu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值