星火计划学习笔记——参考线平滑算法解析及实现(以U型弯道场景仿真调试为例)

news2025/1/12 18:56:28

文章目录

  • 1. Apollo参考线介绍
    • 1.1 参考线的作用
    • 1.2 导航规划的路线
    • 1.3 为什么需要重新生成参考线
    • 1.4 ReferenceLine数据结构
    • 1.5 ReferencePoint数据结构
    • 1.6 参考线处理流程
    • 1.7 参考线生成
  • 2. 参考线平滑算法
    • 2.1 参考线平滑算法总览
    • 2.2 参考线平滑算法流程
      • 2.2.1 设置中间点anchor points
      • 2.2.2 Smooth函数
      • 2.2.3FemPosSmooth
      • 2.2.4 Solve函数
    • 2.3 优化问题构造和求解
    • 2.4 平滑代价
    • 2.5 约束条件
      • 2.5.1 位置约束
      • 2.5.2 曲率约束
  • 3. U型道路仿真演示

1. Apollo参考线介绍

1.1 参考线的作用

在这里插入图片描述

参考线在Planning模块的位置

1.2 导航规划的路线

在这里插入图片描述    导航规划的路线一般由三个部分组成:Routing、参考线以及轨迹。
    Routing: 全局路径规划的结果,一般规划出来的长度为几十公里甚至上百公里,若没有障碍物,车辆将会沿着这条路径一直行驶。若有障碍物,则会由Planning模块发布回Routing模块进行重新规划。
    参考线: 决策规划模块根据车辆的实时位置进行计算。参考线的计算依赖于Routing的结果,但同时需要车辆周围的动态信息(障碍物、交通规则)。参考线相当于Routing结果里局部数据(车辆当前位置的一段Routing),距离通常只有几百米。参考线可能会有多条。
    轨迹: 是规划决策模块的最终输出结果。轨迹的输入是参考线。轨迹不仅包含车辆的路径信息,也包括车辆的动态信息(速度、加速度等)。

1.3 为什么需要重新生成参考线

在这里插入图片描述

导航路径不平滑导致车辆行驶不平稳以及导航路径太长可能会使局部路径产生多个投影点,需要生成局部平滑的参考线,同时节省算力。

1.4 ReferenceLine数据结构

在这里插入图片描述优先级:当有多条参考线时,具有优先级。
SpeedLimit:车辆在一段参考线里的速度限制。起始点的速度限制/终点的限制
speed_limit_:是一个数组,分段限速。
reference_points_:参考点,同样是数组。
map_path_:地图中的参考线,与planning中的参考线具有一一映射关系,参考点的数目是相同的。

1.5 ReferencePoint数据结构

在这里插入图片描述     ReferencePoint继承自MapPathPoint,MapPathPoint继承自Vec2d。
Vec2d:包含了点的二维数据。
MapPathPoint:包含了点的朝向以及地图上的点对应道路上的点。
ReferencePoint:包含了曲线的曲率与曲率的导数。

1.6 参考线处理流程

在这里插入图片描述     参考线的处理主要涉及两个线程:一个是参考线提供线程,另一个是规划线程。参考线提供线程提供参考线给规划线程去规划出一条轨迹。参考线的处理有两个步骤:首先是参考线的生成,直行时生成一条参考线,当有变道发生时,生成多条参考线。接下来是参考线的平滑,常见的参考线平滑方式有:离散点的平滑(Apollo采用)、螺旋线的平滑以及样条曲线的平滑。

1.7 参考线生成

在这里插入图片描述     参考线生成是由CreateReferenceLine函数实现的。首先由UpdateVehicleState函数获取车辆的状态,再由UpdateRoutingResponse函数获取Routing的结果,生成RouteSegment。若routing是新生成的,则对其进行平滑与分割;若不是,则沿用上一个周期的参考线,并对其进行扩展与延伸。

2. 参考线平滑算法

2.1 参考线平滑算法总览

在这里插入图片描述     常见的参考线平滑方式有:离散点的平滑(Apollo采用)、螺旋线的平滑以及样条曲线的平滑。在这里插入图片描述

// /home/yuan/apollo-edu/modules/planning/reference_line/reference_line_provider.cc
  if (smoother_config_.has_qp_spline()) {
    smoother_.reset(new QpSplineReferenceLineSmoother(smoother_config_));
  } else if (smoother_config_.has_spiral()) {
    smoother_.reset(new SpiralReferenceLineSmoother(smoother_config_));
  } else if (smoother_config_.has_discrete_points()) {
    smoother_.reset(new DiscretePointsReferenceLineSmoother(smoother_config_));
  } else {
    ACHECK(false) << "unknown smoother config "
                  << smoother_config_.DebugString();
  }

