BEV学习---LSS-1:论文原理及代码串讲

news2024/11/15 3:29:40

在这里插入图片描述

目前在自动驾驶领域,比较火的一类研究方向是基于采集到的环视图像信息,去构建BEV视角下的特征完成自动驾驶感知的相关任务。所以如何准确的完成从相机视角向BEV视角下的转变就变得由为重要。目前感觉比较主流的方法可以大体分为两种:

1、显式估计图像的深度信息,完成BEV视角的构建,在某些文章中也被称为自下而上的构建方式;
2、利用transformer中的query查询机制,利用BEV Query构建BEV特征,这一过程也被称为自上而下的构建方式;

LSS最大的贡献在于:提供了一个端到端的训练方法,解决了多个传感器融合的问题。传统的多个传感器单独检测后再进行后处理的方法无法将此过程损失进行反向传播而调整相机输入,而LSS则省去了这一阶段的后处理,直接输出融合结果。

LSS提出了一种将多视角的相机图像融合在BEV空间下的编码方法,
其中:
Lift:通过预测深度信息,将2D图像编码到3D空间;
Splat:将3D特征的高度拍扁为BEV特征;
Shoot:运动规划。

一、Lift:
1.1、参数:
我们先介绍一下一些参数。

感知范围:
x轴方向的感知范围 -50m ~ 50m;y轴方向的感知范围 -50m ~ 50m;z轴方向的感知范围 -10m ~ 10m;

BEV单元格大小:
x轴方向的单位长度 0.5m;y轴方向的单位长度 0.5m;z轴方向的单位长度 20m;

BEV的网格尺寸:
200 x 200 x 1;
深度估计范围:
由于LSS需要显式估计像素的离散深度,论文给出的范围是 4m ~ 45m,间隔为1m,也就是算法会估计41个离散深度,也就是下面的dbound。

why dbound:
因为二维像素可以理解为现实世界中的某一个点到相机中心的一条射线,我们如果知道相机的内外参数,就是知道了对应关系,但是我们不知道是射线上面的那一个点(也就是不知道depth),所以作者在距离相机5m到45m的视锥内,每隔1m有一个模型可选的深度值(这样每个像素有41个可选的离散深度值)。

在这里插入图片描述
代码如下:

ogfH=128  
ogfW=352
xbound=[-50.0, 50.0, 0.5]		# x方向网格
ybound=[-50.0, 50.0, 0.5]		# y方向网格
zbound=[-10.0, 10.0, 20.0]		# z方向网格
dbound=[4.0, 45.0, 1.0]			# 深度方向网格
fH, fW = ogfH // 16, ogfW // 16	# 16倍下采样

1.2、创建视锥
建立特征图在世界坐标系下坐标位置的视锥点云,也就是深度特征每个点的在相机坐标系的位置。

 def create_frustum(self):
        # make grid in image plane
        ogfH, ogfW = self.data_aug_conf['final_dim']
        fH, fW = ogfH // self.downsample, ogfW // self.downsample
        ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)
        D, _, _ = ds.shape
        xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)
        ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)

        # D x H x W x 3
        frustum = torch.stack((xs, ys, ds), -1)
        return nn.Parameter(frustum, requires_grad=False)

根据代码可知,它的尺寸是根据一个2dimage构建的,它的尺寸为D H W * 3,维度3表示:【 x,y,depth】。我们可以把这个视锥理解为一个长方体,长x,宽y 高depth,视锥中的每个点都是长方体的坐标。

1.3、CamEncode
这部分主要是通过Efficient Net来提取图像的features,首先看代码:

class CamEncode(nn.Module):			# 提取图像特征,进行图像深度编码
    def __init__(self, D, C, downsample):
        super(CamEncode, self).__init__()
        self.D = D	# 41 深度区间【4-45】
        self.C = C	# 64 点的特征向量维度
	
		# efficientnet 提取特征
        self.trunk = EfficientNet.from_pretrained("efficientnet-b0")

        self.up1 = Up(320+112, 512)	# 上采样模块,输入320+112(多尺度融合),输出通道512
        # 1x1卷积调整通道数,输出通道数为D+C,D为可选深度值个数,C为特征通道数
        self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0)
	
	# 深度维计算softmax,得到每个像素不同深度的概率
    def get_depth_dist(self, x, eps=1e-20):
        return x.softmax(dim=1)

    def get_depth_feat(self, x):
        # 使用efficientnet提取特征  x: BN x 512 x 8 x 22
        x = self.get_eff_depth(x)
        # 1x1卷积变换维度,输出通道数为D+C,x: BN x 105(C+D) x 8 x 22
        x = self.depthnet(x)
        # 第二个维度的前D个作为深度维,进行softmax  depth: BN x 41 x 8 x 22
        depth = self.get_depth_dist(x[:, :self.D])
        
        # 将深度概率分布和特征通道利用广播机制相乘
        # 深度值 * 特征 = 2D特征转变为3D空间(俯视图)内的特征
        new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2)

        return depth, new_x	#  new_x: BN x 64 x 41 x 8 x 22


    def get_eff_depth(self, x):  # 使用efficientnet提取特征
        # adapted from https://github.com/lukemelas/EfficientNet-PyTorch/blob/master/efficientnet_pytorch/model.py#L231
        endpoints = dict()

        # Stem
        x = self.trunk._swish(self.trunk._bn0(self.trunk._conv_stem(x)))  #  x: BN x 32 x 64 x 176
        prev_x = x 

        # Blocks
        for idx, block in enumerate(self.trunk._blocks):
            drop_connect_rate = self.trunk._global_params.drop_connect_rate
            if drop_connect_rate:
                drop_connect_rate *= float(idx) / len(self.trunk._blocks) # scale drop connect_rate
            x = block(x, drop_connect_rate=drop_connect_rate)
            if prev_x.size(2) > x.size(2):
                endpoints['reduction_{}'.format(len(endpoints)+1)] = prev_x
            prev_x = x

        # Head
        endpoints['reduction_{}'.format(len(endpoints)+1)] = x  # x: BN x 320 x 4 x 11
        x = self.up1(endpoints['reduction_5'], endpoints['reduction_4'])  # 对endpoints[4]上采样,然后和endpoints[5] concat 在一起
        return x  # x: 24 x 512 x 8 x 22


    def forward(self, x):

		# depth: B*N x D x fH x fW(24 x 41 x 8 x 22)  x: B*N x C x D x fH x fW(24 x 64 x 41 x 8 x 22)
        depth, x = self.get_depth_feat(x)

        return x

起初与以往的相同,到了init函数的最后一句,把feature的channel下采样到了 D +C,D与上面的视锥的D一致,用来储存深度特征,C为图像的语义特征,然后对channel为D的那部分在执行softmax 用来预测depth的概率分布,然后把D这部分与C这部分单独拿出来让二者做外积,就得到了shape为BNDCHW的feature。

在这里插入图片描述
观察右面的网格图,首先解释一下网格图的坐标,其中a代表某一个深度softmax概率(大小为H * W),c代表语义特征的某一个channel的feature,那么ac就表示这两个矩阵的对应元素相乘,于是就为feature的每一个点赋予了一个depth 概率,然后广播所有的ac,就得到了不同的channel的语义特征在不同深度(channel)的feature map,经过训练,重要的特征颜色会越来越深(由于softmax概率高),反之就会越来越暗淡,趋近于0。

二、Splat

得到了带有深度信息的feature map,那么我们想知道这些特征对应3D空间的哪个点,我们怎么做呢?

由于我们的视锥对原图做了16倍的下采样,而在上面得到feature map的感受野也是16,那么我们可以在接下来的操作把feature map映射到视锥坐标下。

2.1 转换视锥坐标系
首先我们之前得到了一个2D的视锥,现在通过相机的内外参数把它映射到车身(以车中心为原点)坐标系。

代码如下:

def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
    """    
       rots:相机外参旋转, trans:相机外参平移, intrins:相机内参, post_rots:数据增强旋转, post_trans:数据增强平移
       Determine the (x,y,z) locations (in the ego frame) of the points in the point cloud.
       Returns B x N x D x H/downsample x W/downsample x 3
    """
    B, N, _ = trans.shape  # B: batch size N:环视相机个数

    # undo post-transformation
    # B x N x D x H x W x 3
    # 抵消数据增强及预处理对像素的变化,即还原数据增强中旋转和平移对坐标的影响
    points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
    points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))

    # 图像坐标系 -> 归一化相机坐标系 -> 相机坐标系 -> 车身坐标系
    # 但是自认为由于转换过程是线性的,所以反归一化是在图像坐标系完成的,然后再利用
    # 求完逆的内参投影回相机坐标系
    points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
                        points[:, :, :, :, :, 2:3]
                        ), 5)  # 反归一化
                        
    combine = rots.matmul(torch.inverse(intrins))
    points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
    points += trans.view(B, N, 1, 1, 1, 3)
    
    # (bs, N, depth, H, W, 3):其物理含义
    # 每个batch中的每个环视相机图像特征点,其在不同深度下位置对应
    # 在ego坐标系下的坐标
    return points	#维度不变,坐标值从相机坐标系->世界坐标系

2.2、BEV池化

至此,我们将6个环视摄像头拍摄的图片,通过显式的计算41个深度下的特征,将这【6,41 ,fW, fH】个特征点一一放置在了早先准备好的BEV特征空间下。

在这张像棋盘一样的BEV特征空间中,有的位置放了多个特征点,有的位置没放特征点,因此需要在每个栅格进行BEV池化,得到尺寸统一的BEV特征。

官方代码的实现思路是:
1、先根据特征点的XYZ坐标和batch,计算每个点的索引值;索引值相同的点位于同一个栅格中;【代码26-30】
2、根据索引值排序,则索引值变为有序数组,形如【1.2,1.4,2,4,4,5 … 】;
3、只需“遍历”索引值,将相同索引值的位置求和,完成池化,形如【1.2+1.4,2,4+4,5 … 】

下方voxel_pooling函数准备索引,BEV池化在QuickCumsum类中进行;

def voxel_pooling(self, geom_feats, x):
        # geom_feats: B x N x D x H x W x 3 (4 x 6 x 41 x 8 x 22 x 3):在ego坐标系下的坐标点;
        # x: B x N x D x fH x fW x C(4 x 6 x 41 x 8 x 22 x 64):图像点云特征

        B, N, D, H, W, C = x.shape  # B: 4  N: 6  D: 41  H: 8  W: 22  C: 64
        Nprime = B*N*D*H*W  # Nprime: 173184

        # flatten x
        x = x.reshape(Nprime, C)   # 将特征点云展平,一共有 B*N*D*H*W 个点 个点

        # flatten indices
        geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long()  # 将ego下的空间坐标[-50,50] [-10 10]的范围平移转换到体素坐标[0,100] [0,20],计算栅格坐标并取整
        geom_feats = geom_feats.view(Nprime, 3)  # 将体素坐标同样展平  geom_feats: B*N*D*H*W x 3 (173184 x 3)
        batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
                             device=x.device, dtype=torch.long) for ix in range(B)])  # 每个点对应于哪个batch
        geom_feats = torch.cat((geom_feats, batch_ix), 1)  # geom_feats: B*N*D*H*W x 4(173184 x 4), geom_feats[:,3]表示batch_id

        # filter out points that are outside box
        # 过滤掉在边界线之外的点 x:0~199  y: 0~199  z: 0
        kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
            & (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
            & (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
        x = x[kept]  # x: 168648 x 64
        geom_feats = geom_feats[kept]

        # get tensors from the same voxel next to each other
        ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
            + geom_feats[:, 1] * (self.nx[2] * B)\
            + geom_feats[:, 2] * B\
            + geom_feats[:, 3]  # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
        sorts = ranks.argsort()
        x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts]  # 按照rank排序,这样rank相近的点就在一起了
        # x: 168648 x 64  geom_feats: 168648 x 4  ranks: 168648

        # cumsum trick
        if not self.use_quickcumsum:
            x, geom_feats = cumsum_trick(x, geom_feats, ranks)
        else:
            x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks)  # 一个batch的一个格子里只留一个点 x: 29072 x 64  geom_feats: 29072 x 4

        # griddify (B x C x Z x X x Y)
        final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device)  # final: 4 x 64 x 1 x 200 x 200
        final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x  # 将x按照栅格坐标放到final中

        # collapse Z
        final = torch.cat(final.unbind(dim=2), 1)  # 消除掉z维

        return final  # final: 4 x 64 x 200 x 200

torch.autograd.Function是自定义的算子,能在python层面自定义算子的前向和后向过程。代码中前向过程使用前缀和区间求和;向量错位比较前后元素是否一致,这些计算步骤都使计算过程向量化;前向过程存储了保留下特征的位置,反向过程再使用前缀和技巧,将特征保留位置的梯度分给求和的位置。

class QuickCumsum(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, geom_feats, ranks):
        # x: 168648 x 64  geom_feats: 168648 x 4  ranks: 168648 x
        x = x.cumsum(0) # 求前缀和  x: 168648 x 64
        kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool)  # kept: 168648 x
        kept[:-1] = (ranks[1:] != ranks[:-1])  # rank错位比较,rank[0]!=rank[1],则留下rank[1]

        x, geom_feats = x[kept], geom_feats[kept]  # rank值相等的点只留下最后一个,即一个batch中的一个格子里只留最后一个点 x: 29072  geom_feats: 29072 x 4
        x = torch.cat((x[:1], x[1:] - x[:-1]))  # x错位相减,还原前缀和之前的x,此时点的feature是rank相同点之和,相当于把同一个格子的点特征进行了sum

        # save kept for backward
        ctx.save_for_backward(kept)
        # no gradient for geom_feats
        ctx.mark_non_differentiable(geom_feats)

        return x, geom_feats

    @staticmethod
    def backward(ctx, gradx, gradgeom):
        kept, = ctx.saved_tensors
        back = torch.cumsum(kept, 0)
        back[kept] -= 1
        val = gradx[back]
        return val, None, None

以上为LSS的主要创新点。

三、BEV空间下特征再编码

如下为BEV特征编码部分代码,利用卷积堆了些计算量,并做了多尺度融合,因为在此之前所有特征的计算是相机之间独立的,现在所有特征都统一到BEV视角,做特征融合以便于更好的利用相机之间的语义特征信息。

