PathDecider 详细解读

目录

PathDecider的主要功能

PathDecider代码分析

Process()

MakeObjectDecision()

MakeStaticObstacleDecision()

MakeStaticObstacleDecision()的流程图​编辑

MakeStaticObstacleDecision()的代码解析

GenerateObjectStopDecision()

PathDecider里用到的其他函数

path_decision->AddLongitudinalDecision()

两个unordered_map

ObjectDecisionType Obstacle::MergeLongitudinalDecision()

path_decision->MergeWithMainStop()

obstacle.MinRadiusStopDistance()


PathDecider的主要功能

PathDecider路径决策类就是对阻塞自车参考线的障碍物做决策,是否要纵向stop,或者是否可以横向ignore,或者是否要车道内横向让行(nudge),apollo路径决策时是只考虑静态障碍物,动态障碍物决策在速度规划时考虑。

PathDecider代码分析

Process()

pathdecider的主要功能由Process()这个函数实现。

Process()函数的作用:对自车参考线的障碍物调用MakeObjectDecision()函数去对这些阻碍自车参考线障碍物做决策

Status PathDecider::Process(const ReferenceLineInfo *reference_line_info,const PathData &path_data,PathDecision *const path_decision) {/// 如果复用了之前的path则可以跳过if (FLAGS_enable_skip_path_tasks && reference_line_info->path_reusable()) {return Status::OK();}std::string blocking_obstacle_id;if (reference_line_info->GetBlockingObstacle() != nullptr) {blocking_obstacle_id = reference_line_info->GetBlockingObstacle()->Id();}/// 对障碍物做路径决策if (!MakeObjectDecision(path_data, blocking_obstacle_id, path_decision)) {const std::string msg = "Failed to make decision based on tunnel";AERROR << msg;return Status(ErrorCode::PLANNING_ERROR, msg);}return Status::OK();
}

MakeObjectDecision()

MakeObjectDecision()函数的作用:调用MakeStaticObstacleDecision()函数对静态障碍物做决策,路径决策时只考虑静态障碍物
 

bool PathDecider::MakeObjectDecision(const PathData &path_data,const std::string &blocking_obstacle_id,PathDecision *const path_decision) {if (!MakeStaticObstacleDecision(path_data, blocking_obstacle_id,path_decision)) {AERROR << "Failed to make decisions for static obstacles";return false;}return true;
}

MakeStaticObstacleDecision()

MakeStaticObstacleDecision()函数的作用:对静态障碍物做决策,若障碍物边界距离自车未来边界最近的横向距离大于3m就横向Ignore,若<0.15m就做纵向stop决策,若[0.15,3.0]m时就做nudge决策,nudge决策也分为车道内向左绕行,车道内向右绕行。

