Skip to content
Snippets Groups Projects
kf_based_terrain_analysis.cpp 65.20 KiB
#include "kf_based_terrain_analysis.h"

CKf_Based_Terrain_Analysis::CKf_Based_Terrain_Analysis()
{
  performance_supervisor_ptr_ = boost::make_shared<CTimeSupervisor>();
}

CKf_Based_Terrain_Analysis::~CKf_Based_Terrain_Analysis()
{

}

void CKf_Based_Terrain_Analysis::showPerformanceStatistics(void)
{
  performance_supervisor_ptr_->printStatistics();
}

float CKf_Based_Terrain_Analysis::computeTriangleArea(const Eigen::Vector3d vertex_1, const Eigen::Vector3d vertex_2,
                                                      const Eigen::Vector3d vertex_3)
{
  float area = 0.0;

  Eigen::Matrix3d triangle = Eigen::Matrix3d::Ones();

  triangle.row(0) = vertex_1;
  triangle.row(1) = vertex_2;
  triangle.row(2) = vertex_3;

  area = 0.5 * triangle.determinant();

  return (area);
}

bool CKf_Based_Terrain_Analysis::checkInside(
    const kf_based_terrain_analysis_lib::Coordinates2D coord,
    const std::vector<kf_based_terrain_analysis_lib::Coordinates2D> &vertex_list)
{
  bool inside = false;

  //Point to evaluate
  Eigen::Vector3d p = Eigen::Vector3d::Ones();
  p(0) = coord.x;
  p(1) = coord.y;

  //Vertex
  Eigen::Vector3d p1 = Eigen::Vector3d::Ones();
  p1(0) = vertex_list[0].x;
  p1(1) = vertex_list[0].y;

  Eigen::Vector3d p2 = Eigen::Vector3d::Ones();
  p2(0) = vertex_list[1].x;
  p2(1) = vertex_list[1].y;

  Eigen::Vector3d p3 = Eigen::Vector3d::Ones();
  p3(0) = vertex_list[2].x;
  p3(1) = vertex_list[2].y;

  Eigen::Vector3d p4 = Eigen::Vector3d::Ones();
  p4(0) = vertex_list[3].x;
  p4(1) = vertex_list[3].y;

  //We will evaluate the areas of the for triangles that can be form using the
  //four pairs of rectangle vertex and the evaluated point, if all the areas computed
  //using a determinant are positive or negative, the evaluated  point is inside
  //the rectangle (this formula works for any arbitrary convex region on a plane)
  float a1 = computeTriangleArea(p1, p2, p);
  float a2 = computeTriangleArea(p2, p3, p);
  float a3 = computeTriangleArea(p3, p4, p);
  float a4 = computeTriangleArea(p4, p1, p);
  if ((a1 < 0.0 && a2 < 0.0 && a3 < 0.0 && a4 < 0.0) || (a1 > 0.0 && a2 > 0.0 && a3 > 0.0 && a4 > 0.0))
  {
    inside = true;
  }

  return (inside);
}

void CKf_Based_Terrain_Analysis::calculateVertexCoordinates(
    const kf_based_terrain_analysis_lib::Robot2DFootprint footprint,
    std::vector<kf_based_terrain_analysis_lib::Coordinates2D> &vertex_list, int &closest_vertex_index)
{
  // Vertex coordinates in object frame
  Eigen::Vector3d p1 = Eigen::Vector3d::Ones();
  p1(0) = footprint.dimensions.x / 2.0;
  p1(1) = footprint.dimensions.y / 2.0;

  Eigen::Vector3d p2 = Eigen::Vector3d::Ones();
  p2(0) = footprint.dimensions.x / 2.0;
  p2(1) = -1 * footprint.dimensions.y / 2.0;

  Eigen::Vector3d p3 = Eigen::Vector3d::Ones();
  p3(0) = -1 * footprint.dimensions.x / 2.0;
  p3(1) = -1 * footprint.dimensions.y / 2.0;

  Eigen::Vector3d p4 = Eigen::Vector3d::Ones();
  p4(0) = -1 * footprint.dimensions.x / 2.0;
  p4(1) = footprint.dimensions.y / 2.0;

  // Rotation matrix
  Eigen::Matrix3d transform = Eigen::Matrix3d::Identity();
  transform(0, 0) = cos(footprint.yaw);
  transform(0, 1) = -1 * sin(footprint.yaw);
  transform(0, 2) = footprint.centroid.x;

  transform(1, 0) = sin(footprint.yaw);
  transform(1, 1) = cos(footprint.yaw);
  transform(1, 2) = footprint.centroid.y;

  p1 = transform * p1;
  p2 = transform * p2;
  p3 = transform * p3;
  p4 = transform * p4;

  kf_based_terrain_analysis_lib::Coordinates2D p;
  p.x = p1(0);
  p.y = p1(1);
  vertex_list.push_back(p);

  p.x = p2(0);
  p.y = p2(1);
  vertex_list.push_back(p);

  p.x = p3(0);
  p.y = p3(1);
  vertex_list.push_back(p);

  p.x = p4(0);
  p.y = p4(1);
  vertex_list.push_back(p);

  // Find closest vertex
  float min_distance = 1000000.0;
  float x = p1(0);
  float y = p1(1);
  float dp = sqrt(x * x + y * y);
  if (dp < min_distance)
  {
    min_distance = dp;
    closest_vertex_index = 0;
  }

  x = p2(0);
  y = p2(1);
  dp = sqrt(x * x + y * y);
  if (dp < min_distance)
  {
    min_distance = dp;
    closest_vertex_index = 1;
  }

  x = p3(0);
  y = p3(1);
  dp = sqrt(x * x + y * y);
  if (dp < min_distance)
  {
    min_distance = dp;
    closest_vertex_index = 2;
  }

  x = p4(0);
  y = p4(1);
  dp = sqrt(x * x + y * y);
  if (dp < min_distance)
  {
    min_distance = dp;
    closest_vertex_index = 3;
  }

}

void CKf_Based_Terrain_Analysis::initizalizeShadowAreaGroundReferencePoint(
    const kf_based_terrain_analysis_lib::SensorConfiguration lidar_configuration,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration, const float x, const float y,
    const int index, pcl::PointXYZRGBNormal &ground_point)
{
  ground_point.x = x;
  ground_point.y = y;
  ground_point.z = -1.0 * lidar_configuration.sensor_height; // we set the prior

  ground_point.r = 0; // we set these values to cero because it corresponds to cero cost, however
  ground_point.g = 0; // they will be later properly computed (with the exception of point at origin of sensor frame
  ground_point.b = 0; // because this point is already reached, so it has to be a zero cost point

  ground_point.data_n[GRND_REF_DATA_N_2_GOAL_COST] = COST_UNKNOWN;
  ground_point.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] = (float)index;

  // we set the prior
  ground_point.data_c[GRND_REF_DATA_C_1_ROLL] = 0.0;
  ground_point.data_c[GRND_REF_DATA_C_2_PITCH] = 0.0;
  ground_point.data_c[GRND_REF_DATA_C_3_Z_VARIANCE] = filtering_configuration.z_initial_std_dev
      * filtering_configuration.z_initial_std_dev;
  ground_point.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE] = filtering_configuration.roll_initial_std_dev
      * filtering_configuration.roll_initial_std_dev;
  ground_point.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE] = filtering_configuration.pitch_initial_std_dev
      * filtering_configuration.pitch_initial_std_dev;

  //std::cout << "initizalizeShadowAreaGroundReferencePoint: r = " << (int)ground_point.r << "    g = " << (int)ground_point.g
  //    << "    b = " << (int)ground_point.b << std::endl;
}

