一起自学SLAM算法:10.3 机器学习与SLAM

news/2024/11/1 21:32:31/

连载文章,长期更新,欢迎关注:

前面已经分析过的8种SLAM算法案例(Gmapping、Cartographer、LOAM、ORB-SLAM2、LSD-SLAM、SVO、RTABMAP和VINS)都可以称为传统方法,因为这些算法都是在人为精心设计的特定规则下工作,一个算法取得的好效果往往归因于大量小技巧的运用,这与人们心目中的智能相去甚远。比如三段式(前端里程计、后端优化和闭环检测)已经成了SLAM算法的普遍范式,这种特定规则是经过长期发展总结出来的宝贵经验,三段式设计能保证算法运行的实时性,而闭环检测成了解决累积误差的首选方法。再比如从图像中提取特征点(SIFT、SURF、ORB等)然后利用多视图几何和三角化重建求解运动位姿及地图路标,这个思路在工程应用中被证明很有效。

然而机器人所处在一个复杂多变的环境之中,传统方法中设计的各种精妙的规则都仅仅适用于某些特定的环境,如果按照传统方法的思路需要设计一个无限复杂的规则才能适用于所有可能的环境,这显然是不现实的。这就需要一种更通用的方式,让机器人在感知环境的交互过程中,通过自身学习不断提高适应环境的能力,这种新颖的方法可以称为机器学习方法。一种思路是利用机器学习方法对SLAM传统方法中的局部模块进行改进,比如用机器学习的方法来替代传统图像特征点的提取过程、用机器学习的方法来替代传统深度信息重建、用机器学习方法替代传统闭环检测与重定位、将机器学习提取出的环境语义信息添加到地图之中等;另一种思路是完全抛弃SLAM传统方法的东西,感知数据直接通入机器学习算法后直接输出结果,即端到端(End-to-End)方法。下面即将介绍的CNN-SLAM算法就属于第一种思路的典型案例,而DeepVO算法则属于第二种思路的典型案例。近年来机器学习与SLAM的结合研究取得了很多优秀成果,CNN-SLAM和DeepVO仅仅是其中的成果之一,由于篇幅限制不再逐一介绍,感兴趣的读者可以自行研究。考虑到有些读者对机器学习领域还不够熟悉,下面先讨论机器学习方面的问题,接着再具体分析CNN-SLAM和DeepVO两种基于机器学习方法的SLAM典型案例。

10.3.1 机器学习

现今每当提起人脸识别、语音助手、无人驾驶、智能推荐等概念,大家很快会联想到一个名词“人工智能”;然而在学术界常常出现的却是机器学习、神经网络、深度学习之类的概念,下面就进行一些具体讨论和分析。

1.发展历程

要搞清楚人工智能领域的各种概念为什么常常被混为一谈,这就需要先了解一下其发展历程[12]p10~13,如图10-33所示。从宏观层面来看,人工智能走过了推理期、知识期和学习期。在最开始的推理期发展阶段,人们认为机器只要具有基于逻辑关系的推理能力就可以拥有智能,比如利用一些已知的数学定理经过逻辑推理来证明数学难题。缺乏丰富的知识,仅靠逻辑推理很难奏效,于是研究很快转向知识期发展阶段,也就是想办法让机器拥有更渊博的知识,大量由人工总结出来的知识经验赋予给机器的专家系统在这期间诞生。靠人工来整理知识然后传授给机器太费劲了,要是机器能通过自己学习来获取知识该多好,这就迎来了学习期发展阶段。从方法论层面来看,人工智能分为连接主义和符号主义两个流派。连接主义基于神经网络的连接结构来表示知识,也就是说学习到的知识被隐藏在神经网络的连接结构当中,整个系统相当于一个黑盒子。而符号主义将抽象出来的概念用具体符号表示,学习知识的过程更加透明。当然还有很多无法明确分门别类的研究方法,比如基于决策理论提出的强化学习,再比如以统计理论为基础发展起来的非常著名的支持向量机。由于在人工智能的发展过程中,机器学习这个提法广泛出现于人们的视野,于是慢慢就发展成一个专门的研究领域。所谓机器学习,就是让机器通过学习获取知识,并利用知识解决特定的问题。这里的机器指计算机,而学习指运行在计算机上的程序算法。有人将机器学习具体划分成“在问题求解和规划中学习”、“通过观测和发现学习”、“从指令中学习”、“从样例中学习”等种类;也有人将机器学习划分成“机械学习”、“类比学习”、“示教学习”、“归纳学习”等种类,当然我们也可以根据自己的观点来划分所谓的学习类型,这些划分非常主观,不过从样例中学习应该是应用和研究最多的一个。所谓样例就是供训练的数据,包括有监督学习和无监督学习。回到前面的符号主义,其从样例中学习的典型案例是决策树;而对于连接主义,其从样例中学习的典型案例是神经网络的BP算法实现。如今备受追捧的深度学习,其实本质还是神经网络,只是连接层数增多了。以上所叙述的仅仅是人工智能发展过程中一些有代表性的理论、技术、方法的概念性东西,力争让大家拥有一个更开阔的视野。看完这个发展历程,大家可能会发现自己对人工智能领域的这些个学术概念的理解不但没有变清晰,反而越发模糊了。不用担心,这正是我将其介绍给大家的目的。因为关于智能,目前并没有科学的定义,也没有统一的研究方法。推理、知识、神经网络、机器学习、决策理论等诸多的概念,本身就是在不同的层面不同的时期来讨论的,这些概念之间有些是互相重叠的、有些是发展递进的、有些是特例、有些是不同角度出发的同一观点等等。

