最优化理论与自动驾驶(十):纯跟踪算法原理、公式及代码演示

news2024/11/15 12:20:06

纯跟踪算法(Pure Pursuit Algorithm)是一种用于路径跟踪的几何控制算法,广泛应用于自动驾驶、机器人导航等领域。其基本思想是通过选择预定路径上的目标点(预瞄点),并控制转向角,使车辆不断逼近并跟随该目标点,从而达到路径跟踪的效果。

1. 纯跟踪算法的基本原理

在纯跟踪算法中,控制车辆的转向角是通过“追逐”路径上的一个预瞄点来实现的。该预瞄点通常位于车辆前方一定距离处。算法将车辆看作一个简单的自行车模型,并通过几何方法计算车辆需要的转向角,使得车辆沿着路径前进。

1.1 自行车模型

纯跟踪算法常采用自行车模型来简化车辆的运动学行为。该模型假设车辆的后轮作为参考点(简化车辆为一条直线),并且前轮负责控制转向。模型中车辆的运动主要由以下几个变量决定:

  • L:车辆的轴距(前后轮之间的距离)
  • v:车辆的速度
  • \delta:车辆的转向角
  • (x_v, y_v):车辆当前的位置
  • \theta_v:车辆当前的朝向角,即车头的方向
1.2 预瞄点

预瞄点是预定义路径上的一个点,通常位于车辆前方一段距离处,距离为L_d,称为预瞄距离(Lookahead Distance)。预瞄距离L_d是算法的一个重要参数,选择合适的预瞄距离对于跟踪精度和系统稳定性至关重要。

2. 纯跟踪算法的核心几何推导

2.1 预瞄距离计算

假设车辆的当前位置为(x_v, y_v),朝向角为 \theta_v,预瞄点的坐标为(x_g, y_g),则预瞄点与车辆之间的欧几里得距离(预瞄距离)为:

L_d = \sqrt{(x_g - x_v)^2 + (y_g - y_v)^2}

预瞄距离L_d可以是一个固定值,也可以随着车辆速度变化动态调整,通常速度越高,预瞄距离越大。

2.2 目标角度\alpha的计算

偏航角\alpha是车辆当前朝向与车辆到目标点之间连线的夹角。它定义为:

\alpha = \arctan\left( \frac{y_g - y_v}{x_g - x_v} \right) - \theta_v

这个角度反映了车辆当前的行驶方向与目标点方向的偏差。它是生成转向指令的基础。

2.3 轨迹曲率与转向角的关系

根据几何关系,车辆到预瞄点的距离为L_d,目标偏航角为\alpha,通过几何推导可以得到转向角\delta与这些量的关系。具体地,假设车辆的转向角能够控制其沿着圆形轨迹行驶,则车辆沿圆弧行驶的曲率\kappa与转向角\delta的关系为:

Ld\kappa = \frac{2 \sin(\alpha)}{L_d}

由于转向角\delta与车辆曲率之间的关系为:

\delta = \arctan(L \kappa)

代入上式,可以得到转向角\delta的最终表达式:

\delta = \arctan\left(\frac{2L \sin(\alpha)}{L_d}\right)

其中,L为车辆的轴距,\alpha是车辆的偏航角,L_d是预瞄距离。

3. 纯跟踪算法的实现步骤

  1. 确定预瞄点:在车辆当前位置沿着预定路径向前寻找距离L_d处的目标点(x_g, y_g)。这一点是车辆在接下来的控制周期内要追逐的目标。

  2. 计算偏航角\alpha:根据车辆当前的朝向角\theta_v和预瞄点的位置,计算车辆与目标点之间的偏航角 \alpha

  3. 计算转向角\delta:根据偏航角\alpha、车辆轴距L和预瞄距离L_d​,使用上述公式计算车辆的转向角\delta

  4. 执行控制:根据计算得到的转向角\delta,调整车辆的方向,使其逐步逼近并跟随预定路径。

  5. 更新车辆位置:在每个控制周期内,根据车辆的速度和转向角,更新车辆的当前位置和朝向角,然后重复以上步骤,直到车辆完成路径跟踪。

4. 应用场景与优化

