Решатель Ceres дает неверные результаты для трилатерации

Я пытаюсь использовать решатель Google ceres (http://ceres-solver.org/) для вычисления нелинейная трилатерация методом наименьших квадратов (цель — позиционирование в помещении с помощью маяков BLE). Моя проблема в том, что CERES дает результаты со значительной ошибкой, и, сравнивая другое решение, которое также использует алгоритм Левенберга-Марквардта, очевидно, что что-то не так с моей настройкой ceres.

Моя отправная точка такова: https://nrr.mit.edu/sites/default/files/documents/Lab_11_Localization.html B часть документа. Сначала библиотекой, которую я использовал, был этот проект JAVA: https://github.com/lemmingapex/Trilateration который использует вышеупомянутый алгоритм LM для решения проблем трилатерации. Производные и матрицы Якоби предварительно вычисляются/закодированы (я (пока) не понимаю их, чтобы двигаться вперед быстро, к сожалению, мне пришлось пропустить более глубокое понимание). С CERES (мой первый раз, когда я его использую) AutoDiffCostFunction показался мне действительно простым способом определить проблему, которая выглядит следующим образом:

Coordinate TrilaterationCalculator::ComputePosition2D(
    const std::list<std::pair<Coordinate, double>> &measurementPoints) {
  Problem problem;
  double x = 1.0;
  double y = 1.0;

  for (const auto &measurementPoint : measurementPoints) {
    problem.AddResidualBlock(new AutoDiffCostFunction<BeaconCostFunctor2D, 1, 1, 1>(new BeaconCostFunctor2D(measurementPoint)), nullptr, &x, &y);
  }

  Solver::Options options;
  options.minimizer_progress_to_stdout = false;
  options.minimizer_type = ceres::TRUST_REGION;
  // options.minimizer_type = ceres::LINE_SEARCH; // TODO
  //options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
  options.linear_solver_type = ceres::DENSE_QR;
  options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
  options.logging_type = ceres::SILENT;
  options.minimizer_progress_to_stdout = false;

  options.function_tolerance = 1e-12;
  options.gradient_tolerance = 1e-12;
  options.parameter_tolerance = 1e-12;
  options.max_num_iterations = 1000;
  //options.max_solver_time_in_seconds = 1;
  //options.num_threads = 4;

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

  auto result = Coordinate(x, y, 1.4); //TODO proper handling of z coordinate...
  return result;
}

struct BeaconCostFunctor2D {

public:
  BeaconCostFunctor2D(const std::pair<Coordinate, double> &measurementPoint_)
      : measurementPoint(measurementPoint_) {}

  template <typename T>
  bool operator()(const T *const x, const T *const y, T *residual) const {
    //r^2-(x-x1)^2-(y-y1)^2 
    residual[0] = pow(measurementPoint.second, 2) - pow(x[0]-measurementPoint.first.getX(), 2) - pow(y[0]-measurementPoint.first.getY(), 2);
    return true;
  }

private:
  const std::pair<Coordinate, double> &measurementPoint;
};

Сравнение некоторых примеров из решения 2:

Входные данные (из кода JAVA, С++ имеет те же значения, но короче; числа указаны в миллиметрах):

double[][] positions = new double[][]{{-24223,26072}, {-13446,16859}, {-20860,15693}, {-21019,25807}, {-17037,21467}, {-11449,15837}, {-3980,24447}, {-16639,15693}, {-21019,17803}, {-4439,21155}, {-12503,20343}, {-16878,24891}, {-24364,22343}, {-20979,21985}, {-17157,17883}, {-7836,16369}, {-12498,24971}, {-160,24931}, {-8860,24514}, {-8825,21002}, {-8769,18404}, };
double[] distances = new double[]{0.001,7874.97,4182.28,0.001,4382.07,3027.21,4380.63,5801.38,3222.07,6158.16,2676.96,2984.05,0.001,2388.27,3359.42,4153.79,2105.41,6676.31,2981.94,2385.64,2417.16,};
 double[] expectedPosition = new double[]{-24706.0, 26754.0};

Решение библиотеки трилатерации Java: -23085,6 24505,1 (близко к ожидаемой позиции)

Решение Цереры: -13891.2, 22133.1 (далеко)

(Также сравнил эти 2 теста с другими тестами, во многих случаях оба дают одинаковые (хорошие) результаты. Но эти данные из реальной жизни, похоже, сбивают с толку Цереру и дают неверные результаты.)

Я могу думать о 3 возможных вещах, в которых заключается проблема:

1) ceres's automatic differentiation is not working properly (less likely I guess)