图10-33  发展历程

既然从理论上暂时无法得知智能的真正面貌,那么广大读者也就不必拘泥于理论层面的各种概念,仅从实践的角度讨论和学习主流的一些技术应用就已经够了。如图10-34所示,为人工智能技术应用的大致流程。从实际问题出发,对问题进行分析并提取出一些重要的先验信息以帮助我们构造更合适的智能系统来解决问题。一种比较普遍的观点认为,智能系统应该包含3个关键部分:表示、学习和推理[13]p22。表示指存储知识的模型,知识以显式或隐式的形式存储在模型的参数及结构之中;学习指利用数据训练模型的过程,也就是利用训练数据调整模型的参数及结构;而推理指利用学习到的知识解决问题的过程。常用的表示模型有线性模型、核模型、神经网络模型、概率图模型、决策树、组合模型等,而训练方法整体上可以分为有监督学习、无监督学习、强化学习等。选择模型时,需要考虑其表达能力、训练难度、精度、泛化能力等因素,而这些因素之间的关系十分复杂。比如增加模型的结构复杂度和参数规模可以提高表达能力,但这样会增加训练难度;再比如训练过程中当模型精度提高的同时泛化能力可能降低。关于模型和训练方法的选择,目前还没有确切的理论依据。因此常常有人把训练模型的过程比喻成“炼丹”,也就是先挑选一个自己认为熟悉的模型进行训练,并根据经验进行修修改改,最后应用到实际问题时效果好就行了。这里讨论的智能系统是运行在计算机上的程序算法,因此也需要考虑软件框架(开发平台)、算力(硬件计算性能)和数据(训练模型的样本)这些基础资源。除此之外,概率论、决策论、信息论、统计学等基础理论也将一直伴随着整个智能系统的搭建过程。下面展开讨论一下常见的几种模型及对应的训练方法,然后进一步讨论从中脱颖而出的神经网络模型。

图10-34  人工智能技术应用的大致流程

(1)线性模型

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(2)核模型

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(3)神经网络模型

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(4)概率图模型

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(5)决策树

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(6)组合模型

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(7)基于训练样本的模型学习过程

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

2.神经网络

相较于其他机器学习模型,神经网络似乎更加通用,因为它仅通过非线性复合函数方式就能构造出具有足够表达能力的基函数,并通过简单增加神经元数量的方式就能轻松扩展模型的规模。不过由于模型参数w以非线性的方式包含于复合函数之中,这给学习这些参数的过程增加了很大的难度,因此神经网络在很长一段时间里并不被人重视。得益于当今大数据时代数据的极大丰富和硬件计算能力的极大提升,以及神经网络训练方法的改进,使得神经网络几乎成了机器学习领域中最好用的模型。而基于神经网络模型的学习发展起来的深度学习,也成了当今最受关注的研究方向。下面就对常用的一些神经网络及相应的学习方法展开讨论,然后进一步讨论其在深度学习中的应用。

