当前位置: 首页 > news >正文

45.在ROS中实现global planner(1)

前文move_base介绍(4)简单介绍move_base的全局路径规划配置,接下来我们自己实现一个全局的路径规划

1. move_base规划配置

ROS1move_base可以配置选取不同的global plannerlocal planner, 默认move_base.cpp#L70中可以看到是读取该参数决定的`

    private_nh.param("base_global_planner", global_planner, std::string("navfn/NavfnROS"));private_nh.param("base_local_planner", local_planner, std::string("base_local_planner/TrajectoryPlannerROS"));

我们可以通过配置base_global_plannerbase_local_planner参数修改不同的算法

ros1 navigation中提供了3种base_global_planner, 分别是

  • navfn/NavfnROS
  • global_planner::GlobalPlanner
  • carrot_planner/CarrotPlanner

下面我们自己实现一个全局的路径规划,并在模拟器测试其执行效果

2. 实现原理

2.1 加载对象

private_nh.param("base_global_planner", global_planner, std::string("navfn/NavfnROS"));

上面我们已经知道 通过参数配置来决定加载哪一个全局规划器,继续跟踪可以看到

查看源码 move_base.cpp#L125 & move_base.h#L210

pluginlib::ClassLoader<nav_core::BaseGlobalPlanner> bgp_loader_;
planner_ = bgp_loader_.createInstance(global_planner);

pluginlib可以参见这里

  • pluginlib::ClassLoader<nav_core::BaseGlobalPlanner>::createInstance根据输入参数名,加载so,并且获取到库的导出类,且创建该类的一个实例
  • planner_即为该指向该实例的指针, 有了这个对象,就可以通过该成员干活了

2.2 BaseGlobalPlanner接口

planner_定义在move_base.h#L185

boost::shared_ptr<nav_core::BaseGlobalPlanner> planner_;

前面返回的planner_类型可以看到是nav_core::BaseGlobalPlanner类型,我们先来看下该类,在nav_core#L48

class BaseGlobalPlanner{public:virtual bool makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan) = 0;virtual bool makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan,double& cost){cost = 0;return makePlan(start, goal, plan);}virtual void initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros) = 0;virtual ~BaseGlobalPlanner(){}protected:BaseGlobalPlanner(){}};

可以看到该类是一个接口类,需要继承该接口做相应的实现,主要接口比较简单,就两个, initializemakePlan, 顾名思义一个初始化,一个规划路径

  • initialize
    传入了name, 以及地图信息
  • makePlan
    传入起点,目标点,返回plan

我们也可以看看在move_base对应接口的调用

  • move_base.cpp#L126
    在创建完成立即调用完成初始化

  • move_base.cpp#L496
    进行全局路径规划

if(!planner_->makePlan(start, goal, plan) || plan.empty()){...}

``
在MoveBase::makeplan调用了该函数,返回的plan, 保存后用于local planner的输入

3. 实现global planner

3.1 实现步骤

实现一个自己的全局规划需要下面几个步骤

  • 继承nav_core::BaseGlobalPlanner实现接口
  • 导出该实现类
  • 添加plugin.xml插件描述文件并导出
  • 修改move_base配置使用

3.2 实现接口

  • 创建包
mkdir -p ~/pibot_ros/ros_ws/src
cd ~/pibot_ros/ros_ws/src
catkin_create_pkg sample_global_planner

创建完成添加一个cpp和h文件,新增一个类继承与nav_core::BaseGlobalPlanner
上面已经看到该接口定义 我们继承并对两个接口initializemakePlan实现即可

  • initialize
    初始化我们暂时先空实现
void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2DROS *costmap_ros)
{
}
  • makePlan
    规划路径的接口给我们输入起点和终点,我们输出规划出的plan(如可以规划,同时返回true,反之返回false), 我们暂时不考虑具体实现,输出一条从起点到终点的直线路径,这应该是初中几何知识,比较简单如下
