Pure-Pursuit 跟踪双移线 Gazebo 仿真

Pure-Pursuit 跟踪双移线 Gazebo 仿真

主要参考学习下面的博客和开源项目

自动驾驶规划控制(A*、pure pursuit、LQR算法,使用c++在ubuntu和ros环境下实现)

https://github.com/NeXTzhao/planning

Pure-Pursuit 的理论基础见今年六月份的笔记

对参考轨迹进行调整,采用双移线轨迹

#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>
using namespace std;

// 双移线构造的参数
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;
// 参考路径在 X 方向长度以及参考点的步长
const float length = 120.0;
const float step = 0.1;

// 计算 Y 轴参考位置
inline float calculate_reference_y(const float ref_x)
{
    float z1 = shape / dx1 * (ref_x - Xs1) - shape / 2.0;
    float z2 = shape / dx2 * (ref_x - Xs2) - shape / 2.0;
    return dy1 / 2.0 * (1 + tanh(z1)) - dy2 / 2.0 * (1 + tanh(z2));
}

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

    ros::NodeHandle nh;
    ros::Publisher path_pub = nh.advertise<nav_msgs::Path>("/double_lane", 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";

    int points_size = length / step;
    reference_path.poses.resize(points_size + 1);
    for (int i = 0; i <= points_size; ++i)
    {
        float ref_x = i * step;
        float ref_y = calculate_reference_y(ref_x);
        // cout << ref_x << "\t" << ref_y << endl;
        pose.pose.position.x = ref_x;
        pose.pose.position.y = ref_y;
        pose.pose.position.z = 0.0;
        pose.pose.orientation.x = 0.0;
        pose.pose.orientation.y = 0.0;
        pose.pose.orientation.z = 0.0;
        pose.pose.orientation.w = 0.0;
        reference_path.poses[i] = pose;
    }

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

    return 0;
}

编程方面进行了一些简单的优化,轨迹跟踪的算法在 poseCallback 中实现,和博主有所区别

#include <geometry_msgs/Twist.h>
#include <nav_msgs/Path.h>
#include <ros/ros.h>
#include <tf/tf.h>
#include <tf/transform_broadcaster.h>

#include <algorithm>
#include <cassert>
#include <cmath>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "cpprobotics_types.h"
#include "cubic_spline.h"
#include "geometry_msgs/PoseStamped.h"

#define PREVIEW_DIS 1.5 // 预瞄距离

#define Ld 1.868 // 轴距

using namespace std;
using namespace cpprobotics;

ros::Publisher purepersuit_;
ros::Publisher path_pub_;
nav_msgs::Path path;

float carVelocity = 0;
float preview_dis = 0;
float k = 0.1;

// 计算四元数转换到欧拉角
std::array<float, 3> calQuaternionToEuler(const float x, const float y,
                                          const float z, const float w)
{
    std::array<float, 3> calRPY = {(0, 0, 0)};
    // roll = atan2(2(wx+yz),1-2(x*x+y*y))
    calRPY[0] = atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y));
    // pitch = arcsin(2(wy-zx))
    calRPY[1] = asin(2 * (w * y - z * x));
    // yaw = atan2(2(wx+yz),1-2(y*y+z*z))
    calRPY[2] = atan2(2 * (w * z + x * y), 1 - 2 * (y * y + z * z));

    return calRPY;
}

cpprobotics::Vec_f r_x_;
cpprobotics::Vec_f r_y_;

int pointNum = 0; // 保存路径点的个数
int targetIndex = pointNum - 1;