神经元是组成神经网络的基本单元,其结构如图10-45所示。神经元的输入是一个多维变量x=\begin{bmatrix} x_{1} & x_{2} & ... & x_{n} \end{bmatrix}^{T},输入经过w=\begin{bmatrix} w_{1} & w_{2} & ... & w_{n} \end{bmatrix}^{T}加权后送入激活函数h(\cdot )出来后得到神经元的输出y,其中权重w就是后续学习过程中待求的模型参数。

图10-45  神经元

神经元中发挥关键作用的就是其中的激活函数,当激活函数为线性函数时,由多个神经元连接组成的神经网络经过化简后等效于单个神经元,没有实质性用处。因此激活函数要为非线性函数,常见的非线性激活函数如表10-8所示。

表10-8  常见的激活函数

过去在很长一段时间内,sigmoid和tanh是神经网络中最流行的激活函数,主要是这类“S”形激活函数具有连续光滑的特性便于求导,但缺点是误差多层回传后容易梯度弥散。ReLU及其各种改进由于解决了梯度弥散问题,如今已经取代“S”形激活函数逐步成为主流的激活函数了。

将多个神经元相互连接就形成了所谓的神经网络,由于神经元之间的连接方式多种多样,因此可以形成各种结构的神经网络,比如前向网络、卷积网络、循环网络、径向基网络、自适应谐振网络、自组织映射网络、玻尔兹曼机等。这些具体的神经网络结构需要与相应的学习策略结合才能发挥作用,常见的学习策略包括基于误差的学习、玻尔兹曼学习、Hebb学习、竞争学习、基于记忆的学习、达尔文进化学习等。可以想象神经网络的结构以及相应的学习策略非常之多,由于篇幅限制,下面主要对最常用的前向网络、卷积网络和循环网络的结构及学习进行介绍。

(1)前向网络

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(2)卷积网络

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(3)循环网络

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

3.深度学习

深度学习其实是机器学习的一个细分领域,而机器学习又是人工智能的一个细分领域,也就是说深度学习技术对于整个人工智能领域来说是一个非常细分的领域。这里深度的含义是指模型的规模很大,也就是各种神经网络结构大规模堆叠出来的深度神经网络,当然广义上也包含那些非神经网络模型堆叠出来的深度模型;而学习是指这些深度模型配套的学习策略。虽然神经网络模型通过简单增加神经元数量的方式就能提高表达能力,也就是说只要模型足够深就能满足任何问题的表达要求,但也正是由于模型规模过于庞大使得训练异常困难。得益于当今大数据时代数据的极大丰富和硬件计算能力的极大提升,以及神经网络训练方法的改进,深度学习取得的成果显现出了极大的商业价值。可以说深度学习的成功与模型、数据和算力都密不可分,模型方面涌现了诸多精妙的结构(比如深度卷积网络、深度循环网络),互联网的多年发展积累了海量数据可以用于模型训练,而计算机算力的极大提升为深度学习商业化落地提供了支持。讨论深度学习技术,需要从深度模型学习策略这两个方面切入,下面就对深度学习技术中的各种深度模型及配套学习策略进行简单梳理。

在诸多神经网络结构中,大家对前向网络一定不陌生,如图10-46所示。通过增加前向网络中隐藏层的数量就能得到深度前向网络,有些地方也叫多层感知机(Multilayer perceptron,MLP)。有理论表明深度前向网络能逼近任何函数映射关系,即通用函数逼近器。为了处理图像数据提出了卷积网络,将卷积网络结构与其他功能结构(池化层、正则层、全连接层等)进行大规模堆叠就得到了深度卷积网络(CNNs)。为了处理序列数据提出了循环网络,将循环网络结构与其他功能结构进行大规模堆叠就得到了深度循环网络(RNNs)。

就现在几大热门的应用领域(计算机视觉、自然语言处理、推荐系统等)来看,MLP、CNNs、RNNs以及它们之间的有机结合已经基本够用了。但出于讨论的完备性考虑,深度学习中还有一些比较冷门的技术也简单介绍一下。

