对pure pursuit算法的理解和改进

news2025/1/12 16:09:26

算法实现

purepursuit的核心其实是一个曲率半径的几何计算。

(x, y)是转换到机器人坐标系上的路径点。Llookahead distancer是形成的圆弧半径。Drx之间的差值。

根据上面的图形,可以发现有下面的几何关系:

同时通过
y 2 + D 2 = r 2 y^2 + D^2 = r^2 y2+D2=r2
可以推算出下面的等式关系:

曲率的定义为1 /radius,所以可得到下面的式子:

当确定好线速度时,就可以通过下面的方式计算出角速度:

angular_vel = linear_vel * curvature;

比较完整的算法描述可参考这篇文档:

https://www.ri.cmu.edu/pub_files/pub3/coulter_r_craig_1992_1/coulter_r_craig_1992_1.pdf

lookahead distance 调整的影响

image-20221108172747344

lookahead_distance值设置的较大时,机器会缓慢贴合到参考路径;

lookahead_distance值设置的较小时,机器会更快的贴近参考路径。因为没有一个完美的圆弧可以直接让机器贴合到参考路径,所以会产生一些振荡。lookahead_distance值设置的越小,振荡会越大。

没有唯一合适的lookahead_distance

事实上,我们希望针对参考路径的曲率来计算出一个合适的lookahead_distance值。 如果能找到这样一个一一对应关系,我们便可以方便地根据参考路径的曲率设置好lookahead_distance值。

但一个行走圆弧却不能对应唯一的lookahead_distance值。

下图是一个固定曲率的圆形参考路径。在路径上任意取一个目标点计算出的曲率都是一样的。因为无论目标点选在圆弧的何处都可以形成一个等腰三角形,使满足purepursuit的几何关系。

image-20221108193650788

但显然随着目标点的不一样,lookahead_distance值也是不一样的。

image-20221108182509445

目标点在圆弧的任意一个位置都满足车的行走路径为一个以r为半径的圆弧。事实上,lookahead_distance取0~2r都是可行的。所以,给定一个lookahead_distance值可以得到一个唯一的圆弧半径,但给定一个圆弧半径则不能得到一个唯一的lookahead_distance值。

lookahead_distance 的选取策略

从上面的分析可知,同样的曲率并不对应唯一的lookahead_distance值。但可分析出,如果路径比较弯曲,应该要选取较小的lookahead_distance值,这样才更贴合参考路径;路径比较直时,应该要选取较大的lookahead_distance值。执行速度也有同样的需求,路径比较直的时候可以采用更大的速度,路径比较弯曲时则需减少速度。只有路径特征,执行速度和lookahead_distance值达到一种协调的关系,路径跟随的效果才会比较好。

通过查阅一些相关论文和开源代码,整理了如下策略。

1、计算弯曲半径

根据当前点和当前点前后两点(可间隔)形成一个圆,求取该圆的半径。

//会检查三点是否共线
template <typename T>  
CircleData findCircle2(const T & pt1, const T & pt2, const T & pt3)
{
 
	//令:
	//A1 = 2 * pt2.x - 2 * pt1.x      B1 = 2 * pt1.y - 2 * pt2.y       C1 = pt1.y² + pt2.x² - pt1.x² - pt2.y²
	//A2 = 2 * pt3.x - 2 * pt2.x      B2 = 2 * pt2.y - 2 * pt3.y       C2 = pt2.y² + pt3.x² - pt2.x² - pt3.y²
	float A1, A2, B1, B2, C1, C2, temp;
	A1 = pt1.x - pt2.x;
	B1 = pt1.y - pt2.y;
	C1 = (pow(pt1.x, 2) - pow(pt2.x, 2) + pow(pt1.y, 2) - pow(pt2.y, 2)) / 2;
	A2 = pt3.x - pt2.x;
	B2 = pt3.y - pt2.y;
	C2 = (pow(pt3.x, 2) - pow(pt2.x, 2) + pow(pt3.y, 2) - pow(pt2.y, 2)) / 2;
	//为了方便编写程序,令temp = A1*B2 - A2*B1
	temp = A1*B2 - A2*B1;

  // std::cout << "temp = " << temp << std::endl;
	//定义一个圆的数据的结构体对象CD
	CircleData CD;
	//判断三点是否共线
	if (temp == 0){
		//共线则将第一个点pt1作为圆心
		CD.center.x = pt1.x;
		CD.center.y = pt1.y;
    CD.radius = 5.0;
    
    return CD;
	}
	else{
		//不共线则求出圆心:
		//center.x = (C1*B2 - C2*B1) / A1*B2 - A2*B1;
		//center.y = (A1*C2 - A2*C1) / A1*B2 - A2*B1;
		CD.center.x = (C1*B2 - C2*B1) / temp;
		CD.center.y = (A1*C2 - A2*C1) / temp;
	}

	//共线时将第一个点pt1作为圆心,此时计算出来的半径为0
  //计算圆心到其中一个ie点的距离作为半径
	CD.radius = sqrtf((CD.center.x - pt1.x)*(CD.center.x - pt1.x) + (CD.center.y - pt1.y)*(CD.center.y - pt1.y));
	return CD;
}

