最速轨迹规划算法

        在智驾行驶过程中,车辆遇到的很多场景都是“非凸”的,例如超车时可以选择从左边超车也可以选择从右边超车,但是不能直行超车。又比如存在并排车辆时的变道,可以加速超过后变道,也可以减速避让后变道,但是不能直接变道。在这些场景下,常用的处理方式先确定多个(所有)可行的行驶策略(将非凸空间划分成多个凸空间),然后对每个凸空间进行评估,选择最好的一个,交给后续的规控模块处理。这即是决策模块最重要的作用,不合理的决策或决策反复是造成不舒适体感的重要原因。

        比较凸空间的好坏,即比较凸空间中最好的行驶轨迹的好坏,所以一般的做法在每个凸空间中使用优化的方法得到一条最优轨迹,最后优中选优,得到最好的凸空间,即得到左右策略。使用优化方法获取最优轨迹虽然能保证轨迹质量,但是这个过程是一个很耗时的过程,难以在要求实时性的智驾系统评估多个策略。所以好的评价算法不仅要求准确全面,更要求低耗时。

        本文介绍一种简单的满足约束的轨迹生成算法,主要来源这篇论文[1],以满足如下状态方程的运动学系统为例,控制量为j,状态量为p,v,a;且给定初始状态和目标状态,以及相关变量的约束;

        基本思想类似Bang-bang控制,控制量的取值为{umax,0,umin}组成的序列,时刻或以最大输入量将状态量拉到边界值,或以0输入将状态量保持在边界值,所以得到的轨迹为在满足约束的条件下时间最短的轨迹;

        算法过程很简单,轨迹几乎以解析的形式给出,计算量极小,非常适用于对计算资源要求非常严苛的决策模块。具体的算法步骤在论文中有详细描述,下面给出python代码以及运行结果;

C++版本以上传资源栏

#! /usr/bin/python3
import matplotlib.pyplot as plt
import math

class ThirdOrderState:
    def __init__(self, p=0, v=0, a=0, j=0):
        self.p = p
        self.v = v
        self.a = a
        self.j = j

class SecondOrderLimits:
    def __init__(self, a_max, a_min, j_max, j_min):
        self.a_max = a_max
        self.a_min = a_min
        self.j_max = j_max
        self.j_min = j_min

class SecondOrderParameters:
    def __init__(self, t=[0, 0, 0], j=[0, 0, 0]):
        self.t = t
        self.j = j

def KinematicModel(start_state, jerk, dt):
    p = (
        start_state.p
        + start_state.v * dt
        + start_state.a * dt * dt * 0.5
        + jerk * dt * dt * dt / 6.0
    )
    v = start_state.v + start_state.a * dt + jerk * dt * dt * 0.5
    a = start_state.a + jerk * dt
    j = jerk
    return ThirdOrderState(p, v, a, j)