MLP、CNNs和RNNs通常都采用误差反向传播来进行训练,但是这种训练方法的效率并不一定高。深度自编码网络(Deep Auto Encoder,DAE)或者叫堆叠自编码网络(Stacked  Auto Encoder,SAE)采用自编码器学习对深度模型逐层进行训练,DAE的关键是基于自编码器学习的无监督学习方法,而DAE中的深度模型[22,23]可以是MPL、CNN或者别的深度模型。通过自编码器的无监督学习对深度模型逐层进行训练的过程也称为模型预训练(或者粗调),在此训练结果的基础还可以利用误差反向传播进行有监督学习(即精调),这种粗调加精调的学习过程也叫半监督学习。深度置信网络(Deep Belief Network,DBN)由受限玻尔兹曼机堆叠而成[24],受限玻尔兹曼机学习也是一种对深度模型逐层进行训练的无监督学习方法,同样在无监督学习的粗调后还可以进行有监督学习的精调。当然对于数据压缩和特征提取任务来说,DAE和DBN中都只需要进行无监督学习就够了;但对于分类任务就需要在无监督学的基础上再进行有监督学习。其实训练数据充足时,CNNs和RNNs的有监督学习过程已经包含了DAE和DBN中无监督特征提取的过程。

深度模型在最求强大表达能力的同时不可避免会存在冗余问题,模型的稀疏性便很自然受到关注,即深度稀疏网络。深度稀疏网络主要通过稀疏正则和稀疏连接对模型的冗余进行抑制,稀疏正则通过在损失函数中加入模型参数复杂度约束惩罚项来避免过拟合问题,而稀疏连接是在模型训练过程中动态将一些连接权重置零(或Dropout技巧)。深度模型由于参数使用浮点数描述,规模庞大的浮点参数在计算机中势必占用大量内存,并且模型在执行推理过程时也会非常耗时。将模型中的浮点参数量化成仅占单个比特的二值参数,就得到了深度二值网络。当然浮点参数被量化成三值、四值等参数,相应得到的就是深度三值网络、深度四值网络等。另外,从生物神经元中膜电位的激活过程出发,还发展出了深度脉冲网络。除了利用各种神经网络结构堆叠出来的深度神经网络模型之外,还可以用非神经网络结构堆叠深度模型,比如用支持向量机堆叠出来的深度支持向量机、用随机森林堆叠出来的深度森林等。[18]

有监督学习包括生成模型和判别模型,而生成模型中一个著名的方法是生成对抗网络(Generative Adversarial Network,GAN)。当然GAN随后出现了诸多的改进版本[25]p223~233,比如CGAN、LAPGAN、DCGAN、InfoGAN、LSGAN、WGAN等。GAN思想可以运用到很多地方,GAN与CNNs结合可以得到深度卷积生成对抗网络,GAN与强化学习结合可以得到基于GAN的强化学习。

最后介绍一下非常热门的深度强化学习,这要归功于AlphaGo在围棋比赛中取得的巨大成功。深度强化学习是深度学习和强化学习结合的产物,深度学习负责解决感知问题(即是什么),而强化学习解决决策问题(即怎么做)。因此,深度强化学习被看成是进入强人工智能时代的重要研究方向之一。

通过上面的介绍,可以发现深度学习涵盖了各种模型结构、学习策略、训练数据形态等多方面的因素,这些因素通过排列组合可以衍生出种类丰富的深度学习技术,但其中的绝大多数在实际应用中表现并不出色。下面就从时下最流行的几个应用领域出发,介绍一些表现出色的具体算法。

(1)计算机视觉

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(2)自然语言处理

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

(3)人工智能+

(先占个坑,有时间再来补充详细内容,大家可以直接看文后的参考文献)

4.应用实践

随着深度学习的日趋完善,使得应用开发门槛越来越低。一个非计算机或数学专业的开发者,可以在搭载有较高性能CPU或GPU的计算机上基于深度学习软件框架(比如TensorFlow、PyTorch、Caffe、MXNet等)快速搭建出各种流行的深度学习算法,然后下载算法相应的公开数据集(或者自己搜集数据集)就可以进行训练,最后将训练出来的模型部署到实际应用场景。下面就以TensorFlow软件框架编写经典卷积网络LeNet-5的实现程序为例介绍一下大致的程序开发过程,如代码清单10-3所示。

