Fast-Planner(五)详解TopologyPRM

本文上接Fast-Planner第一篇文章的内容,本文主要详解这一系列的第二篇Robust Real-time UAV Replanning Using Guided Gradient-based Optimization and Topological Paths中的TopologyPRM即其代码。如有问题,欢迎各位大佬评论指出,带着我一起进步。

1. 简介

这篇文章主要贡献:

  • 为解决GTO(基于梯度的轨迹优化)带来的陷入局部极小值的问题,提出了一种路径引导优化(PGO)来处理局部极小值。
  • 一个有效的拓扑路径搜索算法,适用于与PGO结合,生成多条备选拓扑路径再通过剪枝优化生成有效的重规划轨迹。

GTO仅依赖于ESDF提供的梯度信息时,但是在下图这类“谷”和“脊”等位置梯度发生突变,这导致目标函数将轨迹朝着不同方向推离,从而导致优化失败。
在这里插入图片描述

2. 拓扑路径搜索

参考博客
最短的路径引导不一定是最优的引导路径,因为路径是低维信息不包含高阶信息(速度、加速度等),不能反映真实运动。仿照笔者整理的可变性路径利用拓扑等价轨迹获得多条轨迹再进一步优化剪枝。
该算法主要体现在topo_prm.cpp代码中,现在解析这部分代码。

2.1 主函数

/*
  function:拓扑路径搜索主函数
  param:
    - start 起点坐标
    - end 终点坐标
    - start_pts 不知道干什么
    - end_pts 同上
    - graph 节点图
    - raw_paths 搜索到的路径集合
    - filtered_paths 剪枝后的路径合集
    - select_paths  最后选择的比较短的几条路径
*/
void TopologyPRM::findTopoPaths(Eigen::Vector3d start, Eigen::Vector3d end,
                                vector<Eigen::Vector3d> start_pts, vector<Eigen::Vector3d> end_pts,
                                list<GraphNode::Ptr>& graph, vector<vector<Eigen::Vector3d>>& raw_paths,
                                vector<vector<Eigen::Vector3d>>& filtered_paths,
                                vector<vector<Eigen::Vector3d>>& select_paths) {
  ros::Time t1, t2;
  //用于计时每个阶段的时间:生成节点时间、路径搜索时间、路径最短时间、路径剪枝时间、选择路径时间
  double graph_time, search_time, short_time, prune_time, select_time;
  /* ---------- create the topo graph ---------- */
  t1 = ros::Time::now();

  start_pts_ = start_pts;
  end_pts_ = end_pts;

  //生成节点图
  graph = createGraph(start, end);

  graph_time = (ros::Time::now() - t1).toSec();

  /* ---------- search paths in the graph ---------- */
  t1 = ros::Time::now();
  //在节点图中进行拓扑路径搜索
  raw_paths = searchPaths();

  search_time = (ros::Time::now() - t1).toSec();

  /* ---------- path shortening ---------- */
  // for parallel, save result in short_paths_
  t1 = ros::Time::now();
  //路径缩短优化
  shortcutPaths();

  short_time = (ros::Time::now() - t1).toSec();

  /* ---------- prune equivalent paths ---------- */
  t1 = ros::Time::now();
  //筛选路径
  filtered_paths = pruneEquivalent(short_paths_);

  prune_time = (ros::Time::now() - t1).toSec();
  // cout << "prune: " << (t2 - t1).toSec() << endl;

  /* ---------- select N shortest paths ---------- */
  t1 = ros::Time::now();

  select_paths = selectShortPaths(filtered_paths, 1);

  select_time = (ros::Time::now() - t1).toSec();

  final_paths_ = select_paths;

  double total_time = graph_time + search_time + short_time + prune_time + select_time;

  std::cout << "\n[Topo]: total time: " << total_time << ", graph: " << graph_time
            << ", search: " << search_time << ", short: " << short_time << ", prune: " << prune_time
            << ", select: " << select_time << std::endl;
}

2.2 Algorithm 1: Topological Roadmap

在这里插入图片描述对应的函数是createGraph
在这里插入图片描述

