يمكن حلها: مشكلة السحابة الليدار من فريق المركبات غير المأهولة Yandex





اسمي أندري جلادكوف ، أنا مطور في اتجاه المركبات غير المأهولة. سأشارك اليوم مع مجتمع هبر مهمة تتعلق بأهم جهاز استشعار للطائرة بدون طيار - الليدار ، وكيفية معالجتنا لبيانات الليدار. يمكنك محاولة حل المشكلة بنفسك على منصة المسابقة. سيقوم النظام بفحص الحل باستخدام الاختبارات التلقائية والإبلاغ عن النتيجة على الفور. الإعراب وكود الحل - في المفسدين في نهاية المنشور. بالنسبة لأولئك الذين حضروا الاجتماع في ورشة العمل الخاصة بنا العام الماضي ، ستبدو المهمة مألوفة - لقد قدمناها كتذكرة دخول ، لكننا لم نشاركها علنًا.



شرط

المهلة الزمنية 15 ثانية
تقييد الذاكرة 64 ميجا بايت
إدخال الإدخال القياسي أو input.txt
انتاج | الإخراج القياسي أو الإخراج. txt
مركبة غير مأهولة تقف على سطح أسفلت مسطح مع ليدار مثبت على سطح السيارة. يتم إعطاء القياسات التي حصل عليها الليدار لفترة مسح واحدة.



القياسات هي مجموعة منN النقاط مع الإحداثيات x، y و z... أكثر من 50٪ من النقاط تنتمي إلى الطريق. نموذج موقع النقاط التي تنتمي إلى الطريق في الفضاء هو مستوى مع تحديد المعلمات

Ax+By+Cz+D=0.

تنحرف النقاط التي تنتمي إلى الطريق عن النموذج بما لا يزيد عن مبلغ معين p...



البحث عن المعلماتA,B,C و Dالطائرة المقابلة للطريق. عدد النقاط التي تختلف عن النموذج بما لا يزيد عنp، يجب أن تكون على الأقل 50٪ من إجمالي عدد النقاط.



نمط الإدخال



يتم تقديم بيانات الإدخال بتنسيق نصي. يحتوي السطر الأول على حد ثابتp (0.01p0.5)... السطر الثاني يحتوي على عدد النقاطN (3N25000)... ما يليN خطوط تحتوي على إحداثيات x، y و z (100x,y,z100)لكل نقطة ، المحدد هو حرف جدولة (تنسيق سلسلة "x[TAB]y[TAB]z"). لا تحتوي الأعداد الحقيقية على أكثر من 6 منازل عشرية.



تنسيق الإخراج



معلمات الإخراج A، B، C و Dالطائرة المقابلة للطريق. افصل بين الأرقام بمسافات. يجب أن تحدد معلمات الإخراج المستوى الصحيح.



مثال 1

إدخال انتاج |
0.01
3
20 0 0
10-10 0
10 10 0
0.000000 0.000000 1.000000 0.000000

مثال 2

إدخال انتاج |
0.01
3
20 0 3
10-10 2
10 10 2
-0.099504 0.000000 0.995037 -0.995037

مثال 3

إدخال انتاج |
0.01
عشرة
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
هذه الأمثلة اصطناعية. هناك الكثير من الكائنات في سحابة النقطة الحقيقية: العمل على حالات الحافة.



أين تقرر



يمكنك تجربة يدك في المسابقة



الاعراب والانتهاء من التعليمات البرمجية



تفسير
, . 50% , , , , , — , . , , . . p.



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



:



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


. — . - , .


كود 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);


( ). matrix xp1.X, yp1.Y zp1.Z. , x, y z, A, B C .



, . unordered_set .



All Articles