EnclosingEllipsoid.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include <cstddef>
6#include <iterator>
7#include <vector>
8
9#include <Eigen/Core>
10#include <Eigen/Geometry>
11
12#include <SimoxUtility/math/periodic/periodic_clamp.h>
13#include <VirtualRobot/math/Helpers.h>
14
16
18{
19
20 // Eigen::Affine2f Ellipsoid::pose() const noexcept
21 // {
22 // Eigen::Affine2f pose;
23 // pose.translation() = center;
24 // pose.linear() = Eigen::Rotation2Df(angle).toRotationMatrix();
25
26 // return pose;
27 // }
28
29 // TODO(fabian.reister): use Eigen built-in stuff.
30 size_t
31 argmin(const Eigen::VectorXf& v)
32 {
33 const std::vector<float> vec(v.data(), v.data() + v.rows() * v.cols());
34 return std::distance(std::begin(vec), std::max_element(vec.begin(), vec.end()));
35 }
36
37 // https://github.com/minillinim/ellipsoid/blob/master/ellipsoid.py
38
40 {
41 ARMARX_CHECK(compute(points));
42 }
43
44 bool
45 EnclosingEllipsoid::compute(const Points& points)
46 {
47 const float tolerance = 0.01;
48
49 const int N = static_cast<int>(points.size());
50 const int d = 2;
51
52 Eigen::MatrixXf P(N, d);
53
54 for (int i = 0; i < N; i++)
55 {
56 P.row(i) = points.at(i);
57 }
58
59 Eigen::MatrixXf Q(d + 1, N);
60 Q.topRows(d) = P.transpose();
61 Q.bottomRows(1).setOnes(); // last row
62
63 Eigen::MatrixXf Qt = Q.transpose();
64
65 float err = 1.F + tolerance;
66 Eigen::VectorXf u = (1.F / N) * Eigen::VectorXf::Ones(N);
67
68 // Khachiyan Algorithm
69 while (err > tolerance)
70 {
71 const Eigen::MatrixXf V = Q * (u.asDiagonal() * Qt);
72 const Eigen::VectorXf M = (Qt * (V.inverse() * Q)).diagonal();
73
74 const int j = static_cast<int>(argmin(M));
75 const float maximum = M(j);
76
77 const float stepSize = (maximum - d - 1.0) / ((d + 1.0) * (maximum - 1.0));
78
79 Eigen::VectorXf uNew = (1.0 - stepSize) * u;
80 uNew(j) += stepSize;
81
82 err = (uNew - u).norm();
83 u = uNew;
84 }
85
86 const Eigen::VectorXf center = P.transpose() * u;
87
88 Eigen::MatrixXf K(d, d);
89 for (int i = 0; i < d; i++)
90 {
91 for (int j = 0; j < d; j++)
92 {
93 K(i, j) = center(i) * center(j);
94 }
95 }
96
97 const Eigen::MatrixXf H = (P.transpose() * (u.asDiagonal() * P)) - K;
98 const Eigen::MatrixXf A = H.inverse() / d;
99
100 const Eigen::JacobiSVD svd(A, Eigen::ComputeThinV);
101
102 // angle
103 const Eigen::VectorXf v0 = svd.matrixV().col(0);
104 const float angle = math::Helpers::Angle(v0);
105
106 // radii
107 const Eigen::Vector2f radii = svd.singularValues().cwiseSqrt().cwiseInverse();
108
109 // As the singular values are sorted descending, the radii are sorted ascending.
110 // To fix this, the vector is reversed.
111 this->radii = radii.reverse();
112
113 // Thus, the angle does no longer match the radii. It has to be altered by 90°.
114 const float angle1 = simox::math::periodic_clamp(angle + M_PI_2, -M_PI, M_PI);
115
116 this->pose.setIdentity();
117 this->pose.translation().head<2>() = center;
118 this->pose.linear() =
119 Eigen::AngleAxisf(angle1, Eigen::Vector3f::UnitZ()).toRotationMatrix();
120
121 return true;
122 }
123
124} // namespace armarx::navigation::components::laser_scanner_feature_extraction
class A(deque< T, A >)) ARMARX_OVERLOAD_STD_HASH_FOR_ITERABLE((class T
Enables hashing of std::list.
#define M_PI
Definition MathTools.h:17
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
double norm(const Point &a)
Definition point.hpp:102
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109