/*
  function:根据起始点和终点的位置信息,采样地图节点(对应文章算法1 Topological Roadmap)
  param:
    - start 起点坐标
    - end 终点坐标
*/
list<GraphNode::Ptr> TopologyPRM::createGraph(Eigen::Vector3d start, Eigen::Vector3d end) {
  // std::cout << "[Topo]: searching----------------------" << std::endl;

  /* init the start, end and sample region */
  graph_.clear();
  // collis_.clear();
  //将起点和值哦观点设为守卫节点,并标定id号分别为0和1
  GraphNode::Ptr start_node = GraphNode::Ptr(new GraphNode(start, GraphNode::Guard, 0));
  GraphNode::Ptr end_node = GraphNode::Ptr(new GraphNode(end, GraphNode::Guard, 1));
  graph_.push_back(start_node);
  graph_.push_back(end_node);

  // sample region
  // 采样区域
  sample_r_(0) = 0.5 * (end - start).norm() + sample_inflate_(0); //x方向采样的半径
  sample_r_(1) = sample_inflate_(1);  //y方向采样的半径
  sample_r_(2) = sample_inflate_(2);  //z方向采样的半径

  // transformation
  // 姿态改变参数
  translation_ = 0.5 * (start + end);
  //创建局部坐标系切线-法线-副法线坐标系
  Eigen::Vector3d xtf, ytf, ztf, downward(0, 0, -1);
  xtf = (end - translation_).normalized();
  ytf = xtf.cross(downward).normalized();
  ztf = xtf.cross(ytf);
  //局部坐标系和全局坐标系的旋转关系
  rotation_.col(0) = xtf;
  rotation_.col(1) = ytf;
  rotation_.col(2) = ztf;

  int node_id = 1;

  /* ---------- main loop ---------- */
  int sample_num = 0;       //采样数目
  double sample_time = 0.0; //采样时间
  Eigen::Vector3d pt;
  ros::Time t1, t2;
  //采样数目和采样时间阈值内循环
  while (sample_time < max_sample_time_ && sample_num < max_sample_num_) {
    t1 = ros::Time::now();

    pt = getSample();
    ++sample_num;
    double dist;
    Eigen::Vector3d grad; //梯度
    // edt_environment_->evaluateEDTWithGrad(pt, -1.0, dist, grad);
    dist = edt_environment_->evaluateCoarseEDT(pt, -1.0);   //距离esdf地图最近的障碍物的距离
    if (dist <= clearance_) {         //最小距离小于安全距离,舍弃该采样点
      sample_time += (ros::Time::now() - t1).toSec(); 
      continue;
    }

    /* find visible guard */
    /* 找到前采样点的可视守卫点合集 */
    vector<GraphNode::Ptr> visib_guards = findVisibGuard(pt);
    if (visib_guards.size() == 0) {   //没有可视守卫点,将其设为守卫点
      GraphNode::Ptr guard = GraphNode::Ptr(new GraphNode(pt, GraphNode::Guard, ++node_id));
      graph_.push_back(guard);
    } else if (visib_guards.size() == 2) {  //如果有且只有两个可见guard,则要利用needConnection函数判断是否要添加新的connector。
      /* try adding new connection between two guard */
      // vector<pair<GraphNode::Ptr, GraphNode::Ptr>> sort_guards =
      // sortVisibGuard(visib_guards);
      bool need_connect = needConnection(visib_guards[0], visib_guards[1], pt);
      if (!need_connect) {
        sample_time += (ros::Time::now() - t1).toSec();
        continue;
      }
      // new useful connection needed, add new connector
      // 如果需要新的节点,添加新的节点
      GraphNode::Ptr connector = GraphNode::Ptr(new GraphNode(pt, GraphNode::Connector, ++node_id));
      graph_.push_back(connector);

      // connect guards
      visib_guards[0]->neighbors_.push_back(connector);
      visib_guards[1]->neighbors_.push_back(connector);

      connector->neighbors_.push_back(visib_guards[0]);
      connector->neighbors_.push_back(visib_guards[1]);
    }

    sample_time += (ros::Time::now() - t1).toSec();
  }

  /* print record */
  std::cout << "[Topo]: sample num: " << sample_num;
  // 修剪节点图
  pruneGraph();
  // std::cout << "[Topo]: node num: " << graph_.size() << std::endl;

  return graph_;
  // return searchPaths(start_node, end_node);
}

