纯跟踪横向控制和算法仿真实现

文章目录

    • 1. Pure Pursuit
    • 2. 算法原理
    • 3. 算法和仿真实现

1. Pure Pursuit

Pure Pursuit是一种几何跟踪控制算法,也被称为纯跟踪控制算法。他的思想就是基于当前车辆的后轮中心的位置,在参考路径上寻找一个预瞄点,假设车辆可按照一定转弯半径下行驶到该目标点,然后根据车辆当前位置到预瞄点距离、转弯半径和预瞄点与车头朝向夹角的几何关系来计算车辆的前轮转角,即控制车辆的横向跟踪误差。

2. 算法原理

在这里插入图片描述

其中

  • P P P:当前车辆的预瞄点
  • l d l_d ld:辆后轴中心点 A A A F F F的距离,即预瞄距离
  • θ \theta θ l d l_d ld与车轴的夹角
  • φ \varphi φ:车辆的航向角
  • e y e_y ey:预瞄点与车辆横向偏差
  • L L L:轴距
  • R R R:纯跟踪的目标转弯半径
  • δ f \delta_f δf:前轮转角

由图中的三角形几何关系可得

s i n ( 2 θ ) l d = s i n ( π 2 − θ ) R (1) \frac{sin(2\theta)}{l_d} = \frac{sin(\frac{\pi}{2} - \theta)}{R} \tag{1} ldsin(2θ)=Rsin(2πθ)(1)

化简后为

R = l d 2 s i n θ (2) R=\frac{l_d}{2sin\theta} \tag{2} R=2sinθld(2)

由简化的车辆运动学模型和上图中的几何关系可得

t a n δ f = L R (3) tan\delta_f=\frac{L}{R} \tag{3} tanδf=RL(3)

由(2)和(3)可得

δ f = a r c t a n L R = a r c t a n 2 L s i n θ l d (4) \delta_f = arctan\frac{L}{R}=arctan\frac{2Lsin\theta}{l_d} \tag{4} δf=arctanRL=arctanld2Lsinθ(4)

θ \theta θ l d l_d ld与车轴的夹角, e y e_y ey为预瞄点与车辆横向偏差,结合图中几何关系可得

s i n θ = e y l d (5) sin\theta = \frac{e_y}{l_d} \tag{5} sinθ=ldey(5)

因此(4)可转换为

δ f = a r c t a n 2 L s i n θ l d = a r c t a n 2 L e y l d 2 (6) \delta_f =arctan\frac{2Lsin\theta}{l_d} = arctan\frac{2Le_y}{l_d^2} \tag{6} δf=arctanld2Lsinθ=arctanld22Ley(6)

由上式可知,纯跟踪控制的效果取决于预瞄距离 l d l_d ld的选取,预瞄距离越长,前轮转角 δ f \delta_f δf变化会越小,控制效果会越平滑,预瞄距离越短,控制效果会更精确,但也会带来一定的震荡。由于车速越快单位时间内车辆移动距离会越长,我们选取的预瞄距离应该更远,因此对于预瞄距离 l d l_d ld的选取应该与车辆的速度 v v v成正比,一般情况下,我们将预瞄距离与车速的关系选取为以下形式:

l d = k v + l f c (7) l_d=kv+l_{fc} \tag{7} ld=kv+lfc(7)

其中 k k k为预瞄距离系数, l f c l_{fc} lfc为预设距离。

3. 算法和仿真实现

pure_pursuit.py


import numpy as np
import math

k = 0.1  # 预瞄距离系数
Lfc = 3.0  # 初始预瞄距离

class TargetCourse:

    def __init__(self, cx, cy):
        self.cx = cx
        self.cy = cy
        self.old_nearest_point_index = None

    def search_target_index(self, vehicle):

        if self.old_nearest_point_index is None:
            # 搜索距离车辆最近的点
            dx = [vehicle.x - icx for icx in self.cx]
            dy = [vehicle.y - icy for icy in self.cy]
            d = np.hypot(dx, dy)
            ind = np.argmin(d)
            self.old_nearest_point_index = ind
        else:
            ind = self.old_nearest_point_index
            distance_this_index = math.hypot(self.cx[ind] - vehicle.x, self.cy[ind] - vehicle.y)
            while True:
                distance_next_index = math.hypot(self.cx[ind+1] - vehicle.x, self.cy[ind+1] - vehicle.y)
                if distance_this_index < distance_next_index:
                    break
                ind = ind + 1 if (ind + 1) < len(self.cx) else ind
                distance_this_index = distance_next_index
            self.old_nearest_point_index = ind

        Lf = k * vehicle.v + Lfc  # 根据速度更新预瞄距离

        # 搜索预瞄点索引
        while Lf > math.hypot(self.cx[ind] - vehicle.x, self.cy[ind] - vehicle.y):
            if (ind + 1) >= len(self.cx):
                break
            ind += 1

        return ind, Lf


