《视觉SLAM十四讲》-- 后端 2
文章目录
- 09 后端 2
- 9.1 滑动窗口滤波和优化
- 9.1.1 实际环境下的 BA 结构
- 9.1.2 滑动窗口法
- 9.2 位姿图
- 9.2.1 位姿图的意义
- 9.2.2 位姿图优化
09 后端 2
9.1 滑动窗口滤波和优化
9.1.1 实际环境下的 BA 结构
由于计算机算力的限制,我们必须控制 BA 的规模,一种简单的思路是仅保留离当前时刻最近的 N N N 个关键帧,去掉时间上更早的关键帧。于是,BA 被固定在一个时间窗口内,离开这个窗口的即被抛弃,称为 滑动窗口法。
或者像 ORB-SLAM 2 那样,定义一种称为 共视图 的结构,即与当前相机存在共同观测的关键帧构成的图。在 BA 优化时,按照某些原则在共视图内取一些关键帧和路标进行优化。

9.1.2 滑动窗口法
(1)现在考虑一个滑动窗口,假设窗口内有 N N N 个关键帧,他们的位姿表达为(李代数形式):
x 1 , x 2 , . . . , x N \boldsymbol{x}_1, \boldsymbol{x}_2,...,\boldsymbol{x}_N x1,x2,...,xN
假设这个滑动窗口中还有 M M M 个路标点 y 1 , y 2 , . . . , y M \boldsymbol{y}_1, \boldsymbol{y}_2,...,\boldsymbol{y}_M y1,y2,...,yM,用上一讲中的 BA 方法来处理这个滑动窗口,包括建立图优化模型,构建海森矩阵,在边缘化所有路标点来加速求解。边缘化时,考虑关键帧的位姿:
[ x 1 , … , x N ] T ∼ N ( [ μ 1 , … , μ N ] T , Σ ) (9-1) \left[\boldsymbol{x}_{1}, \ldots, \boldsymbol{x}_{N}\right]^{\mathrm{T}} \sim N\left(\left[\boldsymbol{\mu}_{1}, \ldots, \boldsymbol{\mu}_{N}\right]^{\mathrm{T}}, \boldsymbol{\Sigma}\right) \tag{9-1} [x1,…,xN]T∼N([μ1,…,μN]T,Σ)(9-1)
其中 μ k \boldsymbol{\mu}_k μk 为第 k k k 个关键帧的位姿均值, Σ \boldsymbol{\Sigma} Σ 为所有关键帧的协方差矩阵。显然,均值部分就是 BA 迭代之后的结果, Σ \boldsymbol{\Sigma} Σ 是对整个 BA 的 H \boldsymbol{H} H 矩阵进行边缘化之后的结果。
(2)当窗口结构改变时:
① 先在窗口中新增一个关键帧,以及观测到的路标点;
② 把窗口中一个旧的关键帧删除,可能会删除他观测到的路标点。
- 新增一个关键帧和路标点
将新的关键帧 x N + 1 \boldsymbol{x}_{N+1} xN+1 按照正常的 BA 流程处理即可。
- 删除一个旧的关键帧
删除旧的关键帧时,就比较麻烦。比如删除 x 1 \boldsymbol{x}_1 x1 ,但 x 1 \boldsymbol{x}_1 x1 并不是孤立的,它会和其它帧观测到同样的路标,将 x 1 \boldsymbol{x}_1 x1 边缘化后将导致整个问题不再稀疏(破坏了路标部分的对角块结构)。

(3)滑动窗口法适合 VO 系统,而不适合大规模建图的系统。
9.2 位姿图
9.2.1 位姿图的意义
(1)随着时间的流逝,机器人的运动轨迹会越来越长,地图规模也会越来越大,BA 的计算效率就会下降。同时我们发现,经过若干次迭代后,收敛的特征点位置变化很小,发散的外点则已被剔除,因此在后续优化中没有必要再将收敛点考虑进来,而是只把他们当做位姿估计的约束。
(2)放开思路,我们完全可以构建一个只有轨迹的图优化,而位姿节点的边,可以由两个关键帧之间通过特征匹配之后得到的运动估计来给定初始值。一旦初始估计完成,就不再优化那些路标点的位置,而只关心相机位姿之间的联系。这样的方式,省去了大量特征点优化的计算,只保留了关键帧的轨迹,构建了所谓的 位姿图。