这个函数里提到一个概念叫做UVD(uniform visibility deformation)均匀可视变形,同样来自上文提到的整理的论文笔记中,其中不一样的点在于相比于右图的分均匀可视变形,可视变形的检测可以通过均匀采样一一可视检测完成,然后从两条可视变形的路径中挑选出最短的一条添加进节点图,这个算法的代码函数为sameTopoPath。论文中提到在概念上,UVD不太普遍,它是VD类型的子集的特征。实际上,它捕获的不同路径的类别比VD略多,但等价性检查的成本要低得多。
在这里插入图片描述

bool TopologyPRM::sameTopoPath(const vector<Eigen::Vector3d>& path1,
                               const vector<Eigen::Vector3d>& path2, double thresh) {
  // 计算两条路径的长度
  double len1 = pathLength(path1);
  double len2 = pathLength(path2);
  double max_len = max(len1, len2);

  int pt_num = ceil(max_len / resolution_);

  // std::cout << "pt num: " << pt_num << std::endl;
  // 离散path的路径点
  vector<Eigen::Vector3d> pts1 = discretizePath(path1, pt_num);
  vector<Eigen::Vector3d> pts2 = discretizePath(path2, pt_num);

  Eigen::Vector3d pc;
  for (int i = 0; i < pt_num; ++i) {
    // 判断轨迹上是否有障碍物(两条轨迹均匀可视变形)
    if (!lineVisib(pts1[i], pts2[i], thresh, pc)) {
      return false;
    }
  }

  return true;
}

2.2 深度搜索

在获得节点图后,从起点到终点有好多个点,本文采用深度搜索找到所用节点最少的路径集合。

// search for useful path in the topo graph by DFS
// 通过DFS在节点图中搜索有用的路径,按节点数目对路径进行排序,选择节点数目较少的路径进行保留
vector<vector<Eigen::Vector3d>> TopologyPRM::searchPaths() {
  raw_paths_.clear();

  vector<GraphNode::Ptr> visited;     //标记节点是否被访问
  visited.push_back(graph_.front());  //存入起点

  depthFirstSearch(visited);  //进行DFS更新raw_paths向量

  // 路径排序
  int min_node_num = 100000, max_node_num = 1;
  vector<vector<int>> path_list(100);
  for (int i = 0; i < raw_paths_.size(); ++i) {
    if (int(raw_paths_[i].size()) > max_node_num) max_node_num = raw_paths_[i].size();
    if (int(raw_paths_[i].size()) < min_node_num) min_node_num = raw_paths_[i].size();
    path_list[int(raw_paths_[i].size())].push_back(i);
  }

  // 选择节点数较少的路径
  vector<vector<Eigen::Vector3d>> filter_raw_paths;
  for (int i = min_node_num; i <= max_node_num; ++i) {
    bool reach_max = false;
    for (int j = 0; j < path_list[i].size(); ++j) {
      filter_raw_paths.push_back(raw_paths_[path_list[i][j]]);
      if (filter_raw_paths.size() >= max_raw_path2_) {
        reach_max = true;
        break;
      }
    }
    if (reach_max) break;
  }
  std::cout << ", raw path num: " << raw_paths_.size() << ", " << filter_raw_paths.size();

  raw_paths_ = filter_raw_paths;

  return raw_paths_;
}

void TopologyPRM::depthFirstSearch(vector<GraphNode::Ptr>& vis) {
  GraphNode::Ptr cur = vis.back();  //从已访问节点列表中获取最后一个节点作为当前节点

  for (int i = 0; i < cur->neighbors_.size(); ++i) {
    // 检查邻居点中是否有目标节点
    if (cur->neighbors_[i]->id_ == 1) {
      // 将当前路径添加到raw_paths
      vector<Eigen::Vector3d> path;
      for (int j = 0; j < vis.size(); ++j) {
        path.push_back(vis[j]->pos_);
      }
      path.push_back(cur->neighbors_[i]->pos_);

      raw_paths_.push_back(path);
      if (raw_paths_.size() >= max_raw_path_) return;

      break;
    }
  }

  for (int i = 0; i < cur->neighbors_.size(); ++i) {
    // 跳过目标节点
    if (cur->neighbors_[i]->id_ == 1) continue;

    // 检查是否已访问过当前邻居节点  
    bool revisit = false;
    for (int j = 0; j < vis.size(); ++j) {
      if (cur->neighbors_[i]->id_ == vis[j]->id_) {
        revisit = true;
        break;
      }
    }
    if (revisit) continue;  //如果已访问过,则跳过该邻居节点

    // 将当前邻居节点添加到访问列表中,并递归搜索
    vis.push_back(cur->neighbors_[i]);
    depthFirstSearch(vis);
    if (raw_paths_.size() >= max_raw_path_) return;

    vis.pop_back(); //回溯,从访问列表中移除当前邻居节点
  }
}

