ROS Navigation Stack之dwa_local_planner源码分析

2024-04-27 01:58

本文主要是介绍ROS Navigation Stack之dwa_local_planner源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DWA和base_local_planner的关系

在base_local_planner包中有两个文件叫trajectory_planner.cpp 以及对应的ros实现,其和DWA是同一层的。
由于nav_core提供了统一的接口,因此我们可以先看看统一的接口有哪些,那我们便知道每一个算法里比较重要的函数有哪些。

nav_core包里的base_local_planner.h文件
//最为关键的地方,计算机器人下一刻的速度
virtual bool computeVelocityCommands(geometry_msgs::Twist& cmd_vel) = 0;
//判断是否到达目标点
virtual bool isGoalReached() = 0;
//加载全局路径
virtual bool setPlan(const std::vector<geometry_msgs::PoseStamped>& plan) = 0;
//初始化
virtual void initialize(std::string name, tf::TransformListener* tf, costmap_2d::Costmap2DROS* costmap_ros) = 0;

下面我们就先看看base_local_planner的computeVelocityCommands的主要实现框架

bool TrajectoryPlannerROS::computeVelocityCommands(geometry_msgs::Twist& cmd_vel)
{//检查初始化、检查是否已经到达目标点...略transformGlobalPlan(*tf_, global_plan_, global_pose, *costmap_, global_frame_, transformed_plan);//如果已经到达目标点,姿态还没到if (xy_tolerance_latch_ || (getGoalPositionDistance(global_pose, goal_x, goal_y) <= xy_goal_tolerance_)) {tc_->updatePlan(transformed_plan);//所以这个函数里最关键的子函数是findBestPathTrajectory path = tc_->findBestPath(global_pose, robot_vel, drive_cmds);return true;}tc_->updatePlan(transformed_plan);Trajectory path = tc_->findBestPath(global_pose, robot_vel, drive_cmds);//然后又是转换,然后就发布出速度了...
}

接下来我们看一下TrajectoryPlanner的findBestPath的实现框架,Come on~

Trajectory TrajectoryPlanner::findBestPath(tf::Stamped<tf::Pose> global_pose, tf::Stamped<tf::Pose> global_vel,tf::Stamped<tf::Pose>& drive_velocities)
{//...Trajectory best = createTrajectories(pos[0], pos[1], pos[2], vel[0], vel[1], vel[2],acc_lim_x_, acc_lim_y_, acc_lim_theta_);//...
}

顺藤摸瓜,一睹createTrajectories的内部实现,这个函数是轨迹采样算法,可以说是一个非常关键的函数。

Trajectory TrajectoryPlanner::createTrajectories(double x, double y, double theta,double vx, double vy, double vtheta,double acc_x, double acc_y, double acc_theta) 
{//检查最终点是否是有效的,判断变量在updatePlan中被赋值if( final_goal_position_valid_ ){double final_goal_dist = hypot( final_goal_x_ - x, final_goal_y_ - y );max_vel_x = min( max_vel_x, final_goal_dist / sim_time_ );}//是否使用dwa算法, sim_peroid_是1/controller_frequency_,暂时不清楚sim_period_和sim_time_的区别if (dwa_){max_vel_x = max(min(max_vel_x, vx + acc_x * sim_period_), min_vel_x_);min_vel_x = max(min_vel_x_, vx - acc_x * sim_period_);max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_period_);min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_period_);}else{max_vel_x = max(min(max_vel_x, vx + acc_x * sim_time_), min_vel_x_);min_vel_x = max(min_vel_x_, vx - acc_x * sim_time_);max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_time_);min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_time_);}//...先忽略其中的逻辑,只要知道按照不同的规则生成路径,调用的子函数是generateTrajectory
}

这个子函数的作用就是生成路径,并且评分

void TrajectoryPlanner::generateTrajectory
{//主要有两大作用://生成路径和速度vx_i = computeNewVelocity(vx_samp, vx_i, acc_x, dt);vy_i = computeNewVelocity(vy_samp, vy_i, acc_y, dt);vtheta_i = computeNewVelocity(vtheta_samp, vtheta_i, acc_theta, dt);//计算位置x_i = computeNewXPosition(x_i, vx_i, vy_i, theta_i, dt);y_i = computeNewYPosition(y_i, vx_i, vy_i, theta_i, dt);theta_i = computeNewThetaPosition(theta_i, vtheta_i, dt);//对路径进行评分if (!heading_scoring_) {//cost = pdist_scale_ * path_dist + goal_dist * gdist_scale_ + occdist_scale_ * occ_cost;} else {cost = occdist_scale_ * occ_cost + pdist_scale_ * path_dist + 0.3 * heading_diff + goal_dist * gdist_scale_;}//这里的顺序与源码不同,我觉得总分来看更有组织性//该轨迹与全局路径的相对距离path_dist = path_map_(cell_x, cell_y).target_dist;//距离目标点距离goal_dist = goal_map_(cell_x, cell_y).target_dist;//离障碍物距离double footprint_cost = footprintCost(x_i, y_i, theta_i);occ_cost = std::max(std::max(occ_cost, footprint_cost), double(costmap_.getCost(cell_x, cell_y)));
}

综上所述,其整一个逻辑顺序就是computeVelocityCommands->findBestTrajectory --> createTrajectories --> generateTrajectory

最终,选择分数最低的轨迹,发布出去。这便是整个局部规划器的实现思路和逻辑。下一篇,谈谈Costmap2D。

这篇关于ROS Navigation Stack之dwa_local_planner源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1