void CKf_Based_Terrain_Analysis::extractPointsInROI(
    const kf_based_terrain_analysis_lib::SensorConfiguration lidar_configuration,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    const pcl::PointXYZRGBNormal reference, const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr points_in_ROI,
    const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr references_in_ROI, float &search_range)
{
  if (filtering_configuration.measure_performance)
    CFunctionMonitor performance_monitor("extractPointsInROI", performance_supervisor_ptr_);

  //std::cout << "Extracting points in ROI: reference.x = " << reference.x << "    reference.y = " << reference.y
  //    << "    reference.z = " << reference.z << std::endl;

  if ((int)round(reference.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]) == INDEX_UNKNOWN
      || (int)round(reference.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]) == 0)
  {
    search_range = filtering_configuration.search_limit_in_shadow_area;

    //std::cout << "Using shadow area reference point! " << std::endl;
    //std::cout << "search_range = " << search_range << std::endl;
  }
  else
  {
    float r = sqrt(reference.x * reference.x + reference.y * reference.y);
    float alpha = atan2(lidar_configuration.sensor_height, r);
    float beta = alpha - (filtering_configuration.angle_reduction_factor * M_PI / 180.0);

    if (beta < 1.0 * M_PI / 180.0)
      beta = 1.0 * M_PI / 180.0;

    //std::cout << "r = " << r << "    alpha = " << alpha * 180.0 / M_PI << "    beta = " << beta * 180.0 / M_PI
    //    << std::endl;

    float r2 = lidar_configuration.sensor_height / tan(beta);
    search_range = r2 - r;

    if (search_range < filtering_configuration.search_limit_in_shadow_area)
      search_range = filtering_configuration.search_limit_in_shadow_area;

    //std::cout << "r2 = " << r2 << "    search_range = " << r2 - r << std::endl;
    //std::cout << "saturated_search_range = " << search_range << std::endl;
    assert(search_range > 0.0 && "negative search range!");
  }

  float x_min = reference.x - search_range;
  float y_min = reference.y - search_range;
  float x_max = reference.x + search_range;
  float y_max = reference.y + search_range;

  // Extracting lidar points
  pcl::PassThrough < pcl::PointXYZRGBNormal > pass;
  pass.setInputCloud(elevation_cloud_ptr);
  pass.setFilterFieldName("x");
  pass.setFilterLimits(x_min, x_max);
  pass.filter(*points_in_ROI);
  pass.setInputCloud(points_in_ROI);
  pass.setFilterFieldName("y");
  pass.setFilterLimits(y_min, y_max);
  pass.filter(*points_in_ROI);

  //std::cout << "X limits = " << x_min << " ; " << x_max << std::endl;
  //std::cout << "Y limits = " << y_min << " ; " << y_max << std::endl;
  //std::cout << "Z ground ref = " << reference.z << std::endl;

  //getchar();

  pass.setInputCloud(ground_reference_cloud_ptr);
  pass.setFilterFieldName("x");
  pass.setFilterLimits(x_min, x_max);
  pass.filter(*references_in_ROI);
  pass.setInputCloud(references_in_ROI);
  pass.setFilterFieldName("y");
  pass.setFilterLimits(y_min, y_max);
  pass.filter(*references_in_ROI);

}

float CKf_Based_Terrain_Analysis::euclideanDistance(const pcl::PointXYZRGBNormal reference_in_sensor_frame,
                                                    const pcl::PointXYZRGBNormal point_in_sensor_frame)
{
  CFunctionMonitor performance_monitor("euclideanDistance", performance_supervisor_ptr_);

  // Expectation
  float roll = reference_in_sensor_frame.data_c[GRND_REF_DATA_C_1_ROLL];
  float pitch = reference_in_sensor_frame.data_c[GRND_REF_DATA_C_2_PITCH];

  float delta_x = point_in_sensor_frame.x - reference_in_sensor_frame.x;
  float delta_y = point_in_sensor_frame.y - reference_in_sensor_frame.y;

  float prediction = reference_in_sensor_frame.z + delta_x * pitch + delta_y * roll;

  // Innovation
  float observation = point_in_sensor_frame.z;
  float innovation = observation - prediction;

  return (innovation);
}

float CKf_Based_Terrain_Analysis::computeLikelihood(const pcl::PointXYZRGBNormal reference_in_sensor_frame,
                                                    const pcl::PointXYZRGBNormal point_in_sensor_frame)
{
  float z_pred = 0.0;
  float var_z_pred = 0.0;

  float delta_x = point_in_sensor_frame.x - reference_in_sensor_frame.x;
  float delta_y = point_in_sensor_frame.y - reference_in_sensor_frame.y;

  predictZWithVariance(reference_in_sensor_frame, delta_x, delta_y, z_pred, var_z_pred);

  float sigma = sqrt(var_z_pred);
  float regularization_term = 1.0 / (sigma * 2.506628275);

  float likelihood = regularization_term
      * (exp(-1.0 * ((point_in_sensor_frame.z - z_pred) * (point_in_sensor_frame.z - z_pred)) / (2 * var_z_pred)));
  //DEBUG!float likelihood = ((point_in_sensor_frame.z - z_pred) * (point_in_sensor_frame.z - z_pred)) / (2 * var_z_pred);

  if (false)  //point_in_sensor_frame.z < -1.75)
  {
    std::cout << "delta_x = " << delta_x << std::endl;
    std::cout << "delta_y = " << delta_y << std::endl;

    std::cout << "z_pred = " << z_pred << std::endl;
    std::cout << "var_z_pred = " << var_z_pred << std::endl;

    std::cout << "sigma = " << sigma << std::endl;
    std::cout << "regularization_term = " << regularization_term << std::endl;

    std::cout << "z observed = " << point_in_sensor_frame.z << std::endl;
    std::cout << "likelihood = " << likelihood << std::endl;

    std::cout << "reference_in_sensor_frame.x = " << reference_in_sensor_frame.x << "    reference_in_sensor_frame.y = "
        << reference_in_sensor_frame.y << "    reference_in_sensor_frame.z = " << reference_in_sensor_frame.z
        << std::endl;

    getchar();
  }
  return (likelihood);
}

float CKf_Based_Terrain_Analysis::mahalanobisDistance(const pcl::PointXYZRGBNormal reference_in_sensor_frame,
                                                      const pcl::PointXYZRGBNormal point_in_sensor_frame,
                                                      float &predicted_std_dev)
{
  //CFunctionMonitor performance_monitor("mahalanobisDistance", performance_supervisor_ptr_);

  //std::cout << "<------- Exectuting CKf_Based_Terrain_Analysis::mahalanobisDistance... --------->" << std::endl;

  float prediction_fast = reference_in_sensor_frame.z
      + (point_in_sensor_frame.x - reference_in_sensor_frame.x)
          * reference_in_sensor_frame.data_c[GRND_REF_DATA_C_2_PITCH]
      + (point_in_sensor_frame.y - reference_in_sensor_frame.y)
          * reference_in_sensor_frame.data_c[GRND_REF_DATA_C_1_ROLL];

  float prediction_std_fast = sqrt(
      reference_in_sensor_frame.data_c[GRND_REF_DATA_C_3_Z_VARIANCE]
          + (point_in_sensor_frame.x - reference_in_sensor_frame.x)
              * (point_in_sensor_frame.x - reference_in_sensor_frame.x)
              * reference_in_sensor_frame.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE]
          + (point_in_sensor_frame.y - reference_in_sensor_frame.y)
              * (point_in_sensor_frame.y - reference_in_sensor_frame.y)
              * reference_in_sensor_frame.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE]);

  float mahalanobis_distance_fast = fabs(point_in_sensor_frame.z - prediction_fast) / prediction_std_fast;

  predicted_std_dev = prediction_std_fast;

  //assert(!std::isnan(mahalanobis_distance_fast));
  if (std::isnan(mahalanobis_distance_fast))
  {
    std::cout << "ref (x,y,z)   = " << reference_in_sensor_frame.x << ", " << reference_in_sensor_frame.y << ", "
        << reference_in_sensor_frame.z << std::endl;
    std::cout << "point (x,y,z) = " << point_in_sensor_frame.x << ", " << point_in_sensor_frame.y << ", "
        << point_in_sensor_frame.z << std::endl;
    std::cout << "ref (z, sz, a, sa, b, sb) = " << reference_in_sensor_frame.z << ", "
        << reference_in_sensor_frame.data_c[GRND_REF_DATA_C_3_Z_VARIANCE] << ", "
        << reference_in_sensor_frame.data_c[GRND_REF_DATA_C_2_PITCH] << ", "
        << reference_in_sensor_frame.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE] << ", "
        << reference_in_sensor_frame.data_c[GRND_REF_DATA_C_1_ROLL] << ", "
        << reference_in_sensor_frame.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE] << std::endl;
  }
  return (mahalanobis_distance_fast);
}

float CKf_Based_Terrain_Analysis::predictZ(const pcl::PointXYZRGBNormal reference, const float delta_x,
                                           const float delta_y)
{
  //CFunctionMonitor performance_monitor("predictZ", performance_supervisor_ptr_);
  float roll_slope = reference.data_c[GRND_REF_DATA_C_1_ROLL];
  float pitch_slope = reference.data_c[GRND_REF_DATA_C_2_PITCH];

  float predicted_z = reference.z + delta_x * pitch_slope + delta_y * roll_slope;
  return (predicted_z);
}