2.3 路径缩短

将原路径离散化成多个点,然后从起点出发,遍历原路径上的离散点连成线,如果与障碍物碰撞,从碰撞处的障碍的ESDF梯度结合连线向外推出一定距离形成新路径的一个点,然后以这个新点做遍历直到达到目标点。这部分代码为函数shortcutPaths,可选择并行缩短多条路径。
在这里插入图片描述在这里插入图片描述

/*
  function:通过对路径进行优化,找到一条更短或更高效的路径,同时避免与障碍物或其他不可行区域发生碰撞(对应论文中的算法2)
  param:
    - path: 待优化的原始路径
    - path_id: 编号
    - iter_num:迭代次数
*/
void TopologyPRM::shortcutPath(vector<Eigen::Vector3d> path, int path_id, int iter_num) {
  vector<Eigen::Vector3d> short_path = path;
  vector<Eigen::Vector3d> last_path;

  for (int k = 0; k < iter_num; ++k) {
    last_path = short_path;
    //将路径稠密化
    vector<Eigen::Vector3d> dis_path = discretizePath(short_path);

    if (dis_path.size() < 2) {
      short_paths_[path_id] = dis_path;
      return;
    }

    /* visibility path shortening */
    //              障碍物id   梯度   方向  推力方向
    Eigen::Vector3d colli_pt, grad, dir, push_dir;
    double dist;
    short_path.clear();
    short_path.push_back(dis_path.front());
    for (int i = 1; i < dis_path.size(); ++i) {
      //利用lineVisib来衡量可见性,找到线上不可见的第一个点
      if (lineVisib(short_path.back(), dis_path[i], resolution_, colli_pt, path_id)) continue;
      //计算障碍点上的梯度
      edt_environment_->evaluateEDTWithGrad(colli_pt, -1, dist, grad);
      if (grad.norm() > 1e-3) {
        grad.normalize();
        //计算到第一个新点的方向
        dir = (dis_path[i] - short_path.back()).normalized();
        push_dir = grad - grad.dot(dir) * dir;  //推离方向
        push_dir.normalize();
        colli_pt = colli_pt + resolution_ * push_dir; //推离到新点
      }
      short_path.push_back(colli_pt); //添加点
    }
    short_path.push_back(dis_path.back());

    /* break if no shortcut */
    // 比较两个路径的长度,保留短的
    double len1 = pathLength(last_path);
    double len2 = pathLength(short_path);
    if (len2 > len1) {
      // ROS_WARN("pause shortcut, l1: %lf, l2: %lf, iter: %d", len1, len2, k +
      // 1);
      short_path = last_path;
      break;
    }
  }

  short_paths_[path_id] = short_path;
}

//对通过DFS找到的原始路径进行shortcut处理,用于减少路径中的冗余节点,以得到更短或更高效的路径。
void TopologyPRM::shortcutPaths() {
  short_paths_.resize(raw_paths_.size());

  if (parallel_shortcut_) { //是否开启多线程并行处理每条路径
    vector<thread> short_threads;
    for (int i = 0; i < raw_paths_.size(); ++i) {
      short_threads.push_back(thread(&TopologyPRM::shortcutPath, this, raw_paths_[i], i, 1));
    }
    for (int i = 0; i < raw_paths_.size(); ++i) {
      short_threads[i].join();
    }
  } else {
    for (int i = 0; i < raw_paths_.size(); ++i) shortcutPath(raw_paths_[i], i);
  }
}

2.4 剪枝

