精华内容
下载资源
问答
  • 局部静态变量是如何做到只初始化一次的? 关于编译选项 -Wa,-adlhn参考 http://blog.csdn.net/lanxinju/article/details/5900986     以下内容来自于内网别的高人的回复 可以写个程序测试一下:  ...

    局部静态变量是如何做到只初始化一次的?

    关于编译选项 -Wa,-adlhn参考

    http://blog.csdn.net/lanxinju/article/details/5900986

     

     

    以下内容来自于内网别的高人的回复

    可以写个程序测试一下: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class  A
    {
    public :
         A() {}
    public :
         int  a;
    };
     
    int  static_var_func()
    {
         static  A a;
         return  a.a++;
    }
     
    int  main( int  argc, char  * argv[])
    {
         static_var_func();
         return  0;

    看看汇编

    1
    g++ -c -g -Wa,-adlhn yy.cpp

    结果: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    10:yy.cpp        **** int  static_var_func()
    35                            .loc 1 10 0
    36 0000 55                    pushq   %rbp
    37                    .LCFI2:
    38 0001 4889E5                movq    %rsp, %rbp
    39                    .LCFI3:
    40                    .LBB2:
    11:yy.cpp        **** {
    12:yy.cpp        ****     static  A a;
    41                            .loc 1 12 0
    42 0004 B8000000              movl    $_ZGVZ15static_var_funcvE1a, %eax
    42      00
    43 0009 0FB600                movzbl  (%rax), %eax
    44 000c 84C0                  testb   %al, %al
    45 000e 7527                  jne     .L4
    46 0010 BF000000              movl    $_ZGVZ15static_var_funcvE1a, %edi
    46      00
    47 0015 E8000000              call    __cxa_guard_acquire
    47      00
    48 001a 85C0                  testl   %eax, %eax
    49 001c 0F95C0                setne   %al
    50 001f 84C0                  testb   %al, %al
    51 0021 7414                  je      .L4
    52 0023 BF000000              movl    $_ZZ15static_var_funcvE1a, %edi
    52      00
    53 0028 E8000000              call    _ZN1AC1Ev
    53      00
    54 002d BF000000              movl    $_ZGVZ15static_var_funcvE1a, %edi
    54      00
    55 0032 E8000000              call    __cxa_guard_release
    55      00
    56                    .L4:
    13:yy.cpp        ****     return  a.a++;
    57                            .loc 1 13 0
    58 0037 8B050000              movl    _ZZ15static_var_funcvE1a(%rip), %eax
    58      0000
    59 003d 89C2                  movl    %eax, %edx
    60 003f 83C001                addl    $1, %eax
    61 0042 89050000              movl    %eax, _ZZ15static_var_funcvE1a(%rip)
    61      0000
    62 0048 89D0                  movl    %edx, %eax
    63                    .LBE2:
    14:yy.cpp        **** }
    64                            .loc 1 14 0
    65 004a C9                    leave
    66 004b C3                    ret

     亮点就在__cxa_guard_acquire和__cxa_guard_release上,这两个函数实现于libstdc++。大意是一个全局的mutex和一个cond来保护一个锁变量(_ZGVZ15static_var_funcvE1a),锁变量再来保护目标变量(_ZZ15static_var_funcvE1a)。锁变量的第一个字节(也就是%al)表示目标变量是否被初始化过了,第二个字节表示目标变量是否在初始化中。__cxa_guard_acquire的时候将锁变量的第二个字节置1,表示初始化中;如果已经为1了,就等待它变0(通过全局cond)再返回(保证初始化结束)。__cxa_guard_release的时候清除第二个字节,再将第一个字节置1。

     简单地说,g++在变量初始化的前后,自动加了锁保护代码。

     另外还有一种可能的情况,变量初始化的时候,如果存在自引用,可能会循环初始化产生死锁。g++对这种情况也有考虑。

     

    http://www.dutor.net/index.php/2013/06/initialization-of-local-static-variables/

      不必说静态变量和普通变量的区别,也不必说静态变量及其作用域的得与失,单单说一下函数作用域的静态变量是如何初始化的。

    1
    2
    3
    4
    5
    6
    
    int foo()
    {
        static int n = init();
        //~ do anything/nothing on 'n'
        return n;
    }

      在 foo() 第一次被调用时,foo()::s 只初始化一次(C 中,静态变量只允许以常量初始化)。“只初始化一次”是如何保证的呢?当然需要编译器维护一个状态,来标识该变量是否已被初始化,并安插代码,在每一次函数被调用时进行判断。咱们通过汇编验证一把:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    .globl _Z3foov
      .type _Z3foov, @function
    _Z3foov:
    .LFB1405:
      pushq %rbp
    .LCFI13:
      movq  %rsp, %rbp
    .LCFI14:
      movl  $_ZGVZ3foovE1n, %eax
      movzbl  (%rax), %eax
      testb %al, %al 
      jne .L18
      movl  $_ZGVZ3foovE1n, %edi
      call  __cxa_guard_acquire
      testl %eax, %eax
      setne %al 
      testb %al, %al 
      je  .L18
      call  _Z4initv
      movl  %eax, _ZZ3foovE1n(%rip)
      movl  $_ZGVZ3foovE1n, %edi
      call  __cxa_guard_release
    .L18:
      movl  _ZZ3foovE1n(%rip), %eax
      leave
      ret

      寄存器、指令和标号不提,其他符号是什么含义呢?通过 c++filt 进行 demangling,_ZGVZ3foovE1n 标识 ‘guard variable for foo()::n’,作为前面提到的“初始化状态标识”用(低字节),_ZZ3foovE1n 标识 ‘foo()::n’,_Z4initv 即 init()。
      那 __cxa_guard_acquire 和 __cxa_guard_release 呢?故名思议,这两个函数具有锁语义。为什么需要锁呢?当然是基于静态变量的线程安全考虑了。静态变量的状态变化属于业务逻辑,编译器管不着也管不了,但静态变量的初始化过程由编译器负责,在初始化线程安全的问题上还是可以出把力的。
      分析上述汇编代码。首先获取 guard 变量,判断低字节是否为 0,若非零,表示已经初始化,可以直接使用。否则,将 guard 作为参数调用 __cxa_guard_acquire,如果锁成功,调用 init() 初始化静态变量 foo()::n,然后释放锁。如果锁失败,说明产生竞态条件,则会阻塞当前线程,不同于普通锁的地方在于,__cxa_guard_acquire 是有返回值的(当然 pthread_lock 也有返回值,但用途不同),如果发生了等待,__cxa_guard_acquire 返回 0,并不会进入 foo()::n 的初始化过程(其他线程已经初始化过了,初始化失败的情况就不细究了)。
      为了验证上述分析,可以将 init() 实现成一个耗时的操作,令多个线程“同时”调用 foo(),然后查看各个线程的运行状态。
      利用该机制,可以很好的实现所谓 Singleton 模式:

    1
    2
    3
    4
    5
    
    Singleton* Singleton::GetInstance()
    {
        static Singleton instance;
        return &instance;
    }

      对于单线程程序,静态变量的保护是没有必要的,g++ 的 -fno-threadsafe-statics 选项可以禁掉该机制。

    展开全文
  • 本文主要介绍VINS状态估计器模块(estimator)中的视觉惯性联合初始化以及相机到IMU的外参估计,其中前半部分对应论文第五章(V. ESTIMATOR INITIALIZATION B. Visual-Inertial Alignment),后半部分参考了沈老师组...

    前言

    本文主要介绍VINS状态估计器模块(estimator)初始化环节中视觉惯性对齐求解陀螺仪偏置、尺度、重力加速度、每帧速度以及相机到IMU的外参估计,其中前半部分对应论文第五章(V. ESTIMATOR INITIALIZATION B. Visual-Inertial Alignment),后半部分参考了沈老师组的之前的论文。

    总的来说,视觉惯性对齐主要包括以下流程:
    1、若旋转外参数 q b c q_{bc} qbc未知,则先估计旋转外参
    2、利用旋转约束估计陀螺仪bias
    3、利用平移约束估计重力方向,速度,以及尺度初始值
    4、对重力向量 g c 0 g^{c_0} gc0进一步优化
    5、求解世界坐标系w和初始相机坐标系 c 0 c_0 c0之间的旋转矩阵,并讲轨迹对齐到世界坐标系

    视觉惯性对齐

    首先我们先推导论文式(14),所有帧的位姿 ( R c k c 0 , q c k c 0 ) (R_{c_k}^{c_0},q_{c_k}^{c_0}) (Rckc0,qckc0)表示相对于第一帧相机坐标系(·)c0。相机到IMU的外参为 ( R c b , q c b ) (R_c^b,q_c^b) (Rcb,qcb),得到姿态从相机坐标系转换到IMU坐标系的关系。
    T c k c 0 = T b k c 0 T c b T^{c_0}_{c_k}=T^{c_0}_{b_k}T^{b}_{c} Tckc0=Tbkc0Tcb
    将T展开有成R与p有:
    [ R c k c 0 s p c k c 0 0 1 ] = [ R b k c 0 s p b k c 0 0 1 ] [ R c b p c b 0 1 ] \left[\begin{array}{cccc} R^{c_0}_{c_k} &sp^{c_0}_{c_k}\\0&1 \end{array}\right]=\left[\begin{array}{cccc} R^{c_0}_{b_k} &sp^{c_0}_{b_k}\\0&1 \end{array}\right] \left[\begin{array}{cccc} R^{b}_{c} &p^{b}_{c}\\0&1 \end{array}\right] [Rckc00spckc01]=[Rbkc00spbkc01][Rcb0pcb1]
    左侧矩阵的两项写开:
    R c k c 0 = R b k c 0 R c b ⇒ R b k c 0 = R c k c 0 ( R c b ) − 1 R^{c_0}_{c_k}=R^{c_0}_{b_k}R^{b}_{c} \Rightarrow R^{c_0}_{b_k}=R^{c_0}_{c_k}(R^{b}_{c})^{-1} Rckc0=Rbkc0RcbRbkc0=Rckc0(Rcb)1
    s p c k c 0 = R b k c 0 p c b + s p b k c 0 ⇒ s p b k c 0 = s p c k c 0 − R b k c 0 p c b sp^{c_0}_{c_k}=R^{c_0}_{b_k} p^{b}_{c} +sp^{c_0}_{b_k}\Rightarrow sp^{c_0}_{b_k}=sp^{c_0}_{c_k}-R^{c_0}_{b_k} p^{b}_{c} spckc0=Rbkc0pcb+spbkc0spbkc0=spckc0Rbkc0pcb
    对于VINS初始化中视觉惯性对齐之外的部分已在博客VINS-Mono代码解读——视觉惯性联合初始化 中详细介绍了。这里就直接从VisualIMUAlignment()函数开始,结合相关数学推导进行解读。这里的代码均位于vins_estimator/src/initial/initial_aligment.cpp/.h。

    VisualIMUAlignment()中调用了两个函数,分别用于对陀螺仪的偏置进行标定,以及估计尺度、重力以及速度。

    bool VisualIMUAlignment(map<double, ImageFrame> &all_image_frame, Vector3d* Bgs, Vector3d &g, VectorXd &x)
    {
        solveGyroscopeBias(all_image_frame, Bgs);
        if(LinearAlignment(all_image_frame, g, x))
            return true;
        else 
            return false;    
    }
    

    陀螺仪偏置标定 solveGyroscopeBias()

    对于窗口中得连续两帧 b k b_k bk b k + 1 b_{k+1} bk+1,已经从视觉SFM中得到了旋转 q b k c 0 q^{c_0}_{b_k} qbkc0 q b k + 1 c 0 q^{c_0}_{b_{k+1}} qbk+1c0,从IMU预积分中得到了相邻帧旋转 γ ^ b k + 1 b k \hat \gamma^{b_k}_{b_{k+1}} γ^bk+1bk。根据约束方程,联立所有相邻帧,最小化代价函数(论文式(15)):
    min ⁡ δ b w ∑ k ∈ B ∥ q b k + 1 c 0 − 1 ⊗ q b k c 0 ⊗ γ b k + 1 b k ∥ 2 \mathop {\min }\limits_{\delta b_w} \sum\nolimits_{k\in \mathcal B} \left\|{q^{c_0}_{b_{k+1}}}^{-1}\otimes q^{c_0}_{b_{k}}\otimes \gamma^{b_k}_{b_{k+1}} \right\|^2 δbwminkBqbk+1c01qbkc0γbk+1bk2
    其中对陀螺仪偏置求IMU预积分项线性化,有:
    γ b k + 1 b k ≈ γ ^ b k + 1 b k ⊗ [ 1 1 2 J b w γ δ b w ] \gamma^{b_k}_{b_{k+1}}\approx \hat \gamma^{b_k}_{b_{k+1}} \otimes \left[\begin{array}{cccc} 1 \\ \frac{1}{2}J^{\gamma}_{b_w}\delta b_w \end{array}\right] γbk+1bkγ^bk+1bk[121Jbwγδbw]

    在具体实现的时候,因为上述约束方程为:
    q b k + 1 c 0 − 1 ⊗ q b k c 0 ⊗ γ b k + 1 b k = [ 1 0 ] {q^{c_0}_{b_{k+1}}}^{-1}\otimes q^{c_0}_{b_{k}}\otimes \gamma^{b_k}_{b_{k+1}}= \left[\begin{array}{cccc} 1 \\ 0\end{array}\right] qbk+1c01qbkc0γbk+1bk=[10]
    有:
    γ b k + 1 b k = q b k c 0 − 1 ⊗ q b k + 1 c 0 ⊗ [ 1 0 ] \gamma^{b_k}_{b_{k+1}}= {q^{c_0}_{b_{k}}}^{-1}\otimes q^{c_0}_{b_{k+1}}\otimes \left[\begin{array}{cccc} 1 \\ 0\end{array}\right] γbk+1bk=qbkc01qbk+1c0[10]
    代入 δ b w \delta b_w δbw得:
    γ ^ b k + 1 b k ⊗ [ 1 1 2 J b w γ δ b w ] = q b k c 0 − 1 ⊗ q b k + 1 c 0 ⊗ [ 1 0 ] \hat \gamma^{b_k}_{b_{k+1}} \otimes \left[\begin{array}{cccc} 1 \\ \frac{1}{2}J^{\gamma}_{b_w}\delta b_w \end{array}\right] ={q^{c_0}_{b_{k}}}^{-1}\otimes q^{c_0}_{b_{k+1}}\otimes \left[\begin{array}{cccc} 1 \\ 0\end{array}\right] γ^bk+1bk[121Jbwγδbw]=qbkc01qbk+1c0[10]
    只考虑虚部,有:
    J b w γ δ b w = 2 ( γ ^ b k + 1 b k − 1 ⊗ q b k c 0 − 1 ⊗ q b k + 1 c 0 ) v e c J^{\gamma}_{b_w}\delta b_w = 2({\hat \gamma^{b_k}_{b_{k+1}}}^{-1} \otimes{q^{c_0}_{b_{k}}}^{-1}\otimes q^{c_0}_{b_{k+1}})_{vec} Jbwγδbw=2(γ^bk+1bk1qbkc01qbk+1c0)vec
    两侧乘以 J b w γ T {J^\gamma_{b_w}}^T JbwγT,用LDLT分解求得 δ b w \delta b_w δbw

    在代码中,Quaterniond q_ij即 q b k + 1 b k = q b k c 0 − 1 ⊗ q b k + 1 c 0 q^{b_k}_{b_{k+1}}={q^{c_0}_{b_k}}^{-1} \otimes q^{c_0}_{b_{k+1}} qbk+1bk=qbkc01qbk+1c0
    tmp_A即 J b w γ J^\gamma_{b_w} Jbwγ
    tmp_B即 2 ( r ^ b k + 1 b k − 1 ⊗ q b k + 1 b k ) v e c = 2 ( r ^ b k + 1 b k − 1 ⊗ q b k c 0 − 1 ⊗ q b k + 1 c 0 ) v e c 2 ({\hat r^{b_k}_{b_{k+1}}}^{-1} \otimes q^{b_k}_{b_{k+1}})_{vec}=2 ({\hat r^{b_k}_{b_{k+1}}}^{-1} \otimes {q^{c_0}_{b_k}}^{-1} \otimes q^{c_0}_{b_{k+1}})_{vec} 2(r^bk+1bk1qbk+1bk)vec=2(r^bk+1bk1qbkc01qbk+1c0)vec
    根据上面的代价函数构造Ax = B即
    J b w γ T J b w γ δ b w = 2 J b w γ T ( r ^ b k + 1 b k − 1 ⊗ q b k c 0 − 1 ⊗ q b k + 1 c 0 ) v e c {J^\gamma_{b_w}}^T J^\gamma_{b_w} \delta b_w=2 {J^\gamma_{b_w}}^T({\hat r^{b_k}_{b_{k+1}}}^{-1} \otimes {q^{c_0}_{b_k}}^{-1} \otimes q^{c_0}_{b_{k+1}})_{vec} JbwγTJbwγδbw=2JbwγT(r^bk+1bk1qbkc01qbk+1c0)vec
    然后采用LDLT分解求得 δ b w \delta b_w δbw

    void solveGyroscopeBias(map<double, ImageFrame> &all_image_frame, Vector3d* Bgs)
    {
        Matrix3d A;
        Vector3d b;
        Vector3d delta_bg;
        A.setZero();
        b.setZero();
        map<double, ImageFrame>::iterator frame_i;
        map<double, ImageFrame>::iterator frame_j;
        for (frame_i = all_image_frame.begin(); next(frame_i) != all_image_frame.end(); frame_i++)
        {
            frame_j = next(frame_i);
            MatrixXd tmp_A(3, 3);
            tmp_A.setZero();
            VectorXd tmp_b(3);
            tmp_b.setZero();
            
            //R_ij = (R^c0_bk)^-1 * (R^c0_bk+1) 转换为四元数 q_ij = (q^c0_bk)^-1 * (q^c0_bk+1)
            Eigen::Quaterniond q_ij(frame_i->second.R.transpose() * frame_j->second.R);
            //tmp_A = J_j_bw
            tmp_A = frame_j->second.pre_integration->jacobian.template block<3, 3>(O_R, O_BG);
            //tmp_b = 2 * ((r^bk_bk+1)^-1 * (q^c0_bk)^-1 * (q^c0_bk+1))_vec
            //      = 2 * ((r^bk_bk+1)^-1 * q_ij)_vec
            tmp_b = 2 * (frame_j->second.pre_integration->delta_q.inverse() * q_ij).vec();
            //tmp_A * delta_bg = tmp_b
            A += tmp_A.transpose() * tmp_A;
            b += tmp_A.transpose() * tmp_b;
        }
        //LDLT方法
        delta_bg = A.ldlt().solve(b);
        ROS_WARN_STREAM("gyroscope bias initial calibration " << delta_bg.transpose());
    
        for (int i = 0; i <= WINDOW_SIZE; i++)
            Bgs[i] += delta_bg;
    
        for (frame_i = all_image_frame.begin(); next(frame_i) != all_image_frame.end( ); frame_i++)
        {
            frame_j = next(frame_i);
            frame_j->second.pre_integration->repropagate(Vector3d::Zero(), Bgs[0]);
        }
    }
    

    速度、重力和尺度初始化 LinearAlignment()

    该函数需要优化的变量有速度、重力加速度和尺度(论文式(16)):
    X I 3 ( n + 1 ) + 3 + 1 = [ v b 0 b 0 , v b 1 b 1 , . . . , v b n b n , g c 0 , s ] \mathcal X^{3(n+1)+3+1}_I=[v^{b_0}_{b_0},v^{b_1}_{b_1},...,v^{b_n}_{b_n},g^{c_0},s] XI3(n+1)+3+1=[vb0b0,vb1b1,...,vbnbn,gc0,s]
    其中, v b k b k v^{b_k}_{b_k} vbkbk是第k帧图像在其IMU坐标系下的速度, g c 0 g^{c_0} gc0是在第0帧相机坐标系下的重力向量。
    在IMU预积分中我们已经推导过论文式(5):

    R w b k p b k + 1 w = R w b k ( p b k w + v b k w Δ t k − 1 2 g w Δ t k 2 ) + α b k + 1 b k R^{b_k}_w p^w_{b_{k+1}}=R^{b_k}_w (p^w_{b_k}+v^w_{b_k} \Delta t_k-\frac{1}{2} g^w\Delta t_k^2)+ \alpha^{b_k}_{b_{k+1}} Rwbkpbk+1w=Rwbk(pbkw+vbkwΔtk21gwΔtk2)+αbk+1bk

    R w b k v b k + 1 w = R w b k ( v b k w − g w Δ t k ) + β b k + 1 b k R^{b_k}_w v^w_{b_{k+1}}=R^{b_k}_w (v^w_{b_k}- g^w\Delta t_k)+\beta^{b_k}_{b_{k+1}} Rwbkvbk+1w=Rwbk(vbkwgwΔtk)+βbk+1bk

    c 0 c_0 c0替代 w w w可以写成:

    α b k + 1 b k = R c 0 b k ( s p b k + 1 c 0 − s p b k c 0 + 1 2 g c 0 Δ t k 2 − R b k c 0 v b k b k Δ t k ) \alpha^{b_k}_{b_{k+1}}=R^{b_k}_{c_0}(sp^{c_0}_{b_{k+1}}-sp^{c_0}_{b_{k}}+\frac{1}{2} g^{c_0}\Delta t_k^2-R^{c_0}_{b_k}v^{b_k}_{b_k} \Delta t_k) αbk+1bk=Rc0bk(spbk+1c0spbkc0+21gc0Δtk2Rbkc0vbkbkΔtk)

    β b k + 1 b k = R c 0 b k ( R b k + 1 c 0 v b k + 1 b k + 1 + g c 0 Δ t k − R b k c 0 v b k b k ) \beta^{b_k}_{b_{k+1}}=R^{b_k}_{c_0}(R^{c_0}_{b_{k+1}}v^{b_{k+1}}_{b_{k+1}}+g^{c_0}\Delta t_k-R^{c_0}_{b_k}v^{b_k}_{b_k}) βbk+1bk=Rc0bk(Rbk+1c0vbk+1bk+1+gc0ΔtkRbkc0vbkbk)

    将论文式(14)带入 α b k + 1 b k \alpha^{b_k}_{b_{k+1}} αbk+1bk有:

    δ α b k + 1 b k = α b k + 1 b k − R c 0 b k ( s p b k + 1 c 0 − s p b k c 0 + 1 2 g c 0 Δ t k 2 − R b k c 0 v b k b k Δ t k ) \delta \alpha^{b_k}_{b_{k+1}}=\alpha^{b_k}_{b_{k+1}}-R^{b_k}_{c_0}(sp^{c_0}_{b_{k+1}}-sp^{c_0}_{b_{k}}+\frac{1}{2} g^{c_0}\Delta t_k^2-R^{c_0}_{b_k}v^{b_k}_{b_k} \Delta t_k) δαbk+1bk=αbk+1bkRc0bk(spbk+1c0spbkc0+21gc0Δtk2Rbkc0vbkbkΔtk)

    = α b k + 1 b k − R c 0 b k ( s p c k + 1 c 0 − R b k + 1 c 0 p c b − ( s p c k c 0 − R b k c 0 p c b ) + 1 2 g c 0 Δ t k 2 − R b k c 0 v b k b k Δ t k ) =\alpha^{b_k}_{b_{k+1}}-R^{b_k}_{c_0}(sp^{c_0}_{c_{k+1}}-R^{c_0}_{b_{k+1}} p^{b}_{c}-(sp^{c_0}_{c_k}-R^{c_0}_{b_k} p^{b}_{c})+\frac{1}{2} g^{c_0}\Delta t_k^2-R^{c_0}_{b_k}v^{b_k}_{b_k} \Delta t_k) =αbk+1bkRc0bk(spck+1c0Rbk+1c0pcb(spckc0Rbkc0pcb)+21gc0Δtk2Rbkc0vbkbkΔtk)

    = α b k + 1 b k − R c 0 b k s ( p c k + 1 c 0 − p c k c 0 ) + R c 0 b k R b k + 1 c 0 p c b − p c b + v b k b k Δ t k − 1 2 R c 0 b k g c 0 Δ t k 2 = 0 3 × 1 =\alpha^{b_k}_{b_{k+1}}-R^{b_k}_{c_0}s(p^{c_0}_{c_{k+1}}-p^{c_0}_{c_k})+R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}p^b_c-p^b_c+v^{b_k}_{b_k}\Delta t_k-\frac{1}{2}R^{b_k}_{c_0} g^{c_0}\Delta t_k^2=0_{3\times1} =αbk+1bkRc0bks(pck+1c0pckc0)+Rc0bkRbk+1c0pcbpcb+vbkbkΔtk21Rc0bkgc0Δtk2=03×1
    这里 v b k b k , s , g c 0 v^{b_k}_{b_k},s,g^{c_0} vbkbk,s,gc0是待优化变量,将其转换成 H x = b Hx=b Hx=b的形式:

    R c 0 b k ( p c k + 1 c 0 − p c k c 0 ) s − v b k b k Δ t k + 1 2 R c 0 b k Δ t k 2 g c 0 = α b k + 1 b k + R c 0 b k R b k + 1 c 0 p c b − p c b R^{b_k}_{c_0}(p^{c_0}_{c_{k+1}}-p^{c_0}_{c_k})s-v^{b_k}_{b_k}\Delta t_k+\frac{1}{2}R^{b_k}_{c_0}\Delta t_k^2 g^{c_0}= \alpha^{b_k}_{b_{k+1}}+R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}p^b_c-p^b_c Rc0bk(pck+1c0pckc0)svbkbkΔtk+21Rc0bkΔtk2gc0=αbk+1bk+Rc0bkRbk+1c0pcbpcb
    写成矩阵形式:
    [ − I Δ t k 0 1 2 R c 0 b k Δ t k 2 R c 0 b k ( p c k + 1 c 0 − p c k c 0 ) ] [ v b k b k v b k + 1 b k + 1 g c 0 s ] = α b k + 1 b k + R c 0 b k R b k + 1 c 0 p c b − p c b \left[\begin{array}{cccc} -I\Delta t_k &0&\frac{1}{2}R^{b_k}_{c_0}\Delta t_k^2 & R^{b_k}_{c_0}(p^{c_0}_{c_{k+1}}-p^{c_0}_{c_k})\end{array}\right] \left[\begin{array}{cccc} v^{b_k}_{b_k}\\v^{b_{k+1}}_{b_{k+1}}\\g^{c_0}\\s \end{array}\right] = \alpha^{b_k}_{b_{k+1}}+R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}p^b_c-p^b_c [IΔtk021Rc0bkΔtk2Rc0bk(pck+1c0pckc0)]vbkbkvbk+1bk+1gc0s=αbk+1bk+Rc0bkRbk+1c0pcbpcb

    对于 β b k + 1 b k \beta^{b_k}_{b_{k+1}} βbk+1bk有:

    δ β b k + 1 b k = β b k + 1 b k − R c 0 b k ( R b k + 1 c 0 v b k + 1 b k + 1 + g c 0 Δ t k − R b k c 0 v b k b k ) = 0 3 × 1 \delta \beta^{b_k}_{b_{k+1}}=\beta^{b_k}_{b_{k+1}}-R^{b_k}_{c_0}(R^{c_0}_{b_{k+1}}v^{b_{k+1}}_{b_{k+1}}+g^{c_0}\Delta t_k-R^{c_0}_{b_k}v^{b_k}_{b_k})=0_{3\times1} δβbk+1bk=βbk+1bkRc0bk(Rbk+1c0vbk+1bk+1+gc0ΔtkRbkc0vbkbk)=03×1
    R c 0 b k R b k + 1 c 0 v b k + 1 b k + 1 + R c 0 b k Δ t k g c 0 − R c 0 b k R b k c 0 v b k b k = β b k + 1 b k R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}v^{b_{k+1}}_{b_{k+1}}+R^{b_k}_{c_0}\Delta t_kg^{c_0}-R^{b_k}_{c_0}R^{c_0}_{b_k}v^{b_k}_{b_k}=\beta^{b_k}_{b_{k+1}} Rc0bkRbk+1c0vbk+1bk+1+Rc0bkΔtkgc0Rc0bkRbkc0vbkbk=βbk+1bk

    写成矩阵形式:
    [ − I R c 0 b k R b k + 1 c 0 R c 0 b k Δ t k 0 ] [ v b k b k v b k + 1 b k + 1 g c 0 s ] = β b k + 1 b k \left[\begin{array}{cccc} -I&R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}&R^{b_k}_{c_0}\Delta t_k& 0\end{array}\right] \left[\begin{array}{cccc} v^{b_k}_{b_k}\\v^{b_{k+1}}_{b_{k+1}}\\g^{c_0}\\s \end{array}\right] = \beta^{b_k}_{b_{k+1}} [IRc0bkRbk+1c0Rc0bkΔtk0]vbkbkvbk+1bk+1gc0s=βbk+1bk

    由此得到论文式(18)(19):

    [ − I Δ t k 0 1 2 R c 0 b k Δ t k 2 R c 0 b k ( p c k + 1 c 0 − p c k c 0 ) − I R c 0 b k R b k + 1 c 0 R c 0 b k Δ t k 0 ] [ v b k b k v b k + 1 b k + 1 g c 0 s ] = [ α b k + 1 b k + R c 0 b k R b k + 1 c 0 p c b − p c b β b k + 1 b k ] \left[\begin{array}{cccc} -I\Delta t_k &0&\frac{1}{2}R^{b_k}_{c_0}\Delta t_k^2 & R^{b_k}_{c_0}(p^{c_0}_{c_{k+1}}-p^{c_0}_{c_k})\\-I&R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}&R^{b_k}_{c_0}\Delta t_k& 0\end{array}\right] \left[\begin{array}{cccc} v^{b_k}_{b_k}\\v^{b_{k+1}}_{b_{k+1}}\\g^{c_0}\\s \end{array}\right] =\left[\begin{array}{cccc} \alpha^{b_k}_{b_{k+1}}+R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}p^b_c-p^b_c\\ \beta^{b_k}_{b_{k+1}} \end{array}\right] [IΔtkI0Rc0bkRbk+1c021Rc0bkΔtk2Rc0bkΔtkRc0bk(pck+1c0pckc0)0]vbkbkvbk+1bk+1gc0s=[αbk+1bk+Rc0bkRbk+1c0pcbpcbβbk+1bk]
    H 6 × 10 X I 10 × 1 = b 6 × 1 H^{6\times10}\mathcal X^{10\times1}_I=b^{6\times1} H6×10XI10×1=b6×1,使用LDLT分解求解:
    H T H X I 10 × 1 = H T b H^TH\mathcal X^{10\times1}_I=H^Tb HTHXI10×1=HTb

    对应代码实现在以下函数中,因为内容和上述理论一致就不放代码了。里面的MatrixXd tmp_A(6, 10)就是H,VectorXd tmp_b(6,1)就是b

    bool LinearAlignment(map<double, ImageFrame> &all_image_frame, Vector3d &g, VectorXd &x)
    

    最后求得尺度s和重力加速度g

    	double s = x(n_state - 1) / 100.0;
        g = x.segment<3>(n_state - 4);
      	//如果重力加速度与参考值差太大或者尺度为负则说明计算错误
        if(fabs(g.norm() - G.norm()) > 1.0 || s < 0)
        {
            return false;
        }
    

    重力细化 RefineGravity()

    考虑到上一步求得的g存在误差,一般认为重力矢量的模是已知的,因此重力向量只剩两个自由度,在切线空间上用两个变量重新参数化重力,将其表示为
    g ^ = ∣ ∣ g ∣ ∣ g ^ ˉ + w 1 b 1 + w 2 b 2 = ∣ ∣ g ∣ ∣ g ^ ˉ + b ⃗ 3 × 2 w 2 × 1 \hat g=||g||\bar{\hat g}+w_1b_1+w_2b_2=||g||\bar{\hat g}+\vec b^{3\times2}w^{2\times1} g^=gg^ˉ+w1b1+w2b2=gg^ˉ+b 3×2w2×1

    其中 w 2 × 1 = [ w 1 , w 2 ] T , b ⃗ 3 × 2 = [ b 1 , b 2 ] w^{2\times1}=[w_1,w_2]^T,\vec b^{3\times2}=[b_1,b_2] w2×1=[w1,w2]T,b 3×2=[b1,b2]
    g ^ ˉ \bar{\hat g} g^ˉ为上一步求得的重力方向,, b 1 、 b 2 b_1、b_2 b1b2 g ^ \hat g g^的正切平面上且正交。 b 1 b_1 b1 b 2 b_2 b2的设置为:
    在这里插入图片描述
    将其带入论文式(18)(19)中,有:

    [ − I Δ t k 0 1 2 R c 0 b k Δ t k 2 b ⃗ R c 0 b k ( p c k + 1 c 0 − p c k c 0 ) − I R c 0 b k R b k + 1 c 0 R c 0 b k Δ t k b ⃗ 0 ] [ v b k b k v b k + 1 b k + 1 w s ] = [ δ α b k + 1 b k + R c 0 b k R b k + 1 c 0 p c b − p c b − 1 2 R c 0 b k Δ t k 2 ∣ ∣ g ∣ ∣ g ^ ˉ β b k + 1 b k − R c 0 b k Δ t k ∣ ∣ g ∣ ∣ g ^ ˉ ] \left[\begin{array}{cccc} -I\Delta t_k &0&\frac{1}{2}R^{b_k}_{c_0}\Delta t_k^2 \vec b & R^{b_k}_{c_0}(p^{c_0}_{c_{k+1}}-p^{c_0}_{c_k})\\-I&R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}&R^{b_k}_{c_0}\Delta t_k \vec b& 0\end{array}\right] \left[\begin{array}{cccc} v^{b_k}_{b_k}\\v^{b_{k+1}}_{b_{k+1}}\\w\\s \end{array}\right] =\left[\begin{array}{cccc} \delta \alpha^{b_k}_{b_{k+1}}+R^{b_k}_{c_0}R^{c_0}_{b_{k+1}}p^b_c-p^b_c-\frac{1}{2}R^{b_k}_{c_0}\Delta t_k^2||g||\bar{\hat g}\\ \beta^{b_k}_{b_{k+1}}-R^{b_k}_{c_0}\Delta t_k||g||\bar{\hat g} \end{array}\right] [IΔtkI0Rc0bkRbk+1c021Rc0bkΔtk2b Rc0bkΔtkb Rc0bk(pck+1c0pckc0)0]vbkbkvbk+1bk+1ws=[δαbk+1bk+Rc0bkRbk+1c0pcbpcb21Rc0bkΔtk2gg^ˉβbk+1bkRc0bkΔtkgg^ˉ]
    H 6 × 9 X I 9 × 1 = b 6 × 1 H^{6\times9}\mathcal X^{9\times 1}_I=b^{6\times1} H6×9XI9×1=b6×1。使用LDLT分解求解:
    H T H X I 9 × 1 = H T b H^TH\mathcal X^{9\times1}_I=H^Tb HTHXI9×1=HTb

    在代码实现中,以下函数用于重力细化,其流程基本对应上文推导,方程迭代求解4次:

    void RefineGravity(map<double, ImageFrame> &all_image_frame, Vector3d &g, VectorXd &x)
    

    函数中的MatrixXd lxly(3, 2)= TangentBasis(g0); 即对应 b ⃗ 3 × 2 = [ b 1 , b 2 ] \vec b^{3\times2}=[b_1,b_2] b 3×2=[b1,b2]
    VectorXd dg = x.segment<2>(n_state - 3); 即对应 w 2 × 1 = [ w 1 , w 2 ] T w^{2\times1}=[w_1,w_2]^T w2×1=[w1,w2]T

    以下函数对应论文中Algorithm 1,用于在半径为g的半球找到切面的一对正交基。

    MatrixXd TangentBasis(Vector3d &g0)
    {
        Vector3d b, c;
        Vector3d a = g0.normalized();
        Vector3d tmp(0, 0, 1);
        if(a == tmp)
            tmp << 1, 0, 0;
        b = (tmp - a * (a.transpose() * tmp)).normalized();
        c = a.cross(b);
        MatrixXd bc(3, 2);
        bc.block<3, 1>(0, 0) = b;
        bc.block<3, 1>(0, 1) = c;
        return bc;
    }
    

    外参标定

    VINS外参标定指的是对相机坐标系到IMU坐标系的变换矩阵进行在线标定与优化。

    在参数配置文件yaml中,参数estimate_extrinsic反映了外参的情况:
    1、等于0表示这外参已经是准确的了,之后不需要优化;
    2、等于1表示外参只是一个估计值,后续还需要将其作为初始值放入非线性优化中;
    3、等于2表示不知道外参,需要进行标定,从代码estimator.cpp中的processImage()中的以下代码进入,主要是标定外参的旋转矩阵。

    	if(ESTIMATE_EXTRINSIC == 2)//如果没有外参则进行标定
        {
            ROS_INFO("calibrating extrinsic param, rotation movement is needed");
            if (frame_count != 0)
            {
                //得到当前帧与前一帧之间归一化特征点
                vector<pair<Vector3d, Vector3d>> corres = f_manager.getCorresponding(frame_count - 1, frame_count);
                Matrix3d calib_ric;
                //标定从camera到IMU之间的旋转矩阵
                if (initial_ex_rotation.CalibrationExRotation(corres, pre_integrations[frame_count]->delta_q, calib_ric))
                {
                    ROS_WARN("initial extrinsic rotation calib success");
                    ROS_WARN_STREAM("initial extrinsic rotation: " << endl << calib_ric);
                    ric[0] = calib_ric;
                    RIC[0] = calib_ric;
                    ESTIMATE_EXTRINSIC = 1;
                }
            }
        }
    

    InitialEXRotation类

    该类位于vins_estimator/src/initial/initial_ex_rotation.cpp/.h中,用于标定外参旋转矩阵。首先简要介绍一下InitialEXRotation类的成员:

    class InitialEXRotation
    {
    public:
    	InitialEXRotation();//构造函数
    	//标定外参旋转矩阵
        bool CalibrationExRotation(vector<pair<Vector3d, Vector3d>> corres, Quaterniond delta_q_imu, Matrix3d &calib_ric_result);
    private:
    	//求解帧间cam坐标系的旋转矩阵
    	Matrix3d solveRelativeR(const vector<pair<Vector3d, Vector3d>> &corres);
    	//三角化验证Rt
        double testTriangulation(const vector<cv::Point2f> &l,
                                 const vector<cv::Point2f> &r,
                                 cv::Mat_<double> R, cv::Mat_<double> t);
        //本质矩阵SVD分解计算4组Rt值
        void decomposeE(cv::Mat E,
                        cv::Mat_<double> &R1, cv::Mat_<double> &R2,
                        cv::Mat_<double> &t1, cv::Mat_<double> &t2);
                        
        int frame_count;//帧计数器
        vector< Matrix3d > Rc;//帧间cam的R,由对极几何得到
        vector< Matrix3d > Rimu;//帧间IMU的R,由IMU预积分得到
        vector< Matrix3d > Rc_g;//每帧IMU相对于起始帧IMU的R
        Matrix3d ric;//cam到IMU的外参
    };
    

    CalibrationExRotation() 标定外参旋转矩阵

    该函数目的是标定外参的旋转矩阵。由于函数内部并不复杂,将所有内部调用的函数也放在一起介绍。
    1、solveRelativeR(corres)根据对极几何计算相邻帧间相机坐标系的旋转矩阵,这里的corres传入的是当前帧和其之前一帧的对应特征点对的归一化坐标。
    1.1、将corres中对应点的二维坐标分别存入ll与rr中。

            vector<cv::Point2f> ll, rr;
            for (int i = 0; i < int(corres.size()); i++)
            {
                ll.push_back(cv::Point2f(corres[i].first(0), corres[i].first(1)));
                rr.push_back(cv::Point2f(corres[i].second(0), corres[i].second(1)));
            }
    

    1.2、根据对极几何求解这两帧的本质矩阵

            cv::Mat E = cv::findFundamentalMat(ll, rr);
    

    1.3、对本质矩阵进行svd分解得到4组Rt解

            cv::Mat_<double> R1, R2, t1, t2;
            decomposeE(E, R1, R2, t1, t2);
    

    1.4、采用三角化对每组Rt解进行验证,选择是正深度的Rt解

            double ratio1 = max(testTriangulation(ll, rr, R1, t1), testTriangulation(ll, rr, R1, t2));
            double ratio2 = max(testTriangulation(ll, rr, R2, t1), testTriangulation(ll, rr, R2, t2));
            cv::Mat_<double> ans_R_cv = ratio1 > ratio2 ? R1 : R2;
    

    1.5、这里的R是上一帧到当前帧的变换矩阵,对其求转置为当前帧相对于上一帧的姿态。

            Matrix3d ans_R_eigen;
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                    ans_R_eigen(j, i) = ans_R_cv(i, j);
            return ans_R_eigen;
    }
    
    

    2、获取相邻帧之间相机坐标系和IMU坐标系的旋转矩阵,存入vector中

        frame_count++;
        Rc.push_back(solveRelativeR(corres));//帧间cam的R,由对极几何得到
        Rimu.push_back(delta_q_imu.toRotationMatrix());//帧间IMU的R,由IMU预积分得到
        Rc_g.push_back(ric.inverse() * delta_q_imu * ric);//每帧IMU相对于起始帧IMU的R
    

    3、求解相机到IMU的外参旋转矩阵,根据等式:
    R c k + 1 b k = R b k + 1 b k ⋅ R c b = R c b ⋅ R c k + 1 c k R_{c_{k+1}}^{b_k}=R_{b_{k+1}}^{b_k} \cdot R_c^b = R_c^b \cdot R_{c_{k+1}}^{c_k} Rck+1bk=Rbk+1bkRcb=RcbRck+1ck
    其中 R b k + 1 b k R_{b_{k+1}}^{b_k} Rbk+1bk为IMU坐标系之间的旋转矩阵, R c k + 1 c k R_{c_{k+1}}^{c_k} Rck+1ck为相机坐标系之间的旋转矩阵, R c b R_c^b Rcb为从相机到IMU的旋转矩阵。
    用四元素重写:
    q b k + 1 b k ⊗ q c b = q c b ⊗ q c k + 1 c k q_{b_{k+1}}^{b_k} \otimes q_c^b = q_c^b \otimes q_{c_{k+1}}^{c_k} qbk+1bkqcb=qcbqck+1ck
    [ Q 1 ( q b k + 1 b k ) − Q 2 ( q c k + 1 c k ) ] ⋅ q c b = Q c b ⋅ q c b = 0 [\mathcal{Q_1}(q_{b_{k+1}}^{b_k})-\mathcal{Q_2}(q_{c_{k+1}}^{c_k})]\cdot q_c^b=Q^b_c\cdot q_c^b=0 [Q1(qbk+1bk)Q2(qck+1ck)]qcb=Qcbqcb=0
    将多个帧之间的等式关系一起构建超定方程 A x = 0 Ax=0 Ax=0。对A进行svd分解,其中最小奇异值对应的右奇异向量便为结果x,即旋转四元数 q c b q_c^b qcb

    在代码中,L为相机旋转四元数的左乘矩阵,R为IMU旋转四元数的右乘矩阵,因此其实构建的是:
    q b c ⊗ q b k + 1 b k = q c k + 1 c k ⊗ q b c q_b^c \otimes q_{b_{k+1}}^{b_k} =q_{c_{k+1}}^{c_k} \otimes q_b^c qbcqbk+1bk=qck+1ckqbc
    所求的x是 q b c q_b^c qbc,在最后需要转换成旋转矩阵并求逆。

        Eigen::MatrixXd A(frame_count * 4, 4);
        A.setZero();
        int sum_ok = 0;
        for (int i = 1; i <= frame_count; i++)
        {
            Quaterniond r1(Rc[i]);
            Quaterniond r2(Rc_g[i]);
            double angular_distance = 180 / M_PI * r1.angularDistance(r2);
            ROS_DEBUG(
                "%d %f", i, angular_distance);
            //huber核函数做加权
            double huber = angular_distance > 5.0 ? 5.0 / angular_distance : 1.0;
            ++sum_ok;
            Matrix4d L, R;
            //L R 分别为左乘和右乘矩阵
            double w = Quaterniond(Rc[i]).w();
            Vector3d q = Quaterniond(Rc[i]).vec();
            L.block<3, 3>(0, 0) = w * Matrix3d::Identity() + Utility::skewSymmetric(q);
            L.block<3, 1>(0, 3) = q;
            L.block<1, 3>(3, 0) = -q.transpose();
            L(3, 3) = w;
    
            Quaterniond R_ij(Rimu[i]);
            w = R_ij.w();
            q = R_ij.vec();
            R.block<3, 3>(0, 0) = w * Matrix3d::Identity() - Utility::skewSymmetric(q);
            R.block<3, 1>(0, 3) = q;
            R.block<1, 3>(3, 0) = -q.transpose();
            R(3, 3) = w;
    
            A.block<4, 4>((i - 1) * 4, 0) = huber * (L - R);
        }
    
        //svd分解中最小奇异值对应的右奇异向量作为旋转四元数
        JacobiSVD<MatrixXd> svd(A, ComputeFullU | ComputeFullV);
        Matrix<double, 4, 1> x = svd.matrixV().col(3);
        Quaterniond estimated_R(x);
        ric = estimated_R.toRotationMatrix().inverse();
    

    4、至少迭代计算了WINDOW_SIZE次,且R的奇异值大于0.25才认为标定成功

        Vector3d ric_cov;
        ric_cov = svd.singularValues().tail<3>();
        if (frame_count >= WINDOW_SIZE && ric_cov(1) > 0.25)
        {
            calib_ric_result = ric;
            return true;
        }
        else
            return false;
    

    在初步标定完外参的旋转矩阵后,对旋转矩阵与平移向量的优化将在非线性优化中一起介绍。

    展开全文
  • 网上找到具体解决方法: 进入cmd; 进入\Microsoft Visual Studio 10.0\Common7\IDE 目录; 执行devenv /resetuserdata命令;...回车即可,此时会初始化用户设置。 4. 重启VS2015   后记: 我在重启...

    网上找到具体解决方法:

    1. 进入cmd;
    2. 进入\Microsoft Visual Studio 10.0\Common7\IDE 目录;
    3. 执行devenv /resetuserdata命令;
    4. 重启VS2015

    如下图:

    1.

    2.

    注意:是Program Files(x86)目录

    3.

    回车即可,此时会初始化用户设置。

    4. 重启VS2015

     

    后记:

    我在重启后出现未能正确加载Vsix包这一错误,在网上也没找到相应错误,但是在博客https://blog.csdn.net/ljt350740378/article/details/50582193有类似问题,

    故解决如下:

    1. 进入cmd;
    2. 进入\Microsoft Visual Studio 10.0\Common7\IDE 目录;
    3. 执行devenv /ResetSettings命令;
    4. 重启VS2015

    具体也如上图示,顺利解决了。

    展开全文
  • 目录变量定义step 1: init sys variable计算blocksizestep 2: mpp system init 为了兼容性的问题,海思为多个版本的3518e编写了sample 我们分析这一路 变量定义 ... /* max count of pools, (0,VB_MAX_PO

    为了兼容性的问题,海思为多个版本的3518e编写了sample
    在这里插入图片描述
    我们分析这一路

    变量定义

    在这里插入图片描述
    这里有几个关键性的变量
    playload数组中有3个成员,对应3个通道。编码尺寸也对应3个通道

    typedef struct hiVB_CONF_S
    {
        HI_U32 u32MaxPoolCnt;     /* max count of pools, (0,VB_MAX_POOLS]  */    
        struct hiVB_CPOOL_S
        {
            HI_U32 u32BlkSize;
            HI_U32 u32BlkCnt;
            HI_CHAR acMmzName[MAX_MMZ_NAME_LEN];
        }astCommPool[VB_MAX_COMM_POOLS];
    } VB_CONF_S;
    

    VB_CONF_S stVbConf;
    这个结构体是视频缓冲池的相关属性
    包括缓冲池的最大值、块的数量和大小等

    step 1: init sys variable

    /******************************************
         step  1: init sys variable 
        ******************************************/
        memset(&stVbConf,0,sizeof(VB_CONF_S));
        
    	SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);
        if (PIC_HD1080 == enSize[0])
        {
            enSize[1] = PIC_VGA;
    		s32ChnNum = 2;
        }
        else if (PIC_HD720 == enSize[0])
        {
            enSize[1] = PIC_VGA;			
    		enSize[2] = PIC_QVGA;
    		s32ChnNum = 3;
        }
        else
        {
            printf("not support this sensor\n");
            return HI_FAILURE;
        }
    #ifdef hi3518ev201
    	s32ChnNum = 1;
    #endif
    	printf("s32ChnNum = %d\n",s32ChnNum);
    
        stVbConf.u32MaxPoolCnt = 128;//最大视频缓存池数量,根据内存大小给出的经验值
    
        /*video buffer*/
    	if(s32ChnNum >= 1)//根据通道数量计算mpp结构体参数,填充视频缓存池结构体
        {
        	//u32BlkSize为一帧图像所需要的大小,也就是块大小
    	    u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\//第一个参数为扫描方式
    	                enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);//计算块大小,//传入分辨率格式,像素格式,对齐排布,用于像素取整//关键是把enSize[0]传进去,
    	    stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;//设置块大小  //从上面的得到enSize[0]为PIC_HD1080
    	    stVbConf.astCommPool[0].u32BlkCnt = g_u32BlkCnt;//设置块数量
    	}
    	if(s32ChnNum >= 2)
        {
    	    u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
    	                enSize[1], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
    	    stVbConf.astCommPool[1].u32BlkSize = u32BlkSize;
    	    stVbConf.astCommPool[1].u32BlkCnt =g_u32BlkCnt;
    	}
    	if(s32ChnNum >= 3)//3个通道对应三个视频缓存池,因为不同通道也就是不同分辨率,他们对应的码流不同
        {
    		u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
                    enSize[2], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
    		stVbConf.astCommPool[2].u32BlkSize = u32BlkSize;
    		stVbConf.astCommPool[2].u32BlkCnt = g_u32BlkCnt;
        }
    

    首先初始化视频缓存池
    接着调用SAMPLE_COMM_VI_GetSizeBySensor,通过配置makefile传入的sensor类型进行图像尺寸及通道数的判断

    由于我们使用的是AR0130,于是走的是else。即三通道码流

    一路主码流(原始的没有裁剪的,全功能版本),另外两路(在主码流的基础上经过裁剪缩放等操作简化而来,阉割版本)为子码流,客户用手机看的话就用子码流看就行了,没必要看主码流,还可以省点网络带宽,还比较流畅。

    裸流:开发过程中的半成品,把文件头一解析就知道这个文件的分辨率, mp4、rtsp等都可以查一下文件头来看。
    裸流(h264)是没有这个文件头的。

    怎么看sensor支持的图像大小?

    • 720p 1280x720=92万像素至少要留点余量,100万像素以上
    • 1080p 1920x1080 = 207万像素 至少sensor也要210万像素以上

    为什么不同的码流要对应不同的pool呢?
    因为不同的码流分辨率不一样,对应的blocksize的大小也就不一样

    计算blocksize

    最后根据不同通道不同分辨率,计算blocksize
    调用了SAMPLE_COMM_SYS_CalcPicVbBlkSize,传入

    • 视频输入制式类型(图像的扫描方式,早期CRT显示用到,现在意义只有向前兼容)
    • 编码尺寸
    • 像素格式
    • 像素对齐(根据像素排布进行向上取整对齐)
    /******************************************************************************
    * function : calculate VB Block size of picture.
    ******************************************************************************/
    HI_U32 SAMPLE_COMM_SYS_CalcPicVbBlkSize(VIDEO_NORM_E enNorm, PIC_SIZE_E enPicSize, PIXEL_FORMAT_E enPixFmt, HI_U32 u32AlignWidth)
    {
        HI_S32 s32Ret = HI_FAILURE;
        SIZE_S stSize;
        HI_U32 u32VbSize;
        HI_U32 u32HeaderSize;
    
        s32Ret = SAMPLE_COMM_SYS_GetPicSize(enNorm, enPicSize, &stSize);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("get picture size[%d] failed!\n", enPicSize);
                return HI_FAILURE;
        }
    
        if (PIXEL_FORMAT_YUV_SEMIPLANAR_422 != enPixFmt && PIXEL_FORMAT_YUV_SEMIPLANAR_420 != enPixFmt)
        {
            SAMPLE_PRT("pixel format[%d] input failed!\n", enPixFmt);
                return HI_FAILURE;
        }
    
        if (16!=u32AlignWidth && 32!=u32AlignWidth && 64!=u32AlignWidth)
        {
            SAMPLE_PRT("system align width[%d] input failed!\n",\
                   u32AlignWidth);
                return HI_FAILURE;
        }
        //SAMPLE_PRT("w:%d, u32AlignWidth:%d\n", CEILING_2_POWER(stSize.u32Width,u32AlignWidth), u32AlignWidth);
        u32VbSize = (CEILING_2_POWER(stSize.u32Width, u32AlignWidth) * \
                CEILING_2_POWER(stSize.u32Height,u32AlignWidth) * \
               ((PIXEL_FORMAT_YUV_SEMIPLANAR_422 == enPixFmt)?2:1.5));
    
        VB_PIC_HEADER_SIZE(stSize.u32Width, stSize.u32Height, enPixFmt, u32HeaderSize);
        u32VbSize += u32HeaderSize;
    
        return u32VbSize;
    }
    

    SAMPLE_COMM_SYS_CalcPicVbBlkSize中又调用了SAMPLE_COMM_SYS_GetPicSize,用于计算图像大小
    在这里插入图片描述
    可以看到,在早期电视显示标准中,需要考虑制式,现在的数字显示就不需要。考虑兼容问题,于是这个sample就这样写了

    接下来判断像素格式,这个sample只支持422和420
    在这里插入图片描述
    再考虑对齐,此sample只考虑16bit、32bit和64bit对齐,我们选用的是64字节对齐
    在这里插入图片描述
    最后就是计算blocksize的大小,宽x高x字节数,用了一个宏来计算宽和高CEILING_2_POWER

    #define CEILING_2_POWER(x,a) ( ((x) + ((a) - 1) ) & ( ~((a) - 1) ) )
    若输入15和2,即15和2对齐,输出16
    若输入15和64,即15和64对齐,输出64
    实际中,比如720p,代码中规定向64取整,即1280向上增加到64的倍数,即1280
    如果是一个其他的数,如WVGA是854,和64向上取整,即896

    每一个像素占多少字节,

    • yuv422
      四个像素占8个字节,每个像素占2字节
    • yuv420
      四个像素占6个字节,每个像素占1.5个字节

    宽x高x字节数就是blocksize

    然后要留一些余量给后续操作做准备
    在这里插入图片描述
    在这里插入图片描述
    为文件头留出余量后就计算完毕

    以上,第一步计算mpp所需要的参数就计算好了
    这些参数是根据应用需求进行核算的

    但是此时系统还没有启动
    于是进入第二步,进行初始化

    step 2: mpp system init

    第二步所有操作包在了一个函数里
    在这里插入图片描述

    /******************************************************************************
    * function : vb init & MPI system init
    ******************************************************************************/
    HI_S32 SAMPLE_COMM_SYS_Init(VB_CONF_S *pstVbConf)
    {
        MPP_SYS_CONF_S stSysConf = {0};
        HI_S32 s32Ret = HI_FAILURE;
    
        HI_MPI_SYS_Exit();
        HI_MPI_VB_Exit();
    
        if (NULL == pstVbConf)
        {
            SAMPLE_PRT("input parameter is null, it is invaild!\n");
            return HI_FAILURE;
        }
    
        s32Ret = HI_MPI_VB_SetConf(pstVbConf);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("HI_MPI_VB_SetConf failed!\n");
            return HI_FAILURE;
        }
    
        s32Ret = HI_MPI_VB_Init();
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("HI_MPI_VB_Init failed!\n");
            return HI_FAILURE;
        }
    
        stSysConf.u32AlignWidth = SAMPLE_SYS_ALIGN_WIDTH;
        s32Ret = HI_MPI_SYS_SetConf(&stSysConf);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("HI_MPI_SYS_SetConf failed\n");
            return HI_FAILURE;
        }
    
        s32Ret = HI_MPI_SYS_Init();
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("HI_MPI_SYS_Init failed!\n");
            return HI_FAILURE;
        }
    
        return HI_SUCCESS;
    }
    

    HI_MPI_SYS_Exit()HI_MPI_VB_Exit()这两个是怕你之前的VB和SYS没处理干净,保险起见,打扫卫生的。需要注意,释放的时候是SYS在前,VB在后的(文档规定)

    HI_MPI_VB_SetConfHI_MPI_VB_Init是SDK中的函数
    在定义好视频缓冲池后将结构体交给SDK进行处理,分配内存

    只要把VB(缓冲池搞好了),SYS里面都是自动化的,海思封装的特别好,我们要操心的是VB的参数pstVbconf

    展开全文
  • 经常使用word软件,难免是遇到这样那样的问题,今天笔者在录制新宏时出现错误提示“无法初始化Visual Basic环境”,对于此问题我们该如何来解决。 工具/原料 word 2003 ...
  • Vb6的变量初始化问题

    2005-01-21 13:12:00
    新建工程,新建两个窗体,代码如下: form1: Private Sub Command1_Click()  Form2.Show 1 ...这是因为VB好像没有每次都初始化 如果 Form2.Show 1 改为 DIM II AS NEW FORM2 II.SHOW 1 那么就不会了
  • 而在局部定义数组的时候,数组会自动初始化为全0,所以数组在刚被定义的时候就塞进Stack区了,才会出现int dis[520073]直接报堆栈溢出的问题。  证明上述说法的代码如下: #include using namespace std; ...
  • xgBLACK, 4, 34 '全部怪物 在初始化里,我们还将对音乐、音效、游戏状态等进行初始化,这些留在后边再说。 这里定义一个全局变量,用来控制游戏的状态: Running = 9 '开始游戏 'Running = 9 '游戏第一面,选择界面...
  • 我在使用 ListView 的时候,有一个问题困扰我挺久:能不能控制 ListView 初始化时加载的Item数量?比如,如果我刚打开一个页面,ListView 关联 Adapter 就开始加载十几条数据,如果加载的 Item 是 TextView 还好,...
  • 局部静态变量的初始化与异常

    千次阅读 2011-03-26 22:34:00
    编程的过程中,思考了一个...当一个局部的静态变量使用一个函数的返回值初始化时,如果该函数抛出异常,那么,局部静态变量是否被定义成功,即,如果再次调用包含局部静态变量的函数,抛出异常的函数会不会再次被调用。
  • 应用程序正常初始化(0xc000007b)失败。请单击“确定”,终止应用程序。 确定 问题分析 情况一、已安装的相关框架、组件损坏或未安装相关框架、组件 在“控制面板\所有控制面板项\程序和功能”查看是否已...
  • PowerPC基于vxWorks的中断初始化分析

    千次阅读 2018-12-07 19:32:08
     本文主要介绍P2020芯片中vxWorks中断初始化过程(部分讲解是以linux为例)。P2020属于PPC85XX系列,内核为e500v2,它是PowerPC体系结构中主要应用于通信领域的片子,PowerPC体系结构规范发布于1993年,它是一个64位...
  • 第三章:属性的设计,属性映射,属性的初始化,属性的保存 第一节:属性的设计  废话不多说,前文再续,书接上一回。上回说到,如何对界面进行布置和写动态修改控件大小的代码;这一回,我来介绍一下如何设计属性,...
  • VIM 初始化

    2015-01-05 21:49:15
    set vb t_vb= 关闭默认的高亮匹配显示 set nohls 在右下角显示光标位置的状态行 set ruler 光标自动移动到匹配的搜索字符串 set incsearch 文件的自动备份(修改后备份) if has("vms")  set nobackup else  set ...
  • [b][color=#000080]说明:在调试时,发现在提取Form1.WillReNew属性值时,会再次进入类Form1,对其中的每一个属性值进行初始化!!! 我猜测问题的关键词可能与“线程”有关。[/color][/b] 5、最后再点击主程序...