bool GlobalPlanner::makePlan(const geometry_msgs::PoseStamped &start,const geometry_msgs::PoseStamped &goal, std::vector<geometry_msgs::PoseStamped> &plan)
{ROS_INFO("make plan start:[%f %f], goal:[%f %f]", start.pose.position.x, start.pose.position.y, goal.pose.position.x, goal.pose.position.y);plan.clear();float yaw = atan2(goal.pose.position.y - start.pose.position.y, goal.pose.position.x - start.pose.position.x);int n = 0;float goal_distance = sqrt(pow((start.pose.position.x - goal.pose.position.x), 2) + pow((start.pose.position.y - goal.pose.position.y), 2));float delta = 0.1; // 间隔delta输出start至end的直线上的点 我们间隔0.1取直线上的所有点,放到输出的参数plan里while (n * delta < goal_distance){geometry_msgs::PoseStamped pose = goal;pose.pose.position.x = (n * delta) * cos(yaw) + start.pose.position.x;pose.pose.position.y = (n * delta) * sin(yaw) + start.pose.position.y;++n;plan.push_back(pose);}plan.push_back(goal); // 这里别忘了终点return !plan.empty();
}
  • 添加相应的CMakeList.txt
## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(include${catkin_INCLUDE_DIRS}
)## Declare a C++ library
add_library(${PROJECT_NAME}src/planner_node.cpp
)## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES})

3.3 导出类

参考navigation里面, 添加宏导出该类

PLUGINLIB_EXPORT_CLASS(sample_global_planner::GlobalPlanner, nav_core::BaseGlobalPlanner)

3.3 添加plugin.xml

添加一个bgp_plugin.xml

<library path="lib/libsample_global_planner"><class name="sample_global_planner/GlobalPlanner" type="sample_global_planner::GlobalPlanner" base_class_type="nav_core::BaseGlobalPlanner"><description>A sample implementation of a grid based planner </description></class>
</library>

3.4 编译

cd ~/pibot_ros/ros_ws
catkin_make

3.5 修改配置测试

修改~/pibot_ros/src/pibot_simulator/move_base_params.yaml

# base_global_planner: global_planner/GlobalPlanner
base_global_planner: sample_global_planner/GlobalPlanner

global_planner/GlobalPlanner ----> sample_global_planner/GlobalPlanner

  • 启动模拟器
pibot_simulator
  • 查看当前的global_planner
❯ rosparam get /move_base/base_global_planner
sample_global_planner/GlobalPlanner  # 输出sample_global_planner/GlobalPlanner表示插件已经被正确加载
  • 启动rviz发送点位,选点导航测试
pibot_view

3.6 路径显示

上面测试可以看到可以规划已经完成, dwa的局部规划已经启动, 为了方便查看全局全规划路径的输出,我们在makeplan完成后发出pathtopic

void GlobalPlanner::publishPlan(const std::vector<geometry_msgs::PoseStamped> &path)
{nav_msgs::Path gui_path;gui_path.poses.resize(path.size());gui_path.header.frame_id = frame_id_;gui_path.header.stamp = ros::Time::now();for (unsigned int i = 0; i < path.size(); i++){gui_path.poses[i] = path[i];}plan_pub_.publish(gui_path);
}

把 rviz Global MapLocal Map中的dwa planner关闭, 只显示Full Plan

修改move_base_params.yamlplanner_frequency值, 0 只规划一次, >0 规划频率

3.7 测试结果

  • 选择空旷区域,可以看到可以正常规划,同时控制也可以启动完成,到达目的地
    plan2
  • 跨过障碍物,可以看到规划出路径,显然无法控制过去
    plan1

4. 总结

本文简单实现了一个global planner的插件,显然实际没啥用,不过可以作为一个模板,基于该模板实现自己的算法。后面我们将基于该模板实现可用的全局规划。

本文代码见sample_global_planner

相关文章:

45.在ROS中实现global planner(1)