void CKf_Based_Terrain_Analysis::predictZWithVariance(const pcl::PointXYZRGBNormal reference, const float delta_x,
                                                      const float delta_y, float &z_predicted, float &var_z_predicted)
{
  CFunctionMonitor performance_monitor("predictZWithVariance", performance_supervisor_ptr_);

  float z_local_frame = reference.z;
  float roll_slope = reference.data_c[GRND_REF_DATA_C_1_ROLL];
  float pitch_slope = reference.data_c[GRND_REF_DATA_C_2_PITCH];

  z_predicted = z_local_frame + delta_x * pitch_slope + delta_y * roll_slope;

  Eigen::Vector3f partial_derivatives;
  partial_derivatives(0) = 1;
  partial_derivatives(1) = delta_x;
  partial_derivatives(2) = delta_y;

  Eigen::Matrix3f cov = Eigen::Matrix3f::Zero();
  cov(0, 0) = reference.data_c[GRND_REF_DATA_C_3_Z_VARIANCE];
  cov(1, 1) = reference.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE];
  cov(2, 2) = reference.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE];

  var_z_predicted = partial_derivatives.transpose() * cov * partial_derivatives;
}
void CKf_Based_Terrain_Analysis::estimateTangentPlane(
    const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr observations_for_tangent_plane_estimation,
    const double measurement_std_dev, pcl::PointXYZRGBNormal &reference_in_sensor_frame)
{
  //CFunctionMonitor performance_monitor("estimateTangentPlane", performance_supervisor_ptr_);

  Eigen::Vector3d x;
  x << (double)reference_in_sensor_frame.z, (double)reference_in_sensor_frame.data_c[GRND_REF_DATA_C_2_PITCH], (double)reference_in_sensor_frame.data_c[GRND_REF_DATA_C_1_ROLL];

  Eigen::Matrix3d P;
  P << (double)reference_in_sensor_frame.data_c[GRND_REF_DATA_C_3_Z_VARIANCE], 0.0, 0.0, 0.0, (double)reference_in_sensor_frame.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE], 0.0, 0.0, 0.0, (double)reference_in_sensor_frame.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE];

  //std::cout << "x prior = " << std::endl << x << std::endl;
  //std::cout << "P prior = " << std::endl << P << std::endl;

  Eigen::RowVector3d H;
  Eigen::Vector3d K;

  double r = measurement_std_dev * measurement_std_dev;

  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = observations_for_tangent_plane_estimation->begin();
      i != observations_for_tangent_plane_estimation->end(); ++i)
  {
    H << (double)1.0, (i->x - (double)reference_in_sensor_frame.x), (i->y - (double)reference_in_sensor_frame.y);

    //std::cout << "H = " << std::endl << H << std::endl;

    K = P * H.transpose() / (H * P * H.transpose() + r);
    //std::cout << "K = " << std::endl << K << std::endl;

    x = x + K * (i->z - (H * x));
    P = P - K * H * P;

    //std::cout << "x posterior = " << std::endl << x << std::endl;
    //std::cout << "P posterior = " << std::endl << P << std::endl;
  }

  reference_in_sensor_frame.z = (float)x(0);
  reference_in_sensor_frame.data_c[GRND_REF_DATA_C_2_PITCH] = (float)x(1);
  reference_in_sensor_frame.data_c[GRND_REF_DATA_C_1_ROLL] = (float)x(2);

  reference_in_sensor_frame.data_c[GRND_REF_DATA_C_3_Z_VARIANCE] = (float)P(0, 0);
  reference_in_sensor_frame.data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE] = (float)P(1, 1);
  reference_in_sensor_frame.data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE] = (float)P(2, 2);

  //std::cout << "x posterior = " << std::endl << x << std::endl;
  //std::cout << "P posterior = " << std::endl << P << std::endl;
  //getchar();
}

bool CKf_Based_Terrain_Analysis::checkCollisionInPath(
    const pcl::PointXYZRGBNormal vertex_a, const pcl::PointXYZRGBNormal vertex_b,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr points_in_ROI, pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_ptr,
    const std::vector<std::vector<int> > &correspondence_indexes,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration)
{
  bool collision = false;

  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = points_in_ROI->begin();
      i != points_in_ROI->end() && !collision; ++i)
  {
    pcl::PointXYZRGBNormal point_in_sensor_frame = *i;

    // 1)We point to the vector of indices related to the elevation_cloud point under evaluation
    std::vector<std::vector<int>>::const_iterator index_iterator = correspondence_indexes.begin()
        + point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX];

    // and iterate over this vector
    for (std::vector<int>::const_iterator point_iterator = index_iterator->begin();
        point_iterator != index_iterator->end() && !collision; ++point_iterator)
    {
      pcl::PointXYZRGBNormal point_under_evaluation = cloud_ptr->points[*point_iterator];
      if ((int)std::floor(point_under_evaluation.data_c[DATA_C_1_ID_CLASS]) != CLASS_GROUND)
      {
        if (point_under_evaluation.z - point_under_evaluation.data_n[DATA_N_1_Z_GROUND]
            < filtering_configuration.robot_height + filtering_configuration.safety_margin)
        {
          kf_based_terrain_analysis_lib::Coordinates2D coord;
          coord.x = point_under_evaluation.x;
          coord.y = point_under_evaluation.y;

          std::vector<kf_based_terrain_analysis_lib::Coordinates2D> vertex_list;
          int closest_vertex_index;

          kf_based_terrain_analysis_lib::Robot2DFootprint rectangular_path_between_vertices;
          float delta_x = vertex_b.x - vertex_a.x;
          float delta_y = vertex_b.y - vertex_a.y;
          rectangular_path_between_vertices.yaw = atan2(delta_y, delta_x);
          rectangular_path_between_vertices.dimensions.x = sqrt(delta_x * delta_x + delta_y * delta_y);
          rectangular_path_between_vertices.dimensions.y = filtering_configuration.robot_width
              + filtering_configuration.safety_margin * 2.0;
          rectangular_path_between_vertices.centroid.x = vertex_a.x
              + (rectangular_path_between_vertices.dimensions.x / 2.0) * cos(rectangular_path_between_vertices.yaw);
          rectangular_path_between_vertices.centroid.y = vertex_a.y
              + (rectangular_path_between_vertices.dimensions.x / 2.0) * sin(rectangular_path_between_vertices.yaw);
          calculateVertexCoordinates(rectangular_path_between_vertices, vertex_list, closest_vertex_index);
          if (!collision)
            collision = checkInside(coord, vertex_list);
          if (false)
          {
            std::cout << "Collision detected! Edge discarded!" << std::endl;
            std::cout << "point_under_evaluation class = "
                << (int)std::floor(point_under_evaluation.data_c[DATA_C_1_ID_CLASS]) << std::endl << "point x = "
                << coord.x << "    point y = " << coord.y << std::endl << "vertex_a.x = " << vertex_a.x
                << "    vertex_a.y = " << vertex_a.y << std::endl << "vertex_b.x = " << vertex_b.x
                << "    vertex_b.y = " << vertex_b.y << std::endl << "rectangular_path_between_vertices.yaw = "
                << rectangular_path_between_vertices.yaw * 180.0 / M_PI << std::endl
                << "rectangular_path_between_vertices.dimensions.x = " << rectangular_path_between_vertices.dimensions.x
                << std::endl << "rectangular_path_between_vertices.dimensions.y = "
                << rectangular_path_between_vertices.dimensions.y << std::endl
                << "rectangular_path_between_vertices.centroid.x = " << rectangular_path_between_vertices.centroid.x
                << std::endl << "rectangular_path_between_vertices.centroid.y = "
                << rectangular_path_between_vertices.centroid.y << std::endl;

            std::cout << "Vertices = " << std::endl << "p1 = " << vertex_list[0].x << "    " << vertex_list[0].y
                << std::endl << "p2 = " << vertex_list[1].x << "    " << vertex_list[1].y << std::endl << "p3 = "
                << vertex_list[2].x << "    " << vertex_list[2].y << std::endl << "p4 = " << vertex_list[3].x << "    "
                << vertex_list[3].y << std::endl;
            //getchar();
          }
        }
      }
    }
  }
  return (collision);
}

