29 #include <QFileDialog>
31 #include <QMessageBox>
33 #include <pcl/common/transforms.h>
34 #include <pcl/filters/filter.h>
35 #include <pcl/filters/passthrough.h>
36 #include <VirtualRobot/MathTools.h>
48 #include <VisionX/interface/components/Calibration.h>
75 dialog->addProxyFinder<visionx::PointCloudProviderInterfacePrx>(
76 {
"PointCloudProvider",
"PointCloudProvider",
"*"});
78 {
"RobotStateComponent",
"RobotStateComponent",
"RobotState*"});
80 return qobject_cast<SimpleConfigDialog*>(dialog);
86 providerName = dialog->getProxyName(
"PointCloudProvider");
87 robotStateComponentName = dialog->getProxyName(
"RobotStateComponent");
95 QMetaObject::invokeMethod(
this,
"initUI");
101 HandEyeCalibrationWidgetController::initUI()
104 widget.lineEdit_file->setText(QDir::currentPath() +
"/" +
defaultFileName);
109 HandEyeCalibrationWidgetController::saveDatapoint()
111 if (activeEndEffector)
114 entry.
Add(
"EndEffector", activeEndEffector->getName());
116 std::map<std::string, float> config = localRobot->getConfig()->getRobotNodeJointValueMap();
117 entry.
Add(
"RobotConfig", config);
122 std::string fileName = widget.lineEdit_file->text().toStdString();
127 QMessageBox::information(
128 getWidget(), tr(
"HandEyeCalibration"), tr(
"Datapoint successful written to file."));
133 HandEyeCalibrationWidgetController::selectFile()
135 QString fileName = QFileDialog::getOpenFileName(
getWidget(),
136 tr(
"Select file for saving data points"),
138 tr(
"JSON-File (*.json);; All Files (*)"));
139 if (!fileName.isEmpty())
141 widget.lineEdit_file->setText(fileName);
146 HandEyeCalibrationWidgetController::getEEFRobot(VirtualRobot::EndEffectorPtr eef)
const
148 if (eef && robotEEFMap.count(eef->getName()) == 1)
150 return robotEEFMap.at(eef->getName()).first;
156 HandEyeCalibrationWidgetController::getOffsetMatrixForEEF(
157 const VirtualRobot::EndEffectorPtr eef)
const
159 if (eef && robotEEFMap.count(eef->getName()) == 1)
161 return robotEEFMap.at(eef->getName()).second;
167 HandEyeCalibrationWidgetController::setOffsetMatrixForEEF(
const VirtualRobot::EndEffectorPtr eef,
170 if (eef && robotEEFMap.count(eef->getName()) == 1)
172 robotEEFMap.at(eef->getName()).second = offset;
181 robotStateComponentPrx =
182 getProxy<RobotStateComponentInterfacePrx>(robotStateComponentName,
true);
186 referenceFrame = getProviderFrame();
187 providerBuffer.reset(
new pcl::PointCloud<PointT>());
189 localRobot = loadRobotFromFile();
197 <<
"Exception while creating local clone of robot in onConnectPointCLoudProcessor()";
203 this, &HandEyeCalibrationWidgetController::taskEEFManipulationCB, 30.0f);
205 this, &HandEyeCalibrationWidgetController::taskLocalRobotUpdateCB, 50.0f);
208 QMetaObject::invokeMethod(
this,
"connectQt", Qt::BlockingQueuedConnection);
212 connect(widget.comboBox_endEffectorSelection,
213 SIGNAL(currentIndexChanged(QString)),
215 SLOT(activeEndEffectorChanged(QString)));
216 connect(widget.horizontalSlider_croppingBox,
217 SIGNAL(valueChanged(
int)),
219 SLOT(slider_croppingRange_changed(
int)));
220 connect(widget.checkBox_croppingActive,
221 SIGNAL(stateChanged(
int)),
223 SLOT(cb_croppingActive_changed(
int)));
225 connect(widget.checkBox_offsetInverted,
226 SIGNAL(toggled(
bool)),
228 SLOT(cb_inverted_changed(
bool)));
229 connect(widget.pushButton_copyToClipboard,
232 SLOT(btn_copyToClipboard_pressed()));
233 connect(widget.pushButton_resetEndeffector,
236 SLOT(btn_resetToModel_pressed()));
238 connect(widget.doubleSpinBox_camera_x,
239 SIGNAL(valueChanged(
double)),
241 SLOT(cameraNodeTransformationChanged(
double)));
242 connect(widget.doubleSpinBox_camera_y,
243 SIGNAL(valueChanged(
double)),
245 SLOT(cameraNodeTransformationChanged(
double)));
246 connect(widget.doubleSpinBox_camera_z,
247 SIGNAL(valueChanged(
double)),
249 SLOT(cameraNodeTransformationChanged(
double)));
250 connect(widget.doubleSpinBox_camera_roll,
251 SIGNAL(valueChanged(
double)),
253 SLOT(cameraNodeTransformationChanged(
double)));
254 connect(widget.doubleSpinBox_camera_pitch,
255 SIGNAL(valueChanged(
double)),
257 SLOT(cameraNodeTransformationChanged(
double)));
258 connect(widget.doubleSpinBox_camera_yaw,
259 SIGNAL(valueChanged(
double)),
261 SLOT(cameraNodeTransformationChanged(
double)));
263 connect(widget.pushButton_backgroundColor,
266 SLOT(btn_backgroundColor_pressed()));
267 connect(widget.pushButton_pointcloudColor,
270 SLOT(btn_pointCloudColor_pressed()));
272 widget.spinBox_pointSize, SIGNAL(valueChanged(
int)),
this, SLOT(sB_pointSize_changed(
int)));
274 connect(widget.pushButton_changeFile, SIGNAL(clicked()),
this, SLOT(selectFile()));
275 connect(widget.pushButton_saveDatapoint, SIGNAL(clicked()),
this, SLOT(saveDatapoint()));
277 taskEEFManipulation->start();
278 taskLocalRobotUpdate->start();
283 HandEyeCalibrationWidgetController::connectQt()
286 widget.displayWidget->getDisplay()->getRootNode()->addChild(pointCloudVisu);
289 widget.displayWidget->getDisplay()->getRootNode()->addChild((SoNode*)manipulatorVisu);
291 widget.displayWidget->getDisplay()->cameraViewAll();
295 setupEndEffectorSelection(localRobot);
301 setupCameraNodeSlider(pointcloudRobot);
302 disableGuiElements();
310 taskEEFManipulation->stop();
311 taskLocalRobotUpdate->stop();
314 QMetaObject::invokeMethod(
this,
"disconnectQt");
317 pointcloudRobot.reset();
318 activeEndEffector.reset();
319 providerBuffer.reset();
323 HandEyeCalibrationWidgetController::disconnectQt()
325 disableGuiElements();
327 widget.comboBox_endEffectorSelection->clear();
328 manipulatorVisu->removeVisualization();
329 pointCloudVisu->removeAllChildren();
330 widget.displayWidget->getDisplay()->getRootNode()->removeAllChildren();
333 QObject::disconnect(widget.comboBox_endEffectorSelection, 0, 0, 0);
354 std::vector<VirtualRobot::EndEffectorPtr> endEffectors;
355 robot->getEndEffectors(endEffectors);
356 for (VirtualRobot::EndEffectorPtr e : endEffectors)
358 widget.comboBox_endEffectorSelection->addItem(QString::fromStdString(e->getName()));
360 r->setGlobalPoseForRobotNode(r->getRobotNode(e->getTcp()->getName()),
361 e->getTcp()->getGlobalPose());
365 widget.comboBox_endEffectorSelection->setCurrentIndex(-1);
369 HandEyeCalibrationWidgetController::findStringIC(
const std::string& strHaystack,
370 const std::string& strNeedle)
const
373 std::search(strHaystack.begin(),
377 [](
char ch1,
char ch2) { return std::toupper(ch1) == std::toupper(ch2); });
378 return (it != strHaystack.end());
387 <<
"Robot " + robot->getName() +
" does not have node with name " + referenceFrame;
388 widget.label_cameraNodeName->setText(QString::fromStdString((referenceFrame)));
390 cameraNode = robot->getRobotNode(referenceFrame);
391 auto trans = VirtualRobot::MathTools::eigen4f2posrpy(cameraNode->getLocalTransformation());
393 widget.doubleSpinBox_camera_x->setValue(trans(0));
394 widget.doubleSpinBox_camera_y->setValue(trans(1));
395 widget.doubleSpinBox_camera_z->setValue(trans(2));
396 widget.doubleSpinBox_camera_roll->setValue(VirtualRobot::MathTools::rad2deg(trans(3)));
397 widget.doubleSpinBox_camera_pitch->setValue(VirtualRobot::MathTools::rad2deg(trans(4)));
398 widget.doubleSpinBox_camera_yaw->setValue(VirtualRobot::MathTools::rad2deg(trans(5)));
403 HandEyeCalibrationWidgetController::loadRobotFromFile()
const
405 Ice::StringSeq includePaths;
409 Ice::StringSeq packages = robotStateComponentPrx->getArmarXPackages();
412 for (
const std::string& projectName : packages)
414 if (projectName.empty())
420 auto pathsString =
project.getDataDir();
421 Ice::StringSeq projectIncludePaths =
armarx::Split(pathsString,
";,",
true,
true);
423 includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end());
428 ARMARX_ERROR <<
"Unable to retrieve robot filename." << std::endl;
434 std::string rfile = robotStateComponentPrx->getRobotFilename();
436 return VirtualRobot::RobotIO::loadRobot(rfile);
440 ARMARX_ERROR <<
"Unable to load robot from file" << std::endl;
446 HandEyeCalibrationWidgetController::getProviderFrame()
const
448 std::string frame =
"Global";
451 visionx::ReferenceFrameInterfacePrx refFramePrx =
452 visionx::ReferenceFrameInterfacePrx::checkedCast(providerInfo.
proxy);
455 frame = refFramePrx->getReferenceFrame();
466 HandEyeCalibrationWidgetController::formatTransformationMatrix(
Eigen::Matrix4f mat,
467 int decimalPlacesOrientation,
468 int decimalPlacesPosition)
const
470 std::stringstream ss;
471 ss.imbue(std::locale::classic());
475 for (
int i = 0; i < 3; i++)
477 float x0 = mat(i, 0);
478 float x1 = mat(i, 1);
479 float x2 = mat(i, 2);
480 ss << std::setprecision(decimalPlacesOrientation);
481 if (!std::signbit(x0))
486 if (!std::signbit(x1))
491 if (!std::signbit(x2))
497 ss << std::setprecision(decimalPlacesPosition);
498 if (!std::signbit(mat(i, 3)))
509 HandEyeCalibrationWidgetController::enableGuiElements()
511 widget.horizontalSlider_croppingBox->setEnabled(
true);
512 widget.horizontalSlider_x->setEnabled(
true);
513 widget.horizontalSlider_y->setEnabled(
true);
514 widget.horizontalSlider_z->setEnabled(
true);
515 widget.checkBox_croppingActive->setEnabled(
true);
516 widget.checkBox_offsetInverted->setEnabled(
true);
517 widget.pushButton_copyToClipboard->setEnabled(
true);
518 widget.pushButton_resetEndeffector->setEnabled(
true);
519 widget.pushButton_pointcloudColor->setEnabled(
true);
520 widget.pushButton_backgroundColor->setEnabled(
true);
521 widget.spinBox_pointSize->setEnabled(
true);
522 widget.pushButton_changeFile->setEnabled(
true);
523 widget.pushButton_saveDatapoint->setEnabled(
true);
524 widget.lineEdit_file->setEnabled(
true);
526 widget.doubleSpinBox_camera_x->setEnabled(
true);
527 widget.doubleSpinBox_camera_y->setEnabled(
true);
528 widget.doubleSpinBox_camera_z->setEnabled(
true);
529 widget.doubleSpinBox_camera_roll->setEnabled(
true);
530 widget.doubleSpinBox_camera_pitch->setEnabled(
true);
531 widget.doubleSpinBox_camera_yaw->setEnabled(
true);
533 widget.label_offsetMatrix->setText(QString(
"No Endeffector selected"));
534 widget.label_offsetMatrix->setStyleSheet(
"QLabel { color : black; }");
536 showPointCloud =
true;
540 HandEyeCalibrationWidgetController::disableGuiElements()
542 widget.horizontalSlider_croppingBox->setEnabled(
false);
543 widget.horizontalSlider_x->setEnabled(
false);
544 widget.horizontalSlider_y->setEnabled(
false);
545 widget.horizontalSlider_z->setEnabled(
false);
546 widget.checkBox_croppingActive->setEnabled(
false);
547 widget.checkBox_offsetInverted->setEnabled(
false);
548 widget.pushButton_copyToClipboard->setEnabled(
false);
549 widget.pushButton_resetEndeffector->setEnabled(
false);
550 widget.pushButton_pointcloudColor->setEnabled(
false);
551 widget.pushButton_backgroundColor->setEnabled(
false);
552 widget.spinBox_pointSize->setEnabled(
false);
553 widget.pushButton_changeFile->setEnabled(
false);
554 widget.pushButton_saveDatapoint->setEnabled(
false);
555 widget.lineEdit_file->setEnabled(
false);
557 widget.doubleSpinBox_camera_x->setEnabled(
false);
558 widget.doubleSpinBox_camera_y->setEnabled(
false);
559 widget.doubleSpinBox_camera_z->setEnabled(
false);
560 widget.doubleSpinBox_camera_roll->setEnabled(
false);
561 widget.doubleSpinBox_camera_pitch->setEnabled(
false);
562 widget.doubleSpinBox_camera_yaw->setEnabled(
false);
564 widget.label_offsetMatrix->setText(QString(
"No Endeffector selected"));
565 widget.label_offsetMatrix->setStyleSheet(
"QLabel { color : red; }");
567 showPointCloud =
false;
571 HandEyeCalibrationWidgetController::updateManipulatorVisualization()
573 if (activeEndEffector && manipulatorVisu)
576 localRobot->getRobotNode(activeEndEffector->getTcp()->getName())->getPoseInRootFrame();
580 Eigen::Matrix4f pointCloudGlobal = localRobot->getGlobalPose() * pointCloudInRoot;
583 manipulatorVisu->setUserDesiredPose(pointCloudGlobal);
586 getEEFRobot(activeEndEffector)
587 ->setGlobalPoseForRobotNode(activeEndEffector->getTcp(), pointCloudGlobal);
590 if (widget.checkBox_offsetInverted->isChecked())
592 offset = offset.inverse();
594 widget.label_offsetMatrix->setText(
595 QString::fromStdString(formatTransformationMatrix(offset)));
604 ARMARX_WARNING <<
"Timeout or error in waiting for pointclouds from provider: "
616 HandEyeCalibrationWidgetController::processPointCloud()
618 if (showPointCloud && pointcloudRobot)
621 pcl::PointCloud<PointT>::Ptr localBufferIn(
new pcl::PointCloud<PointT>());
622 pcl::copyPointCloud(*providerBuffer, *localBufferIn);
623 pcl::PointCloud<PointT>::Ptr localBufferOut(
new pcl::PointCloud<PointT>());
626 if (referenceFrame !=
"Global")
633 pointcloudRobot, robotStateComponentPrx, info->timeProvided);
637 ARMARX_ERROR <<
"Exception while synchronizing robot for pointcloud-processing";
641 sourceFrameGlobalPose = pointcloudRobot->getRobotNode(referenceFrame)->getGlobalPose();
646 if (croppingActive && manipulatorVisu && manipulatorVisu->getIsVisualizing())
649 Eigen::Matrix4f eefInReferenceFrame = sourceFrameGlobalPose.inverse() * eefGlobal;
651 float cropRange = (
float)widget.horizontalSlider_croppingBox->value();
653 Eigen::Vector3f cropMin;
654 cropMin << eefInReferenceFrame(0, 3) - cropRange, eefInReferenceFrame(1, 3) - cropRange,
655 eefInReferenceFrame(2, 3) - cropRange;
656 Eigen::Vector3f cropMax;
657 cropMax << eefInReferenceFrame(0, 3) + cropRange, eefInReferenceFrame(1, 3) + cropRange,
658 eefInReferenceFrame(2, 3) + cropRange;
660 pcl::PassThrough<PointT> pass;
662 pass.setInputCloud(localBufferIn);
663 pass.setFilterFieldName(
"z");
664 pass.setFilterLimits(cropMin(2), cropMax(2));
665 pass.filter(*localBufferOut);
666 localBufferOut.swap(localBufferIn);
668 pass.setInputCloud(localBufferIn);
669 pass.setFilterFieldName(
"x");
670 pass.setFilterLimits(cropMin(0), cropMax(0));
671 pass.filter(*localBufferOut);
672 localBufferOut.swap(localBufferIn);
674 pass.setInputCloud(localBufferIn);
675 pass.setFilterFieldName(
"y");
676 pass.setFilterLimits(cropMin(1), cropMax(1));
677 pass.filter(*localBufferOut);
678 localBufferOut.swap(localBufferIn);
683 pcl::transformPointCloud(*localBufferIn, *localBufferOut, cameraToGlobal);
684 localBufferOut.swap(localBufferIn);
694 HandEyeCalibrationWidgetController::cb_inverted_changed(
bool checked)
698 widget.label_titleOffsetMatrix->setText(
"Offset from Pointcloud to Model:");
702 widget.label_titleOffsetMatrix->setText(
"Offset from Model to Pointcloud:");
707 HandEyeCalibrationWidgetController::btn_copyToClipboard_pressed()
709 if (activeEndEffector)
711 Eigen::Matrix4f offset = this->getOffsetMatrixForEEF(activeEndEffector);
713 if (widget.checkBox_offsetInverted->isChecked())
715 offset = offset.inverse();
721 obj->serializeIceObject(pose);
723 QClipboard* clipboard = QApplication::clipboard();
724 clipboard->setText(QString::fromStdString(obj->asString(
true)));
729 HandEyeCalibrationWidgetController::btn_resetToModel_pressed()
731 if (activeEndEffector)
734 updateManipulatorVisualization();
735 widget.displayWidget->getDisplay()->cameraViewNode((SoNode*)manipulatorVisu, 1.0);
740 HandEyeCalibrationWidgetController::btn_backgroundColor_pressed()
742 QColor selectedColor = this->colorDialog.getColor();
743 widget.displayWidget->getDisplay()->setBackgroundColor(SbColor(selectedColor.red() / 255.0f,
744 selectedColor.green() / 255.0f,
745 selectedColor.blue() / 255.0f));
749 HandEyeCalibrationWidgetController::btn_pointCloudColor_pressed()
751 QColor selectedColor = this->colorDialog.getColor();
752 this->pointCloudVisu->
setDrawColor(SbColor(selectedColor.red() / 255.0f,
753 selectedColor.green() / 255.0f,
754 selectedColor.blue() / 255.0f));
758 HandEyeCalibrationWidgetController::sB_pointSize_changed(
int value)
764 HandEyeCalibrationWidgetController::cameraNodeTransformationChanged(
double value)
768 auto t = VirtualRobot::MathTools::eigen4f2posrpy(cameraNode->getLocalTransformation());
769 QObject* obj = sender();
770 if (obj == widget.doubleSpinBox_camera_x)
774 else if (obj == widget.doubleSpinBox_camera_y)
778 else if (obj == widget.doubleSpinBox_camera_z)
782 else if (obj == widget.doubleSpinBox_camera_roll)
784 t(3) = VirtualRobot::MathTools::deg2rad((
float)
value);
786 else if (obj == widget.doubleSpinBox_camera_pitch)
788 t(4) = VirtualRobot::MathTools::deg2rad((
float)
value);
790 else if (obj == widget.doubleSpinBox_camera_yaw)
792 t(5) = VirtualRobot::MathTools::deg2rad((
float)
value);
795 VirtualRobot::MathTools::posrpy2eigen4f(t(0), t(1), t(2), t(3), t(4), t(5));
796 cameraNode->setLocalTransformation(transformation);
801 HandEyeCalibrationWidgetController::taskEEFManipulationCB()
804 x[0] = (
float)widget.horizontalSlider_x->value();
805 x[1] = (
float)widget.horizontalSlider_y->value();
806 x[2] = (
float)widget.horizontalSlider_z->value();
807 if (activeEndEffector)
810 offsetInRoot(0, 3) = x[0];
811 offsetInRoot(1, 3) = x[1];
812 offsetInRoot(2, 3) = x[2];
814 setOffsetMatrixForEEF(activeEndEffector, offsetInRoot);
819 HandEyeCalibrationWidgetController::taskLocalRobotUpdateCB()
821 if (!robotStateComponentPrx)
828 updateManipulatorVisualization();
836 HandEyeCalibrationWidgetController::activeEndEffectorChanged(QString endEffectorName)
838 if (localRobot && manipulatorVisu)
840 std::string eefName = endEffectorName.toStdString();
841 if (!eefName.empty())
843 if (robotEEFMap.count(eefName) == 1)
845 activeEndEffector = robotEEFMap.at(eefName).first->getEndEffector(eefName);
846 manipulatorVisu->setVisualization(activeEndEffector);
847 widget.displayWidget->getDisplay()->cameraViewNode((SoNode*)manipulatorVisu, 1.0);
853 <<
" does not have endeffector with name " << eefName;
860 HandEyeCalibrationWidgetController::slider_croppingRange_changed(
int value)
862 this->widget.label_croppingBoxValue->setText(QString::fromStdString(
ValueToString(
value)));
866 HandEyeCalibrationWidgetController::cb_croppingActive_changed(
int state)
868 if (state == Qt::Unchecked)
870 this->croppingActive =
true;
874 this->croppingActive =
false;