从RRT到RRT*,再到Informed RRT*,路径规划算法怎么写

从RRT到RRT,再到Informed RRT,路径规划算法怎么写

做个正直的人

RRT中文名字是”快速搜索随机树”(Rapidly-exploring Random Tree),是一种比较常用的基于采样的静态路径规划算法。

我们可以这么理解:小明住在北京,小红住在南京,有一天小红给小明发了一条短信,短信上说只要小明从北京来南京见小红,小红就做小明的女朋友。单身青年小明一看给激动坏了,当即收拾东西要来南京。

1、RRT算法

1.1 假设

为了说明RRT算法,我们作出如下假设:

  • 小明不知道从北京直达南京的路线;
  • 在每一个城市,只能够获得到达相邻城市的路线,而不能到达不相邻的城市;
  • 小明看不懂地图,只有一个列出了中国所有城市的城市清单。

1.2 RRT算法步骤与实现

小明的舍友甲为小明提出了一种找到从北京到达南京的算法——RRT算法。
小明只能拿着这个清单,重复如下过程:

    (1)随机选择一个城市,
    (2)看看这个城市是不是可达,
    (3)离南京(目标位置)多远,如果到达目标位置就终止算法
    (4)在已经选择过得城市中哪一个离它最近,就把那个城市设置为他的父节点。

不断的重复这个过程,这一个随机树就会不断的生长,直到到达目标位置。从下图可以看出经过许许多多次随机选择之后,目标城市就会变成这个随机树的叶节点,也就是说,小明就能找到一条从北京到南京的可行路径(仅仅是可行而已,很大概率上不会是最优)。

从RRT到RRT*,再到Informed RRT*,路径规划算法怎么写
比如上面这幅图就是小明使用RRT算法找到的一条从北京到南京的路线。

1.3 伪代码

我们可以把RRT算法用伪代码的形式写出来,我认为伪代码用英语描述能够更加准确精炼的表达算法的步骤和精髓,所以我用英语来写伪代码:

//RRT算法伪代码——作者黄唐,2021.4.9于南京//initialization
Obtain map, 0 stands for free space and 1 obstacle.