void CKf_Based_Terrain_Analysis::makeVirtualObstacle(
    const pcl::PointXYZRGBNormal point_a, const pcl::PointXYZRGBNormal point_b,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacle_cloud)
{
  float delta_x = point_b.x - point_a.x;
  float delta_y = point_b.y - point_a.y;

  std::vector<kf_based_terrain_analysis_lib::Coordinates2D> vertex_list;
  int closest_vertex_index;
  kf_based_terrain_analysis_lib::Robot2DFootprint rectangular_path_between_vertices;

  rectangular_path_between_vertices.yaw = atan2(delta_y, delta_x);
  rectangular_path_between_vertices.dimensions.x = sqrt(delta_x * delta_x + delta_y * delta_y);
  rectangular_path_between_vertices.dimensions.y = filtering_configuration.robot_width
      + filtering_configuration.safety_margin * 2.0;
  rectangular_path_between_vertices.centroid.x = point_a.x
      + (rectangular_path_between_vertices.dimensions.x / 2.0) * cos(rectangular_path_between_vertices.yaw);
  rectangular_path_between_vertices.centroid.y = point_b.y
      + (rectangular_path_between_vertices.dimensions.x / 2.0) * sin(rectangular_path_between_vertices.yaw);
  calculateVertexCoordinates(rectangular_path_between_vertices, vertex_list, closest_vertex_index);

  float x_min = 10000.0;
  float x_max = -10000.0;

  float y_min = 10000.0;
  float y_max = -10000.0;

  for (int i = 0; i < 4; ++i)
  {
    if (vertex_list[i].x < x_min)
      x_min = vertex_list[i].x;

    if (vertex_list[i].x > x_max)
      x_max = vertex_list[i].x;

    if (vertex_list[i].y < y_min)
      y_min = vertex_list[i].y;

    if (vertex_list[i].y > y_max)
      y_max = vertex_list[i].y;
  }
  //std::cout << "x_min = " << x_min << "    x_max = " << x_max << std::endl;
  //std::cout << "y_min = " << y_min << "    y_max = " << y_max << std::endl;

  float delta_object = 0.1; //TODO: extract as parameter
  for (float i = x_min; i <= x_max; i += delta_object)
  {
    for (float j = y_min; j <= y_max; j += delta_object)
    {
      //std::cout << "Generating virtual obstacle point at x = " << i << "    y = " << j << std::endl;

      kf_based_terrain_analysis_lib::Coordinates2D coord;
      coord.x = i;
      coord.y = j;

      bool inside = checkInside(coord, vertex_list);
      if (inside)
      {
        pcl::PointXYZRGBNormal virtual_obstacle_point;
        virtual_obstacle_point.x = coord.x;
        virtual_obstacle_point.y = coord.y;
        virtual_obstacle_point.z = point_a.z;
        virtual_obstacle_point.data_c[DATA_C_1_ID_CLASS] = CLASS_DISCONTINUITY;
        virtual_obstacle_point.r = 255;
        virtual_obstacle_point.g = 0;
        virtual_obstacle_point.b = 0;
        virtual_obstacle_cloud->push_back(virtual_obstacle_point);
      }
    }
  }
}

void CKf_Based_Terrain_Analysis::makeVirtualObstacle(
    const pcl::PointXYZRGBNormal point, const float yaw,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacle_cloud)
{

}

void CKf_Based_Terrain_Analysis::labelLocalGroundPoints(
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    const std::vector<std::vector<int> > &correspondence_indexes, const int reference_index, const float search_range,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr points_in_ROI,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr references_in_ROI,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_ptr, std::vector<std::vector<int>> &edges,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacles_cloud_ptr)
{
  //std::cout << "Starting labelLocalGroundPoints!!" << std::endl;
  if (filtering_configuration.measure_performance)
    CFunctionMonitor performance_monitor("labelLocalGroundPoints", performance_supervisor_ptr_);

  CObservationDownsampler new_references_creator(filtering_configuration.ground_reference_search_resolution,
                                                 filtering_configuration.propagation_additive_noise,
                                                 filtering_configuration.propagation_z_additive_noise);

//  std::cout << "Reference_index = " << reference_index << std::endl;
//  std::cout << "Using reference with ID = "
//      << ground_reference_cloud_ptr->points[reference_index].data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] << std::endl;
  pcl::PointXYZRGBNormal reference_in_sensor_frame = ground_reference_cloud_ptr->points[reference_index];

  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr observations_for_tangent_plane_estimation(
      new pcl::PointCloud<pcl::PointXYZRGBNormal>);

// points in ROI come from elevation pointcloud, which is a reduction of the original
// pointcloud (we have stored the indexes to be able to label the original pointcloud)
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = points_in_ROI->begin(); i != points_in_ROI->end(); ++i)
  {
    pcl::PointXYZRGBNormal point_in_sensor_frame = *i;

    float prediction_std_dev = -1.0;
    float mahalanobis_distance = mahalanobisDistance(reference_in_sensor_frame, point_in_sensor_frame,
                                                     prediction_std_dev);

    if (std::isnan(mahalanobis_distance))
    {
      std::cout << "prior pred sigma = " << prediction_std_dev << "    uncertainty = "
          << prediction_std_dev * filtering_configuration.mahalanobis_threshold << "    mahalanobis distance = "
          << mahalanobis_distance << std::endl;

      std::getchar();
    }

    if (mahalanobis_distance < filtering_configuration.mahalanobis_threshold)
    {
//        std::cout << "Ground point found! x = " << point_in_sensor_frame.x << "    y = " << point_in_sensor_frame.y
//            << "    z = " << point_in_sensor_frame.z << std::endl
//            << "mahalanobis_distance = " << mahalanobis_distance << "    mahalanobis_threshold = "
//            << filtering_configuration.mahalanobis_threshold << std::endl;

//        std::cout << "Inserting observation for plane estimation!" << std::endl;
      observations_for_tangent_plane_estimation->push_back(point_in_sensor_frame); // independently of exploration, any ground point is
                                                                                   // used for plane estimation, to no discard useful
                                                                                   // information!

      if ((int)std::floor(point_in_sensor_frame.data_c[DATA_C_1_ID_CLASS]) == CLASS_UNKNOWN) // we use unknown points to drive the exploration
      {
//          std::cout << "Inserting observation to drive exploration!" << std::endl;
        new_references_creator.addGroundObservationInSensorFrame(
            reference_in_sensor_frame,
            elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]]);
      }

//        std::cout << "Updating elevation cloud point to represent ground data" << std::endl;
      if ((int)std::floor(point_in_sensor_frame.data_c[DATA_C_1_ID_CLASS]) != CLASS_GROUND) // if the point was seen as obstacle from another POV
                                                                                            // or was unexplored, with set it to ground class
      {
//          std::cout << "Storing z point as z ground" << std::endl;
        // as now the point is classified as ground, we store its own z value as z ground
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].data_n[DATA_N_1_Z_GROUND] =
            elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].z;

        // set the class as ground
        float score = 0.99 - (mahalanobis_distance / filtering_configuration.mahalanobis_threshold);
        if (score < 0.0)
          score = 0.0;
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].data_c[DATA_C_1_ID_CLASS] =
            (float)CLASS_GROUND + score;

        // colour the point properly
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].r = R_CLASS_GROUND;
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].g = G_CLASS_GROUND;
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].b = B_CLASS_GROUND;
      }
    }
    else
    {
//        std::cout << "Obstacle point found! x = " << point_in_sensor_frame.x << "    y = " << point_in_sensor_frame.y
//            << "    z = " << point_in_sensor_frame.z << std::endl
//            << "mahalanobis_distance = " << mahalanobis_distance << "    mahalanobis_threshold = "
//            << filtering_configuration.mahalanobis_threshold << std::endl;

      if ((int)std::floor(point_in_sensor_frame.data_c[DATA_C_1_ID_CLASS]) == CLASS_UNKNOWN) // we only store de data if it has not been done
                                                                                             // previously
      {
        // if it is an obstacle point we use the ground reference z prediction as z ground
        float z_ground = predictZ(ground_reference_cloud_ptr->points[reference_index], point_in_sensor_frame.x,
                                  point_in_sensor_frame.y);
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].data_n[DATA_N_1_Z_GROUND] =
            z_ground;

        // set the class to OBSTACLE
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].data_c[DATA_C_1_ID_CLASS] =
            (float)CLASS_OBSTACLE + 0.99;

        // and colour the point
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].r = R_CLASS_OBSTACLE;
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].g = G_CLASS_OBSTACLE;
        elevation_cloud_ptr->points[point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX]].b = B_CLASS_OBSTACLE;
      }
    }
  }

