VIO优化中不客观自由度 (gauge freedom) 的处理 (gauge handle)

news2024/12/28 4:33:20

文章目录

  • 1. 不可观的解释
  • 2. 几种不同的gauge handle处理方式
    • 2.1. free gauge方式
    • 2.2. fix gauge方式
    • 2.3. prior gauge方式
    • 2.4. g2o tutorial方式
  • 3.不同方式的协方差矩阵

1. 不可观的解释

这篇论文 中对VIO的4-DOF不可观的定义如下,可以看到这种不可观就是如果对最后求得的解进行一个四自由度的变换(类似单应变换),那么损失函数 J ( θ ) J(\theta) J(θ)并不会改变,所以这就是4-DOF的不可观。

另外从公式这个角度出发也是理解不可观的自由度的一种方式,例如VIO的yaw轴不可观,实际上就是把最后估计的位姿的yaw角再加上一个角度,不会改变损失函数。而这个加上yaw角,利用公式来理解就是把最后的位姿 绕着垂直于重力的 z z z再进行旋转

在这里插入图片描述

2. 几种不同的gauge handle处理方式

仿真模型如下,第0次滑窗优化的时候状态变量是 P 0 , P 1 , P 2 , L P_0, P_1, P_2, L P0,P1,P2,L。显然这个系统是1自由度不可观的,所以下面要通过不同的方式来处理这个不可观的自由度。

在这里插入图片描述
先给出此时系统的残差、雅克比矩阵以及H矩阵:
r = [ 6.0 − ( L − P 0 ) 1.1 − ( P 1 − P 0 ) 0.95 − ( P 2 − P 1 ) 5.05 − ( L − P 1 ) 3.8 − ( L − P 2 ) ] J = [ 1 0 0 − 1 1 − 1 0 0 0 1 − 1 0 0 1 0 − 1 0 0 1 − 1 ] H = J ′ J = [ 2 − 1 0 − 1 − 1 3 − 1 − 1 0 − 1 2 − 1 − 1 − 1 − 1 3 ] \begin{aligned} r &= \begin{bmatrix} 6.0 - (L - P_0) \\ 1.1 - (P_1 - P_0) \\ 0.95 - (P_2 - P_1) \\ 5.05 - (L - P_1) \\ 3.8 - (L - P_2) \end{bmatrix} \\\\ J &= \begin{bmatrix} 1 & 0 & 0 & -1 \\ 1 & -1 & 0 & 0 \\ 0 & 1 & -1 & 0 \\ 0 & 1 & 0 & -1 \\ 0 & 0 & 1 & -1 \end{bmatrix} \\\\ H = J'J &= \begin{bmatrix} 2 & -1 & 0 & -1 \\ -1 & 3 & -1 & -1 \\ 0 & -1 & 2 & -1 \\ -1 & -1 & -1 & 3 \end{bmatrix} \end{aligned} rJH=JJ= 6.0(LP0)1.1(P1P0)0.95(P2P1)5.05(LP1)3.8(LP2) = 11000011100010110011 = 2101131101211113
容易发现, H H H 矩阵的每一行都加起来结果为0,所以 H H H 矩阵是不满秩的, r a n k ( H ) = 3 rank(H) = 3 rank(H)=3,所以需要进行一些特殊的处理才能求解。

这种存在自由度不可观的情况在论文中称为 gauge freedom,处理 gauge freedom(称为gauge handle)有多种方式,这篇论文 总结为下面三种方式(g2o中还有一种方式,这里定义为第4种)。实际上在VIO的课程中,总结的应该是四种,恰好对应下面自己写的这四种。

在这里插入图片描述

2.1. free gauge方式