class SecondOrderMinTimeCurve:
    def __init__(self, start_state, v_d, limits):
        self.start_state = start_state
        self.parameters = self.solve_velocity(start_state, v_d, limits)

    @staticmethod
    def solve_velocity(start_state, v_d, limits):
        kEpsilon = 1e-7

        a_max = limits.a_max
        a_min = limits.a_min
        j_max = max(limits.j_max, kEpsilon)
        j_min = min(limits.j_min, -kEpsilon)
        v0 = start_state.v
        a0 = start_state.a

        ve = 0
        if a0 >= 0:
            ve = v0 + a0 * math.fabs(a0 / j_min) / 2.0
        else:
            ve = v0 + a0 * math.fabs(a0 / j_max) / 2.0

        ac = 0
        da = 0
        if v_d - ve > kEpsilon:
            da = 1
            ac = a_max
        elif ve - v_d > kEpsilon:
            da = -1
            ac = a_min

        t1 = 0
        j1 = 0
        if ac - a0 >= 0:
            t1 = (ac - a0) / j_max
            j1 = j_max
        else:
            t1 = (ac - a0) / j_min
            j1 = j_min

        v1 = v0 + a0 * t1 + t1 * t1 * j1 / 2.0

        t3 = 0.0
        j3 = 0.0
        if -ac >= 0.0:
            t3 = -ac / j_max
            j3 = j_max
        else:
            t3 = -ac / j_min
            j3 = j_min

        v3 = ac * t3 + t3 * t3 * j3 / 2.0
        v2 = v_d - v1 - v3

        t2 = 0.0
        if da == 0:
            t2 = 0.0
        else:
            t2 = v2 / ac

        if t2 < 0.0:
            if da == 1:
                an = math.sqrt(
                    (2.0 * (v_d - v0) + a0 * a0 / j_max) / (1 / j_max - 1 / j_min)
                )
                t1 = (an - a0) / j_max
                t2 = 0.0
                t3 = -an / j_min
            elif da == -1:
                an = -math.sqrt(
                    (2.0 * (v_d - v0) + a0 * a0 / j_min) / (1 / j_min - 1 / j_max)
                )
                t1 = (an - a0) / j_min
                t2 = 0.0
                t3 = -an / j_max
        t = [t1, t1 + t2, t1 + t2 + t3]
        j = [j1, 0, j3]
        return SecondOrderParameters(t, j)

    @staticmethod
    def get_state(start_state, parameters, ts):
        if ts <= parameters.t[0]:
            return KinematicModel(start_state, parameters.j[0], ts)

        if ts <= parameters.t[1]:
            state1 = KinematicModel(start_state, parameters.j[0], parameters.t[0])
            return KinematicModel(state1, parameters.j[1], ts - parameters.t[0])

        state1 = KinematicModel(start_state, parameters.j[0], parameters.t[0])
        state2 = KinematicModel(
            state1, parameters.j[1], parameters.t[1] - parameters.t[0]
        )
        if ts < parameters.t[2]:
            return KinematicModel(state2, parameters.j[2], ts - parameters.t[1])

        state3 = KinematicModel(
            state2, parameters.j[2], parameters.t[2] - parameters.t[1]
        )
        state3.p += state3.v * (ts - parameters.t[2])
        state3.a = 0.0
        state3.j = 0.0
        return state3

class ThirdOrderLimits:
    def __init__(self, v_max, v_min, a_max, a_min, j_max, j_min):
        self.v_max = v_max
        self.v_min = v_min
        self.a_max = a_max
        self.a_min = a_min
        self.j_max = j_max
        self.j_min = j_min

class ThirdOrderParameters:
    def __init__(self, P_a, P_b, t_pb, v_c, t_c):
        self.P_a = P_a
        self.P_b = P_b
        self.t_pb = t_pb
        self.v_c = v_c
        self.t_c = t_c