代码清单10-3  LeNet-5实现程序[41]p80~83

  1 from tensorflow.examples.tutorials.mnist import input_data2 import tensorflow as tf3 4 mnist = input_data.read_data_sets('MNIST_data', one_hot=True)5 sess = tf.InteractiveSession()6 7 def weight_variable(shape):8     initial = tf.truncated_normal(shape, stddev=0.1)9     return tf.Variable(initial)10 11 def bias_variable(shape):12     initial = tf.constant(0.1, shape=shape)13     return tf.Variable(initial)14 15 def conv2d(x, W):16     return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')17 18 19 def max_pool_2x2(x):20     return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')21 22 x = tf.placeholder(tf.float32, [None, 28*28])23 y_ = tf.placeholder(tf.float32, [None, 10])24 x_image = tf.reshape(x, [-1, 28, 28, 1])25 26 #layer 1~2: conv + relu +max_pool27 w_conv1 = weight_variable([5, 5, 1, 32])28 b_conv1 = bias_variable([32])29 h_conv1 = tf.nn.relu(conv2d(x_image, w_conv1) + b_conv1)30 h_pool1 = max_pool_2x2(h_conv1)31 #layer 3~4: conv + relu +max_pool32 w_conv2 = weight_variable([5, 5, 32, 64])33 b_conv2 = bias_variable([64])34 h_conv2 = tf.nn.relu(conv2d(h_pool1, w_conv2) + b_conv2)35 h_pool2 = max_pool_2x2(h_conv2)36 #layer 5: fully connection37 w_fc1 = weight_variable([7*7*64, 1024])38 b_fc1 = bias_variable([1024])39 h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])40 h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, w_fc1) + b_fc1)41 #Dropout42 keep_prob = tf.placeholder(tf.float32)43 h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)44 #Softmax45 w_fc2 = weight_variable([1024, 10])46 b_fc2 = bias_variable([10])47 y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2)48 49 cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_*tf.log(y_conv), reduction_indices=[1]))50 train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)51 correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))52 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))53 54 #train55 tf.global_variables_initializer().run()56 for i in range(2000):57     batch = mnist.train.next_batch(50)58     if i%100 == 0:59         train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})60         print("step %d, training accuracy %g"%(i, train_accuracy))61     train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})62 63 #test64 print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))

第1~5行,载入MNIST数据集和TensorFlow库。可以发现TensorFlow是Python的一个库,而TensorFlow库中已经内置了训练LeNet-5所需的数据集MNIST。由于TensorFlow属于声明式编程,所以程序开头需要先为运行创建一个会话对象sess。

第7~20行,定义后面构建模型所需要的一些通用组件,也就是连接权重、偏置、卷积和池化。

第22~24行,定义后面模型的输入输出接口,通过placeholder实现。其中x是输入,y_是相应的标签。

第26~47行,构建LeNet-5的模型,也就是对输入数据逐层进行加权运算最后输出预测结果。其中第26~30行,为第1层的卷积与第2层的池化操作;第31~35行,为第3层的卷积与第4层的池化操作;第36~40行,为第5层的全连接操作。为了避免过拟合,第41~47行还增加了Dropout层与Softmax层,最后得到的就是输出预测结果。

第49~52行,选择损失函数、训练用到的优化方法以及准确率评价方法。

第54~64行,上面的模型和数据都准备好后,这里就可以开始训练和测试模型了。第54~61行,用数据集中的训练集部分训练模型;第63~64行,用数据集中余下的测试集部分测试刚训练好的模型的准确率。

10.3.2 CNN-SLAM算法

由于机器学习(特别是深度学习)是让传统SLAM技术取得重大突破的关键方向,因此上面花了大量篇幅对整个机器学习及深度学习领域的知识进行了系统性梳理。到这里把焦点转移回SLAM上来,具体分析CNN-SLAM和DeepVO两种基于机器学习方法的SLAM典型案例。

CNN-SLAM[42]属于第一种思路,也就是利用机器学习方法对SLAM传统方法中的局部模块进行改进,其系统框架如图10-60所示。CNN-SLAM依然保留了传统SLAM的前端-后端设计范式,即前端里程计(Camera Pose Estimation)和后端优化(Pose Graph Optimization),其创新是引入了CNN对输入图像直接进行深度估计和语义分割。经过CNN直接得到的深度信息与传统SLAM得到的深度信息进行融合,融合后的深度经过后端全局优化后生成全局地图;而CNN语义分割得到的标签信息融合进全局地图后生成具有语义标签的全局地图。

图10-60  CNN-SLAM系统框架

