cam_lidar_calibration代码详解(二)优化计算过程

12 篇文章 2 订阅

目录

一、openga.h解析

二、optimiser.h解析

三、optimiser.cpp解析


先对特征点提取和优化过程进行了梳理,见博客:

​​​​​​​​​​​​cam_lidar_calibration代码详解(一)采样优化部分_可见一班的博客-CSDN博客

这篇博客主要对优化计算的过程及代码进行整理,用到的是遗传算法。主要包含3个文件:

  • openga.h,遗传算法的实现
  • optimiser.h,定义旋转矩阵、变化矩阵优化计算用到的函数
  • optimiser.cpp,执行遗传算法的配置和流程

一、openga.h解析

是一个遗传算法开源库,有三种计算模式,使用的是SOGA模式

enum class GA_MODE
{
  SOGA,
  IGA,
  NSGA_III
};

定义染色体结构,ChromosomeType放在GenerationType里,又放在GenerationType_SO_abstract里

template <typename GeneType, typename MiddleCostType>
struct ChromosomeType
{
  GeneType genes;
  MiddleCostType middle_costs;  // individual costs
  double total_cost;            // for single objective
  vector<double> objectives;    // for multi-objective
};

template <typename GeneType, typename MiddleCostType>
struct GenerationType
{
  vector<ChromosomeType<GeneType, MiddleCostType>> chromosomes;
  double best_total_cost = (std::numeric_limits<double>::infinity());  // for single objective
  double average_cost = 0.0;                                           // for single objective

  int best_chromosome_index = -1;       // for single objective
  vector<int> sorted_indices;           // for single objective
  vector<vector<unsigned int>> fronts;  // for multi-objective
  vector<double> selection_chance_cumulative;
  double exe_time;
};

template <typename GeneType, typename MiddleCostType>
struct GenerationType_SO_abstract
{
  double best_total_cost = (std::numeric_limits<double>::infinity());  // for single objective
  double average_cost = 0.0;                                           // for single objective

  GenerationType_SO_abstract(const GenerationType<GeneType, MiddleCostType>& generation)
    : best_total_cost(generation.best_total_cost), average_cost(generation.average_cost)
  {
  }
};

对矩阵进行操作的一些函数,如置零、清除、获取行数列数等,在矩阵类Matrix中

class Matrix
{
  unsigned int n_rows, n_cols;
  vector<double> data;

public:  
//这是一个构造函数,用于初始化一个名为Matrix的类。其中,n_rows和n_cols是表示矩阵的行数和列数的变量,data是一个指向矩阵数据的指针。在这个构造函数中,n_rows和n_cols被初始化为0,data被初始化为空指针,即还没有分配矩阵数据的内存空间。
  Matrix() : n_rows(0), n_cols(0), data()   
  {
  }

  //这也是一个构造函数,用于初始化一个名为Matrix的类,并指定矩阵的行数和列数。在这个构造函数中,n_rows和n_cols被初始化为传入的参数n_rows和n_cols,data被初始化为一个大小为n_rows * n_cols的向量,也就是说,它为矩阵数据分配了一个连续的内存空间。这个构造函数可以用于创建一个空矩阵,也可以用于创建一个具有指定行数和列数的矩阵。
  Matrix(unsigned int n_rows, unsigned int n_cols) : n_rows(n_rows), n_cols(n_cols), data(n_rows * n_cols)
  {
  }

  void zeros()      //这个函数适用于已经存在的矩阵,可以将矩阵中的所有元素清零
  {
    std::fill(data.begin(), data.end(), 0);
  }

  void zeros(unsigned int rows, unsigned int cols)    //这个函数适用于创建一个新的矩阵,并将其中的所有元素初始化为0。
  {
    n_rows = rows;
    n_cols = cols;
    data.assign(rows * cols, 0);    //其中"assign"函数的第一个参数是要分配的元素数量,第二个参数是要分配的元素的值。
  }

  bool empty()
  {
    return (!n_rows) || (!n_cols);    //如果其中一个为0,则返回true,否则返回false。判断一个矩阵是否为空,即行数或列数是否为0。
  }

  //用于获取矩阵的行数和列数的。返回一个无符号整数类型的值
  unsigned int get_n_rows() const
  {
    return n_rows;
  }
  unsigned int get_n_cols() const
  {
    return n_cols;
  }

  void clear()
  {
    n_rows = 0;
    n_cols = 0;
    data.clear();
  }