前文move_base介绍&#xff08;4&#xff09;简单介绍move_base的全局路径规划配置&#xff0c;接下来我们自己实现一个全局的路径规划 1. move_base规划配置 ROS1的move_base可以配置选取不同的global planner和local planner&#xff0c; 默认move_base.cpp#L70中可以看到是…...

Java中导入、导出Excel——HSSFWorkbook 使用

一、介绍 当前B/S模式已成为应用开发的主流&#xff0c;而在企业办公系统中&#xff0c;常常有客户这样子要求&#xff1a;你要把我们的报表直接用Excel打开(电信系统、银行系统)。或者是&#xff1a;我们已经习惯用Excel打印。这样在我们实际的开发中&#xff0c;很多时候需要…...

c#数据结构-列表

列表 数组可以管理大量数组&#xff0c;但缺点是无法更变容量。 创建小了不够用&#xff0c;创建大了浪费空间。 无法预测需要多少大小的时候&#xff0c;可能范围越大&#xff0c;就会浪费越多的空间。 所以&#xff0c;你可能会想要一种可以扩容的东西&#xff0c;代替数组…...

Sa-Token实现分布式登录鉴权(Redis集成 前后端分离)

文章目录1. Sa-Token 介绍2. 登录认证2.1 登录与注销2.2 会话查询2.3 Token 查询3. 权限认证3.1 获取当前账号权限码集合3.2 权限校验3.3 角色校验4. 前后台分离&#xff08;无Cookie模式&#xff09;5. Sa-Token 集成 Redis6. SpringBoot 集成 Sa-Token6.1 创建项目6.2 添加依…...

leaflet显示高程

很多地图软件都能随鼠标移动动态显示高程。这里介绍一种方法&#xff0c;我所得出的。1 下载高程数据一般有12.5m数据下载&#xff0c;可惜精度根本不够&#xff0c;比如mapbox的免费在线的&#xff0c;或者91卫图提供百度网盘打包下载的&#xff0c;没法用&#xff0c;差距太大…...

电子学会2022年12月青少年软件编程(图形化)等级考试试卷(三级)答案解析

