Pure-Pursuit 跟踪五次多项式轨迹

news2024/11/28 3:48:14

Pure-Pursuit 跟踪五次多项式轨迹

考虑双移线轨迹 X 轴方向位移较大,机械楼停车场长度无法满足 100 ~ 120 m,因此采用五次多项式进行轨迹规划,在轨迹跟踪部分也能水一些内容

调整 double_lane.cpp 为 ref_lane.cpp,结合 FrenetPath 类将参考路径的构造抽离为单独的函数

#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/Quaternion.h>
#include <nav_msgs/Path.h>
#include <ros/ros.h>
#include <std_msgs/String.h>

#include <iostream>
#include <string>
#include <vector>
#include <math.h>
#include <fstream>
#include <Eigen/Geometry>

#include "cpprobotics_types_double.h"
#include "frenet_path_double.h"
#include "quintic_polynomial_double.h"

using namespace std;
using namespace cpprobotics;

// 双移线参考路径在 X 方向长度以及参考点的步长
const float length = 120.0;
const float step = 0.1;

// 五次多项式轨迹参数
#define DT 0.01
const double T = 50.0; // t-t0经历的时间
const double xend = 25.0;
const double yend = 3.0;

array<float, 4> calEulerToQuaternion(const float roll, const float pitch, const float yaw)
{
    array<float, 4> calQuaternion = {0.0f, 0.0f, 0.0f, 1.0f}; // 初始化四元数

    // 使用Eigen库来进行四元数计算
    Eigen::Quaternionf quat;
    quat = Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX()) *
           Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitY()) *
           Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitZ());

    calQuaternion[0] = quat.x();
    calQuaternion[1] = quat.y();
    calQuaternion[2] = quat.z();
    calQuaternion[3] = quat.w();

    return calQuaternion;
}

FrenetPath fp;
void calc_frenet_paths()
{
    // 起始状态
    std::array<double, 3> x_start{0.0, 0.0, 0.0};
    std::array<double, 3> x_end{xend, 0.0, 0.0};
    // 终点状态
    std::array<double, 3> y_start{0.0, 0.0, 0.0};
    std::array<double, 3> y_end{yend, 0.0, 0.0};

    // 纵向
    QuinticPolynomial lon_qp(x_start[0], x_start[1], x_start[2], x_end[0],
                             x_end[1], x_end[2], T);
    // 横向
    QuinticPolynomial lat_qp(y_start[0], y_start[1], y_start[2], y_end[0],
                             y_end[1], y_end[2], T, xend);

    for (double t = 0; t < T; t += DT)
    {
        double x = lon_qp.calc_point_x(t);
        double xd = lon_qp.calc_point_xd(t);
        double xdd = lon_qp.calc_point_xdd(t);

        fp.t.emplace_back(t);
        fp.x.emplace_back(x);
        fp.x_d.emplace_back(xd);
        fp.x_dd.emplace_back(xdd);

        double y_x_t = lat_qp.calc_point_y_x(x);
        double y_x_d = lat_qp.calc_point_y_x_d(x);
        double y_x_t_d = lat_qp.calc_point_y_t_d(y_x_d, xd);

        double y_x_dd = lat_qp.calc_point_y_x_dd(x);
        double y_x_t_dd = lat_qp.calc_point_y_t_dd(y_x_dd, xd, y_x_d, xdd);

        fp.y.emplace_back(y_x_t);
        fp.y_d.emplace_back(y_x_t_d);
        fp.y_dd.emplace_back(y_x_t_dd);
        // 压入航向角
        // fp.threat.emplace_back(lat_qp.calc_point_thetar(y_x_t_d, xd));

        // 压入曲率
        fp.k.emplace_back(lat_qp.calc_point_k(y_x_dd, y_x_d));
        // fp.k.emplace_back(lat_qp.calc_point_k(y_x_t_dd, y_x_t_d, xdd, xd));
    }
    int num = fp.x.size();
    for (int i = 0; i < num; i++)
    {
        double dy = fp.y[i + 1] - fp.y[i];
        double dx = fp.x[i + 1] - fp.x[i];
        fp.threat.emplace_back(lat_qp.calc_point_thetar(dy, dx));
    }
    // 最后一个道路航向角和前一个相同
    // fp.threat.push_back(fp.threat.back());
}