2.2 参考线平滑算法流程

// /home/yuan/apollo-edu/modules/planning/reference_line/reference_line_provider.cc
bool ReferenceLineProvider::SmoothReferenceLine(
    const ReferenceLine &raw_reference_line, ReferenceLine *reference_line) {
  if (!FLAGS_enable_smooth_reference_line) {
    *reference_line = raw_reference_line;
    return true;
  }
  // generate anchor points:
  std::vector<AnchorPoint> anchor_points;
  GetAnchorPoints(raw_reference_line, &anchor_points);
  smoother_->SetAnchorPoints(anchor_points);
  if (!smoother_->Smooth(raw_reference_line, reference_line)) {
    AERROR << "Failed to smooth reference line with anchor points";
    return false;
  }
  if (!IsReferenceLineSmoothValid(raw_reference_line, *reference_line)) {
    AERROR << "The smoothed reference line error is too large";
    return false;
  }
  return true;
}

在这里插入图片描述
GetAnchorPoints(raw_reference_line, &anchor_points);用于设置设置中间点
if (!smoother_->Smooth(raw_reference_line, reference_line)) {为平滑算法

2.2.1 设置中间点anchor points

// /home/yuan/apollo-edu/modules/planning/reference_line/reference_line_smoother.h
struct AnchorPoint {
  common::PathPoint path_point;
  double lateral_bound = 0.0;
  double longitudinal_bound = 0.0;
  // enforce smoother to strictly follow this reference point
  bool enforced = false;
};

     根据ReferenceLine选取中间点。根据ReferenceLine s(纵向距离上的投影)的值,进行均匀采样,采样的间隔约为0.25m。采样之后得到一系列的AnchorPoint,每个AnchorPoint包含PathPoint、横纵向的裕度以及是否为强约束的判断(若是,则必须为满足横纵向裕度,一条参考线只有首尾的点是强约束)。

// /home/yuan/apollo-edu/modules/planning/conf/discrete_points_smoother_config.pb.txt
max_constraint_interval : 0.25
longitudinal_boundary_bound : 2.0
max_lateral_boundary_bound : 0.5
min_lateral_boundary_bound : 0.1
curb_shift : 0.2
lateral_buffer : 0.2

discrete_points {
  smoothing_method: FEM_POS_DEVIATION_SMOOTHING
  fem_pos_deviation_smoothing {
    weight_fem_pos_deviation: 1e10
    weight_ref_deviation: 1.0
    weight_path_length: 1.0
    apply_curvature_constraint: false
    max_iter: 500
    time_limit: 0.0
    verbose: false
    scaled_termination: true
    warm_start: true
  }
}

主要参数
max_constraint_interval采样间隔
longitudinal_boundary_bound纵向边界
max_lateral_boundary_bound最大横向边界
min_lateral_boundary_bound最小横向边界
curb_shift与道路边缘的缓冲,若为实线,再增加一个buffer缓冲
lateral_buffer与道路边缘的缓冲

smoothing_method: FEM_POS_DEVIATION_SMOOTHING采用FemPosSmooth平滑算法
weight_fem_pos_deviation平滑度的代价权值 c o s t s m o o t h {cost_{smooth}} costsmooth
weight_ref_deviation相对原始点偏移量的代价权值 c o s t e r r {cost_{err}} costerr
weight_path_length参考线长度的代价权值 c o s t l e n g t h {cost_{length}} costlength
实验时主要考虑平滑,所以将平滑度的权值设得非常大,而其他权值忽略不计。

2.2.2 Smooth函数

/home/yuan/apollo-edu/modules/planning/reference_line/discrete_points_reference_line_smoother.cc
在这里插入图片描述     两种平滑算法通过配置文件去选择。

2.2.3FemPosSmooth

在这里插入图片描述

2.2.4 Solve函数

在这里插入图片描述
Apollo默认不考虑曲率的约束

2.3 优化问题构造和求解

在这里插入图片描述     Apollo主要是对离散点进行求解,通过构造二次规划问题,利用OSQP求解器进行求解。


优化变量:离散点 P k ( x k , y k ) {P_k(x_k,y_k)} Pk(xk,yk)
优化目标: 平滑度、长度 、相对原始点偏移量
优化函数 c o s t = c o s t s m o o t h + c o s t l e n g t h + c o s t e r r {cost=cost_{smooth}+cost_{length}+cost_{err}} cost=costsmooth+costlength+costerr
c o s t s m o o t h {cost_{smooth}} costsmooth——平滑度的代价
c o s t l e n g t h {cost_{length}} costlength——参考线长度的代价 c o s t l e n g t h = ∑ i = 0 n − 1 ( x i − x i − 1 ) 2 + ( y i − y i − 1 ) 2 cos{t_{length}} = \sum\limits_{i = 0}^{n - 1} {{{({x_i} - {x_{i - 1}})}^2}} + {({y_i} - {y_{i - 1}})^2} costlength=i=0n1(xixi1)2+(yiyi1)2 c o s t e r r {cost_{err}} costerr——相对原始点偏移量的代价 c o s t e r r = ∑ i = 0 n − 1 ( x i − x i _ r e f ) 2 + ( y i − y i _ r e f ) 2 cos{t_{err}} = \sum\limits_{i = 0}^{n - 1} {{{({x_i} - {x_{i\_ref}})}^2}} + {({y_i} - {y_{i\_ref}})^2} costerr=i=0n1(xixi_ref)2+(yiyi_ref)2

2.4 平滑代价

     c o s t s m o o t h {cost_{smooth}} costsmooth有两种计算方式:FemPosSmooth与CosThetaSmooth。
FemPosSmooth


在这里插入图片描述
在这里插入图片描述
    构造方式:一个参考点 P 1 ( x 1 , y 1 ) {P_1(x_1,y_1)} P1(x1,y1)以及其前后两个相邻点 P 0 ( x 0 , y 0 ) {P_0(x_0,y_0)} P0(x0,y0) P 2 ( x 2 , y 2 ) {P_2(x_2,y_2)} P2(x2,y2)构成的向量 P 1 P 0 → \overrightarrow {{P_{\rm{1}}}{P_{\rm{0}}}} P1P0 P 1 P 2 → \overrightarrow {{P_{\rm{1}}}{P_{\rm{2}}}} P1P2 的合向量 P 1 P s → \overrightarrow {{P_{\rm{1}}}{P_{\rm{s}}}} P1Ps ,求其模的平方。

CosThetaSmooth
在这里插入图片描述在这里插入图片描述    构造方式:利用相邻两个向量 P 0 P 1 → \overrightarrow {{P_{\rm{0}}}{P_{\rm{1}}}} P0P1 P 1 P 2 → \overrightarrow {{P_{\rm{1}}}{P_{\rm{2}}}} P1P2 的夹角 θ \theta θ的余弦值进行构造。

2.5 约束条件

2.5.1 位置约束

在这里插入图片描述约束方程:
在这里插入图片描述    保证点在boundingbox之内。

2.5.2 曲率约束

在这里插入图片描述
曲率约束首先有以下三个假设:
在这里插入图片描述
通过三个假设,构造出约束方程:
在这里插入图片描述
∆ s ∆s s是采样平均长度, c u r c s t r {cur_{cstr}} curcstr是最大曲率约束.

3. U型道路仿真演示

云平台地址——Apollo规划之U型路口仿真调试

启动DreamView

bash scripts/apollo_neo.sh bootstrap 

如图所示选好
在这里插入图片描述在这里插入图片描述
曲率较大的U型弯
在这里插入图片描述
打开 jupyter notebook,绘制脚本在/modules/tools

 jupyter notebook

在这里插入图片描述在这里插入图片描述
脚本代码

import json
import sys
import argparse
import sys
import numpy as np
import matplotlib.pyplot as plt
import math
 
from cyber.python.cyber_py3.record import RecordReader
from modules.common_msgs.planning_msgs import planning_pb2
  
class RefLineInfo():
    def __init__(self, x, y, s, theta, kappa, dkappa):
        self.x = x
        self.y = y
        self.s = s
        self.theta = theta
        self.kappa = kappa
        self.dkappa = dkappa
 
def trim_path_by_distance(adc_trajectory, s):
    path_coords = []
    path_points = adc_trajectory.trajectory_point
    for point in path_points:
        if point.path_point.s <= s:
            path_coords.append([point.path_point.x, point.path_point.y])
    return path_coords
 
def project(point, ref_line):
    start_point = ref_line[0]
    end_point = start_point
    for line_point in ref_line:
        if line_point.s - ref_line[0].s < 0.2:
            continue
        point_dir = [start_point.x - point.x, start_point.y - point.y]
        line_dir = [line_point.x - start_point.x, line_point.y - start_point.y]
        dot = point_dir[0] * line_dir[0] + point_dir[1] * line_dir[1]
        if dot > 0.0:
            start_point = line_point
            continue
        s = dot / math.sqrt(line_dir[0] * line_dir[0] + line_dir[1] * line_dir[1])
        return start_point.s - s
        
 
def align_reference_lines(ref_line1, ref_line2):
    if len(ref_line1) < 2 or len(ref_line2) < 2:
        return [0.0, 0.0]
    if ref_line1[-1].s < 0.5 or ref_line2[-1].s < 0.5:
        return [0.0, 0.0]
    
    s_ref_line1 = [ref_line1[0].x, ref_line1[0].y]
    cur_index = 1
    e_ref_line1 = [ref_line1[cur_index].x, ref_line1[cur_index].y]
    while ref_line1[cur_index].s < 0.5:
        cur_index = cur_index + 1
        e_ref_line1 = [ref_line1[cur_index].x, ref_line1[cur_index].y]
        
    start_point2 = ref_line2[0]
    line_dir = [e_ref_line1[0] - s_ref_line1[0], e_ref_line1[1] - s_ref_line1[1]]
    start_dir = [s_ref_line1[0] - start_point2.x, s_ref_line1[1] - start_point2.y]
    dot = line_dir[0] * start_dir[0] + line_dir[1] * start_dir[1]
    if dot > 0.0:
        return [0.0, project(ref_line1[0], ref_line2)]
    return [project(start_point2, ref_line1), 0.0]
    
    
def get_ref_line(record_path, ref_line_index=0):
    reader = RecordReader(record_path)
    current_ref_line_index = 0
    for msg in reader.read_messages():
        if msg.topic == "/apollo/planning":
            if current_ref_line_index != ref_line_index:
                current_ref_line_index = current_ref_line_index + 1
                continue
            adc_trajectory = planning_pb2.ADCTrajectory()
            adc_trajectory.ParseFromString(msg.message)
 
            for path in adc_trajectory.debug.planning_data.path:
                if path.name != 'planning_reference_line':
                    continue
                path_coords = trim_path_by_distance(adc_trajectory, 5.0)
 
                ref_line = []
                last_theta = path.path_point[0].theta
                for point in path.path_point:
                    if point.theta - last_theta > math.pi:
                        point.theta = point.theta - 2.0 * math.pi
                    elif last_theta - point.theta > math.pi:
                        point.theta = point.theta + 2.0 * math.pi
                    ref_line.append(RefLineInfo(point.x, point.y, point.s, point.theta, point.kappa, point.dkappa))
                return ref_line
            
def plot_ref_line(start_s, ref_line, use_dot):
    x = []
    y = []
    s = []
    theta = []
    kappa = []
    dkappa = []
    scale_factor = 10.0
    for point in ref_line:
        if point.s < start_s:
            continue
        x.append(point.x)
        y.append(point.y)
        s.append(point.s)
        theta.append(point.theta)
        kappa.append(point.kappa * scale_factor)
        dkappa.append(point.dkappa * scale_factor)
    if use_dot:
        plt.plot(s, theta, 'b--', alpha=0.5, label='theta')
        plt.plot(s, kappa, 'r--', alpha=0.5, label='kappa')
        plt.plot(s, dkappa, 'g--', alpha=0.5, label='dkappa')
    else:
        plt.plot(s, theta, 'b', alpha=0.5, label='theta')
        plt.plot(s, kappa, 'r', alpha=0.5, label='kappa')
        plt.plot(s, dkappa, 'g', alpha=0.5, label='dkappa')
                    
def plot_ref_path(record_file1, record_file2):
    ref_line1 = get_ref_line(record_file1)
    ref_line2 = get_ref_line(record_file2)
    [s1, s2] = align_reference_lines(ref_line1, ref_line2)
    plot_ref_line(s1, ref_line1, True)
    plot_ref_line(s2, ref_line2, False)
    
        
record_file1 = './20221221113737.record.00000'
record_file2 = './20221221120201.record.00000'
 
plot_ref_path(record_file1, record_file2)
      
plt.legend()
plt.show()

cyber_recorder record -a记录数据

cyber_recorder record -a

在这里插入图片描述
我们所需要记录并绘制的数据在这里插入图片描述将记录好的数据移动到./modules/tools/
在这里插入图片描述
/apollo/modules/planning/conf/planning.conf 下查看所用的是哪个平滑算法

vim /apollo/modules/planning/conf/planning.conf 

在这里插入图片描述
进入平滑算法的配置文件

vim /apollo/modules/planning/conf/discrete_points_smoother_config.pb.txt 

在这里插入图片描述
修改参数,并重启DreamView,防止数据残留。

 bash scripts/bootstrap_neo.sh restart

按照之前的步骤得到第二份数据。
在这里插入图片描述
在这里插入图片描述


max_lateral_boundary_bound : 0.2 其他不变
虚线为未更改的参考线数据,实现为更改后的参考线数据

可以看到,调整横向的参数对整体平滑具有较大的影响。

按照之前的步骤得到第三份数据。
在这里插入图片描述

longitudinal_boundary_bound : 1.0 其他不变
虚线为未更改的参考线数据,实现为更改后的参考线数据

可以看到,调整纵向的参数对于参考线的平滑影响不大。

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

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

相关文章

康希通信冲刺科创板上市:上半年收入2亿元,计划募资约8亿元

12月21日&#xff0c;格兰康希通信科技&#xff08;上海&#xff09;股份有限公司&#xff08;下称“康希通信”&#xff09;在上海证券交易所递交招股书&#xff0c;准备在科创板上市。本次冲刺科创板上市&#xff0c;康希通信计划募资7.82亿元。 据贝多财经了解&#xff0c;康…

Word处理控件Aspose.Words功能演示:在 Java 中将 DOC 或 DOCX 转换为 PNG

aspose.words是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

【C++入门基础(上)】

Cross the stars over the moon to meet your better-self. 目录 1 命名空间 1.1 命名空间定义 1.2 命名空间使用 1.2.1 加命名空间名称及作用域限定符 1.2.2 使用using将命名空间中成员引入 1.2.3 使用using namespace 命名空间名称引入 2 C输入&&输出 3 缺省参数…

Mentor-dft 学习笔记 day47-On-Chip Clock Controller Design Description

On-Chip Clock Controller Design Description有三种类型的片上控制器设计&#xff1a;standard, parent, and child。可以根据设计要求选择它们。使用OCC时&#xff0c;必须考虑本节中讨论的设计元素。The Standard OCC 标准OCC为快速捕获提供快速时钟&#xff0c;为换档和慢速…

Java字节流的使用:字节输入/输出流、文件输入/输出流、字节数组输入/输出流

InputStream 是 Java 所有字节输入流类的父类&#xff0c;OutputStream 是 Java 所有字节输出流类的父类&#xff0c;它们都是一个抽象类&#xff0c;因此继承它们的子类要重新定义父类中的抽象方法。 这里首先介绍上述两个父类提供的常用方法&#xff0c;然后介绍如何使用它们…

springboot整合shiro + jwt + redis实现权限认证(上手即用)

目录前言项目结构依赖导入建数据库表建表语句使用插件生成增删改查添加MyRealm添加ShiroConfig添加JwtFilterJWT相关得类JwtTokenJwtAudienceJwtHelper添加BeanFactory只贴出主要得类&#xff0c;具体得可以看我的gitee&#xff0c;接口都自测过的。前言 最近项目中涉及到使用…

NEST.JS使用心得

最近部门分享了nest.js技术&#xff0c;旨在前端人员通过项目积累将可重复使用的数据或者自己需要的数据通过nest设计出接口方便快速开发&#xff0c;不需要等待后端开发人员的数据。学习了两天发现nest很有意思&#xff0c;所以来分享下最近两天的学习心得。 nest中文文档&am…

linux下使用命令TC进行网络限流 —— 筑梦之路

Linux 下的流量控制原理 通过对包的排队&#xff0c;我们可以控制数据包的发送方式。这种控制&#xff0c;称之为数据整形&#xff0c;shape the data&#xff0c;包括对数据的以下操作: 增加延时 丢包 重新排列 重复、损坏 速率控制 在 qdisc-class-filter 结构下&#x…

ADI Blackfin DSP处理器-BF533的开发详解64:电子相册的设计(含源码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 代码实现功能 代码实现了将 SD 卡根目录下的所有文件进行文件列表&#xff0c;然后将 480*272 尺寸的 JPEG 文件进行 JPEG 解码&#xff0c;将解…

校园跳蚤市场平台/校园二手交易系统

摘 要 本文论述了校园跳蚤市场平台的设计和实现&#xff0c;该网站从实际运用的角度出发&#xff0c;运用了计算机网站设计、数据库等相关知识&#xff0c;网络和Mysql数据库设计来实现的&#xff0c;网站主要包括学生注册、学生登录、浏览商品、搜索商品、查看商品并进行购买…

Blender——“苹果”建模

效果图 1.调出点线面面板&#xff0c;衰减编辑 1.1打开blender&#xff0c;点击常规&#xff0c;按A全选物体&#xff08;摄像头、光源、正方体&#xff09;&#xff0c;按delete删除。 1.2 在3D视图中添加一个经纬球。点击添加&#xff0c;选择网格—>经纬球。 1.3 点击下…

前端JS也可以连点成线(Vue中运用 AntVG6)

前言 什么是 G6&#xff1f;G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。旨在让关系变得透明&#xff0c;简单。让用户获得关系数据的 Insight。其实说白了就是我们前端中的绘图工具&#xff1b;基于 G6&#xff0c;用户可以快速搭…

Linux基础知识-文件目录结构及基本属性

1、前言 上一篇我们讲到了Linux 文件类型7种类型&#xff0c;本篇我们说说Linux文件目录结构。 2、Linux 目录树 所有可操作的计算机资源都存在于目录树这个结构中&#xff0c;对计算资源的访问&#xff0c;可以看做是对这棵目录树的访问。Linux 的目录结构如下&#xff1a;…

常见日志框架使用及日志打印规范设计

文章目录一、slf4j 简介二、常用日志框架1&#xff09;log4jpom 依赖log4j.properties 文件配置测试参考2&#xff09;logbackpom 依赖logback.xml 配置测试参考3&#xff09; java.util.logging4&#xff09;commons loggingpom 依赖配置测试参考5&#xff09;slf4j-simplepom…

MFC UI控件相关

文章目录UI控件相关CDialog::OnInitDialog() 对话框初始化手动添加UpdateData() 刷新窗口数据DoDataExchange()数据与控件动态绑定afx_msg: 声明一个消息响应函数void AFXAPI DDX_Control( CDataExchange* pDX, int nIDC, CWnd& rControl );DDV_MaxChars()UI控件相关 CDia…

20天学会Rust第一天之Helloword

阿sir今天开始学习Rust了&#xff0c;至于为什么学习呢&#xff1f; 以后再说 我们都知道&#xff0c;程序设计 数据结构 算法。 因此&#xff0c;我们依次学习Rust中数据结构的定义&算法的实现&#xff0c;然后用它们实现一个简单的“hello world” 数据结构 Rust提供…

什么是IoC和AOP

IoC是什么&#xff1f; 控制反转&#xff1a;面向对象的设计的理念。上层建筑依赖下层建筑 理解&#xff1a;行李箱设计 轮子 --> 底盘 --> 箱体 --> 行李箱 如果改动轮子&#xff0c;则底盘、箱体、行李箱都需要进行调整。 依赖注入&#xff1a;将底层类作为参数…

Spring之底层架构核心概念-BeanFactory 与ApplicationContext

目录1.BeanFactory2.ApplicationContext3.关系4.总结1.BeanFactory BeanFactory是一个接口 public interface BeanFactory {xxx... }2.ApplicationContext ApplicationContext 也是一个接口&#xff0c;继承自ListableBeanFactory, HierarchicalBeanFactory public interfa…

非零基础自学Golang 第15章 Go命令行工具 15.2 代码获取(get) 15.3 格式化代码(fmt)

非零基础自学Golang 文章目录非零基础自学Golang第15章 Go命令行工具15.2 代码获取(get)15.3 格式化代码(fmt)第15章 Go命令行工具 15.2 代码获取(get) go get命令用于从远程仓库中下载安装远程代码包&#xff0c;这是我们常用且非常重要的指令。 我们在开发程序时往往需要引…

Gateway网关-网关作用介绍

为什么需要网关&#xff1f; 如果允许任何人访问微服务&#xff0c;查看我们的敏感业务&#xff0c;这样数据是不是不安全。如果是我们的工作人员并且有相应的查看权限&#xff0c;我们才提供访问权限。那谁来做这件事呢&#xff1f;就是我们的网关。 网关的功能作用 1&#xf…