代码来源于:https://blog.csdn.net/lijiayu2015/article/details/52541730/

采用此方法计算整条路径上每个点弯曲半径。

2、设置速度

根据执行路径上距离车体最近的点的弯曲半径来调整车体速度。

可采用如下关系来设置期望速度:

弯曲半径 大于 最大阈值(路径越直弯曲半径越大),设置期望速度为最大速度;

弯曲半径 小于 最小阈值(路径越弯弯曲半径越小),设置期望速度为最小速度;

弯曲半径在最大和最小阈值中间则使用最大速度*调整系数来设置期望速度。 但需确保期望速度不小于最小速度。

限制速度变化率

class RateLimiter {
 public:
  RateLimiter() = default;
  ~RateLimiter() = default;

  double limitRateOfChange(double value);
  void setRisingRate(double maxRisingRate);
  void setFallingRate(double minFallingRate);
  void setTimestep(double dt);
  void reset(double startingValue = 0.0);

 private:
  double dt_ = 0.01;
  double maxRisingRate_ = 1.0;
  double minFallingRate_ = -1.0;
  double valuePrev_ = 0.0;
};


double RateLimiter::limitRateOfChange(double value) {
  double retValue = value;
  if (value > valuePrev_ + dt_ * maxRisingRate_) {
    retValue = valuePrev_ + dt_ * maxRisingRate_;
  }

  if (value < valuePrev_ + dt_ * minFallingRate_) {
    retValue = valuePrev_ + dt_ * minFallingRate_;
  }

  valuePrev_ = retValue;

  return retValue;
}
void RateLimiter::setRisingRate(double maxRisingRate) {
  if (maxRisingRate < 0) {
    throw std::runtime_error("Rising rate cannot be negative.");
  }
  maxRisingRate_ = maxRisingRate;
}
void RateLimiter::setFallingRate(double minFallingRate) {
  if (minFallingRate > 0) {
    throw std::runtime_error("Falling rate cannot be positive");
  }
  minFallingRate_ = minFallingRate;
}
void RateLimiter::setTimestep(double dt) {
  if (dt < 0) {
    throw std::runtime_error("Time step cannot be negative");
  }
  dt_ = dt;
}

void RateLimiter::reset(double startingValue) {
  valuePrev_ = startingValue;
}

开源代码来源于:https://github.com/leggedrobotics/se2_navigation.git

**处理接近终点的情况。**速度还需根据距离终点的距离线性调整。参考开源代码如下:

bool AdaptiveVelocityController::computeVelocity() {
  double referenceVelocity = 0.0;
  switch (drivingDirection_) {
    case DrivingDirection::FWD: {
      referenceVelocity = parameters_.desiredVelocity_;
      break;
    }
    case DrivingDirection::BCK: {
      referenceVelocity = -parameters_.desiredVelocity_;
      break;
    }
  }

  // should be path integral strictly speaking
  const double distanceToGoal = (currentRobotState_.pose_.position_ - currentPathSegment_.point_.back().position_).norm();
  const double dWhenBrakingStarts = parameters_.distanceToGoalWhenBrakingStarts_;//距离目标点多远开始刹车
  if (distanceToGoal <= dWhenBrakingStarts) {
    const double slope = parameters_.desiredVelocity_ / dWhenBrakingStarts;
    const double referenceVelocityMagnitude = slope * distanceToGoal;
    const double direction = sgn(referenceVelocity);
    referenceVelocity = direction * referenceVelocityMagnitude;
  }

  desiredLongitudinalVelocity_ = rateLimiter_.limitRateOfChange(referenceVelocity);

  return true;
}