4.1 应用场景
  • 自动驾驶车辆:纯跟踪算法常用于自动驾驶车辆的路径跟踪,尤其是在低速和中速的情况下,例如泊车、低速路径跟踪。
  • 物流机器人:在工业和仓储场景中,物流机器人通常使用纯跟踪算法沿着预定路径移动。
  • 无人机导航:无人机在进行平面路径规划时也可以采用纯跟踪算法,使得无人机能够沿着预设轨迹飞行。
4.2 优化
  1. 动态调整预瞄距离:预瞄距离L_d可以与车辆速度成比例,速度越大,预瞄距离也应该增大,以保持路径跟踪的平滑性和稳定性。

  2. 与其他控制算法结合:在更复杂的场景中,纯跟踪算法可以与更先进的控制算法结合,例如模型预测控制(MPC)和线性二次调节器(LQR),提高系统的鲁棒性和性能。

5. 优缺点

优点

  • 简单易实现:纯跟踪算法基于简单的几何关系,计算复杂度低,适合实时应用。
  • 响应平滑:在低速或路径平缓的情况下,纯跟踪算法能够生成平滑的转向指令,保证车辆平稳行驶。

缺点

  • 精度有限:当车辆速度较高或路径曲率变化较大时,纯跟踪算法的跟踪精度可能下降,容易产生跟踪误差。
  • 忽略动态特性:纯跟踪算法未考虑车辆的动态特性(如车轮打滑、惯性等),因此在某些极端情况下,可能无法准确跟踪预定路径。

6、代码示例

import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib.animation import PillowWriter
import imageio

# 车辆模型
class Vehicle:
    def __init__(self, x=0.0, y=0.0, theta=0.0, speed=0.0, L=2.9):
        """
        初始化车辆模型
        x, y: 初始位置
        theta: 初始航向角
        speed: 初始速度
        L: 车辆轴距
        """
        self.x = x
        self.y = y
        self.theta = theta
        self.speed = speed
        self.L = L
        self.max_speed = 3.0  # 车辆最大速度

    def update(self, delta, acceleration, dt):
        """
        更新车辆状态
        delta: 转向角
        acceleration: 加速度
        dt: 时间步长
        """
        self.speed += acceleration * dt
        self.speed = min(self.speed, self.max_speed)  # 限制车辆速度
        self.x += self.speed * math.cos(self.theta) * dt
        self.y += self.speed * math.sin(self.theta) * dt
        self.theta += self.speed / self.L * math.tan(delta) * dt

# Pure Pursuit控制器
class PurePursuitController:
    def __init__(self, k=0.1, max_lookahead=30.0, min_lookahead=5.0, L=2.9):
        """
        初始化Pure Pursuit控制器
        k: 速度比例因子,用于计算lookahead距离
        max_lookahead: 最大lookahead距离
        min_lookahead: 最小lookahead距离
        L: 车辆轴距
        """
        self.k = k
        self.max_lookahead = max_lookahead
        self.min_lookahead = min_lookahead
        self.L = L

    def calc_lookahead_distance(self, speed):
        """
        根据速度计算lookahead距离
        speed: 当前车辆速度
        """
        lookahead_distance = self.k * speed
        return max(self.min_lookahead, min(lookahead_distance, self.max_lookahead))

    def pure_pursuit_control(self, vehicle, cx, cy):
        """
        计算Pure Pursuit控制下的转向角
        vehicle: 当前车辆状态
        cx, cy: 参考轨迹的x, y坐标
        """
        lookahead_distance = self.calc_lookahead_distance(vehicle.speed)

        # 寻找离车辆最近的轨迹点
        closest_idx = -1
        closest_dist = float("inf")
        for i in range(len(cx)):
            dist = self.calc_distance(cx[i], cy[i], vehicle.x, vehicle.y)
            if dist < closest_dist:
                closest_idx = i
                closest_dist = dist

        # 根据lookahead距离找到目标点
        target_idx = closest_idx
        for i in range(closest_idx, len(cx)):
            dist = self.calc_distance(cx[i], cy[i], vehicle.x, vehicle.y)
            if dist >= lookahead_distance:
                target_idx = i
                break

        # 计算目标点的转向角
        target_x = cx[target_idx]
        target_y = cy[target_idx]
        alpha = math.atan2(target_y - vehicle.y, target_x - vehicle.x) - vehicle.theta
        delta = math.atan2(2 * self.L * math.sin(alpha), lookahead_distance)

        return delta, target_idx

    @staticmethod
    def calc_distance(px, py, x, y):
        """计算两点间的欧氏距离"""
        return np.sqrt((px - x) ** 2 + (py - y) ** 2)

