33 #include <RobotAPI/interface/units/RobotUnit/NJointCartesianVelocityControllerWithRamp.h>
49 using namespace HandOverGroup;
52 ReachOutToHumanPose::SubClassRegistry ReachOutToHumanPose::Registry(ReachOutToHumanPose::GetName(), &ReachOutToHumanPose::CreateInstance);
54 #define ENABLE_DEBUG_DRAWER 0
57 void ReachOutToHumanPose::onEnter()
65 void ReachOutToHumanPose::run()
71 c->getOpenPoseEstimation()->start3DPoseEstimation();
77 std::vector<filters::AverageFilter> targetPositionFilter(3), targetVelocityFilter(3);
78 for (
auto& filter : targetPositionFilter)
80 filter.windowFilterSize = in.getTargetPositionAverageFilterSize();
82 for (
auto& filter : targetVelocityFilter)
84 filter.windowFilterSize = in.getTargetPositionAverageFilterSize();
86 float maxTCPVelocity = in.getMaxTCPVelocity();
87 float considerForceDistanceThreshold = in.getConsiderForceDistanceThreshold();
88 auto robot = getLocalRobot();
91 auto node = robot->getRobotNode(
"ArmR3_Sho2");
92 node->setJointLimits(node->getJointLimitLow(), VirtualRobot::MathTools::deg2rad(90));
95 auto node = robot->getRobotNode(
"ArmR5_Elb1");
96 node->setJointLimits(VirtualRobot::MathTools::deg2rad(5), node->getJointLimitHigh());
99 auto node = robot->getRobotNode(
"ArmR1_Cla1");
100 node->setJointLimits(VirtualRobot::MathTools::deg2rad(-45), VirtualRobot::MathTools::deg2rad(45));
104 auto node = robot->getRobotNode(
"ArmL3_Sho2");
105 node->setJointLimits(node->getJointLimitLow(), VirtualRobot::MathTools::deg2rad(90));
108 auto node = robot->getRobotNode(
"ArmL5_Elb1");
109 node->setJointLimits(VirtualRobot::MathTools::deg2rad(5), node->getJointLimitHigh());
112 auto node = robot->getRobotNode(
"ArmL1_Cla1");
113 node->setJointLimits(VirtualRobot::MathTools::deg2rad(-45), VirtualRobot::MathTools::deg2rad(45));
116 auto cameraRobot = getLocalRobot();
119 forceMagnitudeAvgFilter.windowFilterSize = 10;
121 getHeadIKUnit()->request();
124 ARMARX_INFO <<
"InitialHeadIKTarget(): " << in.getInitialHeadIKTarget()->output();
125 getHeadIKUnit()->setHeadTarget(in.getHeadIKNodeSet(), in.getInitialHeadIKTarget());
128 auto kinematicChainName = in.getKinematicChainName();
132 auto nodeset = robot->getRobotNodeSet(kinematicChainName);
133 auto tcp = nodeset->getTCP();
134 auto kinematicRootName = nodeset->getKinematicRoot()->getName();
136 ARMARX_INFO <<
VAROUT(kinematicRootName) <<
" position in root: " << nodeset->getKinematicRoot()->getPositionInRootFrame();
138 DatafieldRefPtr forceTorqueRef = DatafieldRefPtr::dynamicCast(getForceTorqueObserver()->getDataFieldRef(
new DataFieldIdentifier(getForceTorqueObserver()->getObserverName(),
139 in.getFTSensorName(),
"forces")));
142 NJointCartesianVelocityControllerWithRampConfigPtr ctrlCfg =
new NJointCartesianVelocityControllerWithRampConfig();
143 ctrlCfg->KpJointLimitAvoidance = 0;
144 ctrlCfg->jointLimitAvoidanceScale = 1;
146 ctrlCfg->nodeSetName = kinematicChainName;
147 ctrlCfg->tcpName = tcp->getName();
148 ctrlCfg->maxNullspaceAcceleration = 2;
149 ctrlCfg->maxPositionAcceleration = in.getTCPAcceleration();
150 ctrlCfg->maxOrientationAcceleration = 1;
153 posController.
maxOriVel = in.getMaxOrientationVelocity();
154 posController.
maxPosVel = in.getMaxTCPVelocity();
155 posController.
KpOri = in.getKpOrientation();
156 posController.
KpPos = in.getKp();
158 auto ctrlName =
"ReachOutToHumanTCPCtrl";
161 if (
auto oldCtrl =
getRobotUnit()->getNJointController(ctrlName))
164 while (oldCtrl->isControllerActive())
166 oldCtrl->deactivateController();
177 NJointCartesianVelocityControllerWithRampInterfacePrx ctrl = NJointCartesianVelocityControllerWithRampInterfacePrx::checkedCast(baseCtrl);
180 ctrl->activateController();
182 auto targetOrientation = in.getHandOrientation();
187 const Eigen::Vector3f offset {0, 0, 300};
188 Eigen::Vector3f idlePosi = tcp->getPositionInRootFrame() + offset;
190 idlePosi, robot->getRootNode()->getName(), robot->getName());
193 bool drawDebugInfos = in.getDrawDebugInfos();
195 while (!isRunningTaskStopped())
200 auto timeSinceSinceStart = now - startTime;
204 c->getImageSourceSelectionTopic()->setImageSource(
"OpenPoseEstimationResult", 5000);
207 std::vector<Keypoint3DMap> keypointList;
209 std::tie(timestamp, keypointList) =
c->getPoseData()->getLatestData();
213 Keypoint3DMap poseData = getClosestPerson(keypointList);
214 int validKeypoints = 0;
215 for (
auto& pair : poseData)
217 if (isKeypointValid(pair.second))
224 Keypoint3D rwrist = poseData[
"RWrist"];
226 bool validTarget =
false;
227 if (isKeypointValid(rwrist))
229 rwristFilter.
update(now.toMicroSeconds(),
new Variant(rwristPosition));
234 Vector3Ptr filteredRWristPosition = VariantPtr::dynamicCast(tmp)->get<
Vector3>();
240 #if ENABLE_DEBUG_DRAWER
241 this->getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
"rwrist", globalrWristPosition, DrawColor {1.0f, 0.0f, 0.0f, rwrist.confidence}, 30);
244 .position(globalrWristPosition->toEigen())
248 getDebugObserver()->setDebugChannel(
251 {
"rwristpoint_z",
new Variant(rwristPosition->z) },
252 {
"rwristpoint_z_filtered",
new Variant(filteredRWristPosition->z) },
256 rwristPosition = filteredRWristPosition;
259 Keypoint3D lwrist = poseData[
"LWrist"];
261 if (isKeypointValid(lwrist))
263 lwristFilter.update(now.toMicroSeconds(),
new Variant(lwristPosition));
265 tmp = lwristFilter.getValue();
268 lwristPosition = VariantPtr::dynamicCast(tmp)->get<
Vector3>();
274 #if ENABLE_DEBUG_DRAWER
275 this->getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
"lwrist", globallWristPosition, DrawColor {0.0f, 1.0f, 0.0f, lwrist.confidence}, 30);
278 .position(globallWristPosition->toEigen())
289 Vector3Ptr currentHandPosition = leftHandDistance < rightHandDistance ? lwristPosition : rwristPosition;
293 getDebugObserver()->setDebugChannel(
296 {
"zDiffLShoulder",
new Variant(
std::abs(poseData[
"LWrist"].z - poseData[
"LShoulder"].z)) },
297 {
"zDiffNeckR",
new Variant(
std::abs(poseData[
"RWrist"].z - poseData[
"Neck"].z)) },
298 {
"zDiffNeckL",
new Variant(
std::abs(poseData[
"LWrist"].z - poseData[
"Neck"].z)) }
303 Eigen::Vector3f headIkTarget = getHeadIKTarget(poseData);
305 globalHeadIkTarget->z =
std::max(in.getMinimumGazeZPosition(), globalHeadIkTarget->z);
307 if (validKeypoints > 3 && !in.getHeadIKNodeSet().empty() && headIkTarget.norm() > 0)
311 #if ENABLE_DEBUG_DRAWER
312 this->getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
"Median", globalHeadIkTarget, DrawColor {1.0f, 1.0f, 0.0f, 1.0f}, 50);
315 .position(globalHeadIkTarget->toEigen())
316 .
color(simox::Color::yellow())
320 headIKTargetFilter.
update(now.toMicroSeconds(),
new Variant(globalHeadIkTarget));
321 auto tmp = headIKTargetFilter.
getValue();
327 if (!lastIKTarget || (lastIKTarget->toEigen() - globalFilteredMedianKeypoint->toEigen()).norm() > 100)
329 getHeadIKUnit()->setHeadTarget(in.getHeadIKNodeSet(), globalFilteredMedianKeypoint);
330 lastIKTarget = globalFilteredMedianKeypoint;
337 #if ENABLE_DEBUG_DRAWER
338 this->getDebugDrawerTopic()->removeSphereVisu(
"HandOverState",
"Median");
345 float currentHumanReachDistance = headIkTarget.norm() == 0 && currentHandPosition->toEigen().norm() > 100 ? 0 :
std::abs((currentHandPosition->toEigen() - headIkTarget)(2));
346 bool humanIsReachingOut = currentHumanReachDistance > in.getHumanMinimumReachOutDistance();
348 Eigen::Vector3f currentTargetVec = currentHandPosition->toEigen();
349 for (
int i = 0; i < 3; ++i)
351 targetPositionFilter.at(i).update(now.toMicroSeconds(),
new Variant(currentTargetVec(i)));
353 Eigen::Vector3f currentFilteredTargetPositionVec;
354 for (
int i = 0; i < 3; ++i)
356 currentFilteredTargetPositionVec(i) = targetPositionFilter.at(i).getValue()->getFloat();
360 targetPositioninRootFrame->changeFrame(robot, robot->getRootNode()->getName());
366 targetInKinematicRootFrame->changeFrame(robot, kinematicRootName);
367 float reachDistanceToTarget = targetInKinematicRootFrame->toEigen().norm();
369 getDebugObserver()->setDebugDatafield(
"ReachOutToHuman",
"reachDistanceToTarget",
new Variant(reachDistanceToTarget));
370 float reachDistanceToTargetClamped = reachDistanceToTarget;
371 if (reachDistanceToTarget > in.getDistanceThresholdSoft() && reachDistanceToTarget < in.getDistanceThreshold())
373 Eigen::Vector3f targetPositioninKinematicRootFrameClamped = targetInKinematicRootFrame->toEigen().normalized() * in.getDistanceThresholdSoft();
374 targetPositioninRootFrame =
new FramedPosition(targetPositioninKinematicRootFrameClamped, targetInKinematicRootFrame->frame,
375 targetInKinematicRootFrame->agent);
376 targetPositioninRootFrame->changeFrame(robot, robot->getRootNode()->getName());
377 reachDistanceToTargetClamped = targetPositioninKinematicRootFrameClamped.norm();
382 getDebugObserver()->setDebugChannel(
385 {
"targetPositioninRootFrameX",
new Variant(targetPositioninRootFrame->x) },
386 {
"targetPositioninRootFrameY",
new Variant(targetPositioninRootFrame->y) },
387 {
"targetPositioninRootFrameZ",
new Variant(targetPositioninRootFrame->z) },
388 {
"targetPositioninRootNorm",
new Variant(targetPositioninRootFrame->toEigen().norm()) },
389 {
"reachDistanceToTargetClamped",
new Variant(reachDistanceToTargetClamped) }
393 float distanceToTarget = (tcp->getPositionInRootFrame() - targetPositioninRootFrame->toEigen()).
norm();
395 bool returnToIdlePose = reachDistanceToTarget > in.getDistanceThreshold() || !validTarget || !humanIsReachingOut || validKeypoints < 5 || timeSinceSinceStart.toMilliSeconds() < in.getMinWaitBeforeReachTime();
396 if (returnToIdlePose)
398 targetPositioninRootFrame = in.getIdlePosition();
402 idlePosition = in.getIdlePosition();
403 targetPositioninRootFrame->y -= in.getTargetYOffset();
409 FramedPositionPtr globalTargetPosition = targetPositioninRootFrame->toGlobal(robot);
412 #if ENABLE_DEBUG_DRAWER
413 getDebugDrawerTopic()->setSphereVisu(
"HandOverState",
"TargetPosition", globalTargetPosition, DrawColor {0.0f, 1.0f, 1.0f, 1.0}, 30);
414 getDebugDrawerTopic()->setLineVisu(
"HandOverState",
"ReachOutLine", globalTargetPosition, globalHeadIkTarget, 5,
415 humanIsReachingOut ? DrawColor {0.0f, 1.0f, 0.0f, 1.0} : DrawColor {1.0f, 0, 0, 1.0});
418 .position(globalTargetPosition->toEigen())
419 .
color(simox::Color::cyan())
421 vizLayer.
add(
viz::Cylinder(
"ReachOutLine").fromTo(globalTargetPosition->toEigen(), globalHeadIkTarget->toEigen())
427 pid.
update(duration.toMicroSecondsDouble(), tcp->getPositionInRootFrame(), targetPositioninRootFrame->toEigen());
429 if (targetVelocityVec.norm() > maxTCPVelocity)
431 targetVelocityVec = targetVelocityVec.normalized() * maxTCPVelocity;
433 if (distanceToTarget < considerForceDistanceThreshold)
435 targetVelocityVec = Eigen::Vector3f::Zero();
436 if (!nulledForceTorqueRef)
438 nulledForceTorqueRef = DatafieldRefPtr::dynamicCast(getForceTorqueObserver()->createNulledDatafield(forceTorqueRef));
441 float forceMagnitude = nulledFT->toEigen().norm();
444 getDebugObserver()->setDebugChannel(
447 {
"ForceX",
new Variant(nulledFT->x) },
448 {
"ForceZ",
new Variant(nulledFT->z) },
449 {
"ForceMagnitude",
new Variant(forceMagnitude) }
453 forceMagnitudeAvgFilter.
update(now.toMicroSeconds(),
new Variant(forceMagnitude));
454 float forceMagnitudeFiltered = forceMagnitudeAvgFilter.
getValue()->getFloat();
457 getDebugObserver()->setDebugDatafield(
"ReachOutToHuman",
"forceMagnitudeFiltered",
new Variant(forceMagnitudeFiltered));
461 if (forceMagnitude > in.getForceThreshold())
464 this->emitForceThresholdReached();
470 nulledForceTorqueRef =
nullptr;
484 Eigen::VectorXf
cv = posController.
calculate(
Pose(targetOrientation->toEigen(), targetPositioninRootFrame->toEigen()).toEigen(), VirtualRobot::IKSolver::All);
485 ctrl->setTargetVelocity(
cv(0),
cv(1),
cv(2),
cv(3),
cv(4),
cv(5));
499 ctrl->deactivateAndDeleteController();
504 if (in.isObjectIDSet() && in.getObjectID().find(
"/") != std::string::npos)
506 objpose::DetachObjectFromRobotNodeInput
input;
509 objpose::DetachObjectFromRobotNodeOutput result = getObjectPoseStorage()->detachObjectFromRobotNode(
input);
512 else if (in.isObjectIDSet())
514 ARMARX_IMPORTANT <<
"Not detaching object '" << in.getObjectID() <<
" because it does not look like an ObjectID.";
518 ARMARX_IMPORTANT <<
"Not detaching object in ObjectPoseStorage because 'ObjectID' is not set.";
528 void ReachOutToHumanPose::onExit()
532 #if ENABLE_DEBUG_DRAWER
533 getDebugDrawerTopic()->removeLayer(
"HandOverState");
535 if (in.getStopOpenPoseAfterRun())
538 getOpenPoseEstimation()->stop();
546 Eigen::Vector3f ReachOutToHumanPose::getHeadIKTarget(
const Keypoint3DMap& keypointMap)
567 std::vector<std::pair<std::string, float>> keypointPriorityOrderWithOffset =
569 {
"Neck", 20}, {
"RShoulder", 20}, {
"LShoulder", 20}, {
"Nose", 0}, {
"RElbow", 300}, {
"LElbow", 300},
570 {
"RWrist", 500}, {
"LWrist", 500}, {
"LHip", 600}, {
"RHip", 600}, {
"RKnee", 1100}, {
"LKnee", 1100},
572 for (
auto& pair : keypointPriorityOrderWithOffset)
574 auto it = keypointMap.find(pair.first);
575 if (it != keypointMap.end() && isKeypointValid(it->second))
578 Eigen::Vector3f result(it->second.x, it->second.y - pair.second, it->second.z);
583 return Eigen::Vector3f::Zero();
586 Eigen::VectorXf ReachOutToHumanPose::calculateMedian(
const Keypoint3DMap& keypointMap)
588 if (keypointMap.empty())
590 return Eigen::Vector3f::Zero();
592 Eigen::MatrixXf keypointPositions(keypointMap.size(), 3);
593 std::vector<std::vector<float>>
values(3, Ice::FloatSeq(keypointMap.size(), 0.0));
595 for (
auto& pair : keypointMap)
597 values.at(0).at(i) = pair.second.x;
598 values.at(1).at(i) = pair.second.y;
599 values.at(2).at(i) = pair.second.z;
607 size_t center = keypointMap.size() / 2;
608 return Eigen::Vector3f(
values.at(0).at(center),
613 Eigen::VectorXf ReachOutToHumanPose::calculateCentroid(
const Keypoint3DMap& keypointMap)
615 Eigen::MatrixXf keypointPositions(keypointMap.size(), 3);
617 for (
auto& pair : keypointMap)
619 keypointPositions(i, 0) = pair.second.x;
620 keypointPositions(i, 1) = pair.second.y;
621 keypointPositions(i, 2) = pair.second.z;
624 return keypointPositions.colwise().mean();
627 Keypoint3DMap ReachOutToHumanPose::getClosestPerson(
const std::vector<Keypoint3DMap>& trackingData)
630 Keypoint3DMap result;
631 for (
auto& keypointMap : trackingData)
633 if (keypointMap.empty())
637 float distance = calculateMedian(keypointMap).norm();
641 result = keypointMap;
647 bool ReachOutToHumanPose::isKeypointValid(
const Keypoint3D& point)
const
649 if (point.confidence < in.getMinimumKeypointConfidence())
654 return Eigen::Vector3f(point.x, point.y, point.z).norm() > 100;