Control Libraries 7.4.0
Loading...
Searching...
No Matches
Ellipsoid.cpp
1#include "state_representation/geometry/Ellipsoid.hpp"
2
3#include <eigen3/Eigen/Sparse>
4#include <random>
5
6#include "state_representation/exceptions/EmptyStateException.hpp"
7#include "state_representation/exceptions/IncompatibleSizeException.hpp"
8#include "state_representation/exceptions/NoSolutionToFitException.hpp"
9
10namespace state_representation {
11
12Ellipsoid::Ellipsoid() : Shape(), axis_lengths_({1., 1.}), rotation_angle_(0) {
13 this->set_type(StateType::GEOMETRY_ELLIPSOID);
14}
15
16Ellipsoid::Ellipsoid(const std::string& name, const std::string& reference_frame) :
17 Shape(name, reference_frame), axis_lengths_({1., 1.}), rotation_angle_(0) {
18 this->set_type(StateType::GEOMETRY_ELLIPSOID);
19}
20
21Ellipsoid::Ellipsoid(const Ellipsoid& ellipsoid) : Ellipsoid(ellipsoid.get_name()) {
22 if (ellipsoid) {
23 this->set_center_state(ellipsoid.get_center_state());
24 this->set_axis_lengths(ellipsoid.get_axis_lengths());
25 this->set_rotation_angle(ellipsoid.get_rotation_angle());
26 }
27}
28
29Ellipsoid Ellipsoid::Unit(const std::string& name, const std::string& reference_frame) {
30 Ellipsoid unit = Ellipsoid(name, reference_frame);
31 unit.set_empty(false);
32 return unit;
33}
34
36 Ellipsoid tmp(state);
37 swap(*this, tmp);
38 return *this;
39}
40
41const std::vector<double>& Ellipsoid::get_axis_lengths() const {
42 this->assert_not_empty();
43 return this->axis_lengths_;
44}
45
46double Ellipsoid::get_axis_length(unsigned int index) const {
47 this->assert_not_empty();
48 return this->axis_lengths_[index];
49}
50
52 this->assert_not_empty();
53 return this->rotation_angle_;
54}
55
57 this->assert_not_empty();
58 Eigen::Quaterniond rotation(Eigen::AngleAxisd(this->rotation_angle_, Eigen::Vector3d::UnitZ()));
59 return CartesianPose(
60 this->get_center_pose().get_name() + "_rotated", Eigen::Vector3d::Zero(), rotation,
61 this->get_center_pose().get_name());
62}
63
64void Ellipsoid::set_axis_lengths(const std::vector<double>& axis_lengths) {
65 this->axis_lengths_ = axis_lengths;
66 this->set_empty(false);
67}
68
69void Ellipsoid::set_axis_lengths(unsigned int index, double axis_length) {
70 this->axis_lengths_[index] = axis_length;
71 this->set_empty(false);
72}
73
74void Ellipsoid::set_rotation_angle(double rotation_angle) {
75 this->rotation_angle_ = rotation_angle;
76 this->set_empty(false);
77}
78
79void Ellipsoid::set_data(const Eigen::VectorXd& data) {
80 if (data.size() != 6) {
82 "Input is of incorrect size: expected 6, given " + std::to_string(data.size()));
83 }
84 this->set_center_position(data.head(3));
85 this->set_rotation_angle(data(3));
86 this->set_axis_lengths({data(4), data(5)});
87}
88
89void Ellipsoid::set_data(const std::vector<double>& data) {
90 this->set_data(Eigen::VectorXd::Map(data.data(), data.size()));
91}
92
94 const std::string& name, const std::list<CartesianPose>& points, const std::string& reference_frame,
95 double noise_level
96) {
97 // define the constraint matrix
98 Eigen::SparseMatrix<double> constraint_matrix(6, 6);
99 constraint_matrix.insert(1, 1) = -1;
100 constraint_matrix.insert(0, 2) = 2;
101 constraint_matrix.insert(2, 0) = 2;
102
103 // preprocessing normalize the data TODO using PCA to find the plan
104 size_t nb_points = points.size();
105 Eigen::VectorXd x_value(nb_points);
106 Eigen::VectorXd y_value(nb_points);
107
108 // retry with noise everytime delta > 0
109 double delta;
110 std::vector<double> coefficients(6);
111 Eigen::GeneralizedEigenSolver<Eigen::Matrix<double, 6, 6>> solver;
112
113 std::default_random_engine generator;
114 std::normal_distribution<double> dist(0., noise_level);
115 do {
116 unsigned int i = 0;
117 for (const auto& p: points) {
118 x_value[i] = p.get_position()(0) + dist(generator);
119 y_value[i] = p.get_position()(1) + dist(generator);
120 ++i;
121 }
122
123 double xm = x_value.mean();
124 double ym = y_value.mean();
125 double sx = 0.5 * (x_value.maxCoeff() - x_value.minCoeff());
126 double sy = 0.5 * (y_value.maxCoeff() - y_value.minCoeff());
127 Eigen::VectorXd x_value_centered = (x_value.array() - xm) / sx;
128 Eigen::VectorXd y_value_centered = (y_value.array() - ym) / sy;
129
130 // compute the design matrix
131 Eigen::MatrixXd design_matrix(nb_points, 6);
132 design_matrix.col(0) = x_value_centered.array() * x_value_centered.array();
133 design_matrix.col(1) = x_value_centered.array() * y_value_centered.array();
134 design_matrix.col(2) = y_value_centered.array() * y_value_centered.array();
135 design_matrix.col(3) = x_value_centered;
136 design_matrix.col(4) = y_value_centered;
137 design_matrix.col(5) = Eigen::VectorXd::Ones(nb_points);
138
139 // compute the scatter matrix
140 Eigen::Matrix<double, 6, 6> scatter_matrix = design_matrix.transpose() * design_matrix;
141
142 // solve the generalized eigenvalue problem
143 solver.compute(scatter_matrix, constraint_matrix);
144
145 // solution correspon to the single positive eigenvalue
146 unsigned int solution_index;
147 double eigenvalue = solver.betas().maxCoeff(&solution_index);
148
149 // no solution case
150 if (eigenvalue < 0) {
151 throw exceptions::NoSolutionToFitException("No solution found for the ellipse fitting");
152 }
153
154 // extract the solution
155 Eigen::VectorXd solution = solver.eigenvectors().col(solution_index).real();
156
157 // unnormalize the parameters TODO inverse PCA
158 double kx = xm;
159 double ky = ym;
160 double sx2 = sx * sx;
161 double sy2 = sy * sy;
162
163 coefficients[0] = solution(0) * sy2;
164 coefficients[1] = solution(1) * sx * sy;
165 coefficients[2] = solution(2) * sx2;
166 coefficients[3] = -2 * solution(0) * sy2 * kx - solution(1) * sx * sy * ky + solution(3) * sx * sy2;
167 coefficients[4] = -solution(1) * sx * sy * kx - 2 * solution(2) * sx2 * ky + solution(4) * sx2 * sy;
168 coefficients[5] = solution(0) * sy2 * kx * kx + solution(1) * sx * sy * kx * ky + solution(2) * sx2 * ky * ky
169 - solution(3) * sx * sy2 * kx - solution(4) * sx2 * sy * ky + solution(5) * sx2 * sy2;
170
171 delta = coefficients[1] * coefficients[1] - 4 * coefficients[0] * coefficients[2];
172 } while (delta > 0);
173 return from_algebraic_equation(name, coefficients, reference_frame);
174}
175
177 const std::string& name, const std::vector<double>& coefficients, const std::string& reference_frame
178) {
179 // extract all the components from the coefficients vector
180 double b2 = coefficients[1] * coefficients[1];
181 double delta = b2 - 4 * coefficients[0] * coefficients[2];
182
183 // store intermediate calculations
184 double tmp1 = coefficients[0] * coefficients[4] * coefficients[4] // AE2
185 + coefficients[2] * coefficients[3] * coefficients[3] // CD2
186 - coefficients[1] * coefficients[3] * coefficients[4] // BDE
187 + delta * coefficients[5]; // deltaF
188 double tmp2 = coefficients[2] - coefficients[0]; // C-A
189 double tmp3 = sqrt(tmp2 * tmp2 + b2);
190
191 // create the ellipsoid in the plan and set its center and axis
192 Ellipsoid result(name, reference_frame);
193 std::vector<double> axis_lengths(2);
194
195 // set axis lengths
196 double r1 = -sqrt(2 * tmp1 * (coefficients[0] + coefficients[2] + tmp3)) / delta;
197 double r2 = -sqrt(2 * tmp1 * (coefficients[0] + coefficients[2] - tmp3)) / delta;
198 axis_lengths[0] = (r1 >= r2) ? r1 : r2;
199 axis_lengths[1] = (r1 >= r2) ? r2 : r1;
200 result.set_axis_lengths(axis_lengths);
201
202 // set center position
203 double cx = (2 * coefficients[2] * coefficients[3] - coefficients[1] * coefficients[4]) / delta;
204 double cy = (2 * coefficients[0] * coefficients[4] - coefficients[1] * coefficients[3]) / delta;
205 result.set_center_position(Eigen::Vector3d(cx, cy, 0));
206
207 // set center orientation
208 double phi;
209 if (abs(coefficients[1]) > 1e-4) {
210 phi = atan2(tmp2 - tmp3, coefficients[1]);
211 } else if (coefficients[0] < coefficients[2]) {
212 phi = 0.;
213 } else {
214 phi = M_PI_2;
215 }
216 if (r1 < r2) { phi += M_PI_2; }
217 result.set_rotation_angle(phi);
218
219 return result;
220}
221
222const std::list<CartesianPose> Ellipsoid::sample_from_parameterization(unsigned int nb_samples) const {
223 if (this->is_empty()) {
224 throw exceptions::EmptyStateException(this->get_name() + " state is empty");
225 }
226 // use a linspace to have a full rotation angle between [0, 2pi]
227 std::vector<double> alpha = math_tools::linspace(0, 2 * M_PI, nb_samples);
228
229 std::list<CartesianPose> samples;
230 for (unsigned int i = 0; i < nb_samples; ++i) {
231 CartesianPose point(this->get_name() + "_point" + std::to_string(i), this->get_rotation().get_name());
232 double a = alpha.at(i);
233 Eigen::Vector3d position;
234 position(0) = this->get_axis_length(0) * cos(a);
235 position(1) = this->get_axis_length(1) * sin(a);
236 position(2) = 0;
237 point.set_position(position);
238 samples.push_back(this->get_center_pose() * this->get_rotation() * point);
239 }
240 return samples;
241}
242
243const std::vector<double> Ellipsoid::to_std_vector() const {
244 this->assert_not_empty();
245 std::vector<double> representation(6);
246 // position
247 representation[0] = this->get_center_position()(0);
248 representation[1] = this->get_center_position()(1);
249 representation[2] = this->get_center_position()(2);
250 // rotation angle
251 representation[3] = this->get_rotation_angle();
252 // axis lengths
253 representation[4] = this->get_axis_length(0);
254 representation[5] = this->get_axis_length(1);
255 return representation;
256}
257
258std::ostream& operator<<(std::ostream& os, const Ellipsoid& ellipsoid) {
259 os << ellipsoid.to_string();
260 if (ellipsoid.is_empty()) {
261 return os;
262 }
263 os << std::endl;
264 os << "axis lengths: [" << ellipsoid.get_axis_length(0) << ", " << ellipsoid.get_axis_length(1) << "]";
265 return os;
266}
267}// namespace state_representation
Class to define Cartesian pose in Cartesian space as 3D position and quaternion based orientation.
void set_position(const Eigen::Vector3d &position)
Setter of the position.
static Ellipsoid Unit(const std::string &name, const std::string &reference_frame="world")
Constructor for a Ellipsoid with identity state, unit axis lengths and zero rotation angle.
Definition Ellipsoid.cpp:29
void set_data(const Eigen::VectorXd &data) override
Set the ellipsoid data from an Eigen vector.
Definition Ellipsoid.cpp:79
Ellipsoid & operator=(const Ellipsoid &state)
Copy assignment operator that has to be defined to the custom assignment operator.
Definition Ellipsoid.cpp:35
friend void swap(Ellipsoid &state1, Ellipsoid &state2)
Swap the values of the two Ellipsoids.
void set_rotation_angle(double rotation_angle)
Setter of the rotation angle.
Definition Ellipsoid.cpp:74
double get_axis_length(unsigned int index) const
Getter of the axis length in one direction.
Definition Ellipsoid.cpp:46
static const Ellipsoid from_algebraic_equation(const std::string &name, const std::vector< double > &coefficients, const std::string &reference_frame="world")
Compute an Ellipsoid from its algebraic equation ax2 + bxy + cy2 + cx + ey + f.
const std::list< CartesianPose > sample_from_parameterization(unsigned int nb_samples) const
Function to sample an obstacle from its parameterization.
const CartesianPose get_rotation() const
Getter of the rotation as a pose.
Definition Ellipsoid.cpp:56
void set_axis_lengths(const std::vector< double > &axis_lengths)
Setter of the axis lengths.
Definition Ellipsoid.cpp:64
double get_rotation_angle() const
Getter of the rotation angle.
Definition Ellipsoid.cpp:51
const std::vector< double > to_std_vector() const
Convert the Ellipsoid to an std vector representation of its parameter.
Ellipsoid()
Empty constructor.
Definition Ellipsoid.cpp:12
const std::vector< double > & get_axis_lengths() const
Getter of the axis lengths.
Definition Ellipsoid.cpp:41
static const Ellipsoid fit(const std::string &name, const std::list< CartesianPose > &points, const std::string &reference_frame="world", double noise_level=0.01)
Fit an Ellipsoid on a set of points This method uses direct least square fitting from Fitzgibbon,...
Definition Ellipsoid.cpp:93
void set_center_state(const CartesianState &state)
Setter of the state.
Definition Shape.cpp:60
const Eigen::Vector3d get_center_position() const
Getter of the position from the state.
Definition Shape.cpp:45
const CartesianState & get_center_state() const
Getter of the state.
Definition Shape.cpp:35
const CartesianPose & get_center_pose() const
Getter of the pose from the state.
Definition Shape.cpp:40
void set_center_position(const Eigen::Vector3d &position)
Setter of the position.
Definition Shape.cpp:77
const std::string & get_name() const
Getter of the name attribute.
Definition State.cpp:29
void set_type(const StateType &type)
Setter of the state type attribute.
Definition State.cpp:41
void assert_not_empty() const
Throw an exception if the state is empty.
Definition State.cpp:55
bool is_empty() const
Getter of the empty attribute.
Definition State.cpp:33
void set_empty(bool empty=true)
Setter of the empty attribute.
Definition State.cpp:50
Core state variables and objects.
double dist(const CartesianState &s1, const CartesianState &s2, const CartesianStateVariable &state_variable_type=CartesianStateVariable::ALL)
Compute the distance between two Cartesian states.
std::ostream & operator<<(std::ostream &os, const AnalogIOState &state)