# PID速度控制器
class PIDController:
    def __init__(self, Kp, Ki, Kd, target_speed):
        """
        初始化PID控制器
        Kp, Ki, Kd: PID控制参数
        target_speed: 目标速度
        """
        self.Kp = Kp
        self.Ki = Ki
        self.Kd = Kd
        self.target_speed = target_speed
        self.integral = 0
        self.previous_error = 0

    def control(self, current_speed, dt):
        """
        计算速度控制下的加速度
        current_speed: 当前速度
        dt: 时间步长
        """
        error = self.target_speed - current_speed
        self.integral += error * dt
        derivative = (error - self.previous_error) / dt
        self.previous_error = error
        acceleration = self.Kp * error + self.Ki * self.integral + self.Kd * derivative
        return acceleration

    def update_target_speed(self, distance_to_goal, stop_threshold=3.0):
        """
        根据目标距离更新目标速度(用于平滑停止)
        distance_to_goal: 车辆距离终点的距离
        stop_threshold: 停止距离的阈值
        """
        if distance_to_goal < stop_threshold:
            self.target_speed = 0.0  # 当接近终点时,目标速度设为0

# 轨迹
class Trajectory:
    def __init__(self):
        """
        初始化参考轨迹
        """
        self.cx = np.arange(0, 50, 0.5)
        self.cy = [math.sin(ix / 5.0) * ix / 2.0 for ix in self.cx]

    def get_trajectory(self):
        """获取轨迹的x, y坐标"""
        return self.cx, self.cy

# 主函数
def main():
    vehicle = Vehicle(x=0.0, y=0.0, theta=0.0, speed=0.0, L=2.9)
    controller = PurePursuitController(k=0.1, max_lookahead=30.0, min_lookahead=1.5, L=2.9)
    trajectory = Trajectory()
    cx, cy = trajectory.get_trajectory()
    pid_controller = PIDController(Kp=1.0, Ki=0.1, Kd=0.01, target_speed=3.0)

    dt = 0.1
    x_history = []
    y_history = []

    # 创建图形
    fig, ax = plt.subplots()

    # GIF帧列表
    frames = []

    for t in range(500):
        # 计算车辆到终点的距离
        distance_to_goal = controller.calc_distance(cx[-1], cy[-1], vehicle.x, vehicle.y)
        pid_controller.update_target_speed(distance_to_goal, stop_threshold=3.0)

        # 计算车辆的转向角度并更新车辆位置
        delta, target_idx = controller.pure_pursuit_control(vehicle, cx, cy)
        acceleration = pid_controller.control(vehicle.speed, dt)
        vehicle.update(delta, acceleration, dt)

        # 保存车辆运动轨迹
        x_history.append(vehicle.x)
        y_history.append(vehicle.y)

        # 每2步更新一次图形,提升性能
        if t % 2 == 0:
            ax.cla()
            ax.plot(cx, cy, "-r", label="reference trajectory")
            ax.plot(x_history, y_history, "-b", label="vehicle trajectory")
            ax.plot(cx[target_idx], cy[target_idx], "go", label="lookahead point")
            ax.set_xlim(0, 50)
            ax.set_ylim(-20, 25)
            ax.set_title(f"Pure Pursuit with PID - Step {t}")
            ax.set_xlabel("x [m]")
            ax.set_ylabel("y [m]")
            ax.grid(True)

            # 渲染当前帧
            fig.canvas.draw()

            # 将当前帧保存到 GIF 帧列表
            image = np.frombuffer(fig.canvas.tostring_rgb(), dtype='uint8').reshape(fig.canvas.get_width_height()[::-1] + (3,))
            frames.append(image)

        # 实时显示
        plt.pause(0.01)

        # 到达终点并停止
        if distance_to_goal < 0.5:
            print("Reached the goal!")
            break

    # 保存为GIF
    gif_filename = 'pure_pursuit_simulation.gif'
    imageio.mimsave(gif_filename, frames, fps=10)
    print(f"Simulation saved as {gif_filename}")

