VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection

简介

本项目基于PaddlePaddle框架复现了基于体素的3D目标检测算法VoxelNet,在KITTI据集上进行了实验。
项目提供预训练模型和AiStudio在线体验NoteBook。

背景

3D检测广泛用于自主导航、家政机器人以及AR/VR。LIDAR提供可靠的深度信息用于准确定位目标并表征其形状。

现有方法

  • 将LIDAR投影到某个视角作为输入;
  • 采用3D体素,提取人工设计的体素特征;
  • 虽然有PointNet和Pointnet++这类点云学习网络,但还无法处理大规模点云数据。

这篇文章利用网络学习体素点特征,只使用点云实现了快速高效的3D目标检测。

算法解释

VoxelNet由三个功能块组成:特征学习网络、卷积中间层和区域候选网络。下面一一介绍:

  • 特征学习网络

首先将三维点云划分为一定数量的Voxel(就是将空间划分为一个一个栅格,用格子表示格子里的点云)并对这些voxel进行分组,再经过点的随机采样(每个格子的最大点云采样数量这里是T=35)以及归一化后,对每一个非空Voxel使用若干个VFE(Voxel Feature Encoding)层进行局部特征提取,得到Voxel-wise Feature。这里的VFE模型其实就是FC全连接模型。最后的输出形状为128×10×400×352.

  • 卷积中间层

为了聚合周围环境voxels的信息,使用3D卷积对4D tensor进行卷积,并进行reshape到3D tensor。每个卷积中间层顺序应用3D卷积、BN层和ReLU层。举例:输入尺寸(4D tensor)是128 × 10 × 400 × 352,输出尺寸(经过Convolutional Middle Layers之后)是64 × 2 × 400 × 352,然后reshape到 128 × 400 × 352变成3D tensor(注意到128 × 400 × 352正是BEV视图上的栅格尺寸)。

  • 区域候选网络

在提取到特征后,利用RPN模块预测候选检测框。如图所示,该网络包含三个全卷积层块(Block),每个块的第一层通过步长为2的卷积将特征图采样为一半,之后是三个步长为1的卷积层,每个卷积层都包含BN层和ReLU操作。将每一个块的输出都上采样到一个固定的尺寸并串联构造高分辨率的特征图。最后,该特征图通过两种二维卷积被输出到期望的学习目标:概率评分图(Probability Score Map)和回归图(Regression Map)

  • 损失函数

Probability Score Map的输出通道是2,分别对应positive和negative的分数,Regression map输出通道为14维,对于每个回归的Bounding box都用7维来表示,也就是中心位置 、候选框的长宽高和航向角,另外两个旋转轴默认为0,原因是地面水平。同理,假设预测的anchor用小标a表示,因此可定义如下的残差:

其中,是anchor框底部的对角线长度,采用的目的是用对角线齐次归一化和。然后定义可以最终的损失函数:

损失函数前面两项是正则化分类损失,其中和分别表示softmax层对正锚和负锚的分数,采用的是交叉熵表示,和为正定平衡系数。最后一项是回归损失,和是正锚的回归输出和ground truth,采用的是Smooth L1损失。

详细的参数设定需要查看论文才能更好理解~

论文:

  • [1] Yin Zhou, Oncel Tuzel.
    Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 2018. VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection

博客参考:

  • VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection
  • 【3D目标检测】VoxelNet:End-to-End Learning for Point Cloud Based 3D Object Detection解读

项目参考:

  • https://github.com/qianguih/voxelnet

    github repo实现精度 easy: 53.43 moderate:48.78 hard:48.06

  • https://github.com/traveller59/second.pytorch

由于该论文并未提供开源的代码,目前也找不到能够复现其论文中指标的项目。
因此本项目根据参考项目(voxelnet-tensorflow)和该论文后续的算法改进版本(second)进行了复现。

复现精度

指标解释:>机器学习算法评估指标——3D目标检测

评价3D目标检测结果的指标主要是3D AP 和Bev AP。其含义是当预测框与真值框的交并比(IOU)大于一定阈值时,认为预测框正确的数量与所有真值框的比例。

IOU即两个框的相交范围与并集范围的比例。

在KITTI val数据集(50/50 split as paper)的测试效果如下表。