  //这段代码定义了一个名为set_col的函数,它有两个参数:col_idx和col_vector。col_idx是一个无符号整数类型的值,表示要设置的列的索引。col_vector是一个双精度浮点数向量,表示要分配给该列的值。该函数使用assert函数来确保col_vector的大小与矩阵的行数相同。然后,它使用一个for循环来遍历矩阵的每一行,并将col_vector中对应的值分配给该行的col_idx列。这个函数可能是用于设置矩阵的某一列的值的。
  void set_col(unsigned int col_idx, const vector<double>& col_vector)
  {
    assert(col_vector.size() == n_rows && "Assigned column vector size mismatch."); //如果col_vector的大小与n_rows不同,assert函数将会抛出一个错误,提示“Assigned column vector size mismatch.”,并终止程序的执行。这个assert语句的作用是确保在设置矩阵的某一列时,分配给该列的向量的大小与矩阵的行数相同,以避免出现错误的结果。
    for (unsigned int i = 0; i < n_rows; i++)
      (*this)(i, col_idx) = col_vector[i];
  }

  void set_row(unsigned int row_idx, const vector<double>& row_vector)
  {
    assert(row_vector.size() == n_cols && "Assigned row vector size mismatch.");
    for (unsigned int i = 0; i < n_cols; i++)
      (*this)(row_idx, i) = row_vector[i];
  }

  void get_col(unsigned int col_idx, vector<double>& col_vector) const
  {
    col_vector.resize(n_rows);
    for (unsigned int i = 0; i < n_rows; i++)
      col_vector[i] = (*this)(i, col_idx);
  }

  void get_row(unsigned int row_idx, vector<double>& row_vector) const
  {
    row_vector.resize(n_cols);
    for (unsigned int i = 0; i < n_cols; i++)
      row_vector[i] = (*this)(row_idx, i);
  }

计时用高精度时钟类Chronometer的tic()和toc()函数

class Chronometer   //高度精确的钟表
{
protected:
  typedef std::chrono::time_point<std::chrono::high_resolution_clock> Timetype;
  Timetype time_start, time_stop;
  bool initialized;

public:
  Chronometer() : initialized(false)
  {
  }

  void tic()
  {
    initialized = true;
    time_start = std::chrono::high_resolution_clock::now();
  }

  double toc()
  {
    if (!initialized)
      throw runtime_error("Chronometer is not initialized!");
    time_stop = std::chrono::high_resolution_clock::now();
    return (double)std::chrono::duration<double>(time_stop - time_start).count();
  }
};

主要是类 Genetic,下面是各成员主要函数,protected里的函数较多

class Genetic
{
    private:  //私有成员,类的外部是不可访问的,只有类和友元函数可以访问私有成员
      std::mt19937_64 rng;  // random generator,这是一个C++标准库中的随机数生成器。它可以生成64位的随机数。
      std::uniform_real_distribution<double> unif_dist; //随机数分布器,可以生成指定范围内的均匀分布的随机数
      int average_stall_count;
      int best_stall_count;
      vector<double> ideal_objectives;           // for multi-objective
      Matrix extreme_objectives;                 // for multi-objective
      vector<double> scalarized_objectives_min;  // for multi-objective
      Matrix reference_vectors;
  // double shrink_scale;
      unsigned int N_robj;

    public:  //公有成员,在程序类的外部可以访问
      typedef ChromosomeType<GeneType, MiddleCostType> thisChromosomeType;
      typedef GenerationType<GeneType, MiddleCostType> thisGenerationType;
      typedef GenerationType_SO_abstract<GeneType, MiddleCostType> thisGenSOAbs;
      GA_MODE problem_mode;
      unsigned int population;
      double crossover_fraction;
      double mutation_rate;
      bool verbose;
      int generation_step;
      int elite_count;
      int generation_max;
      double tol_stall_average;
      int average_stall_max;
      double tol_stall_best;
      int best_stall_max;
      unsigned int reference_vector_divisions;
      bool enable_reference_vectors;
      bool multi_threading;
      bool dynamic_threading;
      int N_threads;
      bool user_request_stop;
      long idle_delay_us;

