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
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
48using namespace armarx;
49using namespace visionx;
50
51const 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
63void
65{
66 ARMARX_DEBUG << "Initializing " << getName() << ".";
67
68 ARMARX_DEBUG << "Initialized " << getName() << ".";
69}
70
71void
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
84void
86{
87 ARMARX_DEBUG << "Disconnecting " << getName() << ".";
88
89 m_check_human_task->stop();
90
91 ARMARX_DEBUG << "Disconnected " << getName() << ".";
92}
93
94void
96{
97 ARMARX_DEBUG << "Exiting " << getName() << ".";
98
99 ARMARX_DEBUG << "Exited " << getName() << ".";
100}
101
102void
103HumanObstacleDetection::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
171HumanObstacleDetection::OpenPoseResult
172HumanObstacleDetection::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
231HumanObstacleDetection::HumanApproximation
232HumanObstacleDetection::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
399void
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
416void
418{
419 std::lock_guard l{m_enabled_mutex};
420 m_enabled = enable;
421}
422
423void
425{
426 std::lock_guard l{m_enabled_mutex};
427 m_enabled = true;
428}
429
430void
432{
433 std::lock_guard l{m_enabled_mutex};
434 m_enabled = false;
435}
436
437std::string
442
445{
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}
std::string timestamp()
#define M_PI
Definition MathTools.h:17
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 onInitComponent() override
Pure virtual hook for the subclass.
static const std::string default_name
void onDisconnectComponent() override
Hook for subclass.
void onConnectComponent() override
Pure virtual hook for the subclass.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void enable(const Ice::Current &=Ice::emptyCurrent) override
void setEnabled(bool enable, const Ice::Current &=Ice::emptyCurrent) override
void onExitComponent() override
Hook for subclass.
void disable(const Ice::Current &=Ice::emptyCurrent) override
std::string getDefaultName() const override
Retrieve default name of component.
void report3DKeypoints(const armarx::HumanPose3DMap &kpml, long timestamp, const Ice::Current &=Ice::emptyCurrent) override
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
std::string getName() const
Retrieve name of object.
The periodic task executes one thread method repeatedly using the time period specified in the constr...
Measures the passed time between the construction or calling reset() and stop().
Definition StopWatch.h:42
Duration stop()
Stops the timer and returns the measured duration.
Definition StopWatch.cpp:39
void reset()
Resets the timer.
Definition StopWatch.cpp:46
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
std::shared_ptr< Value > value()
Definition cxxopts.hpp:855
bool isfinite(const std::vector< T, Ts... > &v)
Definition algorithm.h:366
ArmarX headers.
double norm(const Point &a)
Definition point.hpp:102