有关传统SLAM部分的模块就不再展开了,这里重点讨论一下CNN深度估计模块和CNN语义分割模块。CNN深度估计模块采用改进版的ResNet网络结构[43],如图10-61所示。该模型的前半部分基于ResNet-50,并用在ImageNet数据集预训练的结果进行权重初始化;而该模型的后半部分则将原来ResNet-50中的池化层和全连接层替换成了升采样层和卷积层,这样就能直接输出深度图。

图10-61  CNN深度估计模块

而CNN语义分割模块基于了论文[44]所示的方法,如图10-62所示。为了能重新训练该模型用于RGB像素级分割,需要对原模型进行改进。一方面是增加原网络模型的输出通道数,另外就是采用softmax层和交叉熵损失函数以便通过误差反向传播和随机梯度下降进行最优化求解。

图10-62  CNN语义分割模块

关于CNN-SLAM的代码实现,Github上有多种版本。这里推荐一个基于TensorFlow框架的实现版本,代码具体解读方面的内容就不再展开了,读者可以自行研究。

10.3.3 DeepVO算法

DeepVO[45]属于第二种思路,即完全抛弃SLAM传统方法的东西,感知数据直接通入机器学习算法后直接输出结果(所谓的端到端(End-to-End)方法),其系统框架如图10-63所示。可以发现DeepVO并不是真正意义上的SLAM,因为输入图像后最终只输出了位姿信息,也就是说它只是一个VO而已。模型由CNN和RNN堆叠构成,CNN负责从输入图像中提取特征信息,而RNN负责提取特征信息中的时序相关信息。

图10-63  DeepVO系统框架

关于DeepVO的代码实现,Github上有多种版本。这里推荐一个基于TensorFlow框架的实现版本,代码具体解读方面的内容就不再展开了,读者可以自行研究。

参考文献

【1】 张虎,机器人SLAM导航核心技术与实战[M]. 机械工业出版社,2022.


http://www.ppmy.cn/news/20177.html

相关文章

LeetCode[685]冗余连接II

难度:困难题目:在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。输入一个有向图&#xf…

原力分入门技能树-模拟

博客(blog) 以下哪种情况不可以提升原力分: 发高质量博客发低质量博客博客被点赞博客被评论 质量分(qc) 在以下哪个网址中可以查询博文质量分: https://ask.csdn.net/https://bbs.csdn.net/https://blog.csdn.net/https://www.csdn.net/qc 问答(as…

2022回顾

2022年回顾 前言 新年和亲朋好友的相聚差不多接近尾声,假期也所剩无几,开始静下心来写作,回顾一下我的2022年,看下自己去年 做得好的和不足,展望下2023,开始新一年的生活。(因为是公历2023年…

MFC|Toolbox内控件简单介绍

参考: MFC控件工具箱 (https://blog.csdn.net/Hubz131/article/details/77684910) 对应工具的超链接是本人搜到认为较易理解的单个控件介绍。 Pointer:就是普通的鼠标,默认状态Button:按钮,用…

【通信原理(含matlab程序)】实验三 数字基带信号及其频谱特性

💥💥💞💞欢迎来到本博客❤️❤️💥💥 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识,如果大家喜欢,别忘点个赞加个关注哦,让我们一起共同进步~ &#x…

2023编程语言趋势

2023编程语言趋势 作为CTO,我需要持续关注编程语言的发展。按照惯例,每年年初我都会对未来一年关键编程语言的趋势做一定的预判。今年由于众所周知的原因,预测地有些晚,我选择在开年的第一天给出我的预测,也算是祝大家…

【JVM】详解直接内存

文章目录1. 直接内存概述2. 直接内存的使用2.1 Java缓冲区2.2 直接内存3. 直接内存的释放3.1 直接内存释放原理4. 禁用显式回收对直接内存的影响1. 直接内存概述 下面是 《深入理解 Java 虚拟机 第三版》2.2.7 小节 关于 Java 直接内存的描述。 直接内存(Direct Me…

Npm开发自己的第一个库

目录开发并上传第一个库账号注册与登陆新库创建上传插件更新试试你的第一个插件吧!开发并上传第一个库 账号注册与登陆 官网注册一个自己的账户,务必记住用户名,之后链接到 npm 需要使用 安装 nodejs,他会自动帮我们把 npm 添加…