ManagedObstacle.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package armar6_skills::ArmarXObjects::DynamicObstacleManager
17  * @author Fabian PK ( fabian dot peller-konrad at kit dot edu )
18  * @date 2020
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #include "ManagedObstacle.h"
24 
25 // STD/STL
26 #include <string>
27 #include <vector>
28 
29 // Ice
30 #include <Ice/Current.h>
31 using namespace Ice;
32 
33 // OpenCV
34 #include <opencv2/core.hpp>
35 
36 // Eigen
37 #include <Eigen/Core>
38 
39 // ArmarX
40 #include <opencv2/core/core_c.h>
41 
43 
44 #include <RobotAPI/interface/core/PoseBase.h>
46 
47 using namespace Eigen;
48 using namespace armarx;
49 
50 namespace armarx
51 {
52 
53  double
54  ManagedObstacle::calculateObstacleArea(const obstacledetection::Obstacle& o)
55  {
56  return M_PI * std::abs(o.axisLengthX) * std::abs(o.axisLengthY);
57  }
58 
59  bool
60  ManagedObstacle::point_ellipsis_coverage(const Eigen::Vector2f e_origin,
61  const float e_rx,
62  const float e_ry,
63  const float e_yaw,
64  const Eigen::Vector2f point)
65  {
66  // See https://stackoverflow.com/questions/7946187/point-and-ellipse-rotated-position-test-algorithm
67  const float sin = std::sin(e_yaw);
68  const float cos = std::cos(e_yaw);
69 
70  const float a = cos * (point(0) - e_origin(0)) + sin * (point(1) - e_origin(1));
71  const float b = sin * (point(0) - e_origin(0)) - cos * (point(1) - e_origin(1));
72 
73  const float e_rx_sq = e_rx * e_rx;
74  const float e_ry_sq = e_ry * e_ry;
75 
76  return a * a * e_ry_sq + b * b * e_rx_sq <= e_rx_sq * e_ry_sq;
77  }
78 
79  float
80  ManagedObstacle::ellipsis_ellipsis_coverage(const Eigen::Vector2f e1_origin,
81  const float e1_rx,
82  const float e1_ry,
83  const float e1_yaw,
84  const Eigen::Vector2f e2_origin,
85  const float e2_rx,
86  const float e2_ry,
87  const float e2_yaw)
88  {
89  // We use a very simple approach here: We sample points in one ellipsis and check whether it lies in the other (https://www.quora.com/Is-it-possible-to-generate-random-points-within-a-given-rotated-ellipse-without-using-rejection-sampling)
90  // For a real approach to intersect two ellipsis see https://arxiv.org/pdf/1106.3787.pdf
91 
92  unsigned int SAMPLES = 100;
93  unsigned int matches = 0;
94  for (unsigned int i = 0; i < SAMPLES; ++i)
95  {
96  float theta = static_cast<float>(rand()) / static_cast<float>(RAND_MAX / 2 * M_PI);
97  float k = sqrt(static_cast<float>(rand()) / static_cast<float>(RAND_MAX));
98  Eigen::Vector2f sample(k * e2_rx * cos(theta), k * e2_ry * sin(theta));
99  //sample *= Eigen::Rotation2D(e2_yaw);
100  sample += e1_origin;
101 
102  if (ManagedObstacle::point_ellipsis_coverage(e1_origin, e1_rx, e1_ry, e1_yaw, sample))
103  {
104  matches++;
105  }
106  }
107 
108  return (1.0f * matches) / SAMPLES;
109  }
110 
111  float
112  ManagedObstacle::line_segment_ellipsis_coverage(const Eigen::Vector2f e_origin,
113  const float e_rx,
114  const float e_ry,
115  const float e_yaw,
116  const Eigen::Vector2f line_seg_start,
117  const Eigen::Vector2f line_seg_end)
118  {
119  // Again we discretize the line into n points and we check the coverage of those points
120  const Eigen::Vector2f difference_vec = line_seg_end - line_seg_start;
121  const Eigen::Vector2f difference_vec_normed = difference_vec.normalized();
122  const float distance = difference_vec.norm();
123  const unsigned int SAMPLES = std::max(distance / 10.0, 40.0);
124 
125  const Vector2f difference_start_origin = e_origin - line_seg_start;
126  const Vector2f difference_end_origin = e_origin - line_seg_end;
127 
128  if (difference_start_origin.norm() > std::max(e_rx, e_ry) &&
129  distance < std::max(e_rx, e_ry))
130  {
131  return 0.0;
132  }
133 
134  if (difference_end_origin.norm() > std::max(e_rx, e_ry) && distance < std::max(e_rx, e_ry))
135  {
136  return 0.0;
137  }
138 
139 
140  const float step_size = distance / SAMPLES;
141  const Eigen::Vector2f dir = difference_vec_normed * step_size;
142 
143  unsigned int samples_in_ellipsis = 0;
144 
145  // Sample over line segment with a fixed size
146  for (unsigned int i = 0; i < SAMPLES * 0.5; ++i)
147  {
148  Eigen::Vector2f start_sample = line_seg_start + i * dir;
149  Eigen::Vector2f end_sample = line_seg_end - i * dir;
150  unsigned int samples_in_ellipsis_this_iteration = 0;
151 
152  for (const auto& point : {start_sample, end_sample})
153  {
154  if (ManagedObstacle::point_ellipsis_coverage(e_origin, e_rx, e_ry, e_yaw, point))
155  {
156  ++samples_in_ellipsis;
157  ++samples_in_ellipsis_this_iteration;
158  }
159  }
160 
161  if (samples_in_ellipsis_this_iteration == 2)
162  {
163  // Last two points (from the outside) were inside this ellipsis, so the
164  // remaining ones are as well simce ellipsis are concave. Consider this line
165  // segment as explained by the obstacle and bail early.
166  const unsigned int remaining_samples = SAMPLES - 2 * (i + 1);
167  return (1.0f * samples_in_ellipsis + remaining_samples) / SAMPLES;
168  }
169  }
170  return (1.0f * samples_in_ellipsis) / SAMPLES; // only if one point or no point in ellipsis
171  }
172 
173  void
174  ManagedObstacle::update_position(const unsigned int thickness)
175  {
176  if (line_matches.size() < 2)
177  {
178  return;
179  }
180 
181  ARMARX_DEBUG << "Obstacle " << m_obstacle.name << " has " << line_matches.size()
182  << " matching line segments since last update";
183 
184  std::vector<float> x_pos;
185  std::vector<float> y_pos;
186  for (Eigen::Vector2f& match : line_matches)
187  {
188  ARMARX_DEBUG << "Match: " << match;
189  x_pos.push_back(match(0));
190  y_pos.push_back(match(1));
191  }
192 
193  cv::Mat x(x_pos.size(), 1, CV_32F, x_pos.data());
194  cv::Mat y(y_pos.size(), 1, CV_32F, y_pos.data());
195 
196  ARMARX_DEBUG << "X: " << x;
197  ARMARX_DEBUG << "Y: " << y;
198 
199  cv::Mat data(x_pos.size(), 2, CV_32F);
200  x.col(0).copyTo(data.col(0));
201  y.col(0).copyTo(data.col(1));
202 
203  ARMARX_DEBUG << "Data: " << data;
204 
205  cv::PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, 2);
206 
207  cv::Mat mean = pca.mean;
208  ARMARX_DEBUG << "Mean: " << mean;
209  cv::Mat eigenvalues = pca.eigenvalues;
210  ARMARX_DEBUG << "Eigenvalues: " << eigenvalues;
211  cv::Mat eigenvectors = pca.eigenvectors;
212  ARMARX_DEBUG << "Eigenvectors: " << eigenvectors;
213 
214  Eigen::Vector2f pca_center(mean.at<float>(0, 0), mean.at<float>(0, 1));
215  Eigen::Vector2f pca1_direction(eigenvectors.at<float>(0, 0), eigenvectors.at<float>(0, 1));
216  Eigen::Vector2f pca2_direction(eigenvectors.at<float>(1, 0), eigenvectors.at<float>(1, 1));
217  double pca1_eigenvalue = eigenvalues.at<float>(0, 0);
218  double pca2_eigenvalue = eigenvalues.at<float>(1, 0);
219 
220  ARMARX_DEBUG << "PCA: Mean: " << pca_center;
221  ARMARX_DEBUG << "PCA1: Eigenvector: " << pca1_direction
222  << ", Eigenvalue: " << pca1_eigenvalue;
223  ARMARX_DEBUG << "PCA2: Eigenvector: " << pca2_direction
224  << ", Eigenvalue: " << pca2_eigenvalue;
225 
226  // calculate yaw of best line (should be close to current yaw)
227  const Eigen::Vector2f difference_vec = pca1_direction;
228  const Eigen::Vector2f center_vec = pca_center;
229  //const Eigen::Vector2f difference_vec1_normed = difference_vec.normalized();
230 
231  const float yaw1 = getXAxisAngle(difference_vec);
232  const float yaw2 = getXAxisAngle(-1 * difference_vec);
233 
234  const float diff_origin_yaw1 = std::abs(yaw1 - m_obstacle.yaw);
235  const float diff_origin_yaw2 = std::abs(yaw2 - m_obstacle.yaw);
236  const float diff_origin_yaw1_opposite = std::abs(2 * M_PI - diff_origin_yaw1);
237  const float diff_origin_yaw2_opposite = std::abs(2 * M_PI - diff_origin_yaw2);
238 
239 
240  float yaw = yaw2;
241  if ((diff_origin_yaw1 < diff_origin_yaw2 && diff_origin_yaw1 < diff_origin_yaw2_opposite) ||
242  (diff_origin_yaw1_opposite < diff_origin_yaw2 &&
243  diff_origin_yaw1_opposite < diff_origin_yaw2_opposite))
244  {
245  yaw = yaw1;
246  }
247 
248  // Print matches to debug drawer
249  //const std::string debug_line_name = "line_segment_input_" + std::to_string(m_input_index++);
250  //const armarx::Vector3BasePtr start_vec_3d{new Vector3(best_start(0), best_start(1), 50)};
251  //const armarx::Vector3BasePtr end_vec_3d{new Vector3(best_end(0), best_end(1), 50)};
252  //const armarx::Vector3BasePtr center_vec_3d{new Vector3(center_vec(0), center_vec(1), 50)};
253  //const armarx::Vector3BasePtr difference_vec_normed_3d{new Vector3(difference_vec1_normed(0), difference_vec1_normed(1), 0)};
254  //debug_drawer->setCylinderVisu(layer_name, debug_line_name, center_vec_3d, difference_vec_normed_3d, max_length, 15.f, armarx::DrawColor{1, 0, 0, 0.8});
255  //debug_drawer->setSphereVisu(layer_name, debug_line_name + "_ref", start_vec_3d, armarx::DrawColor{1, 1, 0, 0.8}, 35);
256  //debug_drawer->setSphereVisu(layer_name, debug_line_name + "_ref2", end_vec_3d, armarx::DrawColor{0, 1, 0, 0.8}, 35);
257 
258  // Udate position buffer with new best line
259  position_buffer.at(position_buffer_index++) =
260  std::make_tuple(center_vec(0),
261  center_vec(1),
262  yaw,
263  sqrt(pca1_eigenvalue) * 1.3,
264  std::max(1.0f * thickness,
265  static_cast<float>(sqrt(pca2_eigenvalue)) *
266  1.3f)); // add 30% security margin to length of pca
267  position_buffer_index %= position_buffer.size();
268 
269  position_buffer_fillctr++;
270  position_buffer_fillctr =
271  std::min(position_buffer_fillctr, static_cast<unsigned int>(position_buffer.size()));
272 
273  // Calculate means from position buffer
274  double sum_x_pos = 0;
275  double sum_y_pos = 0;
276  double sum_yaw = 0;
277  double sum_sin_yaw = 0;
278  double sum_cos_yaw = 0;
279  double sum_axisX = 0;
280  double sum_axisY = 0;
281 
282  std::string yaws;
283  for (unsigned int i = 0; i < position_buffer_fillctr; ++i)
284  {
285  double current_x_pos = std::get<0>(position_buffer[i]);
286  double current_y_pos = std::get<1>(position_buffer[i]);
287  double current_yaw = std::get<2>(position_buffer[i]);
288  double current_axisX = std::get<3>(position_buffer[i]);
289  double current_axisY = std::get<4>(position_buffer[i]);
290  sum_x_pos += current_x_pos;
291  sum_y_pos += current_y_pos;
292  sum_yaw += current_yaw;
293  sum_sin_yaw += sin(current_yaw);
294  sum_cos_yaw += cos(current_yaw);
295  sum_axisX += current_axisX;
296  sum_axisY += current_axisY;
297 
298  yaws += std::to_string(current_yaw) + ", ";
299  }
300 
301  double mean_x_pos = sum_x_pos / position_buffer_fillctr;
302  double mean_y_pos = sum_y_pos / position_buffer_fillctr;
303  double mean_yaw = atan2(
304  (1.0 / position_buffer_fillctr) * sum_sin_yaw,
305  (1.0 / position_buffer_fillctr) *
306  sum_cos_yaw); //sum_yaw / position_buffer_fillctr; //std::fmod(sum_yaw, 2.0 * M_PI);
307  double mean_axisX = sum_axisX / position_buffer_fillctr;
308  double mean_axisY = sum_axisY / position_buffer_fillctr;
309  mean_yaw = normalizeYaw(mean_yaw);
310 
311  // Update position and size of obstacle
312  m_obstacle.posX = mean_x_pos;
313  m_obstacle.posY = mean_y_pos;
314  m_obstacle.refPosX = mean_x_pos;
315  m_obstacle.refPosY = mean_y_pos;
316  m_obstacle.yaw = mean_yaw;
317  //if (mean_axisX > m_obstacle.axisLengthX)
318  //{
319  m_obstacle.axisLengthX = mean_axisX;
320  //}
321  //if (mean_axisY > m_obstacle.axisLengthY)
322  //{
323  m_obstacle.axisLengthY = mean_axisY;
324  //}
325 
326  line_matches.clear();
327  m_updated = true;
328  }
329 
330  float
331  ManagedObstacle::normalizeYaw(float yaw) const
332  {
333  float pi2 = 2.0 * M_PI;
334  while (yaw < 0)
335  {
336  yaw += pi2;
337  }
338  while (yaw > pi2)
339  {
340  yaw -= pi2;
341  }
342  return yaw;
343  }
344 
345  float
346  ManagedObstacle::getAngleBetweenVectors(const Eigen::Vector2f& v1,
347  const Eigen::Vector2f& v2) const
348  {
349  // Returns an angle between 0 and 180 degree (the minimum angle between the two vectors)
350  const Eigen::Vector2f v1_vec_normed = v1.normalized();
351  const Eigen::Vector2f v2_vec_normed = v2.normalized();
352  const float dot_product_vec =
353  v1_vec_normed(0) * v2_vec_normed(0) + v1_vec_normed(1) * v2_vec_normed(1);
354  float yaw = acos(dot_product_vec);
355  return yaw;
356  }
357 
358  float
359  ManagedObstacle::getXAxisAngle(const Eigen::Vector2f& v) const
360  {
361  // Returns an angle between 0 and 360 degree (counterclockwise from x axis)
362  const Eigen::Vector2f v_vec_normed = v.normalized();
363  const float dot_product_vec = v_vec_normed(0);
364  const float cross_product_vec = v_vec_normed(1);
365  float yaw = acos(dot_product_vec);
366  if (cross_product_vec < 0)
367  {
368  yaw = 2 * M_PI - yaw;
369  }
370  return yaw;
371  }
372 } // namespace armarx
Eigen
Definition: Elements.h:32
armarx::navigation::algorithm::getAngleBetweenVectors
float getAngleBetweenVectors(const Eigen::Vector2f &v1, const Eigen::Vector2f &v2)
Definition: CircularPathSmoothing.cpp:28
Pose.h
ManagedObstacle.h
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:281
armarx::mean
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
Definition: KinematicUnitGuiPlugin.cpp:1620
M_PI
#define M_PI
Definition: MathTools.h:17
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:41
GfxTL::sqrt
VectorXD< D, T > sqrt(const VectorXD< D, T > &a)
Definition: VectorXD.h:704
Ice
Definition: DBTypes.cpp:63
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
armarx::skills::gui::matches
bool matches(std::string skillName, std::vector< std::string > &searches)
Definition: SkillManagerWrapper.cpp:66
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
Logging.h
min
T min(T t1, T t2)
Definition: gdiam.h:44
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27