Set start and goal in this map .
Set step_size which means how long we will search from the closest pos to the
random pos obtained from sampling.(Why we need to set a step_size? Because the sample may be far away from our tree and we don't want to produce a very long edge.)
Set max iteration that means how many times we crop our tree.

Set current iteration as 0.

Set a flag which flags whether we have reached the goal.

Set goal_radius, if the distance from new_point to the goal is less than.

goal_radius we think we have reached the goal.

If we don't set this goal_radius we may never reach the goal because the
probability that we sample the goal is 0.

//make plan
We need a vector to memorize the position the we have detected
Push the start into the vector and set its parent as -1 and its index 0 which
means it is the root of our tree while(we haven't reached the goal and the max iteration){
    Sample in the map and get a random pos as random_point
    Get the closest pos as closest_point in the vector(tree) whose distance to
    random_point is min among all members in the vector
    Move from the closest_point to the random_point and produce a new point as
    new_point in this edge whose distance to closest_point is step_size
    if (the edge from closest_point to new_point is safe which means there is no
        collision with any obstacle){
        Push the new_point into the vector and set its parent as closest_point
        current iteration ++
        if (the distance from new_point to goal < goal_radius){
            if (the edge from new_point to goal is safe){
                flag =true
                Set new_point as parent of the goal
            }
        }
        //RRT*&#x7B97;&#x6CD5;&#x7684;rewrite&#x548C;relink&#x5199;&#x5728;&#x8FD9;&#x91CC;&#x54DF;
     }
     else{
        Abandon this sample and continue
     }
}
return the index of new_point as the parent of the goal

//build plan
if (the index of parent of the goal>=0){
    push the goal at the front of the path
    current_parent =the index of parent of the goal
     while(the index of current_parent>0){
        Push the current_parent at the front of the path
        current_parent= index of parent of current_parent
     }
    Push start at the front of the path
    return path
}
else {
    We can't obtain a path from start to goal
    return -1
}

懒得看英语的可以直接看中文的伪代码:

//&#x521D;&#x59CB;&#x5316;
&#x62FF;&#x5230;&#x4E00;&#x5E45;&#x5730;&#x56FE;&#xFF0C;0&#x8868;&#x793A;&#x81EA;&#x7531;&#x79FB;&#x52A8;&#x7684;&#x7A7A;&#x95F4;&#xFF0C;1&#x8868;&#x793A;&#x969C;&#x788D;&#x7269;
&#x8BBE;&#x5B9A;&#x521D;&#x59CB;&#x4F4D;&#x7F6E;start&#x548C;&#x76EE;&#x6807;&#x4F4D;&#x7F6E;goal
&#x8BBE;&#x5B9A;&#x751F;&#x957F;&#x6B65;&#x957F;step_size&#xFF0C;&#x4E3A;&#x4EC0;&#x4E48;&#x8981;&#x8BBE;&#x7F6E;&#x8FD9;&#x4E48;&#x4E2A;&#x4E1C;&#x897F;&#x5462;&#xFF1F;&#x6BD4;&#x5982;&#x8BF4;&#x91C7;&#x6837;&#x70B9;&#x79BB;&#x6211;&#x4EEC;&#x73B0;&#x6709;&#x7684;&#x6811;&#x5B9E;&#x5728;&#x662F;&#x592A;&#x8FDC;&#x4E86;&#xFF0C;&#x76F4;&#x63A5;&#x628A;&#x8FD9;
&#x4E00;&#x4E2A;&#x70B9;&#x52A0;&#x5165;&#x5230;&#x6811;&#x4E2D;&#x5C31;&#x4F1A;&#x4EA7;&#x751F;&#x4E00;&#x6761;&#x7279;&#x522B;&#x957F;&#x7684;&#x8FB9;&#xFF0C;&#x6211;&#x4EEC;&#x53EF;&#x4E0D;&#x60F3;&#x6709;&#x8FD9;&#x4E48;&#x957F;&#x7684;&#x8FB9;
&#x8BBE;&#x5B9A;&#x4E00;&#x4E2A;&#x6807;&#x5FD7;flag&#xFF0C;&#x5982;&#x679C;&#x6211;&#x4EEC;&#x5230;&#x8FBE;&#x4E86;&#x76EE;&#x6807;&#x4F4D;&#x7F6E;&#x5C31;&#x8BBE;&#x7F6E;&#x5B83;&#x4E3A;true&#xFF0C;&#x6211;&#x4EEC;&#x5C31;&#x7EC8;&#x6B62;&#x5FAA;&#x73AF;&#xFF0C;&#x4E0D;&#x518D;&#x7EE7;&#x7EED;&#x641C;&#x7D22;&#x8DEF;&#x5F84;
&#x8BBE;&#x7F6E;&#x4E00;&#x4E2A;&#x53D8;&#x91CF;goal_radius&#x6765;&#x5F62;&#x6210;&#x4E00;&#x4E2A;&#x4EE5;goal&#x4E3A;&#x5706;&#x5FC3;&#xFF0C;goal_radius&#x4E3A;&#x534A;&#x5F84;&#x7684;&#x5706;&#xFF08;&#x4E09;&#x7EF4;&#x7A7A;&#x95F4;&#x5C31;&#x662F;&#x7403;&#xFF0C;&#x66F4;&#x9AD8;&#x7EF4;&#x5C31;
&#x662F;&#x8D85;&#x7403;&#xFF09;&#xFF0C;&#x56E0;&#x4E3A;&#x5982;&#x679C;&#x6211;&#x4EEC;&#x4E0D;&#x8BBE;&#x7F6E;&#x8FD9;&#x4E2A;&#x4E1C;&#x897F;&#xFF0C;&#x6211;&#x4EEC;&#x7684;&#x7B97;&#x6CD5;&#x53EF;&#x80FD;&#x6C38;&#x8FDC;&#x4E5F;&#x4E0D;&#x4F1A;&#x627E;&#x5230;&#x5230;&#x8FBE;&#x76EE;&#x6807;&#x7684;&#x8DEF;&#x5F84;&#xFF0C;&#x56E0;&#x4E3A;&#x5728;&#x5E73;&#x9762;&#x4E0A;&#x91C7;&#x6837;&#x5230;
&#x4E00;&#x4E2A;&#x70B9;&#x7684;&#x6982;&#x7387;&#x662F;0&#x3002;

//makeplan
&#x58F0;&#x660E;&#x4E00;&#x4E2A;&#x5411;&#x91CF;vector&#x6765;&#x5B58;&#x50A8;&#x6211;&#x4EEC;&#x7684;&#x6811;&#x4E2D;&#x5F53;&#x524D;&#x6709;&#x54EA;&#x4E9B;&#x8282;&#x70B9;&#xFF0C;&#x4EE5;&#x53CA;&#x8FD9;&#x4E9B;&#x8282;&#x70B9;&#x7684;&#x4F4D;&#x7F6E;&#x3001;&#x7D22;&#x5F15;&#x548C;&#x7236;&#x8282;&#x70B9;
&#x628A;&#x8D77;&#x59CB;&#x4F4D;&#x7F6E;&#x538B;&#x5165;vector&#x4E2D;&#x4F5C;&#x4E3A;RRT&#x7684;&#x6839;&#x8282;&#x70B9;
&#x628A;&#x76EE;&#x6807;&#x4F4D;&#x7F6E;&#x7684;&#x7236;&#x8282;&#x70B9;&#x7D22;&#x5F15;parent_of_goal&#x8BBE;&#x7F6E;&#x4E3A;-1
while(flag!=true && &#x672A;&#x8FBE;&#x5230;&#x6700;&#x5927;&#x8FED;&#x4EE3;&#x6B21;&#x6570;){
    &#x968F;&#x673A;&#x91C7;&#x6837;&#x4E00;&#x4E2A;&#x70B9;&#x4F5C;&#x4E3A;random_point
    &#x904D;&#x5386;RRT&#xFF08;&#x4E5F;&#x5C31;&#x662F;vector&#xFF09;&#x627E;&#x5230;&#x8DDD;&#x79BB;&#x91C7;&#x6837;&#x70B9;&#x6700;&#x8FD1;&#x7684;&#x90A3;&#x4E2A;&#x8282;&#x70B9;&#xFF0C;&#x8BB0;&#x4E3A;closest_point
    &#x4ECE;closest_point&#x5411;random_point&#x641C;&#x7D22;step_size&#x8DDD;&#x79BB;&#xFF0C;&#x5C06;&#x8FD9;&#x4E00;&#x70B9;&#x8BB0;&#x4E3A;new_point
    &#x68C0;&#x67E5;&#x4ECE;closest_point&#x5230;new_point&#x8FD9;&#x4E00;&#x6BB5;&#x8DEF;&#x5F84;&#x662F;&#x4E0D;&#x662F;&#x5B89;&#x5168;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;&#x4F1A;&#x4E0D;&#x4F1A;&#x54C8;&#x969C;&#x788D;&#x7269;&#x53D1;&#x751F;&#x78B0;&#x649E;
    if(&#x4E0D;&#x4F1A;&#x53D1;&#x751F;&#x78B0;&#x649E;){
        &#x5C06;&#x8FD9;&#x4E00;&#x4E2A;&#x65B0;&#x8282;&#x70B9;new_point&#x538B;&#x5165;&#x5230;RRT&#x4E2D;&#xFF0C;&#x5E76;&#x5C06;&#x4ED6;&#x7684;&#x7236;&#x8282;&#x70B9;&#x8BBE;&#x7F6E;&#x4E3A;closest_point
        &#x8FED;&#x4EE3;&#x6B21;&#x6570;++
        &#x68C0;&#x67E5;&#x8FD9;&#x4E00;&#x4E2A;&#x65B0;&#x8282;&#x70B9;&#x662F;&#x4E0D;&#x662F;&#x4F4D;&#x4E8E;&#x4EE5;goal&#x4E3A;&#x5706;&#x5FC3;&#x7684;&#x5706;&#x5185;
        if(&#x5728;&#x5706;&#x5185;){
            &#x68C0;&#x67E5;new_point&#x5230;goal&#x662F;&#x4E0D;&#x662F;&#x5B89;&#x5168;
            if(&#x5B89;&#x5168;){
                &#x8BBE;&#x7F6E;flag&#x4E3A;true&#xFF0C;&#x8868;&#x793A;&#x6211;&#x4EEC;&#x5230;&#x8FBE;&#x76EE;&#x6807;&#x533A;&#x57DF;&#x4E86;
                &#x8BBE;&#x7F6E;parent_of_goal&#x4E3A;new_point&#x7684;&#x7D22;&#x5F15;
            }
            else{
                do nothing
            }
        }
        //RRT*&#x7684;rewrite&#x548C;relink&#x5199;&#x5728;&#x8FD9;&#x91CC;&#x54DF;
    }
    else{
        &#x4E0D;&#x5B89;&#x5168;&#xFF0C;&#x820D;&#x5F03;&#x8FD9;&#x4E00;&#x4E2A;&#x70B9;&#xFF0C;&#x4ECE;&#x65B0;&#x91C7;&#x6837;
    }
}
return parent_of_goal

//&#x751F;&#x6210;&#x8DEF;&#x5F84;
if(parent_of_goal >0){
    &#x4ECE;goal&#x4E0D;&#x65AD;&#x7684;&#x5411;&#x7236;&#x8282;&#x70B9;&#x56DE;&#x6EAF;&#xFF0C;&#x76F4;&#x5230;&#x56DE;&#x5230;&#x6839;&#x8282;&#x70B9;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;start&#xFF0C;&#x8FD9;&#x4E00;&#x6761;&#x56DE;&#x6EAF;&#x8DEF;&#x5F84;&#x5C31;&#x662F;RRT&#x7B97;&#x6CD5;&#x627E;&#x5230;&#x7684;&#x8DEF;&#x5F84;
}
else{
    &#x6CA1;&#x6709;&#x627E;&#x5230;&#x8DEF;&#x5F84;&#xFF0C;&#x5931;&#x8D25;
}

2、RRT*算法

小明的舍友乙指出,甲的RRT算法找到的路径并不够优秀,舍友乙对舍友甲的算法作出了改进,提出了一种RRT*算法。

下面讲讲RRT算法。加了个,很明显这是对RRT的一种改进,就像A算法和A算法一样。从前面那副图中我们可以看出来RRT只能找出一条可行路径,并不能保证找到一条最优路径,换句话说,RRT会让小明走很多弯路,这会让小明更晚收获他的爱情。所以乙提出了一种RRT算法。RRT算法在RRT算法的基础上增加了两步: rewriterandom relink*。也就是重写和随机重连。

先解释一下什么是重写,什么是重连。

重写就是,在新节点new_point加入到树中之后,重新为它选择父节点,好让它到起始点的路径长度(代价)更小。

随机重连是在重写完成之后,对新节点new_point附近一定范围内的节点进行重连。重连就是,检查一下如果把new_point附近的这些节点的父节点设置为new_point,这些节点的代价会不会减小。如果能够减小,就把这些节点的父节点更改为new_point;否则,就不更改。
(论文的作者已经证明RRT*算法是一种渐进最优的算法,也就是说当时间趋于无穷大的时候,这个算法就能够找到最优的路径。)

由于RRT算法不考虑距离,RRT算法考虑每一个节点到出发点的距离,为此每一个节点会增加一个属性:distance_to_start,即到出发点的距离。相应的在为每一个节点选择父节点的时候,新节点的距离等于父节点的距离加上父节点到子节点的直线距离,即:
c h i l d . d i s t a n c e _ t o _ s t a r t = p a r e n t . d i s t a n c e _ t o _ s t a r t + g e t D i s t a n c e ( p a r e n t , c h i l d ) child.distance_to_start=parent.distance_to_start+getDistance(parent,child)c h i l d .d i s t a n c e _t o _s t a r t =p a r e n t .d i s t a n c e _t o _s t a r t +g e t D i s t a n c e (p a r e n t ,c h i l d )
原本RRT的代码不用更改,只需要把
rewriterandom relink 加进去就可以。
rewrite*重写的伪代码如下所示:

&#x904D;&#x5386;&#x6574;&#x4E2A;&#x6811;&#xFF0C;
&#x83B7;&#x5F97;&#x5230;&#x65B0;&#x8282;&#x70B9;new_point&#x7684;&#x8DDD;&#x79BB;&#x5C0F;&#x4E8E;&#x4E00;&#x5B9A;&#x9608;&#x503C;&#xFF08;&#x6BD4;&#x5982;1.5&#x500D;&#x7684;&#x6B65;&#x957F;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;1.5*step_size&#xFF09;&#x7684;&#x6240;&#x6709;&#x8282;&#x70B9;
&#x5C06;&#x8FD9;&#x4E9B;&#x8282;&#x70B9;&#x52A0;&#x5165;&#x5230;&#x4E00;&#x4E2A;&#x540D;&#x4E3A;candidate_parent_of_newpoint&#x7684;&#x5217;&#x8868;&#x4E2D;&#xFF0C;
&#x4E3A;&#x4E86;&#x65B9;&#x4FBF;&#xFF0C;&#x8FD9;&#x4E9B;&#x8282;&#x70B9;&#x7684;distance&#x4E0D;&#x518D;&#x7528;&#x6765;&#x5B58;&#x50A8;&#x5230;&#x51FA;&#x53D1;&#x70B9;&#x7684;&#x8DDD;&#x79BB;&#xFF0C;&#x800C;&#x662F;&#x7528;&#x6765;&#x5B58;&#x50A8;&#x5982;&#x679C;&#x628A;&#x8BE5;&#x8282;&#x70B9;&#x8BBE;&#x7F6E;&#x4E3A;newpointt
&#x7684;&#x7236;&#x8282;&#x70B9;&#x7684;&#x8BDD;&#xFF0C;newpoint&#x5230;&#x51FA;&#x53D1;&#x70B9;&#x7684;&#x8DDD;&#x79BB;&#x3002;
&#x627E;&#x5230;candidate_parent_of_newpoint&#x5217;&#x8868;&#x4E2D;&#x5177;&#x6709;&#x6700;&#x5C0F;distance&#x7684;&#x90A3;&#x4E2A;&#x8282;&#x70B9;&#xFF0C;&#x8FD4;&#x56DE;&#x4ED6;&#x7684;&#x7D22;&#x5F15;index&#xFF0C;
&#x5C06;&#x65B0;&#x8282;&#x70B9;newpoint&#x7684;&#x7236;&#x8282;&#x70B9;&#x8BBE;&#x7F6E;&#x4E3A;index&#x3002;

random relink的伪代码如下所示:

&#x904D;&#x5386;&#x6574;&#x4E2A;&#x5217;&#x8868;&#xFF0C;&#x5BF9;&#x6BCF;&#x4E00;&#x4E2A;&#x8282;&#x70B9;&#x6267;&#x884C;&#x5982;&#x4E0B;&#x52A8;&#x4F5C;{
  if(&#x8BE5;&#x8282;&#x70B9;&#x5230;newpoint&#x7684;&#x8DDD;&#x79BB;&#x5C0F;&#x4E8E;&#x4E00;&#x5B9A;&#x7684;&#x9608;&#x503C;&#xFF0C;&#x6BD4;&#x5982;1.6&#x500D;&#x7684;&#x6B65;&#x957F;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;1.6*step_size){
    if&#xFF08;&#x8BE5;&#x8282;&#x70B9;&#x73B0;&#x5728;&#x7684;distance>&#x628A;&#x8BE5;&#x8282;&#x70B9;&#x7684;&#x7236;&#x8282;&#x70B9;&#x66F4;&#x65B0;&#x4E3A;newpoint&#x4E4B;&#x540E;&#x7684;distance){
        &#x628A;&#x8BE5;&#x8282;&#x70B9;&#x7684;&#x7236;&#x8282;&#x70B9;&#x8BBE;&#x7F6E;&#x4E3A;newpoint&#xFF0C;&#x5E76;&#x66F4;&#x65B0;&#x8BE5;&#x8282;&#x70B9;&#x7684;distance&#x503C;
        &#x66F4;&#x65B0;&#x4EE5;&#x8BE5;&#x8282;&#x70B9;&#x4E3A;&#x6839;&#x8282;&#x70B9;&#x7684;&#x5B50;&#x6811;&#x4E2D;&#x7684;&#x6BCF;&#x4E2A;&#x8282;&#x70B9;&#x7684;distance&#x3002;&#x6211;&#x7528;&#x961F;&#x5217;&#x7ED3;&#x6784;&#x5B9E;&#x73B0;&#x4E86;&#x4E00;&#x4E2A;&#xFF0C;&#x4F46;&#x662F;&#x7B97;&#x6CD5;&#x6548;&#x7387;&#x5F88;&#x4F4E;&#xFF0C;
        &#x6B63;&#x5728;&#x60F3;&#x529E;&#x6CD5;&#x6539;&#x8FDB;&#x3002;
    }
  }
  else{
    do nothing
  }
}

