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

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja

Nginx屏蔽服务器名称与版本信息方式(源码级修改)

《Nginx屏蔽服务器名称与版本信息方式(源码级修改)》本文详解如何通过源码修改Nginx1.25.4,移除Server响应头中的服务类型和版本信息,以增强安全性,需重新配置、编译、安装,升级时需重复... 目录一、背景与目的二、适用版本三、操作步骤修改源码文件四、后续操作提示五、注意事项六、总结一、背景与

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码