双舵轮AGV轨迹跟踪Pure Pursuit算法模型分析、python代码实现

一、Pure Pursuit算法模型图解和公式推导

下图分析的双舵轮AGV的运动学模型,是按照前后舵轮均安装在车体中心线上的车型分析的,当然其它结构的车型也可以按照这样的分析方法画出如下图解。

在这里插入图片描述

符号物理量
r(m)车体中心(小圆)转弯半径
R(m)车轮(大圆)转弯半径
L(m)轴距
δ(rad)前轮转角
α(rad)车身与预瞄点夹角
Ld(m)预瞄距离
e(m)与预瞄点的横向偏差
Xr(m)预瞄点横坐标
Yr(m)预瞄点纵坐标

通过正弦定理推导:

在这里插入图片描述

在这里插入图片描述

则可推导出 转弯圆弧的曲率k

在这里插入图片描述

还可得到:

在这里插入图片描述

在这里插入图片描述

即可推导出:(重要)

在这里插入图片描述

由上式可知,本控制器的本质是对 转角进行控制 ,以减少横向误差为目标的横向控制器。其中, 在这里插入图片描述
可视为控制器的 P参数L 为车辆的 轴距Ld 为设定的 预瞄距离 。 本控制器的控制效果主要取决于预瞄距离的选取,一般来说预瞄距离越长,控制效果会越平滑,预瞄距离越短,控制效果会越精确(同时也会带来一定的震荡)。 预瞄距离的选取也和当前车速有关

通常来说ld 被认为是车速的函数,在不同的车速下需要选择不同的前视距离。

一种最常见的调整前视距离的方法就是将前视距离表示成 车辆纵向速度 的线形函数,即 Ld=k*Vx ,那么前轮的转角公式就变成了:

在这里插入图片描述

于是纯追踪控制器的参数调整就变成了调整 前视系数k 。常来说,会使 用最大、最小前视距离约束前视距离 ,越大的前视距离意味着轨迹的追踪越平滑,小的前视距离会使得追踪更加精确(当然也会带来控制的震荡)。

而两轮转角对应的角速度W,根据当前控制速度V可得:

在这里插入图片描述

  • 非线性的预瞄距离选取方式

在有些文献中, 预瞄距离的选取方式 表现为如下形式(二次方程):

在这里插入图片描述

上式中,对于常数A:
在这里插入图片描述

amax最大制动加速度Av2表示 最短车辆制动距离B表示车辆 遇到异常时需要的反应时间Bv则为 对应的反应距离C表示 车辆的最小转弯半径

  • 实际应用总结

在实际应用中,Pure Pursuit算法通常 不要求跟踪的目标点到本车中心的距离切实等于预瞄距离。 而是会选择 采样好的一系列目标点中到中心距离最接近预瞄距离的那个点来 近似跟踪 。这样做的好处是可以 不需要目标轨迹的函数方程来求解真实预瞄距离坐标 ,极大地提升了算法的效率。

实践中, 纵向控制Vx 通常使用一个简单的 P控制器横向控制 (即转角控制)我们使用 纯追踪控制器


二、python实现三阶贝塞尔曲线跟踪

三阶 贝塞尔曲线路径生成以及轨迹跟踪仿真:

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

k = 0.1  # 前视距离系数
Lfc = 0.5  # 前视距离
Kp = 1.0  # 速度P控制器系数
dt = 0.1  # 时间间隔,单位:s
L = 0.68  # 车辆轴距,单位: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.yaw = state.yaw + state.v / (L*0.5) * math.tan(delta) * dt    #TODO:车体中心为运动半径, v + w*dt
    state.v = state.v + a * dt
    
    return state
	
#p速度控制器,平滑小车前向速度
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)
	
	# pind:上一个路点索引值
    if pind >= ind:
        ind = pind

    if ind < len(cx):
    # tx、ty:车子所在(近似)路径上的点
        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)
    delta = math.atan2(L * math.sin(alpha) / (Lf*Lf), 1.0)    #TODO:双舵轮转角进行控制

    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 = cx[ind + 1] - cx[ind]
        L += math.sqrt(dx ** 2 + dy ** 2)
        ind += 1

    return ind
	