不管这个自由度,而 H Δ x = − b H\Delta x=-b HΔx=b 的求解 H H H 不满秩的问题靠 L-M算法 中给 H H H 增加一个 λ I \lambda I λI 来间接解决(因为本来 λ I \lambda I λI 是为了限制高斯牛顿法接近二次拟合的)。但是这样的结果最后 仍然会在零空间漂移,所以在VINS中用的做法就是使用free gauge求解 Δ x \Delta x Δx,但是求解之后给变量赋值的时候,是让第滑窗中第0帧的4-DOF保持不变,也就是认为第0帧的状态不发生改变。

MATLAB 代码如下:

%% 本代码测试不同的零空间处理方法gauge handle
clear all; clc;

%% 0.正常状态,滑窗中维护3个位置+1个路标点
% 真实状态
x_t = [0; 1; 2; 6];  % 滑窗长度为3, 变量分别为P0, P1, P2, L
% 码盘测量值, e=encoder, l=lidar
l0 = 6.0; e1 = 1.1; e2 = 0.95; l1 = 5.05; l2 = 3.8;
e0 = 0.0;  % 为了解决漂移的问题,给一个超强先验
% 状态初值, 由码盘测量值来得到
P0 = e0; P1 = P0 + e1; P2 = P1 + e2; L = P0 + l0; 
% 状态初始估计值
x = [P0; P1; P2; L]; 

%% gauge handle 1: free gauge
disp("****** gauge handle 1: free gauge ******");
J = [1, 0, 0, -1;
     1, -1, 0, 0; 
     0, 1, -1, 0;
     0, 1, 0, -1;
     0, 0, 1, -1];