MakeStaticObstacleDecision()的流程图

 
MakeStaticObstacleDecision()的代码解析
bool PathDecider::MakeStaticObstacleDecision(const PathData &path_data, const std::string &blocking_obstacle_id,PathDecision *const path_decision) {// Sanity checks and get important values.ACHECK(path_decision);/// 获取frenet坐标系下的路径坐标const auto &frenet_path = path_data.frenet_frame_path();if (frenet_path.empty()) {AERROR << "Path is empty.";return false;}/// 获取自车半车宽const double half_width =common::VehicleConfigHelper::GetConfig().vehicle_param().width() / 2.0;/// 设置自车横向半径,意思就是里自车边缘横向距离(FLAGS_lateral_ignore_buffer)以上的障碍物忽略const double lateral_radius = half_width + FLAGS_lateral_ignore_buffer;// Go through every obstacle and make decisions.for (const auto *obstacle : path_decision->obstacles().Items()) {const std::string &obstacle_id = obstacle->Id();const std::string obstacle_type_name =PerceptionObstacle_Type_Name(obstacle->Perception().type());ADEBUG << "obstacle_id[<< " << obstacle_id << "] type["<< obstacle_type_name << "]";/// 如果不是静态障碍物或者是虚拟障碍物就跳过if (!obstacle->IsStatic() || obstacle->IsVirtual()) {continue;}// - skip decision making for obstacles with IGNORE/STOP decisions already./// 如果横向纵向决策都已经有了ignore就跳过if (obstacle->HasLongitudinalDecision() &&obstacle->LongitudinalDecision().has_ignore() &&obstacle->HasLateralDecision() &&obstacle->LateralDecision().has_ignore()) {continue;}/// 如果纵向决策已经有了stop就跳过if (obstacle->HasLongitudinalDecision() &&obstacle->LongitudinalDecision().has_stop()) {// STOP decisioncontinue;}// - add STOP decision for blocking obstacles./// 如果障碍物的id是属于静态阻塞障碍物 blocking_obstacle_id,/// 并且当前injector里的规划状态不处于借道场景的话,就对这个阻塞的障碍物添加stop决策,/// 并打上PathDecider/blocking_obstacle的标签if (obstacle->Id() == blocking_obstacle_id &&!injector_->planning_context()->planning_status().path_decider().is_in_path_lane_borrow_scenario()) {// Add stop decisionADEBUG << "Blocking obstacle = " << blocking_obstacle_id;ObjectDecisionType object_decision;*object_decision.mutable_stop() = GenerateObjectStopDecision(*obstacle);path_decision->AddLongitudinalDecision("PathDecider/blocking_obstacle",obstacle->Id(), object_decision);continue;}// - skip decision making for clear-zone obstacles./// 障碍物投影到参考线的ST边界,跳过那些边界类型是禁停区KEEP_CLEAR的障碍物if (obstacle->reference_line_st_boundary().boundary_type() ==STBoundary::BoundaryType::KEEP_CLEAR) {continue;}// 0. IGNORE by default and if obstacle is not in path s at all.ObjectDecisionType object_decision;object_decision.mutable_ignore();/// 获取障碍物在在参考线上投影的SL边界sl_boundary/// 获取障碍物的SL边界,其实就是(start_s,end_s,start_l,end_l)const auto &sl_boundary = obstacle->PerceptionSLBoundary();/// 如果障碍物的s不在路径path的s范围内,直接对这个障碍物横纵向都添加ignore决策,/// 并打上tag PathDecider/not-in-sif (sl_boundary.end_s() < frenet_path.front().s() ||sl_boundary.start_s() > frenet_path.back().s()) {path_decision->AddLongitudinalDecision("PathDecider/not-in-s",obstacle->Id(), object_decision);path_decision->AddLateralDecision("PathDecider/not-in-s", obstacle->Id(),object_decision);continue;}/// frenet_point就是在自车path路径上找到距离障碍物SLBoundary最近的点const auto frenet_point = frenet_path.GetNearestPoint(sl_boundary);/// curr_l就是frenet_point的L坐标const double curr_l = frenet_point.l();/// min_nudge_l = 自车车宽一半 + 静态障碍物缓冲距离的一半double min_nudge_l =half_width +config_.path_decider_config().static_obstacle_buffer() / 2.0;/// l方向上,静态障碍物离path太远了,就横向决策上ignore,并打上PathDecider/not-in-l的tag/// 横向距离太远判定是自车和静态障碍物之间的横向距离要大于FLAGS_lateral_ignore_bufferif (curr_l - lateral_radius > sl_boundary.end_l() ||curr_l + lateral_radius < sl_boundary.start_l()) {// 1. IGNORE if laterally too far away.path_decision->AddLateralDecision("PathDecider/not-in-l", obstacle->Id(),object_decision);} else if (sl_boundary.end_l() >= curr_l - min_nudge_l &&sl_boundary.start_l() <= curr_l + min_nudge_l) {/// l方向上,静态障碍物离path太近了,而且此障碍物要距离自车最近/// 就做出纵向决策上的stop,并且打上PathDecider/nearest-stop的tag/// 横向距离太近判定是自车和静态障碍物之间的横向距离要小于/// config_.path_decider_config().static_obstacle_buffer()// 2. STOP if laterally too overlapping.*object_decision.mutable_stop() = GenerateObjectStopDecision(*obstacle);if (path_decision->MergeWithMainStop(object_decision.stop(), obstacle->Id(),reference_line_info_->reference_line(),reference_line_info_->AdcSlBoundary())) {path_decision->AddLongitudinalDecision("PathDecider/nearest-stop",obstacle->Id(), object_decision);} else {/// l方向上,静态障碍物离path太近了,而且此障碍物要距离自车不是最近的/// 就做出纵向决策上的ignore,并且打上PathDecider/not-nearest-stop的tagObjectDecisionType object_decision;object_decision.mutable_ignore();path_decision->AddLongitudinalDecision("PathDecider/not-nearest-stop",obstacle->Id(), object_decision);}} else {// 3. NUDGE if laterally very close./// l方向上,当静态障碍物离自车的距离小于FLAGS_lateral_ignore_buffer/// 并且大于config_.path_decider_config().static_obstacle_buffer()/// 那就在横向上做出nudge的决策,并且根据sl_boundary判断出nudge的方向if (sl_boundary.end_l() < curr_l - min_nudge_l) {  // &&// sl_boundary.end_l() > curr_l - min_nudge_l - 0.3) {// LEFT_NUDGEObjectNudge *object_nudge_ptr = object_decision.mutable_nudge();object_nudge_ptr->set_type(ObjectNudge::LEFT_NUDGE);object_nudge_ptr->set_distance_l(config_.path_decider_config().static_obstacle_buffer());path_decision->AddLateralDecision("PathDecider/left-nudge",obstacle->Id(), object_decision);} else if (sl_boundary.start_l() > curr_l + min_nudge_l) {  // &&// sl_boundary.start_l() < curr_l + min_nudge_l + 0.3) {// RIGHT_NUDGEObjectNudge *object_nudge_ptr = object_decision.mutable_nudge();object_nudge_ptr->set_type(ObjectNudge::RIGHT_NUDGE);object_nudge_ptr->set_distance_l(-config_.path_decider_config().static_obstacle_buffer());path_decision->AddLateralDecision("PathDecider/right-nudge",obstacle->Id(), object_decision);}}}return true;
}