      function<void(thisGenerationType&)> calculate_IGA_total_fitness;  //计算当前一代个体的总适应度。
      function<double(const thisChromosomeType&)> calculate_SO_total_fitness;
      function<vector<double>(thisChromosomeType&)> calculate_MO_objectives;
      function<vector<double>(const vector<double>&)> distribution_objective_reductions;
      function<void(GeneType&, const function<double(void)>& rnd01)> init_genes;
      function<bool(const GeneType&, MiddleCostType&)> eval_solution;
      function<bool(const GeneType&, MiddleCostType&, const thisGenerationType&)> eval_solution_IGA;
      function<GeneType(const GeneType&, const function<double(void)>& rnd01, double shrink_scale)> mutate;
      function<GeneType(const GeneType&, const GeneType&, const function<double(void)>& rnd01)> crossover;
      function<void(int, const thisGenerationType&, const GeneType&)> SO_report_generation;
      function<void(int, const thisGenerationType&, const vector<unsigned int>&)> MO_report_generation;
      function<void(void)> custom_refresh;
      function<double(int, const function<double(void)>& rnd01)> get_shrink_scale;
      vector<thisGenSOAbs> generations_so_abs;
      thisGenerationType last_generation;

      Genetic();
      int get_number_reference_vectors(int N_objectives, int N_divisions);
      void calculate_N_robj(const thisGenerationType& g);
      void solve_init();
      StopReason solve_next_generation();
      StopReason solve();
      std::string stop_reason_to_string(StopReason stop)

    protected:   //受保护成员,保护成员在派生类(即子类)中是可访问的
       
};

首先区分代码中对应的遗传算法模式,用到了两个判断项,  bool is_single_objective()和is_interactive,在受保护模式中。这个标定程序用的是SOGA模式,分别对应true和false,一些判断不符合这个的代码段就不用看了,还有一个常用的verbose,默认为false

bool is_single_objective()      // ture SOGA模式/IGA模式
  {
    switch (problem_mode)
    {
      case GA_MODE::SOGA:
        return true;
      case GA_MODE::IGA:
        return true;
      case GA_MODE::NSGA_III:
        return false;
      default:
        throw runtime_error("Code should not reach here!");
    }
  }

  bool is_interactive()       //false是SOGA模式,true是IGA模式
  {
    switch (problem_mode)
    {
      case GA_MODE::SOGA:
        return false;
      case GA_MODE::IGA:
        return true;
      case GA_MODE::NSGA_III:
        return false;
      default:
        throw runtime_error("Code should not reach here!");
    }
  }

定义了一个枚举类型的停止机制和判断条件

enum class StopReason
{
  Undefined,
  MaxGenerations,
  StallAverage,
  StallBest,
  UserRequest
};
    StopReason stop_critera()
  {
    if (generation_step < 2 && !user_request_stop)
      return StopReason::Undefined;

    if (is_single_objective())
    {
      const thisGenSOAbs& g1 = generations_so_abs[int(generations_so_abs.size()) - 2];
      const thisGenSOAbs& g2 = generations_so_abs[int(generations_so_abs.size()) - 1];

      if (std::abs(g1.best_total_cost - g2.best_total_cost) < tol_stall_best)
        best_stall_count++;
      else
        best_stall_count = 0;
      if (std::abs(g1.average_cost - g2.average_cost) < tol_stall_average)
        average_stall_count++;
      else
        average_stall_count = 0;
    }

    if (generation_step >= generation_max)
      return StopReason::MaxGenerations;

    if (average_stall_count >= average_stall_max)
      return StopReason::StallAverage;

    if (best_stall_count >= best_stall_max)
      return StopReason::StallBest;

    if (user_request_stop)
      return StopReason::UserRequest;

    return StopReason::Undefined;
  }

遗传算法的有些代码还是看不太懂,代码量太大……后期看懂了补上。

二、optimiser.h解析

旋转矩阵定义到了Rotation结构体中,按照xyz轴顺序变化。定义的是三个姿态角,可以通过toMat函数将欧拉角转换为旋转矩阵。

注意:x轴向前,与横滚角roll对应;y轴向左,与俯仰角pitch对应;z轴向上,与航向角yaw对应。