if __name__ == '__main__':
    main()

最小预瞄距离1.5m,执行结果:

最小预瞄距离5m,执行结果:

7. 讨论

在纯跟踪(Pure Pursuit, PP)算法中,前视距离(也称为预瞄距离)是一个关键参数,它直接影响到车辆的驾驶性能和路径跟踪的准确性。前视距离是指从车辆当前位置到目标点的距离。这个距离的设置会根据车辆的速度和动态条件进行调整。以下是预瞄距离远近对车辆控制的主要影响:

预瞄距离过长
稳定性增加:较长的预瞄距离可以使车辆更加稳定,特别是在高速行驶时。车辆对即时路况的反应会有所延迟,从而在一定程度上减小因路面突变导致的影响。

减小转向敏感性:随着预瞄距离的增加,车辆对转向的响应会变得不那么敏感,这意味着转向动作更加平缓,对于保持高速行驶的车辆稳定性是有利的。

跟踪误差可能增加:在弯道或复杂路段,长预瞄距离可能导致车辆无法及时调整行驶路径以精确跟踪路线,尤其是在紧急转弯或连续弯道的情况下。

预瞄距离过短
提高转向敏感性:较短的预瞄距离使得车辆对转向输入更加敏感,这有助于在技术性较高的低速路段(如城市环境或障碍物较多的路段)中快速调整方向。

稳定性减小:在高速行驶时,较短的预瞄距离可能会导致车辆稳定性下降,因为车辆需要频繁并且剧烈地调整转向来维持路径,这可能会引起车辆摇摆或其他不稳定行为。

减少跟踪误差:在复杂或紧急转弯情况下,短预瞄距离有助于车辆更精确地跟踪路径,因为车辆可以更快地响应路线上的变化。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用于稀疏自适应深度细化的掩码空间传播网络 CVPR2024

目录 Masked Spatial Propagation Network for Sparsity-Adaptive Depth Refinement &#xff08;CVPR 2024&#xff09;用于稀疏自适应深度细化的掩码空间传播网络1 介绍2 算法流程2.1 问题建模2.2 Guidance Network2.3 MSPN 模块 3 实验结果3.1 稀疏度自适应深度细化对比试验…

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

如何让虚拟机的服务被主机访问

当我们在虚拟机上写了一个服务器&#xff0c;在宿主机访问时&#xff0c;出现无法访问的情况。这可能是虚拟机网络的设置问题。 查看虚拟机防火墙是否关闭 在终端输入&#xff1a; systemctl status firewalld 如果是active就说明防火墙是开启的&#xff0c;需要关闭。 输入…

高级I/O知识分享【epoll || Reactor ET,LT模式】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 一&#xff0c;接口 epo…

SpringBoot 消息队列RabbitMQ 消息可靠性 数据持久化 与 LazyQueue

介绍 在默认情况下&#xff0c;RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟 一旦MO宕机&#xff0c;内存中的消息会丢失内存空间有限&#xff0c;当消费者故障或处理过慢时&#xff0c;会导致消息积压&#xff0c;引发MQ阻塞 在消息队列运行的过程中&#xf…

LeetCode 815.公交路线(BFS广搜 + 建图)(中秋快乐啊)

给你一个数组 routes &#xff0c;表示一系列公交线路&#xff0c;其中每个 routes[i] 表示一条公交线路&#xff0c;第 i 辆公交车将会在上面循环行驶。 例如&#xff0c;路线 routes[0] [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> …

物理感知扩散的 3D 分子生成模型 - PIDiff 评测

PIDiff 是一个针对蛋白质口袋特异性的、物理感知扩散的 3D 分子生成模型&#xff0c;通过考虑蛋白质-配体结合的物理化学原理来生成分子&#xff0c;在原理上&#xff0c;生成的分子可以实现蛋白-小分子的自由能最小。 一、背景介绍 PIDiff 来源于延世大学计算机科学系的 Sang…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

VTD激光雷达(6)——06_OptiX_Variables

文章目录 前言一、总结 前言 感谢VTD官方学习资料 一、 1、 总结 学海无涯回头是岸

