LaserScannerObstacleDetection.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 Armar6Skills::ArmarXObjects::LaserObstacleDetection
17  * @author Christian R. G. Dreher <c.dreher@kit.edu>
18  * @date 2020
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 
25 
26 // ArmarX
28 #include <RobotAPI/interface/core/PoseBase.h>
30 
31 using namespace armarx;
32 
33 
34 const std::string
35 LaserScannerObstacleDetection::default_name = "LaserScannerObstacleDetection";
36 
37 
39  m_enabled(true),
40  m_only_submit_first_n_results(-1),
41  m_max_distance_to_put_together(100),
42  m_max_yaw_difference(M_PI / 4),
43  m_periodic_task_interval(500),
44  m_accept_lines_after(100)
45 {
46 
47 }
48 
49 void
51 {
52  m_last_accepted_lines = IceUtil::Time::now();
53 }
54 
55 
56 void
58 {
59  m_eval_obstacles = new PeriodicTask<LaserScannerObstacleDetection>(this,
60  &LaserScannerObstacleDetection::eval_obstacles, m_periodic_task_interval);
61  m_eval_obstacles->start();
62 }
63 
64 
65 void
67 {
68  m_eval_obstacles->stop();
69 }
70 
71 
72 void
74 {
75 }
76 
77 float
78 LaserScannerObstacleDetection::get_distance_from_point_to_line_segment(const Eigen::Vector2f& start, const Eigen::Vector2f& end, const Eigen::Vector2f& p) const
79 {
80  // Return minimum distance between line segment l (start = v, end = w) and point p
81  const Eigen::Vector2f& v = start;
82  const Eigen::Vector2f& w = end;
83  const Eigen::Vector2f vp = p - v;
84  const Eigen::Vector2f vw = w - v;
85  const float line_length_sq = vw(0) * vw(0) + vw(1) * vw(1);
86 
87 
88  if (line_length_sq == 0.0)
89  {
90  ARMARX_ERROR << "Got invalid line segment!";
91  return 0;
92  }
93 
94  // Consider the line extending the segment, parameterized as v + t (w - v).
95  // We find projection of point p onto the line.
96  // It falls where t = [(p-v) . (w-v)] / |w-v|^2
97  // We clamp t from [0,1] to handle points outside the segment vw.
98 
99  const float pv_dot_wv = vp(0) * vw(0) + vp(1) * vw(1);
100  const float t = std::max(0.0f, std::min(1.0f, pv_dot_wv / line_length_sq));
101  const Eigen::Vector2f projection = v + (t * vw);
102  const float distance = (projection - p).norm();
103  return distance;
104 }
105 
106 
107 float
108 LaserScannerObstacleDetection::get_angle_between_vectors(const Eigen::Vector2f& v1, const Eigen::Vector2f& v2) const
109 {
110  // Returns an angle between 0 and 180 degree (the minimum angle between the two vectors)
111  const Eigen::Vector2f v1_vec_normed = v1.normalized();
112  const Eigen::Vector2f v2_vec_normed = v2.normalized();
113  const float dot_product_vec = v1_vec_normed(0) * v2_vec_normed(0) + v1_vec_normed(1) * v2_vec_normed(1);
114  float yaw = acos(dot_product_vec);
115  if (yaw >= M_PI)
116  {
117  yaw -= M_PI;
118  }
119  return yaw;
120 }
121 
122 
123 
124 void
125 LaserScannerObstacleDetection::eval_obstacles()
126 {
128 
129  {
130  std::unique_lock l{m_lines_buffer_mutex};
131  lines = m_lines_buffer;
132  m_lines_buffer.clear();
133  }
134 
135  if (lines.empty() || m_only_submit_first_n_results == 0)
136  {
137  return;
138  }
139 
140  bool update_found = false;
141  int lines_before = lines.size();
142  IceUtil::Time started = IceUtil::Time::now();
143  do
144  {
145  update_found = false;
146  if (lines.size() < 2)
147  {
148  break;
149  }
150 
151  // search for lines to put together
152  unsigned int remove1 = 0;
153  unsigned int remove2 = 0;
154 
155  // TODO(fabian..)
156  // for (unsigned int i = 0; i < lines.size() - 1; ++i)
157  // {
158  // remove1 = i;
159  // const LineSegment2D& line1 = lines[i];
160  // const Eigen::Vector2f origin_start(line1.start.e0, line1.start.e1);
161  // const Eigen::Vector2f origin_end(line1.end.e0, line1.end.e1);
162  // for (unsigned int j = i + 1; j < lines.size(); ++j)
163  // {
164  // remove2 = j;
165  // const LineSegment2D& line2 = lines[j];
166  // Eigen::Vector2f start(line2.start.e0, line2.start.e1);
167  // Eigen::Vector2f end(line2.end.e0, line2.end.e1);
168 
169  // // check yaws
170  // float yaw = get_angle_between_vectors((origin_end - origin_start), (end - start));
171 
172 
173  // // check points and distances
174  // if (yaw < m_max_yaw_difference && (get_distance_from_point_to_line_segment(line1, start) < m_max_distance_to_put_together || get_distance_from_point_to_line_segment(line1, end) < m_max_distance_to_put_together))
175  // {
176  // // Group those lines. Only take longets one
177  // float length_os_s = (origin_start - start).norm();
178  // float length_os_e = (origin_start - end).norm();
179  // float length_oe_s = (origin_end - start).norm();
180  // float length_oe_e = (origin_end - end).norm();
181 
182  // Eigen::Vector2f new_line_s;
183  // Eigen::Vector2f new_line_e;
184  // if (length_os_s > length_os_e && length_os_s > length_oe_s && length_os_s > length_oe_e)
185  // {
186  // // length_os_s is maximum
187  // new_line_s = origin_start;
188  // new_line_e = start;
189  // }
190  // else if (length_os_e > length_oe_s && length_os_e > length_oe_e)
191  // {
192  // // length_os_e is maximum
193  // new_line_s = origin_start;
194  // new_line_e = end;
195  // }
196  // else if (length_oe_s > length_oe_e)
197  // {
198  // // length_oe_s is maximum
199  // new_line_s = origin_end;
200  // new_line_e = start;
201  // }
202  // else
203  // {
204  // // length_oe_s is maximum
205  // new_line_s = origin_end;
206  // new_line_e = end;
207  // }
208 
209  // LineSegment2D new_line;
210  // new_line.start.e0 = new_line_s(0);
211  // new_line.start.e1 = new_line_s(1);
212  // new_line.end.e0 = new_line_e(0);
213  // new_line.end.e1 = new_line_e(1);
214 
215  // lines.push_back(new_line);
216  // update_found = true;
217  // break;
218  // }
219 
220  // }
221  // if (update_found)
222  // {
223  // break;
224  // }
225  // }
226 
227  // remove lines from update
228  if (update_found)
229  {
230  ARMARX_CHECK(remove2 > remove1);
231  lines.erase(lines.begin() + remove2);
232  lines.erase(lines.begin() + remove1);
233  }
234  }
235  while (update_found);
236 
237  ARMARX_DEBUG << "Finished updating the " << lines_before << " lines. Now we have " << lines.size() << " lines. The operation took " << (IceUtil::Time::now() - started).toMilliSeconds() << "ms";
238 
239  // Try to explain line segments by the obstacles from the obstacle avoidance environment.
240  std::vector<dynamicobstaclemanager::LineSegment> converted_lines;
241  for (const auto& chain : lines)
242  {
243 
244  if (chain.size() < 2)
245  {
246  continue;
247  }
248 
249  for (auto it = chain.begin(); it != chain.end() - 1; it++)
250  {
251  converted_lines.emplace_back(dynamicobstaclemanager::LineSegment{*it, *(it + 1)});
252  }
253  }
254  m_obstacle_manager->add_decayable_line_segments(converted_lines);
255 
256 
257  // Debug
258  if (m_only_submit_first_n_results > 0)
259  {
260  m_only_submit_first_n_results--;
261  }
262  ARMARX_DEBUG << "Finished update";
263 }
264 
265 
266 //void
267 //LaserScannerObstacleDetection::reportExtractedEdges(
268 // const std::vector<LineSegment2D>& lines,
269 // const Ice::Current&)
270 //{
271 // {
272 // std::lock_guard l{m_enabled_mutex};
273 // if (! m_enabled or lines.empty())
274 // {
275 // return;
276 // }
277 // }
278 
279 // if ((IceUtil::Time::now() - m_last_accepted_lines).toMilliSeconds() > m_accept_lines_after)
280 // {
281 // std::unique_lock l{m_lines_buffer_mutex};
282 // ARMARX_DEBUG << "Got " << lines.size() << " new extracted edges to buffer.";
283 // m_lines_buffer.insert(m_lines_buffer.end(), lines.begin(), lines.end());
284 // m_last_accepted_lines = IceUtil::Time::now();
285 // }
286 
287 //}
288 
289 void
290 LaserScannerObstacleDetection::setEnabled(bool enable, const Ice::Current&)
291 {
292  std::lock_guard l{m_enabled_mutex};
293  m_enabled = enable;
294 }
295 
296 
297 void
299 {
300  std::lock_guard l{m_enabled_mutex};
301  m_enabled = true;
302 }
303 
304 
305 void
307 {
308  std::lock_guard l{m_enabled_mutex};
309  m_enabled = false;
310 }
311 
312 
313 std::string
315 {
316  return default_name;
317 }
318 
319 
322 {
324 
325  defs->component(m_obstacle_manager, "DynamicObstacleManager", "ObstacleManager", "The name of the obstacle manager proxy");
326  defs->topic<LaserScannerFeaturesListener>();
327 
328  defs->optional(m_only_submit_first_n_results, "SubmitOnlyFirstNResults", "Only send the first laser scanner result. -1 for all.");
329  defs->optional(m_max_distance_to_put_together, "MaxDistanceOfLinesToGroup", "Max distance in mm of two lines to put them together");
330  defs->optional(m_max_yaw_difference, "MaxAngleDifferenceOfLinesToGroup", "Max angle difference in yaw of two lines to put them together");
331  defs->optional(m_periodic_task_interval, "UpdateInterval", "The interval to check the obstacles");
332  defs->optional(m_accept_lines_after, "AcceptLinesAfter", "Accept new Lines for buffer every x ms");
333  defs->optional(m_enabled, "ActivateOnStartup", "Activate the component on startup.");
334 
335  return defs;
336 }
337 
339 {
340  {
341  std::lock_guard l{m_enabled_mutex};
342  if (! m_enabled or globalLineSegmentChains.empty())
343  {
344  return;
345  }
346  }
347 
348  if ((IceUtil::Time::now() - m_last_accepted_lines).toMilliSeconds() > m_accept_lines_after)
349  {
350  std::unique_lock l{m_lines_buffer_mutex};
351 
352 
353  ARMARX_DEBUG << "Got " << globalLineSegmentChains.size() << " new extracted edges to buffer.";
354 
355  m_lines_buffer.insert(m_lines_buffer.end(), globalLineSegmentChains.begin(), globalLineSegmentChains.end());
356  m_last_accepted_lines = IceUtil::Time::now();
357  }
358 }
359 
armarx::LaserScannerObstacleDetection::getDefaultName
std::string getDefaultName() const override
Retrieve default name of component.
Definition: LaserScannerObstacleDetection.cpp:314
armarx::LaserScannerObstacleDetection::onInitComponent
void onInitComponent() override
Pure virtual hook for the subclass.
Definition: LaserScannerObstacleDetection.cpp:50
armarx::LaserScannerObstacleDetection::default_name
static const std::string default_name
Definition: LaserScannerObstacleDetection.h:58
Pose.h
armarx::LaserScannerObstacleDetection::disable
void disable(const Ice::Current &=Ice::emptyCurrent) override
Definition: LaserScannerObstacleDetection.cpp:306
armarx::LaserScannerObstacleDetection::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: LaserScannerObstacleDetection.cpp:321
armarx::LaserScannerObstacleDetection::setEnabled
void setEnabled(bool enable, const Ice::Current &=Ice::emptyCurrent) override
Definition: LaserScannerObstacleDetection.cpp:290
armarx::LaserScannerObstacleDetection::LaserScannerObstacleDetection
LaserScannerObstacleDetection() noexcept
Definition: LaserScannerObstacleDetection.cpp:38
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::LaserScannerObstacleDetection::onDisconnectComponent
void onDisconnectComponent() override
Hook for subclass.
Definition: LaserScannerObstacleDetection.cpp:66
M_PI
#define M_PI
Definition: MathTools.h:17
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
armarx::LaserScannerObstacleDetection::onExitComponent
void onExitComponent() override
Hook for subclass.
Definition: LaserScannerObstacleDetection.cpp:73
max
T max(T t1, T t2)
Definition: gdiam.h:48
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
Component.h
armarx::navigation::components::laser_scanner_feature_extraction::LineSegment2DChainSeq
armarx::laser_scanner_feature_extraction::LineSegment2DChainSeq LineSegment2DChainSeq
Definition: Component.h:56
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:74
armarx::LaserScannerObstacleDetection::reportExtractedLineSegments
void reportExtractedLineSegments(const laser_scanner_feature_extraction::LineSegment2DChainSeq &globalLineSegmentChains, const Ice::Current &=Ice::Current()) override
Definition: LaserScannerObstacleDetection.cpp:338
armarx::LaserScannerObstacleDetection::enable
void enable(const Ice::Current &=Ice::emptyCurrent) override
Definition: LaserScannerObstacleDetection.cpp:298
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:70
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::LaserScannerObstacleDetection::onConnectComponent
void onConnectComponent() override
Pure virtual hook for the subclass.
Definition: LaserScannerObstacleDetection.cpp:57
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:88
armarx::PeriodicTask
Definition: ArmarXManager.h:70
min
T min(T t1, T t2)
Definition: gdiam.h:42
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
LaserScannerObstacleDetection.h
norm
double norm(const Point &a)
Definition: point.hpp:94