Ceres 三部曲 之 入門簡介


    Ceres 翻譯為谷神星,是太陽系中的一顆矮行星,於1801年被意大利神父 Piazzi 首次觀測到,但隨后 Piazzi 因為生病,跟丟了它的運行軌跡。

    幾個月后,德國數學家 Gauss,利用最小二乘法,僅僅依靠 Piazzi 之前觀測到的12個數據,便成功的預測了谷神星的運行軌跡。

    兩百多年后,為了解決一些復雜的最優化問題 (如:帶邊界約束的非線性最小二乘、一般的無約束最優化等),谷歌開發了一個 C++ 庫 Ceres Solver

    之所以取名 Ceres Solver,是為了紀念 Gauss 利用最小二乘法,成功的預測了谷神星軌跡,這個在最優化、統計學、天文學歷史上,都具有重要意義的事件 

     

1  Ceres 簡介

1.1  非線性最小二乘

    Ceres 可用來解決帶邊界約束的非線性最小二乘問題,如下:

    $\quad \begin{split} \min_x &\quad \frac{1}{2}\sum_{i} \rho_i\left(\left\|f_i\left(x_{i_1}, x_{i_2}, ... ,x_{i_k}\right)\right\|^2\right) \\ &\quad l_j \le x_j \le u_j \end{split}$ 

    其中,1) $ \rho_i\left(\left\|f_i\left(x_{i_1},x_{i_2},...,x_{i_k}\right)\right\|^2\right) $ 為殘差塊

               2) $f_i(\cdot)$ 為代價函數,取決於參數塊 $\left[x_{i_1},x_{i_2},... , x_{i_k}\right]$

               3) $\rho_i$ 為損失函數,是一個標量函數,主要用來消除異常點對求解過程的影響

    令損失函數 $\rho_i = x$ 為恆等函數,並放寬約束條件 $[-\infty, \infty]$,則得到無約束的非線性最小二乘形式

    $\quad \begin{split}\frac{1}{2}\sum_{i} \left\|f_i\left(x_{i_1}, ... ,x_{i_k}\right)\right\|^2 \end{split}$

    以上形式在科學和工程領域有着廣泛的應用,如,統計學中的曲線擬合,計算機視覺的三維重建等

1.2  Ceres 的特點

    1) 模型接口簡潔

        - 求導簡單;魯棒的損失函數;局部參數化

    2) 求解方法多

        - 信賴域法:Levenberg-Marquardt, Powell's Dogleg, Subspace dogleg

        - 線搜索法:Non-linear Conjugate Gradients, BFGS, LBFGS

    3) 求解質量高

        - 在 NIST 數據集下,按照 Mondragon 和 Borchers 的測試標准,Ceres 的准確度最高

                 

1.3  應用實例

    在谷歌內部,Ceres 已經被應用於多個產品中,如:谷歌街景中汽車、飛行器的位姿估計;PhotoTours 中 3D 模型的建立;SLAM 算法 Cartographer 等

    此外,一些公司和研究所也在使用 Ceres,像是 Southwest Research Institute 的機器人視覺系統標定,OpenMVG 的光束平差 (BA) 問題,Willow Garage 的 SLAM 問題等

 

2  編譯配置

    Win10  64-bit ;VS 2019 社區版,下載地址 ;CMake 解壓版,下載地址

2.1  源文件

    - Ceres Solver 源文件,下載地址,從 2.0 開始,需要支持 C++14 的編譯器

    - eigen 源文件,必須,>=3.3,下載地址

    - glog 源文件,推薦,>=0.3.1,下載地址,及其依賴庫 gflags,下載地址

2.2  配置生成

    1)  將源文件 ceres、eigen、glog 和 gflags 解壓,運行 cmake-gui.exe,配置生成 eigen (先點 "Configure",再點 "Generate" 即可)

    2)  編譯 gflags,先點 "Configure",再點 "Generate",然后點 "Open Project",在 VS 中打開工程,最后在 debug 和 release 模式下分別編譯

    3)  編譯 glog,操作同 2),其中 cmake 配置不再贅述,如:CMAKE_CONFGURATION_TYPES 只保留 Debug 和 Release,gflags_DIR 指向含 gflags-config.cmake 的目錄等

    4)  編譯 ceres,操作同 2),注意配置 eigen、glog 和 gflags 的 _DIR 目錄,並勾上 BUILD_SHARED_LIBS 以便生成 dll 庫

         

2.3  VS 配置

    將生成的 .lib 和 .dll 文件放在特定目錄下,並新建 include 文件夾,匯總對應的 .h 文件

  1)環境變量

      計算機 -> 屬性 -> 高級系統設置 -> 環境變量,編輯系統變量里的 path 變量

D:\3rdparty\gflags\bin\Debug
D:\3rdparty\glog\bin\Debug
D:\3rdparty\ceres\bin\Debug  

  2)  頭文件和庫文件

      在 VS 中配置 ceres, eigen, glog 和 gflags 的 頭文件目錄,以及 庫文件目錄

      注:在編譯時,若出現頭文件缺失,則在源文件中找到對應的 .h,拷貝到 include 目錄中即可

             

  3)  依賴庫

      添加對應的 .lib 依賴庫,如下:

ceres-debug.lib
gflags_debug.lib
glogd.lib

  4)  錯誤處理

    運行程序,如出現如下錯誤,則在 "項目屬性 - C/C++ - 預處理器定義" 中,定義 _USE_MATH_DEFINES 宏可解決

      

 

3  代碼實例

    給定一個目標函數 $\begin{split} \frac{1}{2}(10 -x)^2 \end{split}$,求使其取值最小時,對應的 $x$

3.1  求解步驟

    1)  構建代價函數

         Ceres 中利用仿函數,通過重載 operator() 運算符,來實現代價函數的定義,本例中的代價函數為 $f(x) = 10 -x $

// A templated cost functor that implements the residual r = x - 10
struct CostFunctor {
    template <typename T> bool operator()(const T* const x, T* residual) const {
        residual[0] = x[0] - 10.0;
        return true;
    }
};  

    2)  構建殘差塊

   // Build the problem
    Problem problem;
    // Set up the cost function (also known as residual)
    CostFunction* cost_function = new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
    problem.AddResidualBlock(cost_function, nullptr, &x);  

    3)  配置求解器

    // The options structure, which controls how the solver operates
    Solver::Options options;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;  

    4)  求解

    // Run the solver
    Solver::Summary summary;
    Solve(options, &problem, &summary);

3.2  完整代碼      

#include "ceres/ceres.h"

using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;
using ceres::Solve;

// A templated cost functor that implements the residual r = x - 10.
struct CostFunctor {
    template <typename T> bool operator()(const T* const x, T* residual) const {
        residual[0] = 10.0 - x[0];
        return true;
    }
};

int main() 
{
    // The variable to solve for with its initial value
    double x = 0.5;
    const double initial_x = x;

    // Build the problem.
    Problem problem;
    problem.AddResidualBlock(new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor), nullptr, &x);

    // The options structure, which controls how the solver operates
    Solver::Options options;
    options.linear_solver_type = ceres::DENSE_QR;
    options.minimizer_progress_to_stdout = true;

    // Run solver
    Solver::Summary summary;
    Solve(options, &problem, &summary);

    std::cout << summary.BriefReport() << "\n";
    std::cout << "x : " << initial_x << " -> " << x << "\n";
}  

    運行結果如下:

   

  

 

 參考資料

    Ceres Solver 在 Windows 下的安裝

    Mondragon 和 Borchers 標准對比測試    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM