无人机自主探索FUEL:代码阅读1--边界簇检测更新与视点生成

news/2024/11/22 16:22:44/
~/src/catkin_fuel_refactored/fuel_refactored/fuel_planner/bag

bag中包含三个.sh文件,为rosbag指令,给出了录包指令以及有用话题信息

FIS

1、增量边界检测与聚类

  • 路径:~/fuel_planner/active_perception/src/frontier_finder.cpp

主要函数:寻找并更新边界簇 void FrontierFinder::searchAndAddFrontiers()
使用SDF地图,找到该边界簇的轴对齐包围盒(AABB框) B i B_i Bi,并更新更新的框内的新边界;
增量边界聚类;
创建视点集 V P i VP_i VPi以覆盖簇 F i F_i Fi
进行视点检测与筛选,对视点降序排列;
找不到视点时将边界簇移至休眠列表;
重置边界索引;

    void FrontierFinder::searchAndAddFrontiers() {ros::Time t1 = ros::Time::now();//---------------   增量边界检测    ---------------------// Bounding box of updated region  更新区域的AABB框B_iVector3d update_min, update_max;edt_env_->sdf_map_->getUpdatedBox(update_min, update_max, true);// ROS_WARN("update_min %lf %lf %lf: ", update_min[0], update_min[1], update_min[2]);// ROS_WARN("update_max %lf %lf %lf: ", update_max[0], update_max[1], update_max[2]);// Search new frontier within box slightly inflated from updated box//从更新的框中搜索稍微膨胀的框内的新边界 Vector3d search_min = update_min - Vector3d(1, 1, 0.5);Vector3d search_max = update_max + Vector3d(1, 1, 0.5);Vector3d box_min, box_max;                              //新的边界edt_env_->sdf_map_->getBox(box_min, box_max);for (int k = 0; k < 3; ++k) {search_min[k] = max(search_min[k], box_min[k]);search_max[k] = min(search_max[k], box_max[k]);}// ROS_WARN("search_min %lf %lf %lf: ", search_min[0], search_min[1], search_min[2]);// ROS_WARN("search_max %lf %lf %lf: ", search_max[0], search_max[1], search_max[2]);Eigen::Vector3i min_id, max_id;edt_env_->sdf_map_->posToIndex(search_min, min_id);edt_env_->sdf_map_->posToIndex(search_max, max_id);ROS_WARN("min_id %lf %lf %lf: ", min_id[0], min_id[1], min_id[2]);ROS_WARN("max_id %lf %lf %lf: ", max_id[0], max_id[1], max_id[2]);vector<Frontier> tmp_frontiers;                      //当前边界for (int x = min_id(0); x <= max_id(0); ++x)for (int y = min_id(1); y <= max_id(1); ++y)for (int z = min_id(2); z <= max_id(2); ++z) {// Scanning the updated region to find seeds of frontiersEigen::Vector3i cur(x, y, z);if (!is_in_frontier_[toAddress(cur)] && knownFree(cur) && isNeighborUnknown(cur)) {// Expand from the seed cell to find a complete frontier cluster    //找到完整边界Frontier frontier;if (expandFrontier(cur, frontier)) {          //按距离聚类搜索,检查新的簇的可行性tmp_frontiers.push_back(move(frontier));}}}//---------------   增量边界聚类   ---------------------splitLargeFrontiers(tmp_frontiers);         //递归分割边界簇ROS_WARN_THROTTLE(5.0, "Frontier t: %lf", (ros::Time::now() - t1).toSec());origin_frontiers_num_ = frontiers_.size();int new_num = 0;int new_dormant_num = 0;//-----------------------  视点生成  ---------------------------// Try to find viewpoints for each cluster and categorize them according to viewpoint numberfor (Frontier &tmp_ftr: tmp_frontiers) {// Search viewpoints around frontier//采样边界平均位置周围的视点,检查边界单元的覆盖率sampleViewpoints(tmp_ftr);                                       if (!tmp_ftr.viewpoints_.empty()) {++new_num;sort(tmp_ftr.viewpoints_.begin(), tmp_ftr.viewpoints_.end(),     //降序排列[](const Viewpoint &v1, const Viewpoint &v2) -> bool {return v1.visible_num_ > v2.visible_num_;});frontiers_.insert(frontiers_.end(), tmp_ftr);} else {// Find no viewpoint, move cluster to dormant list  找不到视点,将边界簇移至休眠列表dormant_frontiers_.push_back(tmp_ftr);++new_dormant_num;}}// Reset indices of frontiers   重置边界索引int idx = 0;for (Frontier &ft: frontiers_) {ft.id_ = idx++;}}

- 按距离聚类搜索,检查新的簇的可行性 FrontierFinder::expandFrontier

    bool FrontierFinder::expandFrontier(const Eigen::Vector3i &first, Frontier &frontier) {// Data for clusteringqueue<Eigen::Vector3i> cell_queue;vector<Eigen::Vector3d> expanded;Vector3d pos;edt_env_->sdf_map_->indexToPos(first, pos);expanded.push_back(pos);cell_queue.push(first);is_in_frontier_[toAddress(first)] = true;// Search frontier cluster based on region growing (distance clustering)  按距离聚类搜索while (!cell_queue.empty()) {Vector3i cur_cell = cell_queue.front();cell_queue.pop();vector<Vector3i> neighbors = allNeighbors(cur_cell);for (const Vector3i &next_cell: neighbors) {// Qualified cell should be inside bounding box and frontier cell not clustered  //合格单元格应位于边界框内,且边界单元格不在簇内int next_cell_adr = toAddress(next_cell);if (is_in_frontier_[next_cell_adr] || !edt_env_->sdf_map_->isInBox(next_cell) ||!(knownFree(next_cell) && isNeighborUnknown(next_cell)))continue;edt_env_->sdf_map_->indexToPos(next_cell, pos);if (pos[2] < 0.4) continue;  // Remove noise close to ground 消除噪音expanded.push_back(pos);cell_queue.push(next_cell);is_in_frontier_[next_cell_adr] = true;}}if (expanded.size() > cluster_min_) {frontier.cells_ = expanded;computeFrontierInfo(frontier);return true;}return false;}

- 边界簇的分割FrontierFinder::splitLargeFrontiers

void FrontierFinder::searchAndAddFrontiers() 中使用,对每个簇都进行主成分分析,如果最大特征值超过阈值,则将该簇沿第一主轴分成两个均匀的簇。(分割是递归地进行的,因此所有大簇都被分成小簇。)
对应论文 IV.B

    void FrontierFinder::splitLargeFrontiers(vector<Frontier> &frontiers) {vector<Frontier> split_frontiers;for (Frontier &frontier: frontiers) {vector<Frontier> splits;if (splitHorizontally(frontier, splits)) {split_frontiers.insert(split_frontiers.end(), splits.begin(), splits.end());} elsesplit_frontiers.emplace_back(move(frontier));}frontiers = move(split_frontiers);}

其中FrontierFinder::splitHorizontally :使用主成分分析,将较大的簇沿第一主轴分成两个均匀的簇 (单个的分割步骤)
在这里插入图片描述

- 视点集 V P i VP_i VPi的采样与覆盖率检测FrontierFinder::sampleViewpoints

void FrontierFinder::searchAndAddFrontiers() 中使用,对应论文 IV.C
对于自由空间内的每个采样点 p \mathbb{p} p,通过一个偏航角优化方法,yaw角 ξ \xi ξ由传感器对簇覆盖的最大角决定;
覆盖度通过符合传感器模型且未被障碍物遮挡的边界单元的数量来估计,
–> 覆盖度超过阈值的视点 将被保留 并按降序排列
–> 保留 V P i VP_i VPi中最多 N v i e w N_{view} Nview个视点,在第二大部分第2步进行局部更新

// Sample viewpoints around frontier's average position, check coverage to the frontier cellsvoid FrontierFinder::sampleViewpoints(Frontier &frontier) {//通过在原点位于簇中心的柱面坐标系中均匀采样点得到,对应文章fig.4// Evaluate sample viewpoints on circles, find ones that cover most cellsfor (double rc = candidate_rmin_, dr = (candidate_rmax_ - candidate_rmin_) / candidate_rnum_;rc <= candidate_rmax_ + 1e-3; rc += dr)for (double phi = -M_PI; phi < M_PI; phi += candidate_dphi_) {const Vector3d sample_pos = frontier.average_ + rc * Vector3d(cos(phi), sin(phi), 0);//确认视点在AABB框且在无人机可执行的位置// Qualified viewpoint is in bounding box and in safe region if (!edt_env_->sdf_map_->isInBox(sample_pos) ||edt_env_->sdf_map_->getInflateOccupancy(sample_pos) == 1 ||isNearUnknown(sample_pos)) {continue;}//----------------- 计算视点覆盖度,进行筛选与降序排列 ------------------------// Compute average yawconst vector<Vector3d> &cells = frontier.filtered_cells_;Eigen::Vector3d ref_dir = (cells.front() - sample_pos).normalized();double avg_yaw = 0.0;for (size_t i = 1; i < cells.size(); ++i) {Eigen::Vector3d dir = (cells[i] - sample_pos).normalized();double yaw = acos(dir.dot(ref_dir));if (ref_dir.cross(dir)[2] < 0) yaw = -yaw;avg_yaw += yaw;}avg_yaw = avg_yaw / cells.size() + atan2(ref_dir[1], ref_dir[0]);wrapYaw(avg_yaw);     //调整yaw角朝向?// Compute the fraction of covered and visible cells   计算阈值int visible_num = countVisibleCells(sample_pos, avg_yaw, cells);      //计算有效视点if (visible_num > min_visible_num_) {               //保留部分视点并在V部分优化Viewpoint vp = {sample_pos, avg_yaw, visible_num};frontier.viewpoints_.push_back(vp);}}}

- 检测视点 x i , j = ( p i , j , ξ i , j ) x_{i,j}=(\mathbb{p}_{i,j},\xi_{i,j}) xi,j=(pi,j,ξi,j)可行性 FrontierFinder::countVisibleCells

FrontierFinder::sampleViewpoints 中使用
检测视点与传感器覆盖角度关系,检测视点未落在障碍物内

 int FrontierFinder::countVisibleCells(const Eigen::Vector3d &pos, const double &yaw,const vector<Eigen::Vector3d> &cluster) {perception_utils_->setPose(pos, yaw);int visible_num = 0;Eigen::Vector3i idx;for (const Vector3d &cell: cluster) {if (!perception_utils_->insideFOV(cell)) continue;      //是否超过传感器对簇覆盖的最大角// Check if frontier cell is visible (not occluded by obstacles) 检测未落在障碍物内ray_caster_->input(cell, pos);bool is_visible = true;while (ray_caster_->nextId(idx)) {if (edt_env_->sdf_map_->getInflateOccupancy(idx) == 1 ||edt_env_->sdf_map_->getOccupancy(idx) == SDFMap::UNKNOWN) {is_visible = false;break;}}if (is_visible) visible_num += 1;}return visible_num;}

在这里插入图片描述

2. 其他函数

  • 移除过时的簇:FrontierFinder::removeOutDatedFrontiers()
    移除改变的簇;移除改变的簇的代价和path;删除已更改的休眠簇

  • 计算每对簇之间的连接代价(F_k1,F_k2) :FrontierFinder::updateFrontierCostMatrix()

  • 检测两个AABB框是否重叠:FrontierFinder::haveOverlap

  • 对簇进行降采样 :FrontierFinder::downSample


http://www.ppmy.cn/news/971314.html

相关文章

Elemui表单合并

原代码形式 <template><el-table:data"tableData"borderstyle"width: 100%"><el-table-columnprop"date"label"日期"width"180"></el-table-column><el-table-columnprop"name"label…

split函数和merge函数

OpenCV 中的split函数和merge函数 及示例_opencv split_zqx951102的博客-CSDN博客 #include <QApplication> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> #include <iostream> #include &…

什么是Java的Spring框架?

首先&#xff0c;我们来聊一下Spring框架的起源。据说&#xff0c;Spring框架的创始人之一——Rod Johnson曾经是一名Java程序员&#xff0c;他发现当时有很多类似的框架&#xff0c;每个框架都有自己的配置和编码标准&#xff0c;让人们无所适从。于是&#xff0c;他想要创建一…

界面控件DevExpress WPF数据编辑器组件,让数据处理更灵活!(二)

界面控件DevExpress WPF编辑器库可以帮助用户提供直观的用户体验&#xff0c;具有无与伦比的运行时选项和灵活性。WPF数据编辑器提供了全面的掩码和数据验证支持&#xff0c;可以独立使用&#xff0c;也可以作为容器控件(如DevExpress WPF Grid和WPF TreeList)中的单元格编辑器…

【HDFS单测】单测中MiniDFSCluster获取某个DataNode对象

本文提供两种在HDFS单元测试编写过程中获取DataNode对象的方法,如果大家还有其他方法,欢迎在评论区补充。 根据datanode ipc端口获取DataNode对象根据索引在cluster里所有的datanode列表里拿DataNode对象一、根据ipc端口获取某个DataNode: /** @return the datanode having …

chatgpt赋能python:如何让Python输出结果不换行?

如何让Python输出结果不换行&#xff1f; Python是一门高级编程语言&#xff0c;它是开发应用程序的首选语言之一。当使用Python编写应用程序时&#xff0c;我们经常需要输出一些内容到控制台。但是&#xff0c;Python默认情况下&#xff0c;每输出一行内容都会自动换行&#…

chatgpt赋能python:Python换行输出:如何在Python中实现换行输出?

Python换行输出&#xff1a;如何在Python中实现换行输出&#xff1f; 在Python编程中&#xff0c;如何将输出内容分行呈现&#xff0c;是开发者经常会遇到的问题。Python提供了多种方式来实现换行输出&#xff0c;这篇文章将会介绍其中三种。 1. 使用转义字符 \n 在Python中…

chatgpt赋能Python-pythonprint自动换行

Python Print 自动换行 Python是一种广泛使用的高级编程语言&#xff0c;因为它是一种解释型语言&#xff0c;可以轻松地编写脚本和快速原型设计。在Python编程中&#xff0c;我们通常使用print函数来输出和调试程序。当我们输出较长的文本时&#xff0c;可能会遇到自动换行问…