Dapat diselesaikan: masalah tentang awan lidar dari tim kendaraan tak berawak Yandex





Nama saya Andrey Gladkov, saya seorang pengembang di bidang kendaraan tak berawak. Hari ini saya akan berbagi dengan komunitas Habr tugas yang berkaitan dengan sensor terpenting dari drone - lidar, dan bagaimana kami memproses data lidar. Anda dapat mencoba menyelesaikan sendiri masalahnya di platform Kontes. Sistem akan memeriksa solusi menggunakan tes otomatis dan segera melaporkan hasilnya. Kode parsing dan solusi - di spoiler menjelang akhir posting. Bagi mereka yang menghadiri pertemuan di bengkel kami tahun lalu, tugas tersebut akan terasa familier - kami menawarkannya sebagai tiket masuk, tetapi kami tidak pernah membagikannya secara publik.



Kondisi

Batas waktu 15 detik
Batasan memori 64 MB
Memasukkan input standar atau input.txt
Keluaran keluaran standar atau keluaran.txt
Kendaraan tak berawak berdiri di atas permukaan aspal datar, dengan penutup dipasang di atap kendaraan. Pengukuran yang diperoleh lidar untuk satu periode pemindaian diberikan.



Dimensi adalah satu setN poin dengan koordinat x, y dan z... Lebih dari 50% titik dimiliki oleh jalan. Model posisi titik-titik yang menjadi milik jalan di angkasa adalah bidang dengan parameterisasi

SEBUAHā‹…x+Bā‹…y+Cā‹…z+D=0.

Titik-titik yang termasuk dalam jalan menyimpang dari model tidak lebih dari jumlah yang ditentukan p...



Temukan parameterSEBUAH,B,C dan Dpesawat yang sesuai dengan jalan. Jumlah titik yang menyimpang dari model tidak lebih darip, harus minimal 50% dari total jumlah poin.



Masukkan format



Data masukan diberikan dalam format teks. Baris pertama berisi ambang batas tetapp (0,01≤p≤0,5)... Baris kedua berisi jumlah poinN (3≤N≤25000)... PengikutN garis berisi koordinat x, y dan z (-100≤x,y,z≤100)untuk setiap titik, pembatasnya adalah karakter tab (format string "x[TAB]y[TAB]z"). Bilangan real tidak lebih dari 6 tempat desimal.



Format output



Parameter keluaran SEBUAH, B, C dan Dpesawat yang sesuai dengan jalan. Pisahkan angka dengan spasi. Parameter keluaran harus menentukan bidang yang benar.



Contoh 1

Memasukkan Keluaran
0,01
3
20 0 0
10-10 0
10 10 0
0.000000 0.000000 1.000000 0.000000

Contoh 2

Memasukkan Keluaran
0,01
3
20 0 3
10-10 2
10 10 2
-0,099504 0,000000 0,995037 -0,995037

Contoh 3

Memasukkan Keluaran
0,01
sepuluh
20-10 0,2
20 0 0.2
20 10 0.2
15-10 0,15
15 0 0.15
15 10 0.15
10-10 0.1
10 10 0.1
20 18 1.7
15-15 1.2
-0.010000 0.000000 0.999950 0.000000
Contoh ini sintetis. Ada lebih banyak objek di cloud titik nyata: bekerja pada kasus edge.



Di mana harus memutuskan



Anda dapat mencoba tangan Anda di Kontes



Parsing dan kode selesai



Parsing
, . 50% , , , , , — , . , , . . p.



, , . RANSAC ( ). ( ), , .



:



  • , ().
  • , — p , «».
  • , .


. — . - , .


Kode C ++
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <random>
#include <vector>

struct Point3d {
  double X = 0.0;
  double Y = 0.0;
  double Z = 0.0;
};

using PointCloud = std::vector<Point3d>;

struct Plane {
  double A = 0.0;
  double B = 0.0;
  double C = 0.0;
  double D = 0.0;
};

bool CreatePlane(
    Plane& plane,
    const Point3d& p1,
    const Point3d& p2,
    const Point3d& p3) {
  const double matrix[3][3] =
    {{          0,           0,           0},  // this row is not used
     {p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z},
     {p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z}};

  auto getMatrixSignedMinor = [&matrix](size_t i, size_t j) {
    const auto& m = matrix;
    return (m[(i + 1) % 3][(j + 1) % 3] * m[(i + 2) % 3][(j + 2) % 3] -
            m[(i + 2) % 3][(j + 1) % 3] * m[(i + 1) % 3][(j + 2) % 3]);
  };

  const double A = getMatrixSignedMinor(0, 0);
  const double B = getMatrixSignedMinor(0, 1);
  const double C = getMatrixSignedMinor(0, 2);
  const double D = -(A * p1.X + B * p1.Y + C * p1.Z);

  const double norm_coeff = std::sqrt(A * A + B * B + C * C);

  const double kMinValidNormCoeff = 1.0e-6;
  if (norm_coeff < kMinValidNormCoeff) {
    return false;
  }

  plane.A = A / norm_coeff;
  plane.B = B / norm_coeff;
  plane.C = C / norm_coeff;
  plane.D = D / norm_coeff;

  return true;
}