    struct Rotation
    {
        double roll;  // Rotation optimization variables
        double pitch;
        double yaw;
        operator const std::string() const
        {
            return std::string("{") + "roll:" + std::to_string(roll) + ", pitch:" + std::to_string(pitch) +
                   ", yaw:" + std::to_string(yaw) + "}";
        }
        cv::Mat toMat() const
        {
            using cv::Mat_;
            using std::cos;
            using std::sin;

            cv::Mat R_x = (Mat_<double>(3, 3) << 1, 0, 0, 0, cos(roll), -sin(roll), 0, sin(roll), cos(roll));
            // Calculate rotation about y axis
            cv::Mat R_y = (Mat_<double>(3, 3) << cos(pitch), 0, sin(pitch), 0, 1, 0, -sin(pitch), 0, cos(pitch));

            // Calculate rotation about z axis
            cv::Mat R_z = (Mat_<double>(3, 3) << cos(yaw), -sin(yaw), 0, sin(yaw), cos(yaw), 0, 0, 0, 1);

            return R_z * R_y * R_x;
        }
    };

进一步地,以旋转矩阵Rotation为基础定义变换矩阵RotationTranslation,也就是增加了平移向量xyz

struct RotationTranslation
    {
        Rotation rot;
        double x;
        double y;
        double z;
        operator const std::string() const
        {
            return std::string("{") + "roll:" + std::to_string(rot.roll) + ", pitch:" + std::to_string(rot.pitch) +
                   ", yaw:" + std::to_string(rot.yaw) + ", x:" + std::to_string(x) + ", y:" + std::to_string(y) +
                   ", z:" + std::to_string(z) + "}";
        }
    };

然后,定义两个矩阵的计算损失函数,用于存放中间计算量。

struct RotationCost  // equivalent to y in matlab
    {
        double objective1;  // This is where the results of simulation is stored but not yet finalized.
    };

    struct RotationTranslationCost  // equivalent to y in matlab
    {
        double objective2;  // This is where the results of simulation is stored but not yet finalized.
    };

下一部分,定义了存放计算量的数据结构体OptimisationSample,再以此为基础定义SetAssess

    struct OptimisationSample
    {
        cv::Point3d camera_centre{ 0, 0, 0 };
        cv::Point3d camera_normal{ 0, 0, 0 };
        std::vector<cv::Point3d> camera_corners;
        cv::Point3d lidar_centre{ 0, 0, 0 };
        cv::Point3d lidar_normal{ 0, 0, 0 };
        std::vector<cv::Point3d> lidar_corners;
        std::vector<double> angles_0; // Currently unused - only for print statements on capture
        std::vector<double> angles_1; // Currently unused - only for print statements on capture
        std::vector<double> widths;
        std::vector<double> heights;
        float distance_from_origin; // Currently unused - only for print statements on capture
        double pixeltometre;
        int sample_num;
    };

    struct SetAssess
    {
        float voq;
        std::vector<OptimisationSample> set;
    };

angles_0,angles_1,distance_from_origin 没有参与具体计算

定义一个类Optimiser。两套计算内容,旋转矩阵、变化矩阵。先优化求解最优旋转矩阵,再以此为初始值,优化旋转矩阵和平移向量,提高精度。设置遗传算法的适应函数、交叉、变异、评估精度、初始化基因

void SO_report_generation(int generation_number, const EA::GenerationType<Rotation, RotationCost>& last_generation, const Rotation& best_genes);
double calculate_SO_total_fitness(const GA_Rot_t::thisChromosomeType& X);
Rotation crossover(const Rotation& X1, const Rotation& X2, const std::function<double(void)>& rnd01);
Rotation mutate(const Rotation& X_base, const std::function<double(void)>& rnd01, const Rotation& initial_rotation,const double angle_increment, const double shrink_scale);
bool eval_solution(const Rotation& p, RotationCost& c);
void init_genes(Rotation& p, const std::function<double(void)>& rnd01, const Rotation& initial_rotation,double increment);

定义四个损失函数及具体内容

        double perpendicularCost(const Rotation& rot);
        double normalAlignmentCost(const Rotation& rot);
        double reprojectionCost(const RotationTranslation& rot_trans);
        double centreAlignmentCost(const RotationTranslation& rot_trans);


 double Optimiser::perpendicularCost(const Rotation& rot)
    {
        // We do all the alignment of features in the lidar frame
        // Eq (3) in the original baseline paper
        double cost = 0;
        for (const auto& sample : current_set_)
        {
            auto camera_normal_lidar_frame = rot * sample.camera_normal;
            auto perp = cv::Mat(sample.lidar_centre - sample.lidar_corners[0]).reshape(1);
            perp /= cv::norm(perp);
            cost += std::pow(perp.dot(camera_normal_lidar_frame), 2) / current_set_.size();
 
        }
        return cost;
    }

    double Optimiser::normalAlignmentCost(const Rotation& rot)
    {
        // Eq (4) in the original baseline paper
        // We do all the alignment of features in the lidar frame

        double cost;
        for (const auto& sample : current_set_)
        {
            auto camera_normal_lidar_frame = rot * sample.camera_normal;
            cost += cv::norm(camera_normal_lidar_frame - cv::Mat(sample.lidar_normal).reshape(1)) / current_set_.size();
        }
        return cost;
    }