//   std::cout << "Reference Prior = " << std::endl;
//   std::cout << "x = " << ground_reference_cloud_ptr->points[reference_index].x << "    y = "
//   << ground_reference_cloud_ptr->points[reference_index].y << std::endl << "    z     = ( "
//   << ground_reference_cloud_ptr->points[reference_index].z << " , "
//   << sqrt(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_3_Z_VARIANCE]) << " )"
//   << "    roll  = ( " << atan(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_1_ROLL]) * 180.0 / M_PI
//   << " , " << sqrt(ground_reference_cloud_ptr->points[reference_index].data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE]) * 180.0 / M_PI << " )"
//   << "    pitch = ( " << atan(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_2_PITCH]) * 180.0 / M_PI
//   << " , " << sqrt(ground_reference_cloud_ptr->points[reference_index].data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE]) * 180.0 / M_PI << " )"
//   << std::endl;

  estimateTangentPlane(observations_for_tangent_plane_estimation, (double)filtering_configuration.z_observation_std_dev,
                       reference_in_sensor_frame);

  ground_reference_cloud_ptr->points[reference_index] = reference_in_sensor_frame;

//   std::cout << "Reference Posterior = " << std::endl;
//   std::cout << "x = " << ground_reference_cloud_ptr->points[reference_index].x << "    y = "
//   << ground_reference_cloud_ptr->points[reference_index].y << std::endl << "    z     = ( "
//   << ground_reference_cloud_ptr->points[reference_index].z << " , "
//   << sqrt(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_3_Z_VARIANCE]) << " )"
//   << "    roll  = ( " << atan(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_1_ROLL]) * 180.0 / M_PI
//   << " , " << sqrt(ground_reference_cloud_ptr->points[reference_index].data_n[GRND_REF_DATA_N_0_ROLL_VARIANCE]) * 180.0 / M_PI << " )"
//   << "    pitch = ( " << atan(ground_reference_cloud_ptr->points[reference_index].data_c[GRND_REF_DATA_C_2_PITCH]) * 180.0 / M_PI
//   << " , " << sqrt(ground_reference_cloud_ptr->points[reference_index].data_n[GRND_REF_DATA_N_1_PITCH_VARIANCE]) * 180.0 / M_PI << " )"
//   << std::endl;
  // Now we evaluate the points in the original pointcloud using the ROI indexes vectors! using the posterior!
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = points_in_ROI->begin(); i != points_in_ROI->end(); ++i)
  {
    pcl::PointXYZRGBNormal point_in_sensor_frame = *i;

    // 1)We point to the vector of indices related to the elevation_cloud point under evaluation
    std::vector<std::vector<int>>::const_iterator index_iterator = correspondence_indexes.begin()
        + point_in_sensor_frame.data_c[DATA_C_3_ORIGINAL_INDEX];

    // and iterate over this vector
    for (std::vector<int>::const_iterator point_iterator = index_iterator->begin();
        point_iterator != index_iterator->end(); ++point_iterator)
    {
      if ((int)std::floor(cloud_ptr->points[*point_iterator].data_c[DATA_C_1_ID_CLASS]) != CLASS_GROUND)
      {
        float prediction_std_dev = -1.0;
        float mahalanobis_distance = mahalanobisDistance(reference_in_sensor_frame, cloud_ptr->points[*point_iterator],
                                                         prediction_std_dev);
        if (std::isnan(mahalanobis_distance))
        {
          std::cout << "posterior pred sigma = " << prediction_std_dev << "    uncertainty = "
              << prediction_std_dev * filtering_configuration.mahalanobis_threshold << "    mahalanobis distance = "
              << mahalanobis_distance << std::endl;

          std::getchar();
        }

        if (mahalanobis_distance < filtering_configuration.mahalanobis_threshold)
        {
          // Intensity is already in the pointcloud so we do not copy data_n[0] (in fact this value is not present in elevation cloud)
          cloud_ptr->points[*point_iterator].data_n[DATA_N_1_Z_GROUND] = cloud_ptr->points[*point_iterator].z;
          cloud_ptr->points[*point_iterator].data_n[DATA_N_2_Z_MEAN] = point_in_sensor_frame.data_n[DATA_N_2_Z_MEAN];
          cloud_ptr->points[*point_iterator].data_n[DATA_N_3_Z_VARIANCE] =
              point_in_sensor_frame.data_n[DATA_N_3_Z_VARIANCE];

          // data_c[0] is reserved to cast the RGB values into a float (PCL convention) so we do not use it
          cloud_ptr->points[*point_iterator].r = R_CLASS_GROUND;           // We use instead the r g b channels directly
          cloud_ptr->points[*point_iterator].g = G_CLASS_GROUND;
          cloud_ptr->points[*point_iterator].b = B_CLASS_GROUND;

          float score = 0.99 - (mahalanobis_distance / filtering_configuration.mahalanobis_threshold);
          if (score < 0.0)
            score = 0.0;
          cloud_ptr->points[*point_iterator].data_c[DATA_C_1_ID_CLASS] = (float)CLASS_GROUND + score;
          // data_c[2] is reserved for Car probability, as we do not estimate it in this algorithm we leave this value untouched
          // so if there are others segmentators working out there, we do not erase their job!

          // data_c[3] is reserved to store the original index, so we do not want to change it!
        }
        else
        {
          // Intensity is already in the pointcloud so we do not copy data_n[0] (in fact this value is not present in elevation cloud)
          cloud_ptr->points[*point_iterator].data_n[DATA_N_1_Z_GROUND] = point_in_sensor_frame.z;
          cloud_ptr->points[*point_iterator].data_n[DATA_N_2_Z_MEAN] = point_in_sensor_frame.data_n[DATA_N_2_Z_MEAN];
          cloud_ptr->points[*point_iterator].data_n[DATA_N_3_Z_VARIANCE] =
              point_in_sensor_frame.data_n[DATA_N_3_Z_VARIANCE];

          // data_c[0] is reserved to cast the RGB values into a float (PCL convention) so we do not use it
          cloud_ptr->points[*point_iterator].r = R_CLASS_OBSTACLE;         // We use instead the r g b channels directly
          cloud_ptr->points[*point_iterator].g = G_CLASS_OBSTACLE;
          cloud_ptr->points[*point_iterator].b = B_CLASS_OBSTACLE;

          cloud_ptr->points[*point_iterator].data_c[DATA_C_1_ID_CLASS] = (float)CLASS_OBSTACLE + 0.99;
          // data_c[2] is reserved for Car probability, as we do not estimate it in this algorithm we leave this value untouched
          // so if there are others segmentators working out there, we do not erase their job!

          // data_c[3] is reserved to store the original index, so we do not want to change it!
        }
      }
    }
  }

  assert(
      reference_index == reference_in_sensor_frame.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]
          && "something unexpected happened!!");

  // Now we make edges to existing references in the ROI
  //std::cout << "Number of references in ROI = " << references_in_ROI->size() << std::endl;
  int discarded_edges = 0;
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator i = references_in_ROI->begin(); i != references_in_ROI->end();
      ++i)
  {
    pcl::PointXYZRGBNormal reference_in_ROI = *i;

    if (reference_index != reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]
        && (std::find(edges[reference_index].begin(), edges[reference_index].end(),
                      reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]) == edges[reference_index].end())
        && (std::find(edges[reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]].begin(),
                      edges[reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]].end(), reference_index)
            == edges[reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]].end()))
    {
      //std::cout << "reference_index = " << reference_index << std::endl;
      //std::cout << "reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] = "
      //    << reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] << std::endl;
      //std::cout << "edges[reference_index].size() = " << edges[reference_index].size() << std::endl;
      //int n = 0;
      //for (std::vector<int>::iterator i = edges[reference_index].begin(); i != edges[reference_index].end(); i++)
      //{
      //  std::cout << "edges[" << n++ << "] = " << *i << std::endl;
      //}
//      std::cout << "Analizing vertex in ROI with id = " << reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]
//          << std::endl;

      float prediction_std_dev = -1.0;
      float mahalanobis_distance = mahalanobisDistance(reference_in_sensor_frame, reference_in_ROI, prediction_std_dev);

      // Just a check!
      if (std::isnan(mahalanobis_distance))
      {
        std::cout << "posterior pred sigma = " << prediction_std_dev << "    uncertainty = "
            << prediction_std_dev * filtering_configuration.mahalanobis_threshold << "    mahalanobis distance = "
            << mahalanobis_distance << std::endl;

        std::getchar();
      }
      //std::cout << "Checking connections..." << std::endl;
      bool connect = true;
      bool make_virtual_obstacle = false;

      float delta_x = reference_in_ROI.x - reference_in_sensor_frame.x;
      float delta_y = reference_in_ROI.y - reference_in_sensor_frame.y;
      float delta_2D = sqrt(delta_x * delta_x + delta_y * delta_y);
      float delta_z = reference_in_ROI.z - reference_in_sensor_frame.z;
      float slope = delta_z / delta_2D;

      if (mahalanobis_distance > filtering_configuration.mahalanobis_threshold
          || fabs(slope) > filtering_configuration.max_slope_abs)
      {
        std::cout << "Discarding connection! Euclidean distance = " << delta_2D << "    Mahalanobis distance = " << mahalanobis_distance
            << "    Mahalanobis threshold = " << filtering_configuration.mahalanobis_threshold << std::endl
            << " Slope = " << fabs(slope) << "    slope threshold = " << filtering_configuration.max_slope_abs
            << std::endl;
        connect = false;
        if(delta_2D < 2.0)
          make_virtual_obstacle = true;
      }
      else
      {
        if (!filtering_configuration.flag_connect_vertices_even_if_there_are_obstacles
            && checkCollisionInPath(reference_in_sensor_frame, reference_in_ROI, points_in_ROI, cloud_ptr,
                                    correspondence_indexes, filtering_configuration))
        {
          //std::cout << "Obstacle detected, discarding connection!" << std::endl;
          connect = false;
          if (filtering_configuration.flag_generate_discontinuity_obstacles_for_real_obstacles)
          {
            make_virtual_obstacle = true;
          }
        }
      }

      if (connect)
      {
        //std::cout << "Adding edge between vertex " << reference_in_sensor_frame.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]
        //    << " and vertex " << reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] << std::endl;
        edges[reference_in_sensor_frame.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]].push_back(
            reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]);
      }
      else
      {
        discarded_edges++;
      }

      if (make_virtual_obstacle)
      {
        //std::cout << "Generating points for virtual obstacle!" << std::endl;
        pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacle(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
        makeVirtualObstacle(reference_in_sensor_frame, reference_in_ROI, filtering_configuration, virtual_obstacle);
        *virtual_obstacles_cloud_ptr += *virtual_obstacle;
      }
    }
    else
    {
//      std::cout << "Skip adding edge between vertex " << reference_in_sensor_frame.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX]
//          << " and vertex " << reference_in_ROI.data_n[GRND_REF_DATA_N_3_ORIGINAL_INDEX] << std::endl;
    }
  }