double CalcDistance(const Plane& plane, const Point3d& point) {
  // assume that the plane is valid
  const auto numerator = std::abs(
      plane.A * point.X + plane.B * point.Y + plane.C * point.Z + plane.D);
  const auto denominator = std::sqrt(
      plane.A * plane.A + plane.B * plane.B + plane.C * plane.C);
  return numerator / denominator;
}

int CountInliers(
    const Plane& plane,
    const PointCloud& cloud,
    double threshold) {
  return std::count_if(cloud.begin(), cloud.end(),
      [&plane, threshold](const Point3d& point) {
        return CalcDistance(plane, point) <= threshold; });
}

Plane FindPlaneWithFullSearch(const PointCloud& cloud, double threshold) {
  const size_t cloud_size = cloud.size();

  Plane best_plane;
  int best_number_of_inliers = 0;

  for (size_t i = 0; i < cloud_size - 2; ++i) {
    for (size_t j = i + 1; j < cloud_size - 1; ++j) {
      for (size_t k = j + 1; k < cloud_size; ++k) {
        Plane sample_plane;
        if (!CreatePlane(sample_plane, cloud[i], cloud[j], cloud[k])) {
          continue;
        }

        const int number_of_inliers = CountInliers(
            sample_plane, cloud, threshold);
        if (number_of_inliers > best_number_of_inliers) {
          best_plane = sample_plane;
          best_number_of_inliers = number_of_inliers;
        }
      }
    }
  }

  return best_plane;
}

Plane FindPlaneWithSimpleRansac(
    const PointCloud& cloud,
    double threshold,
    size_t iterations) {
  const size_t cloud_size = cloud.size();

  // use uint64_t to calculate the number of all combinations
  // for N <= 25000 without overflow
  const auto cloud_size_ull = static_cast<uint64_t>(cloud_size);
  const auto number_of_combinations =
      cloud_size_ull * (cloud_size_ull - 1) * (cloud_size_ull - 2) / 6;

  if (number_of_combinations <= iterations) {
    return FindPlaneWithFullSearch(cloud, threshold);
  }

  std::random_device rd;
  std::mt19937 gen(rd());
  std::uniform_int_distribution<size_t> distr(0, cloud_size - 1);

  Plane best_plane;
  int best_number_of_inliers = 0;

  for (size_t i = 0; i < iterations; ++i) {
    std::array<size_t, 3> indices;
    do {
      indices = {distr(gen), distr(gen), distr(gen)};
      std::sort(indices.begin(), indices.end());
    } while (indices[0] == indices[1] || indices[1] == indices[2]);

    Plane sample_plane;
    if (!CreatePlane(sample_plane,
                     cloud[indices[0]],
                     cloud[indices[1]],
                     cloud[indices[2]])) {
      continue;
    }

    const int number_of_inliers = CountInliers(
        sample_plane, cloud, threshold);
    if (number_of_inliers > best_number_of_inliers) {
      best_plane = sample_plane;
      best_number_of_inliers = number_of_inliers;
    }
  }

  return best_plane;
}

int main() {
  double p = 0.0;
  std::cin >> p;

  size_t points_number = 0;
  std::cin >> points_number;

  PointCloud cloud;
  cloud.reserve(points_number);
  for (size_t i = 0; i < points_number; ++i) {
    Point3d point;
    std::cin >> point.X >> point.Y >> point.Z;
    cloud.push_back(point);
  }

  const Plane plane = FindPlaneWithSimpleRansac(cloud, p, 100);

  std::cout << plane.A << ' '
            << plane.B << ' '
            << plane.C << ' '
            << plane.D << std::endl;

  return 0;
}


, :



  const double matrix[3][3] =
    {{          0,           0,           0},  // this row is not used
     {p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z},
     {p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z}};

  auto getMatrixSignedMinor = [&matrix](size_t i, size_t j) {
    const auto& m = matrix;
    return (m[(i + 1) % 3][(j + 1) % 3] * m[(i + 2) % 3][(j + 2) % 3] -
            m[(i + 2) % 3][(j + 1) % 3] * m[(i + 1) % 3][(j + 2) % 3]);
  };

  const double A = getMatrixSignedMinor(0, 0);
  const double B = getMatrixSignedMinor(0, 1);
  const double C = getMatrixSignedMinor(0, 2);
  const double D = -(A * p1.X + B * p1.Y + C * p1.Z);


( ). mSebuahtrsayax x-p1.X, y-p1.Y z-p1.Z. , x, y z, SEBUAH, B C .



, . unordered_set .



All Articles