def pure_pursuit_steer_control(vehicle, trajectory, pind):
    ind, Lf = trajectory.search_target_index(vehicle)

    if pind >= ind:
        ind = pind

    if ind < len(trajectory.cx):
        tx = trajectory.cx[ind]
        ty = trajectory.cy[ind]
    else:  # toward goal
        tx = trajectory.cx[-1]
        ty = trajectory.cy[-1]
        ind = len(trajectory.cx) - 1

    alpha = math.atan2(ty - vehicle.y, tx - vehicle.x) - vehicle.yaw
    delta = math.atan2(2.0 * vehicle.L * math.sin(alpha) , Lf)

    return delta, ind

bicycle_model.py

import math
import numpy as np

class Vehicle:
    def __init__(self,
                 x=0.0,
                 y=0.0,
                 yaw=0.0,
                 v=0.0,
                 dt=0.1,
                 l=3.0):
        self.steer = 0
        self.x = x
        self.y = y
        self.yaw = yaw
        self.v = v
        self.dt = dt
        self.L = l  # 轴距
        self.x_front = x + l * math.cos(yaw)
        self.y_front = y + l * math.sin(yaw)

    def update(self, a, delta, max_steer=np.pi):
        delta = np.clip(delta, -max_steer, max_steer)
        self.steer = delta

        self.x = self.x + self.v * math.cos(self.yaw) * self.dt
        self.y = self.y + self.v * math.sin(self.yaw) * self.dt
        self.yaw = self.yaw + self.v / self.L * math.tan(delta) * self.dt

        self.v = self.v + a * self.dt
        self.x_front = self.x + self.L * math.cos(self.yaw)
        self.y_front = self.y + self.L * math.sin(self.yaw)


class VehicleInfo:
    # Vehicle parameter
    L = 3.0  #轴距
    W = 2.0  #宽度
    LF = 3.8  #后轴中心到车头距离
    LB = 0.8  #后轴中心到车尾距离
    MAX_STEER = 0.6  # 最大前轮转角
    TR = 0.5  # 轮子半径
    TW = 0.5  # 轮子宽度
    WD = W  #轮距
    LENGTH = LB + LF  # 车辆长度

def draw_trailer(x, y, yaw, steer, ax, vehicle_info=VehicleInfo, color='black'):
    vehicle_outline = np.array(
        [[-vehicle_info.LB, vehicle_info.LF, vehicle_info.LF, -vehicle_info.LB, -vehicle_info.LB],
         [vehicle_info.W / 2, vehicle_info.W / 2, -vehicle_info.W / 2, -vehicle_info.W / 2, vehicle_info.W / 2]])

    wheel = np.array([[-vehicle_info.TR, vehicle_info.TR, vehicle_info.TR, -vehicle_info.TR, -vehicle_info.TR],
                      [vehicle_info.TW / 2, vehicle_info.TW / 2, -vehicle_info.TW / 2, -vehicle_info.TW / 2, vehicle_info.TW / 2]])

    rr_wheel = wheel.copy() #右后轮
    rl_wheel = wheel.copy() #左后轮
    fr_wheel = wheel.copy() #右前轮
    fl_wheel = wheel.copy() #左前轮
    rr_wheel[1,:] += vehicle_info.WD/2
    rl_wheel[1,:] -= vehicle_info.WD/2

    #方向盘旋转
    rot1 = np.array([[np.cos(steer), -np.sin(steer)],
                     [np.sin(steer), np.cos(steer)]])
    #yaw旋转矩阵
    rot2 = np.array([[np.cos(yaw), -np.sin(yaw)],
                     [np.sin(yaw), np.cos(yaw)]])
    fr_wheel = np.dot(rot1, fr_wheel)
    fl_wheel = np.dot(rot1, fl_wheel)
    fr_wheel += np.array([[vehicle_info.L], [-vehicle_info.WD / 2]])
    fl_wheel += np.array([[vehicle_info.L], [vehicle_info.WD / 2]])

    fr_wheel = np.dot(rot2, fr_wheel)
    fr_wheel[0, :] += x
    fr_wheel[1, :] += y
    fl_wheel = np.dot(rot2, fl_wheel)
    fl_wheel[0, :] += x
    fl_wheel[1, :] += y
    rr_wheel = np.dot(rot2, rr_wheel)
    rr_wheel[0, :] += x
    rr_wheel[1, :] += y
    rl_wheel = np.dot(rot2, rl_wheel)
    rl_wheel[0, :] += x
    rl_wheel[1, :] += y
    vehicle_outline = np.dot(rot2, vehicle_outline)
    vehicle_outline[0, :] += x
    vehicle_outline[1, :] += y

    ax.plot(fr_wheel[0, :], fr_wheel[1, :], color)
    ax.plot(rr_wheel[0, :], rr_wheel[1, :], color)
    ax.plot(fl_wheel[0, :], fl_wheel[1, :], color)
    ax.plot(rl_wheel[0, :], rl_wheel[1, :], color)

    ax.plot(vehicle_outline[0, :], vehicle_outline[1, :], color)
    ax.axis('equal')