GenerateObjectStopDecision()

GenerateObjectStopDecision()函数的作用:设置停止决策的属性,往里设置停止距离,设置停止点x,y,heading等

ObjectStop PathDecider::GenerateObjectStopDecision(const Obstacle &obstacle) const {ObjectStop object_stop;/// 停止距离的计算就是自车以最小转弯半径可以绕过障碍物而不装上障碍物的最小sdouble stop_distance = obstacle.MinRadiusStopDistance(VehicleConfigHelper::GetConfig().vehicle_param());object_stop.set_reason_code(StopReasonCode::STOP_REASON_OBSTACLE);object_stop.set_distance_s(-stop_distance);const double stop_ref_s =obstacle.PerceptionSLBoundary().start_s() - stop_distance;const auto stop_ref_point =reference_line_info_->reference_line().GetReferencePoint(stop_ref_s);object_stop.mutable_stop_point()->set_x(stop_ref_point.x());object_stop.mutable_stop_point()->set_y(stop_ref_point.y());object_stop.set_stop_heading(stop_ref_point.heading());return object_stop;
}    

PathDecider里用到的其他函数

path_decision->AddLongitudinalDecision()

path_decision->AddLongitudinalDecision()函数的主要作用:对障碍物加上对应的纵向decision,但是障碍物原本已有decision时进行逻辑上的merge,主要由ObjectDecisionType Obstacle::MergeLongitudinalDecision()实现,对于所有decision,merge的结果永远是安全价值大的decision覆盖掉安全价值小的decision,横纵向的decision安全价值定义在两个unordered_map里面:const std::unordered_map<ObjectDecisionType::ObjectTagCase, int, Obstacle::ObjectTagCaseHash> Obstacle::s_longitudinal_decision_safety_sorter_、const std::unordered_map<ObjectDecisionType::ObjectTagCase, int, Obstacle::ObjectTagCaseHash> Obstacle::s_lateral_decision_safety_sorter_ 。

两个unordered_map
const std::unordered_map<ObjectDecisionType::ObjectTagCase, int,Obstacle::ObjectTagCaseHash>Obstacle::s_longitudinal_decision_safety_sorter_ = {{ObjectDecisionType::kIgnore, 0},{ObjectDecisionType::kOvertake, 100},{ObjectDecisionType::kFollow, 300},{ObjectDecisionType::kYield, 400},{ObjectDecisionType::kStop, 500}};const std::unordered_map<ObjectDecisionType::ObjectTagCase, int,Obstacle::ObjectTagCaseHash>Obstacle::s_lateral_decision_safety_sorter_ = {{ObjectDecisionType::kIgnore, 0}, {ObjectDecisionType::kNudge, 100}};
ObjectDecisionType Obstacle::MergeLongitudinalDecision()

