• 【自动驾驶】路径规划—— Dubins 曲线公式总结及python代码实现(基于几何的方法)


    参考资料


    前文

    Dubins 曲线推导(基于向量的方法)

    前文已经从向量的角度进行了Dubins曲线公式的推导,这篇博客主要基于这篇论文《Classification of the Dubins set》介绍基于几何的关系下的dubins曲线公式,论文每一种情况都有详细的推导,因此这里只给出每种情况的公式,推导过程大家参考论文即可。


    1. Dubins 曲线计算

    dubins路径集合为 { L S L , R S R , R S L , L S R , R L R , L R L } \{LSL, RSR, RSL, LSR, RLR, LRL\} {LSLRSRRSLLSRRLRLRL} L L L表示向左转的圆弧运动, R R R表示向右转的圆弧运动, S S S表示沿直线运动。

    1.1 坐标变换

    设起点为 s ( x i , y i , α i ) s\left(x_{i}, y_{i}, \alpha_{i}\right) s(xi,yi,αi) ,终点为 g ( x g , y g , β g ) g\left(x_{g}, y_{g}, \beta_{g}\right) g(xg,yg,βg) , 先坐标变换将起点平移至原点,并旋转 θ \theta θ 角,则终点也落在 x \mathrm{x} x 轴上,起点和终点的坐标为 s ( 0 , 0 , α ) , g ( d , 0 , β ) s(0,0, \alpha), g(d, 0, \beta) s(0,0,α),g(d,0,β) ,其中:
    θ = atan ⁡ 2 ( y g − y i x g − x i )   m o d   { 2 π } D = ( x i − x g ) 2 + ( y i − y g ) 2 d = D / R α = ( α i − θ )   m o d   { 2 π } β = ( β g − θ )   m o d   { 2 π } (1) \tag{1}

    θ=atan2(ygyixgxi)mod{2π}D=(xixg)2+(yiyg)2d=D/Rα=(αiθ)mod{2π}β=(βgθ)mod{2π}
    θ=atan2(xgxiygyi)mod{2π}D=(xixg)2+(yiyg)2 d=D/Rα=(αiθ)mod{2π}β=(βgθ)mod{2π}(1)

    几点说明

    • 其中 θ \theta θ 为起点和终点航向角度差,上面的角度都在 [ 0 , 2 π ] [0,2 \pi] [0,2π] 之间。

    • 上面用 D D D 除上 R R R这样处理可以使每个最小转弯半径 R R R都为 1 ,由角度计算弧长时更方便,弧长即等于角度的弧度,因此在后面看到的 cos ⁡ ( α ) \cos (\alpha) cos(α) ,其实是前面省略了 R R R

    • m o d ( ) mod() mod()是取模运算,例如:   m o d   ( 3 π , 2 π ) = 3 π   m o d   2 π = π \bmod(3\pi,2\pi)=3\pi \bmod 2 \pi=\pi mod(3π,2π)=3πmod2π=π。下述的 β (   m o d   2 π ) \beta(\bmod 2 \pi) β(mod2π)也是这个意思,即 β \beta β 2 π 2\pi 2π取模。python实现方式很简单,如下:

      def mod2pi(theta):
          """对2pi取模运算
          """
          return theta - 2.0 * math.pi * math.floor(theta / 2.0 / math.pi)
      
      
      • 1
      • 2
      • 3
      • 4
      • 5

    坐标变换的python实现很简单,如下:

        # 坐标变换
        dx = g_x-s_x
        dy = g_y-s_y
        D = math.hypot(dx, dy)
        d = D * curvature
    
        theta = mod2pi(math.atan2(dy, dx))
        alpha = mod2pi(s_yaw - theta)
        beta = mod2pi(g_yaw - theta)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    设车辆在起始圆上走过的长度为 t t t ,直线段长度为 p p p ,第二个圆上的圆弧长度为 q q q,整个路径长度 L = t + p + q L=t+p+q L=t+p+q。下面依次给出6种情况的轨迹公式。

    1.2 LSL路径

    L S L LSL LSL路径的轨迹长度公式如下:
    t l s l = − α + arctan ⁡ cos ⁡ β − cos ⁡ α d + sin ⁡ α − sin ⁡ β {   m o d   2 π } p l s l = 2 + d 2 − 2 cos ⁡ ( α − β ) + 2 d ( sin ⁡ α − sin ⁡ β ) q l s l = β − arctan ⁡ cos ⁡ β − cos ⁡ α d + sin ⁡ α − sin ⁡ β {   m o d   2 π } (2) \tag{2}

    tlsl=α+arctancosβcosαd+sinαsinβ{mod2π}plsl=2+d22cos(αβ)+2d(sinαsinβ)qlsl=βarctancosβcosαd+sinαsinβ{mod2π}
    tlsl=α+arctand+sinαsinβcosβcosα{mod2π}plsl=2+d22cos(αβ)+2d(sinαsinβ) qlsl=βarctand+sinαsinβcosβcosα{mod2π}(2)
    总长度等于:
    L l s l = t l s l + p l s l + q l s l = − α + β + p l s l (3) \tag{3} \mathcal{L}_{l s l}=t_{l s l}+p_{l s l}+q_{l s l}=-\alpha+\beta+p_{l s l} Llsl=tlsl+plsl+qlsl=α+β+plsl(3)

    python实现如下

    def left_straight_left(alpha, beta, d):
        """LSL路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        tmp0 = d + sa - sb
    
        mode = ["L", "S", "L"]
        p_squared = 2 + (d * d) - (2 * c_ab) + (2 * d * (sa - sb))
        if p_squared < 0:
            return None, None, None, mode
        tmp1 = math.atan2((cb - ca), tmp0)
        t = mod2pi(-alpha + tmp1)
        p = math.sqrt(p_squared)
        q = mod2pi(beta - tmp1)
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.3 RSR路径

    R S R RSR RSR路径的轨迹长度公式如下:

    t r s r = α − arctan ⁡ cos ⁡ α − cos ⁡ β d − sin ⁡ α + sin ⁡ β {   m o d   2 π } p r s r = 2 + d 2 − 2 cos ⁡ ( α − β ) + 2 d ( sin ⁡ β − sin ⁡ α ) q r s r = − β (   m o d   2 π ) + arctan ⁡ cos ⁡ α − cos ⁡ β d − sin ⁡ α + sin ⁡ β {   m o d   2 π } (4) \tag{4}

    trsr=αarctancosαcosβdsinα+sinβ{mod2π}prsr=2+d22cos(αβ)+2d(sinβsinα)qrsr=β(mod2π)+arctancosαcosβdsinα+sinβ{mod2π}
    trsrprsrqrsr=αarctandsinα+sinβcosαcosβ{mod2π}=2+d22cos(αβ)+2d(sinβsinα) =β(mod2π)+arctandsinα+sinβcosαcosβ{mod2π}(4)

    总长度等于:
    L r s r = t r s r + p r s r + q r s r = α − β + p r s r (5) \tag{5} \mathcal{L}_{r s r} =t_{r s r}+p_{r s r}+q_{r s r}=\alpha-\beta+p_{r s r} Lrsr=trsr+prsr+qrsr=αβ+prsr(5)

    python实现如下

    def right_straight_right(alpha, beta, d):
        """RSR路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        tmp0 = d - sa + sb
        mode = ["R", "S", "R"]
        p_squared = 2 + (d * d) - (2 * c_ab) + (2 * d * (sb - sa))
        if p_squared < 0:
            return None, None, None, mode
        tmp1 = math.atan2((ca - cb), tmp0)
        t = mod2pi(alpha - tmp1)
        p = math.sqrt(p_squared)
        q = mod2pi(-mod2pi(beta) + tmp1)
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.4 RSL路径

    R S L RSL RSL路径的轨迹长度公式如下:

    t r s l = α − arctan ⁡ ( cos ⁡ α + cos ⁡ β d − sin ⁡ α − sin ⁡ β ) + arctan ⁡ ( 2 p r s l ) {   m o d   2 π } p r s l = d 2 − 2 + 2 cos ⁡ ( α − β ) − 2 d ( sin ⁡ α + sin ⁡ β ) q r s l = β (   m o d   2 π ) − arctan ⁡ ( cos ⁡ α + cos ⁡ β d − sin ⁡ α − sin ⁡ β ) + arctan ⁡ ( 2 p r s l ) {   m o d   2 π } (6) \tag{6}

    trsl=αarctan(cosα+cosβdsinαsinβ)+arctan(2prsl){mod2π}prsl=d22+2cos(αβ)2d(sinα+sinβ)qrsl=β(mod2π)arctan(cosα+cosβdsinαsinβ)+arctan(2prsl){mod2π}
    trslprslqrsl=αarctan(dsinαsinβcosα+cosβ)+arctan(prsl2){mod2π}=d22+2cos(αβ)2d(sinα+sinβ) =β(mod2π)arctan(dsinαsinβcosα+cosβ)+arctan(prsl2){mod2π}(6)

    总长度等于:
    L r s l = t r s l + p r s l + q r s l = − α + β + 2 t r s l + p r s l (7) \tag{7} \mathcal{L}_{r s l} =t_{r s l}+p_{r s l}+q_{r s l}=-\alpha+\beta+2 t_{r s l}+p_{r s l} Lrsl=trsl+prsl+qrsl=α+β+2trsl+prsl(7)

    python实现如下

    def right_straight_left(alpha, beta, d):
        """RSL路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        p_squared = (d * d) - 2 + (2 * c_ab) - (2 * d * (sa + sb))
        mode = ["R", "S", "L"]
        if p_squared < 0:
            return None, None, None, mode
        p = math.sqrt(p_squared)
        tmp2 = math.atan2((ca + cb), (d - sa - sb)) - math.atan2(2.0, p)
        t = mod2pi(alpha - tmp2)
        q = mod2pi(mod2pi(beta) - tmp2)
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1.5 LSR路径

    L S R LSR LSR路径的轨迹长度公式如下:

    t l s r = ( − α + arctan ⁡ ( − cos ⁡ α − cos ⁡ β d + sin ⁡ α + sin ⁡ β ) − arctan ⁡ ( − 2 p l s r ) ) {   m o d   2 π } p l s r = − 2 + d 2 + 2 cos ⁡ ( α − β ) + 2 d ( sin ⁡ α + sin ⁡ β ) q l s r = − β (   m o d   2 π ) + arctan ⁡ ( − cos ⁡ α − cos ⁡ β d + sin ⁡ α + sin ⁡ β ) − arctan ⁡ ( − 2 p l s r ) {   m o d   2 π } (8) \tag{8}

    tlsr=(α+arctan(cosαcosβd+sinα+sinβ)arctan(2plsr)){mod2π}plsr=2+d2+2cos(αβ)+2d(sinα+sinβ)qlsr=β(mod2π)+arctan(cosαcosβd+sinα+sinβ)arctan(2plsr){mod2π}
    tlsrplsrqlsr=(α+arctan(d+sinα+sinβcosαcosβ)arctan(plsr2)){mod2π}=2+d2+2cos(αβ)+2d(sinα+sinβ) =β(mod2π)+arctan(d+sinα+sinβcosαcosβ)arctan(plsr2){mod2π}(8)

    总长度等于:
    L l s r = t l s r + p l s r + q l s r = α − β + 2 t l s r + p l s r (9) \tag{9} \mathcal{L}_{l s r} =t_{l s r}+p_{l s r}+q_{l s r}=\alpha-\beta+2 t_{l s r}+p_{l s r} Llsr=tlsr+plsr+qlsr=αβ+2tlsr+plsr(9)

    python实现如下

    def left_straight_right(alpha, beta, d):
        """LSR路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        p_squared = -2 + (d * d) + (2 * c_ab) + (2 * d * (sa + sb))
        mode = ["L", "S", "R"]
        if p_squared < 0:
            return None, None, None, mode
        p = math.sqrt(p_squared)
        tmp2 = math.atan2((-ca - cb), (d + sa + sb)) - math.atan2(-2.0, p)
        t = mod2pi(-alpha + tmp2)
        q = mod2pi(-mod2pi(beta) + tmp2)
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1.6 RLR路径

    R L R RLR RLR路径的轨迹长度公式如下:
    t r l r = α − arctan ⁡ ( cos ⁡ α − cos ⁡ β d − sin ⁡ α + sin ⁡ β ) + p r l r 2 {   m o d   2 π } p r l r = arccos ⁡ 1 8 ( 6 − d 2 + 2 cos ⁡ ( α − β ) + 2 d ( sin ⁡ α − sin ⁡ β ) ) q r l r = α − β − t r l r + p r l r {   m o d   2 π } (10) \tag{10}

    trlr=αarctan(cosαcosβdsinα+sinβ)+prlr2{mod2π}prlr=arccos18(6d2+2cos(αβ)+2d(sinαsinβ))qrlr=αβtrlr+prlr{mod2π}
    trlr=αarctan(dsinα+sinβcosαcosβ)+2prlr{mod2π}prlr=arccos81(6d2+2cos(αβ)+2d(sinαsinβ))qrlr=αβtrlr+prlr{mod2π}(10)

    总长度等于:
    L r l r = t r l r + p r l r + q r l r = α − β + 2 p r l r (11) \tag{11} \mathcal{L}_{r l r}=t_{r l r}+p_{r l r}+q_{r l r}=\alpha-\beta+2 p_{r l r} Lrlr=trlr+prlr+qrlr=αβ+2prlr(11)

    python实现如下

    def right_left_right(alpha, beta, d):
        """RLR路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        mode = ["R", "L", "R"]
        tmp_rlr = (6.0 - d * d + 2.0 * c_ab + 2.0 * d * (sa - sb)) / 8.0
        if abs(tmp_rlr) > 1.0:
            return None, None, None, mode
    
        p = mod2pi(math.acos(tmp_rlr))
        t = mod2pi(alpha - math.atan2(ca - cb, d - sa + sb) + mod2pi(p / 2.0))
        q = mod2pi(alpha - beta - t + p)
        return t, p, q, mode
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上述实现方式得不到最优结果,很奇怪,下面这种写法倒是可以得到最优结果:

    def right_left_right(alpha, beta, d):
        """RLR路径,这部分的实现与公式不一致,改成与公式一致后反而得不到最优路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        mode = ["R", "L", "R"]
        tmp_rlr = (6.0 - d * d + 2.0 * c_ab + 2.0 * d * (sa - sb)) / 8.0
        if abs(tmp_rlr) > 1.0:
            return None, None, None, mode
    
        p = mod2pi(2 * math.pi - math.acos(tmp_rlr))
        t = mod2pi(alpha - math.atan2(ca - cb, d - sa + sb) + mod2pi(p / 2.0))
        q = mod2pi(alpha - beta - t + mod2pi(p))
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.7 LRL路径

    L R L LRL LRL路径的轨迹长度公式如下:
    t l r l = ( − α + arctan ⁡ ( − cos ⁡ α + cos ⁡ α d + sin ⁡ α − sin ⁡ β ) + p l r l 2 ) {   m o d   2 π } p l r l = arccos ⁡ 1 8 ( 6 − d 2 + 2 cos ⁡ ( α − β ) + 2 d ( sin ⁡ α − sin ⁡ β ) ) {   m o d   2 π } q l r l = β (   m o d   2 π ) − α + 2 p l r l {   m o d   2 π } (12) \tag{12}

    tlrl=(α+arctan(cosα+cosαd+sinαsinβ)+plrl2){mod2π}plrl=arccos18(6d2+2cos(αβ)+2d(sinαsinβ)){mod2π}qlrl=β(mod2π)α+2plrl{mod2π}
    tlrl=(α+arctan(d+sinαsinβcosα+cosα)+2plrl){mod2π}plrl=arccos81(6d2+2cos(αβ)+2d(sinαsinβ)){mod2π}qlrl=β(mod2π)α+2plrl{mod2π}(12)

    总长度等于:
    L l r l = t l r l + p l r l + q l r l = − α + β + 2 p l r l . (13) \tag{13} \mathcal{L}_{l r l}=t_{l r l}+p_{l r l}+q_{l r l}=-\alpha+\beta+2 p_{l r l} . Llrl=tlrl+plrl+qlrl=α+β+2plrl.(13)
    python实现如下

    def left_right_left(alpha, beta, d):
        """LRL路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        mode = ["L", "R", "L"]
        tmp_lrl = (6.0 - d * d + 2.0 * c_ab + 2.0 * d * (- sa + sb)) / 8.0
        if abs(tmp_lrl) > 1:
            return None, None, None, mode
        p = mod2pi(math.acos(tmp_lrl))
        t = mod2pi(-alpha - math.atan2(ca - cb, d + sa - sb) + p / 2.0)
        q = mod2pi(mod2pi(beta) - alpha +2*p)
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    同样上述实现方式得不到最优结果,下面这种写法可以得到最优结果:

    def left_right_left(alpha, beta, d):
        """LRL路径,这部分的实现与公式不一致,改成与公式一致后反而得不到最优路径
        """
        sa = math.sin(alpha)
        sb = math.sin(beta)
        ca = math.cos(alpha)
        cb = math.cos(beta)
        c_ab = math.cos(alpha - beta)
    
        mode = ["L", "R", "L"]
        tmp_lrl = (6.0 - d * d + 2.0 * c_ab + 2.0 * d * (- sa + sb)) / 8.0
        if abs(tmp_lrl) > 1:
            return None, None, None, mode
        p = mod2pi(2 * math.pi - math.acos(tmp_lrl))
        t = mod2pi(-alpha - math.atan2(ca - cb, d + sa - sb) + p / 2.0)
        q = mod2pi(mod2pi(beta) - alpha - t + mod2pi(p))
    
        return t, p, q, mode
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    以上完整代码文件见github仓库

  • 相关阅读:
    Maven是什么?手把手先创建个Maven项目
    mysql 常用命令练习
    51单片机DS1302时钟
    外贸人如何向国外客户展现我们的合作诚意
    2023 极客巅峰线上
    布隆过滤器和布谷鸟过滤器
    Python基于宽度优先搜索的程序综合-SyGus求解器
    C++11之decltype类型推导(使用场景、推导四规则、cv限定符)
    每章一篇博客带你拿下吉林大学JAVAEE期末(三:JSP)
    【Vue五分钟】 Vue Cli脚手架安装配置
  • 原文地址:https://blog.csdn.net/weixin_42301220/article/details/125493646