/*
  function:将路径集中的其他路径与最短路径进行比较,产生一个长度比值,该比值小于阈值,将该路径放入新的路径集中保留,直到达到新的路径集中保留。
*/
vector<vector<Eigen::Vector3d>> TopologyPRM::selectShortPaths(vector<vector<Eigen::Vector3d>>& paths,
                                                              int step) {
  /* ---------- only reserve top short path ---------- */
  vector<vector<Eigen::Vector3d>> short_paths;
  vector<Eigen::Vector3d> short_path;
  double min_len;

  for (int i = 0; i < reserve_num_ && paths.size() > 0; ++i) {
    int path_id = shortestPath(paths);
    if (i == 0) {
      short_paths.push_back(paths[path_id]);
      min_len = pathLength(paths[path_id]);
      paths.erase(paths.begin() + path_id);
    } else {
      //路径长度比较
      double rat = pathLength(paths[path_id]) / min_len;
      if (rat < ratio_to_short_) {
        short_paths.push_back(paths[path_id]);
        paths.erase(paths.begin() + path_id);
      } else {
        break;
      }
    }
  }
  std::cout << ", select path num: " << short_paths.size();

  /* ---------- merge with start and end segment ---------- */
  for (int i = 0; i < short_paths.size(); ++i) {
    short_paths[i].insert(short_paths[i].begin(), start_pts_.begin(), start_pts_.end());
    short_paths[i].insert(short_paths[i].end(), end_pts_.begin(), end_pts_.end());
  }
  for (int i = 0; i < short_paths.size(); ++i) {
    shortcutPath(short_paths[i], i, 5);
    short_paths[i] = short_paths_[i];
  }

  short_paths = pruneEquivalent(short_paths);

  return short_paths;
}

完整代码参考

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言面试题之返回倒数第 k 个节点

返回倒数第 k 个节点 实例要求 1、实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点&#xff1b;2、返回该节点的值&#xff1b; 示例&#xff1a;输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说明&#xff1a;给定的 k 保证是有效的。实…

Unity 获取RenderTexture像素颜色值

拿来吧你~ &#x1f9aa;功能介绍&#x1f32d;Demo &#x1f9aa;功能介绍 &#x1f4a1;不通过Texture2D 而是通过ComputerShader 提取到RenderTexture的像素值&#xff0c;效率有提升哦&#xff01; &#x1f4a1;通过扩展方法调用&#xff0c;方便快捷&#xff1a;xxxRT.G…

无人机低空数字摄影测量系统

一、 系统概述 系统完全基于IDL设计实现&#xff0c;包括界面布局到人机交互再到底层核心函数功能。整体设计框架基于数字摄影测量的专业处理流程&#xff0c;实现了数据输入、数据预处理、影像信息检测、空间定向、地形三维建模、专题信息提取、成果输出与更新等功能。同时为…

【linux】yum 和 vim

yum 和 vim 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 查看软件包1.3 如何安装软件1.4 如何卸载软件1.5 关于 rzsz 2. Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 vim命令模式命令集2.4 vim底行模式命令集2.5 vim操作总结补充&#xff1a;vim下批量化注释…

Spring AI 应用 - 智能记者

参考实现&#xff1a; https://github.com/mshumer/ai-journalist 上面是通过 Claude 配合 SERP 搜索 API&#xff0c;使用 Python 语言实现的&#xff0c;本文通过 GitHub Copilot 辅助改为了基于 Spring AI 的 Java 版本&#xff0c;本文使用的 OpenAI。 AIJournalist 实现…

绝地求生:经典杜卡迪与传奇杜卡迪的区别以及亮点

大家好&#xff0c;这里是闲游盒. 杜卡迪联名已正式加入PUBG&#xff0c;本次联名分为两个版本分别为&#xff1a;经典杜卡迪以及传奇杜卡迪 那接下来&#xff0c;就给大家展示一下经典杜卡迪&#xff08;红&#xff09;以及传奇版本杜卡迪&#xff08;暮光粉&#xff09;的区…

Acwing.4009 收集卡牌(期望dp)

题目 小林在玩一个抽卡游戏&#xff0c;其中有 n种不同的卡牌&#xff0c;编号为 1到 n。 每一次抽卡&#xff0c;她获得第 i种卡牌的概率为 pi。 如果这张卡牌之前已经获得过了&#xff0c;就会转化为一枚硬币。 可以用 k枚硬币交换一张没有获得过的卡。 小林会一直抽卡&…

2024大模型落地应用案例集(免费下载)

【1】扫码关注本公众号 【2】私信发送 2024大模型落地应用案例集 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。

初识C++之内联函数 auto关键字

初识C之内联函数 auto关键字 文章目录 初识C之内联函数 auto关键字一、 内联函数1.1 定义1.2 应用1.3 特性 二、auto关键字2.1 简介2.2 auto的详细使用2.3 范围for&#xff08;C&#xff09;2.4 注意事项 一、 内联函数 1.1 定义 以inline修饰的函数叫做内联函数&#xff0c;…