ObjectDecisionType Obstacle::MergeLongitudinalDecision(const ObjectDecisionType& lhs, const ObjectDecisionType& rhs) {if (lhs.object_tag_case() == ObjectDecisionType::OBJECT_TAG_NOT_SET) {return rhs;}if (rhs.object_tag_case() == ObjectDecisionType::OBJECT_TAG_NOT_SET) {return lhs;}const auto lhs_val =FindOrDie(s_longitudinal_decision_safety_sorter_, lhs.object_tag_case());const auto rhs_val =FindOrDie(s_longitudinal_decision_safety_sorter_, rhs.object_tag_case());/// 如果decision的value不同就按value就行decision merge,否者相同的的话就比较决策中的distance_s()if (lhs_val < rhs_val) {return rhs;} else if (lhs_val > rhs_val) {return lhs;} else {if (lhs.has_ignore()) {return rhs;} else if (lhs.has_stop()) {return lhs.stop().distance_s() < rhs.stop().distance_s() ? lhs : rhs;} else if (lhs.has_yield()) {return lhs.yield().distance_s() < rhs.yield().distance_s() ? lhs : rhs;} else if (lhs.has_follow()) {return lhs.follow().distance_s() < rhs.follow().distance_s() ? lhs : rhs;} else if (lhs.has_overtake()) {return lhs.overtake().distance_s() > rhs.overtake().distance_s() ? lhs: rhs;} else {DCHECK(false) << "Unknown decision";}}return lhs;  // stop compiler complaining
}
path_decision->MergeWithMainStop()

path_decision->MergeWithMainStop()函数的主要作用:对于多个stop的decision,根据stop decision里的stop距离来进行merge,永远选择离我们最近的stop decision。

bool PathDecision::MergeWithMainStop(const ObjectStop &obj_stop,const std::string &obj_id,const ReferenceLine &reference_line,const SLBoundary &adc_sl_boundary) {common::PointENU stop_point = obj_stop.stop_point();common::SLPoint stop_line_sl;reference_line.XYToSL(stop_point, &stop_line_sl);double stop_line_s = stop_line_sl.s();/// stop decision必须在reference_line范围内if (stop_line_s < 0.0 || stop_line_s > reference_line.Length()) {AERROR << "Ignore object:" << obj_id << " fence route_s[" << stop_line_s<< "] not in range[0, " << reference_line.Length() << "]";return false;}// check stop_line_s vs adc_s, ignore if it is further way than main stop/// 计算停止距离sconst auto &vehicle_config = common::VehicleConfigHelper::GetConfig();stop_line_s = std::fmax(stop_line_s, adc_sl_boundary.end_s() -vehicle_config.vehicle_param().front_edge_to_center());if (stop_line_s >= stop_reference_line_s_) {ADEBUG << "stop point is farther than current main stop point.";return false;}main_stop_.Clear();main_stop_.set_reason_code(obj_stop.reason_code());main_stop_.set_reason("stop by " + obj_id);main_stop_.mutable_stop_point()->set_x(obj_stop.stop_point().x());main_stop_.mutable_stop_point()->set_y(obj_stop.stop_point().y());main_stop_.set_stop_heading(obj_stop.stop_heading());stop_reference_line_s_ = stop_line_s;ADEBUG << " main stop obstacle id:" << obj_id<< " stop_line_s:" << stop_line_s << " stop_point: ("<< obj_stop.stop_point().x() << obj_stop.stop_point().y()<< " ) stop_heading: " << obj_stop.stop_heading();return true;
}
obstacle.MinRadiusStopDistance()

obstacle.MinRadiusStopDistance()函数的作用:计算自车以当前最小的转弯半径,绕靠这个障碍物所需要的最小的S距离(函数内的计算将reference_line当作直线近似处理),外加一个安全的stop_distance_buffer ,要想直观的解释计算过程需要画图(不会用电脑画图,图之后奉上)。