NetWork epochs opt lr batch_size dataset config
VoxelNet 160 SGD 0.0015 2 * 1(V100 card) KITTI config
Car AP@0.70, 0.70, 0.70:
bbox AP:90.26, 86.24, 79.26
bev  AP:89.92, 86.04, 79.14
3d   AP:77.00, 66.40, 63.24
aos  AP:38.34, 37.30, 33.19
Car AP@0.70, 0.50, 0.50:
bbox AP:90.26, 86.24, 79.26
bev  AP:90.80, 89.84, 88.88
3d   AP:90.75, 89.32, 87.84
aos  AP:38.34, 37.30, 33.19Car coco AP@0.50:0.05:0.95:
bbox AP:67.72, 63.70, 61.10
bev  AP:67.13, 63.44, 61.15
3d   AP:53.45, 48.92, 46.34
aos  AP:28.82, 27.54, 25.55

预训练权重和日志:百度网盘 | AiStudio存储

2、当将分类损失改为FocalLoss以及加入针对aos的direction分类损失时(后续实验表明direction损失只对aos起作用,可不用)

NetWork epochs opt lr batch_size dataset config
VoxelNet 160 SGD 0.005 2 * 4 (V100 card) KITTI configFix
Car AP@0.70, 0.70, 0.70:
bbox AP:90.19, 85.78, 79.38
bev  AP:89.79, 85.26, 78.93
3d   AP:81.78, 66.88, 63.51
aos  AP:89.81, 84.55, 77.71
Car AP@0.70, 0.50, 0.50:
bbox AP:90.19, 85.78, 79.38
bev  AP:96.51, 89.53, 88.59
3d   AP:90.65, 89.08, 87.52
aos  AP:89.81, 84.55, 77.71Car coco AP@0.50:0.05:0.95:
bbox AP:67.15, 63.05, 60.58
bev  AP:68.90, 63.78, 61.08
3d   AP:54.88, 49.42, 46.82
aos  AP:66.89, 62.19, 59.23

预训练权重和训练日志:百度网盘 | AiStudio存储

  • 另外,论文中没提及的细节,本项目均参考Second项目的实施

  • 仓库内的log文件夹下存放有两个训练日志和可视化曲线日志。

开始

数据集解压

约15分钟.