    double Optimiser::reprojectionCost(const RotationTranslation& rot_trans)
    {
        // We do all the alignment of features in the lidar frame
        cv::Mat rvec = cv::Mat_<double>::zeros(3, 1);
        cv::Mat tvec = cv::Mat_<double>::zeros(3, 1);

        double cost = 0;
        for (auto& sample : current_set_)
        {

            std::vector<cv::Point3d> cam_centre_3d;
            std::vector<cv::Point3d> lidar_centre_3d;

            auto camera_centre_lidar_frame = cv::Point3d(rot_trans * sample.camera_centre);
            cam_centre_3d.push_back(camera_centre_lidar_frame);
            lidar_centre_3d.push_back(sample.lidar_centre);

            std::vector<cv::Point2d> cam, lidar;
            if (i_params_.fisheye_model)
            {
                cv::fisheye::projectPoints(cam_centre_3d, cam, rvec, tvec, i_params_.cameramat, i_params_.distcoeff);
                cv::fisheye::projectPoints(lidar_centre_3d, lidar, rvec, tvec, i_params_.cameramat, i_params_.distcoeff);
            }
            else
            {
                cv::projectPoints(cam_centre_3d, rvec, tvec, i_params_.cameramat, i_params_.distcoeff, cam);
                cv::projectPoints(lidar_centre_3d, rvec, tvec, i_params_.cameramat, i_params_.distcoeff, lidar);
            }

            double error = cv::norm(cam[0] - lidar[0])*sample.pixeltometre;

            if (error > cost)
            {
                cost = error;
            }
        }
        return cost;
    }

    double Optimiser::centreAlignmentCost(const RotationTranslation& rot_trans)
    {
        // Eq (6) and (7)
        // We do all the alignment of features in the lidar frame

        double abs_mean = 0;
        for (const auto& sample : current_set_)
        {
            auto camera_centre_lidar_frame = rot_trans * sample.camera_centre;
            abs_mean += cv::norm(camera_centre_lidar_frame - cv::Mat(sample.lidar_centre).reshape(1)) / current_set_.size();
        }
        double stddev = 0;
        for (const auto& sample : current_set_)
        {
            auto camera_centre_lidar_frame = rot_trans * sample.camera_centre;
            stddev += std::pow(cv::norm(camera_centre_lidar_frame - cv::Mat(sample.lidar_centre).reshape(1)) - abs_mean, 2) /
                    current_set_.size();
        }
        stddev = std::sqrt(stddev);

        return abs_mean/1000 + stddev/1000;
    }

计算平均误差

void get_mean_stdev(std::vector<float>& input, float& mean, float& stdev);

void Optimiser::get_mean_stdev(std::vector<float>& input_vec, float& mean, float& stdev){
        //这段代码使用了C++标准库中的accumulate函数,对一个浮点数向量input_vec中的所有元素进行求和,并将结果存储在sum变量中。其中,std::begin(input_vec)和std::end(input_vec)分别表示向量input_vec的起始和结束位置,0.0表示初始值为0.0
        float sum = std::accumulate(std::begin(input_vec), std::end(input_vec), 0.0);   
        mean =  sum / input_vec.size();

        //这段代码使用了C++标准库中的for_each函数,对一个名为input_vec的浮点数向量进行遍历。计算了每个元素与平均值的差的平方,并将结果累加到一个名为accum的变量中。
        float accum = 0.0;
        std::for_each (std::begin(input_vec), std::end(input_vec), [&](const float d) {
            accum += (d - mean) * (d - mean);
        });

        stdev = sqrt(accum / (input_vec.size()-1));
    }

声明旋转矩阵转换欧拉角的函数,及具体内容(在cpp中)

std::vector<double> rotm2eul(cv::Mat);

std::vector<double> rotm2eul(cv::Mat mat)
    {
        std::vector<double> euler(3);
        euler[0] = atan2(mat.at<double>(2, 1), mat.at<double>(2, 2));  // rotation about x axis: roll
        euler[1] = atan2(-mat.at<double>(2, 0),
                         sqrt(mat.at<double>(2, 1) * mat.at<double>(2, 1) + mat.at<double>(2, 2) * mat.at<double>(2, 2)));
        euler[2] = atan2(mat.at<double>(1, 0), mat.at<double>(0, 0));  // rotation about z axis: yaw
        return euler;
    }

三、optimiser.cpp解析

初始化第一代, roll,pitch和yaw。函数接受四个参数,其中第一个参数是一个旋转对象,后面三个参数分别是一个返回0到1之间随机数的函数、一个初始旋转对象和一个增量值(优化旋转矩阵时为pi/8,优化变换矩阵时为pi/18)。函数首先创建一个包含两个元素的double类型向量pi_vals。接着,函数分别使用随机选择的增量值和随机数函数计算出新的旋转角度,并将其赋值给旋转对象的roll、pitch和yaw属性。最终,函数返回void类型。

void Optimiser::init_genes(Rotation& p, const std::function<double(void)>& rnd01, const Rotation& initial_rotation,
                               double increment)
    {
        std::vector<double> pi_vals;
        pi_vals.push_back(increment);
        pi_vals.push_back(-increment);
        int RandIndex = rand() % 2;
        p.roll = initial_rotation.roll + pi_vals.at(RandIndex) * rnd01();
        RandIndex = rand() % 2;
        p.pitch = initial_rotation.pitch + pi_vals.at(RandIndex) * rnd01();
        RandIndex = rand() % 2;
        p.yaw = initial_rotation.yaw + pi_vals.at(RandIndex) * rnd01();
    }

对旋转矩阵用perpendicularCost(p) 和 normalAlignmentCost两个函数处理,见optimiser.h文件,赋值到损失函数里,返回true

bool Optimiser::eval_solution(const Rotation& p, RotationCost& c)
    {
        c.objective1 = perpendicularCost(p) + normalAlignmentCost(p);

        return true;  // solution is accepted
    }

变异生成一个新的旋转矩阵。函数接受四个参数:X_base表示基础旋转矩阵,rnd01是一个返回0到1之间随机数的函数,initial_rotation表示初始旋转矩阵,angle_increment表示旋转角度增量,shrink_scale表示缩小比例。函数会在一定范围内随机生成新的旋转矩阵,直到生成的旋转矩阵在指定的范围内。具体实现是通过在每个轴上随机增加一定角度,然后判断是否在指定范围内。 

Rotation Optimiser::mutate(const Rotation& X_base, const std::function<double(void)>& rnd01,
                               const Rotation& initial_rotation, const double angle_increment, double shrink_scale)
    {
        Rotation X_new;
        bool in_range;
        do
        {
            in_range = true;
            X_new = X_base;
            float roll_inc = 0.2 * (rnd01() - rnd01()) * shrink_scale;
            X_new.roll += roll_inc;
            in_range = in_range && (X_new.roll >= (initial_rotation.roll - angle_increment) &&
                                    X_new.roll < (initial_rotation.roll + angle_increment));
            float pitch_inc = 0.2 * (rnd01() - rnd01()) * shrink_scale;
            X_new.pitch += pitch_inc;
            in_range = in_range && (X_new.pitch >= (initial_rotation.pitch - angle_increment) &&
                                    X_new.pitch < (initial_rotation.pitch + angle_increment));
            float yaw_inc = 0.2 * (rnd01() - rnd01()) * shrink_scale;
            X_new.yaw += yaw_inc;
            in_range = in_range && (X_new.yaw >= (initial_rotation.yaw - angle_increment) &&
                                    X_new.yaw < (initial_rotation.yaw + angle_increment));
        } while (!in_range);
        return X_new;
    }

