【Apollo学习笔记】——规划模块TASK之PATH_REUSE_DECIDER
文章目录
- 前言
- PATH_REUSE_DECIDER功能简介
- PATH_REUSE_DECIDER相关配置
- PATH_REUSE_DECIDER总体流程
- PATH_REUSE_DECIDER相关子函数
- IsCollisionFree
- TrimHistoryPath
- IsIgnoredBlockingObstacle和GetBlockingObstacleS
- Else
- 参考
前言
在Apollo星火计划学习笔记——Apollo路径规划算法原理与实践与【Apollo学习笔记】——Planning模块讲到……Stage::Process的PlanOnReferenceLine
函数会依次调用task_list中的TASK,本文将会继续以LaneFollow为例依次介绍其中的TASK部分究竟做了哪些工作。由于个人能力所限,文章可能有纰漏的地方,还请批评斧正。
在modules/planning/conf/scenario/lane_follow_config.pb.txt
配置文件中,我们可以看到LaneFollow所需要执行的所有task。
stage_config: {stage_type: LANE_FOLLOW_DEFAULT_STAGEenabled: truetask_type: LANE_CHANGE_DECIDERtask_type: PATH_REUSE_DECIDERtask_type: PATH_LANE_BORROW_DECIDERtask_type: PATH_BOUNDS_DECIDERtask_type: PIECEWISE_JERK_PATH_OPTIMIZERtask_type: PATH_ASSESSMENT_DECIDERtask_type: PATH_DECIDERtask_type: RULE_BASED_STOP_DECIDERtask_type: SPEED_BOUNDS_PRIORI_DECIDERtask_type: SPEED_HEURISTIC_OPTIMIZERtask_type: SPEED_DECIDERtask_type: SPEED_BOUNDS_FINAL_DECIDERtask_type: PIECEWISE_JERK_SPEED_OPTIMIZER# task_type: PIECEWISE_JERK_NONLINEAR_SPEED_OPTIMIZERtask_type: RSS_DECIDER
本文将继续介绍LaneFollow的第二个TASK——PATH_REUSE_DECIDER
PATH_REUSE_DECIDER功能简介
主要功能:检查路径是否可重用,提高帧间平顺性。
主要逻辑:主要判断是否可以重用上一帧规划的路径。若上一帧的路径未与障碍物发生碰撞,则可以重用,提高稳定性,节省计算量。若上一帧的规划出的路径发生碰撞,则重新规划路径。
PATH_REUSE_DECIDER相关配置
PATH_REUSE_DECIDER
的相关配置集中在以下两个文件:modules/planning/conf/planning_config.pb.txt
和modules/planning/conf/scenario/lane_follow_config.pb.txt
// modules/planning/conf/planning_config.pb.txt
default_task_config: {task_type: PATH_REUSE_DECIDERpath_reuse_decider_config {reuse_path: false}
}
// modules/planning/conf/scenario/lane_follow_config.pb.txttask_config: {task_type: PATH_REUSE_DECIDERpath_reuse_decider_config {reuse_path: false}}
可以看到,默认情况不启用PATH_REUSE
,改为true
后启用。
PATH_REUSE_DECIDER总体流程
接着来看一看PATH_REUSE_DECIDER
的代码逻辑。代码路径:modules/planning/tasks/deciders/path_reuse_decider/path_reuse_decider.cc
主函数逻辑集中在Process
函数中:
Status PathReuseDecider::Process(Frame* const frame,ReferenceLineInfo* const reference_line_info) {// Sanity checks.CHECK_NOTNULL(frame);CHECK_NOTNULL(reference_line_info);if (!Decider::config_.path_reuse_decider_config().reuse_path()) {ADEBUG << "skipping reusing path: conf";reference_line_info->set_path_reusable(false);return Status::OK();}// skip path reuse if not in LANE_FOLLOW_SCENARIOconst auto scenario_type = injector_->planning_context()->planning_status().scenario().scenario_type();if (scenario_type != ScenarioType::LANE_FOLLOW) {ADEBUG << "skipping reusing path: not in LANE_FOLLOW scenario";reference_line_info->set_path_reusable(false);return Status::OK();}// active path reuse during change_lane onlyauto* lane_change_status = injector_->planning_context()->mutable_planning_status()->mutable_change_lane();ADEBUG << "lane change status: " << lane_change_status->ShortDebugString();// skip path reuse if not in_change_laneif (lane_change_status->status() != ChangeLaneStatus::IN_CHANGE_LANE &&!FLAGS_enable_reuse_path_in_lane_follow) {ADEBUG << "skipping reusing path: not in lane_change";reference_line_info->set_path_reusable(false);return Status::OK();}// for hybrid model: skip reuse path for valid path referenceconst bool valid_model_output =reference_line_info->path_data().is_valid_path_reference();if (valid_model_output) {ADEBUG << "skipping reusing path: path reference is valid";reference_line_info->set_path_reusable(false);return Status::OK();}/*count total_path_ when in_change_lane && reuse_path*/++total_path_counter_;/*reuse path when in non_change_lane reference line oroptimization succeeded in change_lane reference line*/bool is_change_lane_path = reference_line_info->IsChangeLanePath();if (is_change_lane_path && !lane_change_status->is_current_opt_succeed()) {reference_line_info->set_path_reusable(false);ADEBUG << "reusable_path_counter[" << reusable_path_counter_<< "] total_path_counter[" << total_path_counter_ << "]";ADEBUG << "Stop reusing path when optimization failed on change lane path";return Status::OK();}// stop reusing current path:// 1. replan path// 2. collision// 3. failed to trim previous path// 4. speed optimization failed on previous pathbool speed_optimization_successful = false;const auto& history_frame = injector_->frame_history()->Latest();if (history_frame) {const auto history_trajectory_type =history_frame->reference_line_info().front().trajectory_type();speed_optimization_successful =(history_trajectory_type != ADCTrajectory::SPEED_FALLBACK);}// const auto history_trajectory_type = injector_->FrameHistory()s// ->Latest()// ->reference_line_info()// .front()// .trajectory_type();if (path_reusable_) {if (!frame->current_frame_planned_trajectory().is_replan() &&speed_optimization_successful && IsCollisionFree(reference_line_info) &&TrimHistoryPath(frame, reference_line_info)) {ADEBUG << "reuse path";++reusable_path_counter_; // count reusable path} else {// stop reuse pathADEBUG << "stop reuse path";path_reusable_ = false;}} else {// F -> Tauto* mutable_path_decider_status = injector_->planning_context()->mutable_planning_status()->mutable_path_decider();static constexpr int kWaitCycle = -2; // wait 2 cycleconst int front_static_obstacle_cycle_counter =mutable_path_decider_status->front_static_obstacle_cycle_counter();const bool ignore_blocking_obstacle =IsIgnoredBlockingObstacle(reference_line_info);ADEBUG << "counter[" << front_static_obstacle_cycle_counter<< "] IsIgnoredBlockingObstacle[" << ignore_blocking_obstacle << "]";// stop reusing current path:// 1. blocking obstacle disappeared or moving far away// 2. trimming successful// 3. no statical obstacle collision.if ((front_static_obstacle_cycle_counter <= kWaitCycle ||ignore_blocking_obstacle) &&speed_optimization_successful && IsCollisionFree(reference_line_info) &&TrimHistoryPath(frame, reference_line_info)) {// enable reuse pathADEBUG << "reuse path: front_blocking_obstacle ignorable";path_reusable_ = true;++reusable_path_counter_;}}reference_line_info->set_path_reusable(path_reusable_);ADEBUG << "reusable_path_counter[" << reusable_path_counter_<< "] total_path_counter[" << total_path_counter_ << "]";return Status::OK();
}
PATH_REUSE_DECIDER相关子函数
IsCollisionFree
bool PathReuseDecider::IsCollisionFree(ReferenceLineInfo* const reference_line_info) {const ReferenceLine& reference_line = reference_line_info->reference_line();static constexpr double kMinObstacleArea = 1e-4;const double kSBuffer = 0.5;static constexpr int kNumExtraTailBoundPoint = 21;static constexpr double kPathBoundsDeciderResolution = 0.5;// current vehicle sl positioncommon::SLPoint adc_position_sl;GetADCSLPoint(reference_line, &adc_position_sl);// current obstaclesstd::vector<Polygon2d> obstacle_polygons;for (auto obstacle :reference_line_info->path_decision()->obstacles().Items()) {// filtered all non-static objects and virtual obstacleif (!obstacle->IsStatic() || obstacle->IsVirtual()) {if (!obstacle->IsStatic()) {ADEBUG << "SPOT a dynamic obstacle";}if (obstacle->IsVirtual()) {ADEBUG << "SPOT a virtual obstacle";}continue;}const auto& obstacle_sl = obstacle->PerceptionSLBoundary();// Ignore obstacles behind ADCif ((obstacle_sl.end_s() < adc_position_sl.s() - kSBuffer) ||// Ignore too small obstacles.(obstacle_sl.end_s() - obstacle_sl.start_s()) *(obstacle_sl.end_l() - obstacle_sl.start_l()) <kMinObstacleArea) {continue;}obstacle_polygons.push_back(Polygon2d({Vec2d(obstacle_sl.start_s(), obstacle_sl.start_l()),Vec2d(obstacle_sl.start_s(), obstacle_sl.end_l()),Vec2d(obstacle_sl.end_s(), obstacle_sl.end_l()),Vec2d(obstacle_sl.end_s(), obstacle_sl.start_l())}));}if (obstacle_polygons.empty()) {return true;}const auto& history_frame = injector_->frame_history()->Latest();if (!history_frame) {return false;}const DiscretizedPath& history_path =history_frame->current_frame_planned_path();// path end point// 将上一段轨迹的终点投影到SL坐标系下common::SLPoint path_end_position_sl;common::math::Vec2d path_end_position = {history_path.back().x(),history_path.back().y()};reference_line.XYToSL(path_end_position, &path_end_position_sl);for (size_t i = 0; i < history_path.size(); ++i) {common::SLPoint path_position_sl;common::math::Vec2d path_position = {history_path[i].x(),history_path[i].y()};reference_line.XYToSL(path_position, &path_position_sl);if (path_end_position_sl.s() - path_position_sl.s() <=kNumExtraTailBoundPoint * kPathBoundsDeciderResolution) {break;}if (path_position_sl.s() < adc_position_sl.s() - kSBuffer) {continue;}const auto& vehicle_box =common::VehicleConfigHelper::Instance()->GetBoundingBox(history_path[i]);std::vector<Vec2d> ABCDpoints = vehicle_box.GetAllCorners();for (const auto& corner_point : ABCDpoints) {// For each corner point, project it onto reference_linecommon::SLPoint curr_point_sl;if (!reference_line.XYToSL(corner_point, &curr_point_sl)) {AERROR << "Failed to get the projection from point onto ""reference_line";return false;}auto curr_point = Vec2d(curr_point_sl.s(), curr_point_sl.l());// Check if it's in any polygon of other static obstacles.for (const auto& obstacle_polygon : obstacle_polygons) {if (obstacle_polygon.IsPointIn(curr_point)) {// for debugADEBUG << "s distance to end point:" << path_end_position_sl.s();ADEBUG << "s distance to end point:" << path_position_sl.s();ADEBUG << "[" << i << "]"<< ", history_path[i].x(): " << std::setprecision(9)<< history_path[i].x() << ", history_path[i].y()"<< std::setprecision(9) << history_path[i].y();ADEBUG << "collision:" << curr_point.x() << ", " << curr_point.y();Vec2d xy_point;reference_line.SLToXY(curr_point_sl, &xy_point);ADEBUG << "collision:" << xy_point.x() << ", " << xy_point.y();return false;}}}}return true;
}
TrimHistoryPath
bool PathReuseDecider::TrimHistoryPath(Frame* frame, ReferenceLineInfo* const reference_line_info) {const ReferenceLine& reference_line = reference_line_info->reference_line();const auto& history_frame = injector_->frame_history()->Latest();if (!history_frame) {ADEBUG << "no history frame";return false;}// 找到上一帧轨迹的起始点const common::TrajectoryPoint history_planning_start_point =history_frame->PlanningStartPoint();common::PathPoint history_init_path_point =history_planning_start_point.path_point();ADEBUG << "history_init_path_point x:[" << std::setprecision(9)<< history_init_path_point.x() << "], y["<< history_init_path_point.y() << "], s: ["<< history_init_path_point.s() << "]";// 当前周期规划的起点const common::TrajectoryPoint planning_start_point =frame->PlanningStartPoint();common::PathPoint init_path_point = planning_start_point.path_point();ADEBUG << "init_path_point x:[" << std::setprecision(9) << init_path_point.x()<< "], y[" << init_path_point.y() << "], s: [" << init_path_point.s()<< "]";const DiscretizedPath& history_path =history_frame->current_frame_planned_path();DiscretizedPath trimmed_path;// 获取自车的SL坐标common::SLPoint adc_position_sl; // current vehicle sl positionGetADCSLPoint(reference_line, &adc_position_sl);ADEBUG << "adc_position_sl.s(): " << adc_position_sl.s();size_t path_start_index = 0;for (size_t i = 0; i < history_path.size(); ++i) {// find previous init point// 找到上周期轨迹规划的起点索引if (history_path[i].s() > 0) {path_start_index = i;break;}}ADEBUG << "!!!path_start_index[" << path_start_index << "]";// get current s=0common::SLPoint init_path_position_sl;// 当前轨迹的起点reference_line.XYToSL(init_path_point, &init_path_position_sl);bool inserted_init_point = false;//匹配当前规划起点位置,裁剪该点之后的轨迹for (size_t i = path_start_index; i < history_path.size(); ++i) {common::SLPoint path_position_sl;common::math::Vec2d path_position = {history_path[i].x(),history_path[i].y()};reference_line.XYToSL(path_position, &path_position_sl);double updated_s = path_position_sl.s() - init_path_position_sl.s();// insert init pointif (updated_s > 0 && !inserted_init_point) {trimmed_path.emplace_back(init_path_point);trimmed_path.back().set_s(0);inserted_init_point = true;}trimmed_path.emplace_back(history_path[i]);// if (i < 50) {// ADEBUG << "path_point:[" << i << "]" << updated_s;// path_position_sl.s();// ADEBUG << std::setprecision(9) << "path_point:[" << i << "]"// << "x: [" << history_path[i].x() << "], y:[" <<// history_path[i].y()// << "]. s[" << history_path[i].s() << "]";// }trimmed_path.back().set_s(updated_s);}ADEBUG << "trimmed_path[0]: " << trimmed_path.front().s();ADEBUG << "[END] trimmed_path.size(): " << trimmed_path.size();// 检查裁剪出来的轨迹是不是过短if (!NotShortPath(trimmed_path)) {ADEBUG << "short path: " << trimmed_path.size();return false;}// set pathauto path_data = reference_line_info->mutable_path_data();ADEBUG << "previous path_data size: " << history_path.size();path_data->SetReferenceLine(&reference_line);ADEBUG << "previous path_data size: " << path_data->discretized_path().size();path_data->SetDiscretizedPath(DiscretizedPath(std::move(trimmed_path)));ADEBUG << "not short path: " << trimmed_path.size();ADEBUG << "current path size: "<< reference_line_info->path_data().discretized_path().size();return true;
}
IsIgnoredBlockingObstacle和GetBlockingObstacleS
前方堵塞的障碍物是否离开足够远的距离
bool PathReuseDecider::IsIgnoredBlockingObstacle(ReferenceLineInfo* const reference_line_info) {const ReferenceLine& reference_line = reference_line_info->reference_line();static constexpr double kSDistBuffer = 30.0; // meterstatic constexpr int kTimeBuffer = 3; // second// vehicle speeddouble adc_speed = injector_->vehicle_state()->linear_velocity();double final_s_buffer = std::max(kSDistBuffer, kTimeBuffer * adc_speed);// current vehicle s positioncommon::SLPoint adc_position_sl;GetADCSLPoint(reference_line, &adc_position_sl);// blocking obstacle start sdouble blocking_obstacle_start_s;if (GetBlockingObstacleS(reference_line_info, &blocking_obstacle_start_s) &&// distance to blocking obstacle(blocking_obstacle_start_s - adc_position_sl.s() > final_s_buffer)) {ADEBUG << "blocking obstacle distance: "<< blocking_obstacle_start_s - adc_position_sl.s();return true;} else {return false;}
}
bool PathReuseDecider::GetBlockingObstacleS(ReferenceLineInfo* const reference_line_info, double* blocking_obstacle_s) {auto* mutable_path_decider_status = injector_->planning_context()->mutable_planning_status()->mutable_path_decider();// get blocking obstacle ID (front_static_obstacle_id)const std::string& blocking_obstacle_ID =mutable_path_decider_status->front_static_obstacle_id();const IndexedList<std::string, Obstacle>& indexed_obstacles =reference_line_info->path_decision()->obstacles();const auto* blocking_obstacle = indexed_obstacles.Find(blocking_obstacle_ID);if (blocking_obstacle == nullptr) {return false;}const auto& obstacle_sl = blocking_obstacle->PerceptionSLBoundary();*blocking_obstacle_s = obstacle_sl.start_s();ADEBUG << "blocking obstacle distance: " << obstacle_sl.start_s();return true;
}
Else
在启用reuse之后,之后的task会有这样一段代码,用以跳过以下流程,沿用之前的path
// skip path_lane_borrow_decider if reused pathif (FLAGS_enable_skip_path_tasks && reference_line_info->path_reusable()) {// for debugAINFO << "skip due to reusing path";return Status::OK();}
参考
[1] Apollo Planning决策规划代码详细解析 (7): PathReuseDecider
[2] Apollo6.0 PathReuseDecider流程与代码解析
相关文章:

【Apollo学习笔记】——规划模块TASK之PATH_REUSE_DECIDER
文章目录 前言PATH_REUSE_DECIDER功能简介PATH_REUSE_DECIDER相关配置PATH_REUSE_DECIDER总体流程PATH_REUSE_DECIDER相关子函数IsCollisionFreeTrimHistoryPathIsIgnoredBlockingObstacle和GetBlockingObstacleS Else参考 前言 在Apollo星火计划学习笔记——Apollo路径规划算…...

框架分析(6)-Ruby on Rails
框架分析(6)-Ruby on Rails 专栏介绍Ruby on Rails核心概念以及组件讲解MVC架构模式约定优于配置强大的ORM支持自动化测试丰富的插件生态系统RESTful路由安全性总结 优缺点优点快速开发简单易学MVC架构强大的ORM支持大量的插件和Gem支持 缺点性能问题学习…...

LLMs NLP模型评估Model evaluation ROUGE and BLEU SCORE
在整个课程中,你看到过类似模型在这个任务上表现良好,或者这个微调模型在性能上相对于基础模型有显著提升等陈述。 这些陈述是什么意思?如何形式化你的微调模型在你起初的预训练模型上的性能改进?让我们探讨一些由大型语言模型开…...

BlazorServer中C#与JavaScript的相互调用
BlazorServer中C#与JavaScript的相互调用 前言: 虽然BlazorServer中推荐使用C#在razor页面中的替代JavaScript来完成逻辑的编写,但当需要使用第三方的javascript文件/组件里的内容时,则难免要在C#中调用其方法或对象。反之当你的(用到第…...
深入理解 MD5 消息摘要算法和在密码存储中的应用及安全隐患
MD5 算法相信很多开发人员都听说过, 一个最常见的使用到它的地方就是密码的存储. 当然, 很多人会说, 这个算法已经不太安全了, 确实如果你想更安全的保存密码, 则应该考虑其它更安全的算法, 不过这不属于此次讨论的主题. 什么是 MD5 MD5 是一种算法, MD5 中的 MD 代表 Message…...

python网络爬虫指南二:多线程网络爬虫、动态内容爬取(待续)
文章目录 一、多线程网络爬虫1.1 线程的基础内容、GIL1.2 创建线程的两种方式1.3 threading.Thread类1.4 线程常用方法和锁机制1.5 生产者-消费者模式1.5.1 生产者-消费者模式简介1.5.2 Condition 类协调线程 1.6 线程中的安全队列1.6 多线程爬取王者荣耀壁纸1.6.1 网页分析1.6…...
华为AirEgine9700S AC配置示例
Vlan97为管理Vlan <AirEgine9700S>dis cu Software Version V200R021C00SPC100 #sysname AirEgine9700S #http timeout 60http secure-server ssl-policy default_policyhttp secure-server server-source -i allhttp server enable #set np rss hash-mode 5-tuple # md…...
VUE3基础
一、vue-router v4.x 介绍 | Vue Router 1、安装 yarn add vue-routernext next代表最新的版本 2、路由配置 在src目录下,新建router/index.ts,具体配置如下 import {RouteRecordRaw,createRouter,createWebHashHistory} from vue-router const r…...

Qt应用开发(基础篇)——日历 QCalendarWidget
一、前言 QCalendarWidget类继承于QWidget,是Qt设计用来让用户更直观的选择日期的窗口部件。 时间微调输入框 QCalendarWidget根据年份和月份初始化,程序员也通过提供公共函数去改变他们,默认日期为当前的系统时间,用户通过鼠标和…...

Python学习笔记:正则表达式、逻辑运算符、lamda、二叉树遍历规则、类的判断
1.正则表达式如何写? 序号实例说明1.匹配任何字符(除换行符以外)2\d等效于[0-9],匹配数字3\D等效于[^0-9],匹配非数字4\s等效于[\t\r\n\f],匹配空格字符5\S等效于[^\t\r\n\f],匹配非空格字符6\w等效于[A-Za-z0-9]&…...

【滑动窗口】leetcode1004:最大连续1的个数
一.题目描述 最大连续1的个数 这道题要我们找最大连续1的个数,看到“连续”二字,我们要想到滑动窗口的方法。滑动窗口的研究对象是一个连续的区间,这个区间需要满足某个条件。那么本题要找的是怎样的区间呢?是一个通过翻转0后得到…...

力扣:73. 矩阵置零(Python3)
题目: 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 来源:力扣(LeetCode) 链接:力扣(LeetCode)官网 - 全球极客挚…...
VB|基础语法 变量定义 函数定义 循环语句 IF判断语句等
文章目录 变量定义函数定义控制台输入输出switch case语句IF语句FOR循环语句不等于逻辑运算符 变量定义 int Dim 变量名 As Int32 0 string Dim 变量名 As String "" bool Dim 变量名 As Boolean False 枚举 Dim 变量名 As 枚举名 数组 Dim array(256) As String…...
Github 博客搭建
Github 博客搭建 准备工作 准备一个 github 账号;建立 github 仓库,仓库名为 username.github.io,同时设置仓库为 public;clone 仓库,写入一个 index.html 文件,推送到仓库(许多网上的教程会有…...
模型预测笔记(三):通过交叉验证网格搜索机器学习的最优参数
文章目录 网络搜索介绍步骤参数代码实现 网络搜索 介绍 网格搜索(Grid Search)是一种超参数优化方法,用于选择最佳的模型超参数组合。在机器学习中,超参数是在训练模型之前设置的参数,无法通过模型学习得到。网格搜索…...

创建型模式-建造者模式
使用多个简单的对象一步一步构建成一个复杂的对象 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部…...

Rust常用加密算法
哈希运算(以Sha256为例) main.rs: use crypto::digest::Digest;use crypto::sha2::Sha256;fn main() { let input "dashen"; let mut sha Sha256::new(); sha.input_str(input); println!("{}", sha.result_str());} Cargo.toml: [package]n…...

[管理与领导-55]:IT基层管理者 - 扩展技能 - 1 - 时间管理 -2- 自律与自身作则,管理者管好自己时间的五步法
前言: 管理好自己的时间,不仅仅是理念,也是方法和流程。 步骤1:理清各种待办事项 当提到工作事项时,这通常指的是要完成或处理的工作任务或事务。这些事项可以包括以下内容: 任务分配:根据工作…...
电子商务员考试题库及答案(中级)--判断题
电子商务员题库 一、判断题 1.EDI就是按照商定的协议,将商业文件分类,并通过计算机网络,在贸易伙伴的计算机网络系统之间进行数据交换和自动处理。〔〕 2.相互通信的EDI的用户必须使用相同类型的计算机。〔 〕 3.EDI采用共同…...

(WAF)Web应用程序防火墙介绍
(WAF)Web应用程序防火墙介绍 1. WAF概述 Web应用程序防火墙(WAF)是一种关键的网络安全解决方案,用于保护Web应用程序免受各种网络攻击和威胁。随着互联网的不断发展,Web应用程序变得越来越复杂&#x…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...