main.py

from bicycle_model import Vehicle, VehicleInfo, draw_trailer
from pure_pursuit import pure_pursuit_steer_control, TargetCourse
import numpy as np
import matplotlib.pyplot as plt
import math
import imageio

MAX_SIMULATION_TIME = 200.0  # 程序最大运行时间200*dt

def NormalizeAngle(angle):
    a = math.fmod(angle + np.pi, 2 * np.pi)
    if a < 0.0:
        a += (2.0 * np.pi)
    return a - np.pi

# 计算距离车辆最近的参考点索引,用于计算横向误差
def calNearestPointIndex(vehicle, ref_path):
    dx = [vehicle.x - icx for icx in ref_path[:, 0]]
    dy = [vehicle.y - icy for icy in ref_path[:, 1]]
    d = np.hypot(dx, dy)
    index = np.argmin(d)
    return index
def main():
    # 设置跟踪轨迹
    ref_path = np.zeros((1000, 2))
    ref_path[:, 0] = np.linspace(0, 50, 1000)  # x坐标
    ref_path[:, 1] = 10 * np.sin(ref_path[:, 0] / 20.0)  # y坐标
    ref = TargetCourse(ref_path[:, 0], ref_path[:, 1])
    # 假设车辆初始位置为(0,0),航向角yaw=pi/2,速度为2m/s,时间周期dt为0.1秒
    vehicle = Vehicle(x=0.0,
                      y=0.0,
                      yaw=np.pi/2,
                      v=2.0,
                      dt=0.1,
                      l=VehicleInfo.L)

    ind, target_ind = ref.search_target_index(vehicle)
    time = 0.0  # 初始时间

    # 记录车辆轨迹
    trajectory_x = []
    trajectory_y = []
    lat_err = []  # 记录横向误差

    i = 0
    image_list = []  # 存储图片
    plt.figure(1)

    last_idx = ref_path.shape[0] - 1  # 跟踪轨迹的最后一个点的索引
    while MAX_SIMULATION_TIME >= time and last_idx > target_ind:
        time += vehicle.dt  # 累加一次时间周期

        # 纯跟踪
        delta_f, target_ind = pure_pursuit_steer_control(vehicle, ref, target_ind)

        # 横向误差计算
        nearest_index = calNearestPointIndex(vehicle, ref_path)
        vehicle_positon = np.zeros(2)
        vehicle_positon[0] = vehicle.x
        vehicle_positon[1] = vehicle.y
        alpha = math.atan2(
            ref_path[nearest_index, 1] - vehicle_positon[1], ref_path[nearest_index, 0] - vehicle_positon[0])
        l_d = np.linalg.norm(ref_path[nearest_index] - vehicle_positon)  # 最近点与车定位点距离l_d

        theta_e = NormalizeAngle(alpha - vehicle.yaw)
        e_y = l_d * math.sin(theta_e)  # 计算实际误差,0为目标距离
        lat_err.append(e_y)

        # 更新车辆状态
        vehicle.update(0.0, delta_f, np.pi / 10)  # 由于假设纵向匀速运动,所以加速度a=0.0
        trajectory_x.append(vehicle.x)
        trajectory_y.append(vehicle.y)

        # 显示动图
        plt.cla()
        plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)
        draw_trailer(vehicle.x, vehicle.y, vehicle.yaw, vehicle.steer, plt)

        plt.plot(trajectory_x, trajectory_y, "-r", label="trajectory")
        plt.plot(ref_path[target_ind, 0], ref_path[target_ind, 1], "go", label="target")
        plt.axis("equal")
        plt.grid(True)
        plt.pause(0.001)
    #     plt.savefig("temp.png")
    #     i += 1
    #     if (i % 50) > 0:
    #         image_list.append(imageio.imread("temp.png"))
    #
    # imageio.mimsave("display.gif", image_list, duration=0.1)

    plt.figure(2)
    plt.subplot(2, 1, 1)
    plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)
    plt.plot(trajectory_x, trajectory_y, 'r')
    plt.title("actual tracking effect")

    plt.subplot(2, 1, 2)
    plt.plot(lat_err)
    plt.title("lateral error")
    plt.show()