 交叉函数,接收两个旋转矩阵X1和X2,根据两者产生X_new,X1的乘r , X2乘(1-r)

Rotation Optimiser::crossover(const Rotation& X1, const Rotation& X2, const std::function<double(void)>& rnd01)
    {
        Rotation X_new;
        double r = rnd01();
        X_new.roll = r * X1.roll + (1.0 - r) * X2.roll;
        r = rnd01();
        X_new.pitch = r * X1.pitch + (1.0 - r) * X2.pitch;
        r = rnd01();
        X_new.yaw = r * X1.yaw + (1.0 - r) * X2.yaw;
        return X_new;
    }

 计算总的损失函数,ChromosomeType结构体

double Optimiser::calculate_SO_total_fitness(const GA_Rot_t::thisChromosomeType& X)
    {
        double final_cost = 0.0;  // finalize the cost
        final_cost += X.middle_costs.objective1;
        return final_cost;
    }

存放当代最优矩阵 ,GenerationType结构体

    void Optimiser::SO_report_generation(int generation_number,
                                         const EA::GenerationType<Rotation, RotationCost>& last_generation,
                                         const Rotation& best_genes)
    {
        best_rotation_.roll = best_genes.roll;
        best_rotation_.pitch = best_genes.pitch;
        best_rotation_.yaw = best_genes.yaw;
    }