class BevEncode(nn.Module):
    def __init__(self, inC, outC):  # inC: 64  outC: 1
        super(BevEncode, self).__init__()
        # 使用resnet的前3个stage作为backbone
        trunk = resnet18(pretrained=False, zero_init_residual=True)
        self.conv1 = nn.Conv2d(inC, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = trunk.bn1
        self.relu = trunk.relu

        self.layer1 = trunk.layer1
        self.layer2 = trunk.layer2
        self.layer3 = trunk.layer3

        self.up1 = Up(64+256, 256, scale_factor=4)
        self.up2 = nn.Sequential(  # 2倍上采样->3x3卷积->1x1卷积
            nn.Upsample(scale_factor=2, mode='bilinear',
                              align_corners=True),
            nn.Conv2d(256, 128, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.Conv2d(128, outC, kernel_size=1, padding=0),
        )

    def forward(self, x):  # x: 4 x 64 x 200 x 200
        x = self.conv1(x)  # x: 4 x 64 x 100 x 100
        x = self.bn1(x)
        x = self.relu(x)
        x1 = self.layer1(x)  # x1: 4 x 64 x 100 x 100
        x = self.layer2(x1)  # x: 4 x 128 x 50 x 50
        x = self.layer3(x)  # x: 4 x 256 x 25 x 25
        x = self.up1(x, x1)  # 给x进行4倍上采样然后和x1 concat 在一起  x: 4 x 256 x 100 x 100
        x = self.up2(x)  # 2倍上采样->3x3卷积->1x1卷积  x: 4 x 1 x 200 x 200

        return x

四、总结:

优点:

1、LSS的方法提供了一个很好的融合到BEV视角下的方法。基于此方法,无论是动态目标检测,还是静态的道路结构认知,甚至是红绿灯检测,前车转向灯检测等等信息,都可以使用此方法提取到BEV特征下进行输出,极大地提高了自动驾驶感知框架的集成度。

2、虽然LSS提出的初衷是为了融合多视角相机的特征,为“纯视觉”模型而服务。但是在实际应用中,此套方法完全兼容其他传感器的特征融合。如果你想融合超声波雷达特征也不是不可以试试。

缺点:

1、极度依赖Depth信息的准确性,且必须显示地提供Depth 特征。当然,这是大部分纯视觉方法的硬伤。如果直接使用此方法通过梯度反传促进Depth网络的优化,如果Depth 网络设计的比较复杂,往往由于反传链过长使得Depth的优化方向比较模糊,难以取得较好效果。当然,一个好的解决方法是先预训练好一个较好的Depth权重,使得LSS过程中具有较为理想的Depth输出。

2、外积操作过于耗时。虽然对于机器学习来说,这样的计算量不足为道,但是对于要部署到车上的模型,当图片的feature size 较大, 且想要预测的Depth距离和精细度高时,外积这一操作带来的计算量则会大大增加。这十分不利于模型的轻量化部署,而这一点上,Transformer的方法反而还稍好一些。

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

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

相关文章

Linux驱动.之字符设备驱动框架,及手动,自动注册创建/dev下设备节点详解(一)

一、Linux 字符设备驱动框架 Linux一切皆文件&#xff0c;通过VFS虚拟文件系统&#xff0c;把所有外设的细节都隐藏起来&#xff0c;最后都以文件的形态呈现于应用层&#xff0c; 操作设备&#xff0c;就像对文件的打开、关闭、读写、控制。这种方式完美的统一了对用户的接口&a…

WIN/MAC 图像处理软件Adobe Photoshop PS2024软件下载安装

目录 一、软件概述 1.1 基本信息 1.2 主要功能 二、系统要求 2.1 Windows 系统要求 2.2 macOS 系统要求 三、下载 四、使用教程 4.1 基本界面介绍 4.2 常用工具使用 4.3 进阶操作 一、软件概述 1.1 基本信息 Adobe Photoshop&#xff08;简称PS&#xff09;是一款…

快手怎么免费的去掉视频水印?分享这三个工具给你

​ 我们经常会遇到想要保存的视频带有水印&#xff0c;这不仅影响美观&#xff0c;也不利于分享。为了解决这个问题&#xff0c;我将分享三个免费去除视频水印的工具&#xff0c;帮助你轻松去除水印&#xff0c;享受无干扰的视频体验。 工具一&#xff1a;奈斯水印助手(小程序…

挑战Infiniband, 爆改Ethernet(3)

Infiniband的特点 Infiniband在HPC集群和AI集群中获得广泛应用是和Infiniband技术的特点密切相关的&#xff0c;今天我们看看Infiniband的技术特点: 1&#xff09;网络分层模型 这个分层模型并不是Infiniband技术独有的&#xff0c;各种现代网络技术都普遍采用分层模型来设计…

在.NET开发中使用 Excel 的最佳方式之一:MiniExcel

前言 在桌面开发应用中&#xff0c;处理 Excel 文件是一个非常常见的需求。无论是生成报表、导入数据&#xff0c;还是与客户或其他系统进行数据交换&#xff0c;Excel 文件都扮演着重要角色。在 .NET 生态系统中&#xff0c;有许多处理 Excel 文件的工具和库&#xff0c;其中…

Flutter ListView 实现不同样式 item

我们在实际开发中会创建显示不同类型内容的列表。以下是使用 Flutter 创建此类结构的方法&#xff1a; 1. 创建包含不同类型项目的数据源。 2. 将数据源转换为小部件列表。 创建包含不同类型项目的数据源 项目类型 要表示列表中不同类型的项目&#xff0c;请为每种类型的项目…

链表(静态/动态创建,遍历,计数,查找,在节点的前/后方插入新节点,头插法,尾插法)

目录 一、前言 二、链表的静态创建和遍历 三、链表统计节点&#xff0c;查找节点是否存在 四、从指定节点的后方插入新节点 五、从指定节点的前方插入新节点 六、动态创建链表&尾插法 七、头插法 八、删除节点 一、前言 链表本质是一个结构体&#xff0c;结构体里…

19448 算法设计与分析(第五版)习题2-7 集合划分问题

### 思路 1. **递归公式**&#xff1a;根据提示信息&#xff0c;递归公式为&#xff1a; - \( f(n, x) f(n-1, x-1) f(n-1, x) \times x \) - 其中&#xff0c;\( f(n, x) \) 表示将 \( n \) 个元素分成 \( x \) 个非空子集的方案数。 2. **边界条件**&#xff1a; …

【STM32】串口(异步通信部分)

经典的串口接口硬件说实话在现在的电脑上已经很难见到了&#xff0c;而是被USB这种通用的串行接口替代了&#xff0c;哪怕外部设备要以串口连接到电脑&#xff0c;都需要进行各种硬件转换。但不得不说&#xff0c;在工业领域&#xff0c;串口还是一个非常常用的数据传输方式。 …

LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用

采用部门分析法建立的LEAP&#xff08;Long Range Energy Alternatives Planning System/ Low emission analysis platform&#xff0c;长期能源可替代规划模型&#xff09;是一种自下而上的能源-环境核算工具&#xff0c;由斯德哥尔摩环境研究所和美国波士顿大学联合研发。该模…

后端Web之登录校验(上篇)

目录 1.概述 2.会话技术 3.JWT令牌 1.概述 基础的登录功能实际上就是查询数据库中有没有输入的用户和密码&#xff0c;有就放行&#xff0c;没有就返回错误信息&#xff0c;根据三层架构进行开发&#xff1a; controller层&#xff1a; service层&#xff1a; mapper层&…

Visual C++ 的免费绘图库 EasyX下载安装

EasyX Graphics Library 是针对 Visual C++ 的免费绘图库,支持 VC6.0 ~ VC2022,简单易用,学习成本极低,应用领域广泛。目前已有许多大学将 EasyX 应用在教学当中。 下载地址:EasyX Graphics Library for C++ 1、应用 2、文中有很多的C++应用案例 3、编程需要的数学知识 …

19530 2的幂次方表示

### 思路 1. **分解为2的幂次方**&#xff1a;将输入的正整数n分解为若干个2的幂次方之和。 2. **递归表示**&#xff1a;使用递归的方法将每个幂次方表示为2的幂次方形式。 3. **组合结果**&#xff1a;将所有的幂次方表示组合成最终的结果。 ### 需要注意的点 - 需要处理幂次…

8.21 补题

六题 C 16进制世界 链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 这是一个16进制的世界&#xff0c;比如522的16进制是20A。 在5月22日那天&#xff0c;有人送给Bob一些月饼&#xff0c;每个月饼有饱食度和幸福度两个属性。 现…

计算机网络面试真题总结(二)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 在浏览器中输入 URL 地址到显示主页的过程&#xff1f; URL解析&a…

【记录】基于Windows系统安装rust环境的过程

到官网下载安装包【入门 - Rust 程序设计语言 (rust-lang.org)】 ![[Pasted image 20240703142911.png]] 选择1&#xff0c;快速安装 选择编译配置&#xff0c;1为标准 安装完成 验证是否安装完毕 rustc --versioncargo --version验证成功&#xff01;

【数据结构篇】~二叉树(堆)

【数据结构篇】~二叉树&#xff08;堆&#xff09; 二叉树1.树2.树的组成3.二叉树4.堆1.向上调整算法2.向下调整算法3.堆排序 4.topk问题源码 二叉树 1.树 树的概念与结构​ 树是一种非线性的数据结构&#xff0c;它是由 n&#xff08;n>0&#xff09; 个有限结点组成一个…

利用“2+1链动模式小程序AI智能名片S2B2C商城源码”优化企业参与外部社群策略

摘要&#xff1a;在当今数字化时代&#xff0c;企业参与外部社群已成为其市场扩张、品牌塑造及用户增长不可或缺的一环。然而&#xff0c;面对浩如烟海的社群类型&#xff0c;包括行业论坛、地区性论坛、特定兴趣爱好的论坛以及短视频网站等&#xff0c;如何精准选择并有效介入…

16.C基础_内存管理

内存分区 1、整体框图 内存分为代码区、全局区、栈区、堆区。代码区和全局区在代码编译完之后就已经确定&#xff0c;栈区和堆区是在程序运行时进行开辟和释放的。整体内存分区框图如下&#xff1a; 对于一个进程&#xff0c;它一共有4G的空间&#xff0c;其中0~3G为上述的4个…

Flask+LayUI开发手记(三):LayUI表格的后端数据分页展现

前几天写了数据表格table的前端分页展现&#xff0c;思路是把数据一次性取到前端&#xff0c;然后由前端来控制分页展现。这种做法主要目的是为了降低后端数据库读写的次数减轻服务端运行压力。但是&#xff0c;如果功能不单是查询还要进行增删改操作&#xff0c;那么一次数据提…