"""
三阶贝塞尔曲线
"""
def bezier_get(start_p, control1_p, control2_p, end_p, t_in):
# t为微分系数,0.01表示
    temp = 1.0 - t_in
    t = t_in
    cx = []
    cy = []
    
    while(t < 1.0):
        t = t_in + t
        temp = 1.0 - t
        pot_x = start_p[0]*temp*temp*temp + 3*control1_p[0]*t*temp*temp + 3*control2_p[0]*t*t*temp + end_p[0]*t*t*t
        cx.append(pot_x)
        
        pot_y = start_p[1]*temp*temp*temp + 3*control1_p[1]*t*temp*temp + 3*control2_p[1]*t*t*temp + end_p[1]*t*t*t
        cy.append(pot_y)

    return cx, cy
	
def main():
# n bezier path
    start_p = [1.0, 5.0]
    control1_p = [4.0, 8.0]
    control2_p = [7.0, 5.0]
    end_p = [11.0, 10.0]

    cx, cy = bezier_get(start_p, control1_p, control2_p, end_p, 0.01)

########### bezier ####################   
     
    target_speed = 1.5   # [m/s]

    T = 10.0  # 最大模拟时间[s]

    # 设置车辆的init状态
    state = VehicleState(x = start_p[0], y = start_p[1], yaw=0.5, 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[m/s]:" + str(state.v)[:4])
        plt.pause(0.001)    # odom 1000Hz
        
    plt.show()
    
    
if __name__ == '__main__':
    main()

注意点:
需要pip安装 numpymatplotlib 等 ,可以直接按照我的另一篇博文推介的开源项目安装(强烈安利这个开源项目,机器人各方面的算法都有涉及)。

在这里插入图片描述

  • 顺便放一个成品效果演示,工程化实现,用任务管理方式实现了直线、贝塞尔曲线、旋转、自然导航等子任务的顺序执行,任务指令使用json方式下达(代码不开放,有问题可以问)。

    双舵轮AGV纯跟踪算法仿真演示

  • 参考链接

1)无人驾驶之车辆控制(1)-- 纯跟踪(Pure Pursuit)算法与Stanley算法:
https://blog.csdn.net/zxxxxxxy/article/details/103665245
2)无人驾驶汽车系统入门(十八)——使用pure pursuit实现无人车轨迹追踪:
https://blog.csdn.net/AdamShan/article/details/80555174
3)LaTex在线公式编辑器:
https://private.codecogs.com/eqnedit.php

  • 23
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
以下是一个简单的舵轮AGV里程计和运动控制核心算法代码示例,仅供参考: 里程计部分: ```python # 初始化编码器或光电传感器 encoder_left = Encoder() encoder_right = Encoder() # 定义AGV的初始位置和方向 pos_x = 0.0 pos_y = 0.0 theta = 0.0 # 定义AGV的初始速度和方向 v = 0.0 omega = 0.0 # 循环读取编码器或光电传感器的数据 while True: # 读取左右轮子的旋转速度和方向 speed_left = encoder_left.get_speed() speed_right = encoder_right.get_speed() direction_left = encoder_left.get_direction() direction_right = encoder_right.get_direction() # 根据轮子的旋转速度和方向计算AGV的速度和方向 v = (speed_left + speed_right) / 2.0 omega = (speed_right - speed_left) / wheelbase # 根据AGV的速度和方向更新AGV的位置和方向 pos_x += v * math.cos(theta) * dt pos_y += v * math.sin(theta) * dt theta += omega * dt ``` 运动控制核心算法部分: ```python # 定义AGV的目标位置和方向 target_x = 5.0 target_y = 5.0 target_theta = 0.0 # 定义控制参数 Kp = 1.0 Ki = 0.0 Kd = 0.0 # 定义误差积分项和误差微分项 integral_error = 0.0 last_error = 0.0 # 循环控制AGV运动 while True: # 计算AGV的位置和方向误差 error_x = target_x - pos_x error_y = target_y - pos_y error_theta = target_theta - theta # 计算误差积分项和误差微分项 integral_error += error_x * dt derivative_error = (error_x - last_error) / dt # 计算控制输出 output = Kp * error_x + Ki * integral_error + Kd * derivative_error # 更新AGV的速度和方向 v = output omega = Kp * error_theta # 控制AGV的电机输出 motor_left.set_speed(v - omega * wheelbase / 2.0) motor_right.set_speed(v + omega * wheelbase / 2.0) # 更新AGV的位置和方向 pos_x += v * math.cos(theta) * dt pos_y += v * math.sin(theta) * dt theta += omega * dt # 更新误差微分项 last_error = error_x ``` 需要注意的是,以上代码示例仅为一个简单的参考,实际的舵轮AGV里程计和运动控制核心算法需要考虑更多的因素,如实际的物理特性、传感器噪声、环境变化等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值