Pure_Pursuit

学习总结(11.10)

车辆主动安全实验室 涂志明

一.用matlab实现Pure_Pursuit算法,基于车辆几何模型(如图)

image-20191110222456755 image-20191110222527578

$$
\tan (\delta)=\frac{L}{R}
$$

$$
\frac{\ell_{d}}{\sin (2 \alpha)}=\frac{R}{\sin \left(\frac{\pi}{2}-\alpha\right)}
$$

$$
\frac{\ell_{d}}{2 \sin (\alpha) \cos (\alpha)}=\frac{R}{\cos (\alpha)}
$$

$$
\frac{\ell_{d}}{\sin (\alpha)}=2 R
$$

$$
\kappa=\frac{2 \sin (\alpha)}{\ell_{d}}
$$

$$
\delta=\tan ^{-1}(\kappa L)
$$

$$
\delta(t)=\tan ^{-1}\left(\frac{2 L \sin (\alpha(t))}{\ell_{d}}\right)
$$

$$
\sin (\alpha)=\frac{e_{\ell_{d}}}{\ell_{d}}
$$

$$
\kappa=\frac{2}{\ell_{d}^{2}} e_{\ell_{d}}
$$

$$
\delta(t)=\tan ^{-1}\left(\frac{2 L \sin (\alpha)}{k v_{x}(t)}\right)
$$

​ 可以得知,影响纯追踪控制算法的参数主要为预瞄距离,预瞄距离与速度又有关系,这种方法在低速情况下几乎不受路径形状的影响。但是,选择最佳的前视距离的方法并不明确。将前视距离表示为速度的函数是一种常见的方法,但是,前视距离也可能是路径曲率的函数,甚至可能和纵向速度有关。在此假设为速度的一次函数,故调参时只需要调整参数K值即可,此处举出k=0.05和k=0.5的运行结果:

clear;
clc;
k = 0.05;   % look forward gain         %速度对预瞄距离的影响因数
Lfc = 2.0;  % look-ahead distance       %固定的预瞄距离
Kp = 1.0 ;  % speed propotional gain    %由目标速度和当前速度的差值,乘以kp系数,得到加速度
dt = 0.1  ; % [s]                       %时间间隔
L = 2.9  ;  % [m] wheel base of vehicle %汽车的轴距
cx = 0:0.1:50;                          %给定路径的横坐标cx取值,共501个点

for i = 1:length(cx)                   
    cy(i) = sin(cx(i)/5)*cx(i)/2;       %通过循环,得到给定路径的501个路点
end

i = 1;                                    %从第一个路点开始,i=1
target_speed = 10/3.6;                  %期望的汽车行驶速度
T = 100;                                %行驶时间不超过100s
lastIndex = length(cx);                 %cx表示路点横坐标的行向量,将最后一个路点赋给LastIndex
x = 0; y = -3; yaw = 0; v = 0;          %汽车初始状态,坐标为(0,-3),横摆角yaw=0,初速度v=0
time = 0;                               %初始时间为0 
Lf = k * v + Lfc;                       %由固定预瞄距离和k*v得到最终的预瞄距离Lf
figure

while T > time                      
    target_ind= calc_target_index(x,y,cx,cy,Lf); %得到在最小路径路点基础上,得到预瞄点的顺序数
    if(target_ind>=lastIndex)                    %判断预瞄点是否到达最后路点,到达则跳出循环结束
        return;
    end
    ai = PIDcontrol(target_speed, v,Kp);         %输入为期望速度、当前速度、P控制的kp系数,a
    di = pure_pursuit_control(x,y,yaw,v,cx,cy,target_ind,k,Lfc,L,Lf);%得到车辆期望转角
    [x,y,yaw,v] = update(x,y,yaw,v, ai, di,dt,L);                    %更新车辆t+dt时刻的状态
    time = time + dt;
    %pause(0.1)
    plot(cx,cy,'b',x,y,'r-*');
    drawnow;
    hold on;
end
    % plot(cx,cy,x,y,'*')
    % hold on

%根据根据当前的坐标(x,y),横摆角yaw,速度v,加速度a,前轮转角delta,轴距L以及时间间隔dt,
%得到t+dt时刻的汽车实时状态,用于下一次循环
function [x, y, yaw, v] = update(x, y, yaw, v, a, delta,dt,L) 
    x = x + v * cos(yaw) * dt;                        %t+dt时刻的横坐标x
    y = y + v * sin(yaw) * dt;                        %t+dt时刻的纵坐标y
    yaw = yaw + v / L * tan(delta) * dt;              %t+dt时刻的车辆横摆角
    v = v + a * dt;                                   %t+dt时刻的车辆速度
end

function [a] = PIDcontrol(target_v, current_v, Kp)    %对速度的控制函数
    a = Kp * (target_v - current_v);                  %得到控制速度的加速度
end

function [delta] = pure_pursuit_control(x,y,yaw,v,cx,cy,ind,k,Lfc,L,Lf)
    tx = cx(ind);                                     %得到预瞄点的横坐标
    ty = cy(ind);                                     %得到预瞄点的纵坐标
    alpha = atan2((ty-y),(tx-x))-yaw;                 %预瞄点与后轴中心连线与车纵轴的夹角
    Lf = k * v + Lfc;                                 %车的实时预瞄距离
    delta = atan2((2*L * sin(alpha)/Lf),1.0) ;        %车辆期望转角
end