// 计算发送给模型车的转角
void poseCallback(const geometry_msgs::PoseStamped &currentWaypoint)
{
    auto currentPositionX = currentWaypoint.pose.position.x;
    auto currentPositionY = currentWaypoint.pose.position.y;
    auto currentPositionZ = 0.0;

    auto currentQuaternionX = currentWaypoint.pose.orientation.x;
    auto currentQuaternionY = currentWaypoint.pose.orientation.y;
    auto currentQuaternionZ = currentWaypoint.pose.orientation.z;
    auto currentQuaternionW = currentWaypoint.pose.orientation.w;

    std::array<float, 3> calRPY =
        calQuaternionToEuler(currentQuaternionX, currentQuaternionY,
                             currentQuaternionZ, currentQuaternionW);

    cout << currentPositionX << "\t" << currentPositionY << "\t" << calRPY[2] << endl;

    for (int i = pointNum - 1; i >= 0; --i)
    {
        float distance = sqrt(pow((r_x_[i] - currentPositionX), 2) +
                              pow((r_y_[i] - currentPositionY), 2));
        if (distance < preview_dis)
        {
            targetIndex = i + 1;
            break;
        }
    }

    if (targetIndex >= pointNum)
    {
        targetIndex = pointNum - 1;
    }

    float alpha =
        atan2(r_y_[targetIndex] - currentPositionY, r_x_[targetIndex] - currentPositionX) -
        calRPY[2];

    // 当前点和目标点的距离Id
    float dl = sqrt(pow(r_y_[targetIndex] - currentPositionY, 2) +
                    pow(r_x_[targetIndex] - currentPositionX, 2));

    // 发布小车运动指令及运动轨迹
    if (targetIndex == pointNum - 1 && dl < 0.2) // 离终点很近时停止运动
    {
        geometry_msgs::Twist vel_msg;
        vel_msg.linear.x = 0;
        vel_msg.angular.z = 0;
        purepersuit_.publish(vel_msg);
    }
    else
    {
        float theta = atan(2 * Ld * sin(alpha) / dl);
        geometry_msgs::Twist vel_msg;
        vel_msg.linear.x = 3;
        vel_msg.angular.z = theta;
        purepersuit_.publish(vel_msg);
        // 发布小车运动轨迹
        geometry_msgs::PoseStamped this_pose_stamped;
        this_pose_stamped.pose.position.x = currentPositionX;
        this_pose_stamped.pose.position.y = currentPositionY;

        geometry_msgs::Quaternion goal_quat = tf::createQuaternionMsgFromYaw(theta);
        this_pose_stamped.pose.orientation.x = currentQuaternionX;
        this_pose_stamped.pose.orientation.y = currentQuaternionY;
        this_pose_stamped.pose.orientation.z = currentQuaternionZ;
        this_pose_stamped.pose.orientation.w = currentQuaternionW;

        this_pose_stamped.header.stamp = ros::Time::now();

        this_pose_stamped.header.frame_id = "world";
        path.poses.push_back(this_pose_stamped);
    }

    path_pub_.publish(path);
}

void velocityCall(const geometry_msgs::TwistStamped &carWaypoint)
{
    carVelocity = carWaypoint.twist.linear.x;
    // 预瞄距离计算
    preview_dis = k * carVelocity + PREVIEW_DIS;
}

void pointCallback(const nav_msgs::Path &msg)
{
    // 避免参考点重复赋值
    if (pointNum != 0)
    {
        return;
    }

    // geometry_msgs/PoseStamped[] poses
    pointNum = msg.poses.size();

    // 提前开辟内存
    r_x_.resize(pointNum);
    r_y_.resize(pointNum);
    for (int i = 0; i < pointNum; i++)
    {
        r_x_[i] = msg.poses[i].pose.position.x;
        r_y_[i] = msg.poses[i].pose.position.y;
    }
}
int main(int argc, char **argv)
{
    // 创建节点
    ros::init(argc, argv, "pure_pursuit");

    // 创建节点句柄
    ros::NodeHandle n;
    // 创建Publisher,发送经过pure_pursuit计算后的转角及速度
    purepersuit_ = n.advertise<geometry_msgs::Twist>("/smart/cmd_vel", 20);

    path_pub_ = n.advertise<nav_msgs::Path>("/rvizpath", 100, true);
    // ros::Rate loop_rate(10);

    path.header.frame_id = "world";
    // 设置时间戳
    path.header.stamp = ros::Time::now();
    geometry_msgs::PoseStamped pose;
    pose.header.stamp = ros::Time::now();
    // 设置参考系
    pose.header.frame_id = "world";

    ros::Subscriber splinePath = n.subscribe("/double_lane", 20, pointCallback);
    ros::Subscriber carVel = n.subscribe("/smart/velocity", 20, velocityCall);
    ros::Subscriber carPose = n.subscribe("/smart/rear_pose", 20, poseCallback);
    ros::spin();
    return 0;
}