 求标准偏差,先对向量内所有元素求和,再求平均数,和每个数作差,得标准偏差、开方得stdev

void Optimiser::get_mean_stdev(std::vector<float>& input_vec, float& mean, float& stdev){
        //这段代码使用了C++标准库中的accumulate函数,对一个浮点数向量input_vec中的所有元素进行求和,并将结果存储在sum变量中。其中,std::begin(input_vec)和std::end(input_vec)分别表示向量input_vec的起始和结束位置,0.0表示初始值为0.0
        float sum = std::accumulate(std::begin(input_vec), std::end(input_vec), 0.0);   
        mean =  sum / input_vec.size();

        //这段代码使用了C++标准库中的for_each函数,对一个名为input_vec的浮点数向量进行遍历。计算了每个元素与平均值的差的平方,并将结果累加到一个名为accum的变量中。再除以个数,得到平均偏差stdev
        float accum = 0.0;
        std::for_each (std::begin(input_vec), std::end(input_vec), [&](const float d) {
            accum += (d - mean) * (d - mean);
        });

        stdev = sqrt(accum / (input_vec.size()-1));
    }

从捕获的总样本生成大小为k的组合,设置总的样本数offset,每个组合的样本数k,选出的集合set,总样本集合samples,都是OptimisationSample结构体组成的vector容器

// Generate combinations of size k from the total samples captured
    //这段代码是一个递归函数,用于生成从总样本中选择k个样本的所有组合。函数接受四个参数,其中第一个参数是当前已选择的样本数,第二个参数是需要选择的样本数,第三个参数是当前已选择的样本集合,第四个参数是总样本集合。函数首先判断如果需要选择的样本数为0,则将当前已选择的样本集合加入到所有组合的集合中,并返回。否则,函数使用循环从当前已选择的样本数开始,到总样本数减去需要选择的样本数为止,依次选择一个样本,并递归调用自身,将需要选择的样本数减1,当前已选择的样本集合加入该样本,然后再将该样本从当前已选择的样本集合中移除。最终,函数将生成的所有组合存储在一个集合中。
    void Optimiser::generate_sets(int offset, int k, std::vector<OptimisationSample>& set, std::vector<OptimisationSample>& samples) {
        if (k == 0) {
            sets.push_back(set);
            return;
        }
        for (int i = offset; i <= samples.size() - k; ++i) {
            set.push_back(samples[i]);
            generate_sets(i+1, k-1, set, samples);
            set.pop_back();
        }
    }

旋转矩阵部分计算完成,接下来是变换矩阵,主要分析相对于旋转矩阵增加的部分:

  1. init_genes里,旋转矩阵的angle_increment角度增量由pi/8改为pi/18,translation_increment位移增量为0.05*1000。
  2. eval_solution里,旋转矩阵部分的误差函数一样,计算位移误差用到了另外两个函数centreAlignmentCost和reprojectionCost,总误差为4项的和,保存到objective2
  3. mutate里,xyz的变换量有个*1000,是转换到m,增加增量,不乘的话变化量太小。
  4. crossover里,与旋转矩阵的一样
  5. calculate_SO_total_fitness里,对所有的objective2求和,为final_cost
  6. SO_report_generation里,将包括xyz在内的最优变换矩阵保存到best_rotation_translation_

 optimise求解函数

优化旋转矩阵和变换矩阵的遗传算法配置是一样的,由openga.h里的solve执行计算,包括solve_init和solve_next_generation。

