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