if __name__ == '__main__':
    main()

运行效果:
在这里插入图片描述

控制效果和横向误差:
在这里插入图片描述

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/322034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

odoo16 销售订单中数量与单价,手机录入不方便

odoo16 销售订单中数量与单价&#xff0c;手机录入不方便 在销售订单中&#xff0c;服装批发&#xff0c;数量与单价均是整数&#xff0c;系统默认的为保留两位小数的float类型&#xff0c;输入起来很不方便&#xff0c;如何修改 电脑版&#xff0c;输入时&#xff0c;自动选取…

Linux:NTP校时、PTP校时

目录 前言一、NTP校时1、简介2、ubuntu使用 NTP3、嵌入式设备使用 NTP 校时4、NTP 服务器的校时精度 二、PTP校时1、简介2、ubuntu使用 PTP3、嵌入式设备使用 PTP 校时 三、PTP 校时和 NTP 校时那个精度高一些 前言 在进行网络协议通信时&#xff0c;我们有时候需要计算通信的延…

桌面显示器type-c接口方案

在当今时代&#xff0c;TYPE-C接口桌面显示器已经成为了我们生活和工作中不可或缺的重要设备之一。与传统显示器相比&#xff0c;新型的TYPE-C接口桌面显示器具有更多的功能和优势&#xff0c;其中最显著的特点就是支持视频传输和充电功能。 首先&#xff0c;TYPE-C接口桌面显示…

mybatis在resultmap的子查询中使用传到mapper中的参数

1.将要传递的参数作为虚拟列加入到select语句中 2.使用column进行虚拟列的数据传递&#xff0c;在子查询中就能直接使用了&#xff0c;等于号两边名字一样免得区分&#xff0c;只要是出现在夫查询select语句中的都能传递 3.在子查询中使用参数 上图 上述的方法只限单值变量 …

NLP论文阅读记录 - 2021 | WOS 智能树提取文本摘要深度学习

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.前提三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 An Intelligent Tree Extractive Text Summarization Deep Learning …

优优聚美团代运营服务:实现业务增长的不二之选

随着互联网的普及和电商行业的快速发展&#xff0c;越来越多的商家选择在美团等电商平台开展业务。然而&#xff0c;很多商家由于缺乏专业的电商运营知识和经验&#xff0c;难以在激烈的市场竞争中脱颖而出。此时&#xff0c;美团代运营服务应运而生&#xff0c;成为商家实现业…

Machine Trap Delegation Registers

默认情况下&#xff0c;所有的trap都是在machine mode中处理的&#xff1b;虽然machine-mode可以通过调用MRET指令&#xff0c;来重新将traps给到其他的mode来执行&#xff0c;但是性能上并不好&#xff1b;因此增加如下两个寄存器&#xff1a;mideleg/medeleg&#xff0c;分别…

Sentinel 降级、限流、熔断

前言 在现代分布式系统中&#xff0c;如何有效地保护系统免受突发流量和故障的影响&#xff0c;是每个开发人员和架构师都需要思考的重要问题。在这样的背景下&#xff0c;Sentinel作为一个强大的系统保护和控制组件&#xff0c;为我们提供了降级、限流、熔断等多种策略&#…

边缘数据采集网关无法上传数据是什么原因?如何解决?