这里和 CarSim-Simulink 联合仿真的代码类似

function [sys,x0,str,ts] = MY_MPCController3(t,x,u,flag)
%   该函数是写的第3个S函数控制器(MATLAB版本:R2011a)
%   限定于车辆运动学模型,控制量为速度和前轮偏角,使用的QP为新版本的QP解法
%   [sys,x0,str,ts] = MY_MPCController3(t,x,u,flag)
%
% is an S-function implementing the MPC controller intended for use
% with Simulink. The argument md, which is the only user supplied
% argument, contains the data structures needed by the controller. The
% input to the S-function block is a vector signal consisting of the
% measured outputs and the reference values for the controlled
% outputs. The output of the S-function block is a vector signal
% consisting of the control variables and the estimated state vector,
% potentially including estimated disturbance states.

switch flag,
 case 0
  [sys,x0,str,ts] = mdlInitializeSizes; % Initialization
  
 case 2
  sys = mdlUpdates(t,x,u); % Update discrete states
  
 case 3
  sys = mdlOutputs(t,x,u); % Calculate outputs

 case {1,4,9} % Unused flags
  sys = [];
  
 otherwise
  error(['unhandled flag = ',num2str(flag)]); % Error handling
end
% End of dsfunc.

%==============================================================
% Initialization
%==============================================================

function [sys,x0,str,ts] = mdlInitializeSizes
% Call simsizes for a sizes structure, fill it in, and convert it 
% to a sizes array.
sizes = simsizes;
sizes.NumContStates  = 0;
sizes.NumDiscStates  = 4; % this parameter doesn't matter
sizes.NumOutputs     = 1;
sizes.NumInputs      = 5;
sizes.DirFeedthrough = 1; % Matrix D is non-empty.
sizes.NumSampleTimes = 1;
sys = simsizes(sizes); 
x0 =[0.00001;0.00001;0.00001;0.00001];   
global U; % store current ctrl vector:[vel_m, delta_m]
U=[0];

global cx;
cx = 0:0.01:160;
global cy;
shape=2.4;%参数名称,用于参考轨迹生成
dx1=25;dx2=21.95;%没有任何实际意义,只是参数名称
dy1=4.05;dy2=5.7;%没有任何实际意义,只是参数名称
Xs1=27.19;Xs2=56.46;%参数名称
for i = 1:length(cx)                      %全局路径c(y)生成 路径初始化
    z1=shape/dx1*(cx(i)-Xs1)-shape/2;
    z2=shape/dx2*(cx(i)-Xs2)-shape/2;
    cy(i) = dy1/2*(1+tanh(z1))-dy2/2*(1+tanh(z2));
end

% Initialize the discrete states.
str = [];             % Set str to an empty matrix.
ts  = [0.05 0];       % sample time: [period, offset]
%End of mdlInitializeSizes
		      
%==============================================================
% Update the discrete states
%==============================================================
function sys = mdlUpdates(t,x,u)
  sys = x;
%End of mdlUpdate.

%==============================================================
% Calculate outputs
%==============================================================
function sys = mdlOutputs(t,x,u)
    global U; %store chi_tilde=[vel-vel_ref; delta - delta_ref]
    global cx;
    global cy;
    pi = 3.1415926;
    
    tic
    fprintf('Update start, t=%6.3f\n',t);
    x = u(1);
    y = u(2);
    yaw_angle =u(3)*pi/180;%CarSim输出的Yaw angle为角度,角度转换为弧度
    v = u(4) / 3.6;

    k = 0.1;                                  % look forward gain  前向预测距离所用增益
    Lfc = 3;                                  % 基础预瞄距离
    L = 2.7;                                % [m] wheel base of vehicle
    Ld = k * v + Lfc;
    
    N =  length(cx);
    ind = N;
    for i = N : -1 : 1
        distance = sqrt((cx(i)-x)^2 + (cy(i)-y)^2);
        if distance < Ld
            ind = i + 1;
            break;
        end
    end
    
    if ind > N
       ind = N; 
    end
   
    tx = cx(ind);
    ty = cy(ind);
    Ld = sqrt((tx-x)^2 + (ty-y)^2);
    alpha = atan((ty-y)/(tx-x))-yaw_angle;       %该处定义向左转为alpha=beta-Fai,所以向右转就输出-alpha
    delta = atan(2*L * sin(alpha)/Ld);                %前轮转角
    U = delta;
    sys= U; % vel, steering, x, y
    toc
