27 #include <unordered_set>
31 #include <QDoubleSpinBox>
32 #include <QFileDialog>
35 #include <QPushButton>
39 #include <QTableWidget>
40 #include <QTableWidgetItem>
43 #include <VirtualRobot/Robot.h>
44 #include <VirtualRobot/RobotNodeSet.h>
45 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualization.h>
46 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
47 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationNode.h>
48 #include <VirtualRobot/Visualization/Visualization.h>
56 #include <RobotComponents/interface/components/MotionPlanning/Tasks/MotionPlanningTask.h>
58 #include <MotionPlanning/CSpace/CSpaceSampled.h>
63 while (QLayoutItem* item = layout->takeAt(0))
67 if (QWidget* widget = item->widget())
69 widget->deleteLater();
72 if (QLayout* childLayout = item->layout())
82 static const float EDGE_WIDTH{1.f};
83 static const armarx::DrawColor EDGE_COLOR_TREE{0.f, 1.f, 0.f, 1.f};
84 static const armarx::DrawColor EDGE_COLOR_PATH{0.f, 0.f, 1.f, 1.f};
94 widget.sliderPathPosition->setScalePosition(QwtSlider::LeadingScale);
95 widget.sliderPathPosition->setScaleStepSize(0.001);
100 widget.loadFromServerLayout->addWidget(motionPlanningServerTaskList);
102 updateCollisionStateTimer.setSingleShot(
true);
110 motionPlanningServerProxyName =
112 ->value(
"motionPlanningServerProxyName",
113 QString::fromStdString(motionPlanningServerProxyName))
121 settings->setValue(
"motionPlanningServerProxyName",
122 QString::fromStdString(motionPlanningServerProxyName));
128 return "MotionPlanning.SimoxCSpaceVisualizer";
134 visuRoot =
new SoSeparator();
138 visuAgent =
new SoSeparator();
140 visuRoot->addChild(visuAgent);
144 ARMARX_INFO <<
"Creating debug drawer component " << debugDrawerComponentName;
145 debugDrawer = Component::create<armarx::DebugDrawerComponent>(
getIceProperties(),
146 debugDrawerComponentName);
150 debugDrawer->setMutex(
mutex3D);
160 std::unique_lock lock(*
mutex3D);
161 visuRoot->addChild(debugDrawer->getVisualization());
176 dialog->addProxyFinder<MotionPlanningServerInterfacePrx>(
177 {
"MotionPlanningServer",
"The MotionPlanningServer (empty for none)",
"*Server"});
179 return qobject_cast<SimpleConfigDialog*>(dialog);
185 motionPlanningServerProxy =
186 getProxy<MotionPlanningServerInterfacePrx>(motionPlanningServerProxyName);
187 motionPlanningServerTaskList->setMotionPlanningServer(motionPlanningServerProxy);
188 motionPlanningServerTaskList->enableAutoUpdate(
true);
197 std::cout <<
"Returning scene = " << visuRoot->getName() << std::endl;
210 debugDrawer->setMutex(
mutex3D);
217 connect(widget.pushButtonLoadTask, SIGNAL(clicked()),
this, SLOT(
loadTask()));
220 connect(widget.checkBoxDrawTrace, SIGNAL(clicked(
bool)),
this, SLOT(
drawEdges(
bool)));
222 connect(widget.doubleSpinBoxTraceStep,
223 SIGNAL(valueChanged(
double)),
226 connect(widget.pushButtonHideAllPaths, SIGNAL(clicked()),
this, SLOT(
hideAllPaths()));
228 connect(widget.pushButtonSetConfig, SIGNAL(clicked()),
this, SLOT(
setCurrentConfig()));
229 connect(widget.spinBoxPathNumber, SIGNAL(valueChanged(
int)),
this, SLOT(
setCurrentPath()));
230 connect(widget.sliderPathPosition,
231 SIGNAL(valueChanged(
double)),
234 connect(widget.sliderPathPosition,
235 SIGNAL(valueChanged(
double)),
238 connect(widget.checkBoxCollisionNodeHighlighting,
239 SIGNAL(toggled(
bool)),
242 startTimer(timerPeriod);
248 if (widget.checkBoxDrawTrace->isChecked())
257 for (std::size_t pIdx = 0; pIdx < paths.size(); ++pIdx)
259 auto&& path = paths.at(pIdx);
261 path.visible && widget.checkBoxShowTreeEdges->isChecked());
276 int newPath = widget.spinBoxPathNumber->value() - 1;
283 widget.sliderPathPosition->setScale(0.0,
getCurrentPath().getLength());
284 widget.sliderPathPosition->setValue(0);
285 widget.sliderPathPosition->setScaleStepSize(
getCurrentPath().getLength() * 0.3);
286 widget.sliderPathPosition->setTotalSteps(100);
287 widget.labelCurrentPathNodeCount->setText(QString::number(
getCurrentPath().path.size()));
288 widget.labelCurrentPathTotalLen->setText(QString::number(
getCurrentPath().getLength()));
295 if (!widget.spinBoxPathNumber->value())
297 widget.labelCurrentPathCurrentLen->setText(QString::number(0));
302 const auto val = widget.sliderPathPosition->value();
303 widget.labelCurrentPathCurrentLen->setText(QString::number(val));
305 std::distance(pathAcc.begin(), std::upper_bound(pathAcc.begin(), pathAcc.end(), val));
306 widget.labelCurrentPathCurrentNode->setText(QString::number(indexTo));
308 assert(indexTo >= 0);
309 assert(
static_cast<std::size_t
>(indexTo) <= path.size());
317 if (
static_cast<std::size_t
>(indexTo) == path.size())
323 auto indexFrom = indexTo - 1;
324 auto cfgFrom = path.at(indexFrom);
325 auto cfgTo = path.at(indexTo);
327 auto t = (val - pathAcc.at(indexFrom)) / (pathAcc.at(indexTo) - pathAcc.at(indexFrom));
331 SimoxCSpacePtr simoxcspace = SimoxCSpacePtr::dynamicCast(cspaceUtil);
334 CSpaceAdaptorBasePtr simoxcspaceadapter = CSpaceAdaptorBasePtr::dynamicCast(cspaceUtil);
335 if (simoxcspaceadapter)
337 simoxcspace = SimoxCSpacePtr::dynamicCast(simoxcspaceadapter->getOriginalCSpace());
343 VirtualRobot::RobotNodeSetPtr rns = VirtualRobot::RobotNodeSet::createRobotNodeSet(
344 simoxcspace->getAgentSceneObj(),
"tmp", simoxcspace->getAgentJointNames());
345 Saba::CSpaceSampled tmpCSpace(
346 simoxcspace->getAgentSceneObj(),
347 VirtualRobot::CDManagerPtr(
new VirtualRobot::CDManager(simoxcspace->getCD())),
349 Eigen::VectorXf from = Eigen::Map<Eigen::VectorXf>(cfgFrom.data(), cfgFrom.size());
350 Eigen::VectorXf to = Eigen::Map<Eigen::VectorXf>(cfgTo.data(), cfgTo.size());
351 auto config = tmpCSpace.interpolate(from, to, t);
352 cfgToSet = (VectorXf{config.data(), config.data() + config.rows()});
359 std::back_inserter(cfgToSet),
360 [t](
float from,
float to) { return t * to + (1 - t) * from; });
373 VectorXf cfg(cspaceVisu->getDimensionality());
375 for (std::size_t i = 0; i < cfg.size(); ++i)
378 dynamic_cast<QDoubleSpinBox*
>(widget.tableWidgetDims->cellWidget(i, 2))->
value();
383 const float* it = cfg.data();
384 cspaceVisu->setConfig(it);
385 widget.labelCurrentMinObjDist->setText(
"loading...");
386 widget.labelCurrentCollisionState->setText(
"loading...");
387 updateCollisionStateTimer.start(100);
393 widget.labelCurrentMinObjDist->setText(QString::number(cspaceVisu->getCD().getDistance()));
394 widget.labelCurrentCollisionState->setText(
395 cspaceVisu->getCD().isInCollision() ? QString{
"in collision"} : QString{
"no collision"});
401 std::vector<std::string> nodeNames;
402 nodeNames.reserve(robotNodes.size());
403 for (
const auto& node : robotNodes)
407 nodeNames.emplace_back(node.first->getName());
411 for (std::size_t idxPath = 0; idxPath < paths.size(); ++idxPath)
413 const auto& pDat = paths.at(idxPath);
414 const auto& p = pDat.path;
424 const float traceStep = drawTrace
425 ?
static_cast<float>(widget.doubleSpinBoxTraceStep->value())
426 : std::numeric_limits<float>::infinity();
427 auto traces = cspaceUtil->getTraceVisu(p, nodeNames, traceStep);
430 for (std::size_t i = 0; i < traces.size(); ++i)
432 DebugDrawerLineSet&
trace = traces.at(i);
433 trace.colorFullIntensity = EDGE_COLOR_PATH;
434 trace.colorNoIntensity = EDGE_COLOR_PATH;
435 trace.lineWidth = EDGE_WIDTH;
436 trace.intensities.assign(
trace.points.size() / 2, 1);
437 debugDrawer->setLineSetVisu(layer,
"trace:" + nodeNames.at(i),
trace);
445 auto&& path = paths.at(
index);
446 const bool vis = (state == Qt::Checked);
449 vis && widget.checkBoxShowTreeEdges->isChecked());
455 for (
int i = 0;
static_cast<std::size_t
>(i) < paths.size(); ++i)
457 dynamic_cast<QCheckBox*
>(widget.tableWidgetPaths->cellWidget(i, 0))->setChecked(
false);
464 if (!motionPlanningServerProxy)
468 const auto taskId = motionPlanningServerTaskList->getSelectedId();
475 auto taskCSpaceAndPaths = motionPlanningServerProxy->getTaskCSpaceAndPathsById(*taskId);
477 auto& taskCSpace = taskCSpaceAndPaths.cspace;
481 ARMARX_ERROR_S <<
"Task with id " << *taskId <<
" returned an empty cspace!";
486 while (taskCSpace->ice_isA(CSpaceAdaptor::ice_staticId()))
488 if (taskCSpace->ice_isA(ScaledCSpace::ice_staticId()))
490 auto scalingCSpace = ScaledCSpacePtr::dynamicCast(taskCSpace);
491 for (
auto& path : taskCSpaceAndPaths.paths)
493 scalingCSpace->unscalePath(path);
497 taskCSpace = CSpaceAdaptorPtr::dynamicCast(taskCSpace)->getOriginalCSpace();
500 if (taskCSpace->ice_isA(SimoxCSpace::ice_staticId()))
504 setPaths(std::move(taskCSpaceAndPaths.paths));
518 std::unique_lock lock(*
mutex3D);
522 toClear->removeAllChildren();
531 std::unique_lock lock(*
mutex3D);
533 if (!visuRoot || !toRemove)
538 if (visuRoot->findChild(toRemove) >= 0)
540 visuRoot->removeChild(toRemove);
547 std::unique_lock lock(*
mutex3D);
549 if (!visuRoot || !toAdd)
554 if (visuRoot->findChild(toAdd) < 0)
556 visuRoot->addChild(toAdd);
563 std::unique_lock lock(*
mutex3D);
565 if (!visuRoot || !toToggle)
570 if (visuRoot->findChild(toToggle) < 0)
572 visuRoot->addChild(toToggle);
576 visuRoot->removeChild(toToggle);
583 std::unique_lock lock(*
mutex3D);
585 if (!visuRoot || !child)
592 if (visuRoot->findChild(child) < 0)
594 visuRoot->addChild(child);
599 if (!(visuRoot->findChild(child) < 0))
601 visuRoot->removeChild(child);
610 cspaceUtil = SimoxCSpacePtr::dynamicCast(newCSpace->clone());
611 cspaceVisu = SimoxCSpacePtr::dynamicCast(newCSpace->clone(
true));
612 cspaceUtil->initCollisionTest();
613 cspaceVisu->initCollisionTest();
616 const auto&& dimensions = cspaceUtil->getDimensionsBounds();
617 auto jointNames = cspaceUtil->getAgentJointNames();
618 assert(jointNames.size() == dimensions.size());
620 widget.tableWidgetDims->setRowCount(dimensions.size());
622 for (std::size_t i = 0; i < dimensions.size(); ++i)
624 const std::string& name = jointNames.at(i);
625 const FloatRange& bounds = dimensions.at(i);
627 widget.tableWidgetDims->setItem(i, 0,
new QTableWidgetItem{QString::fromStdString(name)});
629 widget.tableWidgetDims->setItem(i, 1,
new QTableWidgetItem{QString::number(bounds.min)});
630 auto spinbox =
new QDoubleSpinBox{};
631 spinbox->setRange(bounds.min, bounds.max);
632 widget.tableWidgetDims->setCellWidget(i, 2, spinbox);
633 widget.tableWidgetDims->setItem(i, 3,
new QTableWidgetItem{QString::number(bounds.max)});
634 spinbox->setSingleStep((bounds.max - bounds.min) / 10000.0);
636 spinbox, SIGNAL(valueChanged(
double)), widget.pushButtonSetConfig, SIGNAL(clicked()));
639 visuObjStat.resize(cspaceVisu->getStationaryObjectSet()->getSize());
642 std::unique_lock lock(*
mutex3D);
643 auto robot = cspaceVisu->getAgentSceneObj();
644 robot->setUpdateVisualization(
true);
645 robotVisu = robot->getVisualization();
647 auto robotCoinVisu = robotVisu->getCoinVisualization(
true);
650 visuAgent->removeAllChildren();
651 visuAgent->addChild(robotCoinVisu);
653 auto boxVis =
new QCheckBox{QString::fromStdString(robot->getName())};
654 boxVis->setChecked(
true);
657 widget.scrollAreaWidgetContentsObj->layout()->addWidget(boxVis);
662 for (
int i = 0; i < static_cast<int>(visuObjStat.size()); ++i)
664 auto sceneObj = cspaceVisu->getStationaryObjectSet()->getSceneObject(i);
666 auto& sep = visuObjStat.at(i);
668 sep =
new SoSeparator();
670 sceneObj->setUpdateVisualization(
true);
671 sceneObj->setUpdateCollisionModel(
true);
672 auto* visu =
dynamic_cast<VirtualRobot::CoinVisualizationNode*
>(
673 sceneObj->getVisualization(VirtualRobot::SceneObject::Collision).get());
674 auto coin = visu->getCoinVisualization();
675 ARMARX_INFO << sceneObj->getName() <<
" visu: " << visu <<
" coin: " << coin;
679 auto boxVis =
new IndexedQCheckBox{i, QString::fromStdString(sceneObj->getName())};
680 boxVis->setChecked(
true);
682 SIGNAL(stateChangedIndex(
int, Qt::CheckState)),
685 widget.scrollAreaWidgetContentsObj->layout()->addWidget(boxVis);
690 auto nodes = cspaceUtil->getAgentSceneObj()->getRobotNodes();
694 std::back_inserter(robotNodes),
695 [](VirtualRobot::RobotNodePtr& nptr) {
696 return std::pair<VirtualRobot::RobotNodePtr, bool>{std::move(nptr), false};
700 std::set<std::string> visTcps;
701 const auto& kinematicChainNames = cspaceUtil->getAgent().kinemaicChainNames;
703 kinematicChainNames.begin(),
704 kinematicChainNames.end(),
705 std::inserter(visTcps, visTcps.begin()),
706 [
this](
const std::string& name)
707 { return cspaceUtil->getAgentSceneObj()->getRobotNodeSet(name)->getTCP()->getName(); });
709 for (
int i = 0; i < static_cast<int>(robotNodes.size()); ++i)
711 const auto& node = robotNodes.at(i).first;
712 const auto& name = node->getName();
713 bool visible = visTcps.find(name) != visTcps.end();
714 robotNodes.at(i).second = visible;
716 auto boxVis =
new IndexedQCheckBox{i, QString::fromStdString(node->getName())};
717 boxVis->setChecked(visible);
719 SIGNAL(stateChangedIndex(
int, Qt::CheckState)),
721 SLOT(toggleRobotNodePath(
int, Qt::CheckState)));
722 widget.scrollAreaWidgetContentsJoints->layout()->addWidget(boxVis);
726 setEnabledSimoxCSpace();
730 SimoxCSpaceVisualizerWidgetController::toggleRobotNodePath(
int index, Qt::CheckState)
732 robotNodes.at(
index).second ^= 1;
741 assert(paths.empty());
745 std::back_inserter(paths),
749 dat.path = std::move(p.nodes);
752 dat.accPathLength.emplace_back(0);
753 for (std::size_t i = 1; i < dat.path.size(); ++i)
755 const auto& cfg1 = dat.path.at(i - 1);
756 const auto& cfg2 = dat.path.at(i);
758 lengthAcc += euclideanDistance(cfg1.begin(), cfg1.end(), cfg2.begin());
759 dat.accPathLength.emplace_back(lengthAcc);
765 widget.spinBoxPathNumber->setMaximum(paths.size());
767 widget.tableWidgetPaths->setRowCount(paths.size());
768 for (std::size_t i = 0; i < paths.size(); ++i)
770 const auto& p = paths.at(i);
771 widget.tableWidgetPaths->setItem(
772 i, 1,
new QTableWidgetItem{QString::number(p.path.size())});
773 widget.tableWidgetPaths->setItem(
774 i, 2,
new QTableWidgetItem{QString::number(p.getLength())});
777 QString::fromStdString(newPaths.at(i).pathName)};
779 SIGNAL(stateChangedIndex(
int, Qt::CheckState)),
781 SLOT(setVisibilityPath(
int, Qt::CheckState)));
782 boxVis->setChecked(
true);
783 widget.tableWidgetPaths->setCellWidget(i, 0, boxVis);
786 setEnabledPaths(
true);
789 updateEdgeVisibility();
795 if (widget.tableWidgetDims->columnCount() < 2)
799 for (std::size_t i = 0; i < cfg.size() && (
int)i < widget.tableWidgetDims->rowCount(); ++i)
801 auto spinbox =
dynamic_cast<QDoubleSpinBox*
>(widget.tableWidgetDims->cellWidget(i, 2));
804 spinbox->setValue(cfg.at(i));
816 widget.labelCurrentMinObjDist->setText(
"");
817 widget.tableWidgetDims->clearContents();
818 widget.tableWidgetDims->setRowCount(0);
820 clearLayout(widget.scrollAreaWidgetContentsJoints->layout());
821 clearLayout(widget.scrollAreaWidgetContentsObj->layout());
823 for (
auto& sep : visuObjStat)
829 visuAgent->removeAllChildren();
830 debugDrawer->clearAll();
831 cspaceUtil =
nullptr;
832 cspaceVisu =
nullptr;
840 widget.tableWidgetPaths->clearContents();
841 widget.spinBoxPathNumber->setMaximum(0);
842 widget.doubleSpinBoxMovePerMs->setValue(0);
849 widget.groupBoxCurCfg->setEnabled(active);
850 widget.groupBoxVisPath->setEnabled(active);
851 widget.groupBoxVisObj->setEnabled(active);
857 widget.groupBoxPaths->setEnabled(active);
858 widget.groupBoxPathOpt->setEnabled(active);
864 const auto step = widget.doubleSpinBoxMovePerMs->value() * timerPeriod;
866 if (widget.checkBoxAnimate->isChecked() && step != 0)
868 widget.sliderPathPosition->setValue(widget.sliderPathPosition->value() + step);
883 auto robot = cspaceVisu->getAgentSceneObj();
885 std::unique_lock lock(*
mutex3D);
886 robot->setUpdateVisualization(
true);
889 std::set<std::string> nodesToHighlight;
890 for (
auto&
setName : cspaceVisu->getAgent().collisionSetNames)
893 for (
size_t i = 0; i <
set->getSize(); ++i)
895 VirtualRobot::RobotNodePtr node =
set->getNode(i);
896 nodesToHighlight.insert(node->getName());
900 for (
auto& name : nodesToHighlight)
902 auto node = robot->getRobotNode(name);
904 node->highlight(robotVisu,
enabled);