void double_lane_path()
{
  // 双移线构造的参数
  const float shape = 2.4;
  const float dx1 = 25.0, dx2 = 21.95;
  const float dy1 = 4.05, dy2 = 5.7;
  const float Xs1 = 27.19, Xs2 = 56.46;

  int points_size = length / step;
  fp.x.resize(points_size);
  fp.y.resize(points_size);
  fp.threat.resize(points_size);
  fp.k.resize(points_size);
  for (int i = 0; i <= points_size; ++i)
  {
    // 计算参考路径点信息
    float ref_x = i * step;
    float z1 = shape / dx1 * (ref_x - Xs1) - shape / 2.0;
    float z2 = shape / dx2 * (ref_x - Xs2) - shape / 2.0;
    float ref_y = dy1 / 2.0 * (1 + tanh(z1)) - dy2 / 2.0 * (1 + tanh(z2));
    float ref_phi = atan(pow(dy1 * (1 / cosh(z1)), 2) * (1.2 / dx1) - pow(dy2 * (1 / cosh(z2)), 2) * (1.2 / dx2));

    float y_dot = dy1 / 2 * pow(1 / cosh(z1), 2) * (shape / dx1) - dy2 / 2 * pow(1 / cosh(z2), 2) * (shape / dx2);
    float y_ddot = -2 * dy1 * ((cosh(z1) - 1) / pow(cosh(z1), 3)) * pow(shape / dx1, 2) + 2 * dy2 * ((cosh(z2) - 1) / pow(cosh(z2), 3)) * pow(shape / dx2, 2);
    float ref_k = abs(y_ddot) / pow(1 + y_dot * y_dot, 1.5);
    
    fp.x[i] = ref_x;
    fp.y[i] = ref_y;
    fp.threat[i] = ref_phi;
    fp.k[i] = ref_k;
  }
}

int main(int argc, char *argv[])
{
    ros::init(argc, argv, "ref_lane");

    ros::NodeHandle nh;
    ros::Publisher path_pub = nh.advertise<nav_msgs::Path>("/ref_path", 1000, true);

    nav_msgs::Path reference_path;
    reference_path.header.frame_id = "world";
    reference_path.header.stamp = ros::Time::now();

    geometry_msgs::PoseStamped pose;
    pose.header.stamp = ros::Time::now();
    pose.header.frame_id = "world";

    // 五次多项式轨迹
    calc_frenet_paths();

    // 双移线轨迹
    // double_lane_path();

    int points_size = fp.x.size();
    reference_path.poses.resize(points_size);
    for (int i = 0; i < points_size; ++i)
    {        
        cout << fp.x[i] << "," << fp.y[i] << "," << fp.threat[i] << endl;
        // 计算四元数位姿
        array<float, 4> calQuaternion = calEulerToQuaternion(0.0, 0.0, fp.threat[i]);

        pose.pose.position.x = fp.x[i];
        pose.pose.position.y = fp.y[i];
        pose.pose.position.z = 0.0;
        pose.pose.orientation.x = calQuaternion[0];
        pose.pose.orientation.y = calQuaternion[1];
        pose.pose.orientation.z = calQuaternion[2];
        pose.pose.orientation.w = calQuaternion[3];
        reference_path.poses[i] = pose;
    }

    ros::Rate loop(10);
    while (ros::ok())
    {
        path_pub.publish(reference_path);
        ros::spinOnce();
        loop.sleep();
    }

    return 0;
}

跟踪过程如下

在这里插入图片描述

跟踪效果如下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里只分析出横向跟踪误差,因为 y 与 x 的关系可以直接获得,yaw 与 x 的关系没有直接的表达式