% End of mdlOutputs.

注意处理接近终点的情况,不加限制的话容易出现绕着终点转圈的现象

在这里插入图片描述

限制后整体的跟踪效果尚可,但在终点处仍旧会出现异常的偏航,仍有较大的优化空间

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

大模型在代码缺陷检测领域的应用实践

作者 | 小新、车厘子 导读 静态代码扫描(SA)能快速识别代码缺陷&#xff0c;如空指针访问、数组越界等&#xff0c;以较高ROI保障质量及提升交付效率。当前扫描能力主要依赖人工经验生成规则&#xff0c;泛化能力弱且迭代滞后&#xff0c;导致漏出。本文提出基于代码知识图谱解…

【python基础】python切片—如何理解[-1:],[:-1],[::-1]的用法

文章目录 前言一、基本语法二、切片1.a[i:j]2.a[i:j:k] 总结&#xff1a;[-1] [:-1] [::-1] [n::-1] 前言 在python中&#xff0c;序列是python最基本的数据结构&#xff0c;包括有string&#xff0c;list&#xff0c;tuple等数据类型&#xff0c;切片对序列型对象的一种索引方…

Spring Boot Actuator 漏洞利用

文章目录 前言敏感信息泄露env 泄露配置信息trace 泄露用户请求信息mappings 泄露路由信息heapdump泄露堆栈信息 前言 spring对应两个版本&#xff0c;分别是Spring Boot 2.x和Spring Boot 1.x&#xff0c;因此后面漏洞利用的payload也会有所不同 敏感信息泄露 env 泄露配置信…

【音视频 | Ogg】RFC3533 :Ogg封装格式版本 0(The Ogg Encapsulation Format Version 0)

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Linux越学越头疼,我要怎么办?

最近&#xff0c;听到一些同学说&#xff0c;“Linux越学越头疼”。其实这句话&#xff0c;在我之前刚接触Linux的时候&#xff0c;也是深有感触。Linux越学越不明所以。最后干脆放弃学习&#xff0c;转而学习其他东西。 其实大家在初学Linux的时候&#xff0c; 有这个感受&am…

深度学习之基于Tensorflow卷积神经网络学生课堂坐姿姿势识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Tensorflow的卷积神经网络学生课堂坐姿姿势识别系统介绍 Tensorflow是一个流行的开源机器学习框架&#xff0c…

Vue项目运行时报错:‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件

报错原因及解决 1.package.json 文件中未定义依赖项vue/cli-service&#xff0c;因此在 npm install 之后并没有安装vue/cli-service 依赖&#xff1b; 解决&#xff1a;项目目录下执行命令&#xff0c;npm i -D vue/cli-service。2.第1步排查后&#xff0c;还是报同样的错&a…

软件测试面试题及答案2024

1、你们的缺陷等级如何划分的&#xff1f;☆☆☆☆☆ 我们的缺陷一般分为四个等级&#xff0c;致命级&#xff0c;严重级&#xff0c;一般级和轻微级。致命级指能够导致软件程序无法使用的缺陷&#xff0c;比如宕机&#xff0c;崩溃&#xff0c;手机APP的闪退&#xff0c;数据…

技术干货 | 基于Modelica的1553B总线模型设计

一、引言 1553B总线是一种常用于航空航天领域的数据总线标准&#xff0c;广泛应用于各类航天器和航空器中。对1553B总线系统进行建模仿真&#xff0c;有助于验证设计、测试功能和排除潜在故障&#xff0c;帮助开发人员提高工作效率、降低开发成本&#xff0c;具有重要的工程意义…

实现dialog在页面随意拖拽

实现dialog在页面随意拖拽 1.先建一个文件如图所示&#xff1a; 文件名:dialog-directive.js 文件内容&#xff1a; import Vue from vue // v-dialogDrag: 弹窗拖拽Vue.directive(dialogDrag, {bind(el, binding, vnode, oldVnode) {// 获取拖拽内容的头部const dialogHeade…

