1. 基础


Ceres solver 是谷歌开发的一款用于非线性优化的库,在谷歌的开源激光雷达slam项目cartographer中被大量使用。Ceres官网上的文档非常详细地介绍了其具体使用方法。文档转入


1.1 下载



1.2 Linux 安装


1.2.1 依赖安装


# CMake
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev
# BLAS & LAPACK
sudo apt-get install libatlas-base-dev
# Eigen3
sudo apt-get install libeigen3-dev
# SuiteSparse and CXSparse (optional)
# - If you want to build Ceres as a _static_ library (the default)
# you can use the SuiteSparse package in the main Ubuntu package
# repository:
sudo apt-get install libsuitesparse-dev
# - However, if you want to build Ceres as a _shared_ library, you must
# add the following PPA:
sudo add-apt-repository ppa:bzindovic/suitesparse-bugfix-1319687
sudo apt-get update
sudo apt-get install libsuitesparse-dev

1.2. 2 安装


tar zxf ceres-solver-1.14.0.tar.gz
mkdir ceres-bin
cd ceres-bin
cmake ../ceres-solver-1.14.0
make -j3
make test
# Optionally install Ceres, it can also be exported using CMake which
# allows Ceres to be used without requiring installation, see the documentation
# for the EXPORT_BUILD_DIR option for more information.
make install

    1.2.3 测试


    您还可以尝试运行命令行捆绑应用程序,其中包含一个包含的问题,这些问题来自华盛顿大学的BAL数据集[Agarwal]。


    bin/simple_bundle_adjuster ../ceres-solver-1.14.0/data/problem-16-22106-pre.txt

      1.3 简易使用


      • Ceres必须已安装 make install

      1.3.1 cmake中


      简单helloworld


      find_package(Ceres REQUIRED)
      include_directories(${CERES_INCLUDE_DIRS})

      # helloworld
      add_executable(helloworld helloworld.cc)
      target_link_libraries(helloworld ${CERES_LIBRARIES})

        1.3.2 ros package


        cmakelists.txt


        find_package(Ceres REQUIRED COMPONENTS SuiteSparse)
        target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC
        “${CERES_INCLUDE_DIRS}”)
        target_link_libraries(${PROJECT_NAME} PUBLIC ${CERES_LIBRARIES})
        • 1
        • 2
        • 3
        • 4

        package.xml


        <depend>ceres-solver</depend>
        • 1

        注意:


        • 在调用find_package(Ceres)时,您可以指定所需的特定Ceres组件(以便将Ceres报告为已找到)。 这允许您指定,例如,您需要使用SuiteSparse支持构建的Ceres版本。 根据定义,如果在调用find_package(Ceres)时没有指定任何组件(默认值),则检测到的任何Ceres版本都将被报告为found,而不管它是使用哪个组件构建的。


        LAPACK: Ceres built using LAPACK (LAPACK=ON).
        SuiteSparse: Ceres built with SuiteSparse (SUITESPARSE=ON).
        CXSparse: Ceres built with CXSparse (CXSPARSE=ON).
        EigenSparse: Ceres built with Eigen’s sparse Cholesky factorization (EIGENSPARSE=ON).
        SparseLinearAlgebraLibrary: Ceres built with at least one sparse linear algebra library. This is equivalent to SuiteSparse OR CXSparse OR EigenSparse.
        SchurSpecializations: Ceres built with Schur specializations (SCHUR_SPECIALIZATIONS=ON).
        OpenMP: Ceres built with OpenMP (OPENMP=ON).
        TBB: Ceres built with Intel Thread Building Blocks (TBB) (TBB=ON).
        Multithreading: Ceres built with a multithreading library. This is equivalent to OpenMP OR TBB.
        C++11: Ceres built with C++11 (CXX11=ON).


        1.4 指定版本+本地安装


        此外,当CMake找到Ceres时,它可以选择检查包版本,如果已在find_package()调用中指定。例如:


        find_package(Ceres 1.2.3 REQUIRED)
        • 1

        本地安装
        如果通过指定-DCMAKE_INSTALL_PREFIX =“/ some / where / local”将Ceres安装在非标准路径中,则用户应将PATHS选项添加到find_package()命令,例如:


        find_package(Ceres REQUIRED PATHS “/some/where/local/“)

          1.5 使用步骤


          使用Ceres求解非线性优化问题,一共分为三个部分:


          1. 第一部分:构建cost fuction,即代价函数,也就是寻优的目标式。这个部分需要使用仿函数(functor)这一技巧来实现,做法是定义一个cost function的结构体,在结构体内重载()运算符,具体实现方法后续介绍。
          2. 第二部分:通过代价函数构建待求解的优化问题。
          3. 第三部分:配置求解器参数并求解问题,这个步骤就是设置方程怎么求解、求解过程是否输出等,然后调用一下Solve方法。

          2. Ceres的Options详解


          Ceres是一个非常优秀的非线性优化库(谷歌出品)。能完成很复杂的优化功能,选项也非常的多,本篇博客就来梳理下这些选项。Ceres的参数主要有三类,一类通用参数,比如迭代次数什么的;第二类是和优化算法的参数;第三类是和线性求解器(在信任域算法中被使用)有关的参数。


          2.1 常用参数


          • bool Solver::Options::IsValid(string _error) const
            检查options是否合法,不合法的话返回false,并将错误信息存到error里面。
          • int Solver::Options::max_num_iterations
            默认值:50
            最大迭代次数。
          • double Solver::Options::max_solver_time_in_seconds
            默认值:1e6
            最长运行时间,单位为秒。
          • int Solver::Options::num_threads
            默认值:1
            Ceres用于评估Jacobian的线程数,越多优化速度越快。
          • DenseLinearAlgebraLibrary Solver::Options::dense_linear_algebra_library_type
            默认值:EIGEN
            Ceres支持使用多个密集线性代数库进行稠密矩阵分解。 目前可选的为EIGEN和LAPACK。 EIGEN始终可用;LAPACK指的是BLAS + LAPACK库,可能有也可能没有(得看编译Ceres库时有没有加入BLAS + LAPACK的支持)。此设置会影响DENSE_QR,DENSE_NORMAL_CHOLESKYDENSE_SCHUR求解器。 对于小到中等大小的求解器(这里的大小应该是指运算量,也就是问题规模的大小),EIGEN是一个很好的选择,但对于大问题,LAPACK + BLAS实现可以在性能上优势很大。
          • SparseLinearAlgebraLibrary Solver::Options::sparse_linear_algebra_library_type
            默认值:SUITE_SPARSE > CX_SPARSE > EIGEN_SPARSE > NO_SPARSE
            Ceres支持使用三个稀疏线性代数库SuiteSparse、CXSparse、EIGEN_SPARSENO_SPARSE意味着不应使用稀疏线性求解器。
            SuiteSparse:高性能稀疏Cholesky分解和近似最低学位排序是一个非常复杂的稀疏线性代数库(就是一群非常牛_的人写的),性能最好,推荐使用。
            CXSparse:SuiteSparse的轻量级替换,不需要 LAPACK / BLAS实现。 因此,它的性能是比SuiteSparse低一点。如果SuiteSparse不可用(编译Ceres的时候没有加入SuiteSparse支持),请考虑使用CXSparse,这是一个更小,更容易编译的库。但是,它在大问题上的性能与SuiteSparse的性能相差巨大。
            EIGEN_SPARSE:Eigen的稀疏线性代数例程,目前,这个库的表现是三个中最差的。说不定在不久的将来性能会有改善。
          • int Solver::Options::num_linear_solver_threads
            默认值:-1
            这是一个废弃的选项,将在1.15中删除。
          • LoggingType Solver::Options::logging_type
            默认值:PER_MINIMIZER_ITERATION 最小化迭代
            每次迭代都打印信息,另一个可选的为SILENT。(无声)
          • bool Solver::Options::minimizer_progress_to_stdout
            默认值:false
            默认情况下,Minimizer(优化器)进度会记录到stderr,具体取决于vlog级别。 如果此标志设置为true,并且Solver::Option:: logging_type不是SILENT,则日志记录输出将发送到stdout(在终端打印出信息)。
          • bool Solver::Options::check_gradients
            默认值:false
            检查由具有有限差分的每个残差块计算的所有雅可比行列式,比较结果,如果它们大不相同,则优化失败。如果设置为true的花比较耗费性能,一般保持默认。
          • double Solver::Options::gradient_check_relative_precision
            默认值: 1e-8
            在gradient checker中检查的精度。 如果雅可比行列式中的元素之间的相对差异超过此数字,则dump该cost term的雅可比行列式。
          • vector Solver::Options::callbacks
            在Minimizer的每次迭代结束时执行的回调。
            它们按照在此vector中指定的顺序执行。 默认情况下,参数仅在优化结束时更新。 如果希望在执行回调时访问更新的参数,则将Solver::Options::update_state_every_iteration需要设置为true。
          • bool Solver::Options::update_state_every_iteration
            默认值:false
            如果为true,则在每个迭代结束时更新参数,否则在优化终止时才更新参数。更多细节请参考链接

          2.2 优化方法无关参数


          • MinimizerType Solver::Options::minimizer_type
            默认值:TRUST_REGION
            可选的为LINE_SEARCH和TRUST_REGION,这是非线性优化的两类算法。参考:Trust Region MethodsLine Search Methods
          • double Solver::Options::gradient_tolerance
            默认值:1e-10
            求解器会在满足










            x





            Π





            (


            x


            ,





            g


            (


            x


            )


            )













            <


            =


            g


            r


            a


            d


            i


            e


            n



            t


            t



            o


            l


            e


            r


            a


            n


            c


            e



            ||x - Π ⊞(x,-g(x))||_∞ <= gradient_tolerance


            xΠ(x,g(x))<=gradienttolerance

            时停止求解。其中||⋅||∞是指最大范数,Π是对边界约束的投影,⊞是与参数矢量相关的整体局部参数化的加法运算。

          2.3 信任区域


          • double Solver::Options::function_tolerance
            默认值:1e-6
            求解器满足












            Δ


            c


            o


            s


            t




            c


            o


            s


            t




            <


            =


            f


            u


            n


            c


            t


            i


            o



            n


            t



            o


            l


            e


            r


            a


            n


            c


            e









            || \frac {\Delta cost}{cost} <=function_tolerance ||


            costΔcost<=functiontolerance

            时停止求解,其中Δcost是Levenberg-Marquardt方法中当前迭代中目标函数值(也就是损失函数)的变化。上面的公式可以这样理解,Δcost/cost非常小了,就说明cost基本没啥变化,就认为已经得到一个解了,故停止优化。
          • TrustRegionStrategyType Solver::Options::trust_region_strategy_type
            默认值:LEVENBERG_MARQUARDT —Ceres支持用于计算信任区域步骤的不同策略。
            LEVENBERG_MARQUARDT :默认的信任区域策略是使用逐步计算用于Levenberg-Marquardt算法
            DOGLEG:Powell的dogleg算法在Cauchy点和Gauss-Newton步骤之间进行插值。 如果VENBERG_MARQUARDT算法使大量的
            不成功的步骤
          • double Solver::Options::max_trust_region_radius
            默认值:1e16
            信任区域半径最大值。
          • double Solver::Options::min_trust_region_radius
            默认值:1e-32
            信任区域的最小值。当信任区域小于此值,会停止优化。
          • double Solver::Options::min_relative_decrease
            默认值:1e-3
            信任域步长(trust region step)相对减少的最小值。
          • double Solver::Options::min_lm_diagonal
            默认值:1e-6
            LEVENBERG MARQUARDT算法使用对角矩阵来规范(regularize)信任域步长。 这是该对角矩阵的值的下限。
          • double Solver::Options::max_lm_diagonal
            默认值:1e32
            LEVENBERG MARQUARDT算法使用对角矩阵来规范(regularize)信任域步长。这是该对角矩阵的值的上限。
          • int Solver::Options::max_num_consecutive_invalid_steps
            默认值:5
            信任区域策略返回的步骤有时可能在数值上无效,通常是因为条件问题。 优化器可以继续尝试使用较小的信任区域/更好的条件问题来解决,而不是崩溃或停止优化。 此参数设置最小化器停止优化之前的连续重试次数。

          2.4 线搜索


          • LineSearchDirectionType Solver::Options::line_search_direction_type
            默认值:LBFGS
            可选的为STEEPEST_DESCENT、NONLINEAR_CONJUGATE_GRADIENT、BFGS和LBFGS,都属于LINE_SEARCH类的算法。
          • LineSearchType Solver::Options::line_search_type
            默认值:WOLFE
            选择是ARMIJO和WOLFE(强WOLFE条件)。 为了保证BFGS和LBFGS线搜索方向算法的基本假设得到满足,应使用WOLFE线性搜索。
          • int Solver::Options::max_lbfgs_rank
            默认值:20
            L-BFGS的Hessian矩阵是对Hessian矩阵的逆的低秩近似。 近似的秩的大小与近似算法的空间和时间复杂度线性相关。 秩越高,近似的质量越好。 然而,由于以下两种原因,质量的提高受到限制。
            ~The method only uses secant information and not actual derivatives.
            ~The Hessian approximation is constrained to be positive definite.
            因此,将该秩增加到一个特别大的数字将大量将花费时间和空间而不会相应地提高求解的质量。不同的问题,可能最佳的秩也不同。

          待续