//std::cout << "Total edges evaluated = " << references_in_ROI->points.size() << "    discarded = " << discarded_edges
//    << std::endl;
//getchar();
  new_references_creator.generateNewGroundReferences(reference_index, reference_in_sensor_frame,
                                                     ground_reference_cloud_ptr, edges);
}

void CKf_Based_Terrain_Analysis::labelGroundPoints(
    const kf_based_terrain_analysis_lib::SensorConfiguration lidar_configuration,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    const std::vector<std::vector<int> > &correspondence_indexes, const int reference_index,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_ptr, std::vector<std::vector<int>> &edges,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacles_cloud_ptr)
{
  float search_range = 0.0;
  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr points_in_ROI(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr references_in_ROI(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
  extractPointsInROI(lidar_configuration, filtering_configuration, ground_reference_cloud_ptr->points[reference_index],
                     elevation_cloud_ptr, points_in_ROI, ground_reference_cloud_ptr, references_in_ROI, search_range);

//  std::cout << "Reference index = " << reference_index << std::endl;
//  std::cout << "Points in ROI extracted!! Number of points in ROI = " << points_in_ROI->points.size() << std::endl;
//  std::cout << "References in ROI extracted!! Number of references in ROI = " << references_in_ROI->points.size()
//      << std::endl;

  //std::cout << "Number of edges in reference prior to labeling = " << edges[reference_index].size() << std::endl;
  labelLocalGroundPoints(filtering_configuration, correspondence_indexes, reference_index, search_range, points_in_ROI,
                         references_in_ROI, ground_reference_cloud_ptr, elevation_cloud_ptr, cloud_ptr, edges,
                         virtual_obstacles_cloud_ptr);

  //std::cout << "Number of edges after labeling = " << edges[reference_index].size() << std::endl;

//getchar();
}

void CKf_Based_Terrain_Analysis::generateElevationCloud(
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pcl_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud, std::vector<std::vector<int>> &correspondence_indexes)
{
  if (filtering_configuration.measure_performance)
    CFunctionMonitor performance_monitor("generateElevationCloud", performance_supervisor_ptr_);

  pcl::PointCloud<pcl::PointXY>::Ptr pcl_pointcloud_2D_ptr(new pcl::PointCloud<pcl::PointXY>);
  pcl::PointXY point;
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::const_iterator it = pcl_cloud_ptr->begin(); it != pcl_cloud_ptr->end();
      ++it)
  {
    //if(it->data_c[DATA_C_1_ID_CLASS] != CLASS_UNKNOWN)
    //{
    //  std::cout << "class in original point cloud prior to label process = " <<
    //  it->data_c[DATA_C_1_ID_CLASS] << std::endl;
    //}
    point.x = it->x;
    point.y = it->y;
    pcl_pointcloud_2D_ptr->points.push_back(point);
  }

//std::cout << "Number of points in pcl_pointcloud_2D_ptr = " << pcl_pointcloud_2D_ptr->points.size() << std::endl;

  pcl::KdTreeFLANN < pcl::PointXY > kdtree_2D;
  kdtree_2D.setInputCloud(pcl_pointcloud_2D_ptr);

// We want to find the x and y values, but not interested in z
//std::cout << "Elevation grid resolution = " << filtering_configuration.elevation_grid_resolution << std::endl;
  pcl::VoxelGrid < pcl::PointXYZRGBNormal > voxel_grid;
  voxel_grid.setInputCloud(pcl_cloud_ptr);
  voxel_grid.setLeafSize(filtering_configuration.elevation_grid_resolution,
                         filtering_configuration.elevation_grid_resolution, OUT_OF_RANGE);
  voxel_grid.filter(*elevation_cloud);
//std::cout << "Number of points after voxelization = " << elevation_cloud->points.size() << std::endl;

  const float DISTANCE_FROM_CELL_CENTER_TO_CELL_CORNER = 0.7071067812
      * filtering_configuration.elevation_grid_resolution; //sqrt(2)/2.0

  int i = 0;
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator it = elevation_cloud->begin(); it != elevation_cloud->end();
      ++it)
  {
    point.x = it->x;
    point.y = it->y;

    std::vector<int> pointIdxRadiusSearch;
    std::vector<float> pointRadiusSearchSquaredDistance;

    kdtree_2D.radiusSearch(point, DISTANCE_FROM_CELL_CENTER_TO_CELL_CORNER, pointIdxRadiusSearch,
                           pointRadiusSearchSquaredDistance);

    std::vector<float> z_coordinates;
    std::vector<float> x_coordinates;
    std::vector<float> y_coordinates;

    bool outlier_detected = false;
    for (std::vector<int>::const_iterator iterator = pointIdxRadiusSearch.begin();
        iterator != pointIdxRadiusSearch.end(); ++iterator)
    {
      z_coordinates.push_back(pcl_cloud_ptr->points[*iterator].z);
      x_coordinates.push_back(pcl_cloud_ptr->points[*iterator].x);
      y_coordinates.push_back(pcl_cloud_ptr->points[*iterator].y);

      if (pcl_cloud_ptr->points[*iterator].data_c[DATA_C_1_ID_CLASS] == OUTLIER)
        outlier_detected = true;
    }

    if (outlier_detected)
    {
      it->data_c[DATA_C_1_ID_CLASS] = OUTLIER;
      it->r = R_CLASS_OUTLIER;
      it->g = G_CLASS_OUTLIER;
      it->b = B_CLASS_OUTLIER;
    }
    else
    {
      it->data_c[DATA_C_1_ID_CLASS] = CLASS_UNKNOWN;
      it->r = R_CLASS_UNKNOWN;
      it->g = G_CLASS_UNKNOWN;
      it->b = B_CLASS_UNKNOWN;
    }

    float sum = std::accumulate(z_coordinates.begin(), z_coordinates.end(), 0.0);
    float z_mean = sum / (float)z_coordinates.size();
    float var = 0;
    for (int n = 0; n < z_coordinates.size(); ++n)
      var += ((z_coordinates[n] - z_mean) * (z_coordinates[n] - z_mean));

    var /= (float)z_coordinates.size();

    std::vector<float>::iterator lowest_point_in_cell = std::min_element(z_coordinates.begin(), z_coordinates.end());
    int position_in_vector = std::distance(z_coordinates.begin(), lowest_point_in_cell);

    it->z = z_coordinates[position_in_vector];
    it->x = x_coordinates[position_in_vector];
    it->y = y_coordinates[position_in_vector];

    // Filling remaining fields
    it->data_n[DATA_N_0_INTENSITY] = INTENSITY_UNKNOWN;
    it->data_n[DATA_N_1_Z_GROUND] = Z_GROUND_UNKNOWN;
    it->data_n[DATA_N_2_Z_MEAN] = z_mean;
    it->data_n[DATA_N_3_Z_VARIANCE] = var;

    it->data_c[DATA_C_2_CAR_PROB] = PROB_UNKNOWN;
    it->data_c[DATA_C_3_ORIGINAL_INDEX] = i++;    //storing the index for later in labelling step

    //std::cout << "var z = " << it->data_n[DATA_N_3_Z_VARIANCE] << std::endl;
    //std::cout << "z_coordinates.size() = " << z_coordinates.size() << std::endl;

    correspondence_indexes.push_back(pointIdxRadiusSearch);
  }
}