y = 0.00000000 + 0.00000000 * x + 0.00000000 * x^2 + 0.00192000 * x^3 + -0.00011520 * x^4 + 0.00000184 * x^5

💡 需要完善对于五次多项式的学习

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

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

相关文章

基于 GPS 定位信息的 Pure-Pursuit 轨迹跟踪实车测试(1)

基于 GPS 定位信息的 Pure-Pursuit 轨迹跟踪实车测试&#xff08;1&#xff09; 进行了多组实验&#xff0c;包括顺逆时针转向&#xff0c;直线圆弧轨迹行驶&#xff0c;以及Pure-Pursuit 轨迹跟踪测试 代码修改 需要修改的代码并不多&#xff0c;主要对 gps_sensor 功能包和…

蓝桥杯每日一题2023.11.26

题目描述 奖券数目 - 蓝桥云课 (lanqiao.cn) 将每一个数字进行一一枚举&#xff0c;如果检查时不带有数字4则答案可以加1 #include<bits/stdc.h> using namespace std; int ans; bool check(int n) {while(n){if(n % 10 4)return false;n / 10; }return true; } int m…

基于Haclon的标签旋转项目案例

项目要求&#xff1a; 图为HALCON附图“25interleaved_exposure_04”&#xff0c;里面为旋转的二维码标签&#xff0c;请将其旋转到水平位置。 项目知识&#xff1a; 在HALCON中进行图像平移和旋转通常有以下步骤&#xff1a; &#xff08;1&#xff09;通过hom_mat2d_ident…

<JavaEE> Thread线程类 和 Thread的常用方法

目录 一、Thread概述 二、构造方法 三、常用方法 1.1 getId()、getName()、getState()、getPririty() 1.2 start() 1.3 isDaemon()、setDaemon() 1.4 isAlive() 1.5 currentThread() 1.6 Interrupt()、interrupted()、isInterrupted() 1.6.1 方法一&#xff1a;添加共…

基于Haclon的图形镜像案例

项目要求&#xff1a; 图为HALCON的例图“green-dot”&#xff0c;请将其中的圆形图案按水平和垂直两个方向分别进行镜像。 项目知识&#xff1a; 首先要用BLOB分析的方法&#xff0c;得到圆形图案的目标区域&#xff0c;再对其进行镜像。 在HALCON中与镜像相关的算子为mirr…

跟着chatgpt学习|1.spark入门

首先先让chatgpt帮我规划学习路径&#xff0c;使用Markdown格式返回&#xff0c;并转成思维导图的形式 目录 目录 1. 了解spark 1.1 Spark的概念 1.2 Spark的架构 1.3 Spark的基本功能 2.spark中的数据抽象和操作方式 2.1.RDD&#xff08;弹性分布式数据集&#xff09; 2…

实战oj题——括号匹配问题

前言&#xff1a;前面我们已经做了一些关于顺序表和链表的oj题&#xff0c;今天我们就来解决一些有关于栈和队列的oj题。 我们对这个题看起来毫无头绪&#xff0c;但是我们刚学习了栈&#xff0c;就可以用栈来解决这一类问题&#xff0c;如果我们读取到左括号就入栈&#xff0c…

Cache学习(3):Cache地址映射(直接映射缓存组相连缓存全相连缓存)

1 Cache的与存储地址的映射 以一个Cache Size 为 128 Bytes 并且Cache Line是 16 Bytes的Cache为例。首先把这个Cache想象成一个数组&#xff0c;数组总共8个元素&#xff0c;每个元素大小是 16 Bytes&#xff0c;如下图&#xff1a; 现在考虑一个问题&#xff0c;CPU从0x0654…

再见 Pandas,再见算法

大家好,《再见pandas》 系列已有200多位朋友加入学习了,这段时间亲眼见证了很多朋友的飞跃进步,从无到有,从一个问问题的小白到开始慢慢回答别人的问题,在讨论和练习中不断成长。虽说pandas已经很普及了,但普及内容的深度却远远不够。 下面这套原创图文是我和几位小伙伴…