double Obstacle::MinRadiusStopDistance(const common::VehicleParam& vehicle_param) const {if (min_radius_stop_distance_ > 0) {return min_radius_stop_distance_;}static constexpr double stop_distance_buffer = 0.5;const double min_turn_radius = VehicleConfigHelper::MinSafeTurnRadius();double lateral_diff =vehicle_param.width() / 2.0 + std::max(std::fabs(sl_boundary_.start_l()),std::fabs(sl_boundary_.end_l()));const double kEpison = 1e-5;lateral_diff = std::min(lateral_diff, min_turn_radius - kEpison);double stop_distance =std::sqrt(std::fabs(min_turn_radius * min_turn_radius -(min_turn_radius - lateral_diff) *(min_turn_radius - lateral_diff))) +stop_distance_buffer;stop_distance -= vehicle_param.front_edge_to_center();/// 将stop_distance限制在最大值和最小值之间stop_distance = std::min(stop_distance, FLAGS_max_stop_distance_obstacle);stop_distance = std::max(stop_distance, FLAGS_min_stop_distance_obstacle);return stop_distance;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM功耗管理框架之PPU

安全之安全(security)博客目录导读 思考&#xff1a;功耗管理框架&#xff1f;SCP&#xff1f;PPU&#xff1f;LPI&#xff1f;之间的关系&#xff1f;如何配合&#xff1f; 目录 一、功耗管理框架中的PPU 二、PPU的结构与连接关系 三、PPU操作模式和电源模式及其之间的转…

管理不到位,活该执行力差?狠抓这4点要素,强化执行力

管理不到位&#xff0c;活该执行力差&#xff1f;狠抓这4点要素&#xff0c;强化执行力 一&#xff1a;强化制度管理 1、权责分明&#xff0c;追责管理 要知道&#xff0c;规章制度其实就是一种“契约”。 在制定制度和规则的时候&#xff0c;民主一点&#xff0c;征求团队成员…

HTTP网络协议

1.HTTP &#xff08;1&#xff09;概念&#xff1a; Hyper Text Transfer Protocol&#xff0c;超文本传输协议规定了浏览器和服务器之间数据传输的规则。 &#xff08;2&#xff09;特点 基于TCP协议:面向连接&#xff0c;安全基于请求-响应模型的:一次请求对应一次响应HTTP协…

mysql高级语句2存储过程

CREATE VIEW 视图&#xff0c;可以被当作是虚拟表或存储查询。 视图跟表格的不同是&#xff0c;表格中有实际储存数据记录&#xff0c;而视图是建立在表格之上的一个架构&#xff0c;它本身并不实际储存数据记录。 临时表在用户退出或同数据库的连接断开后就自动消失了&…

利用竞争智慧与大型语言模型:假新闻检测的新突破

Explainable Fake News Detection With Large Language Model via Defense Among Competing Wisdom 论文地址: Explainable Fake News Detection with Large Language Model via Defense Among Competing Wisdom | Proceedings of the ACM on Web Conference 2024https://dl.…

Docker常用命令与实战示例

docker 1. 安装2. 常用命令3. 存储4. 网络5. redis主从复制示例6. wordpress示例7. DockerFile8. 一键安装超多中间件&#xff08;compose&#xff09; 1. 安装 以centOS系统为例 # 移除旧版本docker sudo yum remove docker \docker-client \docker-client-latest \docker-c…

CTO的职责是什么?

看《架构思维》作者是这样讲的&#xff1a; CTO 到底是做什么的&#xff1f; 我当下的答案是&#xff1a;“CTO 就是一个从技术视角出发&#xff0c;为公司或者所在的部门做正确决策的 CEO。”怎么理解这句话呢&#xff1f;作为一个 CTO&#xff0c;其长期目标和决策优先级与…

论文笔记:Spatial-Temporal Interval Aware Sequential POI Recommendation

ICDE 2022 1 intro 1.1 背景 空间&#xff08;Spatial&#xff09;和时间&#xff08;Temporal&#xff09;信息是序列 POI 推荐中两个重要且相辅相成的因素。 空间因素&#xff08;如地理距离间隔&#xff09;可以在用户的历史轨迹呈现空间分簇现象时&#xff0c;细粒度刻画…

2000年 - 2022年 Fama-French三因子模型数据+代码

Fama-French三因子模型是由著名经济学家尤金法玛&#xff08;Eugene Fama&#xff09;和肯尼斯法兰奇&#xff08;Kenneth French&#xff09;提出的&#xff0c;旨在改进资本资产定价模型&#xff08;CAPM&#xff09;&#xff0c;更全面地解释资产收益率的变化。该模型认为&a…

【动态规划】简单多状态dp问题

一、经验总结 在分析dp问题的状态表示时&#xff0c;发现当前阶段的状态可以继续细分为多个状态&#xff0c;且多个状态之间可以通过某种方式进行转换&#xff0c;这就是动态规划的多状态问题。 多状态问题的关键有以下几点&#xff1a; 找出dp问题的多个状态表示&#xff1a…

开源与在线 M3U8 Downloader 项目介绍及使用指南

M3U8 是一种用于播放列表格式的文件类型&#xff0c;广泛应用于流媒体服务中&#xff0c;特别是 HLS&#xff08;HTTP Live Streaming&#xff09;协议。它包含了一系列的 TS&#xff08;Transport Stream&#xff09;视频片段地址&#xff0c;使得视频能够分段加载&#xff0c…

【服务器06】之【如何不开外网连接GitHub】

登录GitHub官网 GitHub: Let’s build from here GitHub 注册账号 登录账号 输入一个自定义名字&#xff0c;点击创建存储库就可以了 首先 如何在不开外网的条件下使用GitHub 第一步 下载安装Steam(Watt TooklKit) 区分一下如何查看哪个官网&#xff08;没有百度广告就是…

如何在Android中实现多线程与线程池?

目录 一、Android介绍二、什么是多线程三、什么是线程池四、如何在Android中实现多线程与线程池 一、Android介绍 Android是一种基于Linux内核的开源操作系统&#xff0c;由Google公司领导开发。它最初于2007年发布&#xff0c;旨在为移动设备提供一种统一、可扩展的操作系统。…

UltraEditUEStudio软件安装包下载及安装教程

​根据软件大数据显示提供预定义的或使用者创建的编辑“环境”&#xff0c;能记住 UltraEdit 的所有可停靠窗口、工具栏等的状态。实际上我们可以这样讲HTML 工具栏&#xff0c;对常用的 HTML 功能作了预配置;文件加密/解密;多字节和集成的 IME。根据使用者情况表明Git Editor&…

【Linux基础IO】磁盘的结构、文件系统与inode、软硬链接

目录 磁盘的物理存储结构 磁盘的逻辑结构 文件系统与inode 硬链接 软链接 关于目录文件 磁盘的物理存储结构 磁盘是计算机中唯一的机械硬件设备&#xff0c;由磁头&#xff0c;盘面&#xff0c;磁道&#xff0c;扇区这四部分对磁盘上的数据进行增删查改操作。盘面上存储的…

面向对象修炼手册(二)(消息与继承)(Java宝典)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;面向对象修炼手册 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 消息传递 1 基本概念 1.…

App推广告别邀请码,Xinstall助您一键触达海量用户!

在移动互联网高速发展的今天&#xff0c;App的推广与运营已成为每个开发者都必须面对的问题。然而&#xff0c;随着互联网流量的日益分散和用户需求的不断变化&#xff0c;传统的App推广方式已经难以满足现代市场的需求。尤其是在获取用户时&#xff0c;很多开发者还在采用传统…

动手学深度学习(Pytorch版)代码实践 -卷积神经网络-26网络中的网络NiN

26网络中的网络NiN import torch from torch import nn import liliPytorch as lp import matplotlib.pyplot as plt# 定义一个NiN块 def nin_block(in_channels, out_channels, kernel_size, strides, padding):return nn.Sequential(# 传统的卷积层nn.Conv2d(in_channels, ou…

图的学习.

目录 一、图的基本概念 1.1图的种类 1.2顶点的度、入度和出度 1.3边的权和网 1.4路径、路径长度和回路 二、图的存储结构 2.1邻接矩阵法 2.2邻接表法 2.3十字链表 2.4邻接多重表 三、图的遍历 3.1广度优先搜索 3.2深度优先搜索 四、图的应用 4.1最小生成树 4.1.…

VSCode 安装Remote-SSH

1、打开扩展商店安装Remote-SSH 快捷键&#xff1a;CtrlShiftX 2、配置ssh连接 打开命令面板&#xff08;CtrlShiftP&#xff09; 输入"Remote-SSH: Connect to Host"并选择。 输入你的Ubuntu服务器的IP地址或主机名。 3、连接到ubuntu服务器 如果是第一次连接&…