边缘数据采集网关是物联网系统中的常见设备&#xff0c;主要用途包括数据采集、协议转换、边缘数据处理、数据传输分发等&#xff0c;实现多设备和多系统的互联互通和数据协同应用&#xff0c;对于提高物联网感知和响应效率、加强物联网联动协同能力、提升数据安全性等方面都具…

MATLAB实验Simulink的应用

本文MATLAB源码&#xff0c;下载后直接打开运行即可[点击跳转下载]-附实验报告https://download.csdn.net/download/Coin_Collecter/88740734 一、实验目的 1.熟悉Simulink操作环境。 2.掌握建立系统仿真模型以及系统仿真分析的方法。 二、实验内容 1.利用Simulink仿真下列曲…

怎样实现安全便捷的网间数据安全交换?

数据安全交换是指在数据传输过程中采取一系列措施来保护数据的完整性、机密性和可用性。网间数据安全交换&#xff0c;则是需要进行跨网络、跨网段甚至跨组织地进行数据交互&#xff0c;对于数据的传输要求会更高。 大部分企业都是通过网闸、DMZ区、VLAN、双网云桌面等方式实现…

【Azure 架构师学习笔记】- Azure Databricks (6) - 配置Unity Catalog

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (5) - Unity Catalog 简介 UC的关键特性 之所以DataBricks要用UC&#xff0c; 很大程度是对安全的管控。从上文可以了解到它的四大特性&#…

Python 以相对/绝对路径的方式压缩文件

文章目录 1. tarfile 简单介绍2. tarfile 支持的模式3. 绝对路径压缩4. 相对路径压缩5. 参考 1. tarfile 简单介绍 Python 的 tarfile 模块提供了对 .tar 格式归档文件的全面支持&#xff0c;允许用户创建、读取、修改和写入 tar 归档文件。在实际应用中&#xff0c;tar 文件通…

护眼台灯是智商税吗?写作业使用的护眼台灯推荐

在当今社会&#xff0c;越来越多的人在工作和生活中长时间地盯着电脑屏幕或手机屏幕&#xff0c;给眼睛带来了很大的压力和损害。为了缓解眼睛的疲劳和不适&#xff0c;护眼台灯成为了很多人的选择。然而&#xff0c;市场上的护眼台灯种类繁多&#xff0c;价格各异&#xff0c;…

M-A352AD10高精度三轴加速度计

一般描述 M-A352是一种三轴数字输出加速度计&#xff0c;具有超低噪声、高稳定性、低功耗等特点&#xff0c;采用了夸特的精细处理技术。. 多功能M-A352具有高精度和耐久性&#xff0c;非常适合广泛的具有挑战性的应用&#xff0c;如SHM、地震观测、工业设备的状态监测和工业…

pandas查看数据常用方法(以excel为例)

目录 1.查看指定行数的数据head() 2. 查看数据表头columns 3.查看索引index 4.指定索引列index_col 5.按照索引排序 6.按照数据列排序sort_values() 7.查看每列数据类型dtypes 8.查看指定行列数据loc 9.查看数据是否为空isnull() 1.查看指定行数的数据head() &#xff…

软信天成:数据安全管理解决方案分享

近年来&#xff0c;随着数据环境日趋复杂多变和潜在的数据隐私泄露风险潜伏&#xff0c;如何确保企业数据安全已成为众多企业亟待面对与妥善处理的重要问题。 为了应对这一严峻的现实挑战&#xff0c;软信天成凭借专业的知识体系和丰富的实战经验积累&#xff0c;总结出了一套…

Java实现海南旅游景点推荐系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

计算机组成原理-程序中断(基本概念 中断分类 流程 )

文章目录 总览中断的基本概念中断请求的分类中断请求标记中断判优-实现中断判优-优先级设置中断处理过程-中断隐指令中断处理过程-中断服务程序小结 总览 中断的基本概念 中断隐指令就是修改PC的值到中断服务程序 在每条指令执行完后&#xff0c;在指令周期末尾检查是否有中断…

【linux】软链接创建(linux的快捷方式创建)

软连接的概念 类似于windows系统中的快捷方式。有的文件目录很长或者每次使用都要找很不方便&#xff0c;于是可以用类似windows的快捷方式的软链接在home&#xff08;初始目录类似于桌面&#xff09;上创建一些软链接方便使用。 软链接的语法 ln -s 参数1 参数2 参数1&#…