%cd /home/aistudio/
!rm -rf kitti/
!mkdir -p kitti/training/velodyne_reduced
!mkdir -p kitti/testing/velodyne_reduced
!unzip data/data50186/data_object_calib.zip -d kitti/
!unzip data/data50186/image_training.zip -d kitti/training/
!unzip data/data50186/data_object_label_2.zip -d kitti/training/
!unzip data/data50186/velodyne_training_1.zip -d kitti/training/
!unzip data/data50186/velodyne_training_2.zip -d kitti//training/
!unzip data/data50186/velodyne_training_3.zip -d kitti/training/
!unzip data/data50186/image_testing.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_1.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_2.zip -d kitti/testing/
!unzip data/data50186/velodyne_testing_3.zip -d kitti/testing/
!mv kitti/training/training/* kitti/training/
!rm -rf kitti/training/training/
!mv kitti/testing/testing/* kitti/testing/
!rm -rf kitti/testing/testing/
!mkdir kitti/training/velodyne
!mv kitti/training/velodyne_training_1/* kitti/training/velodyne/
!mv kitti/training/velodyne_training_2/* kitti/training/velodyne/
!mv kitti/training/velodyne_training_3/* kitti/training/velodyne/
!rm -rf kitti/training/velodyne_training_1
!rm -rf kitti/training/velodyne_training_2
!rm -rf kitti/training/velodyne_training_3
!mkdir kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_1/* kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_2/* kitti/testing/velodyne
!mv kitti/testing/velodyne_testing_3/* kitti/testing/velodyne
!rm -rf kitti/testing/velodyne_testing_1
!rm -rf kitti/testing/velodyne_testing_2
!rm -rf kitti/testing/velodyne_testing_3
!mv kitti data/

至此,数据集的结构:

└── KITTI_DATASET_ROOT├── training    <-- 7481 train data|   ├── image_2 <-- for visualization|   ├── calib|   ├── label_2|   ├── velodyne|   └── velodyne_reduced <-- empty directory└── testing     <-- 7580 test data├── image_2 <-- for visualization├── calib├── velodyne└── velodyne_reduced <-- empty directory

3,712 data samples fortraining and 3,769 data samples for validation

安装依赖

最适合的环境配置:

  • python版本:3.7.4
  • PaddlePaddle框架版本:2.2.1
  • CUDA 版本: NVIDIA-SMI 450.51.06 Driver Version: 450.51.06 CUDA Version: 11.0 cuDNN:7.6

注意:
由于PaddlePaddle/cuDNN本身的BUG,CUDA 10.1版本当batch size > 2时会报如下错误

OSError: (External) CUDNN error(7), CUDNN_STATUS_MAPPING_ERROR. [Hint: 'CUDNN_STATUS_MAPPING_ERROR'.  An access to GPU memory space failed, which is usually caused by a failure to bind a texture.  To correct, prior to the function call, unbind any previously bound textures.  Otherwise, this may indicate an internal error/bug in the library.  ] (at /paddle/paddle/fluid/operators/conv_cudnn_op.cu:758)

因此单卡如果环境不是CUDA 11.0以上,config文件中batch size设置为2即可,后续通过训练的accum_step参数开启梯度累加起到增大bs的效果。设置accum_step=8即表示bs=16,并做相应config文件的初始学习率调整。

!pip install distro shapely pybind11 pillow fire memory_profiler psutil scikit-image==0.14.2
!pip install numpy==1.17.0
!pip install numba==0.48.0

(由于Notebook不支持导入当前整个项目到Python环境,以下操作在终端命令行执行)

准备部分

1. 为numba设置cuda环境

export NUMBAPRO_CUDA_DRIVER=/usr/lib/x86_64-linux-gnu/libcuda.so
export NUMBAPRO_NVVM=/usr/local/cuda/nvvm/lib64/libnvvm.so
export NUMBAPRO_LIBDEVICE=/usr/local/cuda/nvvm/libdevice

2. 将当前项目加到环境中

export PYTHONPATH=$PYTHONPATH:/home/aistudio/VoxelNet

3. 数据预处理

从label中分类别抽取真值信息以及对点云进行降采样。(约7分钟)

cd /home/aistudio/VoxelNet/voxelnet/
python create_data.py create_kitti_info_file --data_path=/home/aistudio/data/kitti  #  Create kitti infos
python create_data.py create_reduced_point_cloud --data_path=/home/aistudio/data/kitti # Create kitti reduced point
python create_data.py create_groundtruth_database --data_path=/home/aistudio/data/kitti # Create kitti gt

打印信息如下:

Generate info. this may take several minutes.
Kitti info train file is saved to /home/aistudio/data/kitti/kitti_infos_train.pkl
Kitti info val file is saved to /home/aistudio/data/kitti/kitti_infos_val.pkl
Kitti info trainval file is saved to /home/aistudio/data/kitti/kitti_infos_trainval.pkl
Kitti info test file is saved to /home/aistudio/data/kitti/kitti_infos_test.pkl
[100.0%][===================>][40.86it/s][01:44>00:00]
[100.0%][===================>][35.31it/s][01:47>00:00]
[100.0%][===================>][39.13it/s][03:49>00:00]
[100.0%][===================>][28.71it/s][01:53>00:00]
load 14357 Car database infos
load 2207 Pedestrian database infos
load 734 Cyclist database infos
load 1297 Van database infos
load 56 Person_sitting database infos
load 488 Truck database infos
load 224 Tram database infos
load 337 Misc database infos

4. 修改配置文件

voxelnet/configs/car.configs

train_input_reader: {...database_sampler {database_info_path: "/home/aistudio/data/kitti/kitti_dbinfos_train.pkl"...}kitti_info_path: "/home/aistudio/data/kitti/kitti_infos_train.pkl"kitti_root_path: "/home/aistudio/data/kitti"
}
...
eval_input_reader: {...kitti_info_path: "/home/aistudio/data/kitti/kitti_infos_val.pkl"kitti_root_path: "/home/aistudio/data/kitti"
}

设置注意事项:

1、若训练要开启梯度累加选项,则:

  • 学习率的decay_steps按照梯度累加后的batch size对应的总steps来设置。
  • train_config.steps则按未梯度累加时对应的初始batch size对应的总steps来设置

2、 配置文件需放置于voxelnet/configs/***.py

快速开始

1. 训练

训练一个epoch, V100 16G大约15分钟。显存占用11G左右。

python ./pypaddle/train.py train --config_path=./configs/config.py --model_dir=./output

2. 评估

V100 16G 大约5分钟

python ./pypaddle/train.py evaluate --config_path=./configs/config.py --model_dir=./output --ckpt_path=./output/voxelnet-278400.ckpt

3. 可视化预测

3D可视化需要GUI,Notebook环境不支持动态GUI调用显示。需在本地测试。
详细查看README.md。

4. 一个简单的BEV视角可视化例子

为了方便查看预测结果,下面的cell提供了一个在notebook中查看二维bev视角的可视化例子,可以在notebook执行。
由于只保留了相机视角范围内的结果(Points that are projectedoutside of image boundaries are removed(in Paper Section 3.1)),所以车身后面没有检测框。

%cd /home/aistudio/VoxelNet/
!export PYTHONPATH=$PYTHONPATH:/home/aistudio/VoxelNetimport paddle
import numpy as np
import matplotlib.pyplot as plt
import pickle
from pathlib import Path
import voxelnet.pypaddle.builder.voxelnet_builder as voxelnet_builder
import voxelnet.builder.voxel_builder as voxel_builder
import voxelnet.builder.target_assigner_builder as target_assigner_builder
import voxelnet.pypaddle.builder.box_coder_builder as box_coder_builder
from voxelnet.data.preprocess import merge_voxelnet_batch
from voxelnet.configs import cfg_from_config_py_file
from voxelnet.utils import visdef example_convert_to_paddle(example, dtype=paddle.float32,) -> dict:example_paddle = {}float_names = ["voxels", "anchors", "reg_targets", "reg_weights", "bev_map", "rect","Trv2c", "P2"]for k, v in example.items():if k in float_names:example_paddle[k] = paddle.to_tensor(v, dtype=dtype)elif k in ["coordinates", "labels", "num_points"]:example_paddle[k] = paddle.to_tensor(v, dtype=paddle.int32)elif k in ["anchors_mask"]:example_paddle[k] = paddle.to_tensor(v, dtype=paddle.uint8)else:example_paddle[k] = vreturn example_paddlepaddle.set_device('gpu') # 设置cpu/gpuconfig_path = "home/aistudio/VoxelNet/voxelnet/configs/config.py"
config = cfg_from_config_py_file(config_path)
input_cfg = config.eval_input_reader
model_cfg = config.model.voxelnetckpt_path = "/home/aistudio/VoxelNet/voxelnet/output/voxelnet-278400.ckpt"
model_cfg.voxel_generator.point_cloud_range = [0, -40, -3, 70.4, 40, 1]
voxel_generator = voxel_builder.build(model_cfg.voxel_generator)
######################
# BUILD TARGET ASSIGNER
######################
bv_range = voxel_generator.point_cloud_range[[0, 1, 3, 4]]
box_coder = box_coder_builder.build(model_cfg.box_coder)
target_assigner_cfg = model_cfg.target_assigner
target_assigner = target_assigner_builder.build(target_assigner_cfg,bv_range, box_coder)
net = voxelnet_builder.build(model_cfg, voxel_generator, target_assigner)
net.eval()state = paddle.load(ckpt_path)
net.set_state_dict(state)out_size_factor = model_cfg.rpn.layer_strides[0] // model_cfg.rpn.upsample_strides[0]
grid_size = voxel_generator.grid_size
feature_map_size = grid_size[:2] // out_size_factor
feature_map_size = [*feature_map_size, 1][::-1]anchors = target_assigner.generate_anchors(feature_map_size)["anchors"]
anchors = anchors.reshape((1, -1, 7))info_path = input_cfg.kitti_info_path
root_path = Path(input_cfg.kitti_root_path)
with open(info_path, 'rb') as f:infos = pickle.load(f)info = infos[564] # 测试目标点云
# print(info)
v_path = info['velodyne_path']
v_path = str(root_path / v_path)
points = np.fromfile(v_path, dtype=np.float32, count=-1).reshape([-1, 4])
voxels, coords, num_points = voxel_generator.generate(points, max_voxels=40000)
print(voxels.shape)
# add batch idx to coords
# coords = np.pad(coords, ((0, 0), (1, 0)), mode='constant', constant_values=0)image_idx = info['image_idx']
rect = info['calib/R0_rect'].astype(np.float32)
Trv2c = info['calib/Tr_velo_to_cam'].astype(np.float32)
P2 = info['calib/P2'].astype(np.float32)example = {"anchors": anchors,"voxels": voxels,"num_points": num_points,"num_voxels": np.array([voxels.shape[0]], dtype=np.int64),"coordinates": coords,"rect": rect,"P2": P2,"Trv2c":Trv2c,'image_idx': image_idx}
batch_example = [example]
examples = merge_voxelnet_batch(batch_example)
examples = example_convert_to_paddle(examples)
with paddle.no_grad():pred = net(examples)[0]boxes_lidar = pred["box3d_lidar"].detach().cpu().numpy()
vis_voxel_size = [0.1, 0.1, 0.1]
vis_point_range = [-50, -30, -3, 50, 30, 1]
bev_map = vis.point_to_vis_bev(points, vis_voxel_size, vis_point_range)
bev_map = vis.draw_box_in_bev(bev_map, vis_point_range, boxes_lidar, [0, 255, 0], 2)# plt.savefig('/home/aistudio/val564.png')plt.imshow(bev_map)
paddle.device.cuda.empty_cache()
/home/aistudio/VoxelNet
cfg_file must be located in ./configs/***.py...
load config from home/aistudio/VoxelNet/voxelnet/configs/config.py...
(13282, 35, 4)

结语

复现心得:

这篇论文在第四届论文复现赛的时候我就进行了复现,并未成功,相差甚远。由于论文没有开源代码,网络上也没有达到论文精度的项目,很难从头开始自己全部重写。于是我换了一个思路,既然这篇论文是非常经典的论文,后续肯定有人基于这个思路进行改进。果然找到了second。second这篇论文几乎重现了voxelnet的所有方法(但据作者说并没有完全复现原始的voxelnet),不过加入了稀疏卷积使得速度和精度得到了巨大提升。于是我的思路就变成了从second中去掉它改进的部分内容,使其复原原始的voxelnet,来减少自己重写代码的工作量。

由于之前第四届比赛时的经验,这次遇到的问题都不多,或者说当时已经遇到了,直接拿我当时的代码进行替换,一个函数一个函数输入输出对齐测试即可。并且只需要对照X2Paddle进行对齐即可。

第四届和这一次遇到的主要问题都是内存泄漏问题。不过这一次,找到了问题所在。

1.这里总结一下几个常犯的内存泄漏错误原因。

  • loss在用于取值打印或者用于其他计算时,没有.detach()或者.numpy()
  • 如果model本身内部要存一些每一次前向计算后的指标,指标一定也要在前向计算后detach再赋值到model内的变量(这是我找了超级久的泄漏问题)。

2.enisum函数。这个函数虽然paddle2.2提供了,但是还有bug。用的时候报错了。

我在paddlenlp(https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/ops/einsum.py)
中找到了可以正常使用的版本。

3.mask赋值。问题如下:
写了一个mask(bool类型)的赋值函数:

def mask_slice_v1(data, mask):"""问题:data.shape = [x,y], type: float...mask.shape = [x] ,type: boolin torch, can do it by data[mask] to get shape:[x,y] result.但是padlde目前还不行。"""data_shape = data.shapemask = mask.unsqueeze(-1) # [x,1]mask = paddle.tile(mask,[1,data_shape[1]]) # [x,y]slice = paddle.masked_select(data,mask) # [x*y]return slice.reshape([-1,data_shape[1]]) # [x,y]

相关信息:

信息 描述
作者 xbchen
日期 2021年1月
框架版本 PaddlePaddle>=2.2.1
应用场景 3D目标检测
硬件支持 GPU
在线体验 Notebook
多卡脚本 Shell

引用

  • Thanks for yan yan’s second.pytorch project.
@inproceedings{Yin2018voxelnet,author={Yin Zhou, Oncel Tuzel},title={VoxelNet: End-to-End Learning for Point Cloud Based 3D Object Detection},booktitle = {Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},year = {2018}
}

基于体素的3D目标检测网络:VoxelNet相关推荐

  1. 两阶段3D目标检测网络 SIENet: Spatial Information Enhancement Network for 3D Object Detection from Point Cloud

    本文介绍一篇两阶段的3D目标检测网络:SIENet. 这里重点是理解本文提出的 Hybrid-Paradigm Region Proposal Network 和 Spatial Informatio ...

  2. 详解两阶段3D目标检测网络 Voxel R-CNN:Towards High Performance Voxel-based 3D Object Detection

    本文介绍一篇两阶段的3D目标检测网络:Voxel R-CNN,论文已收录于AAAI 2021. 这里重点是理解本文提出的 Voxel RoI pooling. 论文链接为:https://arxiv. ...

  3. CaDDN:基于单目的3D目标检测新方法(CVPR2021)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨元气满满的打工人 来源丨CV研习社 文章导读 导读:在自动驾驶的技术中,3D目标检测能够提更加丰 ...

  4. CVPR2022 3D目标检测(GLENet )增强型3D目标检测网络

    图 1:(a) 给定一个不完整 LiDAR 观测的对象,可能存在多个具有不同大小和形状的潜在合理的真实边界框. (b) 当注释来自 2D 图像和部分点时,标签过程中的模糊和不准确是不可避免的.在给定的 ...

  5. 详解两阶段3D目标检测网络PVRCNN:Point-Voxel Feature Set Abstraction for 3D Object Detection

    在<动手学无人驾驶(4):基于激光雷达点云数据3D目标检测>一文中介绍了3D目标检测网络PointRCNN.今天介绍该作者新提出的3D检测模型:PVRCNN,论文已收录于CVPR2020. ...

  6. 计算机视觉算法——基于Anchor Free的目标检测网络总结

    计算机视觉算法--基于Anchor Free的目标检测网络总结 计算机视觉算法--基于Anchor Free的目标检测网络总结 1. CornerNet 1.1 关键知识点--网络结构及特点 1.2 ...

  7. 3D目标检测(二)—— 直接处理点云的3D目标检测网络VoteNet、H3DNet

    前言 上次介绍了基于Point-Based方法处理点云的模块,3D目标检测(一)-- 基于Point-Based方法的PointNet点云处理系列,其中相关的模块则是构成本次要介绍的,直接在点云的基础 ...

  8. 【lidar】基于YOLO的3D目标检测(激光雷达点云)课程设计

    基于YOLO的3D目标检测(激光雷达点云)课程设计 代码+数据集下载地址:下载地址

  9. 让M3D-RPN的3D目标检测网络初步跑起来!

    让M3D-RPN的3D目标检测网络初步跑起来! @写在前面碎碎念@ 最近被安排研究一下M3D-RPN的目标检测网络,说是<研究>感觉贬低了<研究>这个词语,说成碰运气可能会更好 ...

最新文章

  1. 为virtualbox配置网络环境
  2. 用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
  3. Linux命令netstat解读
  4. 博士生想逃离科研,导师还要帮他吗?|Nature专访
  5. AAAI 2021中的目标检测(详细版with code)
  6. java加载jdbc驱动,加载JDBC驱动
  7. android view 屏幕外,安卓如何让View往屏幕外隐藏?
  8. Enfocus PitStop Pro 2021 for Mac(pdf增强插件)
  9. 降低站长成本 推荐8个免费或低廉小型建站工具
  10. java jre32下载_JRE7 32位官方下载
  11. 博士申请 | 香港大学黄凯斌教授招收6G通信与机器学习方向全奖博士生
  12. Mybatis狂神说完整笔记
  13. 从工厂运作的实质看生产
  14. c++ 圆上任意点坐标计算_已知圆上任意三点坐标如何编程来计算这个圆的圆心和半径...
  15. MVC4 AspNet MVC下的Ajax / 使用JQuery做相关的Ajax请求
  16. 转载:香港实习生微软实习经验分享
  17. 什么是IEEE、EI、SCI?
  18. STM32MP157驱动开发——Linux RS232/485/GPS 驱动
  19. 你并不理解的 电快速瞬变脉冲群 实验
  20. C语言学习笔记(0)

热门文章

  1. html5简介的文本框,html5的文本框样式有哪些
  2. 【微信小程序】云开发(Cloud Base)环境配置以及编写第一个云函数
  3. 西方祝酒贺词辞大搜罗
  4. JavaSE知识总结
  5. Solder Mask和Paste Mask
  6. ShowMeBug 助力元戎启行校园招聘
  7. 2020计算机专业录取分数排名,2021全国计算机专业大学排名一览表
  8. 三星发布新款智能硬件Family Hub
  9. 日式mac虚拟机安装win7,中文输入法适配日文键盘!
  10. eclipse使用lombok注解不起作用