Category: Software Engineering Practices

  • SLAM系列之ORB-SLAM3开源系统代码解析-mono(+imu)tracking

    这篇文章将向大家介绍orbslam3开源系统中的单目(+惯导)跟踪算法模块的实现逻辑分析。 跟踪模块在orbslam3中作为计算频率最高的算法模块,在实现中也作为程序的主线程来运行,其他的模块如局部建图(LocalMapping),回环检测(LoopClosing)以及可视化模块(Viewer)均以其他背景线程来并发运行。跟踪模块的主要作用为相机的运动模型(位姿)的实时跟踪和优化。 跟踪模块作为整个系统的入口,其接收从感知传感器捕获的环境数据,如图像数据,IMU,Lidar等,这里将以单目(+imu)场景,且以跟踪+定位(也有只跟踪的模式)的模式为例来进行分析介绍。 一、接收图像并进行处理的函数调用路径(以mono_euroc的example为例)为mono_euroc.cc文件中的SLAM.TrackMonocular(im,tframe)->system.cc文件中的mpTracker->GrabImageMonocular(imToFeed,timestamp,filename)->tracking.cc文件中的GrabImageMonocular函数将图像先转换成灰度图,然后构建Frame类的对象,最后调用Track()函数进行跟踪。这里对Frame构造数据帧对象做一下补充说明:Frame类支持多态的构造函数以支持多个应用场景,如单目,双目,深度相机等,在构造函数里首先直接在畸变的图像上进行ORB特征点和特征描述符的计算提取,然后对特征点进行去畸变的处理。 二、跟踪过程初始化:跟踪过程先判断当前的跟踪状态,跟踪的状态候选有[NOT_INITIALIZED, LOST, RECENTLY_LOST, OK],当判断到系统当前跟踪状态还尚未初始化时(状态为NOT_INITIALIZED),则首先进行单目的初始化工作,其实现函数为MonocularInitialization,初始化的状态由mbReadyToInitializate变量来表示,系统启动时为false,其状态的判别和转换的逻辑详见[1],完整的初始化过程需要两个间隔较短的图像帧且两帧的特征点的数量都超过了设定的阈值。当满足这样的条件时,根据这前后临近的两帧的匹配特征点集合基于对极几何和三角化测量方法(ReconstructWithTwoViews)去初始化相机位姿和地图点坐标,如果成功初始化相机位姿,则调用函数CreateInitialMapMonocular创建初始地图,将初始帧和当前帧作为关键帧加入到当前地图里,同时更新对应的地图点和关键帧之间的关联关系,并进行全局的BA优化以进一步优化相机初始位姿。 三、当跟踪状态为OK时,如果运动模型不可用,则调用TrackReferenceKeyFrame,如果运动模型可用,则先调用TrackWithMotionModel,如果用运动模型跟踪不成功,则回退到用TrackReferenceKeyFrame进行跟踪。其中: (1)、TrackReferenceKeyFrame将当前帧和参考关键帧(临近创建的关键帧)进行特征点匹配(如果匹配的特征点不够,则返回跟踪失败,需要进行重定位),并基于此通过Optimizer的PoseOptimization函数对当前帧进行位姿优化;(2)、TrackWithMotionModel根据运动模型预测当前的相机位姿,并将当前帧和上一帧进行投影搜索,如果匹配点过少,将加大阈值再次将当前帧和上一帧进行投影搜索,满足匹配条件则跟踪成功调用PoseOptimization函数进行优化,否则如果有IMU辅助预测,直接返回true,如果既搜索失败又没有IMU辅助预测,则返回false。TrackReferenceKeyFrame和TrackWithMotionModel分别基于参考关键帧和运动模型对当前帧的特征点进行匹配,并得到和参考关键帧或上一帧所匹配的特征点对应的地图点,Optimizer的PoseOptimization函数主要采用了前面帧得出的地图点世界坐标和对应的当前帧关键点的运动映射所建立的最小化重投影误差模型对相机的位姿和地图点进行联合优化,其具体采用了李代数和g2o的图优化方法,具体的介绍见后续的相关博文。 四、当跟踪的状态为RECENTLY_LOST,如果有IMU,则用IMU进行状态的辅助预测,保持状态为RECENTLY_LOST不变;否则调用Relocalization函数进行重定位,如果重定位成功,则状态转换为OK,否则状态转换为LOST。这里对重定位函数的实现也做一下说明,其主要基于当前帧从地图里查找候选关键帧(调用mpKeyFrameDB->DetectRelocalizationCandidates去检测),然后基于每个候选帧分别和当前帧进行位姿优化(先采用的MLPnPSolver的优化求解方法,实时速度快,只优化相机位姿,找到匹配较优秀的结果作为初始值,和PoseOptimization相结合去进行进一步的更高精确度的位姿优化,具体的实现逻辑参考[2]。 五、当跟踪的状态为LOST,则重新创建新的地图,开始进入新的跟踪初始化状态。 六、当前帧本身的跟踪任务完成后,接下来需要调用TrackLocalMap对局部地图进行跟踪和相关的位姿和地图点的优化,在上一篇文章里也介绍过,局部地图没有专门的封装类,其主要的信息数据在Tracking类的两个变量中,分别为mvpLocalKeyFrames和mvpLocalMapPoints,在实现内部,其通过UpdateLocalMap函数的调用实现了局部关键帧和局部地图点的更新(只对当前的参考帧的匹配的地图点作为约束,约束量偏少,在更大的局部地图点中去搜索更多的匹配点,从而有更多的地图点加入到重投影误差的计算中来,对当前帧的位姿优化也更加准确),然后通过函数SearchLocalPoints去搜索匹配和当前帧在视锥体范围内的匹配的特征点,并基于这些特征点和地图点以及相机位姿等信息进行相机位姿优化。 七、接下来需要通过函数NeedNewKeyFrame来判断是否需要加入关键帧,根据多种条件,比如时间,比如有足够多的新的“接近点”等等,如果需要创建关键帧则调用CreateNewKeyFrame函数来创建关键帧,并将关键帧插入到局部建图器以便进行局部建图的工作。其中局部建图器的大体作用有:1、从尚未处理的关键帧队列里取出关键帧,对关键帧的地图点相关信息进行更新;2、更新关键帧的共视关键帧的图连接;3、将关键帧加入到地图集的当前地图中。这些具体的内容将会在局部建图器的文章中专门介绍。 问题:TrackLocalMap也是通过地图点和特征点的重投影误差采用的Optimizer的PoseOptimization函数进行的相机位姿和地图点的联合优化,为什么在当前帧的跟踪后面还要加上这个过程,其利用了更多的关键帧搜索地图点,因此优化会更准确些吗?回答:在 跟踪阶段,位姿优化依赖当前帧与前一帧(或参考关键帧)之间的特征点匹配关系,但 TrackLocalMap 可以通过当前帧的地图点去搜索局部的多个共视关键帧,查找更多的匹配的地图点。以从更多的视角来优化进一步提升定位精度和地图精度,使得SLAM系统在面对动态场景或挑战性环境时更加稳健。 References

  • SLAM系列之ORB-SLAM3开源系统代码解析-基本数据结构简介

    感谢读者的关注,近期准备向大家介绍ORB-SLAM3的源码解读,准备主要从基本的数据结构、跟踪模块、局部建图模块、回环检测和回环纠正模块、可视化模块以及系统概览这些主题内容分别向大家介绍。欢迎大家的反馈和意见建议以便纠错和补充更新。 这篇短文将向大家介绍ORBSLAM3的基本关键的数据及相关计算的抽象,具体表现为以下几个有代表性的类, 帧(Frame),图像帧数据结构,是视觉slam中基本的传感器单元数据,在单目场景下,一帧的数据为camera某个时间戳对应的图像数据。帧的类中还包含对图像进行ORBExtractor所获取的特征点,特征描述符以及BOW(bag of words,将特征描述符向量根据k叉树结构的词汇表模型进行遍历搜索获得,便于后面的回环检测算法的需要)。帧的类实现中还包括保存优化后当前帧对应的相机的位姿(mTcw),在优化的过程中还有关键点对应的地图点(成员mvpMapPoints,见MapPoint部分的介绍),帧中也维护了camera的内参信息,以及如果是双目立体视觉场景,一帧的数据中还包含右侧camera的图像数据,以及对应的特征点等相关数据。 关键帧(KeyFrame),并不是所有的帧都作为关键帧(这里借鉴了视频压缩里类似的概念,由于camera的帧率一般较高,进行局部和全局相机位姿和地图点优化时一般以关键帧的数据为优化对象),在跟踪逻辑里,会检查是否需要将当前帧作为关键帧的逻辑,比如根据时间和跟踪效果等(具体在跟踪逻辑在NeedNewKeyFrame函数里进行的实现[1])。关键帧主要用于局部地图位姿优化和全局位姿优化,也是构成地图数据的基本单元。keyframe的类实现中也对共视关键帧进行了维护,通过地图点结构就可以得出共视的关键帧,具体的函数实现为UpdateConnections函数,其根据和当前关键帧共视地图点的数量对共视的关键帧进行排序,其中变量mvpOrderedConnectedKeyFrames按和当前关键帧共视的地图点数量的多少将共视关键帧进行排序存放到数组里,mvOrderedWeights存放对应的共视的地图点的权重(数量),mConnectedKeyFrameWeights记录共视关键帧和共视地图点数量的映射关系(以STL中的字典map容器实现)。并根据需要建立关键帧之间的父子关系(父子关键帧通常共享大量的地图点,如果当前关键帧为第一次建立连接且不是地图的初始关键帧则选择排序中的第一个共视关键帧为父关键帧),关键帧按照先后顺序构建成双向的链表,CreateNewKeyFrame的函数里有对这个双向链表进行维护的操作。 关键帧数据库(KeyFrameDatabase),存放了关键帧的集合,并且实现了基于BOW等信息的关键帧查找的一系列方法函数供相关模块使用,比如在回环检测模块,提供了基于根据当前帧查找相似度较大的回环匹配帧候选集合和合并关键帧候选集合等。 地图点(MapPoint),地图点为物理世界中的具体位置点的抽象,包括地图点的世界坐标,观测到该地图点的关键帧集合(mObservations),以及对应的每个关键帧图像中的哪个特征点和该地图点相关联等信息(双目的时候分左右camera的特征点的关联索引)。 地图(Map),地图管理的类里包含了关键帧集合(mspKeyFrames)和地图点的集合(mspMapPoints)[2],在orbslam3中没有专门的类用来抽象局部地图,局部地图的相关数据的维护和更新体现在tracking模块(代码见tracking.cc)的两个相关的成员变量:局部关键帧(mvpLocalKeyFrames)和局部地图点(mvpLocalMapPoints)中,通过TrackLocalMap()函数根据当前帧进行更新和维护[3]。 地图集(Atlas),多个地图的集合(mspMaps),地图集会维护多个map,当系统初始化的时候,或者跟踪出现了LOST状态时,一般也会创建新的map,会生成新的地图并加入到地图集中。关于地图的合并,将在回环检测和全局优化的部分做专门介绍。 ORBVocabulary,为ORB特征的词汇表,其结构为一个KD树(k-dimensional tree),基于大量的多样的场景数据(如KITTI,EUROC,TUM RGB-D等)经过层次聚类而生成,这个词典是离线已经构建好的,直接加载使用就可以。 References

  • 关于知识分享和工程技术实践的一点经验

    1、PPT制作需要兼顾简洁和细致,风格要基本一致(title以及整体样式格式等),逻辑流程清晰,内容相对完整;有动画演示的页面要先预播一下确认一下播放的顺序和效果,看上去要顺畅协调自然。 2、实验环节要多动手,保证全过程要完整的验证,有些问题在实验的某个环节看似走通了,其实在后续的环境还会出现其他的问题,多次尝试后可能会发现在前面那个看似走通的过程中的解决方法只是暂时的,并不能完整的解决问题。引起问题的原因在于其他地方,找到了问题的原因后整个过程才能顺利解决。所以要完整的验证实验整个环节过程。 3、及时将过程总结成文档,勤于文档撰写,markdown格式的文档编写比较方便,将实验中的一些细节,如软件开发版本,代码中出现的bug及修正,实验的流程以及注意的地方及时记录下来,以防止忘记; 4、同理一些新的技能,新的技巧trick,和自己的新想法以及TODO等内容也要勤于记录; 5、不断学习积累,工程经验也只有在实践中加以锻炼,孰能手巧,重复+接受新知识才能不断进步;科技进步发展很快,终身学习时代持续的学习和知识积累才能不断进步,不落后于时代。

  • Linux常用CLI命令使用方法介绍

    linux作为服务器的常用的系统,其稳定性已经得到较为普遍的认可,在我们开发和系统运维的过程中,经常会用到相关的CLI命令程序(Command Line Interface,因为用linux系统作为服务器,如ubuntu系统,经常会裁剪掉其图形用户窗口的功能,工程师一般都使用过CLI去和系统进行交互)。 第一个向大家介绍的命令为“netstat -tulnp”,netstat是一个网络工具,用以显示当前网络相关的软件和系统网络相关硬件的信息,参数tulnp是个累积的量,t表示显示当前系统所有的tcp sockets(tcp连接),u表示udp连接,n表示显示数字形式的地址和端口,l表示当前监听的端口(一般服务程序启动后会在特定端口监听远程客户端前来访问的连接请求)p代表网络相关的进程。如安装(可以通过sudo apt install mysql-server命令进行安装)并启动(可以通过sudo systemctl start mysql命令进行启动)了mysql服务器后执行命令后会显示mysql进程相关的信息。有时提示需要系统用户才能显示相关信息时,可以在命令前面加上sudo。执行后如下: 如果只想看mysql服务相关的信息,只需执行sudo netstat -tulnp | grep mysql命令,在linux命令中 ‘|’ 可以理解为pipeline模式,前面的命令作为后面命令的输入。而grep在linux中是一个强大的文本搜索匹配工具,可以将满足条件的文本行输出到终端屏幕上。 第二个向大家介绍的命令示例在前面的基础上综合命令awk(过滤提取,和print结合着用,后面$符号后面跟着个数字表示第几列信息),grep以及xargs(将过滤提取的数据作为参数传递给命令行)的使用。比如在你的机器上,docker的本地镜像有点多,或者你想批处理一次性删除满足某些条件的镜像,可以用如下的命令示例来参考: 以上命令的含义为找到含有keyword相关字眼的docker镜像,并且过滤出第三个参数(即为docker镜像的ID),然后将满足这些条件的docker镜像一次批处理进行删除。 这篇文档后面将持续更新,将常用的命令行汇总一下,以备后需查阅。

  • 用manim库开发算法动画演示程序

    数据结构和算法大都涉及程序的高效执行,算法过程通过良好的算法流程的设计,使得具有较好的时间和空间复杂度。但是这些流程通过文本的代码来表达理解起来比较抽象,如果能够通过动态的可视化过程来演示算法的执行的步骤,对于学习算法的同学,特别是初学者,将会能够对算法的思想的理解更加的具象,对于算法思想的理解的也会更加深入彻底。今天将向大家介绍通过用一些工具和开发库,通过编写动画演示程序来方便对算法的执行流程进行可视化的外在表达。以便于算法程序的教学和理解。 今天向大家介绍通过manim和pygame两个python开发库实现交互式动画展示,以便于教学中可以用算法动态可视化过程来辅以教学过程中讲解,使得算法的讲解过程更加具体生动,可理解性大大增强。很多时候知识的难度是一个方面,如果辅以更加可理解的解释方法和途径,知识的可理解的难度也会大大降低。这里先分享一个自己借助大模型的代码生成功能辅助实现的快速排序演示动画的原型版本(代码还在整理和更新中)。由于生成视频动画需要用到流媒体程序ffmpeg,在windows下需要下载ffmpeg程序包(见参考引文1)然后将其对应的bin目录放到环境变量Path中。执行程序生成动画视频的命令行为: 对应的脚本代码请参考引文2的链接。生成的视频的目录在相对代码为根目录的子目录\media\videos\quick_sort_manim\480p15下面,如果要控制动画的播放进度,可以通过pygame相关程序去播放视频,并且通过鼠标和键盘事件控制播放或暂停(参考引文3的链接)。最后生成的动画效果请参考引文4链接。后续有进一步的心得体会会将持续更新相关文字说明或代码。欢迎读者提供更多的更好的方法和途径来通过动画展示计算机执行算法或特殊功能函数的过程。 References

  • AIGC体验7-Stable Diffusion使用技巧之ControlNet模型的使用2

    上篇短文向大家部分的介绍了ControlNet的使用示例,在Stable Diffusion的使用过程中,基础模型对于图像生成的质量的影响是挺关键的。在上篇介绍的短文中,我们使用了v1-5-pruned-emaonly这个模型,而在本文的示例中,我们将选择AnythingV5V3_v5PrtRE这个模型,从使用体验来说,该模型在生成人物方面的性能要明显优于v1-5-pruned-emaonly模型。相关的模型链接可以参考引文。 ControlNet还有更多的用法,读者可以进一步自行去尝试和积累经验,关于提示词的写法等还有不少的trick去在实践中模型和进一步总结,后续有相关的心得再去继续在这里分享。

  • AIGC体验6-Stable Diffusion使用技巧之ControlNet模型的使用1

    在文生图的应用中,用户经常会对生成的图形有特殊的要求,如局部重绘,满足边缘或深度条件,模糊变清晰,黑白变彩色,人体特定姿势(pose)等等。这篇短文将向大家介绍基于Stable Diffusion的ControlNet实现图像可控生成的几个示例,更加完整的介绍可以参考相关文献(这篇短文基于stable diffusion 1.5版本,具体可以使用引文3中给出的notebook)。 现有的stable diffusion的基础模型在人物生成上效果不是特别好,这篇短文暂时没有演示可控人物生成的效果,更多的场景会在后面的短文中进一步补充,欢迎大家关注,并提出意见建议。 References

  • AIGC体验5-Stable Diffusion使用技巧之LoRA模型的使用

    Stable Diffusion(SD)是开源的文生图大模型,随着相关技术的发展,已经迭代了多个版本(关于版本间不同的特性可以搜索查询,这里以1.5版本为例进行介绍,如和其兼容的lora模型比较多),并且其支持多种可控图像生成技巧,这篇短文将要向大家介绍相关的Stable Diffusion的使用技巧,不足支持欢迎读者提出意见建议并予以补充。 首先我们采用webui的方式使用比较便捷,参考文献1即为我们的Stable Diffusion V2.1版本的webui的jupyter notebook代码链接。具体使用可以在本地或colab里边。这篇短文将介绍SD的一般用法和基于LoRA模型的风格化用法。 一、一般用法:这里没有特别要强调说明的地方,主要是要写好提示词。这里举一个示例,提示词为:Two yellow orioles sing amid the green willows. 效果图为: 二、LoRA模型,lora模型是一种小模型技术,其显著的优点表现在:1、LoRA 通过低秩适应方法在较少的参数增加下微调大模型,资源消耗较少。2、在特定任务或小数据集上快速适应大模型,提升生成质量或特定任务的表现。3、LoRA模型可以作为额外模块加载到现有大模型中,灵活性高。使用LoRA模型的方法如下,1、首先,下载LoRA模型,如参考文献1所示;2、将下载的模型拷贝到lora的模型目录,如/content/stable-diffusion-webui/models/Lora,然后更新webui加载最新加入的lora模型(设置扫描lora模型路径),提示词为:Two yellow orioles sing amid the green willows. SONG DYNASTY FLOWER AND BIRD PAINTING如下图所示。 其中该界面的操作流程为:settings–>additional networks–>Extra paths to scan for LoRA models(设置为/content/stable-diffusion-webui/models/Lora)–>apply settings–>Reload UI。在上面的图中,我们用到了一个LoRA模型,具体可以参考引文1。 下一篇相关文章将介绍ControlNet的相关用法,欢迎读者继续关注,提出问题和意见建议。 References

  • AIGC体验4-midjourney中人物一致性的实现

    在一般的电影和短视频中,一般的都存在的多个镜头(storyboard),为了保证镜头切换的时候的人物的一致性,需要文生图模型对此有较好的支持,否则在文生图的自动生成的场景中就会存在着不满足分镜头设计的需求,大模型的可用性就会受到交大影响。 今天向大家介绍midjourney中怎么实现人物一致性,主要通过一个实例向大家做演示介绍。 首先,我们通过提示词生成一个人物的不同角度的4张图片,提示词如下: 根据上述提示词生成的图片效果为: 将图像保存到本地,然后通过工具裁剪成4个小的图片,然后点击下图中左下脚的“+”号按钮在弹出的菜单中选择“上传文件”,将上面裁剪的4个图片上传后,将其链接保存下来(通过点击放大每个图片然后鼠标右键图片上方获取图片链接,供四个链接)。 然后用/prefer option set命令来进行设置风格一致性,具体的指令形如下面截图: 然后,就可以指定以该”littleboy”的option来生成人物一致性的场景了。具体的示例指令为: 最终的效果为: 后面有机会将继续向大家介绍关于图像视频多媒体方面的大模型应用的一些技巧。欢迎感兴趣的读者关注并提出问题一起来商议探讨共同提高。

  • AIGC体验3-用Fliki+大模型制作短视频

    前面两篇短文向大家简要介绍了midjourney来进行绘图,今天向大家简要介绍一个支持在线的短视频制作的工具Fliki,不足之处欢迎大家批评指正并予以补充。 Fliki的官网为Fliki – Turn text into videos with AI voices, 其主要是根据故事分镜头来制作短视频,分镜头(storyboard)是影视制作中的术语,大概是一个连续的镜头来描述一个场景(如长镜头,宽镜头,远镜头等)。这方面经验也不是很足,以后有机会再补充相关细节。 首先我们用大模型(ChatGPT,通义千问,文心一言等)生成故事的分镜头脚本描述,也可以自己去描写。这里我们以登鹳雀楼为例,自己写的一个简单的分镜头场景描述来进行说明(暂时没有用到大模型来生成场景描述),用的是通义万象来生活场景图。 基于上述场景文字生成的图片后,我们就可以再Fliki中去生成这首小诗的短视频了,具体方法为下面的截图演示: 有必要对语音进行优化,可以考虑借助第三方语音合成平台生成整个的语音放到common sense的场景中去(最上方)。然后在每个分场景中去掉voiceover的layer。基本设置好后,就可以export然后就download了(注:为了去除水印,需要upgrade成会员) 更多的经验会在后续继续向大家介绍。 References