深入了解 Pinia:现代 Vue 应用的状态管理利器

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

手把手教你如何在Linux下写进度条小程序(附源码)

效果展示 录屏2023 一、建立文件 mkdir ProgressBar //在当前目录下&#xff0c;建立新的目录 cd ProgressBar //进入这个目录 touch main.c makefile progressbar.c progressbar.h //在ProgressBar这个目录建立这几个文件进入ProgressBar这个目录之后&#xff0c;使…

python树长子兄弟链存储结构(孩子兄弟链存储结构)

长子兄弟链存储结构&#xff08;孩子兄弟链存储结构&#xff09;解释&#xff1a; 长子兄弟链存储结构是一种树的存储结构&#xff0c;它使用孩子兄弟表示法&#xff08;也称作左孩子右兄弟表示法&#xff09;来表示树的结构。这种表示方法主要用于存储一般的树&#xff0c;而不…

Linux服务器SSH客户端断开后保持程序继续运行的方法

目录 1. nohup 命令&#xff1a; 2. tmux 或 screen&#xff1a; 3 final shell 断开后服务器如何继续执行令&#xff1f; 方法一&#xff1a;使用 nohup 命令 方法二&#xff1a;将命令放在后台执行 4 你可以使用 jobs 命令查看当前终端中正在后台运行的任务 &#xff…

飞翔的鸟小游戏

第一步是创建项目 项目名自拟 第二步创建个包名 来规范class 再创建一个包 来存储照片 如下&#xff1a; package game; import java.awt.*; import javax.swing.*; import javax.imageio.ImageIO;public class Bird {Image image;int x,y;int width,height;int size;doub…

Qt4用子类化ProxyModel和子类化MainWindow实现全表筛选,中文排序和复制粘贴

目录 1 需求 2 子类化ProxyModel实现全表筛选 3 字符串列表实现中文排序 3.1 Qt5中文排序 3.2 Qt4排序 4 表格的复制粘贴 5 应用 1 需求 模型视图编程是Qt开发的基本功&#xff0c;其中有几个关键问题需要解决&#xff1a; 全表筛选&#xff0c;或者说多列搜索中文排序…

【精选必看】MyBatis映射文件及动态SQL,一级,二级缓存介绍

文章目录 MyBatis映射文件 < r e s u l t M a p > <resultMap> <resultMap>resultMap < sql>&< include>特殊字符处理 动态SQL < i f > < if> <if> < w h e r e > <where> <where> < s e t > <…

叠加原理(superposition principle)、线性系统

叠加原理&#xff08;superposition principle&#xff09;&#xff1a;指对一个系统而言&#xff0c;两个或多个输入产生的输出&#xff0c;等于这几个输入单独引起的输出的和&#xff0c;即输入的叠加等于各输入单独引起的输出的叠加。 线性系统&#xff1a;一个系统&#x…

SAP Smartform小结

SAP系统做打印单据用的, 感觉很不好用, 特别是要嵌入韩文时必须使用嵌入的word编辑器,运行速度简直不可忍受. 见过一些Adobe interactive form的示例, 看着相当不错, 不过据说需要花money额外买licence, 哪有smartform这种免费东西来得实惠. 一般打印需求,会要求有标题抬头,打…

【开源】基于Vue+SpringBoot的农家乐订餐系统

项目编号&#xff1a; S 043 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S043&#xff0c;文末获取源码。} 项目编号&#xff1a;S043&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户2.2 管理员 三、系统展示四、核…

CSDN助手:一键下载CSDN博客:高效保存,随时阅读

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 环境 &#x1f3e1;&#x1f4d2; 使用方法 &#x1f4d2;⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 这是我自己无聊的时候写的一个应用&#xff0c;以前UI有点丑&#xff0c;这次重写了一下UI 功能如下 …