DynamicObstacleManager.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
24
25#include <VirtualRobot/MathTools.h>
26
27// STD/STL
28#include <cmath>
29#include <limits>
30#include <string>
31#include <vector>
32
33// Ice
34#include <Ice/Current.h>
35using namespace Ice;
36
37// Eigen
38#include <Eigen/Core>
39using namespace Eigen;
40
41// Simox
42#include <VirtualRobot/Nodes/RobotNode.h>
43#include <VirtualRobot/XML/RobotIO.h>
44
45// ArmarX
48
49#include <RobotAPI/interface/core/PoseBase.h>
51
53
54
55using namespace armarx;
56
57
58const std::string armarx::DynamicObstacleManager::default_name = "DynamicObstacleManager";
59
60namespace armarx
61{
62
64 m_obstacle_index(0),
65 m_decay_after_ms(5000),
66 m_periodic_task_interval(500),
67 m_min_value_for_accepting(1000),
68 m_min_coverage_of_obstacles(0.7f),
69 m_min_coverage_of_line_samples_in_obstacle(0.9f),
70 m_min_size_of_obstacles(100),
71 m_min_length_of_lines(50),
72 m_max_size_of_obstacles(600),
73 m_max_length_of_lines(10000),
74 m_thickness_of_lines(200),
75 m_security_margin_for_obstacles(500),
76 m_security_margin_for_lines(400),
77 m_remove_enabled(false),
78 m_only_visualize(false),
79 m_allow_spwan_inside(false)
80 {
81 }
82
83 std::string
88
89 void
93
94 void
96 {
97 m_decay_factor = m_periodic_task_interval;
98 m_access_bonus = m_periodic_task_interval;
99
100 m_update_obstacles = new PeriodicTask<DynamicObstacleManager>(
101 this, &DynamicObstacleManager::update_decayable_obstacles, m_periodic_task_interval);
102 m_update_obstacles->start();
103 }
104
105 void
107 {
108 m_update_obstacles->stop();
109 }
110
111 void
115
116 void
117 DynamicObstacleManager::add_decayable_obstacle(const Eigen::Vector2f& e_origin,
118 float e_rx,
119 float e_ry,
120 float e_yaw,
121 const Ice::Current&)
122 {
123 // TODO
124 }
125
126 void
127 DynamicObstacleManager::add_decayable_line_segment(const Eigen::Vector2f& line_start,
128 const Eigen::Vector2f& line_end,
129 const Ice::Current&)
130 {
132 const Eigen::Vector2f difference_vec = line_end - line_start;
133 const float length = difference_vec.norm();
134 const Eigen::Vector2f center_vec = line_start + 0.5 * difference_vec;
135 const Eigen::Vector2f difference_vec_normed = difference_vec.normalized();
136 const float dot_product = difference_vec_normed(0);
137 const float cross_product = difference_vec_normed(1);
138 float yaw = acos(dot_product);
139
140 if (cross_product < 0)
141 {
142 yaw = 2 * M_PI - yaw;
143 }
144
145 if (length < m_min_length_of_lines)
146 {
147 return;
148 }
149
150 {
151 std::shared_lock<std::shared_mutex> l(m_managed_obstacles_mutex);
152
153 TIMING_START(add_decayable_line_segment_mutex);
154 // First check own obstacles
155 for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
156 {
157
158 std::lock_guard<std::shared_mutex> l(managed_obstacle->m_mutex);
160 {managed_obstacle->m_obstacle.posX, managed_obstacle->m_obstacle.posY},
161 managed_obstacle->m_obstacle.axisLengthX,
162 managed_obstacle->m_obstacle.axisLengthY,
163 managed_obstacle->m_obstacle.yaw,
164 line_start,
165 line_end);
166 //ARMARX_DEBUG << "The coverage of the new line to obstacle " << managed_obstacle.obstacle.name << "(" << ManagedObstacle::calculateObstacleArea(managed_obstacle.obstacle) << ") is " << coverage;
167 if (coverage >= m_min_coverage_of_line_samples_in_obstacle)
168 {
169 // Obstacle already in list. Increase counter and update current obstacle.
170 managed_obstacle->line_matches.push_back(line_start);
171 managed_obstacle->line_matches.push_back(line_end);
172 managed_obstacle->m_last_matched = IceUtil::Time::now();
173 TIMING_END(add_decayable_line_segment_mutex);
174 return;
175 }
176 }
177 TIMING_END(add_decayable_line_segment_mutex);
178 }
179
180 // Second check the obstacles from the obstacle avoidance
181 /*for (const auto& published_obstacle : m_obstacle_detection->getObstacles())
182 {
183 float coverage = ManagedObstacle::line_segment_ellipsis_coverage(
184 {published_obstacle.posX, published_obstacle.posY}, published_obstacle.axisLengthX, published_obstacle.axisLengthY, published_obstacle.yaw,
185 line_start, line_end);
186 if (coverage >= m_min_coverage_of_line_samples_in_obstacle)
187 {
188 // If own managed obstacle we already updatet its value. If its a static obstacle from the obstacle avoidance, then we can't update the position anyway...
189 ARMARX_DEBUG << "Found the obstacle in the static obstacle list! Matching name: " << published_obstacle.name;
190 return;
191 }
192 }*/
193
194 ARMARX_DEBUG << " No matching obstacle found. Create new obstacle and add to list";
195 ManagedObstaclePtr new_managed_obstacle(new ManagedObstacle());
196 new_managed_obstacle->m_obstacle.name =
197 "managed_obstacle_" + std::to_string(m_obstacle_index++);
198 new_managed_obstacle->m_obstacle.posX = center_vec(0);
199 new_managed_obstacle->m_obstacle.posY = center_vec(1);
200 new_managed_obstacle->m_obstacle.refPosX = center_vec(0);
201 new_managed_obstacle->m_obstacle.refPosY = center_vec(1);
202 new_managed_obstacle->m_obstacle.yaw = yaw;
203 new_managed_obstacle->m_obstacle.axisLengthX =
204 std::clamp(length * 0.5f,
205 static_cast<float>(m_min_length_of_lines),
206 static_cast<float>(m_max_length_of_lines));
207 new_managed_obstacle->m_obstacle.axisLengthY = m_thickness_of_lines;
208 new_managed_obstacle->m_obstacle.safetyMarginX = m_security_margin_for_lines;
209 new_managed_obstacle->m_obstacle.safetyMarginY = m_security_margin_for_lines;
210 new_managed_obstacle->m_last_matched = IceUtil::Time::now();
211 new_managed_obstacle->m_published = false;
212 new_managed_obstacle->m_value = m_min_value_for_accepting * 0.5;
213 new_managed_obstacle->position_buffer_index = 0;
214 new_managed_obstacle->position_buffer_fillctr = 0;
215 new_managed_obstacle->line_matches.push_back(line_start);
216 new_managed_obstacle->line_matches.push_back(line_end);
217
218 {
219 std::lock_guard<std::shared_mutex> l(m_managed_obstacles_mutex);
220 m_managed_obstacles.push_back(new_managed_obstacle);
221 }
223 }
224
225 void
227 const dynamicobstaclemanager::LineSegments& lines,
228 const Ice::Current& c)
229 {
230 for (const auto& line : lines)
231 {
232 add_decayable_line_segment(line.lineStart, line.lineEnd, c);
233 }
234 }
235
236 void
238 {
239 std::lock_guard l(m_managed_obstacles_mutex);
240
241 ARMARX_DEBUG << "Remove all obstacles from obstacle map by setting their value to -inf";
242 for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
243 {
244 managed_obstacle->m_value = std::numeric_limits<double>::min();
245 managed_obstacle->m_updated = true;
246 }
247 }
248
249 void
250 DynamicObstacleManager::remove_obstacle(const std::string& name, const Ice::Current&)
251 {
252 std::lock_guard l(m_managed_obstacles_mutex);
253
254 ARMARX_DEBUG << "Remove Obstacle " << name
255 << " from obstacle map and from obstacledetection";
256 for (ManagedObstaclePtr managed_obstacle : m_managed_obstacles)
257 {
258 if (managed_obstacle->m_obstacle.name == name)
259 {
260 managed_obstacle->m_value = std::numeric_limits<double>::min();
261 managed_obstacle->m_updated = true;
262 managed_obstacle->m_published = false;
263 break;
264 }
265 }
266 m_obstacle_detection->removeObstacle(name);
267 m_obstacle_detection->updateEnvironment();
268 }
269
270 void
272 {
273 ARMARX_INFO << "Waiting for obstacles to get ready";
274 usleep(2.0 * m_min_value_for_accepting);
275 ARMARX_INFO << "Finished waiting for obstacles";
276 }
277
278 float
279 DynamicObstacleManager::distanceToObstacle(const Eigen::Vector2f& agentPosition,
280 float safetyRadius,
281 const Eigen::Vector2f& goal,
282 const Ice::Current&)
283 {
284 std::shared_lock<std::shared_mutex> l{m_managed_obstacles_mutex};
285
286 const Eigen::Vector2f diff = goal - agentPosition;
287 const Eigen::Vector2f orthogonal_normalized =
288 Eigen::Vector2f(Eigen::Rotation2Df(M_PI_2) * diff).normalized();
289
290 const float sample_step = 5; // in [mm], sample step size towards goal.
291 const float distance_to_goal = diff.norm() + safetyRadius;
292
293 float current_distance = safetyRadius;
294
295 while (current_distance < distance_to_goal)
296 {
297 for (const auto& man_obstacle : m_managed_obstacles)
298 {
299 Eigen::Vector2f sample =
300 agentPosition + ((goal - agentPosition).normalized() * current_distance);
301 Eigen::Vector2f sample_left = sample + (orthogonal_normalized * 250);
302 Eigen::Vector2f sample_right = sample - (orthogonal_normalized * 250);
303
304 obstacledetection::Obstacle obstacle = man_obstacle->m_obstacle;
305 Eigen::Vector2f obstacle_origin{obstacle.posX, obstacle.posY};
306
308 obstacle.axisLengthX,
309 obstacle.axisLengthY,
310 obstacle.yaw,
311 sample))
312 {
313 return current_distance - safetyRadius;
314 }
315
317 obstacle.axisLengthX,
318 obstacle.axisLengthY,
319 obstacle.yaw,
320 sample_left))
321 {
322 return current_distance - safetyRadius;
323 }
324
326 obstacle.axisLengthX,
327 obstacle.axisLengthY,
328 obstacle.yaw,
329 sample_right))
330 {
331 return current_distance - safetyRadius;
332 }
333 }
334
335 current_distance += sample_step;
336 }
337
338 return std::numeric_limits<float>::infinity();
339 }
340
341 void
343 const Eigen::Vector2f& obs_pos,
344 float e_rx,
345 float e_ry,
346 float e_yaw,
347 const Ice::Current&)
348 {
349 obstacledetection::Obstacle new_unmanaged_obstacle;
350 new_unmanaged_obstacle.name = name;
351 new_unmanaged_obstacle.posX = obs_pos(0);
352 new_unmanaged_obstacle.posY = obs_pos(1);
353 new_unmanaged_obstacle.refPosX = obs_pos(0);
354 new_unmanaged_obstacle.refPosY = obs_pos(1);
355 new_unmanaged_obstacle.yaw = e_yaw;
356 new_unmanaged_obstacle.axisLengthX =
357 std::clamp(e_rx,
358 static_cast<float>(m_min_size_of_obstacles),
359 static_cast<float>(m_max_size_of_obstacles));
360 new_unmanaged_obstacle.axisLengthY =
361 std::clamp(e_ry,
362 static_cast<float>(m_min_size_of_obstacles),
363 static_cast<float>(m_max_size_of_obstacles));
364 new_unmanaged_obstacle.safetyMarginX = m_security_margin_for_obstacles;
365 new_unmanaged_obstacle.safetyMarginY = m_security_margin_for_obstacles;
366 m_obstacle_detection->updateObstacle(new_unmanaged_obstacle);
367 m_obstacle_detection->updateEnvironment();
368 }
369
370 void
371 DynamicObstacleManager::update_decayable_obstacles()
372 {
373 ARMARX_DEBUG << "update obstacles";
374 IceUtil::Time started = IceUtil::Time::now();
375 std::vector<std::string> remove_obstacles;
376
377 // Some debug definitions
378 viz::Layer obstacleLayer = arviz.layer(m_obstacle_manager_layer_name);
379
380 // Update positions
381 {
382 ARMARX_DEBUG << "update obstacle position";
383 std::shared_lock<std::shared_mutex> l(m_managed_obstacles_mutex);
384 if (!m_managed_obstacles.size())
385 {
386 return;
387 }
388
389 for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
390 {
391 std::lock_guard<std::shared_mutex> l(managed_obstacle->m_mutex);
392 managed_obstacle->update_position(m_thickness_of_lines);
393 }
394 }
395
396 // Update obstacle avoidance
397 int checked_obstacles = 0;
398 bool updated = false;
399 int published_obstacles = 0;
400 int updated_obstacles = 0;
401 {
402 ARMARX_DEBUG << "update obstacle values and publish";
403 std::lock_guard l(m_managed_obstacles_mutex);
404 std::sort(m_managed_obstacles.begin(),
405 m_managed_obstacles.end(),
407
408 checked_obstacles = m_managed_obstacles.size();
409
410 for (ManagedObstaclePtr& managed_obstacle : m_managed_obstacles)
411 {
412 std::lock_guard<std::shared_mutex> l(managed_obstacle->m_mutex);
413 if (!managed_obstacle->m_updated)
414 {
415 managed_obstacle->m_value =
416 std::max(-1.0f, managed_obstacle->m_value - m_decay_factor);
417 }
418 else
419 {
420 managed_obstacle->m_value = std::min(
421 1.0f * m_decay_after_ms, managed_obstacle->m_value + m_access_bonus);
422 }
423
424 if (managed_obstacle->m_published)
425 {
426 if (managed_obstacle->m_value < m_min_value_for_accepting)
427 {
428 updated_obstacles++;
429 managed_obstacle->m_published = false;
430 if (!m_only_visualize)
431 {
432 m_obstacle_detection->removeObstacle(managed_obstacle->m_obstacle.name);
433 updated = true;
434 }
435 }
436 else
437 {
438 published_obstacles++;
439 if (managed_obstacle->m_updated)
440 {
441 updated_obstacles++;
442 if (!m_only_visualize)
443 {
444 m_obstacle_detection->updateObstacle(managed_obstacle->m_obstacle);
445 updated = true;
446 }
447 //visualize_obstacle(obstacleLayer, managed_obstacle, armarx::DrawColor{0, 1, 1, std::min(0.9f, managed_obstacle->m_value / m_decay_after_ms)}, 50, false);
448 }
449 else
450 {
451 //visualize_obstacle(obstacleLayer, managed_obstacle, armarx::DrawColor{0, 1, 0, std::min(0.9f, managed_obstacle->m_value / m_decay_after_ms)}, 50, false);
452 }
453 }
454 }
455 else // if (!managed_obstacle.published)
456 {
457 if (managed_obstacle->m_value >= m_min_value_for_accepting)
458 {
459 published_obstacles++;
460 updated_obstacles++;
461 managed_obstacle->m_published = true;
462 if (!m_only_visualize)
463 {
464 m_obstacle_detection->updateObstacle(managed_obstacle->m_obstacle);
465 updated = true;
466 }
467 //visualize_obstacle(obstacleLayer, managed_obstacle, armarx::DrawColor{0, 1, 0, std::min(0.9f, managed_obstacle->m_value / m_decay_after_ms)}, 50, false);
468 }
469 else if (managed_obstacle->m_value < 0 &&
470 m_remove_enabled) // Completely forget the obstacle
471 {
472 remove_obstacles.push_back(managed_obstacle->m_obstacle.name);
473 }
474 else if (managed_obstacle->m_updated)
475 {
476 visualize_obstacle(
477 obstacleLayer,
478 managed_obstacle,
479 armarx::DrawColor{
480 1,
481 1,
482 1,
483 std::min(0.9f,
484 managed_obstacle->m_value / m_min_value_for_accepting)},
485 40,
486 true);
487 }
488 else
489 {
490 visualize_obstacle(
491 obstacleLayer,
492 managed_obstacle,
493 armarx::DrawColor{
494 1,
495 1,
496 1,
497 std::min(0.9f,
498 managed_obstacle->m_value / m_min_value_for_accepting)},
499 40,
500 true);
501 }
502 }
503 managed_obstacle->m_updated = false;
504 }
505 }
506
507 if (updated)
508 {
509 ARMARX_DEBUG << "update environment";
510 m_obstacle_detection->updateEnvironment();
511 }
512 arviz.commit({obstacleLayer});
513
514 if (!remove_obstacles.empty())
515 {
516 std::lock_guard<std::shared_mutex> l(m_managed_obstacles_mutex);
517 for (const auto& name : remove_obstacles)
518 {
519 // TODO schöner machen ohne loop. Iteratoren in main loop
520 m_managed_obstacles.erase(std::remove_if(m_managed_obstacles.begin(),
521 m_managed_obstacles.end(),
522 [&](const ManagedObstaclePtr& oc)
523 { return oc->m_obstacle.name == name; }),
524 m_managed_obstacles.end());
525 }
526 }
527
528 ARMARX_DEBUG << "Finished updating the " << checked_obstacles
529 << " managed obstacles (removed: " << remove_obstacles.size()
530 << ", updated: " << updated_obstacles << "). " << published_obstacles
531 << " of them should be published. The whole operation took "
532 << (IceUtil::Time::now() - started).toMilliSeconds() << "ms";
533 }
534
535 void
536 DynamicObstacleManager::visualize_obstacle(viz::Layer& layer,
537 const ManagedObstaclePtr& o,
538 const armarx::DrawColor& color,
539 double pos_z,
540 bool text)
541 {
542 // Visualize ellipses.m_obstacle_manager_layer_name
543 Eigen::Vector3f dim(o->m_obstacle.axisLengthX * 2,
544 o->m_obstacle.axisLengthY * 2,
545 o->m_obstacle.axisLengthZ * 2);
546
547 const std::string name = o->m_obstacle.name;
548
549 layer.add(viz::Box(name)
550 .position(Eigen::Vector3f(o->m_obstacle.posX, o->m_obstacle.posY, pos_z))
551 .orientation(Eigen::AngleAxisf(o->m_obstacle.yaw, Eigen::Vector3f::UnitZ())
552 .toRotationMatrix())
553 .size(dim)
554 .color(color.r, color.g, color.b, color.a));
555 }
556
559 {
561
562 defs->component(m_obstacle_detection,
563 "PlatformObstacleAvoidance",
564 "ObstacleAvoidanceName",
565 "The name of the used obstacle avoidance interface");
566
567 defs->optional(m_min_coverage_of_obstacles,
568 "MinSampleRatioPerEllipsis",
569 "Minimum percentage of samples which have to be in an elllipsis to be "
570 "considered as known obsacle");
571 defs->optional(m_min_coverage_of_line_samples_in_obstacle,
572 "MinSampleRatioPerLineSegment",
573 "Minimum percentage of samples which have to be in an elllipsis to be "
574 "considered as known obsacle");
575 defs->optional(
576 m_min_size_of_obstacles, "MinObstacleSize", "The minimal obstacle size in mm.");
577 defs->optional(
578 m_max_size_of_obstacles, "MaxObstacleSize", "The maximal obstacle size in mm.");
579 defs->optional(m_min_length_of_lines, "MinLengthOfLines", "Minimum length of lines in mm");
580 defs->optional(m_max_length_of_lines, "MaxLengthOfLines", "Maximum length of lines in mm");
581 defs->optional(m_decay_after_ms, "MaxObstacleValue", "Maximum value for the obstacles");
582 defs->optional(m_min_value_for_accepting,
583 "MinObstacleValueForAccepting",
584 "Minimum value for the obstacles to get accepted");
585 defs->optional(
586 m_periodic_task_interval, "UpdateInterval", "The interval to check the obstacles");
587 defs->optional(m_thickness_of_lines, "LineThickness", "The thickness of line obstacles");
588 defs->optional(m_security_margin_for_obstacles,
589 "ObstacleSecurityMargin",
590 "Security margin of ellipsis obstacles");
591 defs->optional(
592 m_security_margin_for_lines, "LineSecurityMargin", "Security margin of line obstacles");
593 defs->optional(m_remove_enabled, "EnableRemove", "Delete Obstacles when value < 0");
594 defs->optional(
595 m_only_visualize, "OnlyVisualizeObstacles", "Connection to obstacle avoidance");
596 defs->optional(m_allow_spwan_inside,
597 "AllowInRobotSpawning",
598 "Allow obstacles to spawn inside the robot");
599
600 return defs;
601 }
602
604} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define M_PI
Definition MathTools.h:17
constexpr T c
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
void remove_all_decayable_obstacles(const Ice::Current &=Ice::Current()) override
void onInitComponent() override
Pure virtual hook for the subclass.
static const std::string default_name
void add_decayable_line_segment(const Eigen::Vector2f &, const Eigen::Vector2f &, const Ice::Current &=Ice::Current()) override
void remove_obstacle(const std::string &name, const Ice::Current &=Ice::Current()) override
void add_decayable_obstacle(const Eigen::Vector2f &, float, float, float, const Ice::Current &=Ice::Current()) override
float distanceToObstacle(const Eigen::Vector2f &agentPosition, float safetyRadius, const Eigen::Vector2f &goal, const Ice::Current &=Ice::Current()) override
void onDisconnectComponent() override
Hook for subclass.
void wait_unitl_obstacles_are_ready(const Ice::Current &=Ice::Current()) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void add_decayable_line_segments(const dynamicobstaclemanager::LineSegments &lines, const Ice::Current &=Ice::Current()) override
void onConnectComponent() override
Pure virtual hook for the subclass.
void directly_update_obstacle(const std::string &name, const Eigen::Vector2f &, float, float, float, const Ice::Current &=Ice::Current()) override
void onExitComponent() override
Hook for subclass.
std::string getDefaultName() const override
Retrieve default name of component.
static float line_segment_ellipsis_coverage(const Eigen::Vector2f e_origin, const float e_rx, const float e_ry, const float e_yaw, const Eigen::Vector2f line_seg_start, const Eigen::Vector2f line_seg_end)
static bool point_ellipsis_coverage(const Eigen::Vector2f e_origin, const float e_rx, const float e_ry, const float e_yaw, const Eigen::Vector2f point)
static bool ComparatorDESCPrt(ManagedObstaclePtr &i, ManagedObstaclePtr &j)
The periodic task executes one thread method repeatedly using the time period specified in the constr...
virtual Layer layer(std::string const &name) const
Definition Client.cpp:80
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define TIMING_START(name)
Helper macro to do timing tests.
Definition TimeUtil.h:289
#define TIMING_END(name)
Prints duration.
Definition TimeUtil.h:306
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::shared_ptr< ManagedObstacle > ManagedObstaclePtr
void add(ElementT const &element)
Definition Layer.h:31