2) my problem setup in Ceres is wrong (most likely)

3) (something very stupid coding mistake somewhere?)

Не могли бы вы помочь мне с тем, что мне не хватает? (Мы перешли на C++ из-за технических требований, поэтому нам нужно заменить эту уже работающую версию нелинейного трилатериона в JAVA)

Кстати, разве это решение не вычисляет производные каждый раз, когда вызывается этот расчет трилатерации? Таким образом, это приводит к значительной задержке по сравнению с тем, если бы я не использовал autocostfunctor, верно?

Спасибо за любое понимание! (Я пытался присоединиться к группам ceres google, но еще не был одобрен, поэтому задал вопрос здесь, так как были также некоторые вопросы, связанные с решателем ceres)


person bartfer    schedule 27.11.2020    source источник
comment
Как только вы правильно рассчитаете математику, будьте осторожны, чтобы протестировать в очень маленьком пространстве, предпочтительно на открытом воздухе с небольшим количеством препятствий для отражения или препятствий. В противном случае вы не узнаете, является ли проблема математикой или сигнальным шумом. Мой опыт показывает, что трилатерация на основе RSSI маяка BLE дает очень большие ошибки в оценках местоположения, если маяки расположены на расстоянии более 2 метров от приемника или если есть препятствия, вызывающие отражение или затухание сигнала. (Даже человеческое тело может сделать это, поэтому для достижения наилучших результатов поместите приемник на пластиковый штатив в пределах 2 метров от всех передатчиков.)   -  person davidgyoung    schedule 27.11.2020
comment
Я прочитал много ваших постов/комментариев о позиционировании маяков, которые были действительно полезны, так что спасибо и за них! Моя проблема с этим подходом заключается в том, что вам придется проверять и адаптировать систему позиционирования для каждого нового местоположения - аналогично снятию отпечатков пальцев. В этом случае любой новый источник сигнального шума может сбить нас с толку. Вот почему я подхожу к этому более широко. Возможно, таким образом я не буду опускаться ниже определенной точности. Если это неприемлемо, мне придется приспосабливаться к каждому новому месту. Я могу проверить правильность математики, посмотрев на (правильный) ввод/вывод.   -  person bartfer    schedule 01.12.2020


Ответы (1)


ceres оптимизируют сумму квадратов остатков. Вместо этого вы предоставляете само значение в квадрате, таким образом, вы оптимизируете 4-ю степень расстояний.

residual[0] = measurementPoint.second- sqrt(pow(x[0]-measurementPoint.first.getX(), 2) - pow(y[0]-measurementPoint.first.getY(), 2)); должно работать

person Solon    schedule 22.12.2020
comment
Спасибо за Ваш ответ! Да, вы правы в этом, но это все еще не дает такого хорошего результата, как JAVA lib. Так как - я попробовал собственную библиотеку и использовал это уравнение 1 - sqrt (pow (x - x0, 2) + pow (y - y0, 2)) / Distance; и это дает даже лучший результат, чем библиотека JAVA, но в основном то же самое. Я не понимаю, чем эта форма уравнения отличается от такой: sqrt(pow(x - x0, 2) + pow(y - y0, 2)) - расстояние; Я спрошу и попытаюсь очистить это на математическом стеке и вернусь с объяснением. Конечно, пока, если кто знает, пожалуйста, ответьте. - person bartfer; 04.01.2021