HumanObstacleDetection.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::HumanAvoidance
17  * @author Christian R. G. Dreher <c.dreher@kit.edu>
18  * @author Fabian Peller
19  * @date 2019
20  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
21  * GNU General Public License
22  */
23 
24 
25 #include "HumanObstacleDetection.h"
26 
27 
28 // STD/STL
29 #include <cmath>
30 #include <limits>
31 #include <map>
32 #include <string>
33 
34 // Simox
35 #include <VirtualRobot/Nodes/RobotNode.h>
36 #include <VirtualRobot/XML/RobotIO.h>
37 
38 // Ice
39 #include <Ice/Current.h>
40 
41 // ArmarX
45 
47 
48 using namespace armarx;
49 using namespace visionx;
50 
51 const std::string HumanObstacleDetection::default_name = "HumanObstacleDetection";
52 
54  m_enabled(true),
55  m_onlyUseFirstNResults(-1),
56  m_warn_distance(100),
57  m_human_confidence_filter_value(0.3),
58  m_min_velocity_treshold(15),
59  m_keypoint_after(IceUtil::Time::milliSeconds(1500))
60 {
61 }
62 
63 void
65 {
66  ARMARX_DEBUG << "Initializing " << getName() << ".";
67 
68  ARMARX_DEBUG << "Initialized " << getName() << ".";
69 }
70 
71 void
73 {
74  ARMARX_DEBUG << "Connecting " << getName() << ".";
75 
76  const unsigned int periodic_task_interval = 100;
77  m_check_human_task = new PeriodicTask<HumanObstacleDetection>(
78  this, &HumanObstacleDetection::checkHumanVisibility, periodic_task_interval);
79  m_check_human_task->start();
80 
81  ARMARX_DEBUG << "Connected " << getName() << ".";
82 }
83 
84 void
86 {
87  ARMARX_DEBUG << "Disconnecting " << getName() << ".";
88 
89  m_check_human_task->stop();
90 
91  ARMARX_DEBUG << "Disconnected " << getName() << ".";
92 }
93 
94 void
96 {
97  ARMARX_DEBUG << "Exiting " << getName() << ".";
98 
99  ARMARX_DEBUG << "Exited " << getName() << ".";
100 }
101 
102 void
103 HumanObstacleDetection::report3DKeypoints(const armarx::HumanPose3DMap& kpml,
104  long timestamp,
105  const Ice::Current&)
106 {
107  ARMARX_DEBUG << "Got human pose";
108  if (m_onlyUseFirstNResults == 0)
109  {
110  return;
111  }
112 
113  if (m_onlyUseFirstNResults > 0)
114  {
115  m_onlyUseFirstNResults--;
116  }
117 
118  IceUtil::Time start = IceUtil::Time::now();
119  // Exit if component disabled or kpml is empty.
120  {
121  std::lock_guard l{m_enabled_mutex};
122  if (not m_enabled or kpml.empty())
123  {
124  return;
125  }
126  }
127  ARMARX_DEBUG << "TIme delta: " << start - IceUtil::Time::microSeconds(timestamp);
128  ARMARX_DEBUG << "start report keypoints";
129 
130  std::lock_guard l{m_human_mutex};
131  const IceUtil::Time time = IceUtil::Time::microSeconds(timestamp);
133 
134  // Create vector of maps
135  std::vector<Keypoint3DMap> legacy_kpml;
136  for (const auto& [name, entity] : kpml)
137  {
138  legacy_kpml.push_back(entity.keypointMap);
139  }
140 
141  const OpenPoseResult closestHuman = find_closest_human(legacy_kpml, time);
142  ARMARX_DEBUG << "find closest human" << sw.stop();
143 
144  if (not closestHuman.valid)
145  {
146  return;
147  }
148 
149  if (closestHuman.distance < m_warn_distance)
150  {
151 
152  ARMARX_IMPORTANT << deactivateSpam(30) << "HUMAN CLOSE! Attention!";
153  }
154  sw.reset();
155  HumanApproximation human = approximate_human(closestHuman);
156  ARMARX_DEBUG << "approximation" << sw.stop();
157 
158 
159  sw.reset();
160  m_obstacle_manager->directly_update_obstacle("human",
161  Eigen::Vector2f(human.posX, human.posY),
162  human.axisLengthX,
163  human.axisLengthY,
164  human.yaw);
165  ARMARX_DEBUG << "manager" << sw.stop();
166 
167  ARMARX_DEBUG << "Drawing Human Obsacle at position (" << human.posX << ", " << human.posY
168  << "). Needed time: " << IceUtil::Time::now() - start;
169 }
170 
171 HumanObstacleDetection::OpenPoseResult
172 HumanObstacleDetection::find_closest_human(const std::vector<Keypoint3DMap>& kpml,
173  const IceUtil::Time& timestamp)
174 {
175  ARMARX_DEBUG << "Find closest human.";
176  OpenPoseResult closest_human;
177  closest_human.mean_position = Eigen::Vector3f(0, 0, 0);
178  closest_human.timestamp = timestamp;
179  closest_human.distance = std::numeric_limits<double>::max();
180  closest_human.confidence = 0.0;
181  closest_human.index = -1;
182  closest_human.valid = false;
183 
184  for (unsigned int i = 0; i < kpml.size(); ++i)
185  {
186  // Calculate mean distance of keypoints to camera.
187  auto& kpm = kpml[i];
188  Eigen::Vector3f sum_positions(0, 0, 0);
189  float sum_distance = 0;
190  float sum_confidence = 0;
191  int amount = 0;
192 
193  for (const auto& [key, value] : kpm)
194  {
195  if (std::isfinite(value.x) and value.x != 0 and std::isfinite(value.y) and
196  value.y != 0 and std::isfinite(value.z) and value.z != 0)
197  {
198  sum_confidence += value.confidence;
199  sum_positions += Eigen::Vector3f(value.globalX, value.globalY, value.globalZ);
200  sum_distance += value.z;
201  ++amount;
202  }
203  }
204 
205  if (amount == 0)
206  {
207  continue;
208  }
209 
210  const Eigen::Vector3f mean_position = sum_positions * (1.0 / amount);
211  const float mean_confidence = sum_confidence / amount;
212  const float mean_distance = sum_distance / amount;
213  ARMARX_DEBUG << "Found a human at a distance of " << sum_distance << "mm with confidence "
214  << mean_confidence << ".";
215 
216  if (sum_distance < closest_human.distance and
217  mean_confidence >= m_human_confidence_filter_value)
218  {
219  closest_human.mean_position = mean_position;
220  closest_human.distance = mean_distance;
221  closest_human.index = i;
222  closest_human.keypoints = kpml[i];
223  closest_human.confidence = mean_confidence;
224  closest_human.valid = true;
225  }
226  }
227 
228  return closest_human;
229 }
230 
231 HumanObstacleDetection::HumanApproximation
232 HumanObstacleDetection::approximate_human(const OpenPoseResult& closest_human)
233 {
234  ARMARX_DEBUG << "Approximate human";
235 
236  std::map<std::string, std::pair<IceUtil::Time, Eigen::Vector2f>>
237  transformed_points_with_timestamp;
238 
239  // Initialize transformed points with old ones.
240  if (m_pose_buffer_fillctr > 0)
241  {
242  auto& pose_buf = m_pose_buffer.getUpToDateReadBuffer();
243  for (unsigned int i = 0; i < m_pose_buffer_fillctr; ++i)
244  {
245  for (const auto& [key, timestamped_keypoint] : pose_buf[i].transformed_keypoints)
246  {
247  IceUtil::Time keypoint_timestamp = timestamped_keypoint.first;
248  if (closest_human.timestamp - keypoint_timestamp > m_keypoint_after)
249  {
250  transformed_points_with_timestamp[key] = timestamped_keypoint;
251  }
252  }
253  }
254  }
255 
256  // Update new transformed keypoints
257  for (const auto& [key, value] : closest_human.keypoints)
258  {
259  //ARMARX_DEBUG << "kpm[" << key << "] = (" << value.x << ", " << value.y << ", "
260  // << value.z << ")";
261  if (std::isfinite(value.globalX) and value.globalX != 0 and std::isfinite(value.globalY) and
262  value.globalY != 0 and std::isfinite(value.globalZ) and value.globalZ != 0)
263  {
264  Eigen::Vector3f pos{value.globalX, value.globalY, value.globalZ};
265 
266  transformed_points_with_timestamp[key] =
267  std::make_pair(closest_human.timestamp, Eigen::Vector2f{pos(0), pos(1)});
268  }
269  }
270 
271  // Generate sum of all transformed keypoints in order to calculate mean.
272  float sum_x = 0;
273  float sum_y = 0;
274  unsigned int amount = 0;
275  for (const auto& [key, timestamped_keypoint] : transformed_points_with_timestamp)
276  {
277  Eigen::Vector2f keypoint = timestamped_keypoint.second;
278 
279  sum_x += keypoint(0);
280  sum_y += keypoint(1);
281  ++amount;
282  }
283 
284  // Calculate mean.
285  Eigen::Vector2f mean = Eigen::Vector2f::Zero();
286  if (amount != 0)
287  {
288  mean = Eigen::Vector2f{sum_x / amount, sum_y / amount};
289  }
290 
291  // Calculate max distance from mean position. Needs mean in advance.
292  float max_distance = 0;
293  for (const auto& [key, timestamped_keypoint] : transformed_points_with_timestamp)
294  {
295  Eigen::Vector2f keypoint = timestamped_keypoint.second;
296  max_distance = std::max(max_distance, (keypoint - mean).norm());
297  }
298 
299  ARMARX_DEBUG << "Found positions in current frame [X,Y,Distance]: (" << mean(0) << ", "
300  << mean(1) << ", " << max_distance << ").";
301  if ((last_human_approximation.valid and mean.isZero()) or max_distance == 0.0)
302  {
303  // Safety return to avoid 0,0 positions (although a human has been found).
304  return last_human_approximation;
305  }
306 
307  // Calculate yaw from the last poses (according to velocity).
308  Eigen::Vector2f velocity;
309  float velocity_length = 0.0;
310  float yaw = 0.0;
311  if (m_pose_buffer_fillctr > 0)
312  {
313  Eigen::Vector2f sum{0, 0};
314  auto& pose_buf = m_pose_buffer.getUpToDateReadBuffer();
315  for (unsigned int i = 0; i < m_pose_buffer_fillctr; ++i)
316  {
317  sum += Eigen::Vector2f{pose_buf[i].posX, pose_buf[i].posY};
318  }
319  Eigen::Vector2f temp_mean = sum / m_pose_buffer_fillctr;
320  velocity = mean - temp_mean;
321  velocity_length = velocity.norm();
322  Eigen::Vector2f velocity_normalized = velocity.normalized();
323 
324  const float cross_product = velocity_normalized(1);
325  const float dot_product = velocity_normalized(0);
326  yaw = acos(dot_product);
327 
328  if (cross_product < 0)
329  {
330  yaw = 2 * M_PI - yaw;
331  }
332  }
333 
334  ARMARX_DEBUG << "Got yaw: " << yaw << " and velocity vector " << velocity << " and length "
335  << velocity_length << ".";
336  float velocity_multiplier = velocity_length / 15.0;
337 
338  HumanApproximation current_frame_human;
339  current_frame_human.valid = true;
340  current_frame_human.transformed_keypoints = transformed_points_with_timestamp;
341  current_frame_human.posX = mean(0);
342  current_frame_human.posY = mean(1);
343  current_frame_human.axisLengthX = max_distance * velocity_multiplier;
344  current_frame_human.axisLengthY = max_distance;
345  current_frame_human.yaw = yaw;
346  current_frame_human.timestamp = closest_human.timestamp;
347 
348  // Add human of current frame to buffer.
349  auto& buf = m_pose_buffer.getWriteBuffer();
350  buf.at(m_pose_buffer_index++) = current_frame_human;
351  m_pose_buffer_index %= buf.size();
352  m_pose_buffer.commitWrite();
353  ++m_pose_buffer_fillctr;
354  m_pose_buffer_fillctr = std::min(m_pose_buffer_fillctr, static_cast<unsigned int>(buf.size()));
355 
356  // Calculate means of last frames.
357  HumanApproximation mean_frame_human = current_frame_human;
358  if (m_pose_buffer_fillctr > 0)
359  {
360  Eigen::Vector2f pose_sum{0, 0};
361  Eigen::Vector2f axis_length_sum{0, 0};
362  double sum_cos_yaw = 0;
363  double sum_sin_yaw = 0;
364  auto& pose_buf = m_pose_buffer.getUpToDateReadBuffer();
365  for (unsigned int i = 0; i < m_pose_buffer_fillctr; ++i)
366  {
367  pose_sum += Eigen::Vector2f{pose_buf[i].posX, pose_buf[i].posY};
368  axis_length_sum += Eigen::Vector2f{pose_buf[i].axisLengthX, pose_buf[i].axisLengthY};
369  sum_sin_yaw += std::sin(pose_buf[i].yaw);
370  sum_cos_yaw += std::cos(pose_buf[i].yaw);
371  }
372  mean_frame_human.posX = pose_sum(0) / m_pose_buffer_fillctr;
373  mean_frame_human.posY = pose_sum(1) / m_pose_buffer_fillctr;
374  //mean_frame_human.axisLengthX = axis_length_sum(0) / m_pose_buffer_fillctr;
375  //mean_frame_human.axisLengthX = axis_length_sum(1) / m_pose_buffer_fillctr;
376 
377  // In order to avoid jitter, we only use the mean yaw if the velocity is great enough.
378  if (velocity_length > m_min_velocity_treshold)
379  {
380  double mean_yaw = std::atan2((1.0 / m_pose_buffer_fillctr) * sum_sin_yaw,
381  (1.0 / m_pose_buffer_fillctr) * sum_cos_yaw);
382  if (mean_yaw < 0)
383  {
384  mean_yaw += 2.0 * M_PI;
385  }
386 
387  mean_frame_human.yaw = mean_yaw;
388  }
389  else
390  {
391  mean_frame_human.yaw = last_human_approximation.yaw;
392  }
393  }
394 
395  last_human_approximation = mean_frame_human;
396  return mean_frame_human;
397 }
398 
399 void
401 {
402  std::lock_guard l{m_human_mutex};
403  const bool is_data_outdated =
404  (IceUtil::Time::now() - last_human_approximation.timestamp) > m_keypoint_after;
405  if (last_human_approximation.valid and is_data_outdated)
406  {
407  ARMARX_IMPORTANT << "Have not seen a human for " << m_keypoint_after << ". "
408  << "Deleting obstacle.";
409  HumanApproximation human;
410  last_human_approximation = human;
411 
412  m_obstacle_manager->remove_obstacle("human");
413  }
414 }
415 
416 void
417 HumanObstacleDetection::setEnabled(bool enable, const Ice::Current&)
418 {
419  std::lock_guard l{m_enabled_mutex};
420  m_enabled = enable;
421 }
422 
423 void
424 HumanObstacleDetection::enable(const Ice::Current&)
425 {
426  std::lock_guard l{m_enabled_mutex};
427  m_enabled = true;
428 }
429 
430 void
432 {
433  std::lock_guard l{m_enabled_mutex};
434  m_enabled = false;
435 }
436 
437 std::string
439 {
440  return default_name;
441 }
442 
445 {
446  PropertyDefinitionsPtr def{new ComponentPropertyDefinitions{getConfigIdentifier()}};
447 
448  def->topic<armarx::OpenPose3DListener>("OpenPoseEstimation3D");
449 
450  def->component(m_obstacle_manager,
451  "DynamicObstacleManager",
452  "ObstacleManager",
453  "The name of the obstacle manager proxy");
454 
455  def->optional(m_enabled, "ActivateOnStartup", "Activate the component on startup.");
456  def->optional(m_onlyUseFirstNResults,
457  "OnlyUseFirstNResults",
458  "Only use the first n results of OpenPose.");
459  def->optional(m_keypoint_after,
460  "ForgetOpenPoseKeypointAfter",
461  "Forget the OpenPose keypoints after X ms if not visible anymore.");
462  def->optional(m_min_velocity_treshold,
463  "MinVelocityThreshold",
464  "Velocities below this value will be considered zero.");
465  def->optional(m_human_confidence_filter_value,
466  "HumanConfidenceFilterValue",
467  "Poses with a mean confidence below this value will be discarded.");
468  def->optional(
469  m_warn_distance, "WarnDistance", "Distance in [mm] at which a warning should be issued.")
470  .setMin(100);
471  return def;
472 }
armarx::HumanObstacleDetection::disable
void disable(const Ice::Current &=Ice::emptyCurrent) override
Definition: HumanObstacleDetection.cpp:431
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:190
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
StopWatch.h
armarx::HumanObstacleDetection::getDefaultName
std::string getDefaultName() const override
Retrieve default name of component.
Definition: HumanObstacleDetection.cpp:438
armarx::HumanObstacleDetection::enable
void enable(const Ice::Current &=Ice::emptyCurrent) override
Definition: HumanObstacleDetection.cpp:424
armarx::HumanObstacleDetection::onExitComponent
void onExitComponent() override
Hook for subclass.
Definition: HumanObstacleDetection.cpp:95
armarx::HumanObstacleDetection::HumanObstacleDetection
HumanObstacleDetection() noexcept
Definition: HumanObstacleDetection.cpp:53
armarx::HumanObstacleDetection::checkHumanVisibility
void checkHumanVisibility()
Definition: HumanObstacleDetection.cpp:400
armarx::HumanObstacleDetection::setEnabled
void setEnabled(bool enable, const Ice::Current &=Ice::emptyCurrent) override
Definition: HumanObstacleDetection.cpp:417
IceUtil
Definition: Instance.h:21
armarx::HumanObstacleDetection::onConnectComponent
void onConnectComponent() override
Pure virtual hook for the subclass.
Definition: HumanObstacleDetection.cpp:72
armarx::PropertyDefinitionContainer::topic
void topic(IceInternal::ProxyHandle< PropertyType > &setter, const std::string &default_name="", const std::string &property_name="", const std::string &description="")
Define a property to set the name of a topic which is subscribed to.
Definition: PropertyDefinitionContainer.h:424
armarx::core::time::StopWatch::stop
Duration stop()
Stops the timer and returns the measured duration.
Definition: StopWatch.cpp:39
std::isfinite
bool isfinite(const std::vector< T, Ts... > &v)
Definition: algorithm.h:366
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:75
armarx::HumanObstacleDetection::default_name
static const std::string default_name
Definition: HumanObstacleDetection.h:115
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:855
armarx::mean
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
Definition: KinematicUnitGuiPlugin.cpp:1620
armarx::HumanObstacleDetection::onDisconnectComponent
void onDisconnectComponent() override
Hook for subclass.
Definition: HumanObstacleDetection.cpp:85
armarx::HumanObstacleDetection::onInitComponent
void onInitComponent() override
Pure virtual hook for the subclass.
Definition: HumanObstacleDetection.cpp:64
M_PI
#define M_PI
Definition: MathTools.h:17
armarx::HumanObstacleDetection::report3DKeypoints
void report3DKeypoints(const armarx::HumanPose3DMap &kpml, long timestamp, const Ice::Current &=Ice::emptyCurrent) override
Definition: HumanObstacleDetection.cpp:103
FramedPose.h
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
timestamp
std::string timestamp()
Definition: CartographerAdapter.cpp:85
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
Component.h
armarx::HumanObstacleDetection::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: HumanObstacleDetection.cpp:444
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:69
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::core::time::StopWatch
Measures the passed time between the construction or calling reset() and stop().
Definition: StopWatch.h:41
armarx::PeriodicTask
Definition: ArmarXManager.h:70
min
T min(T t1, T t2)
Definition: gdiam.h:44
ArmarXDataPath.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::core::time::StopWatch::reset
void reset()
Resets the timer.
Definition: StopWatch.cpp:46
HumanObstacleDetection.h
norm
double norm(const Point &a)
Definition: point.hpp:102