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];// 当前点和目标点的距离Idfloat 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[] posespointNum = 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; % Initializationcase 2sys = mdlUpdates(t,x,u); % Update discrete statescase 3sys = mdlOutputs(t,x,u); % Calculate outputscase {1,4,9} % Unused flagssys = [];otherwiseerror(['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;ticfprintf('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 vehicleLd = k * v + Lfc;N =  length(cx);ind = N;for i = N : -1 : 1distance = sqrt((cx(i)-x)^2 + (cy(i)-y)^2);if distance < Ldind = i + 1;break;endendif ind > Nind = N; endtx = cx(ind);ty = cy(ind);Ld = sqrt((tx-x)^2 + (ty-y)^2);alpha = atan((ty-y)/(tx-x))-yaw_angle;       %该处定义向左转为alpha=beta-Fai,所以向右转就输出-alphadelta = atan(2*L * sin(alpha)/Ld);                %前轮转角U = delta;sys= U; % vel, steering, x, ytoc
% End of mdlOutputs.

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

在这里插入图片描述

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

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

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

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

相关文章

如何巧妙公布成绩

宝子们&#xff0c;来来来&#xff01;听说你们对如何公布学生成绩很头疼&#xff1f;别担心&#xff0c;今天就让我来给大家支支招&#xff01; 1在家长群内发公告&#xff0c;孩子的成绩已出&#xff0c;想知道具体成绩可以私信哦&#xff5e;简单粗暴&#xff01;关心孩子的…

频谱仪超外差和零中频架构

文章目录 超外差结构零中频结构接收机结构发射机结构 优缺点对比附录相关词汇多次变频的形象解释 参考文献 频谱仪的本质就是一个超宽带、超宽调谐范围、高动态范围的通信接收机&#xff0c; 频谱仪的原理即通信接收机的原理。 遇到高频率高带宽谐波成分复杂的通信信号的话&am…

【网络协议】聊聊HTTPS协议

前面的文章&#xff0c;我们描述了网络是怎样进行传输数据包的&#xff0c;但是网络是不安全的&#xff0c;对于这种流量门户网站其实还好&#xff0c;对于支付类场景其实容易将数据泄漏&#xff0c;所以安全的方式是通过加密&#xff0c;加密方式主要是对称加密和非对称加密。…

【蓝桥杯选拔赛真题08】C++最大值最小值平均值 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析

目录 C/C++最大值最小值平均值 一、题目要求 1、编程实现 2、输入输出 二、算法分析</

gcc/g++使用格式+各种选项,预处理/编译(分析树,编译优化,生成目标代码)/汇编/链接过程(函数库,动态链接)

目录 gcc/g--编译器 介绍 使用格式 通用选项 编译选项 链接选项 程序编译过程 预处理(宏替换) 编译 (生成汇编) 分析树(parse tree) 编译优化 删除死代码 寄存器分配和调度 强度削弱 内联函数 生成目标代码 汇编 (生成二进制代码) 链接(生成可执行文件) 函…

数据抽取+dataworks的使用+ADB的应用

一&#xff0c;大数据处理之数据抽取 1&#xff0c;什么是数据抽取 在大数据领域中&#xff0c;数据抽取是指从原始数据源中提取所需的数据子集或特定数据项的过程&#xff0c; 数据抽取是数据预处理的重要步骤&#xff0c;它为后续的数据分析和建模提供了基础。 2&#xff…

嵌入式linux常用的文件传输方式

做嵌入式就避免不了移植工作&#xff0c;所谓移植就是将交叉编译生成的可执行程序&#xff0c;库&#xff0c;配置文件等传输到开发板上进行工作。 常用传输方式有以下几种&#xff1a;1.串口传输 就是使用串口传输工具rz/sz; 该工具通过串口传输在SRT串口工具…

什么是用户体验测试? 为什么很重要?

在当今数字化时代&#xff0c;用户体验(User Experience&#xff0c;简称UX)已经成为产品成功的关键因素之一。无论是应用程序、网站、硬件设备还是软件&#xff0c;提供出色的用户体验不仅能够吸引更多用户&#xff0c;还能够增加用户满意度&#xff0c;提高品牌忠诚度&#x…