        GA_Rot_t ga_obj;
        ga_obj.problem_mode = EA::GA_MODE::SOGA;
        ga_obj.multi_threading = false;
        ga_obj.verbose = false;
        ga_obj.population = 200;
        ga_obj.generation_max = 1000;
        ga_obj.calculate_SO_total_fitness = [&](const GA_Rot_t::thisChromosomeType& X) -> double {
            return this->calculate_SO_total_fitness(X);
        };
        ga_obj.init_genes = [&, initial_rotation, rotation_increment](Rotation& p,
                                                                      const std::function<double(void)>& rnd01) -> void {
            this->init_genes(p, rnd01, initial_rotation, rotation_increment);
        };
        ga_obj.eval_solution = [&](const Rotation& r, RotationCost& c) -> bool { return this->eval_solution(r, c); };
        ga_obj.mutate = [&, initial_rotation, rotation_increment](
                const Rotation& X_base, const std::function<double(void)>& rnd01, double shrink_scale) -> Rotation {
            return this->mutate(X_base, rnd01, initial_rotation, rotation_increment, shrink_scale);
        };
        ga_obj.crossover = [&](const Rotation& X1, const Rotation& X2, const std::function<double(void)>& rnd01) {
            return this->crossover(X1, X2, rnd01);
        };
        ga_obj.SO_report_generation = [&](int generation_number,
                                          const EA::GenerationType<Rotation, RotationCost>& last_generation,
                                          const Rotation& best_genes) -> void {
            this->SO_report_generation(generation_number, last_generation, best_genes);
        };
        ga_obj.best_stall_max = 100;
        ga_obj.average_stall_max = 100;
        ga_obj.tol_stall_average = 1e-8;
        ga_obj.tol_stall_best = 1e-8;
        ga_obj.elite_count = 10;
        ga_obj.crossover_fraction = 0.8;
        ga_obj.mutation_rate = 0.2;
        ga_obj.best_stall_max = 10;
        ga_obj.elite_count = 10;
        ga_obj.solve();
        tf::Matrix3x3 rot;
        rot.setRPY(best_rotation_.roll, best_rotation_.pitch, best_rotation_.yaw);
        cv::Mat tmp_rot = (cv::Mat_<double>(3, 3) << rot.getRow(0)[0], rot.getRow(0)[1], rot.getRow(0)[2], rot.getRow(1)[0],
                rot.getRow(1)[1], rot.getRow(1)[2], rot.getRow(2)[0], rot.getRow(2)[1], rot.getRow(2)[2]);
        // Analytical Translation
        cv::Mat cp_trans = tmp_rot * camera_centres_.t();
        cv::Mat trans_diff = lidar_centres_.t() - cp_trans;
        cv::Mat summed_diff;
        cv::reduce(trans_diff, summed_diff, 1, CV_REDUCE_SUM, CV_64F);
        summed_diff = summed_diff / trans_diff.cols;
        const RotationTranslation initial_rotation_translation{ best_rotation_, summed_diff.at<double>(0),
                                                                summed_diff.at<double>(1), summed_diff.at<double>(2) };

        rotation_increment = M_PI / 18;
        constexpr double translation_increment = 0.05*1000;
        // extrinsics stored the vector of extrinsic parameters in every iteration
        std::vector<std::vector<double>> extrinsics;
        // Joint optimization for Rotation and Translation (Perform this 10 times and take the average of the extrinsics)
        GA_Rot_Trans_t ga_rot_trans;
        ga_rot_trans.problem_mode = EA::GA_MODE::SOGA;
        ga_rot_trans.multi_threading = false;
        ga_rot_trans.verbose = false;
        ga_rot_trans.population = 200;
        ga_rot_trans.generation_max = 1000;
        ga_rot_trans.calculate_SO_total_fitness = [&](const GA_Rot_Trans_t::thisChromosomeType& X) -> double {
            return this->calculate_SO_total_fitness(X);
        };
        ga_rot_trans.init_genes = [&, initial_rotation_translation, rotation_increment, translation_increment](
                RotationTranslation& p, const std::function<double(void)>& rnd01) -> void {
            this->init_genes(p, rnd01, initial_rotation_translation, rotation_increment, translation_increment);
        };
        ga_rot_trans.eval_solution = [&](const RotationTranslation& rt, RotationTranslationCost& c) -> bool {
            return this->eval_solution(rt, c);
        };
        ga_rot_trans.mutate = [&, initial_rotation_translation, rotation_increment, translation_increment](
                const RotationTranslation& X_base, const std::function<double(void)>& rnd01,
                double shrink_scale) -> RotationTranslation {
            return this->mutate(X_base, rnd01, initial_rotation_translation, rotation_increment, translation_increment,
                                shrink_scale);
        };
        ga_rot_trans.crossover = [&](const RotationTranslation& X1, const RotationTranslation& X2,
                                        const std::function<double(void)>& rnd01) { return this->crossover(X1, X2, rnd01); };
        ga_rot_trans.SO_report_generation =
                [&](int generation_number,
                    const EA::GenerationType<RotationTranslation, RotationTranslationCost>& last_generation,
                    const RotationTranslation& best_genes) -> void {
                    this->SO_report_generation(generation_number, last_generation, best_genes);
                };
        ga_rot_trans.best_stall_max = 100;
        ga_rot_trans.average_stall_max = 100;
        ga_rot_trans.tol_stall_average = 1e-8;
        ga_rot_trans.tol_stall_best = 1e-8;
        ga_rot_trans.elite_count = 10;
        ga_rot_trans.crossover_fraction = 0.8;
        ga_rot_trans.mutation_rate = 0.2;
        ga_rot_trans.best_stall_max = 10;
        ga_rot_trans.elite_count = 10;
        ga_rot_trans.solve();

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可见一班

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值