UDP实现Mini版在线聊天室

实现原理 只有当客户端先对服务器发送online消息的时候&#xff0c;服务器才会把客户端加入到在线列表。当在线列表的用户发消息的时候&#xff0c;服务器会把消息广播给在线列表中的所有用户。而当用户输入offline时&#xff0c;表明自己要下线了&#xff0c;此时服务器把该用…

跨域问题一文解决

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Vue ⛺️稳中求进&#xff0c;晒太阳 一、为什么会出现跨域的问题&#xff1f; 是浏览器的同源策略&#xff0c;跨域也是因为浏览器这个机制引起的&#xff0c;这个机制的存在还是在于安全…

【MATLAB源码-第6期】基于matlab的QPSK的误码率BER和误符号率SER仿真。

1、算法描述 QPSK&#xff0c;有时也称作四位元PSK、四相位PSK、4-PSK&#xff0c;在坐标图上看是圆上四个对称的点。通过四个相位&#xff0c;QPSK可以编码2位元符号。图中采用格雷码来达到最小位元错误率&#xff08;BER&#xff09; — 是BPSK的两倍. 这意味著可以在BPSK系统…

利用DataX工具,实现MySQL与OceanBase的数据同步实践

数据迁移是经常遇到的需求&#xff0c;市面上为此提供了众多同步工具。这里将为大家简要介绍DataX的使用。DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;它作为离线数据同步的工具/平台&#xff0c;在阿里巴巴集团内部被广泛应用。DataX 能够实现多种异构数据源之间…

图书馆自习室|基于SSM的图书馆自习室座位预约小程序设计与实现(源码+数据库+文档)

图书馆自习室目录 基于SSM的图书馆自习室座位预约小程序设计与实现 一、前言 二、系统设计 三、系统功能设计 1、小程序端&#xff1a; 2、后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a…

[opencv]VideoWriter写出fourcc格式

fourcc支持的格式 fourcc全名Four-Character Codes&#xff0c;四字符代码&#xff0c;该编码由四个字符组成 cv2.VideoWriter_fourcc(O,O,O,O) cv2.VideoWriter_fourcc(*OOOO) 通常写法有上述两种形式&#xff0c;O代表一个字符&#xff0c;通常有 支持avi格式的有&#…

麒麟KOS删除鼠标右键新建菜单里不需要的选项

原文链接&#xff1a;麒麟KOS删除鼠标右键新建菜单里不需要的选项 Hello&#xff0c;大家好啊&#xff01;在日常使用麒麟KOS操作系统时&#xff0c;我们可能会发现鼠标右键新建菜单里包含了一些不常用或者不需要的选项。这不仅影响我们的使用效率&#xff0c;也让菜单显得杂乱…

【C++学习】C++11新特性(第一节)

文章目录 ♫一.文章前言♫二.C11新特性♫一.统一的列表初始化♫二.std::initializer_list♫三.声明♫四.decltype关键字♫五.nullptr♫六.新增加容器---静态数组array、forward_list以及unordered系列♫6.1unordered_map与unoredered_set♫6.2array♫6.3 forward_list&#xff…

上海亚商投顾:创业板指低开低走 低空经济概念股尾盘拉升

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日集体调整&#xff0c;沪指午后跌超1%&#xff0c;深成指、创业板指盘中跌超2%&#xff0c;尾盘跌…

计算机视觉——基于深度学习UNet实现的复杂背景文档二值化算法实现与模型训练

1. 引言 阈值分割可以被视为一个分类问题&#xff0c;通常涉及两个类别&#xff0c;这也是为什么阈值分割也被称为二值化。对于文档图像&#xff0c;我们期望阈值算法能够正确地将墨水分类为黑色&#xff0c;将纸张分类为白色&#xff0c;从而得到二值化图像。对于数字灰度图像…

腾讯云人脸服务开通详解:快速部署,畅享智能体验

请注意&#xff0c;在使用人脸识别服务时&#xff0c;需要确保遵守相关的法律法规和政策规定&#xff0c;保护用户的合法权益&#xff0c;并依法收集、使用、存储用户信息。此外&#xff0c;腾讯云每个月会提供一定次数的人脸识别调用机会&#xff0c;对于一般的小系统登录来说…