3、设置lookahead_distance

根据Real-time Motion Planning with Applications to Autonomous Urban Driving这篇论文中提到的方法。这里根据上面输出的期望速度来调整lookahead_distance值。

上图的比例关系是论文中针对乘用车设置的。在实际工程项目中应该还需要根据实际情况调整。

4、适时进行原地旋转

通过lookahead_distance值选取lookahead point。计算lookahead point相对于机器人位置的朝向与机器人朝向的差值,如果差值太大就进行原地旋转。

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

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

相关文章

java项目-第134期ssm社团管理系统-java毕业设计

java项目-第134期ssm社团管理系统-毕业设计 【源码请到资源专栏下载】 今天分享的项目是《社团管理系统》 该项目分为前台和后台。主要分成两个角色&#xff1a;普通用户、管理员角色。 普通用户登录前台&#xff0c;看到社团官网发布的一些信息。 比如&#xff1a;首页、新闻…

如何在 .NET MAUI 中加载 json 文件?

引言: 按core传统方式添加 AddJsonFile(“appsettings.json”) 在windows平台和ssr工作正常,但是在 ios 和 android 无法用这种方式,因为资源生成方式不一样. 使用内置资源方式不够灵活而且 ios 平台会提示不能复制 json 文件到目录,于是进行了几天的研究,终于能正确使用了. 资…

1-STM32之GPIO点亮LED

我们在基础部分讲了有关GPIO的方面&#xff0c;从这章开始我们进入模块的讲解&#xff0c;从最开始的LED灯到各种传感器模块进行。专栏预计25个章节。后续可能会不定时的增加。 本专栏芯片为STM32F429 对于工程的移植和新建这里不做讲解&#xff0c;对工程建立不懂得&#xff0…

Vue3 - watch 侦听器(超详细使用教程)

前言 它之所以叫侦听器呢&#xff0c;是因为它可以侦听一个或多个响应式数据源&#xff0c;并在数据源变化时调用所给的回调函数。 大白话说呢&#xff0c;就是你传给 watch 侦听器一个响应式变量&#xff0c;然后当这个变量变化时&#xff0c;自动触发一个你定义的函数&#x…

超实用Word小技巧,常用但很少有人记得住

我们在日常工作中经常使用 Word 进行办公。以下常用Word提示可以为您的工作节省时间和精力&#xff0c;让我们来看看。 技巧一&#xff1a;如何纵向复制文本我们一般水平选择文本&#xff0c;你有没有想过垂直选择文本&#xff1f;先按住【Alt】键&#xff0c;然后拖动鼠标左键…

【Python百日进阶-WEB开发】Day179 - Django案例:11短信验证码

文章目录九、短信验证码9.1 短信验证码逻辑分析9.2 容联云通讯短信平台9.2.1 容联云通讯短信平台介绍9.2.2 容联云通讯短信SDK测试9.2.2.1 美多商城meiduo_mall.apps.verifications.libs中新建yuntongxun包&#xff0c;结构如下&#xff1a;9.2.2.2 ccp_sms.py代码9.2.2.3 CCPR…

一起来庆祝属于GISer的节日GIS DAY!

01 概述 作为一名GISer的你&#xff0c;有没有想过其实我们GISer也有自己的节日&#xff1f;这个节日便是GIS DAY&#xff0c;今年的GIS DAY恰在今天&#xff08;2022年11月16日&#xff09;。究竟什么是GIS DAY&#xff1f;这里为大家介绍一下这个节日。 02 什么是GIS DAY …

vue的移动端项目打包成手机的app软件apk格式

目录 前提准备&#xff1a; 1、vue项目npm run build打包成dist文件夹 2、注册hbuilderx账号&#xff0c;获取appid 步骤 一、创建h5app空模版 二、 将打包完成生成dist文件目录复制到新建的项目里 三、检测打包的index.html是否白屏 四、 配置manifest.js应用入口页面…

作为项目经理必须具备的能力