OpenGL ES入门教程(一)编写第一个OpenGL程序

OpenGL ES入门教程&#xff08;一&#xff09;编写第一个OpenGL程序 前言 从本文开始我将参考学习OpenGL ES应用开发实践指南 Android卷 [&#xff08;美&#xff09;KevinBrothaler著]&#xff08;提取码: 394m&#xff09;&#xff0c;并基于自己的理解以更加通俗易懂的方式…

Unity之NetCode多人网络游戏联机对战教程(5)--ConnectionData与MemoryPack

文章目录 前言使用场景ConnectionData数据序列化处理MemoryPack安装MemoryPack日志输出后话学习链接 前言 ConnectionData 与 ConnectionApproval 是搭配使用的&#xff0c;在ConnectionApproval系列讲解中涉及的几个使用场景将会在这里讲解 使用场景 使用密码加入房间 玩家选…

Leetcode—485.最大连续1的个数【简单】

2023每日刷题&#xff08;十五&#xff09; Leetcode—485.最大连续1的个数 实现代码 int findMaxConsecutiveOnes(int* nums, int numsSize){int max 0;int i;int flag 0;int cnt 0;for(i 0; i < numsSize; i) {if(nums[i] 1) {if(flag 0) {flag 1;cnt 1;} else {…

【框架篇】统一用户登录权限验证

✅作者简介&#xff1a;大家好&#xff0c;我是小杨 &#x1f4c3;个人主页&#xff1a;「小杨」的csdn博客 &#x1f433;希望大家多多支持&#x1f970;一起进步呀&#xff01; 统一用户登录权限验证 1&#xff0c;自定义拦截器 对于统一用户登录权限验证的问题&#xff0c…

413 (Payload Too Large) 2023最新版解决方法

文章目录 出现问题解决方法 出现问题 博主在用vue脚手架开发的时候&#xff0c;在上传文件的接口中碰到 这样一个错误&#xff0c;查遍所有csdn&#xff0c;都没有找到解决方法&#xff0c;通过一些方式&#xff0c;终于解决了。 解决方法 1.打开Vue项目的根目录。 2.在根目…

顺序表(2)

目录 Test.c主函数 test5 test6 test7 菜单 Test.c总代码 SeqList.h头文件&函数声明 头文件 函数声明 SeqList.h总代码 SeqList.c函数实现 查找SeqListFind 某位置插入SeqListInsert 某位置删除SeqListErase SeqList.c总代码 顺序表的问题及其思考 多文件…

Vue项目创建与启动(2023超详细的图文教程)

目录 一、下载node.js 二、下载vue-cli与webpack插件 三、项目初始化(项目配置详细信息) 四、项目启动 五、Vue项目工程结构&#xff08;扩展知识&#xff09; 一、下载node.js 1.检测是否已经安装过node.js 打开控制台,输入 npm -v如果有会显示对应版本 如果没有会显示…

如何看待腾讯云双11活动3年轻量服务器突然涨价?

腾讯云双十一优惠活动提供的3轻量应用服务器涨价了&#xff0c;最初双11优惠活动3年轻量2核4G5M服务器从566.6元涨价到756元三年&#xff0c;3年轻量2核2G4M服务器从366.6元恢复到540元三年&#xff0c;大家抓紧吧&#xff0c;三年轻量已经库存已经不多了&#xff0c;看看隔壁阿…

基于Electron27+React18+ArcoDesign客户端后台管理EXE

基于electron27.xreact18搭建电脑端exe后台管理系统模板 electron-react-admin 基于electron27整合vite.jsreact18搭建桌面端后台管理程序解决方案。 前几天有分享electron27react18创建跨平台应用实践&#xff0c;大家感兴趣可以去看看。 https://blog.csdn.net/yanxinyun1990…

云安全-云原生基于容器漏洞的逃逸自动化手法(CDK check)

0x00 docker逃逸的方法种类 1、不安全的配置&#xff1a; 容器危险挂载&#xff08;挂载procfs&#xff0c;Scoket&#xff09; 特权模式启动的提权&#xff08;privileged&#xff09; 2、docker容器自身的漏洞 3、linux系统内核漏洞 这里参考Twiki的云安全博客&#xff0c;下…