3、Informed RRT*算法

使用了这个RRT算法之后,小明发现找到的路径明显变得优秀多了。但是室友丙这时候站出来说话了,他说,RRT算法是找到了一条更优的路径,但是它的计算量太大了,导致算法的效率比较低,我给改进了一下。舍友丙提出了Informed RRT*算法。

Informed RRT顾名思义,是加入了一些已经informed的信息。实际上informed RRT的思路非常简单,它仅仅是对RRT和RRT的采样函数做了一些限制。在没有搜索到任何一条可达路径之前,informed RRT算法就是RRT算法,在找到了一条可达路径之后,informed 就不再采用原来的采样函数,而是采用一个椭圆采样函数。

从RRT到RRT*,再到Informed RRT*,路径规划算法怎么写
我们假定上图中的蓝色线是一个非常非常标准的椭圆。
假设目前已经找到一个可达的路径:北京——石家庄——济南——徐州——南京。但是这时候,informed RRT*算法并不会终止,而是会在这一条路径的基础上进行优化。

informed RRT*的做法是,在找到一条可达路径之后,把这一条路径的长度作为cMax,把出发点和目标点之间的距离作为cMin。这样做的目的就是,构造一个椭圆出来,把出发点和目标点作为椭圆的两个焦点F1和F2,这一条可达路径的长度做作为2a。因为我们知道,椭圆上的点P满足如下关系:
∣ P F 1 ∣ + ∣ P F 2 ∣ = 2 a |PF1|+|PF2|=2a ∣P F 1 ∣+∣P F 2 ∣=2 a
此后我们就只在这一个椭圆内部采样,而不会再去椭圆外部采样。因为很明显,我已经找到了一个可行路径,比如上图中的路径,那么这个时候,我再去采样西安或者兰州,并不会对我现有路径的优化产生什么作用,除了增大计算量以外。该算法每发现一个更小的cMax就会更新一次cMax,直到cMax满足一定的条件或者达到最大迭代次数才会终止。比如当 c M a x < 1.2 c M i n cMax 时终止算法。
乍一看,这个改进似乎没什么了不起的,仅仅是优化了一下采样函数而已嘛。但是这仅仅是在二维空间中进行采样而已,当遇到更高维问题比如六自由度的机械臂的运动规划是在六维空间中,一般的移动机械臂甚至可以达到12个自由度,这种时候,对采样空间进行限制带来的好处是巨大的。
就这样,小明在三位热心舍友的帮助下成功的找打了一条非常不错的去南京的路线,不知道小明会不会如愿以偿呀?
关于如何在该椭圆内部采样,其实可以用坐标变换的方法,在单位圆内采样是很容易实现的,然后把该采样点通过坐标变换变换到椭圆内就可以了。

Original: https://blog.csdn.net/qq_41845878/article/details/120020737
Author: 听得见我的声音吗
Title: 从RRT到RRT,再到Informed RRT,路径规划算法怎么写

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/597660/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球