目录 一、单选题(共25题&#xff0c;共50分) 二、判断题(共10题&#xff0c;共20分) 三、编程题(共3题&#xff0c;共30分) 青少年软件编程&#xff08;图形化&#xff09;等级考试试卷&#xff08;三级&#xff09; 一、单选题(共25题&#xff0c;共50分) 1. 默认小猫角色…...

ubuntu 驱动更新后导致无法进入界面

**问题描述&#xff1a; **安装新ubuntu系统后未禁止驱动更新导致无法进入登录界面。 解决办法&#xff1a; 首先在进入BIOS中&#xff0c;修改设置以进行命令行操作&#xff0c;然后卸载已有的系统驱动&#xff0c;最后安装新的驱动即可。 开机按F11进入启动菜单栏&#xf…...

解决访问GitHub时出现的“您的连接不是私密连接”的问题!

Content问题描述解决办法问题描述 访问github出现您的连接不是私密连接问题&#xff0c;无法正常访问&#xff0c;如下图所示&#xff1a; 解决办法 修改hosts文件。hosts文件位于&#xff1a;C:\Windows\System32\drivers\etc\hosts 首先在https://www.ipaddress.com/查找两…...

初识数据仓库

一、什么是数据仓库数据库 --> OLTP&#xff1a;&#xff08;on-line transaction processing&#xff09;翻译为联机事务处理记录某类业务事件的发生&#xff0c;如购买行为&#xff0c;银行交易行为&#xff0c;当行为产生后&#xff0c;系统会记录是谁在何时何地做了何事…...

FilenameUtils工具类部分源码自研

FilenameUtils工具类部分源码自研getExtension(orgFileName)源码如下逐行分析getExtension(orgFileName)源码如下 public class FilenameUtils {public static int indexOfExtension(String fileName) throws IllegalArgumentException {if (fileName null) {return -1;} els…...

【前端领域】3D旋转超美相册(HTML+CSS)

世界上总有一半人不理解另一半人的快乐。 ——《爱玛》 目录 一、前言 二、本期作品介绍 3D旋转相册 三、效果展示 四、详细介绍 五、编码实现 index.html style.css img 六、获取源码 公众号获取源码 获取源码&#xff1f;私信&#xff1f;关注&#xff1f;点赞&…...

Java——聊聊JUC中的原子变量类

文章目录&#xff1a; 1.什么是原子变量类&#xff1f; 2.AtomicInteger&#xff08;基本类型原子变量类&#xff09; 3.AtomicIntegerArray&#xff08;数组类型原子变量类&#xff09; 4.AtomicMarkableReference&#xff08;引用类型原子变量类&#xff09; 5.AtomicInteger…...

elasticsearch索引与搜索初步

ES支持cURL交互&#xff0c;使用http请求完成索引和搜索操作&#xff0c;最基本的格式如下&#xff1a;创建索引我们可以使用PUT方法创建索引&#xff0c;通过指定“索引”、“类型”、“文档ID”锁定文档&#xff0c;通过参数指定文档的数据。红色部分的路由分别指定了“索引”…...

【Python】多线程与多进程学习笔记

本文是一篇学习笔记&#xff0c;学习内容主要来源于莫凡python的文档&#xff1a;https://mofanpy.com/tutorials/python-basic/threading/thread 多线程 线程基本结构 开启子线程的简单方式如下&#xff1a; import threadingdef thread_job():print(This is a thread of %…...

MySQL基础知识点

1.在Linux上安装好MySQL8.0之后&#xff0c;默认数据目录的具体位置是什么&#xff1f;该目录下都保存哪些数据库组件&#xff1f;在目录/usr/sbin、/usr/bin、/etc、/var/log 分别保存哪些组件&#xff1f; 答&#xff1a;默认数据目录&#xff1a;/var/lib/mysql。保存有mysq…...

代码随想录算法训练营第五十九天| 583. 两个字符串的删除操作、72. 编辑距离

Leetcode - 583dp[i][j]代表以i-1结尾的words1的子串 要变成以j-1结尾的words2的子串所需要的次数。初始化&#xff1a; "" 变成"" 所需0次 dp[0][0] 0, ""变成words2的子串 需要子串的长度的次数,所以dp[0][j] j, 同理&#xff0c;dp[i][0] …...

指针引用字符串问题(详解)

通过指针引用字符串可以更加方便灵活的使用字符串。 字符串的引用方式有两种&#xff0c;下面简单介绍一下这两种方法。 1.用字符数组来存放一个字符串。 1.1 可以通过数组名和下标来引用字符串中的一个字符。 1.2 还可以通过数组名和格式声明符%s输出整个字符串。 具体实…...

数据结构——哈夫曼树编程,输入权值实现流程图代码

一、须知 本代码是在数据结构——哈夫曼树编程上建立的&#xff0c;使用时需将代码剪切到C等软件中。需要输入权值方可实现流程图&#xff0c;但是还需要按照编程换算出的结果自己用笔画出流程图。 下面将代码粘贴到文章中&#xff0c;同时举一个例子&#xff1a;二、代…...

【MySQL】 事务

&#x1f60a;&#x1f60a;作者简介&#x1f60a;&#x1f60a; &#xff1a; 大家好&#xff0c;我是南瓜籽&#xff0c;一个在校大二学生&#xff0c;我将会持续分享Java相关知识。 &#x1f389;&#x1f389;个人主页&#x1f389;&#x1f389; &#xff1a; 南瓜籽的主页…...

Java测试——selenium常见操作(2)

这篇博客继续讲解一些selenium的常见操作 selenium的下载与准备工作请看之前的博客&#xff1a;Java测试——selenium的安装与使用教程 先创建驱动 ChromeDriver driver new ChromeDriver();等待操作 我们上一篇博客讲到&#xff0c;有些时候代码执行过快&#xff0c;页面…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...

Vue3 PC端 UI组件库我更推荐Naive UI

一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用&#xff0c;前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率&#xff0c;还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库&#xff08;Naive UI、Element …...