curl格式化json之jq工具?

jq 是一个轻量级的命令行工具&#xff0c;用于解析、操作和格式化 JSON 数据。它类似于 sed 或 awk&#xff0c;但专门用于处理 JSON 格式。使用 jq&#xff0c;你可以从复杂的 JSON 数据中提取所需的信息&#xff0c;格式化输出&#xff0c;进行数据筛选&#xff0c;甚至修改 …

正点原子阿尔法ARM开发板-IMX6ULL(六)——通过官方SDK完成实验

文章目录 一、引言1.1 cc.h1.2 main.c1.2 fsl_common.h、MCIMX6Y2.h、fsl_iomuxc.h1.3 对于宏定义能多个参数 其他 一、引言 在开发过程中&#xff0c;如果一个人来写寄存器、汇编等东西&#xff0c;会变得特别繁琐&#xff0c;好在官方NXP官方给出了SDK包&#xff0c; 1.1 c…

牛客周赛 Round 60(下)

构造序列 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include <iostream> #include<stdio.h> #include<math.h> using namespace std; int main() {int n, m;cin >> n >> m;int minVal min(n, m);int maxVal max(n, m);cout …

如何解决“json schema validation error ”错误? -- HarmonyOS自学6

一. 问题描述 DevEco Studio工程关闭后&#xff0c;再重新打开时&#xff0c;出现了如下错误提示&#xff1a; json schema validation error 原因&#xff1a; index.visual或其他visual文件中的left等字段的值为负数时&#xff0c;不能以”-0.x“开头&#xff0c;否则就会…

了解华为云容器引擎(Cloud Container Engine)

1.什么是云容器引擎&#xff1f; 云容器引擎&#xff08;Cloud Container Engine&#xff0c;简称CCE&#xff09;提供高度可扩展的、高性能的企业级Kubernetes集群。借助云容器引擎&#xff0c;您可以在华为云上轻松部署、管理和扩展容器化应用程序。云容器引擎是一个企业级的…

【Android】使用Room数据库解决本地持久化

【Android】使用Room数据库解决本地持久化 Room概述 Room 是一个持久性库&#xff0c;属于 Android Jetpack 的一部分。 Room 是 SQLite 数据库之上的一个抽象层。Room 并不直接使用 SQLite&#xff0c;而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外&a…

ElasticSearch-2-核心语法集群高可用实战-Week2

ES批量操作 1.批量获取文档数据 这里多个文档是指&#xff0c;批量操作多个文档&#xff0c;搜索查询文档将在之后的章节讲解 批量获取文档数据是通过_mget的API来实现的 (1)在URL中不指定index和type 请求方式&#xff1a;GET 请求地址&#xff1a;_mget 功能说明 &#…

数据结构 - 树与二叉树

一.普通有序树的定义 1.树的概念及特性 二.二叉树的定义 1.二叉树的性质 2.二叉树的分类 ①.满二叉树 每一层的结点数都为最大值 ②.完全二叉树 完全二叉树是由满二叉树&#xff0c;从下向上&#xff0c;从右向左依次擦除若干个结点 3.二叉树的结构 三.链式二叉树的创建 1.链式…

24-9-17-读书笔记(十八)-《契诃夫文集》(二)上([俄] 契诃夫 [译] 汝龙 )

文章目录 《契诃夫文集》&#xff08;二&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;目录阅读笔记记录总结 《契诃夫文集》&#xff08;二&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09; 中秋夜&#xff0c;最近有些忙&#xff0c;看书的进度…

【PHP代码审计】PHP常见配置解析

&#x1f31d;博客主页&#xff1a;菜鸟小羊 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 php.ini配置文件 php函数禁用 disable_functions该选项可以设置哪些php函数是禁止使用的&#xff0c;重启生效&#xff0…

哪款宠物空气净化器能清除浮毛,希喂、米家、美的测评分享

要说市面上评价最为两极分化的家电产品&#xff0c;宠物空气净化器可以说是当仁不让了&#xff0c;几乎一半人说真香&#xff0c;另一半人却在吐槽鸡肋。 作为用过宠物空气净化器实测过市面上多个品牌多款宠物空气净化器产品的专业养宠测评博主&#xff0c;对宠物空气净化器这…