%输入为汽车当前坐标(x,y), 给定路径(cx,cy),预瞄距离Lf
function [ind] = calc_target_index(x,y, cx,cy,Lf)     
    N =  length(cx);                                  %取cx的个数,作为N进行循环
    Distance = zeros(N,1);                            %Distance为Nx1的列向量,每个元素均为0
    for i = 1:N                                         
        Distance(i) = sqrt((cx(i)-x)^2 + (cy(i)-y)^2);%对应每个路点与汽车当前坐标(x,y)的距离
    end
    [~, location]= min(Distance);                     %得到最小距离路点的位置,即第location个
    ind = location;                                   %ind即为此时最小距离路点在给定路径路点序号
    LL = 0;                                              
    while Lf > LL && (ind + 1) < length(cx)%第一种方法,用循环来计算达到预瞄距离Lf时的路点位置
        dx = cx(ind + 1) - cx(ind);
        dy = cx(ind + 1) - cx(ind);
        LL = LL + sqrt(dx * dx + dy * dy);
        ind  = ind + 1;
    end
    %ind = ind + 10;                       %第二种方法在最小距离路点的基础上,向前预瞄到第ind+10
end

运行结果如图(k=0.05):

image-20191110222129427

运行结果如图(k=0.5):

image-20191110224859841

​ 因此纯追踪控制器的前视距离调整应注意,很短的前视距离(k小)会造成车辆控制的不稳定甚至震荡,为了确保车辆稳定而设置较长的前视距离(k大)又会出现车辆在大转角处转向不足的问题。

二.用Python实现Pure_Pursuit算法:

import numpy as np
import math
import matplotlib.pyplot as plt

k = 0.1    # 速度对预瞄距离的影响因数,当起步时速度慢,则预瞄距离小来更快到达路径上;速度快则预瞄距离长,变化更平滑
Lfc = 2.0  # 固定预瞄距离
Kp = 1.0   # 速度P控制器系数
dt = 0.1   # 时间间隔,单位:s
L = 2.9    # 车辆轴距,单位:m


# 车辆状态类
class VehicleState:
    def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
        self.x = x
        self.y = y
        self.yaw = yaw
        self.v = v

# 车辆状态实时更新
def update(state, a, delta):
    state.x = state.x + state.v * math.cos(state.yaw) * dt
    state.y = state.y + state.v * math.sin(state.yaw) * dt
    state.yaw = state.yaw + state.v / L * math.tan(delta) * dt
    state.v = state.v + a * dt
    return state

# 横向控制器
def PControl(target, current):
    a = Kp * (target - current)
    return a

# 纵向控制器
def pure_pursuit_control(state, cx, cy, pind):
    ind = calc_target_index(state, cx, cy)
    if pind >= ind:
        ind = pind
    if ind < len(cx):
        tx = cx[ind]
        ty = cy[ind]
    else:
        tx = cx[-1]
        ty = cy[-1]
        ind = len(cx) - 1
    alpha = math.atan2(ty - state.y, tx - state.x) - state.yaw
    if state.v < 0:  # back
        alpha = math.pi - alpha
    Lf = k * state.v + Lfc
    delta = math.atan2(2.0 * L * math.sin(alpha) / Lf, 1.0)
    return delta, ind

    # 搜索最临近的路点,且符合预瞄距离要求

def calc_target_index(state, cx, cy):
    dx = [state.x - icx for icx in cx]
    dy = [state.y - icy for icy in cy]
    d = [abs(math.sqrt(idx ** 2 + idy ** 2)) for (idx, idy) in zip(dx, dy)]
    ind = d.index(min(d))
    L = 0.0
    Lf = k * state.v + Lfc      #当前位置的实际预瞄距离
    while Lf > L and (ind + 1) < len(cx):
        dx = cx[ind + 1] - cx[ind]
        dy = cy[ind + 1] - cy[ind]
        L += math.sqrt(dx ** 2 + dy ** 2)
        ind += 1
    return ind

def main():
    # 设置目标路点
    cx = np.arange(0, 50, 0.1)
    cy = [math.sin(ix / 5.0) * ix / 2.0 for ix in cx]
    target_speed = 10.0 / 3.6  # [m/s]
    T = 100.0  # 最大模拟时间
    # 设置车辆的初始状态
    state = VehicleState(x=-0.0, y=-3.0, yaw=0.0, v=0.0)
    lastIndex = len(cx) - 1
    time = 0.0
    x = [state.x]
    y = [state.y]
    yaw = [state.yaw]
    v = [state.v]
    t = [0.0]
    target_ind = calc_target_index(state, cx, cy)
    while T >= time and lastIndex > target_ind:
        ai = PControl(target_speed, state.v)
        di, target_ind = pure_pursuit_control(state, cx, cy, target_ind)
        state = update(state, ai, di)
        time = time + dt
        x.append(state.x)
        y.append(state.y)
        yaw.append(state.yaw)
        v.append(state.v)
        t.append(time)
        plt.cla()
        plt.plot(cx, cy, ".r", label="course")
        plt.plot(x, y, "-b", label="trajectory")
        plt.plot(cx[target_ind], cy[target_ind], "go", label="target")
        plt.axis("equal")
        plt.grid(True)
        plt.title("Speed[km/h]:" + str(state.v * 3.6)[:4])
        plt.pause(0.001)
    plt.show()

if __name__ == '__main__':
    main()

# plt.cla()清除轴,当前活动轴在当前图中。 它保持其他轴不变。
# plt.clf()清除整个当前数字。与所有的轴,但离开窗口打开,这样它就可以再用在其他的 plots上了。
# plt.close()关上窗户,如果未另指定,则该窗口将是当前窗口。
image-20191110225822486

三.接下来任务规划:

继续完成Stanley控制算法实现,掌握MPC基本理论,11月内能够独立写出MPC的控制算法实现。


  转载请注明: 涂志明の博客 Pure_Pursuit

  目录