残差网络ResNet

残差网络的提出,是为了解决深度学习中的退化问题。 退化问题指的是随着神经网络层数的增加&#xff0c;网络性能反而逐渐降低的现象。换句话说&#xff0c;当我们不断增加神经网络的层数时&#xff0c;神经网络的训练误差可能会持续下降&#xff0c;但是验证集误差却不断增加&…

前端项目 index.html 中发请求 fetch

想要在前端项目 index.html文件中向后端发起请求&#xff0c;但是引入axios报错&#xff08;我这边会报错&#xff09;&#xff0c;可以使用fetch。 //window.location.origin----获取域名&#xff0c;包括协议、主机号、端口号fetch(window.location.origin "/api/pla…

MPLAB X IDE 仿真打断点提示已中断的断点?

这种中间带裂缝的是无效断点。 原因可能与XC编译器的优化有关&#xff0c;最后生成的汇编与C语言并不是一一对应的(官方给的解释是效率高)。所以这一行C语言转换的汇编代码可能并不在这个位置&#xff0c;也可能与其它汇编合并后根本就没有 我的解决方法是把优化等级调到最低&a…

2014年亚太杯APMCM数学建模大赛A题无人机创造安全环境求解全过程文档及程序

2014年亚太杯APMCM数学建模大赛 A题 无人机创造安全环境 原题再现 20 国集团&#xff0c;又称 G20&#xff0c;是一个国际经济合作论坛。2016 年第 11 届 20 国集团峰会将在中国召开&#xff0c;这是继 APEC 后中国将举办的另一个大型峰会。此类大型峰会&#xff0c;举办城市…

prometheus服务发现

Consul简介 ◼ 一款基于golang开发的开源工具&#xff0c;主要面向分布式&#xff0c;服务化的系统提供服务注册、服务发现和配置管理 的功能 ◼ 提供服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等功能 部署 curl -LO https://releases.hashicorp…

保障效率与可用,分析Kafka的消费者组与Rebalance机制

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…

opengl基础笔记1

1、opengl运行模式及opengl规范 运行模式&#xff1a;核心模式与立即渲染模式&#xff08;弃用&#xff09; 由于OpenGL的大多数实现都是由显卡厂商编写的&#xff0c;当产生一个bug时通常可以通过升级显卡驱动来解决。这些驱动会包括你的显卡能支持的最新版本的OpenGL&#xf…

YOLOv8将注意力机制融合进入C2f模块

1. 引言 1.1 YOLOv8添加注意力机制方法 yolov8添加注意力机制是一个非常常见的操作&#xff0c;常见的操作直接将注意力机制添加至YOLOv8的某一层之后&#xff0c;这种改进特别常见。 示例如下&#xff1a; 新版yolov8添加注意力机制&#xff08;以NAMAttention注意力机制为例…

鸿蒙问题记录

1、Variables decorated by Prop link, "Consume, and Obiectlink cannot be initialized locally 原因&#xff1a;被装饰器修饰的数据&#xff0c;不能初始化。这个应该是后续版本做了优化。当前使用 DevEco Studio 3.1.1 Release

Web3时代:探索DAO的未来之路

Web3 的兴起不仅代表着技术进步&#xff0c;更是对人类协作、创新和价值塑造方式的一次重大思考。在 Web3 时代&#xff0c;社区不再仅仅是共同兴趣的聚集点&#xff0c;而变成了一个价值交流和创新的平台。 去中心化&#xff1a;超越技术的革命 去中心化不仅仅是 Web3 的技术…

CRM系统如何帮助企业实现管理信息化?

21世纪的今天&#xff0c;企业不重视CRM信息化会导致什么后果&#xff1f;我们先来看这个例子—— 假设有一家中小型电子商务公司&#xff0c;他们销售各种电子产品&#xff0c;如手机、平板、电脑和配件等。在开始使用CRM系统之前&#xff0c;他们的客户数据分散在各个部门的…

Redis高可用解决方案之Redis集群,和Spring Cloud集成实战

专栏集锦&#xff0c;大佬们可以收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https:/…