24 #include <VirtualRobot/MathTools.h>
25 #include <VirtualRobot/RobotNodeSet.h>
39 #include <RobotAPI/interface/units/RobotUnit/NJointCartesianVelocityControllerWithRamp.h>
50 using namespace HandOverGroup;
53 ReachOutToHumanPose::SubClassRegistry
54 ReachOutToHumanPose::Registry(ReachOutToHumanPose::GetName(),
55 &ReachOutToHumanPose::CreateInstance);
57 #define ENABLE_DEBUG_DRAWER 0
60 ReachOutToHumanPose::onEnter()
69 ReachOutToHumanPose::run()
75 getContext<HandOverGroupStatechartContextExtension>();
76 c->getOpenPoseEstimation()->start3DPoseEstimation();
81 rwristFilter(in.getPoseMedianFilterSize());
83 std::vector<filters::AverageFilter> targetPositionFilter(3), targetVelocityFilter(3);
84 for (
auto& filter : targetPositionFilter)
86 filter.windowFilterSize = in.getTargetPositionAverageFilterSize();
88 for (
auto& filter : targetVelocityFilter)
90 filter.windowFilterSize = in.getTargetPositionAverageFilterSize();
92 float maxTCPVelocity = in.getMaxTCPVelocity();
93 float considerForceDistanceThreshold = in.getConsiderForceDistanceThreshold();
94 auto robot = getLocalRobot();
97 auto node = robot->getRobotNode(
"ArmR3_Sho2");
98 node->setJointLimits(node->getJointLimitLow(), VirtualRobot::MathTools::deg2rad(90));
101 auto node = robot->getRobotNode(
"ArmR5_Elb1");
102 node->setJointLimits(VirtualRobot::MathTools::deg2rad(5), node->getJointLimitHigh());
105 auto node = robot->getRobotNode(
"ArmR1_Cla1");
106 node->setJointLimits(VirtualRobot::MathTools::deg2rad(-45),
107 VirtualRobot::MathTools::deg2rad(45));
111 auto node = robot->getRobotNode(
"ArmL3_Sho2");
112 node->setJointLimits(node->getJointLimitLow(), VirtualRobot::MathTools::deg2rad(90));
115 auto node = robot->getRobotNode(
"ArmL5_Elb1");
116 node->setJointLimits(VirtualRobot::MathTools::deg2rad(5), node->getJointLimitHigh());
119 auto node = robot->getRobotNode(
"ArmL1_Cla1");
120 node->setJointLimits(VirtualRobot::MathTools::deg2rad(-45),
121 VirtualRobot::MathTools::deg2rad(45));
124 auto cameraRobot = getLocalRobot();
127 forceMagnitudeAvgFilter.windowFilterSize = 10;
129 getHeadIKUnit()->request();
132 ARMARX_INFO <<
"InitialHeadIKTarget(): " << in.getInitialHeadIKTarget()->output();
133 getHeadIKUnit()->setHeadTarget(in.getHeadIKNodeSet(), in.getInitialHeadIKTarget());
136 auto kinematicChainName = in.getKinematicChainName();
140 auto nodeset = robot->getRobotNodeSet(kinematicChainName);
141 auto tcp = nodeset->getTCP();
142 auto kinematicRootName = nodeset->getKinematicRoot()->getName();
145 <<
" position in root: " << nodeset->getKinematicRoot()->getPositionInRootFrame();
149 getForceTorqueObserver()->getObserverName(), in.getFTSensorName(),
"forces")));
152 NJointCartesianVelocityControllerWithRampConfigPtr ctrlCfg =
153 new NJointCartesianVelocityControllerWithRampConfig();
154 ctrlCfg->KpJointLimitAvoidance = 0;
155 ctrlCfg->jointLimitAvoidanceScale = 1;
157 ctrlCfg->nodeSetName = kinematicChainName;
158 ctrlCfg->tcpName = tcp->getName();
159 ctrlCfg->maxNullspaceAcceleration = 2;
160 ctrlCfg->maxPositionAcceleration = in.getTCPAcceleration();
161 ctrlCfg->maxOrientationAcceleration = 1;
164 posController.
maxOriVel = in.getMaxOrientationVelocity();
165 posController.
maxPosVel = in.getMaxTCPVelocity();
166 posController.
KpOri = in.getKpOrientation();
167 posController.
KpPos = in.getKp();
169 auto ctrlName =
"ReachOutToHumanTCPCtrl";
172 if (
auto oldCtrl =
getRobotUnit()->getNJointController(ctrlName))
175 while (oldCtrl->isControllerActive())
177 oldCtrl->deactivateController();
187 "NJointCartesianVelocityControllerWithRamp", ctrlName, ctrlCfg);
189 NJointCartesianVelocityControllerWithRampInterfacePrx ctrl =
190 NJointCartesianVelocityControllerWithRampInterfacePrx::checkedCast(baseCtrl);
193 ctrl->activateController();
195 auto targetOrientation = in.getHandOrientation();
200 const Eigen::Vector3f offset{0, 0, 300};
201 Eigen::Vector3f idlePosi = tcp->getPositionInRootFrame() + offset;
203 new FramedPosition(idlePosi, robot->getRootNode()->getName(), robot->getName());
206 bool drawDebugInfos = in.getDrawDebugInfos();
208 while (!isRunningTaskStopped())
213 auto timeSinceSinceStart = now - startTime;
217 c->getImageSourceSelectionTopic()->setImageSource(
"OpenPoseEstimationResult", 5000);
220 std::vector<Keypoint3DMap> keypointList;
222 std::tie(
timestamp, keypointList) =
c->getPoseData()->getLatestData();
225 cameraRobot, getRobotStateComponent(),
timestamp.toMicroSeconds());
227 Keypoint3DMap poseData = getClosestPerson(keypointList);
228 int validKeypoints = 0;
229 for (
auto& pair : poseData)
231 if (isKeypointValid(pair.second))
238 Keypoint3D rwrist = poseData[
"RWrist"];
240 bool validTarget =
false;
241 if (isKeypointValid(rwrist))
243 rwristFilter.
update(now.toMicroSeconds(),
new Variant(rwristPosition));
248 Vector3Ptr filteredRWristPosition = VariantPtr::dynamicCast(tmp)->get<
Vector3>();
252 filteredRWristPosition->toEigen(), in.getCameraFrameName(), robot->getName())
257 #if ENABLE_DEBUG_DRAWER
258 this->getDebugDrawerTopic()->setSphereVisu(
261 globalrWristPosition,
262 DrawColor{1.0f, 0.0f, 0.0f, rwrist.confidence},
266 .position(globalrWristPosition->toEigen())
270 getDebugObserver()->setDebugChannel(
273 {
"rwristpoint_z",
new Variant(rwristPosition->z)},
274 {
"rwristpoint_z_filtered",
new Variant(filteredRWristPosition->z)},
278 rwristPosition = filteredRWristPosition;
281 Keypoint3D lwrist = poseData[
"LWrist"];
283 if (isKeypointValid(lwrist))
285 lwristFilter.update(now.toMicroSeconds(),
new Variant(lwristPosition));
287 tmp = lwristFilter.getValue();
290 lwristPosition = VariantPtr::dynamicCast(tmp)->get<
Vector3>();
293 FramedPosition(lwristPosition->toEigen(), in.getCameraFrameName(), robot->getName())
298 #if ENABLE_DEBUG_DRAWER
299 this->getDebugDrawerTopic()->setSphereVisu(
302 globallWristPosition,
303 DrawColor{0.0f, 1.0f, 0.0f, lwrist.confidence},
307 .position(globallWristPosition->toEigen())
315 float leftHandDistance = lwristPosition && isKeypointValid(lwrist)
316 ? lwristPosition->toEigen().norm()
318 float rightHandDistance = rwristPosition && isKeypointValid(rwrist)
319 ? rwristPosition->toEigen().norm()
323 leftHandDistance < rightHandDistance ? lwristPosition : rwristPosition;
327 getDebugObserver()->setDebugChannel(
330 new Variant(
std::abs(poseData[
"LWrist"].z - poseData[
"LShoulder"].z))},
331 {
"zDiffNeckR",
new Variant(
std::abs(poseData[
"RWrist"].z - poseData[
"Neck"].z))},
332 {
"zDiffNeckL",
new Variant(
std::abs(poseData[
"LWrist"].z - poseData[
"Neck"].z))}});
336 Eigen::Vector3f headIkTarget = getHeadIKTarget(poseData);
338 FramedPosition(headIkTarget, in.getCameraFrameName(), robot->getName())
339 .toGlobal(cameraRobot);
340 globalHeadIkTarget->z =
std::max(in.getMinimumGazeZPosition(), globalHeadIkTarget->z);
342 if (validKeypoints > 3 && !in.getHeadIKNodeSet().empty() && headIkTarget.norm() > 0)
346 #if ENABLE_DEBUG_DRAWER
347 this->getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
350 DrawColor{1.0f, 1.0f, 0.0f, 1.0f},
354 .position(globalHeadIkTarget->toEigen())
355 .
color(simox::Color::yellow())
359 headIKTargetFilter.
update(now.toMicroSeconds(),
new Variant(globalHeadIkTarget));
360 auto tmp = headIKTargetFilter.
getValue();
368 (lastIKTarget->toEigen() - globalFilteredMedianKeypoint->toEigen()).norm() >
371 getHeadIKUnit()->setHeadTarget(in.getHeadIKNodeSet(),
372 globalFilteredMedianKeypoint);
373 lastIKTarget = globalFilteredMedianKeypoint;
380 #if ENABLE_DEBUG_DRAWER
381 this->getDebugDrawerTopic()->removeSphereVisu(
"HandOverState",
"Median");
388 float currentHumanReachDistance =
389 headIkTarget.norm() == 0 && currentHandPosition->toEigen().norm() > 100
391 :
std::abs((currentHandPosition->toEigen() - headIkTarget)(2));
392 bool humanIsReachingOut = currentHumanReachDistance > in.getHumanMinimumReachOutDistance();
394 Eigen::Vector3f currentTargetVec = currentHandPosition->toEigen();
395 for (
int i = 0; i < 3; ++i)
397 targetPositionFilter.at(i).update(now.toMicroSeconds(),
398 new Variant(currentTargetVec(i)));
400 Eigen::Vector3f currentFilteredTargetPositionVec;
401 for (
int i = 0; i < 3; ++i)
403 currentFilteredTargetPositionVec(i) = targetPositionFilter.at(i).getValue()->getFloat();
407 currentFilteredTargetPositionVec, in.getCameraFrameName(), cameraRobot->getName());
408 targetPositioninRootFrame->changeFrame(robot, robot->getRootNode()->getName());
415 targetInKinematicRootFrame->changeFrame(robot, kinematicRootName);
416 float reachDistanceToTarget = targetInKinematicRootFrame->toEigen().norm();
418 getDebugObserver()->setDebugDatafield(
419 "ReachOutToHuman",
"reachDistanceToTarget",
new Variant(reachDistanceToTarget));
420 float reachDistanceToTargetClamped = reachDistanceToTarget;
421 if (reachDistanceToTarget > in.getDistanceThresholdSoft() &&
422 reachDistanceToTarget < in.getDistanceThreshold())
424 Eigen::Vector3f targetPositioninKinematicRootFrameClamped =
425 targetInKinematicRootFrame->toEigen().normalized() * in.getDistanceThresholdSoft();
426 targetPositioninRootFrame =
428 targetInKinematicRootFrame->frame,
429 targetInKinematicRootFrame->agent);
430 targetPositioninRootFrame->changeFrame(robot, robot->getRootNode()->getName());
431 reachDistanceToTargetClamped = targetPositioninKinematicRootFrameClamped.norm();
435 getDebugObserver()->setDebugChannel(
437 {{
"targetPositioninRootFrameX",
new Variant(targetPositioninRootFrame->x)},
438 {
"targetPositioninRootFrameY",
new Variant(targetPositioninRootFrame->y)},
439 {
"targetPositioninRootFrameZ",
new Variant(targetPositioninRootFrame->z)},
440 {
"targetPositioninRootNorm",
441 new Variant(targetPositioninRootFrame->toEigen().norm())},
442 {
"reachDistanceToTargetClamped",
new Variant(reachDistanceToTargetClamped)}});
445 float distanceToTarget =
446 (tcp->getPositionInRootFrame() - targetPositioninRootFrame->toEigen()).
norm();
448 bool returnToIdlePose =
449 reachDistanceToTarget > in.getDistanceThreshold() || !validTarget ||
450 !humanIsReachingOut || validKeypoints < 5 ||
451 timeSinceSinceStart.toMilliSeconds() < in.getMinWaitBeforeReachTime();
452 if (returnToIdlePose)
454 targetPositioninRootFrame = in.getIdlePosition();
458 idlePosition = in.getIdlePosition();
459 targetPositioninRootFrame->y -= in.getTargetYOffset();
460 targetPositioninRootFrame->z += in.getTargetZOffset();
466 FramedPositionPtr globalTargetPosition = targetPositioninRootFrame->toGlobal(robot);
469 #if ENABLE_DEBUG_DRAWER
470 getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
472 globalTargetPosition,
473 DrawColor{0.0f, 1.0f, 1.0f, 1.0},
475 getDebugDrawerTopic()->setLineVisu(
"HandOverState",
477 globalTargetPosition,
480 humanIsReachingOut ? DrawColor{0.0f, 1.0f, 0.0f, 1.0}
481 : DrawColor{1.0f, 0, 0, 1.0});
484 .position(globalTargetPosition->toEigen())
485 .
color(simox::Color::cyan())
489 .fromTo(globalTargetPosition->toEigen(), globalHeadIkTarget->toEigen())
496 pid.
update(duration.toMicroSecondsDouble(),
497 tcp->getPositionInRootFrame(),
498 targetPositioninRootFrame->toEigen());
500 if (targetVelocityVec.norm() > maxTCPVelocity)
502 targetVelocityVec = targetVelocityVec.normalized() * maxTCPVelocity;
504 if (distanceToTarget < considerForceDistanceThreshold)
506 targetVelocityVec = Eigen::Vector3f::Zero();
507 if (!nulledForceTorqueRef)
509 nulledForceTorqueRef = DatafieldRefPtr::dynamicCast(
510 getForceTorqueObserver()->createNulledDatafield(forceTorqueRef));
514 float forceMagnitude = nulledFT->toEigen().norm();
517 getDebugObserver()->setDebugChannel(
519 {{
"ForceX",
new Variant(nulledFT->x)},
520 {
"ForceZ",
new Variant(nulledFT->z)},
521 {
"ForceMagnitude",
new Variant(forceMagnitude)}});
523 forceMagnitudeAvgFilter.
update(now.toMicroSeconds(),
new Variant(forceMagnitude));
524 float forceMagnitudeFiltered = forceMagnitudeAvgFilter.
getValue()->getFloat();
527 getDebugObserver()->setDebugDatafield(
"ReachOutToHuman",
528 "forceMagnitudeFiltered",
529 new Variant(forceMagnitudeFiltered));
533 <<
" threshold: " << in.getForceThreshold();
534 if (forceMagnitude > in.getForceThreshold())
537 this->emitForceThresholdReached();
543 nulledForceTorqueRef =
nullptr;
558 Pose(targetOrientation->toEigen(), targetPositioninRootFrame->toEigen()).toEigen(),
559 VirtualRobot::IKSolver::All);
560 ctrl->setTargetVelocity(
cv(0),
cv(1),
cv(2),
cv(3),
cv(4),
cv(5));
574 ctrl->deactivateAndDeleteController();
579 if (in.isObjectIDSet() && in.getObjectID().find(
"/") != std::string::npos)
581 objpose::DetachObjectFromRobotNodeInput
input;
584 objpose::DetachObjectFromRobotNodeOutput result =
585 getObjectPoseStorage()->detachObjectFromRobotNode(
input);
588 else if (in.isObjectIDSet())
591 <<
" because it does not look like an ObjectID.";
596 <<
"Not detaching object in ObjectPoseStorage because 'ObjectID' is not set.";
607 ReachOutToHumanPose::onExit()
611 #if ENABLE_DEBUG_DRAWER
612 getDebugDrawerTopic()->removeLayer(
"HandOverState");
614 if (in.getStopOpenPoseAfterRun())
617 getOpenPoseEstimation()->stop();
626 ReachOutToHumanPose::getHeadIKTarget(
const Keypoint3DMap& keypointMap)
647 std::vector<std::pair<std::string, float>> keypointPriorityOrderWithOffset = {
661 for (
auto& pair : keypointPriorityOrderWithOffset)
663 auto it = keypointMap.find(pair.first);
664 if (it != keypointMap.end() && isKeypointValid(it->second))
666 ARMARX_INFO <<
"Selected node for head ik target is: " << it->first
668 Eigen::Vector3f result(it->second.x, it->second.y - pair.second, it->second.z);
673 return Eigen::Vector3f::Zero();
677 ReachOutToHumanPose::calculateMedian(
const Keypoint3DMap& keypointMap)
679 if (keypointMap.empty())
681 return Eigen::Vector3f::Zero();
683 Eigen::MatrixXf keypointPositions(keypointMap.size(), 3);
684 std::vector<std::vector<float>>
values(3, Ice::FloatSeq(keypointMap.size(), 0.0));
686 for (
auto& pair : keypointMap)
688 values.at(0).at(i) = pair.second.x;
689 values.at(1).at(i) = pair.second.y;
690 values.at(2).at(i) = pair.second.z;
698 size_t center = keypointMap.size() / 2;
699 return Eigen::Vector3f(
704 ReachOutToHumanPose::calculateCentroid(
const Keypoint3DMap& keypointMap)
706 Eigen::MatrixXf keypointPositions(keypointMap.size(), 3);
708 for (
auto& pair : keypointMap)
710 keypointPositions(i, 0) = pair.second.x;
711 keypointPositions(i, 1) = pair.second.y;
712 keypointPositions(i, 2) = pair.second.z;
715 return keypointPositions.colwise().mean();
719 ReachOutToHumanPose::getClosestPerson(
const std::vector<Keypoint3DMap>& trackingData)
722 Keypoint3DMap result;
723 for (
auto& keypointMap : trackingData)
725 if (keypointMap.empty())
729 float distance = calculateMedian(keypointMap).norm();
733 result = keypointMap;
740 ReachOutToHumanPose::isKeypointValid(
const Keypoint3D& point)
const
742 if (point.confidence < in.getMinimumKeypointConfidence())
747 return Eigen::Vector3f(point.x, point.y, point.z).norm() > 100;