void CKf_Based_Terrain_Analysis::generateGroundReferencesInShadowArea(
    const kf_based_terrain_analysis_lib::SensorConfiguration lidar_configuration,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr, std::vector<std::vector<int>> &edges)
{
//std::cout << "Generating vertices in shadow area!" << std::endl;
//CFunctionMonitor performance_monitor("generateGroundReferencesInShadowArea", performance_supervisor_ptr_);
  pcl::PointXYZRGBNormal ground_at_shadow_area;

// The first reference point (root vertex) is the ground in (0,0) approximately known by sensor extrinsic calibration
  int index = 0;
  initizalizeShadowAreaGroundReferencePoint(lidar_configuration, filtering_configuration, 0.0, 0.0, index++,
                                            ground_at_shadow_area);

//std::cout << "generateGroundReferencesInShadowArea: r = " << (int)ground_at_shadow_area.r << "    g = " << (int)ground_at_shadow_area.g
//    << "    b = " << (int)ground_at_shadow_area.b << std::endl;

// we change the UNKNOWN cost to MIN_COST because we are already in the origin so it is absolutely reachable!!
  ground_at_shadow_area.data_n[GRND_REF_DATA_N_2_GOAL_COST] = MIN_GOAL_COST;
// but we keep the UNKNOWN original index because the origin has not a previous origin!
  ground_reference_cloud_ptr->points.push_back(ground_at_shadow_area);
//std::cout << "First ground reference point generated! Npoints = " << ground_reference_cloud_ptr->points.size()
//  << std::endl;

  float range = filtering_configuration.distance_to_virtual_references_in_shadow_area;
  float delta_theta = filtering_configuration.ground_reference_search_resolution;

  std::vector<int> edges_from_root_to_virtual_points_in_shadow_area;
  for (float theta = 0; theta < 1.99 * M_PI; theta += delta_theta) // the 1.99 value is for just giving a margin to not repeat the first reference
  {
    float x = range * cos(theta);
    float y = range * sin(theta);

    initizalizeShadowAreaGroundReferencePoint(lidar_configuration, filtering_configuration, x, y, index,
                                              ground_at_shadow_area);

    //std::cout << "Added edge between root and vertex " << index << std::endl;
    edges[0].push_back(index++);  // all shadow vertex are connected with the root

    //std::cout << "generateGroundReferencesInShadowArea: r = " << (int)ground_at_shadow_area.r << "    g = "
    //    << (int)ground_at_shadow_area.g << "    b = " << (int)ground_at_shadow_area.b << std::endl;

    ground_reference_cloud_ptr->points.push_back(ground_at_shadow_area);

    //getchar();
  }
}

void CKf_Based_Terrain_Analysis::labelGround(
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_ptr)
{
  CFunctionMonitor performance_monitor("labelGround", performance_supervisor_ptr_);

  pcl::KdTreeFLANN < pcl::PointXYZRGBNormal > kdtree;
  kdtree.setInputCloud(ground_reference_cloud_ptr);

//  std::vector<int> pointIdxNKNSearch(filtering_configuration.number_of_references_used_in_labelling); //to store the id of the closest real data point
//  std::vector<float> pointNKNSquaredDistance(filtering_configuration.number_of_references_used_in_labelling);

  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::iterator it_points = cloud_ptr->begin(); it_points != cloud_ptr->end();
      ++it_points)
  {
    std::vector<int> pointIdxNKNSearch(filtering_configuration.number_of_references_used_in_labelling); //to store the id of the closest real data point
    std::vector<float> pointNKNSquaredDistance(filtering_configuration.number_of_references_used_in_labelling);

    assert(
        kdtree.nearestKSearch(*it_points, filtering_configuration.number_of_references_used_in_labelling,
                              pointIdxNKNSearch, pointNKNSquaredDistance) > 0 && "Kdtree could not find a neighbourg!");

    pcl::PointXYZRGBNormal point_to_evaluate = *it_points;
    float min_mahalanobis_distance = 100000.0;
    pcl::PointXYZRGBNormal best_reference;
    for (int i = 0; i < filtering_configuration.number_of_references_used_in_labelling; ++i)
    {
      pcl::PointXYZRGBNormal reference = ground_reference_cloud_ptr->points[pointIdxNKNSearch[i]];
      float prediction_std_dev = -1.0;
      float mahalanobis_distance = mahalanobisDistance(reference, point_to_evaluate, prediction_std_dev);

      //std::cout << "mahalab = " << mahalanobis_distance << "    min mahalab = " << min_mahalanobis_distance << std::endl;

      if (mahalanobis_distance < min_mahalanobis_distance)
      {
        best_reference = reference;
        min_mahalanobis_distance = mahalanobis_distance;
      }
    }

//    std::cout << "----------------------------------------------------------------------------" << std::endl;
//    std::cout << "Number of references found = " << pointIdxNKNSearch.size() << std::endl;
//    std::cout << "z_observed = " << point_to_evaluate.z
//        << "    z_predicted = " << predictZ(best_reference, point_to_evaluate.x, point_to_evaluate.y)
//        << std::endl;
//    std::cout << "min_mahalanobis_distance = " << min_mahalanobis_distance << std::endl;
//
//    std::getchar();

    if (min_mahalanobis_distance < filtering_configuration.mahalanobis_threshold)
    {
      it_points->data_n[DATA_N_1_Z_GROUND] = it_points->z;

      // set the class as ground
      float threshold = filtering_configuration.mahalanobis_threshold;
      if (min_mahalanobis_distance > threshold)
        threshold = min_mahalanobis_distance * 1.2;

      it_points->data_c[DATA_C_1_ID_CLASS] = (float)CLASS_GROUND + 1.0 - (min_mahalanobis_distance / threshold);

      // colour the point properly
      it_points->r = R_CLASS_GROUND;
      it_points->g = G_CLASS_GROUND;
      it_points->b = B_CLASS_GROUND;
    }
    else
    {
      it_points->data_n[DATA_N_1_Z_GROUND] = predictZ(best_reference, point_to_evaluate.x, point_to_evaluate.y);
      // set the class to OBSTACLE
      it_points->data_c[DATA_C_1_ID_CLASS] = (float)CLASS_OBSTACLE + 1.0
          - (filtering_configuration.mahalanobis_threshold / min_mahalanobis_distance);

      // and colour the point
      it_points->r = R_CLASS_OBSTACLE;
      it_points->g = G_CLASS_OBSTACLE;
      it_points->b = B_CLASS_OBSTACLE;
    }
  }
}