class ThirdOrderMinTimeCurve:
    def __init__(self, start_state, p_f, v_f, limits):
        self.start_state = start_state
        self.parameters = self.solve_position(start_state, p_f, v_f, limits)

    @staticmethod
    def solve_position(start_state, p_f, v_f, limits):
        kMaxN = 100
        kEpsilon = 1e-3

        P = SecondOrderMinTimeCurve.solve_velocity(start_state, v_f, limits)
        state_sp = SecondOrderMinTimeCurve.get_state(start_state, P, P.t[2])

        d_p = 0
        if p_f - state_sp.p > kEpsilon:
            d_p = 1
        elif state_sp.p - p_f > kEpsilon:
            d_p = -1

        v_c = 0.0
        if d_p == 1:
            v_c = max(limits.v_max, kEpsilon)
        elif d_p == -1:
            v_c = min(limits.v_min, -kEpsilon)
        else:
            P_a = P
            P_b = SecondOrderParameters()
            t_pb = P.t3
            v_c = v_c
            t_c = 0.0
            outputs = ThirdOrderParameters(P_a, P_b, t_pb, v_c, t_c)
            return outputs

        P_a = SecondOrderMinTimeCurve.solve_velocity(start_state, v_c, limits)
        state_fa = SecondOrderMinTimeCurve.get_state(start_state, P_a, P_a.t[2])
        P_b = SecondOrderMinTimeCurve.solve_velocity(state_fa, v_f, limits)
        state_fb = SecondOrderMinTimeCurve.get_state(state_fa, P_b, P_b.t[2])

        p_fb = state_fb.p
        t_c = 0.0
        t_pb = 0.0
        if (p_fb - p_f) * d_p <= 0:
            t_c = (p_f - p_fb) / v_c
            t_pb = P_a.t[2]
        else:
            t_c = 0.0
            t_H = P_a.t[2]
            t_L = 0.0
            for i in range(kMaxN):
                t_pb = (t_H + t_L) / 2.0
                state_pb = SecondOrderMinTimeCurve.get_state(start_state, P_a, t_pb)
                P_b = SecondOrderMinTimeCurve.solve_velocity(state_pb, v_f, limits)
                state_fb = SecondOrderMinTimeCurve.get_state(state_pb, P_b, P_b.t[2])

                if (state_fb.p - p_f) * d_p < 0:
                    t_L = t_pb
                else:
                    t_H = t_pb

                if math.fabs(state_fb.p - p_f) < kEpsilon:
                    break

        return ThirdOrderParameters(P_a, P_b, t_pb, v_c, t_c)

    @staticmethod
    def get_state(start_state, parameters, t_s):
        kEpsilon = 1e-3

        state_pb = SecondOrderMinTimeCurve.get_state(
            start_state, parameters.P_a, parameters.t_pb
        )

        state = ThirdOrderState()
        if t_s < parameters.t_pb:
            state = SecondOrderMinTimeCurve.get_state(start_state, parameters.P_a, t_s)
        elif t_s >= parameters.t_pb and t_s < parameters.t_pb + parameters.t_c:
            cruise_state = ThirdOrderState()
            cruise_state.p = state_pb.p + parameters.v_c * (t_s - parameters.t_pb)
            cruise_state.v = parameters.v_c
            cruise_state.a = 0.0
            state = cruise_state
        else:
            cruise_state = ThirdOrderState()
            cruise_state.p = state_pb.p + parameters.v_c * parameters.t_c
            cruise_state.v = parameters.v_c
            cruise_state.a = 0.0
            if math.fabs(parameters.t_c) > kEpsilon:
                state = SecondOrderMinTimeCurve.get_state(
                    cruise_state,
                    parameters.P_b,
                    t_s - (parameters.t_c + parameters.t_pb),
                )
            else:
                state = SecondOrderMinTimeCurve.get_state(
                    state_pb, parameters.P_b, t_s - (parameters.t_c + parameters.t_pb)
                )
        return state

    def plot(self):
        N = 100
        t = 0
        max_time = self.parameters.t_pb + self.parameters.t_c + self.parameters.P_b.t[2]
        delta_t = max_time / N
        lst_t = []
        lst_j = []
        lst_a = []
        lst_v = []
        lst_p = []
        while t <= max_time:
            state = self.get_state(self.start_state, self.parameters, t)
            lst_j.append(state.j)
            lst_a.append(state.a)
            lst_v.append(state.v)
            lst_p.append(state.p)
            lst_t.append(t)

            t = t + delta_t

        plt.figure(figsize=(8, 8))
        plt.subplot(4, 1, 1)
        plt.plot(lst_t, lst_p)
        plt.ylabel("p")

        plt.subplot(4, 1, 2)
        plt.plot(lst_t, lst_v)
        plt.ylabel("v")

        plt.subplot(4, 1, 3)
        plt.plot(lst_t, lst_a)
        plt.ylabel("a")

        plt.subplot(4, 1, 4)
        plt.plot(lst_t, lst_j)
        plt.ylabel("j")

        plt.show()

def main():
    start_state = ThirdOrderState(0, 5, -1.5, 0)
    limits = ThirdOrderLimits(15, 0, 2, -2, 2, -2)
    third_curve = ThirdOrderMinTimeCurve(start_state, 150, 6, limits)
    third_curve.plot()

if __name__ == "__main__":
    main()

运行结果如下:

[1] Lai et al. Safe navigation of quadrotors with jerk limited trajectory. Front Inform Technol Electron Eng 2019 20(1):107-119

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值