H = J' * J
fprintf("rank(H) = %d\n", rank(H));  % rank(H) = 3
miu = 0.1;
H = H + miu * eye(4);
% 其实由于是线性模型,所以没必要迭代,一步就是最速下降法得到最优解
for i = 1 : 5   
    fprintf("第 %d 次迭代\n", i); 
    % 残差
    r = [l0 - (x(4) - x(1));
         e1 - (x(2) - x(1)); 
         e2 - (x(3) - x(2));
         l1 - (x(4) - x(2));
         l2 - (x(4) - x(3))];
    b = -J' * r;
    delta_x = H \ b;
    x = x + delta_x;
    disp("delta_x = "); disp(delta_x');
    disp("x = "); disp(x');
end
x = x + 0 - x(1);  % 手动对齐第0帧的位置到想要的位置上
disp("x = "); disp(x');
fprintf("error = %f\n", (x_t-x)' * (x_t-x));

代码输出如下:
在这里插入图片描述

2.2. fix gauge方式

固定滑窗中的第0帧,具体操作就是和滑窗中的 第0帧有关的雅克比矩阵全都置为0,这就意味第0帧的变化对所有的残差都不会有贡献,所以第0帧在优化中就不会改变了。

从数学上来看,也就是上面仿真的例子中雅克比矩阵 J J J 的第1列(对应状态 P 0 P_0 P0)全为0,即:

J = [ 0 0 0 − 1 0 − 1 0 0 0 1 − 1 0 0 1 0 − 1 0 0 1 − 1 ] H = J ′ J = [ 0 0 0 0 0 3 − 1 − 1 0 − 1 2 − 1 0 − 1 − 1 3 ] b = − J ′ e = [ 0 . . . . . . . . . ] \begin{aligned} J &= \begin{bmatrix} 0 & 0 & 0 & -1 \\ 0 & -1 & 0 & 0 \\ 0 & 1 & -1 & 0 \\ 0 & 1 & 0 & -1 \\ 0 & 0 & 1 & -1 \end{bmatrix} \\\\ H = J'J &= \begin{bmatrix} 0 & 0 & 0 & 0 \\ 0 & 3 & -1 & -1 \\ 0 & -1 & 2 & -1 \\ 0 & -1 & -1 & 3 \end{bmatrix} \\\\ b = -J'e &= \begin{bmatrix} 0 \\ ... \\ ... \\ ... \end{bmatrix} \end{aligned} JH=JJb=Je= 00000011100010110011 = 0000031101210113 = 0.........

由于最后我们求解还是使用 L-M算法 求解,即会在 H H H 矩阵对角线上加一个数 μ \mu μ,所以最后求解的方程变成

[ μ 0 0 0 0 3 + μ − 1 − 1 0 − 1 2 + μ − 1 0 − 1 − 1 3 + μ ] [ Δ P 0 Δ P 1 Δ P 2 Δ L ] = [ 0 . . . . . . . . . ] \begin{bmatrix} \mu & 0 & 0 & 0 \\ 0 & 3+\mu & -1 & -1 \\ 0 & -1 & 2+\mu & -1 \\ 0 & -1 & -1 & 3+\mu \end{bmatrix} \begin{bmatrix} \Delta P_0 \\ \Delta P_1 \\ \Delta P_2 \\ \Delta L \\ \end{bmatrix} = \begin{bmatrix} 0 \\ ... \\ ... \\ ... \end{bmatrix} μ00003+μ11012+μ10113+μ ΔP0ΔP1ΔP2ΔL = 0.........

这时候关于 Δ P 0 \Delta P_0 ΔP0 这一项的解就变成了 ( 0 + μ ) Δ P 0 = 0 (0+\mu)\Delta P_0 = 0 (0+μ)ΔP0=0,结果只能是 Δ P 0 = 0 \Delta P_0 = 0 ΔP0=0,从而起到了固定第0帧的位置的作用。我感觉这个本质上属于 一种数学技巧,并不是很有物理意义。

代码实现如下:

%% gauge handle 2: fix gauge
fprintf("\n");
disp("****** gauge handle 2: fix gauge ******"); 
x = [P0; P1; P2; L]; 
% 雅克比第1列是关于状态P0的,全部置为0
J = [0, 0, 0, -1;
     0, -1, 0, 0; 
     0, 1, -1, 0;
     0, 1, 0, -1;
     0, 0, 1, -1];
H = J' * J
fprintf("rank(H) = %d\n", rank(H));  % rank(H) = 3
miu = 0.1;
H = H + miu * eye(4);

for i = 1 : 5 
    fprintf("第 %d 次迭代\n", i); 
    % 残差
    r = [l0 - (x(4) - x(1));
         e1 - (x(2) - x(1)); 
         e2 - (x(3) - x(2));
         l1 - (x(4) - x(2));
         l2 - (x(4) - x(3))];
    b = -J' * r;
    delta_x = H \ b;
    x = x + delta_x;
    disp("delta_x = "); disp(delta_x');
    disp("x = "); disp(x');
end
fprintf("error = %f\n", (x_t-x)' * (x_t-x));

代码输出如下,可见迭代速度不快,这个迭代速度可以通过调节 μ \mu μ来改变。

2.3. prior gauge方式

给滑窗中的第0帧添加先验,这种方式实际上是 最正规的方式,因为它有明确的物理意义,而其他方式更像是一种数学上的技巧。这种添加先验的方式就是从存在不可观的原因出发,即由于测量全都是相对测量而没有绝对测量,导致优化结果可以漂移。所以这种方式就是增加一个绝对位置的先验测量,比如给滑窗中第0帧添加一个先验观测的位置是0,这样优化的时候最终结果就不会离0太远。

(1) 如果想固定第0帧就是在先验观测0的位置,那么只需要把这个先验观测的协方差矩阵设置的非常小,也就是信息矩阵(权重)设置的非常大,这样优化状态离先验观测稍微偏离一点就会造成非常大的误差项,从而最后会把第0帧的优化状态固定在先验的位置。

(2) 如果就是有一个实际观测到的先验,比如GPS观测,有明确的协方差矩阵,那么就可以把它作为先验融合到滑窗优化中,这样滑窗中第0帧位置可能会发生变化,但是此时就是需要让它有变化,因为我们已经有先验了,需要来修正这个状态。

代码如下:

%% gauge handle 3: prior gauge
fprintf("\n");
disp("****** gauge handle 3: prior gauge ******"); 
x = [P0; P1; P2; L];
w = 30;   % 超强先验的权重,注意一定是正数。而且这个数值还不能太大,如果太大会导致H矩阵直接退化成rank=1
J = [-w, 0, 0, 0;   % 超强先验, 残差是 w(e0 - P0), 所以雅克比是-w
     1, 0, 0, -1;
     1, -1, 0, 0; 
     0, 1, -1, 0;
     0, 1, 0, -1;
     0, 0, 1, -1];
% 开始构造高斯牛顿的优化问题,认为信息权重矩阵都是I
H = J' * J
fprintf("rank(H) = %d\n", rank(H));  % rank(H) = 4

% 其实由于是线性模型,所以没必要迭代,一步就是最速下降法得到最优解
for i = 1 : 2   
    fprintf("第 %d 次迭代\n", i); 
    % 残差
    r = [ w * (e0 - x(1));
         l0 - (x(4) - x(1));
         e1 - (x(2) - x(1)); 
         e2 - (x(3) - x(2));
         l1 - (x(4) - x(2));
         l2 - (x(4) - x(3))];
    b = -J' * r;
    delta_x = H \ b;
    x = x + delta_x;
    disp("delta_x = "); disp(delta_x');
    disp("x = "); disp(x');
end
fprintf("error = %f\n", (x_t-x)' * (x_t-x));

代码输出:

注意

  • 如果是想固定第0帧的话,给的先验权重大小要合适。首先不能太小,否则起不到固定的效果;其次也不能太大,否则此时的 H H H 矩阵中数值差异过大,会直接导致 H H H 矩阵的秩变成1,相当于整个 H H H 矩阵中只有 w w ww ww 构成的这一项,因为其他项相比它都太小了,这样反而会直接导致系统更加不可观。
  • 可以发现这种方式系统一次就迭代到了最小值,到底这几种方法为什么会有这种迭代速度的区别还需要后面仔细思考。但是根据论文里说的,这几种方法实际上速度差别不大。

2.4. g2o tutorial方式

这种方式也是属于 固定第0帧,具体叫什么名字不知道,在手写VIO的课程中有讲解,因为g2o就是使用的这种方式来固定某些帧,所以这里就称之为g2o tutorial的方式了。

其操作就是在最后的 H H H 矩阵要固定的状态变量的位置强行加一个 I I I,但是对应的 b b b 的位置并不加任何东西。这样其实本质上就是更改了原来的 H Δ x = b H\Delta x = b HΔx=b 的方程组,导致求解的结果中 Δ x 0 \Delta x_0 Δx0 只能为0。所以这个本质上也属于一种 数学技巧,并没有物理意义。

比如上面的例子,假设 H Δ x = b H\Delta x = b HΔx=b 的形式如下:

[ h 11 h 12 h 13 h 14 h 21 h 22 h 23 h 24 h 31 h 32 h 33 h 34 h 41 h 42 h 43 h 44 ] [ Δ P 0 Δ P 1 Δ P 2 Δ L ] = [ b 1 b 2 b 3 b 4 ] \begin{bmatrix} h_{11} & h_{12} & h_{13} & h_{14} \\ h_{21} & h_{22} & h_{23} & h_{24} \\ h_{31} & h_{32} & h_{33} & h_{34} \\ h_{41} & h_{42} & h_{43} & h_{44} \end{bmatrix} \begin{bmatrix} \Delta P_0 \\ \Delta P_1 \\ \Delta P_2 \\ \Delta L \\ \end{bmatrix} = \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ b_4 \end{bmatrix} \\ h11h21h31h41h12h22h32h42h13h23h33h43h14h24h34h44 ΔP0ΔP1ΔP2ΔL = b1b2b3b4
现在想要固定 P 0 P_0 P0 的位置,那么就给 H H H 矩阵对应 P 0 P_0 P0 的位置增加一个单位阵 I I I,这样上面的方程变为:

[ h 11 + 1 h 12 h 13 h 14 h 21 h 22 h 23 h 24 h 31 h 32 h 33 h 34 h 41 h 42 h 43 h 44 ] [ Δ P 0 Δ P 1 Δ P 2 Δ L ] = [ b 1 b 2 b 3 b 4 ] \begin{bmatrix} h_{11} + 1 & h_{12} & h_{13} & h_{14} \\ h_{21} & h_{22} & h_{23} & h_{24} \\ h_{31} & h_{32} & h_{33} & h_{34} \\ h_{41} & h_{42} & h_{43} & h_{44} \end{bmatrix} \begin{bmatrix} \Delta P_0 \\ \Delta P_1 \\ \Delta P_2 \\ \Delta L \\ \end{bmatrix} = \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ b_4 \end{bmatrix} \\ h11+1h21h31h41h12h22h32h42h13h23h33h43h14h24h34h44 ΔP0ΔP1ΔP2ΔL = b1b2b3b4

利用矩阵的乘法分配律,可以把上述方程拆解为两个方程:

[ h 11 h 12 h 13 h 14 h 21 h 22 h 23 h 24 h 31 h 32 h 33 h 34 h 41 h 42 h 43 h 44 ] [ Δ P 0 Δ P 1 Δ P 2 Δ L ] = [ b 1 b 2 b 3 b 4 ] [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] [ Δ P 0 Δ P 1 Δ P 2 Δ L ] = [ 0 0 0 0 ] \begin{aligned} \begin{bmatrix} h_{11} & h_{12} & h_{13} & h_{14} \\ h_{21} & h_{22} & h_{23} & h_{24} \\ h_{31} & h_{32} & h_{33} & h_{34} \\ h_{41} & h_{42} & h_{43} & h_{44} \end{bmatrix} \begin{bmatrix} \Delta P_0 \\ \Delta P_1 \\ \Delta P_2 \\ \Delta L \\ \end{bmatrix} &= \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ b_4 \end{bmatrix} \\\\ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} \Delta P_0 \\ \Delta P_1 \\ \Delta P_2 \\ \Delta L \\ \end{bmatrix} &= \begin{bmatrix} 0 \\ 0 \\ 0 \\ 0 \end{bmatrix} \end{aligned} h11h21h31h41h12h22h32h42h13h23h33h43h14h24h34h44 ΔP0ΔP1ΔP2ΔL 1000000000000000 ΔP0ΔP1ΔP2ΔL = b1b2b3b4 = 0000

可见我们可以利用第2个方程先求写出 Δ P 0 = 0 \Delta P_0 = 0 ΔP0=0,然后带入到第1个方程中再求解处剩下的变量。也就是说,实际上这种方式就是在原来的4个方程的基础上又增加了一个附加方程为 1 × Δ P 0 = 0 1 \times \Delta P_0 = 0 1×ΔP0=0,从而强行改变了原来方程组的解。所以这个本质上也属于一种 数学技巧,并没有物理意义。

代码如下:

%% gauge handle 4: g2o tutorial
fprintf("\n");
disp("****** gauge handle 4: g2o tutorial ******"); 
x = [P0; P1; P2; L]; 
J = [1, 0, 0, -1;
     1, -1, 0, 0;  
     0, 1, -1, 0;
     0, 1, 0, -1;
     0, 0, 1, -1];
H = J' * J
fprintf("rank(H) = %d\n", rank(H));  % rank(H) = 3
H(1, 1) = H(1, 1) + 1;

% 其实由于是线性模型,所以没必要迭代,一步就是最速下降法得到最优解
for i = 1 : 2   
    fprintf("第 %d 次迭代\n", i); 
    % 残差
    r = [l0 - (x(4) - x(1));
         e1 - (x(2) - x(1)); 
         e2 - (x(3) - x(2));
         l1 - (x(4) - x(2));
         l2 - (x(4) - x(3))];
    b = -J' * r;
    delta_x = H \ b;
    x = x + delta_x;
    disp("delta_x = "); disp(delta_x');
    disp("x = "); disp(x');
end
fprintf("error = %f\n", (x_t-x)' * (x_t-x));

代码输出如下,可见这种方式的迭代速度也非常快。

3.不同方式的协方差矩阵

待总结,参见论文。

这个地方还是很重要的,如果能够求解出协方差矩阵的话,那么可以利用prior gauge的方式来对下一次滑窗优化添加先验,而不用像VINS一样每次滑窗优化都是把滑窗中第0帧当做不变的帧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403749.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gerrit操作和jinkens编译合入代码

gerrit 先 查看自己的push 找到后添加reviewer 填写邮箱开头就可以出来 记得1 然后send 让人review 编译不过,gerrit上查看 1.是不是checkstyle问题 2.编译不过,去jinkens查看 先retrigger重新编译 如果发现多次编译失败 则要看下console output 查…

【ONE·Data || 顺序表】

总言 数据结构基础:顺序表模拟实现。    文章目录总言1、顺序表各接口功能实现描述1.1、如何创建一个顺序表?1.2、如何初始化顺序表:SLInit1.3、顺序表的尾插、头插1.3.1、顺序表尾插1.0:SLPushBack1.3.2、顺序表头插1.0&#x…

网络连接的三种模式

文章目录前言一、三种连接模式介绍二、三种网络连接模式的区别前言 在进行虚拟机配置时,网络连接分为三种模式:桥接模式,NAT模式,主机模式 一、三种连接模式介绍 张三、李四、王五在同一个网段,所以他们之间可以相互…

数据结构---双链表

专栏:数据结构 个人主页:HaiFan. 专栏简介:从零开始,数据结构!! 双链表前言双链表各接口的实现为要插入的值开辟一块空间BuyLN初始化LNInit和销毁LNDestory打印链表中的值LNPrint尾插LNPushBack和尾删LNPop…

vue2+elementUI完成添加学生删除学生案列

效果图&#xff1a; 点击添加学生按钮&#xff0c;弹出Dialog,收集用户信息&#xff1a; el-table中自定义复选框&#xff0c;选中一行&#xff0c;可以点击删除 代码区域&#xff1a;就一个HTML文件 <!DOCTYPE html> <html lang"en"> <head>&…

Flume基操

Flume概述 Flume 定义 Flume 是 Cloudera 提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传输的系统。Flume 基于流式架构&#xff0c;灵活简单。 Flume最主要的作用就是&#xff0c;实时读取服务器本地磁盘的数据&#xff0c;将数据写入到…

带恒温冷藏功能的便携式自动采样器——可用于毒情监测

污水采样在验毒的工作流程中是怎样进行的呢&#xff1f; 污水采样&#xff1a;每个季度采样一次。例如在某市48家污水处理厂54个进水口采取水样&#xff0c;用便携式水质自动采样器连续采样7天&#xff0c;一天采样12次成为一个混合样。也就是说&#xff0c;一次采样的话&…

如何在 VS Code 中安装运行、编写C语言程序

1.下载 安装VS Code 去官网下载&#xff1a;https://code.visualstudio.com/Download 直接下载&#xff0c;安装即可。 2.安装VS code中2个插件 打开软件运行&#xff0c;在扩展商店中分别搜索安装 C/C 和 code runner 插件 3.下载mingb64 官网下载 https://sourceforge…

消息队列MQ用来做什么的,市场上主流的四大MQ如何选择?RabbitMQ带你HelloWorld!

文章目录MQ用来做什么的MQ会有什么样的麻烦MQ消息队列模式分类MQ消息队列常用协议市场主流四大MQRabbitMQ项目开发RabbitMQ中的组成部分MQ用来做什么的 省流 &#xff1a;系统解耦、异步调用、流量削峰 系统解耦 首先举例下面这个场景&#xff0c;现有ABCDE五个系统&#xff…

小黑子—Java从入门到入土过程:第二章

Java零基础入门2.0Java系列第二章1. 注释和关键字2. 字面量3. 变量3.1 基本用法3.2 使用方式3.3 注意事项4. 变量练习5. 计算机中的数据存储5.1 计算机的存储规则5.2 进制5.3 进制间转换二进制转十八进制转十十六进制转十十进制转其他进制6. 数据类型7. 定义变量的练习8. 标识符…

MATLAB——将直接型转化为并联型和级联型

题目1(IIR)&#xff1a; 已知一个系统的传递函数为&#xff1a; H&#xff08;z&#xff09;8−4z−111z−2−2z−31−1.25z−10.75z−2−0.125z−3H&#xff08;z&#xff09;\frac{8-4z^{-1}11z^{-2}-2z^{-3}}{1-1.25z^{-1}0.75z^{-2}-0.125z^{-3}}H&#xff08;z&#xff09…

Leedcode 1137. 第 N 个泰波那契数

泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;4 解释&#xff1a; T_3 0 1 1 2 T_4 1 …

2.线性表的顺序表示

数据结构很重要&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01; 数据结构很重要&#xff01;&#xff01;&#xff01;&#xff01; 思考 1.线性表的顺序表示内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要学线性表的顺序表示? ? (Why)…

POI 操作Excel的单元格样式超过64000的异常问题解决

文章目录POI 操作Excel的单元格样式超过64000的异常问题解决问题描述问题原因问题分析和解决简单的Excel文件生成Demo最终的解决方案POI 操作Excel的单元格样式超过64000的异常问题解决 问题描述 在用POI 生成Excel文件时&#xff0c;如果自定义的单元格的样式超过64000行&am…

SpringBoot+WebSocket实时监控异常

# 写在前面此异常非彼异常&#xff0c;标题所说的异常是业务上的异常。最近做了一个需求&#xff0c;消防的设备巡检&#xff0c;如果巡检发现异常&#xff0c;通过手机端提交&#xff0c;后台的实时监控页面实时获取到该设备的信息及位置&#xff0c;然后安排员工去处理。因为…

2.9.1 Packet Tracer - Basic Switch and End Device Configuration(作业)

Packet Tracer - 交换机和终端设备的基本 配置地址分配表目标使用命令行界面 (CLI)&#xff0c;在两台思科互联网络 操作系统 (IOS) 交换机上配置主机名和 IP 地址。使用思科 IOS 命令指定或限制对设备 配置的访问。使用 IOS 命令来保存当前的运行配置。配置两台主机设备的 IP …

JavaScript Boolean(布尔)对象

Boolean&#xff08;布尔&#xff09;对象用于将非布尔值转换为布尔值&#xff08;true 或者 false&#xff09;&#xff0c;是三种包装对象&#xff1a;Number、String和Boolean中最简单的一种&#xff0c;它没有大量的实例属性和方法。在线实例检查布尔值检查布尔对象是 true…

大数据-玩转数据-mysql规范

整体图谱 正文部分 一、数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字&#xff08;如果表名中包含关键字查询时&#xff0c;需要将其用单引号括起来&#xff09; 数据库对象的命名要能做到见名识意&#xff…

【算法时间复杂度】学习记录

最近开算法课&#xff0c;开几篇文章记录一下算法的学习过程。 关于算法的重要性 学习计算机当程序员的话&#xff0c;在编程过程中是绕不开算法这个大矿山的&#xff0c;需要我们慢慢挖掘宝藏。 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组…

信创和去O大潮下,Oracle OCP(1z0-082 1z0-083)的含金量有多少?(文末附录像)

我自己就考了挺多Oracle的认证&#xff0c;下面是从Oracle的certview网站&#xff08;网址是certview加上点oracle点com&#xff09;上面查询到的姚远老师自己的Oracle证书。 目前国内强调自主可控&#xff0c;推信创和去Oracle数据库&#xff0c;很多想考Oracle OCP认证的学员…