void CKf_Based_Terrain_Analysis::labelGroundUsingElevationCloudAndCorrespondenceIndexes(
    const std::vector<std::vector<int> > &correspondence_indexes,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud_ptr)
{
  CFunctionMonitor performance_monitor("labelGroundUsingElevationCloudAndCorrespondenceIndexes",
                                       performance_supervisor_ptr_);
  std::vector<std::vector<int>>::const_iterator index_iterator = correspondence_indexes.begin();
  for (pcl::PointCloud<pcl::PointXYZRGBNormal>::const_iterator it = elevation_cloud_ptr->begin();
      it != elevation_cloud_ptr->end(); ++it)
  {
    for (std::vector<int>::const_iterator point_iterator = index_iterator->begin();
        point_iterator != index_iterator->end(); ++point_iterator)
    {
      // Intensity is already in the pointcloud so we do not copy data_n[0] (in fact this value is not present in elevation cloud)
      cloud_ptr->points[*point_iterator].data_n[DATA_N_1_Z_GROUND] = it->data_n[DATA_N_1_Z_GROUND];
      cloud_ptr->points[*point_iterator].data_n[DATA_N_2_Z_MEAN] = it->data_n[DATA_N_2_Z_MEAN];
      cloud_ptr->points[*point_iterator].data_n[DATA_N_3_Z_VARIANCE] = it->data_n[DATA_N_3_Z_VARIANCE];

      // data_c[0] is reserved to cast the RGB values into a float (PCL convention) so we do not use it
      cloud_ptr->points[*point_iterator].r = it->r; // We use instead the r g b channels directly
      cloud_ptr->points[*point_iterator].g = it->g;
      cloud_ptr->points[*point_iterator].b = it->b;

      cloud_ptr->points[*point_iterator].data_c[DATA_C_1_ID_CLASS] = it->data_c[DATA_C_1_ID_CLASS] + 0.99;
      // data_c[2] is reserved for Car probability, as we do not estimate it in this algorithm we leave this value untouched
      // so if there are others segmentators working out there, we do not erase their job!

      // data_c[3] is reserved to store the original index, so we do not want to change it!
    }
    ++index_iterator;
  }
}

void CKf_Based_Terrain_Analysis::createDenseGroundCloud(
    const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr,
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pcl_dense_cloud_ptr)
{
  std::cout << "setting input cloud" << std::endl;
  pcl::KdTreeFLANN < pcl::PointXYZRGBNormal > kdtree;
  kdtree.setInputCloud(ground_reference_cloud_ptr);

  std::vector<int> pointIdxNKNSearch(1); //to store the id of the closest real data point
  std::vector<float> pointNKNSquaredDistance(1);

  std::cout << "Starting dense cloud creation" << std::endl;
  for (float i = -50.0; i < 50.0; i = i + 0.5)
  {
    for (float j = -35.0; j < 35.0; j = j + 0.5)
    {
      pcl::PointXYZRGBNormal point;
      point.x = i;
      point.y = j;
      point.z = -1.75;

      point.r = 127;
      point.g = 127;
      point.b = 127;

      assert(
          kdtree.nearestKSearch(point, 1, pointIdxNKNSearch, pointNKNSquaredDistance) > 0
              && "Kdtree could not find a neighbourg!");

      //std::cout << "Extracting closest reference" << std::endl;
      pcl::PointXYZRGBNormal reference = ground_reference_cloud_ptr->points[pointIdxNKNSearch[0]];

      float delta_x = point.x - reference.x;
      float delta_y = point.y - reference.y;

      //std::cout << "Predicting point height" << std::endl;
      point.z = predictZ(reference, delta_x, delta_y);

      //std::cout << "Storing the point!" << std::endl;
      pcl_dense_cloud_ptr->push_back(point);
    }
  }
  std::cout << "Finished dense cloud creation!!" << std::endl;

}

void CKf_Based_Terrain_Analysis::groundSegmentation(
    const kf_based_terrain_analysis_lib::SensorConfiguration lidar_configuration,
    const kf_based_terrain_analysis_lib::FilteringConfiguration filtering_configuration,
    pcl::PointCloud<pcl::PointXYZRGBNormal> &pcl_cloud,
    pcl::PointCloud<pcl::PointXYZRGBNormal> &ground_references_pcl_cloud, std::vector<std::vector<int>> &edges)
{
//std::cout << "Starting ground segmentation!!" << std::endl;
//std::cout << "filtering_configuration_.show_dense_reconstruction = "
//    << filtering_configuration.show_dense_reconstruction << std::endl;
//  std::cout << "Initializing pointcloud: Number of points in pcl_cloud = " << pcl_cloud.points.size() << std::endl;

  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pcl_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
  *pcl_cloud_ptr = pcl_cloud;

// We create a pointcloud that will contain only trusted subsampled ground points, these points will be used
// as references to compute local planes to label near points
  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr ground_reference_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);

  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr elevation_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
  std::vector < std::vector<int> > correspondence_indexes;
  generateElevationCloud(filtering_configuration, pcl_cloud_ptr, elevation_cloud_ptr, correspondence_indexes);
//  std::cout << "Elevation cloud generated! Npoints = " << elevation_cloud_ptr->points.size() << std::endl;

  generateGroundReferencesInShadowArea(lidar_configuration, filtering_configuration, ground_reference_cloud_ptr, edges);
//  std::cout << "Shadow area reference points generated! Npoints = " << ground_reference_cloud_ptr->points.size()
//      << std::endl << "Number of edges = " << edges[0].size() << std::endl;

//getchar();

// We run the algorithm, starting from the reference at sensor origin it will try to find others trusty local
// ground references, which will increase the value of ground_reference_cloud->points.size(), evaluating
// local planes using these reference points, once no new references appear. The algorithm ends after evaluating
// all of them

  pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr virtual_obstacles_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);

  int reference_index = 0;
  while (reference_index < ground_reference_cloud_ptr->points.size())
  {
    //std::cout << "Evaluating reference num " << reference_index << ":" << std::endl << "x = "
    //    << ground_reference_cloud_ptr->points[reference_index].x << "    y = "
    //    << ground_reference_cloud_ptr->points[reference_index].y << "    z = "
    //    << ground_reference_cloud_ptr->points[reference_index].z << std::endl;
//
//    std::cout << "r = " << (int)ground_reference_cloud_ptr->points[reference_index].r << "    g = "
//        << (int)ground_reference_cloud_ptr->points[reference_index].g << "    b = "
//        << (int)ground_reference_cloud_ptr->points[reference_index].b << std::endl;

    labelGroundPoints(lidar_configuration, filtering_configuration, correspondence_indexes, reference_index++,
                      ground_reference_cloud_ptr, elevation_cloud_ptr, pcl_cloud_ptr, edges,
                      virtual_obstacles_cloud_ptr);

//    std::cout << "reference_index = " << reference_index << std::endl;
//    std::cout << "number of references = " << ground_reference_cloud_ptr->points.size() << std::endl;
  }

//  std::cout << "reference_index = " << reference_index << std::endl;
//  std::cout << "number of references = " << ground_reference_cloud_ptr->points.size() << std::endl;
//getchar();

  ground_references_pcl_cloud.clear();
  if (filtering_configuration.show_dense_reconstruction)
  {
    std::cout << "Ready to create dense reconstruction" << std::endl;
    pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pcl_dense_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGBNormal>);
    createDenseGroundCloud(ground_reference_cloud_ptr, pcl_dense_cloud_ptr);
    ground_references_pcl_cloud = *pcl_dense_cloud_ptr;
  }
  else
  {
    //std::cout << "Not creating dense reconstruction!"
    //    << "    filtering_configuration.show_dense_reconstruction = "
    //    << filtering_configuration.show_dense_reconstruction << std::endl;
    ground_references_pcl_cloud = *ground_reference_cloud_ptr;
  }
  pcl_cloud.clear();
  pcl_cloud = *pcl_cloud_ptr + *virtual_obstacles_cloud_ptr;

//pcl_cloud = *elevation_cloud_ptr;
//pcl_cloud.width = pcl_cloud.size();
//std::cout << "pcl_cloud.size() = " << pcl_cloud.size() << "    pcl_cloud.width = " << pcl_cloud.width << std::endl;
//std::cout << "elevation_cloud_ptr->size() = " << elevation_cloud_ptr->size() << "    elevation_cloud_ptr->width = " << elevation_cloud_ptr->width << std::endl;

//performance_supervisor_ptr_->printStatistics();
}