35#include <VirtualRobot/Nodes/RobotNode.h>
36#include <VirtualRobot/XML/RobotIO.h>
39#include <Ice/Current.h>
55 m_onlyUseFirstNResults(-1),
57 m_human_confidence_filter_value(0.3),
58 m_min_velocity_treshold(15),
59 m_keypoint_after(
IceUtil::Time::milliSeconds(1500))
76 const unsigned int periodic_task_interval = 100;
79 m_check_human_task->start();
89 m_check_human_task->stop();
108 if (m_onlyUseFirstNResults == 0)
113 if (m_onlyUseFirstNResults > 0)
115 m_onlyUseFirstNResults--;
118 IceUtil::Time start = IceUtil::Time::now();
121 std::lock_guard l{m_enabled_mutex};
122 if (not m_enabled or kpml.empty())
130 std::lock_guard l{m_human_mutex};
131 const IceUtil::Time time = IceUtil::Time::microSeconds(
timestamp);
135 std::vector<Keypoint3DMap> legacy_kpml;
136 for (
const auto& [name, entity] : kpml)
138 legacy_kpml.push_back(entity.keypointMap);
141 const OpenPoseResult closestHuman = find_closest_human(legacy_kpml, time);
144 if (not closestHuman.valid)
149 if (closestHuman.distance < m_warn_distance)
155 HumanApproximation
human = approximate_human(closestHuman);
160 m_obstacle_manager->directly_update_obstacle(
"human",
168 <<
"). Needed time: " << IceUtil::Time::now() - start;
171HumanObstacleDetection::OpenPoseResult
172HumanObstacleDetection::find_closest_human(
const std::vector<Keypoint3DMap>& kpml,
176 OpenPoseResult closest_human;
177 closest_human.mean_position = Eigen::Vector3f(0, 0, 0);
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;
184 for (
unsigned int i = 0; i < kpml.size(); ++i)
188 Eigen::Vector3f sum_positions(0, 0, 0);
189 float sum_distance = 0;
190 float sum_confidence = 0;
193 for (
const auto& [key, value] : kpm)
198 sum_confidence += value.confidence;
199 sum_positions += Eigen::Vector3f(value.globalX, value.globalY, value.globalZ);
200 sum_distance += value.z;
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 <<
".";
216 if (sum_distance < closest_human.distance and
217 mean_confidence >= m_human_confidence_filter_value)
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;
228 return closest_human;
231HumanObstacleDetection::HumanApproximation
232HumanObstacleDetection::approximate_human(
const OpenPoseResult& closest_human)
236 std::map<std::string, std::pair<IceUtil::Time, Eigen::Vector2f>>
237 transformed_points_with_timestamp;
240 if (m_pose_buffer_fillctr > 0)
242 auto& pose_buf = m_pose_buffer.getUpToDateReadBuffer();
243 for (
unsigned int i = 0; i < m_pose_buffer_fillctr; ++i)
245 for (
const auto& [key, timestamped_keypoint] : pose_buf[i].transformed_keypoints)
247 IceUtil::Time keypoint_timestamp = timestamped_keypoint.first;
248 if (closest_human.timestamp - keypoint_timestamp > m_keypoint_after)
250 transformed_points_with_timestamp[key] = timestamped_keypoint;
257 for (
const auto& [key, value] : closest_human.keypoints)
266 transformed_points_with_timestamp[key] =
267 std::make_pair(closest_human.timestamp, Eigen::Vector2f{pos(0), pos(1)});
274 unsigned int amount = 0;
275 for (
const auto& [key, timestamped_keypoint] : transformed_points_with_timestamp)
277 Eigen::Vector2f keypoint = timestamped_keypoint.second;
279 sum_x += keypoint(0);
280 sum_y += keypoint(1);
285 Eigen::Vector2f
mean = Eigen::Vector2f::Zero();
288 mean = Eigen::Vector2f{sum_x / amount, sum_y / amount};
292 float max_distance = 0;
293 for (
const auto& [key, timestamped_keypoint] : transformed_points_with_timestamp)
295 Eigen::Vector2f keypoint = timestamped_keypoint.second;
296 max_distance = std::max(max_distance, (keypoint -
mean).
norm());
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)
304 return last_human_approximation;
308 Eigen::Vector2f velocity;
309 float velocity_length = 0.0;
311 if (m_pose_buffer_fillctr > 0)
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)
317 sum += Eigen::Vector2f{pose_buf[i].posX, pose_buf[i].posY};
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();
324 const float cross_product = velocity_normalized(1);
325 const float dot_product = velocity_normalized(0);
326 yaw = acos(dot_product);
328 if (cross_product < 0)
330 yaw = 2 *
M_PI - yaw;
334 ARMARX_DEBUG <<
"Got yaw: " << yaw <<
" and velocity vector " << velocity <<
" and length "
335 << velocity_length <<
".";
336 float velocity_multiplier = velocity_length / 15.0;
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;
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()));
357 HumanApproximation mean_frame_human = current_frame_human;
358 if (m_pose_buffer_fillctr > 0)
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)
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);
372 mean_frame_human.posX = pose_sum(0) / m_pose_buffer_fillctr;
373 mean_frame_human.posY = pose_sum(1) / m_pose_buffer_fillctr;
378 if (velocity_length > m_min_velocity_treshold)
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);
384 mean_yaw += 2.0 *
M_PI;
387 mean_frame_human.yaw = mean_yaw;
391 mean_frame_human.yaw = last_human_approximation.yaw;
395 last_human_approximation = mean_frame_human;
396 return mean_frame_human;
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)
407 ARMARX_IMPORTANT <<
"Have not seen a human for " << m_keypoint_after <<
". "
408 <<
"Deleting obstacle.";
409 HumanApproximation
human;
410 last_human_approximation =
human;
412 m_obstacle_manager->remove_obstacle(
"human");
419 std::lock_guard l{m_enabled_mutex};
426 std::lock_guard l{m_enabled_mutex};
433 std::lock_guard l{m_enabled_mutex};
448 def->topic<armarx::OpenPose3DListener>(
"OpenPoseEstimation3D");
450 def->component(m_obstacle_manager,
451 "DynamicObstacleManager",
453 "The name of the obstacle manager proxy");
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.");
469 m_warn_distance,
"WarnDistance",
"Distance in [mm] at which a warning should be issued.")
Default component property definition container.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
void onInitComponent() override
Pure virtual hook for the subclass.
static const std::string default_name
HumanObstacleDetection() noexcept
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 checkHumanVisibility()
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.
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().
Duration stop()
Stops the timer and returns the measured duration.
void reset()
Resets the timer.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
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()
bool isfinite(const std::vector< T, Ts... > &v)
double norm(const Point &a)