Category: ICT-Information and Communication Technology
-
LLM技术系列之-DeepSeek V1/V2/V3模型结构简介
这篇文章将向大家简要介绍DeepSeek-V1/V2/V3的模型结构及其发展演化过程。 在介绍DeepSeek的模型结构之前,这里简要复习一下LLM关键的基于Transformer架构图的模型结构的主要知识和技术点。 Transformer的基本架构,包括多头自注意力机制,FFN等关键组件。其中自注意力机制也分为双向的(Multi-Head Self Attention)以及causal的(Masked Multi-head Self Attention),一般双向自注意力模块的用于编码器结构(如BERT,基于WordPiece分词器),主要用于上下文丰富语义信息提取,为下游任务如文本分类等提供很好的文本特征提取功能;causal自注意模块用于解码器架构(如GPT,基于BPE分词器),主要用于文本生成(自回归语言生成,基于之前生成的内容来生成后续的内容,为基于单向时序因果的计算逻辑,通过掩码机制进行计算),如问答系统等的应用,两种模块都会用到的为编码器解码器组合结构(Auto Encoder,双向自注意力结构和caual+交叉自注意力模块),主要用于机器翻译,序列转换等应用任务。 DeepSeek的文本生成,数学推理,代码补全等任务上基于传统的Transformer的解码器架构框架,但在两个核心组件上进行了调整:1为MSA到MLA(Multi-head Latent Attention)的调整;2为FFN到MoE组件的调整。下面将一一进行简要介绍。 MLA组件和原始Transformer版本的MSA的结构类似,差异仅仅在于对于token embedding的输入维度进行了降维,将其映射到隐空间上进行表示。可以理解为将token的embedding维度进行了压缩后进行的自注意力机制的计算,在计算之后再通过提升维度达到和原始的token embedding的维度一致。而原始版本的MSA中K,Q,V矩阵的第二个维度会保持和token embedding的维度一致(可以理解为一种在token上进行了基于类似扩展的VAE的算法实现了计算性能上的优化)。 MoE(Mixture of Experts)的基本原理是使用混合专家来替代原transformer架构中的前向反馈层(FFN),MOE层由多个FFN组成,每个FFN理论上更关注不同领域知识,即领域专家,每个token会选择其中的top-K(top-K为模型超参)个FFN向前传递。在具体实现时由路由选择组件和前向FFN专家集合组成。 问题1:MoE可以理解为首先用类似于早期的注意力机制选择token embedding的重要信息从而实现降维,然后将所得结果输送给多个领域专家进行计算聚合,可以这么理解对吗? 回答: 这种理解不是很正确,TopKRouter实现动态路,其核心功能为选择部分的token给对应的专家网络模块实现领域专家的计算。具体点的实现思路为: 基于门控网络(简单线性层+Softmax)实现TopK进行稀疏选择,将token输入给前topk个专家进行领域计算(剩下的专家不参与token的计算),这样可以较大效率提升计算效率,同时实现了不同专家的隐式领域划分,采用了负载均衡损失保证专家之间的计算平衡。同时聚合是基于token的,如token的结果由两个专家来进行领域计算,则结果为两个专家计算结果的加权和(topk权重之和归一化到1)。 其中负载均衡损失函数的定义可以参考参考[2]中的说明,其中fi为理想的专家分配概率分布(均匀分布),pi为实际的第i个token对于每个专家的概率分布,loss的计算可以基于KL散度进行计算。负载均衡损失只是一个约束项,在均匀性和有效性之间找到平衡。负载均衡损失的目的是防止专家塌陷,而不是让选择完全随机。 DeepSeek-V1模型在MoE的设计上做了两处的改进,具体如下图所示:(1)、细粒度的专家模型分割,采用了更多的专家模型子模块;(2)、采用了共享专家模型,学习通用知识。 问题2:请分析一下switch transformer和deepseek v1/v2/v3中的moe的实现上的模型结构,负载均衡损失等上的设计差别? 回答:DeepSeek的MoE架构的介绍见参考引文[2]。其中由于MoE的数量较多(v2版本路由专家达到160个),通常也会采用专家并行的策略,将专家分配到多个GPU设备上进行并行计算,但由于如果同一个token分配到的GPU设备如果过多,就会存在着GPU之间的通信开销问题,因此也会在路由中加入了设备数量限制。以达到通信的负载较为均衡的目的。v2中有160个路由专家,6个激活专家,v3的路由专家有256个,8个激活专家。v3在负载均衡的损失设计上也做了较大调整, References
-
SLAM系列之FAST-LIVO2代码分析-总体处理流程简介
基于ROS1平台,整个流程是while循环,循环体内的流程以VIO和LIO迭代交替循环执行(如图1所示),这也是error state iterated kalman filter (ESIKF)的实现所需要的逻辑。 1、数据同步(sync_packages函数),如图所示,mode将在VIO和LIO间轮流变换,sync_pages会根据当前的mode进行对应的逻辑处理。 在VIO模式下,将以图片帧作为处理的截至时间,对齐imu和点云数据进行处理;如果图片的帧率过快,从buffer里获取的图片比last_lio_update_time时间还要早,则对图片进行丢帧处理,由于lidar传感器的扫描特性,原始帧每一个点云的采样时间不同,需要对原始帧的点云集合按对齐时间进行逻辑划分,比当前图片帧还要晚的点云点数据将会分配到下一个点云数据集合里。获取对齐的imu数据和lidar数据将更新到测量组里(MeasureGroup)。数据对齐完成后切换到LIO mode进行下一步的处理。 LIO模式下的数据同步主要来获取图像数据,图像数据获取后将切换到VIO mode进行下一步的处理。 图1:FAST-LIVO2处理流程图 2、数据预处理(processImu函数)在LIO模式下,主要处理包括:(1)、以预积分的方式计算imu在每一个时间戳下的初始位姿以及计算状态误差状态雅可比矩阵和误差传播协方差矩阵_state.cov,代码实现可以参考引文[1];(2)、通过imu预积分的结果(imu预积分计算在imu采样的时间戳序列下的对应的初始位姿)对lidar点云数据进行运动补偿去除畸变。运动补偿基于点云点时间戳和其对应的最近imu的时间戳的时间差和imu位姿及加速度角速度等数据按照运动方程进行时间对齐后的点云位姿估计,具体代码实现可以参考引文[2]。在VIO模式下,在基于LIO的点云和IMU紧耦合的ESKF优化的基础上,在对点云数据基于图像可视视角范围的过滤后在于图像的光度误差作为误差观测状态(又称为直接法)和前期IMU的姿态优化的基础上采用ESKF算法再次对位姿进行优化。 3、状态优化和建图,函数名为stateEstimationAndMapping,在步骤2中也有说明,其中LIO和VIO的迭代ESKF优化框架ESIKF(error state iterated kalman filter)在LIO和VIO依次都用到了ESKF滤波方法,其误差状态方程都是基于IMU的误差量(如基于李代数的旋转扰动量等19个维度),但误差观测方程不同,基于LIO的ESKF的误差观测基于点云点到匹配平面的距离残差,而VIO的ESKF的误差观测基于光度误差(直接法),具体后续将会在相关文章中详细介绍。 References
-
SLAM系列之FAST-LIVO2代码分析-基于点云的体素地图构建和更新方法介绍
在参考文献[2]中,已经向大家介绍了点云相关的数据结构, 比如基于激光点集合的点云帧数据,体素地图的构建以及自适应体素分割方法。这篇短文将继续以问答的形式介绍FAST-LIVO2中的体素地图构建的更多内容。 问题1:该系统中有没有维护全局地图,还是只维护车机当前局部地图,以及如何实现的? 回答:该系统中只维护了局部地图,通过mapSliding()函数处理激光传感器所处位置的变化所带来的体素地图中点云数据的“可见性”,将当前“不可见”的点云数据从地图中进行清理删除,从而维护了当前系统运行所需的局部地图。当每次调用LIO处理激光点云和IMU进行ESKF算法联合优化位姿后,会判断地图当前的激光传感器所处的世界坐标位置和上一次进行滑动操作时激光传感器所处位置的距离,如果大于设定的阈值,则会将超过当前位置为中心点的地图大小范围外(定义了half_map_size变量可以作为参数在系统初始化时进行设定,体素坐标在x,y,z三个轴方向上在[-half_map_size:half_map_size]范围内的点云数据从地图内存中进行删除,从而维护了精简的局部地图。 问题2:地图创建及更新,自适应体素分割,体素平面的拟合等操作实现的关键的思想和作用分别是什么? 回答:1、地图的创建在系统初始化的时候进行,实现的函数为BuildVoxelMap,其主要思想是根据点云数据构建点云索引到八叉树地图节点的字典(unordered_map),并对每一个节点进行自适应体素划分;2、地图的更新函数为UpdateVoxelMap,当新的lidar帧到达的时候,会基于lidar帧的每一个点云点来更新体素地图,相邻lidar帧的点云可能会更新到同一个体素八叉树的节点上,需要根据具体的体素节点的状态,是否更新平面拟合,或进一步进行体素分割等;3、自适应体素分割主要针对的是体素内的点云的几何特征属性,如果能成功拟合一个平面,则保存拟合平面信息,否则如果体素内点云可能构成丰富的曲面,需要进一步去细分体素用更小的平面去拟合曲面信息;4、体素平面拟合的思路为:计算体素内三维点云的协方差矩阵的特征向量和对应的特征值,如果最小的特征值小于一定的阈值,说明只有两个主要向量,这两个主要的向量近似构成一个平面。 References
-
SLAM系列之FAST-LIVO2代码分析-LIV(LiDAR-Inertial-Visual)多传感器数据预处理过程介绍
这篇文章将向大家介绍FAST-LIVO2开源系统中的数据预处理部分。包括多传感器数据同步、IMU预积分与激光点云去畸变(运动补偿)等。关于激光点云和IMU传感器的数据特性的详细说明请参考附录(基于像素表示的摄像头RGB图像数据比较普及不做介绍)。 其中多传感器数据同步的实现在sync_packages函数里,这里主要分析slam模式在LIVO下的数据同步方法。其主要的思想是以图片帧作为处理的时间基准(从lidar_header_time_buffer找出小于当前图片基准时间戳的所有点云原始帧数据),将原始的pcl数据帧按照时间关系重新组织到两个逻辑点云帧结构体(pcl_proc_cur和pcl_proc_next)中。 激光点云数据的时间戳是以point为粒度,扫描时间不同,激光雷达传感器的帧率相比图像和IMU是最低的(大概10-20Hz),在一帧图像数据中,点云之间被扫描到的时间点各异(因为扫描有先后,位置在时间上没有对齐),可以根据IMU的预积分的结果进行运动补偿。一般将这个过程分为两步:1、IMU预积分,也称前向传播(propogation of IMU); 2、基于IMU的预积分的结果对PointCloudXYZI类型的待处理的点云数据用反向传播的方式进行运动补偿。 附录: 附录A:关于激光点云的基本数据结构的介绍 1、激光点云原始数据帧PointCloudXYZI类,包含XYZ坐标和强度值(Intensity),为激光点的容器集合。在本开源系统中,点云数据等传感器数据通过ROS的message发出来,并进行预处理,激光点云信息中,将curvature属性作为当前点云点扫描时的时间信息加以存储(相对于原始点云帧的起始点时间差)。激光点云数据的一帧数据一般定义为一个完整的扫描周期内所获取的所有点云的集合,而且以最先扫描到的点的时间戳作为帧起始时间。激光雷达传感器也分为传统的激光的旋转式机械激光扫描和livox非重复式较高密度点云点采样扫描。有时如果点云分辨率过高,会需要更多的计算资源,点云处理库pcl中也有针对点云进行降采样的相关类pcl::VoxelGrid,直接调用相关函数即可实现点云降采样。 2、Voxel,体素,相对于二维平面的像素而言,为三维空间的细分表示单元,通常表示为一个小的立方体,存储的信息可能包括:1、该空间是否被占据;2、颜色或强度值;3、法向量;4、置信度。在体素地图(Voxel map)中,三维空间被划分成类似于魔方结构的多个体素单元,体素地图用于点云建图,三维重建等任务; 3、Octree(八叉树),满足一般的树结构的递归定义,为一种八叉空间划分树,如一开始的L*L*L尺寸的空间,第一级划分将空间的“长宽高”各对半切分,上下各四个子体素(八个子节点),以此类推进行递归再划分。八叉树的表示空间的好处在于对于稀疏的区域表示,可以用大体素(对应的叶子节点的层次比较低,可以提高存储效率),对于密集表面比较丰富的区域,可以用小体素(可以有更高的精细度),这种表示方法又称为自适应体素分割。 4、Octree Voxel Map(八叉树体素地图),体素的位置按照整数坐标存储,在空间上对应三个坐标轴上的整数索引x,y,z。某个点云的索引为点云的三个维度世界坐标除以根体素的大小(max_voxel_size)。八叉树体素地图可以基于字典结构(unordered_map,哈希表)实现体素的快速索引(map的key为三维整数坐标缩影,value为八叉树的根节点,可以存储多个点云数据)。一般如果max_voxel_size=0.5,则表示根体素的分辨率为0.5m*0.5m*0.5m。在该体素内部一般也会有几个到几十上百个点云点,具体要看采样的设备和是否进行了降采样处理。体素地图基于世界坐标构建,在系统运行过程中其体量可能较大。通过八叉树体素地图可以动态构建和更新全局地图。同时该体素地图支持点云的自适应体素分割,在代码实现中max_layer和voxel_size密切相关,如max_layer>1,则当原始体素内的点云点如果不满足拟合出平面的条件(说明表面为较为丰富的曲面),则会递归的进行八叉树子节点创建以支持构建更加丰富的表面信息。 5、Voxel Plane,代表了体素对应的局部平面信息,常用于基于八叉树的三维环境建模,对于体素内的多个点云,通过拟合平面模型,提取点云在该体素单元内的主要几何结构信息(如物体所表现的在该位置处的主体表面信息)。如果拟合平面成功,说明该体素对应的区域为平面如地面,否则可能对应着丰富的曲面表面,需要再进行细粒度的分割,用更小的平面去拟合曲面。 附录B:IMU数据基本结构介绍 IMU(Inertial Measurement Unit),即惯性测量单元,一般提供智能体运动的线性加速度(linear_acceleration,通过加速度计获取),角速度(angular_velocity,通过陀螺仪获取)等信息,通过线速度和角速度以及时间戳信息就可以通过预积分获取智能体的在时间戳上累积运动的位姿。 References
-
LLM技术系列之-GRPO算法介绍
本篇文章将向大家介绍GRPO算法的基本原理,其也是DeepSeek模型训练所采用的关键算法。 GRPO(Group Relative Policy Optimization)算法和PPO(近端策略优化算法)类似,其中相似的地方有基于优势函数和重要性采样机制以及梯度裁剪相结合的loss定义。但GRPO算法的创新的地方在于其优势函数的定义和计算方式不同,也是其优势所在。 PPO算法在rollout时基于一个prompt生成一个response(completion),而GRPO中会基于一个prompt生成多个completions,图1为两个算法的优势函数定义对比图。PPO算法的优势函数的公式中[1]有依赖于即时奖励和价值函数的计算,而GRPO算法中没有Value Model,从而省去了训练时Value Model的推理和梯度更新的计算资源。GRPO的优势函数的计算方式可以参考[2],其基于生成的多个completions分别用奖励模型去计算奖励,然后基于多个奖励值进行归一化(减去均值除以标准差)后得出优势函数的计算结果,这也是群体相对策略优化(Group Relative Policy Optimization)名称的由来。 completions 之间的差异(diversity)正是由随机采样机制(如 top-k, top-p, temperature)来保证的,具体的这些超参在generation_config字典变量里进行设置。关于为什么用unwrapped model进行多个completions的生成以及采用model模型根据completion_ids进行logprobs的计算的原因可以参考相关issue[4]中的说明。 GPRO算法中的目标函数定义方法见图2。具体的计算时采用per_token_logps和ref_per_token_logps来计算训练模型的KL散度loss,同时用old_per_token_logps来计算模型当前rollout周期时生成completions样本时对应的per_token_logps(old_per_token_logps为当前数据采样周期时策略模型的生成的结果,可以达到一定程度的复用,用于策略模型的一个周期内的多次迭代;而ref_per_token_logps是参考模型的输出,用于计算KL loss,着PPO算法的机制相同)。详细的具体的实现参考代码[5]。 问题:ppo中critic和policy网络可以共享backbone,计算量也大不了多少? 回答:critic实现的价值网络虽然可以和policy网络共享backbone,但valuehead也需要在推理和梯度回传更新时需要不少资源。详细答案可以参考[3]中的描述。 问题:问答模型的数据集的格式是什么样的,如何实现不同推理场景的数据的统一描述? 回答:关于模型的训练数据方面的内容,如数据如何收集,数据的内容主题特性,以及如何通过角色实现对话上下文的管理并支持基于对话上下文的长token处理支持等内容,可以参考issue[6]中的描述。 References
-
LLM技术系列之-基于RLHF的生成式大模型实现思路及其中的关键算法PPO介绍
这篇文章将向大家介绍基于RLHF的生成式大模型总体思路流程,以及介绍TRL库中的PPO训练算法实现中的几个关键细节点。 1、rollout数据回合采样的方法,rollout为对数据进行采集,类似于强化学习的gym模拟仿真环境中的step等环境仿真模拟方法(传统的RL任务,游戏,机器人仿真环境等,提供了统一的接口,如step,render等)。在LLM场景下,rollout 过程用于收集序列(states, actions, rewards)以供训练策略优化,states对应于queries tokens,actions对应于生成的response的tokens,以及通过奖励模型(Reward Model, RM) 计算生成的tokens序列奖励得分(一个query的得分为一个scalar的标量值来度量生成的tokens序列的质量)。这里对计算过程中的相关变量做一下说明:(1)logits为未经过softmax的原始值,如GTP-2的输出output有两个属性,一个为scores即为logits,另一个sequences为输出的tokens的序列。logprob为对logits进行log_softmax计算后选择生成的token对应索引位置的值,这里的参考模型和策略模型输出的KL散度的定义为基于对应的token id的概率之间的差异,而不是基于整个logits的所有词汇表(vocabulary)空间的概率分布去计算KL散度信息,详细的代码注释说明请见[1]。 2、从训练框架代码上需要说明的是,accelerator库提供不同GPU型号的memory伸缩训练支持(通过超参如local_batch_size,local_mini_batch_size,per_device_train_batch_size进行设置)。accelerator库可以通过多个小批次的梯度累积计算来模拟大批量的训练,具体的实现方法为:在 with accelerator.accumulate(model) 作用域内:减少显存占用,允许在小显存的 GPU 上模拟大批量训练。仅在梯度累积步数达到设定值时执行 optimizer.step()和 optimizer.zero_grad(),否则只进行梯度计算,不更新权重。避免手动使用 if step % accumulation_steps == 0: 逻辑,提高代码可读性及实现便捷性。 问题1:rewards代表即时回报,计算过程中为什么中间token的rewards值只与KL散度相关,而最后的token为KL散度相关的奖励加上reward_model给出的评分(score)之和?这么设计为什么有效? 回答:rewards在优势函数中参与了相关的计算,引文[2]公式中的r即为reward,根据回报函数或优势函数的计算方式,回报函数或优势函数为当前即时奖励到最后的每一个step对应奖励的序列的折扣和(具体公式见[2]),这样最后的回报通过传递都会对前面的token的优势函数有影响。在具体的设计过程中,KL loss前还有一个权重超参kl_coef。 问题2:在RLHRF算法中,参考策略模型,策略网络,价值网络,评分模型(网络)之间的关系是怎样的?以及ppo(近端策略优化)算法中的数据采样和迭代训练的大体流程是怎样的? 回答:在RLHF的训练过程中,一般包含三个步骤:(1)、在超大规模文本数据上基于自监督预训练的LLM模型上进行有监督微调(Supervised Fine-Tunning,基于人类标注的监督式数据),SFT模型在基于RLHF的PPO算法中用作参考策略模型提供训练数据样本生成;(2)、奖励建模,通过对比损失函数和基于人类反馈的偏好评分rank标记数据集(RLHF数据集)训练reward model;(3)采用PPO算法训练问答大模型,在实现时,参考模型既可以是独立传入的SFT训练的模型,也可以和策略网络共享部分或所有参数参与更新(具体可以查看参数num_shared_layers以及函数create_reference_model,但基于现有的调研来看,参考模型通常是冻结的,不参与训练,参数也不共享。策略模型从参考模型初始化,但在训练过程中独立更新),而价值网络一般可以和策略模型共享backbone(如类AutoModelForCausalLMWithValueHead),其在输出时多一个输出token对应的标量的value值(value值和reward值一起用于参与优势函数的计算)。而上述的rollout过程即为训练数据在线生成的过程,通过queries分别调用策略模型,参考策略模型,价值网络模型(可能和策略模型共享backbone),以及reward model(打分模型)推理以生成训练数据,然后基于一次rollout生成的数据进行num_ppo_epochs次参数更新迭代训练(policy网络和value网络的联合loss实现参数更新)。 References
-
SLAM技术系列之李群李代数简介
这篇短文将向大家介绍李群李代数的相关知识点,以及其在slam中具体的应用。 群(group)是一种定义在特殊集合以及在该集合上定义的一种运算的特殊代数结构,比如整数集合及加法运算满足群的定义,旋转矩阵和矩阵的乘法构成了特殊正交群,欧式变换矩阵(又称特殊欧氏群,n维空间的欧式变换矩阵为(n+1)*(n+1)的欧式变换矩阵,基于欧式变换的计算采用齐次坐标系)和矩阵的乘法构成了n维欧式变换群等,群的定义需要满足三个约束条件:1、结合律,2、存在幺元和可逆,3、封闭性。而李群是指具有连续光滑性质的群,如上述的SO(3),SE(3)。 每个李群都有其对应的李代数,拿特殊正交群来举例,根据旋转矩阵的正交性以及微分方程的求解,可以得出旋转矩阵随时间变化的函数可以表示为其正切空间(为反对称矩阵,代表角速度的李代数元)上的指数映射(通常用于描述从李代数到李群的映射,描述几何变换的动态过程),具体的解释如下图所示: 通过将指数映射进行泰勒展开,由于旋转向量的大小和方向分别描述了旋转的角度和旋转轴。反对称矩阵是旋转向量的矩阵表示,通常通过叉乘矩阵来构造(待补充)。在SO(3)中,旋转向量和反对称矩阵等价,具体理解如图2所示。 具体的李群和李代数的指数映射的计算基于指数函数的泰勒展开,并且利用到了标准方向向量对应的反对称矩阵的一些计算性质,最后可以得出罗德里格斯公式。 两个李代数的指数映射的乘积和一般的标量的指数函数的乘积不同,根据泰勒展开以及多项式相乘的展开式,以及矩阵乘法的非交互性,计算过程如图3所示,由于BCH公式中在二次和更高阶的余项中只保留了非交换性的部分,因此BCH公式是对指数映射的乘积的一种近似。BCH公式为李代数的求导和扰动模型的计算提供了基础,而李代数的求导或扰动模型为优化非线性的变换矩阵(没有定义加法运算,不便求导)提供了基于李代数计算的导数优化。 李代数的扰动模型比李代数求导更加简洁,基于李代数的扰动模型由于比李代数的求导更加方便,在实际中用的会更多。 李代数在slam中的使用主要作用体现在优化相机位姿和地图点的过程中,具体可以参考g2o优化库的相关优化方法,如g2o::VertexSE3Expmap,就是基于SE(3)的指数映射进行的。 修订:关于BCH的公式的理解有出入:后面的解释请参考最新的手册[2] References
-
SLAM系列之ORB-SLAM3开源系统代码解析-系统实现概览和简要总结
这篇文章将向大家总体介绍一下ORB-SLAM3系统的设计和实现,其中每个模块的实现逻辑和思路已经在主题文章中向大家做了介绍。 ORB-SLAM3实现了多种传感器配置下的同步定位(相机位姿的实时跟踪定位)和建图(基于稀疏三维地图点的三维地图构建)功能,且通过Atlas类实现了多地图集(跟踪失效时会重新创新新地图)的管理,以及包括地图的回环检测和矫正以及地图的合并。整个系统大体由单帧实时跟踪模块(Tracking.cc),局部建图模块(LocalMapping.cc)和回环检测和矫正(LoopClosing.cc)模块以及可视化模块(View.cc)等构成。其中各种传感器配置场景在代码实现时是耦合在一起的,根据配置的条件的不同对其逻辑的差异性分别按条件进行处理。 Tracking模块需要处理频率较高的视频帧,采用参考关键帧或基于上一帧的运动跟踪模式对特征点进行匹配和相机位姿进行优化等。一般如果处理一帧的tracking较为耗时,可能要降低帧率或丢弃部分帧,一般来说,tracking一帧的耗时可以在帧率的一半,而仅有部分帧将作为关键帧来加入到地图中,关键帧帧率大概在0.5-2秒左右,因此关键帧占总帧数的比率较低,即使在tracking的全过程中使用地图更新访问加锁,其他模块如局部建图和回环检测和矫正模块都会有较为充裕的时间来调度执行。 这里对于模块涉及地图更新的操作的地方做一下说明。首先在Tracking过程中涉及地图更新的操作的地方有:(1)单目初始化函数MonocularInitialization里调用了初始地图的创建,将当前帧和初始帧作为两个关键帧插入了初始地图里;(2)TrackReferenceKeyFrame或TrackWithMotionModel函数里对相机位姿进行了优化(也更新了地图点的信息);(3)重定位时从候选的关键帧中计算匹配点并优化位姿;(4)、TrackLocalMap利用了关键帧邻近关键帧的更多的匹配地图点进行位姿优化;(5)最后判断是否将当前帧作为关键帧插入,如果插入关键帧,同样也需要更新地图。 问题:ORBSLAM3中的LocalMapping的函数SetAcceptKeyFrames(false);只限制了Tracking模块不能插入新的关键帧,但如果在LocalMapping的过程中如实现冗余关键帧剔除(KeyFrameCulling)或者地图点的剔除操作的同时,有没有可能和Tracking的访问地图操作存在着数据访问竞争的问题,因为在LocalMapping的处理过程中好像没有加锁访问地图更新? 回答:在局部建图的处理过程中虽然没有使用当前地图的互斥量mMutexMapUpdate进行加锁,但是在地图的细粒度的访问中都加了细粒度的互斥量的定义,同时ORB-SLAM3不追求严格的实时一致性,而是通过以下设计实现最终一致性:(1)闭环校正的全局同步:当检测到闭环时,系统通过全局BA或位姿图优化对所有关键帧和地图点进行全局调整,此时所有线程暂停,确保全局状态的一致性。(2)高频跟踪,低频优化:如Tracking线程以相机帧率(30Hz)运行,而LocalMapping和LoopClosing线程以更低频率(约10Hz)执行优化。优化结果对Tracking的影响是“延迟生效”的,但系统通过BA和闭环校正逐步收敛到全局一致状态,如在Tracking的高频计算过程中,低频的局部建图和回环检测运行的数据是基于时间线上靠前面的数据,从而也减少了冲突访问的可能性。(3)、采用一些鲁棒的核函数和优化算法,即使存在着临时的量的不一致性,也能足够健壮得出较为理想的优化结果。(4)、局部BA等优化过程更新相机位姿和地图点时采用了地图更新互斥量这种粒度较大的锁,实现了和tracking过程的互斥访问。 关于ORB-SLAM3系统中可能存在的bug问题,可以参考文献[7]中作者给出的修正。ORB-SLAM3系统主要的功能是定位和建图,在机器人无人机等系统中作为感知的大模块的一部分,也需要和PNC(Planning and Control)等模块集成形成一个完整的自主导航系统。 References
-
SLAM系列之ORB-SLAM3开源系统代码解析-相关优化算法简介
本篇文章将向大家介绍在SLAM中使用的相关优化算法及其在ORB-SLAM3中相关模块中的具体应用案例。 首先,SLAM系统的核心功能之一为视觉里程计(Visual Odometry, VO,也通常称为前端,记录智能体随时间变化在场景中运动的姿势方向和位移,由于前端长时间运行会出现累积漂移和误差,因此也还有回环和地图合并检测等后端模块来进行误差矫正和消除). 在系统初始化或tracking跟踪丢失创新新地图时,地图初始化时的第一帧的位姿设定为单位矩阵,第二帧的位姿以及两个连续帧的匹配特征点对应的地图点初始值通过Camera类的ReconstructWithTwoViews函数进行计算实现,具体是通过对极约束和三角测量来分别估计相机运动位姿和求解像素的深度信息和对应的物理世界的地图点坐标)。 MLPnPSolver优化器,该优化器的作用为基于最大似然的PnP(Perspective-n-Point)问题求解器,PnP问题的一种典型应用场景为基于匹配的前后帧的3D坐标和2D图像特征点集合来估计相机运动的位姿(旋转+平移),在Tracking的重定位算法中用于快速找出相机位姿的初始解供后续进一步优化。MLPnPSolver基于最小二乘的高斯牛顿法迭代求解逐步减小重投影误差来优化相机位姿,其中MLE(最大似然估计)是一种基于统计概率的参数估计方法,在SLAM应用场景中,参数为相机位姿,观察值为图像中ORB特征点的像素坐标,通过MLE算法,找到最优的参数解等价于使得重投影误差最小化。在算法的实现过程中,由于图像的位姿如旋转矩阵的参数对于误差函数不方便直接求导,而是通过罗德里格斯公式将旋转矩阵转化为旋转向量,和位移一起构成6维向量根据李代数的扰动模型进行求导,而在进行优化时,误差项e为图像uv平面的二维向量。因此目标函数将表现为向量的形式,基于雅可比的矩阵导数的优化比较直观,在大规模问题场景中(如基于地图的全局位姿和地图点优化),利用雅可比矩阵的稀疏性能更高效求解。在MLPnPSolver中,同时采用了RANSAC(Random Sample Consensus,重采样一致性算法)算法,RANSAC是一种鲁棒估计方法,其也采用了多次迭代求解,每次迭代随机选择一定数量的匹配3D2D点对进行求解,并统计内点(求得的位姿进行匹配点映射后误差在一定小的范围内)的数量,最后选择内点数量最多的为最后的位姿优化结果。 Optimizer类的相关优化函数;(1)、PoseOptimization,基于3D2D匹配点对和已经根据Tracking的过程优化的初始位姿,采用g2o的图优化方法来优化位姿,其中g2o的图优化中,节点有基于位姿的g2o::VertexSE3Expmap,边有ORB_SLAM3::EdgeSE3ProjectXYZOnlyPose(顾名思义,这条边仅仅通过相机位姿SE3将地图点坐标映射到图像像素且仅仅优化位姿Pose),以及设置观测值(像素坐标),核函数等,(2)、LocalBundleAdjustment,基于传入的关键帧及其共视关键帧集合对多帧位姿和地图点进行联合优化,同样基于g2o的图优化算法,这里有一个需要注意的地方在于地图点的观测帧不属于共视关键帧的观测帧将作为位姿固定的节点(不参与位姿优化的节点)加入到优化的graph里。这里用的边为EdgeSE3ProjectXYZ,将同时优化位姿和地图点;(3)、GlobalBundleAdjustemnt和BundleAdjustment将以关键帧数组和地图点数组来构建g2o优化图进行联合位姿和地图点优化。(4),Optimizer还有一些和IMU相关的联合优化方法实现,具体可以参考文献2中的代码实现[2]。 Sim3Solver:Horn’s 四元数方法(Horn’s Method)是一种用于最优对齐两个三维点集的方法,广泛用于位姿估计、点云配准、结构光扫描等场景。它通过最小化均方误差(Least-Squares Error),利用四元数来高效求解旋转矩阵R,在ORB-SLAM3系统中,其主要用于(1)、回环匹配关键帧和当前关键帧的变换求解;求解的参数有旋转、平移和尺度信息,为七个自由度;(2)、合并匹配关键帧和当前关键帧的变换求解。 References
-
SLAM系列之ORB-SLAM3开源系统代码解析-地图回环矫正和地图合并及矫正
这篇文章将向大家介绍LoopClosing模块中的地图回环矫正和地图合并和矫正部分的内容,其函数分别为CorrectLoop和MergeLocal(或MergeLocal2),其根据从回环检测过程中求解出的回环匹配关键帧或合并匹配关键帧到当前帧的Sim(3)变换进行回环和地图合并及矫正。 关于回环矫正的几个问题: 问题1: 在ORB-SLAM3中CorrectLoop的函数[1]的功能中根据前面优化求得的变换mg2oLoopScw应用到当前关键帧及其共视帧(统称为连接帧mvpCurrentConnectedKFs)上,然后进行OptimizeEssentialGraph进行优化,请详细分析一下具体的步骤及思路。 回答:mg2oLoopScw是 Sim3Solver计算得到的回环帧到当前帧的 Sim(3) 变换所得矫正后位姿,用于修正关键帧及其连接帧集合(当前关键帧的所有共视关键帧)的位姿(将该位姿和调整关键帧和当前关键帧的相对变换进行叠加及可以对每一个共视关键帧的位姿进行初步矫正),使整个局部关键帧集合做统一一致的初步矫正。然后调用OptimizeEssentialGraph进行通常在闭环检测后调整关键帧的位姿,保持整体一致性。这里使用sim(3)变换,因为闭环可能涉及尺度漂移,需要调整尺度。(而GlobalBundleAdjustment是全局BA,优化所有关键帧和地图点的位置,使用se(3)因为BA通常处理的是刚体变换,不考虑尺度,将回环约束均匀传播到整个关键帧图)。关于优化部分的内容,将会在专门的文章中加以介绍。关于回环矫正的更详细的思路,可以参考本文后面地图合并的部分内容。 问题2:首先将当前关键帧及其共视关键帧应用一致的矫正变换然后再进行全局优化。这个步骤不添加是否会有影响? 回答:一般的情况是回环匹配帧由于在地图的时间点前面,其误差和尺度漂移的程度要小,当前关键帧和回环匹配帧之间也许走过不少的其他关键帧,积累的较多的误差,因此首先通过将当前关键帧的共视关键帧首先应用统一的矫正变换以实现初步的累积误差和尺度漂移矫正,这对于后面的全局优化的过程能够更加容易收敛。 关于地图合并的几个问题: 问题1:地图合并的场景有没有比较具体的应用场景的示例帮助理解? 回答:ORB-SLAM3支持多地图(multi-map,由Atlas类来管理地图集),在地图合并和回环状态的检测的过程中不同的地方在于匹配关键帧和当前关键帧在不在同一个地图里。比如家庭里的多个房间,工厂里的多个车间等。 问题2:地图合并时的实现逻辑是怎样的? 回答: 地图合并时主要采取如下的步骤:(1)、首先根据当前帧来更新一定窗口大小的共视帧数量(如果当前关键帧的直接共视关键帧的数量不够,则继续在共视帧的共视帧中去扩展),同理也对合并匹配帧去获取其共视帧的集合。(2)、然后对当前关键帧窗口内的关键帧采用和回环纠正第一步的Sim(3)变换一样的操作实现初步的位姿矫正以便于后续的全局优化的迭代收敛。(3)、执行地图合并操作,将当前帧相关的关键帧(spLocalWindowKFs变量里的当前这的局部窗口内的帧的集合,包括前后一段时间内的连续关键帧和共视关键帧等)合并到融合的地图里pMergeMap->AddKeyFrame(pKFi),并从当前的地图里移除对应的关键帧pCurrentMap->EraseKeyFrame(pKFi)。地图点的合并过程类似。然后Atlas地图集管理将融合地图设为当前地图(表示当前系统已经实现了地图跳转),并将当前地图标记为失效地图;(4)、将当前关键帧的父关键帧设为合并匹配关键帧,并逆序当前关键帧的父子关系链条;(5)、在合并后有些地图点存在着重复(比如合并匹配帧及附近的共视关键帧和当前关键帧及附近的共视关键帧有些地图点是重复的),通过sim(3)变换后去搜索这些重复的地图点并剔除冗余(调用SearchAndFuse(vCorrectedSim3, vpCheckFuseMapPoint)函数),针对每一个当前关键帧邻近窗口内的关键帧集合和融合关键帧相连接的关键帧集合的每一个关键帧调用UpdateConnections更新相关连接信息;(6)调用函数Optimizer::LocalBundleAdjustment(mpCurrentKF, vpLocalCurrentWindowKFs, vpMergeConnectedKFs,&bStop)进行局部优化,其中vpMergeConnectedKFs参数中的关键帧的位姿为fixed不参与优化(与优化的关键帧共享的地图点会参与优化,但也会受约束于固定的位姿,这给优化的过程提供了基准参考,防止优化出现漂移现象)。(7)对当前地图的没有融合到pMergeMap部分的关键帧同样采用Sim(3)矫正变换以保证整个地图的一致性,并再一次通过加锁机制实现地图的合并操作(相关关键帧和地图点在地图集里的关系更新,在代码的实现上和第三步的合并操作一样)(8)调用全局线束优化,对合并后的当前地图的所有关键帧位姿和地图点集合进行联合优化,以消除较长时间运行的累积误差,在优化前会让局部建图器停止工作。 问题3:地图合并的时候,将当前地图设置为失效,下次合并的时候也可能会再次变为当前地图对吗? 回答:是的,在 ORB-SLAM3 中,地图合并时将当前地图设置为“失效”(即 SetMapBad()),目的是标记当前地图已经不再使用,并且可能在之后的合并过程中被再次激活作为当前地图。 问题4:由于在地图合并的时候存在着关键帧父子关系链的调整,会出现两个子问题:(1)、将当前关键帧的父关键这设为融合匹配关键帧,并调整当前关键帧的父子关键帧链条上的顺序结构,则之前的融合匹配关键帧的子关键帧链条如何维护?(2)、关键帧之间的父子关系链和关键帧之间的前后顺序双向链表定义是不同的数据结构意义,双向链表主要体现在时间上的先后顺序,父子关系主要体现在共视的重合程度吗? 回答:(1)、在支持多地图的合并场景下,地图的合并将会形成在合并点出现树的分叉结构(每次融合可以理解将融合关键帧的现有子关键帧链保持,同时添加一个当前关键帧的子关键帧链条),这种分叉结构可以保证父子关键帧关系图不会出现闭环形结构。(2)、父子关键帧在一般情况下保持时序(一般在mbFirstConnection为true时建立父子关系),且主要体现了关键帧之间的共视重合度,但在上述的合并地图的场景下,为了优化地图结构,会调整关键帧之间的父子关系结构,此时部分父子关系链上的结构将不再保证时序关系。关键帧里的双向链表结构确实保持了关键帧的时序关系。可以方便获取当前帧在时间序列上的前一个和后一个关键帧。 问题5:地图合并时由于每个地图的坐标系独立,即一般在跟踪失效的时候会创建新地图,新地图的初始关键帧的坐标为标准的单位矩阵?在两个地图的融合时坐标如何对齐?和回环检测和纠正时的处理有什么具体的差异? 回答:一般在回环检测和矫正以及地图合并检测和合并的处理过程中,会以先前的回环匹配帧或合并匹配帧作为基准(后面经过了一段时间的累积出现了累积的漂移误差),基于两个匹配帧的变换将当前帧及当前地图的所有帧都首先进行Sim(3)的变换进行初步纠正,这个变换逻辑是一样的。 References