通过舍弃对路标点的优化,提高计算效率。
9.2.2 位姿图优化
位姿图中的节点表示相机位姿,用 T 1 , T 2 , . . . , T n , \boldsymbol{T}_1,\boldsymbol{T}_2,...,\boldsymbol{T}_n, T1,T2,...,Tn, 表示,边则是两个位姿节点之间相对运动的估计,这个估计可以通过特征点法或直接法得到。假设我们估计了 T i \boldsymbol{T}_i Ti 和 T j \boldsymbol{T}_j Tj 之间的相对运动 T i j \boldsymbol{T}_{ij} Tij,则有
T i T i j = T j \boldsymbol{T}_i \boldsymbol{T}_{ij}=\boldsymbol{T}_j TiTij=Tj
也即
T i j = T i − 1 T j (9-2) \boldsymbol{T}_{ij}=\boldsymbol{T}_i^{-1}\boldsymbol{T}_j \tag{9-2} Tij=Ti−1Tj(9-2)
写成李代数形式
ξ i j = ξ i − 1 ∘ ξ j = ln ( T i − 1 T j ) ∨ (9-3) \boldsymbol{\xi}_{i j}=\boldsymbol{\xi}_{i}^{-1} \circ \boldsymbol{\xi}_{j}=\ln \left(\boldsymbol{T}_{i}^{-1} \boldsymbol{T}_{j}\right)^{\vee} \tag{9-3} ξij=ξi−1∘ξj=ln(Ti−1Tj)∨(9-3)
将式(9-2)中的 Δ T i j \Delta \boldsymbol{T}_{ij} ΔTij 移至方程右侧,则
T i j − 1 T i − 1 T j = I (9-4) \boldsymbol{T}_{ij}^{-1}\boldsymbol{T}_i^{-1}\boldsymbol{T}_j=\boldsymbol{I} \tag{9-4} Tij−1Ti−1Tj=I(9-4)
但实际上,并不可能完全相等。定义
e i j = ln ( T i j − 1 T i − 1 T j ) ∨ (9-5) \boldsymbol{e}_{ij}=\ln( \boldsymbol{T}_{ij}^{-1}\boldsymbol{T}_i^{-1}\boldsymbol{T}_j)^{\vee} \tag{9-5} eij=ln(Tij−1Ti−1Tj)∨(9-5)
我们需要优化的是 T i \boldsymbol{T}_i Ti 和 T j \boldsymbol{T}_j Tj,也即 ξ i \boldsymbol{\xi}_i ξi 和 ξ j \boldsymbol{\xi}_j ξj,因此需要求这两个变量关于 e i j \boldsymbol{e}_{ij} eij 的导数。分别左乘一个左扰动: δ ξ i \boldsymbol{\delta \xi}_i δξi 和 δ ξ j \boldsymbol{\delta \xi}_j δξj
e ^ i j = ln ( T i j − 1 T i − 1 exp ( ( − δ ξ i ) ∧ exp ( ( δ ξ j ) ∧ T j ) ∨ (9-6) \boldsymbol{\hat{e}}_{ij}=\ln( \boldsymbol{T}_{ij}^{-1}\boldsymbol{T}_i^{-1} \exp((-\boldsymbol{\delta \xi}_i)^{\wedge}\exp((\boldsymbol{\delta \xi}_j)^{\wedge} \boldsymbol{T}_j)^{\vee} \tag{9-6} e^ij=ln(Tij−1Ti−1exp((−δξi)∧exp((δξj)∧Tj)∨(9-6)
根据伴随矩阵的性质:
exp ( ( Ad ( T ) ξ ) ∧ ) = T exp ( ξ ∧ ) T − 1 (9-7) \exp \left((\operatorname{Ad}(\boldsymbol{T}) \boldsymbol{\xi})^{\wedge}\right)=\boldsymbol{T} \exp \left(\boldsymbol{\xi}^{\wedge}\right) \boldsymbol{T}^{-1} \tag{9-7} exp((Ad(T)ξ)∧)=Texp(ξ∧)T−1(9-7)
稍作改变(把 Ad ( T ) \operatorname{Ad}(\boldsymbol{T}) Ad(T) 移到右侧,$ \boldsymbol{T}^{-1}$ 移到左侧)
exp ( ξ ∧ ) T = T exp ( ( Ad ( T − 1 ) ξ ) ∧ ) (9-9) \exp \left(\boldsymbol{\xi}^{\wedge}\right) \boldsymbol{T}=\boldsymbol{T} \exp \left(\left(\operatorname{Ad}\left(\boldsymbol{T}^{-1}\right) \boldsymbol{\xi}\right)^{\wedge}\right) \tag{9-9} exp(ξ∧)T=Texp((Ad(T−1)ξ)∧)(9-9)
那么,式(9-6)可写为(从右往左化简)
e ^ i j = ln ( T i j − 1 T i − 1 exp ( ( − δ ξ i ) ∧ ) exp ( δ ξ j ∧ ) T j ‾ ) ∨ = ln ( T i j − 1 T i − 1 exp ( ( − δ ξ i ) ∧ ) T j ‾ exp ( ( Ad ( T j − 1 ) δ ξ j ) ∧ ) ) ∨ = ln ( T i j − 1 T i − 1 T j exp ( ( − Ad ( T j − 1 ) δ ξ i ) ∧ ) exp ( ( Ad ( T j − 1 ) δ ξ j ) ∧ ) ) ∨ ≈ ln ( T i j − 1 T i − 1 T j [ I − ( Ad ( T j − 1 ) δ ξ i ) ∧ + ( Ad ( T j − 1 ) δ ξ j ) ∧ ] ) ∨ ≈ e i j + ∂ e i j ∂ δ ξ i δ ξ i + ∂ e i j ∂ δ ξ j δ ξ j (9-10) \begin{aligned} \hat{\boldsymbol{e}}_{i j} &=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \exp \left(\left(-\boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \underline{\exp \left(\delta \boldsymbol{\xi}_{j}^{\wedge}\right) \boldsymbol{T}_{j}}\right)^{\vee} \\ &=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \underline{\exp \left(\left(-\boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \boldsymbol{T}_{j}} \exp \left(\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{j}\right)^{\wedge}\right)\right)^{\vee} \\ &=\ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \boldsymbol{T}_{j} \exp \left(\left(-\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}\right) \exp \left(\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{j}\right)^{\wedge}\right)\right)^{\vee} \\ & \approx \ln \left(\boldsymbol{T}_{i j}^{-1} \boldsymbol{T}_{i}^{-1} \boldsymbol{T}_{j}\left[\boldsymbol{I}-\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{i}\right)^{\wedge}+\left(\operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \boldsymbol{\delta} \boldsymbol{\xi}_{j}\right)^{\wedge}\right]\right)^{\vee} \\ & \approx \boldsymbol{e}_{i j}+\frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{i}} \boldsymbol{\delta} \boldsymbol{\xi}_{i}+\frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{j}} \boldsymbol{\delta} \boldsymbol{\xi}_{j} \end{aligned} \tag{9-10} e^ij=ln(Tij−1Ti−1exp((−δξi)∧)exp(δξj∧)Tj)∨=ln(Tij−1Ti−1exp((−δξi)∧)Tjexp((Ad(Tj−1)δξj)∧))∨=ln(Tij−1Ti−1Tjexp((−Ad(Tj−1)δξi)∧)exp((Ad(Tj−1)δξj)∧))∨≈ln(Tij−1Ti−1Tj[I−(Ad(Tj−1)δξi)∧+(Ad(Tj−1)δξj)∧])∨≈eij+∂δξi∂eijδξi+∂δξj∂eijδξj(9-10)
其中,第四步将两个指数一阶泰勒展开,相乘后舍去二次项;第四步到第五步则使用了 BCH 近似。
按照李代数上的求导法则,我们得到了误差关于两个位姿的雅克比矩阵,即
∂ e i j ∂ δ ξ i = − J r − 1 ( e i j ) Ad ( T j − 1 ) \frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{i}}=-\mathcal{J}_{r}^{-1}\left(\boldsymbol{e}_{i j}\right) \operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) ∂δξi∂eij=−Jr−1(eij)Ad(Tj−1)
∂ e i j ∂ δ ξ j = J r − 1 ( e i j ) Ad ( T j − 1 ) (9-11) \frac{\partial \boldsymbol{e}_{i j}}{\partial \boldsymbol{\delta} \boldsymbol{\xi}_{j}}=\mathcal{J}_{r}^{-1}\left(\boldsymbol{e}_{i j}\right) \operatorname{Ad}\left(\boldsymbol{T}_{j}^{-1}\right) \tag{9-11} ∂δξj∂eij=Jr−1(eij)Ad(Tj−1)(9-11)
J r \mathcal{J}_{r} Jr 的形式比较复杂,通常取它的近似,
J r − 1 ( e i j ) ≈ I + 1 2 [ ϕ e ∧ ρ e ∧ 0 ϕ e ∧ ] (9-12) \mathcal{J}_{r}^{-1}\left(e_{i j}\right) \approx \boldsymbol{I}+\frac{1}{2}\left[\begin{array}{cc} \phi_{e}^{\wedge} & \rho_{e}^{\wedge} \\ 0 & \phi_{e}^{\wedge} \end{array}\right] \tag{9-12} Jr−1(eij)≈I+21[ϕe∧0ρe∧ϕe∧](9-12)
了解雅克比求导后,剩下的部分就是普通的图优化。 记所有的边(也就是位姿)为 E \mathcal{E} E,则总体目标函数为
min 1 2 ∑ i , j ∈ E e i j T Σ i j − 1 e i j (9-13) \min \frac{1}{2} \sum_{i, j \in \mathcal{E}} \boldsymbol{e}_{i j}^{\mathrm{T}} \boldsymbol{\Sigma}_{i j}^{-1} \boldsymbol{e}_{i j} \tag{9-13} min21i,j∈E∑eijTΣij−1eij(9-13)
然后再用高斯牛顿法或 L-M 法优化求解。
相关文章:
《视觉SLAM十四讲》-- 后端 2
文章目录 09 后端 29.1 滑动窗口滤波和优化9.1.1 实际环境下的 BA 结构9.1.2 滑动窗口法 9.2 位姿图9.2.1 位姿图的意义9.2.2 位姿图优化 09 后端 2 9.1 滑动窗口滤波和优化 9.1.1 实际环境下的 BA 结构 由于计算机算力的限制,我们必须控制 BA 的规模,…...
安装插件时Vscode XHR Failed 报错ERR_CERT_AUTHORITY_INVALID
安装插件时Vscode XHR Failed 报错ERR_CERT_AUTHORITY_INVALID 今天用vscode 安装python插件时报XHR failed,无法拉取应用商城的数据, 报的错如下: ERR_CERT_AUTHORITY_INVALID 翻译过来就是证书有问题 找错误代码的方法: 打开vscode, 按F1…...
ON_WM_TIMER()
ON_WM_TIMER() static_cast: cannot convert from void (__cdecl CPop::* )(UINT) to void (__cdecl CWnd::* )(UINT_PTR) OnTimer(UINT nIDEvent) ----> OnTimer(UINT_PTR nIDEvent)...
【Unity】单例模式及游戏声音管理类应用
【Unity】单例模式及游戏声音管理类应用 描述 在日常游戏项目开发中,单例模式是一种常用的设计模式,它允许在应用程序的生命周期中只创建一个对象实例,并提供对该实例的全局访问点。通过使用单例模式,可以提高代码的可维护性和可…...
视频剪辑技巧:轻松搞定视频随机合并,一篇文章告知所有秘诀
在视频制作的过程中,视频随机合并是一种创新的剪辑手法,它打破了传统的线性剪辑模式,使得视频剪辑更加灵活和有趣。通过将不同的视频片段随机组合在一起,我们可以创造出独特的视觉效果和情感氛围。这种剪辑方式让观众在观看视频时…...
torch.stack
看网上看多没讲的不是很明白,我来试试空间上的理解 # 假设是时间步T1的输出 T1 torch.tensor([[1, 2, 3],[4, 5, 6],[7, 8, 9]]) # 假设是时间步T2的输出 T2 torch.tensor([[10, 20, 30],[40, 50, 60],[70, 80, 90]])输出: print(torch.stack((T1,T2…...
手搓哈希表、列表、队列,只为了用C语言快速求解华容道游戏,我不是大佬,只是一个游戏算法爱好者
背景 多年前曾经写过C语言求解华容道,当时没有用到哈希表,导致整个查重搜索数组过大,每次求解都得花上数分钟的时间,如今时过境迁,对数据结构和算法有了更深的理解,所以得把这一块补上了。(其实就是最近想…...
MySQL 的执行原理(一)
5.1 单表访问之索引合并 我们前边说过 MySQL 在一般情况下执行一个查询时最多只会用到单个二级 索引,但存在有特殊情况,在这些特殊情况下也可能在一个查询中使用到多个二 级索引,MySQL 中这种使用到多个索引来完成一次查询的执行方法称之为&…...
2023_“数维杯”问题B:棉秸秆热解的催化反应-详细解析含代码
题目翻译: 随着全球对可再生能源需求的不断增加,生物质能作为一种成熟的可再生能源得到了广泛的关注。棉花秸秆作为一种农业废弃物,因其丰富的纤维素、木质素等生物质成分而被视为重要的生物质资源。虽然棉花秸秆的热解可以产生各种形式的可…...
django理解01
接在Vue理解01后 项目创建 pycharm上下载django框架 在需要创建项目的文件夹终端django-admin startproject 项目名终端创建APPpython manage.py startapp app名注册APP,settings.py里INSTALLED_APPS下,增加一项:app名.apps.类名࿰…...
限制Domain Admin登录非域控服务器和用户计算机
限制Domain Admin管理员使用敏感管理员帐户(域或林中管理员组、域管理员组和企业管理员组中的成员帐户)登录到信任度较低的服务器和用户端计算机。 此限制可防止管理员通过登录到信任度较低的计算机来无意中增加凭据被盗的风险。 建议采用的策略 建议使用以下策略限制对信任度…...
原来机械硬盘比内存慢10万倍
我们都知道机械硬盘的速度很慢,内存的速度很快,那么不同存储器之间的差距到底有多大呢? 我们先来看一幅图: CPU访问寄存器的时间是0.3纳秒,访问L1高速缓存的时间是1纳秒,访问L2高速缓存的时间是4纳秒… 秒…...
ElementUI的Dialog弹窗实现拖拽移动功能
文章目录 1. ElementUI简介2. 弹窗基本使用3. 实现拖拽移动功能4. 拓展与分析 🎉欢迎来到Java学习路线专栏~ElementUI的Dialog弹窗实现拖拽移动功能 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒🍹✨博客主页:IT陈寒的博客🎈该系列文章专栏&a…...
生成式AI模型量化简明教程
在不断发展的人工智能领域,生成式AI无疑已成为创新的基石。 这些先进的模型,无论是用于创作艺术、生成文本还是增强医学成像,都以产生非常逼真和创造性的输出而闻名。 然而,生成式AI的力量是有代价的—模型大小和计算要求。 随着生…...
机器人制作开源方案 | 智能快递付件机器人
一、作品简介 作者:贺沅、聂开发、王兴文、石宇航、盛余庆 单位:黑龙江科技大学 指导老师:邵文冕、苑鹏涛 1. 项目背景 受新冠疫情的影响,大学校园内都采取封闭式管理来降低传染的风险,导致学生不能外出,…...
PostgreSQL技术大讲堂 - 第34讲:调优工具pgBagder部署
PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUG PG技术大讲堂。 第34讲&#…...
《Python日志新宠——Loguru,轻松记录,笑对Bug!》
嘿,程序媛和程序猿们!👩💻👨💻 你们是不是也曾为日志处理这个“小事”而头疼?别着急,今天我给你们介绍一个简直比拥抱泰迪熊还要温暖的Python库——Loguru!这货不仅强…...
NET8 ORM 使用AOT SqlSugar
.NET AOT8 基本上能够免强使用了, SqlSugar ORM也支持了CRUD 能在AOT下运行了 Nuget安装 SqlSugarCore 具体代码 StaticConfig.EnableAot true;//启用AOT 程序启动执行一次就好了//用SqlSugarClient每次都new,不要用单例模式 var db new SqlSugarClient(new ConnectionC…...
CCRC认证是什么?
什么是CCRC认证? 信息安全服务资质,是信息安全服务机构提供安全服务的一种资格,包括法律地位、资源状况、管理水平、技术能力等方面的要求。 信息安全服务资质(CCRC)是依据国家法律法规、国家标准、行业标准和技术规范…...
linux内核面试题(2)
整理了一些网上的linux驱动岗位相关面试题,如果错误,欢迎指正。 工作队列是运行在进程上下文,还是中断上下文?它的回调函数是否允许睡眠? 工作队列是运行在进程上下文的。工作队列的回调函数是允许睡眠的,…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