作为项目管理者&#xff0c;每天都要应对项目中的所有问题&#xff0c;安排任务&#xff0c;还要照顾下属的情绪。管理者应该怎么做。 1、计划制定 项目经理作为项目管理者&#xff0c;需要制定计划&#xff0c;合理化分配任务。 项目经理可以使用甘特图制定项目计划&#xf…

微服务feign接口声明的3种方式使用与分析

前言 feign调用方式是微服务调用最为广泛的使用方式&#xff0c;feign接口声明位置也是比较关键的一环。目前来说&#xff0c;feign的3种接口声明方式各自存在利弊&#xff0c;并不存在最优解决方案&#xff0c;只能根据需求去选择。本文中不作详细项目搭建过程&#xff0c;但…

做3D建模的女生多吗?揭露行业比列

有&#xff0c;但是不多&#xff0c;这是常态✅ 其实就像是IT领域的男女比例一样&#xff0c;但是也不是没有。更何况现在女孩子顶半边天&#xff0c;遇到领导是女生的也不少&#xff0c;未来的情况如何也不能完全的预估。 • ❤️事业是热爱做的事&#xff0c;工作是不得不做…

索引【MySQL】

1.1 概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c; 并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 1.2 作用 数据库中的表、数据、索引之间的关系&#xff0c;类似于书架上的图书、…

简洁直观解释精确率、召回率、F1 值、ROC、AUC

混淆矩阵 当我们在做二分类预测时&#xff0c;把预测情况与实际情况的所有结果两两混合&#xff0c;结果就会出现以下4种情况&#xff0c;就组成了混淆矩阵。 P&#xff08;Positive&#xff09;&#xff1a;代表正样本N&#xff08;Negative&#xff09;&#xff1a;代表负样…

在vscode中开发sass教程:sass语法

sass官网&#xff1a;Sass世界上最成熟、稳定和强大的CSS扩展语言 | Sass中文网sass&#xff1a;世界上最成熟、最稳定、最强大的专业级css扩展语言&#xff01;sass是一个css的预编译工具&#xff0c;也就是能够更优雅的书写css&#xff1b;1、sass使用说明&#xff1a; 基于…

【毕业设计】后端实现——账单通过关键词简单分析收支

&#x1f308;据说&#xff0c;看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。 前言&#xff1a; &#x1f9e1;作者简介&#xff1a;大家好我是 user_from_future &#xff0c;意思是 “ 来自未来的用户 ” &#xff0c;寓意着未来的自己一定很棒~ ✨个…

C++ STL中的set详解

前言 在学习csp题解的时候接触到这个数据结构&#xff0c;故在此记录一下其概念及应用。 基本概念 set的底层采用的是红黑树&#xff0c;所有元素都会根据元素的键值自动排序&#xff0c;方便管理元素&#xff0c;但不支持直接修改键值。 应用 头文件调用 #include <i…

python中is和==的区别,地址和重新复制后,地址变化

简单总结 现象描述&#xff1a;一、“编辑器“中和把赋值语句放在”同一行的cmd环境“中&#xff0c;相同值的不同变量会指向同一个地址 二、交互式/cmd环境中&#xff0c;若赋值相同值的变量在不同行输入&#xff0c;那么变量也会指向不同地址 三、程序编辑器的程序运行就相当…

B2B撮合管理系统优势有哪些?如何助力传统仪器仪表制造业企业数字化转型

仪器仪表制造业是我国制造业的重要组成部分&#xff0c;经过多年的快速发展&#xff0c;我国仪器仪表制造业生产和开发能力产业体系日趋庞大&#xff0c;产销增幅也是高歌猛进&#xff0c;对推动国民经济发展具有重要意义。 然而&#xff0c;随着我国仪器仪表制造业的发展&…

GAN模型1

本次是用GAN模型弄出时光机效果~ 穿越时空的人脸 得到你100年前的样子~~ 时光穿梭第一步&#xff1a;解决数据集难题 鉴于最近StyleGAN在高质量人脸合成和编辑方面的成功&#xff0c;许多工作都集中在使用预先训练好的StyleGAN模型进行人像编辑。 然而&#xff0c;现有的技术…

[附源码]计算机毕业设计JAVA儿童资源教育网站

[附源码]计算机毕业设计JAVA儿童资源教育网站 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…